2
$\begingroup$

I want to perform the controlled time-evolution of some 2 or 3-qubit Hamiltonian. Say we have this example: $$ H= Z_0\otimes Z_1 + Z_1\otimes Z_2 $$ The circuit performing the time-evolution controlled by qubit 3 could be generated as (the arguments of R_Z gate are randomly chosen to be pi):

qrz = QuantumRegister(4,'q')
crz = ClassicalRegister(1,'c')
qcz = QuantumCircuit(qrz,crz)
qcz.cx(0,1)
qcz.crz(np.pi,3,1)
qcz.cx(0,1)
qcz.cx(1,2)
qcz.crz(np.pi,3,2)
qcz.cx(1,2)
qcz.draw(output = 'mpl')

enter image description here

However, I wonder is it possible that we can turn the circuit responsible for time-evolution directly into a quantum gate, and then the gate is controlled by qubit 3? Here's what I tried:

my_circ = QuantumCircuit(3)
my_circ.cx(0,1)
my_circ.rz(np.pi,1)
my_circ.cx(0,1)
my_circ.cx(1,2)
my_circ.rz(np.pi,2)
my_circ.cx(1,2)
my_circ.draw(output = 'mpl')

enter image description here

Then I have

new_circ = QuantumCircuit(4)
new_circ.append(custom,[3,2,1,0])
new_circ.draw(output = 'mpl')

enter image description here

Are the two circuits qcz and new_circ equivalent? Generally, if I know how to construct the quantum circuit for the time evolution of some Hamiltonian but don't know exactly how to perform controlled time-evolution, can I just transform the time-evolution circuit into a gate, and then do something similar to this example? Thanks for the help!

$\endgroup$
4
$\begingroup$

Yes, they should be.

You can check their statevector to confirm that they indeed generate the same state. First, you can generate some arbitrary initial state and use the following two methods to show that they generate the same statevector. For instance:

init_circ = QuantumCircuit(3)
init_circ.ry(2.5,0)
init_circ.ry(1.2, 1)
init_circ.ry(0.5, 2) 
print(init_circ)
     ┌─────────┐
q_0: ┤ RY(2.5) ├
     ├─────────┤
q_1: ┤ RY(1.2) ├
     ├─────────┤
q_2: ┤ RY(0.5) ├
     └─────────┘
                    


my_circ = QuantumCircuit(3)
my_circ.cx(0,1)
my_circ.rz(np.pi,1)
my_circ.cx(0,1)
my_circ.cx(1,2)
my_circ.rz(np.pi,2)
my_circ.cx(1,2)
print(my_circ)

                                           
q_0: ──■─────────────■─────────────────────
     ┌─┴─┐┌───────┐┌─┴─┐                   
q_1: ┤ X ├┤ RZ(π) ├┤ X ├──■─────────────■──
     └───┘└───────┘└───┘┌─┴─┐┌───────┐┌─┴─┐
q_2: ───────────────────┤ X ├┤ RZ(π) ├┤ X ├
                        └───┘└───────┘└───┘



xs_gate = my_circ.to_gate()
cxs_gate = xs_gate.control()
circuit = QuantumCircuit(4) 
circuit.append(init_circ, [1,2,3]) 
circuit.append(cxs_gate, [0,1,2,3])
print(circuit)

                                     
q_0: ───────────────────────■────────
     ┌──────────────┐┌──────┴───────┐
q_1: ┤0             ├┤0             ├
     │              ││              │
q_2: ┤1 circuit-155 ├┤1 circuit-156 ├
     │              ││              │
q_3: ┤2             ├┤2             ├
     └──────────────┘└──────────────┘

from qiskit.quantum_info import Statevector
circuit_statevector = Statevector(circuit)
print(circuit_statevector )
Statevector([0.25215633+0.j, 0.        +0.j, 0.75888206+0.j,
             0.        +0.j, 0.17250943+0.j, 0.        +0.j,
             0.51917915+0.j, 0.        +0.j, 0.06438608+0.j,
             0.        +0.j, 0.1937744 +0.j, 0.        +0.j,
             0.04404889+0.j, 0.        +0.j, 0.1325682 +0.j,
             0.        +0.j],
            dims=(2, 2, 2, 2))

Note: I added a layer of some arbitrary $RY$ rotations at the beginning just so that we don't start at the $|0\rangle$ state.

And for the other circuit you have:

qcz = QuantumCircuit(4)
qcz.ry(2.5,1)
qcz.ry(1.2, 2)
qcz.ry(0.5, 3)
qcz.cx(1,2)
qcz.crz(np.pi,0,2)
qcz.cx(1,2)
qcz.cx(2,3)
qcz.crz(np.pi,0,3)
qcz.cx(2,3)
print(qcz)
                                                      
q_0: ────────────────────■──────────────────■─────────
     ┌─────────┐         │                  │         
q_1: ┤ RY(2.5) ├──■──────┼──────■───────────┼─────────
     ├─────────┤┌─┴─┐┌───┴───┐┌─┴─┐         │         
q_2: ┤ RY(1.2) ├┤ X ├┤ RZ(π) ├┤ X ├──■──────┼──────■──
     ├─────────┤└───┘└───────┘└───┘┌─┴─┐┌───┴───┐┌─┴─┐
q_3: ┤ RY(0.5) ├───────────────────┤ X ├┤ RZ(π) ├┤ X ├
     └─────────┘                   └───┘└───────┘└───┘

qcz_statevector = Statevector(qcz)
print(qcz_statevector )
Statevector([0.25215633+0.j, 0.        +0.j, 0.75888206+0.j,
             0.        +0.j, 0.17250943+0.j, 0.        +0.j,
             0.51917915+0.j, 0.        +0.j, 0.06438608+0.j,
             0.        +0.j, 0.1937744 +0.j, 0.        +0.j,
             0.04404889+0.j, 0.        +0.j, 0.1325682 +0.j,
             0.        +0.j],
            dims=(2, 2, 2, 2))

Notice how the two statevectors are the same. This is because the two circuits are equivalent.



Update:

I didn't think much when I answered this, and knew that the two circuits were equivalent to start so I just tested it out using the method above. Which is kinda convoluted. The other answer used Operator from quantum_info to show that the two operators are indeed equivalent is much better as it show they are two equivalent unitary matrix. There is a another method to do this, and I will provide it below:

qcz = QuantumCircuit(4)
qcz.cx(1,2)
qcz.crz(np.pi,0,2)
qcz.cx(1,2)
qcz.cx(2,3)
qcz.crz(np.pi,0,3)
qcz.cx(2,3)
backend = Aer.get_backend('unitary_simulator')
job = execute(qcz, backend)
result = job.result()
qcz_unitary  = result.get_unitary(qcz, decimals=3)


my_circ = QuantumCircuit(3)
my_circ.cx(0,1)
my_circ.rz(np.pi,1)
my_circ.cx(0,1)
my_circ.cx(1,2)
my_circ.rz(np.pi,2)
my_circ.cx(1,2)
xs_gate = my_circ.to_gate()
cxs_gate = xs_gate.control()
circuit = QuantumCircuit(4) 
circuit.append(cxs_gate, [0,1,2,3])
backend = Aer.get_backend('unitary_simulator')
job = execute(circuit, backend)
result = job.result()
circuit_unitary  = result.get_unitary(circuit, decimals=3)

Now you can check that the two operators, qcz_unitary and circuit_unitary, represent the two circuits are equivalent.

Also note that these two circuits are equivalent fundamentally as pointed out by DaftWullie in this answer. Here, we are just checking to make sure what being implemented in Qiskit is correct.

$\endgroup$
6
  • $\begingroup$ Thanks so much, that really helps! $\endgroup$
    – ZR-
    Aug 24 at 19:00
  • 2
    $\begingroup$ @ZR- No problem! Glad to see you still around the community. $\endgroup$
    – KAJ226
    Aug 24 at 19:02
  • $\begingroup$ Haha thanks!! In principle, can I apply this method to any quantum circuit controlled by an additional qubit? $\endgroup$
    – ZR-
    Aug 24 at 19:05
  • 2
    $\begingroup$ @ZR- yes. I don't see why not. $\endgroup$
    – KAJ226
    Aug 24 at 19:10
  • $\begingroup$ You need to check all basis vectors as inputs to verify unitary equivalence using this method. $\endgroup$ Aug 25 at 11:24
7
$\begingroup$

You can also check their equivalence using Operator, a class from the Qiskit's quantum_info module as follows.

from qiskit import *
from qiskit.quantum_info import Operator
import numpy as np

Build and convert each circuit to an operator.( used your name convention for the circuits )

The code below builds and converts the first circuit qrz to op1

qrz = QuantumCircuit(4)
qrz.cx(0, 1)
qrz.crz(np.pi, 3, 1)
qrz.cx(0, 1)
qrz.cx(1, 2)
qrz.crz(np.pi, 3, 2)
qrz.cx(1, 2)

op1 = Operator(qrz)

print(qrz)

q_0: ──■─────────────■─────────────────────
     ┌─┴─┐┌───────┐┌─┴─┐                   
q_1: ┤ X ├┤ RZ(π) ├┤ X ├──■─────────────■──
     └───┘└───┬───┘└───┘┌─┴─┐┌───────┐┌─┴─┐
q_2: ─────────┼─────────┤ X ├┤ RZ(π) ├┤ X ├
              │         └───┘└───┬───┘└───┘
q_3: ─────────■──────────────────■─────────
                                       

Following code transform your my_circ to op2.

my_circ = QuantumCircuit(3, name='custom')
my_circ.cx(0,1)
my_circ.rz(np.pi,1)
my_circ.cx(0,1)
my_circ.cx(1,2)
my_circ.rz(np.pi,2)
my_circ.cx(1,2)
print(my_circ)

custom = my_circ.to_gate().control()

new_circ = QuantumCircuit(4)
new_circ.append(custom,[3,2,1,0])
print(new_circ)

op2 = Operator(new_circ) 


q_0: ──■─────────────■─────────────────────
     ┌─┴─┐┌───────┐┌─┴─┐                   
q_1: ┤ X ├┤ RZ(π) ├┤ X ├──■─────────────■──
     └───┘└───────┘└───┘┌─┴─┐┌───────┐┌─┴─┐
q_2: ───────────────────┤ X ├┤ RZ(π) ├┤ X ├
                        └───┘└───────┘└───┘
     ┌─────────┐
q_0: ┤2        ├
     │         │
q_1: ┤1 custom ├
     │         │
q_2: ┤0        ├
     └────┬────┘
q_3: ─────■─────
            

Now compare the two operator to check the equivalence of the circuits.

op1 == op2
True
$\endgroup$
2
4
$\begingroup$

Yes. In general, a trivial construction of controlled-$U$, given a circuit for $U$ is to make every circuit element controlled off the control qubit.

However, this introduces more overhead than necessary. If you have a part of your circuit that looks like $VWV^\dagger$, it is only necessary to make the $W$ controlled because the $VV^\dagger$ cancel in the absence of the $W$. This is what you've effectively done in your circuit where $V=V^\dagger$ is the controlled-not and $W$ is the $R_z$ gate.

$\endgroup$

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.