# How to formulate Dynamical Decoupling passes in Qiskit to improve result upon circuit execution

First, let me say that I am not familiar with the idea of Dynamical Decoupling. The goal of this question is to understand how to set up a circuit with dynamical decoupling to improve my hardware result.

Due to the interaction with the environment, the dynamics of an open quantum system is not unitary. Thus, dynamical decoupling is about an open loop control technique for cancellation of the environment noise.

In the Dynamical coupling technique, a series if strong fast pulses are applied on the system which on average neutralizes the system-bath coupling, decouple system from bath (environment).

For instance, given the original circuit as:

This can be done as shown in this Qiskit's documentation:

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import XGate
from qiskit.transpiler import PassManager, InstructionDurations
from qiskit.transpiler.passes import ALAPSchedule, DynamicalDecoupling
from qiskit.visualization import timeline_drawer
circ = QuantumCircuit(4)
circ.h(0)
circ.cx(0, 1)
circ.cx(1, 2)
circ.cx(2, 3)
circ.measure_all()
durations = InstructionDurations(
[("h", 0, 50), ("cx", [0, 1], 700), ("reset", None, 10),
("cx", [1, 2], 200), ("cx", [2, 3], 300),
("x", None, 50), ("measure", None, 1000)]
)

# balanced X-X sequence on all qubits
dd_sequence = [XGate(), XGate()]
pm = PassManager([ALAPSchedule(durations),
DynamicalDecoupling(durations, dd_sequence)])
circ_dd = pm.run(circ)
timeline_drawer(circ_dd)


Now you can varies the durations parameters to change the length of the gate, and also you can change the number of $$X$$ gates in the sequence you can apply to each qubit in the circuit (the above circuit has the same number of $$X$$ gates on both qubits $$q_0$$ and $$q_1$$ but this doesn't have to be the case).

As you can see, with the example given above, the first $$CX$$ was set at $$700 \ dt$$ , the second $$CX$$ was set at $$200 \ dt$$, and then the last $$CX$$ was set at $$300 \ dt$$. How were these specific numbers picked?

I also want to mention something else. The Dynamical_Decoupling circuit (the bottom circuit) upon execution didn't give me an improved results!

I have tried different combinations as well, by setting longer or shorter $$dt$$ for different $$CX$$ gates, add more number of $$X$$ gates to the qubits that need to be idle longer, etc. But similarly, when I compare such circuit results to the original circuit (the circuit without performing Dynamical Decoupling) results, they were not better.

After discussing this with someone and going back through the API documentation on dynamical decoupling, it was clear on how these numbers were chosen. They corresponds to the gate duration time on the specific device in use. Which makes sense as why the specified durations in the questions are different.

I didn't read the documentation carefully to see that you actually don't have to specify these instruction durations like they did. This makes sense as these numbers will be device dependent and going through each device's properties to set up these durations would be a pain. The nice thing is you can use InstructionDurations method within Qiskit to do this for you. It extracts the hardware properties with your specify your hardware and automated this process.

For example: Taking the example in the question, instead of specify the durations as:

durations = InstructionDurations(
[("h", 0, 50), ("cx", [0, 1], 700), ("reset", None, 10),
("cx", [1, 2], 200), ("cx", [2, 3], 300),
("x", None, 50), ("measure", None, 1000)]
)


we can specify it as:

provider = IBMQ.load_account()
backend =  provider.get_backend('ibmq_bogota')
durations = InstructionDurations.from_backend(backend)


Note how we have to pass in a specific backend here.

One then can defined a transpiled circuit (convert the original circuit to build the GHZ state in the question using native gates in the device) and set up the dynamical decoupling circuit from this transpiled circuit using PassManager.

transpiled_circ = transpile(circ, provider.get_backend('ibmq_bogota') , initial_layout =None, optimization_level=3)
dd_sequence = [XGate(), XGate()]
pm = PassManager([ALAPSchedule(instruction_durations),
DynamicalDecoupling(instruction_durations, dd_sequence)])
circ_dd = pm.run(transpiled_circ)
timeline_drawer(circ_dd)


Here I used the same number of $$X$$ gates in both qubits to keep it the same as the example in the API documentation but we will change that later. But upon executing this circuit on the hardware for $$100,000$$ shots, I have the following result:

And if I execute the original circuit (also with $$100,000$$ shots) without dynamical decoupling then I have the following result:

From here, we see there is no improvement in the result. However, this doesn't mean that dynamical decoupling doesn't work. It is most likely has to do with the way I have implemented the dynamical decoupling scheme. What I mean by this is there are more ways we can specify the spacing and the number of $$X$$ gates to use depending on the idle time that each qubit has to go through.

So far, I have not been able to find the optimal strategy or a strategy at all to determine the number of $$X$$ gates to be applied and their spacing (how they should be separated from each other). But below are some results from different combinations of different number of $$X$$ gates with even spacing that I have tried. None seems to show any improvement. All experiments were done with $$100,000$$ shots on ibmq_bogota.

### 2. $$4$$$$X$$ gates on Qubit_0 and $$2$$$$X$$ gates on Qubit_1

### You can create something like this as
n1, n2 = 6, 2
dd_sequence1 = [XGate()] * n1
dd_sequence2 = [XGate()] * n2
pm = PassManager(
[
ALAPSchedule(instruction_durations),
DynamicalDecoupling(instruction_durations, dd_sequence1, qubits= [0], spacing=None),
DynamicalDecoupling(instruction_durations, dd_sequence2, qubits= [1], spacing=None),
]
)
circ_dd = pm.run(transpiled_circ)


### 4. $$6$$$$X$$ gates on Qubit_0 and $$4$$$$X$$ gates on Qubit_1

So the remaining question that have not been answered here is: How do we set up the optimal number of $$X$$ gates and the spacing so to see improvement on the hardware result?