I am having troubles with the creation of controlled custom gates in Qiskit.

I am using the qiskit.extensions.UnitaryGate object but the execution time to create the controlled version of the gate becomes extremely long as the number on controls increase.

The example code below shows the problem : it takes about 70 seconds just to create the gate

import time
import numpy as np
from qiskit.extensions import UnitaryGate

Dmatrix = np.array([[-1/3,  2/3,  0,  2/3],
                    [ 2/3, -1/3,  0,  2/3],
                    [   0,    0, -1,    0],
                    [ 2/3,  2/3,  0, -1/3]])
Dgate = UnitaryGate(Dmatrix)

st = time.time()
C6Dgate = Dgate.control(6) # Step that takes a long time
print(round(time.time() - st, 2)) # print ~70 secs

Why is it taking so long ?
Is this the right way to create controlled custom gate in Qiskit ?
Is there another qiskit tool that does a similar thing in less time ?


1 Answer 1


I took a quick look at this and ran your script under cprofile to see where it's spending most of the time. I then used snakeviz to visualize the profile:


Looking at this we can see it's spending most of the time computing the isometry decomposition of the matrix to create a definition of custom instruction which will be controlled. Then the second most amount of time is spent computing the operator of the inverse of isometry decomposed circuit to correct the global phase of the definition. There might be some tuning we can do here in the future to improve the performance of this code path. For example, there is a better decomposition method being added for > 2 qubit unitaries being added in the next release which I think will improve the runtime performance here. If you could open an issue here: https://github.com/Qiskit/qiskit-terra/issues/new/choose about this we can work together on improving the performance of this code path.

As for a better way to do this, I'm not sure if there is currently a better method to go from an arbitrary unitary matrix and creating a controlled version of that gate. I do think if you make a custom gate object that is defined via a circuit instead of a matrix that would be faster to build the controlled version of that gate. But, that's obviously not doing the same thing. However, since your unitary is only 2q you can use the built in 2q decomposition to do define a custom gate class and then control that:

from qiskit.circuit.gate import Gate
from qiskit.quantum_info.synthesis import two_qubit_cnot_decompose

decomp = two_qubit_cnot_decompose(Dmatrix)

class CustomDGate(Gate):
    def __init__(self, label=None):
        super().__init__("dgate", 2, [], label=label)

    def _define(self):
        self.definition = decomp.copy()

    def __array__(self, dtype=None):
        return np.asarray(DMatrix, dtype=dtype)

Dgate = CustomDGate()
C6Dgate = Dgate.control(6)

When I ran this code it created the controlled gate instance in < 1 second. But it's also worth checking that this is correct for your use case. I think it should be equivalent but it's definitely worth checking. This also doesn't work if your unitary is more than 2 qubits.

  • $\begingroup$ Thanks, I understand better now. Since I only have custom two-qubits gates, the method you suggest works for me. It reduces the time needed to build the gate, but the step of transpiling a circuit containing the created gate takes a lot of time (in statevector_simulator backend). Is there a way to improve this step as well ? $\endgroup$ Apr 21 at 12:45
  • 1
    $\begingroup$ Transpile locally took ~5 sec to run when targeting Aer's statevector simulator. Since there are no connectivity constraints on an ideal simulator most of the time is being spent trying to optimize runs of 1q gates on each qubit. Since the decomposition of C6Dgate is huge (with >5500 1q gates) this takes some time. But, since you're running an ideal simulation there is no value in this optimization so you can just do transpile(qc, aer_sim, optimization_level=0) to turn off all the optimization and it will just translate to the gates native to the sim which took ~0.5 seconds locally. $\endgroup$ Apr 21 at 13:17
  • 1
    $\begingroup$ If you're running on a real backend or a simulator backend that is emulating a real backend (with noise and connectivity constraints) you don't want to do this and likely will want to use optimization_level=3 instead. But this will take more time as the circuit is fairly large and will take time for the transpiler to processes it (the layout and routing will be slower than the optimization given the depth of the circuit). $\endgroup$ Apr 21 at 13:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.