Repository: Qiskit/qiskit

Issue:
bind_parameters() example from advanced circuits notebook doesn't work
<!-- ⚠️ 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.7
- **Operating system**: Linux

### What is the current behavior?

When running through the notebook, the cell demonstrating the `QuantumCircuit.bind_parameters()` method fails with this stacktrace:

```
---------------------------------------------------------------------------
QiskitError                               Traceback (most recent call last)
<ipython-input-15-d7ba5e55b137> in <module>
      4 
      5 circuits = [qc.bind_parameters({theta: theta_val})
----> 6             for theta_val in theta_range]
      7 
      8 print(circuits[-1].draw(line_length=120))

<ipython-input-15-d7ba5e55b137> in <listcomp>(.0)
      4 
      5 circuits = [qc.bind_parameters({theta: theta_val})
----> 6             for theta_val in theta_range]
      7 
      8 print(circuits[-1].draw(line_length=120))

~/git/qiskit/qiskit-core/qiskit/circuit/quantumcircuit.py in bind_parameters(self, value_dict)
    857 
    858         for parameter, value in unrolled_value_dict.items():
--> 859             new_circuit._bind_parameter(parameter, value)
    860         # clear evaluated expressions
    861         for parameter in unrolled_value_dict:

~/git/qiskit/qiskit-core/qiskit/circuit/quantumcircuit.py in _bind_parameter(self, parameter, value)
    879         """Assigns a parameter value to matching instructions in-place."""
    880         for (instr, param_index) in self._parameter_table[parameter]:
--> 881             instr.params[param_index] = instr.params[param_index].bind({parameter: value})
    882 
    883     def _substitute_parameters(self, parameter_map):

~/git/qiskit/qiskit-core/qiskit/circuit/parameterexpression.py in bind(self, parameter_values)
     66         """
     67 
---> 68         self._raise_if_passed_unknown_parameters(parameter_values.keys())
     69         self._raise_if_passed_non_real_value(parameter_values)
     70 

~/git/qiskit/qiskit-core/qiskit/circuit/parameterexpression.py in _raise_if_passed_unknown_parameters(self, parameters)
    134             raise QiskitError('Cannot bind Parameters ({}) not present in '
    135                               'expression.'.format(
--> 136                                   [str(p) for p in unknown_parameters]))
    137 
    138     def _raise_if_passed_non_real_value(self, parameter_values):

QiskitError: "Cannot bind Parameters (['θ']) not present in expression."
```

### Steps to reproduce the problem

Run through the advanced circuits notebook here: https://github.com/Qiskit/qiskit-tutorials/blob/master/qiskit/advanced/terra/advanced_circuits.ipynb

or just run this script:

```
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Gate
from qiskit.circuit import Parameter

theta = Parameter('θ')

n = 5

qc = QuantumCircuit(5, 1)

qc.h(0)
for i in range(n-1):
    qc.cx(i, i+1)

qc.barrier()
qc.rz(theta, range(5))
qc.barrier()

for i in reversed(range(n-1)):
    qc.cx(i, i+1)
qc.h(0)
qc.measure(0, 0)

qc.draw()

import numpy as np

theta_range = np.linspace(0, 2 * np.pi, 128)
circuits = [qc.bind_parameters({theta: theta_val})
            for theta_val in theta_range]

print(circuits[-1].draw(line_length=120))
print(circuits[-1].parameters)
```


### What is the expected behavior?
It doesn't stack trace and works as expected.


### Suggested solutions

Fix this bug. It looks like it was introduced in #2537 (reverting it fixes the failure)



Discussion:

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -355,8 +355,24 @@ def append(self, instruction, qargs=None, cargs=None):
         expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []]
 
         instructions = InstructionSet()
+
+        # When broadcasting was handled by decorators (prior to #2282), append
+        # received multiple distinct instruction instances, one for each expanded
+        # arg. With broadcasting as part of QuantumCircuit.append, the
+        # instruction instance is constructed before append is called. However,
+        # (at least) ParameterTable expects instruction instances to be unique
+        # within a circuit, so make instruction deepcopies for expanded_args[1:].
+
+        first_instruction = True
         for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
-            instructions.add(self._append(instruction, qarg, carg), qarg, carg)
+            if first_instruction:
+                instructions.add(
+                    self._append(instruction, qarg, carg), qarg, carg)
+                first_instruction = False
+            else:
+                instructions.add(
+                    self._append(deepcopy(instruction), qarg, carg), qarg, carg)
+
         return instructions
 
     def _append(self, instruction, qargs, cargs):


Test patch:
diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py
--- a/test/python/circuit/test_extensions_standard.py
+++ b/test/python/circuit/test_extensions_standard.py
@@ -942,6 +942,16 @@ def test_z_reg_inv(self):
         self.assertEqual(instruction_set.qargs[1], [self.qr[1]])
         self.assertEqual(instruction_set.instructions[2].params, [])
 
+    def test_broadcast_does_not_duplicate_instructions(self):
+        self.circuit.rz(0, range(6))
+        instructions = [inst for inst, qargs, cargs in self.circuit.data]
+        self.assertEqual(len(set(id(inst) for inst in instructions)), 6)
+
+        self.circuit.data = []
+        self.circuit.rz(0, self.qr)
+        instructions = [inst for inst, qargs, cargs in self.circuit.data]
+        self.assertEqual(len(set(id(inst) for inst in instructions)), 3)
+
 
 class TestStandard2Q(QiskitTestCase):
     """Standard Extension Test. Gates with two Qubits"""
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
@@ -587,3 +587,39 @@ def test_to_instruction_expression_parameter_map(self):
         self.assertEqual(float(bound_qc.data[1][0].params[0]), 2)
         self.assertEqual(float(bound_qc.data[2][0].params[0]), numpy.pi/2)
         self.assertEqual(float(bound_qc.data[3][0].params[0]), 2 * 3)
+
+    def test_binding_across_broadcast_instruction(self):
+        """Bind a parameter which was included via a broadcast instruction."""
+        # ref: https://github.com/Qiskit/qiskit-terra/issues/3008
+
+        from qiskit.extensions.standard import RZGate
+        theta = Parameter('θ')
+        n = 5
+
+        qc = QuantumCircuit(n, 1)
+
+        qc.h(0)
+        for i in range(n-1):
+            qc.cx(i, i+1)
+
+        qc.barrier()
+        qc.rz(theta, range(n))
+        qc.barrier()
+
+        for i in reversed(range(n-1)):
+            qc.cx(i, i+1)
+        qc.h(0)
+        qc.measure(0, 0)
+
+        theta_range = numpy.linspace(0, 2 * numpy.pi, 128)
+        circuits = [qc.bind_parameters({theta: theta_val})
+                    for theta_val in theta_range]
+
+        self.assertEqual(len(circuits), len(theta_range))
+        for theta_val, bound_circ in zip(theta_range, circuits):
+            rz_gates = [inst for inst, qargs, cargs in bound_circ.data
+                        if isinstance(inst, RZGate)]
+
+            self.assertEqual(len(rz_gates), n)
+            self.assertTrue(all(float(gate.params[0]) == theta_val
+                                for gate in rz_gates))