Repository: Qiskit/qiskit

Issue:
assemble.py _expand_parameters(circuits, run_config) apparently broken
<!-- ⚠️ 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**: master
- **Python version**: 3.6.8
- **Operating system**: Linux

### What is the current behavior?
`Parameter` binding does not succeed as reported by user `@Adrian Auer` in Qiskit Slack.

### Steps to reproduce the problem

```
from qiskit import Aer, QuantumCircuit, QuantumRegister, execute
from qiskit.circuit import Parameter

# create m = 2 circuits
qr = QuantumRegister(1)
quantum_circuit_1 = QuantumCircuit(qr)
quantum_circuit_2 = QuantumCircuit(qr)

theta = Parameter('theta')

# add parametrized gates
quantum_circuit_1.u3(theta, 0, 0, qr[0])
quantum_circuit_2.u3(theta, 3.14, 0, qr[0])

circuits = [quantum_circuit_1, quantum_circuit_2]

# inspect parameters property
for circuit in circuits:
    print(circuit.parameters)

# bind parameter to n = 1 values
job = execute(circuits,
              Aer.get_backend('qasm_simulator'),
              shots=512,
              parameter_binds=[{theta: 1}])
```
Result is error:
```
Traceback (most recent call last):
  File "adrian_auer_example.py", line 25, in <module>
    parameter_binds=[{theta: 1}])
  File "/home/jax/work/QISKit/DEV/qiskit-terra/qiskit/execute.py", line 218, in execute
    run_config=run_config
  File "/home/jax/work/QISKit/DEV/qiskit-terra/qiskit/compiler/assemble.py", line 149, in assemble
    run_config=run_config)
  File "/home/jax/work/QISKit/DEV/qiskit-terra/qiskit/compiler/assemble.py", line 298, in _expand_parameters
    'Circuit parameters: {}').format(all_bind_parameters, all_circuit_parameters))
qiskit.exceptions.QiskitError: 'Mismatch between run_config.parameter_binds and all circuit parameters. Parameter binds: [dict_keys([Parameter(theta)])] Circuit parameters: [{Parameter(theta)}, {Parameter(theta)}]'
```
### What is the expected behavior?
Parameter would bind and circuits would execute.


### Suggested solutions
In `qiskit/compiler/assembly.py:_expand_parameters` lines 293-294 both of the following tests are failing:
```
or any(unique_parameters != bind_params for bind_params in all_bind_parameters) \
           or any(unique_parameters != parameters for parameters in all_circuit_parameters):
```
It appears to be because `unique_parameters` is a `list` of `Parameter` each of which is being compared to the elements of a list of dictionaries.
The comparison should be re-examined so that types match up.




Discussion:
This bug is similar to #2429 . Right now, `Parameter`s depend on python identity for equality, but when they are serialized and sent to another process (here by `parallel_map` inside `transpile` inside `execute`), and return, they are instantiated with a new identity and so no longer treated as equal.

The comparison in lines 293-294 of `assembly.py` attempts to check that there is only one set of parameters (`unique_parameters`), which is fully used by every circuit and fully bound by every set of bindings.

Just prior to the comparison, `unique_parameters` should hold the single instance of `theta` used to build the circuits, e.g. `{Parameter(theta)}`, but:
```
(Pdb) p unique_parameters
{Parameter(theta), Parameter(theta), Parameter(theta)}
(Pdb) p [id(p) for p in unique_parameters]
[5126415024, 5126414408, 5126158936]
```

As a possible workaround, each circuit could be transpiled one-by-one, and then assembled as a group and executed. Alternately, `phi` could be made a parameter to the `u3` gate, and then bound along with `theta`.

Resolving patch:
diff --git a/qiskit/circuit/parameter.py b/qiskit/circuit/parameter.py
--- a/qiskit/circuit/parameter.py
+++ b/qiskit/circuit/parameter.py
@@ -15,6 +15,8 @@
 Parameter Class for variable parameters.
 """
 
+from uuid import uuid4
+
 import sympy
 
 from .parameterexpression import ParameterExpression
@@ -22,6 +24,27 @@
 
 class Parameter(ParameterExpression):
     """Parameter Class for variable parameters"""
+
+    def __new__(cls, _, uuid=None):
+        # Parameter relies on self._uuid being set prior to other attributes
+        # (e.g. symbol_map) which may depend on self._uuid for Parameter's hash
+        # or __eq__ functions.
+
+        obj = object.__new__(cls)
+
+        if uuid is None:
+            obj._uuid = uuid4()
+        else:
+            obj._uuid = uuid
+
+        return obj
+
+    def __getnewargs__(self):
+        # Unpickling won't in general call __init__ but will always call
+        # __new__. Specify arguments to be passed to __new__ when unpickling.
+
+        return (self.name, self._uuid)
+
     def __init__(self, name):
         self._name = name
 
@@ -48,3 +71,9 @@ def __deepcopy__(self, memo=None):
 
     def __repr__(self):
         return '{}({})'.format(self.__class__.__name__, self.name)
+
+    def __eq__(self, other):
+        return isinstance(other, Parameter) and self._uuid == other._uuid
+
+    def __hash__(self):
+        return hash(self._uuid)


Test patch:
diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py
--- a/test/python/circuit/test_parameters.py
+++ b/test/python/circuit/test_parameters.py
@@ -14,6 +14,7 @@
 
 """Test circuits with variable parameters."""
 
+import pickle
 from operator import add, sub, mul, truediv
 
 import numpy
@@ -21,9 +22,10 @@
 from qiskit import BasicAer
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit.circuit import Gate, Parameter, ParameterVector, ParameterExpression
-from qiskit.compiler import transpile
-from qiskit.compiler import assemble
+from qiskit.compiler import assemble, transpile
+from qiskit.execute import execute
 from qiskit.test import QiskitTestCase
+from qiskit.tools import parallel_map
 from qiskit.exceptions import QiskitError
 
 
@@ -296,6 +298,84 @@ def test_instruction_ryrz_vector(self):
             for param in vec:
                 self.assertIn(param, qc_aer.parameters)
 
+    def test_parameter_equality_through_serialization(self):
+        """Verify parameters maintain their equality after serialization."""
+
+        # pylint: disable=invalid-name
+        x = Parameter('x')
+        x1 = Parameter('x')
+
+        x_p = pickle.loads(pickle.dumps(x))
+        x1_p = pickle.loads(pickle.dumps(x1))
+
+        self.assertEqual(x, x_p)
+        self.assertEqual(x1, x1_p)
+
+        self.assertNotEqual(x, x1_p)
+        self.assertNotEqual(x1, x_p)
+
+    def test_binding_parameterized_circuits_built_in_multiproc(self):
+        """Verify subcircuits built in a subprocess can still be bound."""
+        # ref: https://github.com/Qiskit/qiskit-terra/issues/2429
+
+        num_processes = 4
+
+        qr = QuantumRegister(3)
+        cr = ClassicalRegister(3)
+
+        circuit = QuantumCircuit(qr, cr)
+        parameters = [Parameter('x{}'.format(i))
+                      for i in range(num_processes)]
+
+        results = parallel_map(_construct_circuit,
+                               [(param) for param in parameters],
+                               task_args=(qr,),
+                               num_processes=num_processes)
+
+        for qc in results:
+            circuit += qc
+
+        parameter_values = [{x: 1 for x in parameters}]
+
+        qobj = assemble(circuit,
+                        backend=BasicAer.get_backend('qasm_simulator'),
+                        parameter_binds=parameter_values)
+
+        self.assertEqual(len(qobj.experiments), 1)
+        self.assertEqual(len(qobj.experiments[0].instructions), 4)
+        self.assertTrue(all(len(inst.params) == 1
+                            and isinstance(inst.params[0], ParameterExpression)
+                            and float(inst.params[0]) == 1
+                            for inst in qobj.experiments[0].instructions))
+
+    def test_transpiling_multiple_parameterized_circuits(self):
+        """Verify several parameterized circuits can be transpiled at once."""
+        # ref: https://github.com/Qiskit/qiskit-terra/issues/2864
+
+        qr = QuantumRegister(1)
+        qc1 = QuantumCircuit(qr)
+        qc2 = QuantumCircuit(qr)
+
+        theta = Parameter('theta')
+
+        qc1.u3(theta, 0, 0, qr[0])
+        qc2.u3(theta, 3.14, 0, qr[0])
+
+        circuits = [qc1, qc2]
+
+        job = execute(circuits,
+                      BasicAer.get_backend('unitary_simulator'),
+                      shots=512,
+                      parameter_binds=[{theta: 1}])
+
+        self.assertTrue(len(job.result().results), 2)
+
+
+def _construct_circuit(param, qr):
+    qc = QuantumCircuit(qr)
+    qc.ry(param, qr[0])
+    return qc
+
 
 class TestParameterExpressions(QiskitTestCase):
     """Test expressions of Parameters."""