diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/block_base_padder.py b/qiskit_ibm_provider/transpiler/passes/scheduling/block_base_padder.py index 833bb525..9456643b 100644 --- a/qiskit_ibm_provider/transpiler/passes/scheduling/block_base_padder.py +++ b/qiskit_ibm_provider/transpiler/passes/scheduling/block_base_padder.py @@ -363,6 +363,7 @@ def _visit_block( prev_block_duration = self._block_duration prev_block_idx = self._current_block_idx self._terminate_block(self._block_duration, self._current_block_idx) + new_block_dag.duration = prev_block_duration # Edge-case: Add a barrier if the final node is a fast-path if self._prev_node in self._fast_path_nodes: diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py index 56c4a339..1d1cbac0 100644 --- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py +++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py @@ -420,6 +420,10 @@ def _pad( ) return + if not self._skip_reset_qubits and qubit not in self._dirty_qubits: + # mark all qubits as dirty if skip_reset_qubits is False + self._dirty_qubits.update([qubit]) + if ( not isinstance(prev_node, DAGInNode) and self._skip_reset_qubits diff --git a/releasenotes/notes/dd-include-clean-qubits-a98d98e4849e2452.yaml b/releasenotes/notes/dd-include-clean-qubits-a98d98e4849e2452.yaml new file mode 100644 index 00000000..ca59e885 --- /dev/null +++ b/releasenotes/notes/dd-include-clean-qubits-a98d98e4849e2452.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Modify ``skip_reset_qubits`` optional flag to the constructor for :class:`.PadDynamicalDecoupling`. If ``False``, dynamical decoupling is applied on qubits regardless of their state, even on delays that are at the beginning of a circuit. This option now matches the behavior in qiskit. diff --git a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py index df53f4cc..02c46ea6 100644 --- a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py +++ b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py @@ -185,8 +185,17 @@ def test_insert_dd_ghz_everywhere(self): expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(50), [1]) - expected = expected.compose(Delay(750), [2], front=True) - expected = expected.compose(Delay(950), [3], front=True) + expected = expected.compose(Delay(162), [2], front=True) + expected = expected.compose(YGate(), [2], front=True) + expected = expected.compose(Delay(326), [2], front=True) + expected = expected.compose(YGate(), [2], front=True) + expected = expected.compose(Delay(162), [2], front=True) + + expected = expected.compose(Delay(212), [3], front=True) + expected = expected.compose(YGate(), [3], front=True) + expected = expected.compose(Delay(426), [3], front=True) + expected = expected.compose(YGate(), [3], front=True) + expected = expected.compose(Delay(212), [3], front=True) self.assertEqual(ghz4_dd, expected) @@ -1095,3 +1104,91 @@ def test_no_unused_qubits(self): dont_use = qc_dd.qubits[-2:] for op in qc_dd.data: self.assertNotIn(dont_use, op.qubits) + + def test_dd_include_clean_qubits(self): + """Test DD applied to all non-idle qubits, including those + that are clean""" + + qc = QuantumCircuit(3, 1) + qc.delay(800, 1) + with qc.if_test((0, True)): + qc.x(1) + qc.delay(1000, 2) + + dd_sequence = [XGate(), XGate()] + pm = PassManager( + [ + ASAPScheduleAnalysis(self.durations), + PadDynamicalDecoupling( + self.durations, + dd_sequence, + pulse_alignment=1, + sequence_min_length_ratios=[0.0], + skip_reset_qubits=False, + ), + ] + ) + + qc_dd = pm.run(qc) + + expected = qc.copy_empty_like() + for ind in range(1, 3): + expected.compose(Delay(175), [ind], front=True, inplace=True) + expected.compose(XGate(), [ind], inplace=True) + expected.compose(Delay(350), [ind], inplace=True) + expected.compose(XGate(), [ind], inplace=True) + expected.compose(Delay(175), [ind], inplace=True) + expected.barrier([1, 2]) + with expected.if_test((0, True)): + expected.x(1) + expected.delay(50, 2) + expected.barrier([1, 2]) + for ind in range(1, 3): + expected.compose(Delay(225), [ind], inplace=True) + expected.compose(XGate(), [ind], inplace=True) + expected.compose(Delay(450), [ind], inplace=True) + expected.compose(XGate(), [ind], inplace=True) + expected.compose(Delay(225), [ind], inplace=True) + + self.assertEqual(qc_dd, expected) + + def test_dd_exclude_clean_qubits(self): + """Exclude DD on clean qubits (default)""" + + qc = QuantumCircuit(3, 1) + qc.delay(800, 1) + with qc.if_test((0, True)): + qc.x(1) + qc.delay(1000, 2) + + dd_sequence = [XGate(), XGate()] + pm = PassManager( + [ + ASAPScheduleAnalysis(self.durations), + PadDynamicalDecoupling( + self.durations, + dd_sequence, + pulse_alignment=1, + sequence_min_length_ratios=[0.0], + ), + ] + ) + + qc_dd = pm.run(qc) + + expected = qc.copy_empty_like() + expected.compose(Delay(800), [1], front=True, inplace=True) + expected.compose(Delay(800), [2], inplace=True) + expected.barrier([1, 2]) + with expected.if_test((0, True)): + expected.x(1) + expected.delay(50, 2) + expected.barrier([1, 2]) + expected.compose(Delay(225), [1], inplace=True) + expected.compose(XGate(), [1], inplace=True) + expected.compose(Delay(450), [1], inplace=True) + expected.compose(XGate(), [1], inplace=True) + expected.compose(Delay(225), [1], inplace=True) + expected.compose(Delay(1000), [2], inplace=True) + + self.assertEqual(qc_dd, expected)