3
$\begingroup$

I've been using a circuit that needs a specific initialization in the beginning, and to do so I've been using qc.initialize() as mentioned in previously asked similar questions (How can I create an arbitrary superposition state of two qubits in Qiskit?).

However, I've noticed that even when using qc.initialize() to initialize some very simple states (|000>, etc.) it takes roughtly 3-4 seconds to just execute the circuit (something that would only take ~0.2 sec if I just start the circuit at |000> without qc.initialize()). The time for me is important because I need to execute the circuit many times (different than shots, each time I execute, the circuit is modified slightly). Regardless, the extra 3-4 seconds seems abnormal, because even when I use qc.initialize() on even just one qubit in the circuit, it still takes the extra 3 seconds to execute.

One property about the initialization I need for the circuit is that its kind of "sparse". With n qubits, my initial state is only built with a linear combination of ~2n basis vectors (as opposed to $2^n$).

Given this is there some known algorithm or workaround to make an arbitrary initialization that avoids qc.initialize()? Perhaps is there some way other way through Qiskit I can use?

$\endgroup$

1 Answer 1

2
$\begingroup$

I noticed something about Qiskit initialize functionality, it doesn't always find the best possible circuit to prepare the state. For instance, if I want to put the state into uniform superposition state, $|\psi \rangle = \dfrac{|00\rangle + |01\rangle + |10\rangle + |11\rangle }{4}$ by specifying the following:

from qiskit import QuantumCircuit, Aer, execute
provider = IBMQ.load_account()
qc = QuantumCircuit(2,2)
vector = [0.5, 0.5, 0.5, 0.5]
qc.initialize(vector, [0,1])
qc.x(0)
qc.measure(range(2), range(2))
qc.draw('mpl', style = {'name': 'bw'}, scale = 0.75, initial_state = False,plot_barriers = True) 

The output is something like:

     ┌──────────────────────────────┐┌───┐┌─┐
q_0: ┤0                             ├┤ X ├┤M├
     │  initialize(0.5,0.5,0.5,0.5) │└┬─┬┘└╥┘
q_1: ┤1                             ├─┤M├──╫─
     └──────────────────────────────┘ └╥┘  ║ 
c: 2/══════════════════════════════════╩═══╩═
                                       1   0 

Now if you this on one of the machine, and look at the circuit being submitted then you can see that it does it not in a very efficient way. For instance, I submitted the above circuit to a hardware, and the circuit below is what being submitted:

enter image description here

So instead of preparing the initial state by simply apply two Hadamard gate to each qubit individually, enter image description here

it actually doing something else that is equivalent but less efficient. This might be the reason you see longer experiment time as well.


If you tried this for 3 qubits, that is, if you want to create a uniform superposition state for 3 qubits, then you will get something even worst... That is if you tried to submit this circuit:

«     ┌──────────────────────────────────────────────────────────────────────────────┐»
«q_0: ┤0                                                                             ├»
«     │                                                                              │»
«q_1: ┤1 initialize(0.35355,0.35355,0.35355,0.35355,0.35355,0.35355,0.35355,0.35355) ├»
«     │                                                                              │»
«q_2: ┤2                                                                             ├»
«     └──────────────────────────────────────────────────────────────────────────────┘»
«c: 3/════════════════════════════════════════════════════════════════════════════════»
«                                                                                     »
«     ┌───┐   ┌─┐
«q_0: ┤ X ├───┤M├
«     └┬─┬┘   └╥┘
«q_1: ─┤M├─────╫─
«      └╥┘ ┌─┐ ║ 
«q_2: ──╫──┤M├─╫─
«       ║  └╥┘ ║ 
«c: 3/══╩═══╩══╩═
«       1   2  0

which is can simply being done as: enter image description here

But instead what being submitted to the hardware is:

enter image description here


This is probably just because the stochastic part of the transpiler within Qiskit. So it might be better than you design a function that make the circuit more efficient yourself. This issue is the same if one tried to use the Custom function... If you tried to specified a custom vector here, like what we did above, it will also prepare that initial state in a very convoluted way that is not always efficient.

I think Qiskit uses technique from this paper: Quantum Circuits for Isometries to create the circuit for an arbitrary state that you gave it.


Update:

For the two qubit case, funny enough, I just check Qiskit and they actually have recently changed their native gates on their device! So that, it used to be $U_1, U_2, U_3, CNOT$, but now it is $CNOT, ID, RZ, SX, X$ and so what you saw on the circuit $RZ(\pi/2) \ S_X \ RZ(\pi/2)$ is actually the native level implementation of the Hadamard gate now!

$\endgroup$
9
  • $\begingroup$ Thanks for the response -- that is a fair point, but how does that explain for the fact that when I call q.initialize() over just one qubit the circuit still takes precisely 3.5 seconds. It seem like regardless of the size, it adds the same number of seconds to the execution time. $\endgroup$ Jan 7, 2021 at 0:09
  • $\begingroup$ And actually another point is that, if I just have q.initialize() and remove the rest of the circuit I wrote, the circuit executes super quickly. And if I just have the circuit I wrote without q.initialize(), the circuit also executes super quickly! Combining the two, and suddenly the circuit takes forever (even with simple |000> init) $\endgroup$ Jan 7, 2021 at 0:11
  • $\begingroup$ @RehaanAhmad That actually is pretty interesting. So I guess the extra time is due to the transpilation process.. trying to piece together the two information to create the circuit rather than the circuit execution time. $\endgroup$
    – KAJ226
    Jan 7, 2021 at 0:29
  • 1
    $\begingroup$ Yeah. Actually to build on that, I think I have a hunch here. In my previous experience, whenever you have a large set of gates and then combine them with the .to_gate() function in Qiskit, executing the circuit becomes significantly slower than just executing with the individual gates. In fact, when you draw the circuit with qc.init (as you did), it seems that the initialize function is all compacted into one unitary ".to_gate()" $\endgroup$ Jan 7, 2021 at 1:20
  • 1
    $\begingroup$ Ok, so just for future reference, I have arrived at a verdict. If you do qc.intialize(), and then either set the circuit to qc.decompose() or transpile(...), the execution time dramatically drops (it executes in roughly 0.2 to 0.4 sec) $\endgroup$ Jan 7, 2021 at 7:32

Your Answer

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

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