3
$\begingroup$

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 ?

$\endgroup$

2 Answers 2

6
$\begingroup$

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:

profile_unitary_control

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.

$\endgroup$
3
  • $\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, 2022 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, 2022 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, 2022 at 13:19
1
$\begingroup$

I have the same problem although I just use a one-qubit unitary!

I attempted to implement a multiple qubit unitary myself with Gray codes and two-level matrices(acting on one qubit) as explained in the Nielsen/Chuang book. I thought this would speed up the runtime compared to handing an 8 or more qubit unitary since in my case I already have the two-level matrices at hand. I was wrong there.

How are mulit qubit unitaries decomposed in quiskit?

This is strange to me. I am looking ahead for implementing 40 or more qubit-unitaries and I wanted to do this with a series of one qubit unitaries and Grey codes, since those one qubit unitaries are easy to compute in the case I am working on. This should normally be faster than decomposing a 40 qubit-unitary...

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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