Tutorial 2: Teleportation

wizard image

yaw is a Python-based language for doing algebraic quantum programming. This tutorial explains how to perform teleportation, and along the way, how to do projective measurements. We assume familiarity with the introductory tutorial.

1 Setup


We'll assume that you're familiar with the basic teleportation protocol. It has a few moving parts:

  • Alice has a state \(\psi\);
  • Bob and Alice share a Bell state \(\phi_{00}\);
  • Alice measures in the Bell basis \(\Pi_{ab}\);
  • Bob now has a "masked" state \(Z^b X^a \triangleleft \psi\);
  • Alice sends \(a, b\) to Bob;
  • Bob applies \(X^a Z^b\) to unmask the teleported state.

We'll set up everything but the measurement, explain how to do measurements in general, then tie it all together.

1.1 Alice's state

Suppose Alice possesses a pure state \(\psi = U \triangleleft \pi_0\) of a qubit (equivalently, \(|\psi\rangle = U|\psi_0\rangle\)) where \(\pi_0(\cdot) = \langle 0| \cdot |0\rangle\), and \(U\) is a unitary operator. (We'll discuss the specific form unitaries take, and generalizations to qudits, in the exercises below.) In yaw, we can define a function which applies an operator to \(\pi_0\):

yaw> $alg = qubit()
yaw> pi0 = char(Z, 0)
yaw> def alice(U):
...    return U << pi0

For instance, the \(\pi_+(\cdot) = \langle +|\cdot|+\rangle\) state is simply \(H \triangleleft \pi_0\) (the analogue of \(H|0\rangle = |+\rangle\)).

1.2 The shared Bell pair

The next stage is to prepare a Bell pair \(\phi\). We covered this at the end of the the introductory tutorial, but we'll review it briefly.:

yaw> H = (X + Z)/sqrt(2)
yaw> CNOT = ctrl(Z, [I, X])
yaw> phi_circ = CNOT*(H @ I)
yaw> phi  = phi_circ << pi0 @ pi0

The combined initial state is just aliceSends(U) tensored with phi:

yaw> def initial_state(U):
...    return alice(U) @ phi

Unfortunately, this has a nested tensor structure which yaw currently has trouble with. This needs to be manually "flattened" by defining

yaw> def initial_state(U):
...      return (U @ phi_circ) << pi0 @ pi0 @ pi0

This is exactly the same thing, but without parantheses separating the tensor products!

1.3 The Bell basis

Recall from the previous tutorial that for two qubits, we have a set of entangled Bell states \[ \phi_{ab} = \text{CNOT}(H\otimes I) \triangleleft (\pi_a \otimes \pi_b). \] In yaw:

yaw> def phi(a, b):
...      init = char(Z, a) @ char(Z, b)
...      return phi_circ << init

The projectors \(\Pi_a = |a\rangle\langle a|\) are dual to the states \(\pi_a(\cdot) = \langle a |\cdot|a\rangle\), in the sense that \[ \pi_a(\Pi_b) = \delta_{ab}. \] Similarly, the dual projectors to the Bell states \(\phi_{ab}\) are the Bell projectors \[ \Phi_{ab} = [\text{CNOT}(H\otimes I)]^\dagger \triangleright (\Pi_a \otimes \Pi_b). \] We can define them by The .e after the projector forces it to expand algebraically; this is currently needed to prevent errors downstream, but will be fixed in future versions.

yaw> def Phi(a, b):
...      init = proj(Z, a).e @ proj(Z, b).e
...      return phi_circ.d >> init

We can check these are dual, e.g.

yaw> Phi(1, 0) | phi(1, 0)
1
yaw> Phi(1, 0) | phi(0, 0)
0.0
yaw> Phi(1, 0) | phi(0, 1)
0.0
yaw> Phi(1, 0) | phi(1, 1)
0.0

Now that we've defined the projectors, we should measure with respect to them. That necessitates that we introduce measurement!

2 Measurement

We'll first explain how to do projective measurement in general, and then apply it to Alice's Bell measurement in particular. We'll then apply Bob's correction and check he gets the right state! For a way to keep track of the "many worlds" involved in quantum measurement, check out the final exercise.

2.1 Projective measurement

A projector-valued measurement (PVM) or projective measurement is a set of projectors \(\Pi_i\) that are orthogonal and "resolve the identity": \[ \Pi_i \Pi_j = \delta_{ij}\Pi_i, \quad \sum_i \Pi_i = I. \] If we measure in state \(\pi\), then (according to Born's rule) with probability \[ p_i = \pi(\Pi_i), \] we observe \(i\), and (according to the Lüders rule) the post-measurement state is \[ \pi'_i = \frac{1}{p_i} (\Pi_i \triangleleft \pi). \] This is stochastic. We can implement it in yaw using the stMeasure method, which returns the post-measurement state \(\pi'_i\) and index \(i\):

yaw> PVM = [Pi1, Pi2, ...]
yaw> measure = stMeasure(PVM, state)
yaw> measure()
(measured_state, index)

The method opMeasure does something similar, but instead of returning the state \(\pi'_i\), returns the operator \(\Pi_i\). It works for channels more generally, but we leave a full discussion to another tutorial!

2.2 Alice's measurement

For Alice's Bell measurement, we already have initial_state(U), so we just need to define the Bell PVM. Since it does nothing to Bob's qubit, we can write

yaw> bell_PVM = [Phi(0,0)@I, Phi(0,1)@I, Phi(1,0)@I, Phi(1,1)@I]

We order them this way so that, from index \(i\) observed, we can easily read off the bits by simply converted into binary. The measurement is then

yaw> bell_measure = stMeasure(bell_PVM, initial_state(U))
yaw> measured, index = bell_measure()

Now, according to the usual lore, Bob has a masked version of Alice's state. Once she tells him index, he can unmask it!

2.3 Bob's correction

In general, Bob will apply a correction operator based on the index \(ab\) in binary:

yaw> def correction(a, b):
...      return X**a * Z**b

Running this experiment with \(U = H\), I get

yaw> index
1

This correponds to \(ab = 01\) in binary. This means Bob applies \(Z\) to recover the state, i.e. the total state is

yaw> corrected = (I @ I @ correction(0, 1)) << measured

We can test that this has the correct properties. For instance, for \(U = H\), the transported state \(\pi_+\) is characterized by its expectations: \[ \pi_+(X)= \langle +|X|+\rangle = 1, \quad \pi_+(Y) = \pi_+(Z) = 0. \] We can check:

yaw> I @ I @X | corrected
1
yaw> I @ I @ Z | corrected
0.0
yaw> I @ I @ X*Z | corrected
0.0

So we have successfully teleported! Feel free to repeat the experiment with your own unitary \(U\) and see what happens. If you need some inspiration picking a unitary, check out the first exercise below!

2.4 Summary

Let's briefly summarize the code, in compiled form:

$alg = <X, Z | herm, unit, anti>

# Basic definitions
pi0      = char(Z, 0)
H        = (X + Z)/sqrt(2)
CNOT     = ctrl(Z, [I, X])
phi_circ = CNOT*(H @ I)

# Initial global state
def initial_state(U):
    return (U @ phi_circ) << pi0 @ pi0 @ pi0

# Bell states and dual projectors
def Phi(a, b):
    init = proj(Z, a).e @ proj(Z, b).e
    return phi_circ.d >> init

bell_PVM     = [Phi(0,0)@I, Phi(0,1)@I, Phi(1,0)@I, Phi(1,1)@I]

# Measurement and correction
def corrected(U):
    bell_measure = stMeasure(bell_PVM, initial_state(U))
    measured, i  = bell_measure()
    a, b         = int(f"{i:02b}"[0]), int(f"{i:02b}"[1])
    return (I @ I @ (X**a * Z**b)) << measured

Note that on the second last line, we write \(i\) as a binary string and extract the bits. We can condense this further if we want to play code golf:

$alg = <X, Z | herm, unit, anti>

phi_circ = ctrl(Z,[I,X])*((X+Z)/sqrt(2) @ I)

def ψ(U): return (U @ phi_circ) << (@3) char(Z, 0)
def Φ(a, b): return phi_circ.d >> proj(Z,a).e @ proj(Z,b).e
def corrected(U):
    m, i  = stMeasure([Φ(a,b)@I for a in (0,1) for b in (0,1)], ψ(U))()
    return (I @ I @ (X**((i >> 1) & 1)) * Z**(i & 1))) << m

At seven lines, it's far less intelligible (in particular the bit-shifting at the end), and we've also introduced the prefix notation (@n) for repeated tensor powers. But it's remarkably compact for a complete implementation of teleportation: a magic trick with no sleight of hand!

3 Exercises

Under construction!

3.1 Unitaries

3.2 Qudits

3.3 Branching

4 References