HPU/GPU
Matrix Multiplication with GPU Announcing alkomp Collatz Conjecture
Robotics
FRC Kickoff 2019

Quantum Teleportation

import numpy as np

Quantum Teleportation, Density Matrix, and Quantum Measurement

Alice (A) wants to send Bob (B) a qubit. Alice owns a qubit that she wants to send as well as a qubit that is entangled with Bob's qubit.

Create an entangled pair: $$\frac{1}{\sqrt{2}} \left( \left | 00 \right \rangle + \left | 11 \right \rangle \right)$$

EPR = np.array([[1], [0], [0], [1]])/np.sqrt(2)
print(EPR)
[[0.70710678]
 [0.        ]
 [0.        ]
 [0.70710678]]

Create an arbitrary state $(\alpha \left | 0 \right \rangle + \beta \left | 1 \right \rangle)$ that Alice will send to Bob. In this case the state is simply: $$\left | \phi \right \rangle = \frac{1}{\sqrt{2}} \left( \left | 0 \right \rangle - \left | 1 \right \rangle \right)$$

phi = np.array([[1], [-1]])
phi = phi /np.linalg.norm(phi)
print(*phi.flatten())
0.7071067811865475 -0.7071067811865475

Now the state of the three qubits looks like:

$$\left | \psi \right \rangle = \left | \phi \right \rangle \otimes \frac{1}{\sqrt{2}} \left( \left | 00 \right \rangle + \left | 11 \right \rangle \right)$$

# Combine the psi and EPR states to the full space
psi = np.kron(phi, EPR)
print(psi)
[[ 0.5]
 [ 0. ]
 [ 0. ]
 [ 0.5]
 [-0.5]
 [-0. ]
 [-0. ]
 [-0.5]]

Now let us preform the quantum teleportation. For visualization, a circuit is drawn. The first stage initalizes the qubits; one which denotes what we want to send, and the other initalization for constructing an EPR pair. The second stage entangles Alice's qubit with the qubit from the EPR pair which Alice owns -- this is the main "teleportation" step.

from qiskit import QuantumCircuit, QuantumRegister
q_psi = QuantumRegister(1, 'psi')
q_epr = QuantumRegister(2, 'epr')
qc = QuantumCircuit(q_psi, q_epr)
qc.initialize([*phi.flatten()], [q_psi[0]])
qc.initialize([*EPR.flatten()], q_epr)
qc.barrier()
qc.cx(q_psi[0], q_epr[0])
qc.h(q_psi[0])
qc.barrier()
qc.draw()
         ┌──────────────────────────────┐   ░      ┌───┐ ░ 
psi_0: ──┤ initialize(0.70711,-0.70711) ├───░───■──┤ H ├─░─
       ┌─┴──────────────────────────────┴─┐ ░ ┌─┴─┐└───┘ ░ 
epr_0: ┤0                                 ├─░─┤ X ├──────░─
       │  initialize(0.70711,0,0,0.70711) │ ░ └───┘      ░ 
epr_1: ┤1                                 ├─░────────────░─
       └──────────────────────────────────┘ ░            ░ 
# Manually construct matrices and preform evolution
# Define CNOT operation
CNOT = np.eye(4)
CNOT[2,2] = 0; CNOT[2,3] =1; CNOT[3,2] = 1; CNOT[3,3] = 0
CNOT = np.kron(CNOT, np.eye(2))

# Define hadamard operation
HADAMARD = np.array([[1,1], [1,-1]])/np.sqrt(2)
IDENTITY = np.kron(np.eye(2), np.eye(2))
HADAMARD = np.kron(HADAMARD, IDENTITY)

After performing all the operations we have a complete state (which is pure):

psi = CNOT.dot(psi)
psi = HADAMARD.dot(psi)
print(psi)
[[ 0.35355339]
 [-0.35355339]
 [-0.35355339]
 [ 0.35355339]
 [ 0.35355339]
 [ 0.35355339]
 [ 0.35355339]
 [ 0.35355339]]

The cool part...

In the quantum teleportation protocol, we would then measure both of Alice's qubits and then send to Bob so that he can recover the sent state. But why do we need the classical bits? Generally this is motiviated by showing we will have 4 possibilities after measuring Alice's qubits.

Let's view through the lens of quantum measurement. What if we measure but ignore the results of Alice's qubits. Well now our pure states becomes a state matrix.

In particular, we have an observable operator ($\hat{\Lambda}$) which can be diagonalized as: $$\hat{\Lambda} = \sum_\lambda \lambda \Pi_\lambda$$ The $\Pi_\lambda$ are projective measurements and $\lambda$ are the eigenvalues.

So by performing a measurement and ignoring the results, we take our pure state $\rho_a = \left | \psi \right\rangle \left \langle \psi \right |$ and created a mixed state: $\rho_b = \sum_\lambda \Pi_\lambda \rho_a \Pi_\lambda$

Moreover, this shows that, in general, a measurement is an entropy-increasing process, unless we keep track of the measurement results.

Classical Measurement

This result is different from what happens classically. When we have a classical state and perform a measurement, we can model it using Bayesian inference (which captures the conditional, disturbing, nature of the system - where the state of a system changes given the measurement outcome.) Using this model, non-disturbing measurements show that a-posteriori state is identical to the a-priori state.

# Preform the projective measurement
rho = np.zeros((8,8))
for i in range(4):
    P_i = np.zeros((8,8)); P_i[i*2,i*2] = 1; P_i[i*2+1,i*2+1] = 1
    p_i = psi.conj().T.dot(P_i.dot(psi))
    after_measure = P_i.dot(psi) / np.sqrt(p_i)
    rho = rho + p_i * after_measure.dot(after_measure.conj().T)
rho
array([[ 0.125, -0.125,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ],
       [-0.125,  0.125,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.125, -0.125,  0.   ,  0.   ,  0.   ,  0.   ],
       [ 0.   ,  0.   , -0.125,  0.125,  0.   ,  0.   ,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.   ,  0.   ,  0.125,  0.125,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.   ,  0.   ,  0.125,  0.125,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.125,  0.125],
       [ 0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.125,  0.125]])

Let us take a look at the purity of the system now as compared to before measurement. We can see entropy was increased!

r = np.outer(psi.conj().T, psi)
print("Before measurement")
print(np.trace(r.dot(r)))
print("After measurement")
print(np.trace(rho.dot(rho)))
Before measurement
0.9999999999999991
After measurement
0.24999999999999967

Moreover, let use trace out Alice's system and only look at Bob's system. This will give us insight to what Bob currently knows (after the measurement but ignoring the results.)

tr_A = np.zeros((2,2))

# By linearity in trace input
for i in range(4):
    # |b_1><b_2| <00|00>{<01|01|}{10|10}{11|11} = is already encoded in rho just find |b_1><b_2| (which are just blocks in rho)
    tr_i = rho[np.ix_([i*2,i*2+1], [i*2, i*2+1])]
    tr_A = tr_A + tr_i
print('Bob\'s state matrix:')
print(tr_A)

print('Clearly, this is maximally mixed:')
# Mixed state since < 1
print(np.trace(tr_A.dot(tr_A)))
Bob's state matrix:
[[0.5 0. ]
 [0.  0.5]]
Clearly, this is maximally mixed:
0.49999999999999933

As we can see, Bob's state is $\frac{1}{2} \mathbf{I}$.

Well this is a maximally-mixed state! Namely, Bob's state is mixture of states that all occur with equal probability; we have no clue what those states are, as there are infinitely many ways to construct (summing to) the identity.

Now going back, if we did record the results then we would have 4 possible combinations (tracked by classical bits) and likewise we would know exactly the 4 pure states that Bob has, each occuring with probablity $\frac{1}{4}$. And hence we know what the summation to produce the identity!

Conclusion

Although this is probably a bit cryptic and hard to follow, this mathematically shows the really interesting ideas concerning quantum measurement. And more importantly, the difference between classical and quantum measurement -- where classically, if we ignore the measured result (i.e. non-disturbing), our a-posteriori state (how much we know about a system) does not change, where as in quantum it does!