Heralded CNOT

This notebook demonstrates the operation of the Heralded CNOT gate from [KLM01].

[1]:
import numpy as np

import lightworks as lw
from lightworks import emulator

Layout

The CNOT is built from a set of known phase settings below. These settings require the beam splitter to be in the “H” convention

[2]:
# Set required element reflectivity
n1 = 2 * np.arccos(1 / (4 - 2 * 2**0.5) ** 0.5)
n2 = 2 * np.arccos(2**0.5 - 1)

cnot_circuit = lw.PhotonicCircuit(8)

# Loop over required elements, the format here is:
# (mode, [bs reflectivity, phase 1, phase 2])
data = [
    (0, [n1, np.pi, np.pi]),
    (4, [np.pi / 2, 0, 0]),
    (6, [n1, 0, 0]),
    (1, [np.pi, 0, 0]),
    (3, [np.pi / 2, 0, 0]),
    (5, [np.pi, 0, 0]),
    (2, [n2, 0, 0]),
    (4, [n2, np.pi, np.pi]),
    (1, [np.pi, 0, 0]),
    (3, [np.pi / 2, 0, 0]),
    (5, [np.pi, 0, 0]),
    (0, [n1, np.pi, np.pi]),
    (4, [np.pi / 2, 0, 0]),
    (6, [n1, 0, 0]),
]

# Loop over each element and add
for mode, d in data:
    theta, phi1, phi2 = d
    if phi1 > 0:
        cnot_circuit.ps(mode + 1, phi1)
    cnot_circuit.bs(mode, reflectivity=np.cos(theta / 2) ** 2, convention="H")
    if phi2 > 0:
        cnot_circuit.ps(mode, phi2)

# Add the required heralds to the circuit on the ancillary modes.
cnot_circuit.herald(0, 0)
cnot_circuit.herald(1, 1)
cnot_circuit.herald(1, 6)
cnot_circuit.herald(0, 7)

Once created, the circuit can be viewed with labelled modes. The c and t mode are the control and target qubit modes respectively, and the a and b modes are the ancillary modes required for implementation of the gate. The heralded modes can also be seen here.

[3]:
cnot_circuit.display(
    mode_labels=["a0", "a1", "c0", "c1", "t0", "t1", "b1", "b0"]
)
../_images/examples_heralded_cnot_5_0.svg

Sampling

Once created, the circuit is then simulated with the Sampler.

Note

This cell can take quite a while to run (~30 seconds).

[4]:
# Set qubit input here
in_qubits = "10"

# This is then converted into modes
in_state = lw.convert.qubit_to_dual_rail(in_qubits)

# Run sampler with imperfect properties
source = emulator.Source(indistinguishability=0.93, purity=0.98)
detector = emulator.Detector(photon_counting=False)

# Apply heralding rules and sample
N_rep = 10000
sampler = lw.Sampler(
    cnot_circuit,
    in_state,
    N_rep,
    source=source,
    detector=detector,
    min_detection=2,
    random_seed=7,
)

backend = emulator.Backend("slos")
results = backend.run(sampler)

Once results are collected, it can be seen that in the case of a \(\ket{10}\) input, the most likely output is \(\ket{11}\) (|0,1,0,1> in mode language), as expected.

[9]:
print("Mode encoding:")
results.plot()

print("Qubit encoding:")
conv_results = results.map(lw.convert.dual_rail_to_qubit, allow_invalid=True)
conv_results.plot()
Mode encoding:
../_images/examples_heralded_cnot_9_1.png
Qubit encoding:
../_images/examples_heralded_cnot_9_3.png