Different Types of Measurement API#

Overview#

TensorCircuit allows for two kinds of operations to be performed that are related to the outcomes of measurements. These are (i) conditional measurements, the outcomes of which can be used to control downstream conditional quantum gates, and (ii) post-selection, which allows the user to select the post-measurement state corresponding to a particular measurement outcome.

Setup#

[1]:
import tensorcircuit as tc
import numpy as np

K = tc.set_backend("tensorflow")

Conditional Measurements#

The cond_measure command is used to simulate the process of performing a Z measurement on a qubit, generating a measurement outcome with probability given by the Born rule, and then collapsing the wavefunction in accordance with the measured outcome. The classical measurement outcome obtained can then act as a control for a subsequent quantum operation via the conditional_gate API and can be used, for instance, to implement the canonical teleportation circuit.

[2]:
# quantum teleportation of state |psi> = a|0> + sqrt(1-a^2)|1>
a = 0.3
input_state = np.kron(np.array([a, np.sqrt(1 - a**2)]), np.array([1, 0, 0, 0]))

c = tc.Circuit(3, inputs=input_state)
c.h(2)
c.cnot(2, 1)
c.cnot(0, 1)
c.h(0)

# mid-circuit measurements
z = c.cond_measure(0)
x = c.cond_measure(1)

# if x = 0 apply I, if x = 1 apply X (to qubit 2)
c.conditional_gate(x, [tc.gates.i(), tc.gates.x()], 2)

# if z = 0 apply I, if z = 1 apply Z (to qubit 2)
c.conditional_gate(z, [tc.gates.i(), tc.gates.z()], 2)
[3]:
# we indeed recover the state at the third qubit.

c.measure(2, with_prob=True), a**2, 1 - a**2
[3]:
((<tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>,
  <tf.Tensor: shape=(), dtype=float32, numpy=0.90999997>),
 0.09,
 0.91)

The teleportation circuit is shown below. image0

Post Selection#

Post-selection is enabled in TensorCircuit via the post_select method. This allows the user to select the post-\(Z\)-measurement state of a qubit via the keep argument. Unlike cond_measure, the state returned by post_select is collapsed but not normalized.

[4]:
c = tc.Circuit(2, inputs=np.array([1, 0, 0, 1] / np.sqrt(2)))
c.post_select(0, keep=1)  # measure qubit 0, post-select on outcome 1
c.state()
[4]:
<tf.Tensor: shape=(4,), dtype=complex64, numpy=
array([0.        +0.j, 0.        +0.j, 0.        +0.j, 0.70710677+0.j],
      dtype=complex64)>

This example initialize a \(2\)-qubit maximally entangled state \(\vert{\psi}\rangle = \frac{\vert{00}\rangle+\vert{11}\rangle}{\sqrt{2}}\). The first qubit (\(q_0\)) is then measured in the \(Z\)-basis, and the unnormalized state \(\vert{11}\rangle/\sqrt{2}\) corresponding to measurement outcome \(1\) is post-selected.

This post-selection scheme with unnormalized states is fast and can, for instance, be used to explore various quantum algorithms and nontrivial quantum physics such as measurement-induced entanglement phase transitions.

Plain Measurements#

[5]:
c = tc.Circuit(3)
c.H(0)
print(c.measure(0, with_prob=True))
print(c.measure(0, 1, with_prob=True))
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.5>)
(<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 0.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.49999997>)

Note how the plain measure API is virtual in the sense that the state is not collapsed after measurement.

[6]:
for _ in range(5):
    print(c.measure(0, with_prob=True))
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.49999997>)
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.5>)
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.49999997>)
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.49999997>)
(<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, <tf.Tensor: shape=(), dtype=float32, numpy=0.49999997>)

Let’s jit measure! (with careful random number manipulation)

[7]:
n = 3
key = K.get_random_state(42)


def measure_on(param, index, key):
    K.set_random_state(key)
    c = tc.Circuit(n)
    for i in range(n):
        c.rx(i, theta=param[i])
    return c.measure(*index)[0]


measure_on_jit = K.jit(measure_on, static_argnums=1)

key1 = key
for _ in range(30):
    key1, key2 = K.random_split(key1)
    print(measure_on_jit(K.ones([n]), [0, 1, 2], key2))
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 1.], shape=(3,), dtype=float32)
tf.Tensor([1. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([1. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 1.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([1. 0. 1.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 0. 0.], shape=(3,), dtype=float32)
tf.Tensor([0. 1. 0.], shape=(3,), dtype=float32)

For a summary of the differences between plain measure and the two types of measurement we mentioned here, please see FAQ documentation.