Repository: Qiskit/qiskit

Issue:
bug in scheduling: gate lengths are wrongly attached
```py
from qiskit.circuit.library import HiddenLinearFunction
from qiskit.test.mock import FakeMontreal
from qiskit import transpile
circ = HiddenLinearFunction([[1, 1, 0], [1, 0, 1], [0, 1, 1]])
circ.draw('mpl')
sc = transpile(circ, FakeMontreal(), scheduling_method='asap')
```

here if you look inside sc.data you'll see that the duration of CX_{0,1} is set to the duration of CX_{1,2}=2560 dt, but it should be 1728 dt as reported by backend. Seems like the second CX over-rides the first CX somewhere.


```py
print(FakeMontreal().properties().gate_length('cx', (0, 1)) / FakeMontreal().configuration().dt) # 1728
print(FakeMontreal().properties().gate_length('cx', (1, 2)) / FakeMontreal().configuration().dt) # 2560
```

This bug causes the following obviously wrong timeline:
![image](https://user-images.githubusercontent.com/8622381/106604368-8ab54000-652d-11eb-9dc2-e51875dc5cac.png)



Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/scheduling/alap.py b/qiskit/transpiler/passes/scheduling/alap.py
--- a/qiskit/transpiler/passes/scheduling/alap.py
+++ b/qiskit/transpiler/passes/scheduling/alap.py
@@ -70,12 +70,14 @@ def pad_with_delays(qubits: List[int], until, unit) -> None:
             start_time = max(qubit_time_available[q] for q in node.qargs)
             pad_with_delays(node.qargs, until=start_time, unit=time_unit)
 
-            new_node = new_dag.apply_operation_front(node.op, node.qargs, node.cargs,
-                                                     node.condition)
             duration = self.durations.get(node.op, node.qargs, unit=time_unit)
+
             # set duration for each instruction (tricky but necessary)
-            new_node.op.duration = duration
-            new_node.op.unit = time_unit
+            new_op = node.op.copy()  # need different op instance to store duration
+            new_op.duration = duration
+            new_op.unit = time_unit
+
+            new_dag.apply_operation_front(new_op, node.qargs, node.cargs, node.condition)
 
             stop_time = start_time + duration
             # update time table
diff --git a/qiskit/transpiler/passes/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/asap.py
--- a/qiskit/transpiler/passes/scheduling/asap.py
+++ b/qiskit/transpiler/passes/scheduling/asap.py
@@ -70,11 +70,14 @@ def pad_with_delays(qubits: List[int], until, unit) -> None:
             start_time = max(qubit_time_available[q] for q in node.qargs)
             pad_with_delays(node.qargs, until=start_time, unit=time_unit)
 
-            new_node = new_dag.apply_operation_back(node.op, node.qargs, node.cargs, node.condition)
             duration = self.durations.get(node.op, node.qargs, unit=time_unit)
+
             # set duration for each instruction (tricky but necessary)
-            new_node.op.duration = duration
-            new_node.op.unit = time_unit
+            new_op = node.op.copy()  # need different op instance to store duration
+            new_op.duration = duration
+            new_op.unit = time_unit
+
+            new_dag.apply_operation_back(new_op, node.qargs, node.cargs, node.condition)
 
             stop_time = start_time + duration
             # update time table


Test patch:
diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py
--- a/test/python/circuit/test_scheduled_circuit.py
+++ b/test/python/circuit/test_scheduled_circuit.py
@@ -13,7 +13,7 @@
 # pylint: disable=missing-function-docstring
 
 """Test scheduled circuit (quantum circuit with duration)."""
-
+from ddt import ddt, data
 from qiskit import QuantumCircuit, QiskitError
 from qiskit import transpile, assemble
 from qiskit.test.mock.backends import FakeParis
@@ -23,6 +23,7 @@
 from qiskit.test.base import QiskitTestCase
 
 
+@ddt
 class TestScheduledCircuit(QiskitTestCase):
     """Test scheduled circuit (quantum circuit with duration)."""
     def setUp(self):
@@ -290,3 +291,17 @@ def test_change_dt_in_transpile(self):
                               dt=self.dt/2
                               )
         self.assertEqual(scheduled.duration, org_duration*2)
+
+    @data('asap', 'alap')
+    def test_duration_on_same_instruction_instance(self, scheduling_method):
+        """See: https://github.com/Qiskit/qiskit-terra/issues/5771"""
+        assert(self.backend_with_dt.properties().gate_length('cx', (0, 1))
+               != self.backend_with_dt.properties().gate_length('cx', (1, 2)))
+        qc = QuantumCircuit(3)
+        qc.cz(0, 1)
+        qc.cz(1, 2)
+        sc = transpile(qc,
+                       backend=self.backend_with_dt,
+                       scheduling_method=scheduling_method)
+        cxs = [inst for inst, _, _ in sc.data if inst.name == 'cx']
+        self.assertNotEqual(cxs[0].duration, cxs[1].duration)