Repository: Qiskit/qiskit

Issue:
ParameterVectors aren't unrolled by `execute(..., parameter_binds=[...])`
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**:
- **Python version**:
- **Operating system**:

### What is the current behavior?



### Steps to reproduce the problem

```

pv1 = qk.circuit.ParameterVector('pv1', 3)
pv2 = qk.circuit.ParameterVector('pv2', 3)
qc = qk.QuantumCircuit(2)
for i in range(3):
    qc.rx(pv1[i], 0)
    qc.ry(pv2[i], 1)
qc.draw()

     ┌────────────┐┌────────────┐┌────────────┐
q_0: ┤ RX(pv1[0]) ├┤ RX(pv1[1]) ├┤ RX(pv1[2]) ├
     ├────────────┤├────────────┤├────────────┤
q_1: ┤ RY(pv2[0]) ├┤ RY(pv2[1]) ├┤ RY(pv2[2]) ├
     └────────────┘└────────────┘└────────────┘


qc.bind_parameters({pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}).draw()

     ┌───────────────────────┐┌───────────────────────┐»
q_0: ┤ RX(0.100000000000000) ├┤ RX(0.200000000000000) ├»
     ├───────────────────────┤├───────────────────────┤»
q_1: ┤ RY(0.400000000000000) ├┤ RY(0.500000000000000) ├»
     └───────────────────────┘└───────────────────────┘»
«     ┌───────────────────────┐
«q_0: ┤ RX(0.300000000000000) ├
«     ├───────────────────────┤
«q_1: ┤ RY(0.600000000000000) ├
«     └───────────────────────┘

qk.execute(qc, qk.Aer.get_backend('qasm_simulator'), parameter_binds=[{pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}])


---------------------------------------------------------------------------
QiskitError                               Traceback (most recent call last)
<ipython-input-5-d02093a85edb> in <module>
----> 1 qk.execute(qc, qk.Aer.get_backend('qasm_simulator'), parameter_binds=[{pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}])

~/q/qiskit-terra/qiskit/execute.py in execute(experiments, backend, basis_gates, coupling_map, backend_properties, initial_layout, seed_transpiler, optimization_level, pass_manager, qobj_id, qobj_header, shots, memory, max_credits, seed_simulator, default_qubit_los, default_meas_los, schedule_los, meas_level, meas_return, memory_slots, memory_slot_size, rep_time, rep_delay, parameter_binds, schedule_circuit, inst_map, meas_map, scheduling_method, init_qubits, **run_config)
    296                         backend=backend,
    297                         init_qubits=init_qubits,
--> 298                         **run_config)
    299 
    300         # executing the circuits on the backend and returning the job

~/q/qiskit-terra/qiskit/compiler/assemble.py in assemble(experiments, backend, qobj_id, qobj_header, shots, memory, max_credits, seed_simulator, qubit_lo_freq, meas_lo_freq, qubit_lo_range, meas_lo_range, schedule_los, meas_level, meas_return, meas_map, memory_slot_size, rep_time, rep_delay, parameter_binds, parametric_pulses, init_qubits, **run_config)
    148         # If circuits are parameterized, bind parameters and remove from run_config
    149         bound_experiments, run_config = _expand_parameters(circuits=experiments,
--> 150                                                            run_config=run_config)
    151         end_time = time()
    152         _log_assembly_time(start_time, end_time)

~/q/qiskit-terra/qiskit/compiler/assemble.py in _expand_parameters(circuits, run_config)
    426                 ('Mismatch between run_config.parameter_binds and all circuit parameters. ' +
    427                  'Parameter binds: {} ' +
--> 428                  'Circuit parameters: {}').format(all_bind_parameters, all_circuit_parameters))
    429 
    430         circuits = [circuit.bind_parameters(binds)

QiskitError: 'Mismatch between run_config.parameter_binds and all circuit parameters. Parameter binds: [dict_keys([ParameterVector(name=pv1, length=3), ParameterVector(name=pv2, length=3)])] Circuit parameters: [{Parameter(pv1[1]), Parameter(pv2[1]), Parameter(pv1[0]), Parameter(pv2[0]), Parameter(pv1[2]), Parameter(pv2[2])}]'
```

### What is the expected behavior?



### Suggested solutions





Discussion:

Resolving patch:
diff --git a/qiskit/compiler/assemble.py b/qiskit/compiler/assemble.py
--- a/qiskit/compiler/assemble.py
+++ b/qiskit/compiler/assemble.py
@@ -420,11 +420,14 @@ def _expand_parameters(circuits, run_config):
     """
 
     parameter_binds = run_config.parameter_binds
+
     if parameter_binds or \
        any(circuit.parameters for circuit in circuits):
 
-        all_bind_parameters = [bind.keys()
+        # Unroll params here in order to handle ParamVects
+        all_bind_parameters = [QuantumCircuit()._unroll_param_dict(bind).keys()
                                for bind in parameter_binds]
+
         all_circuit_parameters = [circuit.parameters for circuit in circuits]
 
         # Collect set of all unique parameters across all circuits and binds


Test patch:
diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py
--- a/test/python/compiler/test_assembler.py
+++ b/test/python/compiler/test_assembler.py
@@ -19,7 +19,7 @@
 
 import numpy as np
 import qiskit.pulse as pulse
-from qiskit.circuit import Instruction, Gate, Parameter
+from qiskit.circuit import Instruction, Gate, Parameter, ParameterVector, ParameterExpression
 from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit.compiler.assemble import assemble
 from qiskit.exceptions import QiskitError
@@ -230,6 +230,30 @@ def test_assemble_opaque_inst(self):
         self.assertEqual(qobj.experiments[0].instructions[0].memory, [3, 0])
         self.assertEqual(qobj.experiments[0].instructions[0].params, [0.5, 0.4])
 
+    def test_assemble_unroll_parametervector(self):
+        """Verfiy that assemble unrolls parametervectors ref #5467"""
+        pv1 = ParameterVector('pv1', 3)
+        pv2 = ParameterVector('pv2', 3)
+        qc = QuantumCircuit(2, 2)
+        for i in range(3):
+            qc.rx(pv1[i], 0)
+            qc.ry(pv2[i], 1)
+        qc.barrier()
+        qc.measure([0, 1], [0, 1])
+
+        qc.bind_parameters({pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]})
+
+        qobj = assemble(qc, parameter_binds=[{pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}])
+        validate_qobj_against_schema(qobj)
+
+        self.assertIsInstance(qobj, QasmQobj)
+        self.assertEqual(qobj.experiments[0].instructions[0].params[0], 0.100000000000000)
+        self.assertEqual(qobj.experiments[0].instructions[1].params[0], 0.400000000000000)
+        self.assertEqual(qobj.experiments[0].instructions[2].params[0], 0.200000000000000)
+        self.assertEqual(qobj.experiments[0].instructions[3].params[0], 0.500000000000000)
+        self.assertEqual(qobj.experiments[0].instructions[4].params[0], 0.300000000000000)
+        self.assertEqual(qobj.experiments[0].instructions[5].params[0], 0.600000000000000)
+
     def test_measure_to_registers_when_conditionals(self):
         """Verify assemble_circuits maps all measure ops on to a register slot
         for a circuit containing conditionals."""