From ae94cbb832fe8024e0e09a65038bdca0c3ec340f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Fri, 31 Mar 2023 14:27:30 -0400
Subject: [PATCH 1/9] First draft of staggered DD

---
 .../passes/scheduling/dynamical_decoupling.py | 83 +++++++++++++++++--
 1 file changed, 76 insertions(+), 7 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index 8ad77924c..8aaf7e67b 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -16,6 +16,7 @@
 from typing import Dict, List, Optional, Union
 
 import numpy as np
+import rustworkx as rx
 from qiskit.circuit import Qubit, Gate
 from qiskit.circuit.delay import Delay
 from qiskit.circuit.library.standard_gates import IGate, UGate, U3Gate
@@ -26,6 +27,7 @@
 from qiskit.transpiler.exceptions import TranspilerError
 from qiskit.transpiler.instruction_durations import InstructionDurations
 from qiskit.transpiler.passes.optimization import Optimize1qGates
+from qiskit.transpiler import CouplingMap
 
 from .block_base_padder import BlockBasePadder
 
@@ -117,6 +119,8 @@ def __init__(
         extra_slack_distribution: str = "middle",
         sequence_min_length_ratios: Optional[Union[int, List[int]]] = None,
         insert_multiple_cycles: bool = False,
+        coupling_map: CouplingMap = None,
+        alt_spacings: Optional[Union[List[List[float]], List[float]]] = None,
     ):
         """Dynamical decoupling initializer.
 
@@ -133,7 +137,8 @@ def __init__(
                 The available slack will be divided according to this.
                 The list length must be one more than the length of dd_sequence,
                 and the elements must sum to 1. If None, a balanced spacing
-                will be used [d/2, d, d, ..., d, d, d/2].
+                will be used [d/2, d, d, ..., d, d, d/2]. This spacing only applies to the first subcircuit, if a
+                ``coupling_map`` is specified
             skip_reset_qubits: If True, does not insert DD on idle periods that
                 immediately follow initialized/reset qubits
                 (as qubits in the ground state are less susceptible to decoherence).
@@ -161,6 +166,11 @@ def __init__(
             insert_multiple_cycles: If the available duration exceeds
                 2*sequence_min_length_ratio*duration(dd_sequence) enable the insertion of multiple
                 rounds of the dynamical decoupling sequence in that delay.
+            coupling_map: coupling map for the device. Specifying a coupling map partitions the device into two subcircuits,
+                in order to stagger the inter-pulse delays between the two.
+            alt_spacings: A list of lists of spacings between the DD gates, for the second subcircuit, as
+                determined by the coupling map. If None, a balanced spacing that is staggered with respect
+                to the first subcircuit will be used [d, d, d, ..., d, d, 0].
         Raises:
             TranspilerError: When invalid DD sequence is specified.
             TranspilerError: When pulse gate with the duration which is
@@ -169,6 +179,7 @@ def __init__(
 
         super().__init__()
         self._durations = durations
+
         # Enforce list of DD sequences
         if dd_sequences:
             try:
@@ -179,19 +190,35 @@ def __init__(
         self._qubits = qubits
         self._skip_reset_qubits = skip_reset_qubits
         self._alignment = pulse_alignment
-
+        self._coupling_map = coupling_map
         if spacings is not None:
             try:
                 iter(spacings[0])  # type: ignore
             except TypeError:
                 spacings = [spacings]  # type: ignore
+        if alt_spacings is not None:
+            try:
+                iter(alt_spacings[0])  # type: ignore
+            except TypeError:
+                alt_spacings = [alt_spacings]  # type: ignore
         self._spacings = spacings
+        self._alt_spacings = alt_spacings
 
         if self._spacings and len(self._spacings) != len(self._dd_sequences):
             raise TranspilerError(
                 "Number of sequence spacings must equal number of DD sequences."
             )
 
+        if self._alt_spacings:
+            if not self._coupling_map:
+                warnings.warn(
+                    "Alternate spacings are ignored because a coupling map was not provided"
+                )
+            elif len(self._alt_spacings) != len(self._dd_sequences):
+                raise TranspilerError(
+                    "Number of alternate sequence spacings must equal number of DD sequences."
+                )
+
         self._extra_slack_distribution = extra_slack_distribution
 
         self._dd_sequence_lengths: Dict[Qubit, List[List[Gate]]] = {}
@@ -217,9 +244,26 @@ def __init__(
     def _pre_runhook(self, dag: DAGCircuit) -> None:
         super()._pre_runhook(dag)
 
+        if self._coupling_map:
+            physical_qubits = [dag.qubits.index(q) for q in dag.qubits]
+            sub_coupling_map = self._coupling_map.reduce(physical_qubits)
+            self._coupling_coloring = rx.graph_greedy_color(
+                sub_coupling_map.graph.to_undirected()
+            )
+            if any(c > 1 for c in self._coupling_coloring.values()):
+                raise TranspilerError(
+                    "This circuit topology not supported for staggered dynamical decoupling. Maximum connectivity is 3 nearest neighbors per qubit."
+                )
+
         spacings_required = self._spacings is None
         if spacings_required:
             self._spacings = []  # type: ignore
+        alt_spacings_required = (
+            self._alt_spacings is None and self._coupling_map is not None
+        )
+        print("alt required", alt_spacings_required)
+        if alt_spacings_required:
+            self._alt_spacings = []
 
         for seq_idx, seq in enumerate(self._dd_sequences):
             num_pulses = len(self._dd_sequences[seq_idx])
@@ -242,6 +286,19 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
                         "of the slack period and sum to 1."
                     )
 
+            if self._coupling_map:
+                if alt_spacings_required:  # TODO: merge final pulse if delay is 0
+                    mid = 1 / num_pulses
+                    self._alt_spacings.append([mid] * num_pulses + [0])  # type: ignore
+                else:
+                    if sum(self._alt_spacings[seq_idx]) != 1 or any(  # type: ignore
+                        a < 0 for a in self._alt_spacings[seq_idx]  
+                    ):
+                        raise TranspilerError(
+                            "The spacings must be given in terms of fractions "
+                            "of the slack period and sum to 1."
+                        )
+
             # Check if DD sequence is identity
             if num_pulses != 1:
                 if num_pulses % 2 != 0:
@@ -362,7 +419,12 @@ def _pad(
             seq_lengths = self._dd_sequence_lengths[qubit][sequence_idx]
             seq_length = np.sum(seq_lengths)
             seq_ratio = self._sequence_min_length_ratios[sequence_idx]
-            spacings = self._spacings[sequence_idx]
+            spacings = np.asarray(self._spacings[sequence_idx])
+            alt_spacings = (
+                np.asarray(self._alt_spacings[sequence_idx])
+                if self._coupling_map
+                else None
+            )
 
             # Verify the delay duration exceeds the minimum time to insert
             if time_interval / seq_length <= seq_ratio:
@@ -383,18 +445,17 @@ def _pad(
             # multiple dd sequences may be inserted
             if num_sequences > 1:
                 dd_sequence = list(dd_sequence) * num_sequences
-                spacings = spacings * num_sequences
                 seq_lengths = seq_lengths * num_sequences
                 seq_length = np.sum(seq_lengths)
 
-            spacings = np.asarray(spacings) / num_sequences
+            spacings = np.asarray(spacings)
             slack = time_interval - seq_length
             sequence_gphase = self._sequence_phase
 
             if slack <= 0:
                 continue
 
-            if len(dd_sequence) == 1:
+            if len(dd_sequence) == 1:  # TODO: include the case where last delay is 0
                 # Special case of using a single gate for DD
                 u_inv = dd_sequence[0].inverse().to_matrix()
                 theta, phi, lam, phase = OneQubitEulerDecomposer().angles_and_phase(
@@ -431,8 +492,16 @@ def _pad(
             def _constrained_length(values: np.array) -> np.array:
                 return self._alignment * np.floor(values / self._alignment)
 
+            if self._coupling_map:
+                if self._coupling_coloring[qubit.index] == 0:
+                    sub_spacings = spacings
+                else:
+                    sub_spacings = alt_spacings
+            else:
+                sub_spacings = spacings
+
             # (1) Compute DD intervals satisfying the constraint
-            taus = _constrained_length(slack * spacings)
+            taus = _constrained_length(slack * sub_spacings)
             extra_slack = slack - np.sum(taus)
             # (2) Distribute extra slack
             if self._extra_slack_distribution == "middle":

From 0b7d30bd4d2060f121fdaeacf046b3960fa3ad08 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Fri, 31 Mar 2023 15:34:36 -0400
Subject: [PATCH 2/9] Fix docstring

---
 .../transpiler/passes/scheduling/dynamical_decoupling.py   | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index 8aaf7e67b..c89de0b9e 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -166,8 +166,8 @@ def __init__(
             insert_multiple_cycles: If the available duration exceeds
                 2*sequence_min_length_ratio*duration(dd_sequence) enable the insertion of multiple
                 rounds of the dynamical decoupling sequence in that delay.
-            coupling_map: coupling map for the device. Specifying a coupling map partitions the device into two subcircuits,
-                in order to stagger the inter-pulse delays between the two.
+            coupling_map: directed graph representing the coupling map for the device. Specifying a coupling map partitions the device into subcircuits,
+                in order to apply DD sequences with different pulse spacings within each. Currently support 2 subcircuits.
             alt_spacings: A list of lists of spacings between the DD gates, for the second subcircuit, as
                 determined by the coupling map. If None, a balanced spacing that is staggered with respect
                 to the first subcircuit will be used [d, d, d, ..., d, d, 0].
@@ -175,6 +175,8 @@ def __init__(
             TranspilerError: When invalid DD sequence is specified.
             TranspilerError: When pulse gate with the duration which is
                 non-multiple of the alignment constraint value is found.
+            TranspilerError: When alternate spacings are specified without a coupling map.
+            TranspilerError: When the coupling map is not supported (i.e., if degree > 3)
         """
 
         super().__init__()
@@ -261,7 +263,6 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
         alt_spacings_required = (
             self._alt_spacings is None and self._coupling_map is not None
         )
-        print("alt required", alt_spacings_required)
         if alt_spacings_required:
             self._alt_spacings = []
 

From 8b35c55941c96678a9cd5f11abc25b6754d4497c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Mon, 3 Apr 2023 13:02:50 -0400
Subject: [PATCH 3/9] First unit test for staggered DD

---
 .../scheduling/test_dynamical_decoupling.py   | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
index f0701fc4d..4698baa4a 100644
--- a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
+++ b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
@@ -22,6 +22,7 @@
 from qiskit.quantum_info import Operator
 from qiskit.transpiler.passmanager import PassManager
 from qiskit.transpiler.exceptions import TranspilerError
+from qiskit.transpiler.coupling import CouplingMap
 
 from qiskit_ibm_provider.transpiler.passes.scheduling.dynamical_decoupling import (
     PadDynamicalDecoupling,
@@ -75,6 +76,8 @@ def setUp(self):
             ]
         )
 
+        self.coupling_map = CouplingMap([[0, 1], [1, 2], [2, 3]])
+
     def test_insert_dd_ghz(self):
         """Test DD gates are inserted in correct spots."""
         dd_sequence = [XGate(), XGate()]
@@ -853,3 +856,63 @@ def test_multiple_dd_sequence_cycles(self):
         expected.x(0)
         expected.delay(225, 0)
         self.assertEqual(qc_dd, expected)
+
+    def test_staggered_dd(self):
+        """Test that timing on DD can be staggered if coupled with each other"""
+        dd_sequence = [XGate(), XGate()]
+        pm = PassManager(
+            [
+                ASAPScheduleAnalysis(self.durations),
+                PadDynamicalDecoupling(
+                    self.durations,
+                    dd_sequence,
+                    coupling_map=self.coupling_map,
+                    alt_spacings=[0.1, 0.8, 0.1],
+                ),
+            ]
+        )
+
+        qc_barriers = QuantumCircuit(4, 1)
+        qc_barriers.x(0)
+        qc_barriers.x(1)
+        qc_barriers.x(2)
+        qc_barriers.x(3)
+        qc_barriers.barrier()
+        qc_barriers.measure(0, 0)
+        qc_barriers.delay(14, 0)
+        qc_barriers.x(1)
+        qc_barriers.x(2)
+        qc_barriers.x(3)
+        qc_barriers.barrier()
+
+        qc_dd = pm.run(qc_barriers)
+
+        expected = QuantumCircuit(4, 1)
+        expected.x(0)
+        expected.x(1)
+        expected.x(2)
+        expected.x(3)
+        expected.barrier()
+        expected.x(1)
+        expected.delay(208, 1)
+        expected.x(1)
+        expected.delay(448, 1)
+        expected.x(1)
+        expected.delay(208, 1)
+        expected.x(2)
+        expected.delay(80, 2)  # q1-q2 are coupled, staggered delays
+        expected.x(2)
+        expected.delay(704, 2)
+        expected.x(2)
+        expected.delay(80, 2)  # q2-q3 are uncoupled, same delays
+        expected.x(3)
+        expected.delay(208, 3)
+        expected.x(3)
+        expected.delay(448, 3)
+        expected.x(3)
+        expected.delay(208, 3)
+        expected.measure(0, 0)
+        expected.delay(14, 0)
+        expected.barrier()
+
+        self.assertEqual(qc_dd, expected)

From 91497d726432ad3fcf127dcdaa16c5ec988b195f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Mon, 3 Apr 2023 15:58:51 -0400
Subject: [PATCH 4/9] Testing bad cases

---
 .../passes/scheduling/dynamical_decoupling.py |  7 ++-
 .../scheduling/test_dynamical_decoupling.py   | 50 +++++++++++++++++++
 2 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index c89de0b9e..a5994b082 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -175,7 +175,6 @@ def __init__(
             TranspilerError: When invalid DD sequence is specified.
             TranspilerError: When pulse gate with the duration which is
                 non-multiple of the alignment constraint value is found.
-            TranspilerError: When alternate spacings are specified without a coupling map.
             TranspilerError: When the coupling map is not supported (i.e., if degree > 3)
         """
 
@@ -288,12 +287,12 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
                     )
 
             if self._coupling_map:
-                if alt_spacings_required:  # TODO: merge final pulse if delay is 0
+                if alt_spacings_required:
                     mid = 1 / num_pulses
                     self._alt_spacings.append([mid] * num_pulses + [0])  # type: ignore
                 else:
                     if sum(self._alt_spacings[seq_idx]) != 1 or any(  # type: ignore
-                        a < 0 for a in self._alt_spacings[seq_idx]  
+                        a < 0 for a in self._alt_spacings[seq_idx]
                     ):
                         raise TranspilerError(
                             "The spacings must be given in terms of fractions "
@@ -456,7 +455,7 @@ def _pad(
             if slack <= 0:
                 continue
 
-            if len(dd_sequence) == 1:  # TODO: include the case where last delay is 0
+            if len(dd_sequence) == 1:
                 # Special case of using a single gate for DD
                 u_inv = dd_sequence[0].inverse().to_matrix()
                 theta, phi, lam, phase = OneQubitEulerDecomposer().angles_and_phase(
diff --git a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
index 4698baa4a..5d298fda3 100644
--- a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
+++ b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
@@ -916,3 +916,53 @@ def test_staggered_dd(self):
         expected.barrier()
 
         self.assertEqual(qc_dd, expected)
+
+    def test_insert_dd_bad_spacings(self):
+        """Test DD raises when spacings don't add up to 1."""
+        dd_sequence = [XGate(), XGate()]
+        pm = PassManager(
+            [
+                ASAPScheduleAnalysis(self.durations),
+                PadDynamicalDecoupling(
+                    self.durations,
+                    dd_sequence,
+                    spacings=[0.1, 0.9, 0.1],
+                    coupling_map=self.coupling_map,
+                ),
+            ]
+        )
+
+        with self.assertRaises(TranspilerError):
+            pm.run(self.ghz4)
+
+    def test_insert_dd_bad_alt_spacings(self):
+        """Test DD raises when alt_spacings don't add up to 1."""
+        dd_sequence = [XGate(), XGate()]
+        pm = PassManager(
+            [
+                ASAPScheduleAnalysis(self.durations),
+                PadDynamicalDecoupling(
+                    self.durations,
+                    dd_sequence,
+                    alt_spacings=[0.1, 0.9, 0.1],
+                    coupling_map=self.coupling_map,
+                ),
+            ]
+        )
+
+    def test_unsupported_coupling_map(self):
+        """Test DD raises if coupling map is not supported."""
+        dd_sequence = [XGate(), XGate()]
+        pm = PassManager(
+            [
+                ASAPScheduleAnalysis(self.durations),
+                PadDynamicalDecoupling(
+                    self.durations,
+                    dd_sequence,
+                    coupling_map=CouplingMap([[0, 1], [0, 2], [1, 2], [2, 3]]),
+                ),
+            ]
+        )
+
+        with self.assertRaises(TranspilerError):
+            pm.run(self.ghz4)

From a1615b22ffedd80a378f8d3233e4d12f683cb922 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Mon, 3 Apr 2023 16:11:24 -0400
Subject: [PATCH 5/9] Release note

---
 releasenotes/notes/staggered-dd-95ebe09ebc46f60f.yaml | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 releasenotes/notes/staggered-dd-95ebe09ebc46f60f.yaml

diff --git a/releasenotes/notes/staggered-dd-95ebe09ebc46f60f.yaml b/releasenotes/notes/staggered-dd-95ebe09ebc46f60f.yaml
new file mode 100644
index 000000000..8d17144b1
--- /dev/null
+++ b/releasenotes/notes/staggered-dd-95ebe09ebc46f60f.yaml
@@ -0,0 +1,9 @@
+---
+upgrade:
+  - |
+    For each qubit, add the ability to select one of two different sets of spacings between dynamical decoupling pulses, 
+    based on the input coupling map. 
+fixes:
+  - |
+    Staggered inter-pulse spacings allow the user to disable certain interactions between neighboring qubits by
+    Refer to `#539 <https://github.com/Qiskit/qiskit-ibm-provider/issues/539>` for more details
\ No newline at end of file

From 047d0a216faeb8760aab010b12f6c6ccc735c760 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Tue, 4 Apr 2023 13:12:16 -0400
Subject: [PATCH 6/9] Bypass some type checking

---
 .../transpiler/passes/scheduling/dynamical_decoupling.py      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index a5994b082..770123ddc 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -263,7 +263,7 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
             self._alt_spacings is None and self._coupling_map is not None
         )
         if alt_spacings_required:
-            self._alt_spacings = []
+            self._alt_spacings = []  # type: ignore
 
         for seq_idx, seq in enumerate(self._dd_sequences):
             num_pulses = len(self._dd_sequences[seq_idx])
@@ -292,7 +292,7 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
                     self._alt_spacings.append([mid] * num_pulses + [0])  # type: ignore
                 else:
                     if sum(self._alt_spacings[seq_idx]) != 1 or any(  # type: ignore
-                        a < 0 for a in self._alt_spacings[seq_idx]
+                        a < 0 for a in self._alt_spacings[seq_idx]  # type: ignore
                     ):
                         raise TranspilerError(
                             "The spacings must be given in terms of fractions "

From 6826fe34b8e8a97a310e2b5e8fe1cde72e3ac646 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Tue, 4 Apr 2023 14:06:31 -0400
Subject: [PATCH 7/9] Missing assert

---
 .../transpiler/passes/scheduling/dynamical_decoupling.py       | 2 ++
 .../transpiler/passes/scheduling/test_dynamical_decoupling.py  | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index 770123ddc..15e0bc377 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -192,6 +192,8 @@ def __init__(
         self._skip_reset_qubits = skip_reset_qubits
         self._alignment = pulse_alignment
         self._coupling_map = coupling_map
+        self._coupling_coloring = None
+
         if spacings is not None:
             try:
                 iter(spacings[0])  # type: ignore
diff --git a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
index 5d298fda3..3eae3feba 100644
--- a/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
+++ b/test/unit/transpiler/passes/scheduling/test_dynamical_decoupling.py
@@ -950,6 +950,9 @@ def test_insert_dd_bad_alt_spacings(self):
             ]
         )
 
+        with self.assertRaises(TranspilerError):
+            pm.run(self.ghz4)
+
     def test_unsupported_coupling_map(self):
         """Test DD raises if coupling map is not supported."""
         dd_sequence = [XGate(), XGate()]

From e84090f5d68022187b6d1343e465e17c88d1da3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Tue, 4 Apr 2023 14:45:30 -0400
Subject: [PATCH 8/9] Revert list/array confusion

First list of spacings must be replicated for each sequence, then the spacings are rescaled as fraction of total delay
---
 .../transpiler/passes/scheduling/dynamical_decoupling.py     | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index 15e0bc377..ecb8d0211 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -421,7 +421,7 @@ def _pad(
             seq_lengths = self._dd_sequence_lengths[qubit][sequence_idx]
             seq_length = np.sum(seq_lengths)
             seq_ratio = self._sequence_min_length_ratios[sequence_idx]
-            spacings = np.asarray(self._spacings[sequence_idx])
+            spacings = self._spacings[sequence_idx]
             alt_spacings = (
                 np.asarray(self._alt_spacings[sequence_idx])
                 if self._coupling_map
@@ -449,8 +449,9 @@ def _pad(
                 dd_sequence = list(dd_sequence) * num_sequences
                 seq_lengths = seq_lengths * num_sequences
                 seq_length = np.sum(seq_lengths)
+                spacings = spacings * num_sequences
 
-            spacings = np.asarray(spacings)
+            spacings = np.asarray(spacings) / num_sequences
             slack = time_interval - seq_length
             sequence_gphase = self._sequence_phase
 

From e9d8bfde4e55eaa519fd6ad15d32fbcad994973c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Rist=C3=A8?=
 <10402430+dieris@users.noreply.github.com>
Date: Tue, 4 Apr 2023 15:37:44 -0400
Subject: [PATCH 9/9] Formatting

---
 .../passes/scheduling/dynamical_decoupling.py | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
index ecb8d0211..51ebc76fe 100644
--- a/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
+++ b/qiskit_ibm_provider/transpiler/passes/scheduling/dynamical_decoupling.py
@@ -137,8 +137,9 @@ def __init__(
                 The available slack will be divided according to this.
                 The list length must be one more than the length of dd_sequence,
                 and the elements must sum to 1. If None, a balanced spacing
-                will be used [d/2, d, d, ..., d, d, d/2]. This spacing only applies to the first subcircuit, if a
-                ``coupling_map`` is specified
+                will be used [d/2, d, d, ..., d, d, d/2]. This spacing only
+                applies to the first subcircuit, if a ``coupling_map`` is
+                specified
             skip_reset_qubits: If True, does not insert DD on idle periods that
                 immediately follow initialized/reset qubits
                 (as qubits in the ground state are less susceptible to decoherence).
@@ -166,11 +167,12 @@ def __init__(
             insert_multiple_cycles: If the available duration exceeds
                 2*sequence_min_length_ratio*duration(dd_sequence) enable the insertion of multiple
                 rounds of the dynamical decoupling sequence in that delay.
-            coupling_map: directed graph representing the coupling map for the device. Specifying a coupling map partitions the device into subcircuits,
-                in order to apply DD sequences with different pulse spacings within each. Currently support 2 subcircuits.
-            alt_spacings: A list of lists of spacings between the DD gates, for the second subcircuit, as
-                determined by the coupling map. If None, a balanced spacing that is staggered with respect
-                to the first subcircuit will be used [d, d, d, ..., d, d, 0].
+            coupling_map: directed graph representing the coupling map for the device. Specifying a
+                coupling map partitions the device into subcircuits, in order to apply DD sequences
+                with different pulse spacings within each. Currently support 2 subcircuits.
+            alt_spacings: A list of lists of spacings between the DD gates, for the second subcircuit,
+                as determined by the coupling map. If None, a balanced spacing that is staggered with
+                respect to the first subcircuit will be used [d, d, d, ..., d, d, 0].
         Raises:
             TranspilerError: When invalid DD sequence is specified.
             TranspilerError: When pulse gate with the duration which is
@@ -255,7 +257,8 @@ def _pre_runhook(self, dag: DAGCircuit) -> None:
             )
             if any(c > 1 for c in self._coupling_coloring.values()):
                 raise TranspilerError(
-                    "This circuit topology not supported for staggered dynamical decoupling. Maximum connectivity is 3 nearest neighbors per qubit."
+                    "This circuit topology is not supported for staggered dynamical decoupling."
+                    "The maximum connectivity is 3 nearest neighbors per qubit."
                 )
 
         spacings_required = self._spacings is None