Measurements and classical control¶

This example details how to perform measurements, and how to use the result to control quantum gates.

For this, we are going to write a script generating a circuit of quantum teleportation. Quantum teleportation is a famous, and simple, algorithm that transfers the state of a qbit into another qbit using an EPR pair.

For explanation about the EPR pair, take a look at the corresponding tutorial.

In [1]:
from qat.lang.AQASM import Program, H, CNOT, Z, X, RZ, RX
tp_prog = Program()
# We need 3 qbits this time
qbits = tp_prog.qalloc(3)
# We will teleport the state of the first qbit
source = qbits[0] 
# into the last qbit
target = qbits[2]
# Qbit 1 will be used to create an EPR pair with the target qbit
tp_prog.apply(H, qbits[1])
tp_prog.apply(CNOT,qbits[1],target)

Now we need to perform a measurement in the Bell basis of the source and the entangled qbit.

We start by rotating the first two qbits in the Bell basis:

In [2]:
tp_prog.apply(CNOT,source,qbits[1])
tp_prog.apply(H,source)

We can then measure these two qbits in the computational basis. In order to store the results of the measures, we need to allocate two classical bits:

In [3]:
results = tp_prog.calloc(2)

A measurement is applied to a (list of) qbit(s) and a corresponding (list of) cbit(s)

In [4]:
#tp_prog.measure([source,qbits[1]],results)
# equivalent to:
tp_prog.measure(source, results[0])
tp_prog.measure(qbits[1], results[1])

We can now use the results of the measurements to "fix" the last qbit to conclude the teleportation. This is done using conditional gate application. If the classical bit passed as argument evaluates to 1, then the gate will be applied.

In [5]:
tp_prog.cc_apply(results[0], Z, target)
tp_prog.cc_apply(results[1], X, target)

A final export, and we are good to go!

In [6]:
#tp_prog.export("teleportation.aqasm")
#!cat teleportation.aqasm
circ = tp_prog.to_circ()
circ.display()
No description has been provided for this image

Let us try and teleport a quantum state!

In [7]:
# We will generate a random state over the first qubit by applying three random rotations
import random, numpy

tp_prog = Program()
qbits = tp_prog.qalloc(3)
source = qbits[0] 
target = qbits[2]

# Lets prepare a random state
angles = [random.random() * 2. * numpy.pi for _ in range(3)]

tp_prog.apply(RZ(angles[0]), source)
tp_prog.apply(RX(angles[1]), source)
tp_prog.apply(RZ(angles[2]), source)

# Lets fetch the circuit at this stage, in order to compare with the end result
c_init = tp_prog.to_circ()

# And apply the teleportation circuit
tp_prog.apply(H, qbits[1])
tp_prog.apply(CNOT,qbits[1],target)
tp_prog.apply(CNOT,source,qbits[1])
tp_prog.apply(H,source)
results = tp_prog.calloc(2)
#tp_prog.measure([source,qbits[1]],results)
# equivalent to:
tp_prog.measure(source, results[0])
tp_prog.measure(qbits[1], results[1])
tp_prog.cc_apply(results[0], Z, target)
tp_prog.cc_apply(results[1], X, target)

circuit = tp_prog.to_circ()

Now we can simulate both circuits using the linear algebra simulator.

In [8]:
from qat.qpus import get_default_qpu
qpu = get_default_qpu()

job_full = circuit.to_job(nbshots=1000)
result_full = qpu.submit(job_full)
job_init = c_init.to_job(nbshots=1000)
result_init = qpu.submit(job_init)

print("Initial state of the system")
for s in result_init:
    print(s.state, s.probability)
    
print("\nAnd after teleportation")
for s in result_full:
    print(s.state, s.probability)
Initial state of the system
|100> 0.48
|000> 0.52

And after teleportation
|111> 0.121
|001> 0.117
|100> 0.135
|110> 0.124
|010> 0.136
|011> 0.118
|000> 0.138
|101> 0.111