Examples for No-go theorems for photon state transformations in quantum linear optics¶
In this short notebook, we perform the calculations used in the article No-go theorems for photon state transformations in quantum linear optics using the library QOptCraft
.
import numpy as np
from qoptcraft import (
Fock,
photon_invariant,
forbidden_transition,
get_algebra_basis,
get_photon_basis,
)
from qoptcraft.math import gram_schmidt
Hilbert space and algebra bases¶
We begin by obtaining the basis of the Hilbert space with 2 modes and 2 photons:
modes = 2
photons = 2
get_photon_basis(modes, photons)
[(2, 0), (1, 1), (0, 2)]
QOptCraft
returns the basis of the image algebra as sparse matrices to save space. The following cell returns a basis of the image of the Lie algebra of hermitian matrices of dimension 2 under the photonic homomorphism for 2 photons.
basis_algebra, basis_image_algebra = get_algebra_basis(modes, photons)
for i, matrix in enumerate(basis_image_algebra):
print(f"b{i + 1} = {matrix.toarray()}\n")
b1 = [[0.+2.j 0.+0.j 0.+0.j] [0.+0.j 0.+1.j 0.+0.j] [0.+0.j 0.+0.j 0.+0.j]] b2 = [[0.+0.j 0.+0.70710678j 0.+0.j ] [0.+0.70710678j 0.+0.j 0.+0.70710678j] [0.+0.j 0.+0.70710678j 0.+0.j ]] b3 = [[0.+0.j 0.+0.j 0.+0.j] [0.+0.j 0.+1.j 0.+0.j] [0.+0.j 0.+0.j 0.+2.j]] b4 = [[ 0. +0.j -0.70710678+0.j 0. +0.j] [ 0.70710678+0.j 0. +0.j -0.70710678+0.j] [ 0. +0.j 0.70710678+0.j 0. +0.j]]
We can obtain an orthonormal basis using the gram-schmidt algorithm
ortho_basis_image = gram_schmidt(basis_image_algebra)
for i, matrix in enumerate(ortho_basis_image):
print(f"c{i + 1} = {matrix.toarray()}\n")
c1 = [[0.+0.89442719j 0.+0.j 0.+0.j ] [0.+0.j 0.+0.4472136j 0.+0.j ] [0.+0.j 0.+0.j 0.+0.j ]] c2 = [[0.+0.j 0.+0.5j 0.+0.j ] [0.+0.5j 0.+0.j 0.+0.5j] [0.+0.j 0.+0.5j 0.+0.j ]] c3 = [[0.-0.18257419j 0.+0.j 0.+0.j ] [0.+0.j 0.+0.36514837j 0.+0.j ] [0.+0.j 0.+0.j 0.+0.91287093j]] c4 = [[ 0. +0.j -0.5+0.j 0. +0.j] [ 0.5+0.j 0. +0.j -0.5+0.j] [ 0. +0.j 0.5+0.j 0. +0.j]]
Computing invariants¶
Fock state¶
The function photon_invariant
computes the tangent and perpendicular invariants (in this order).
state = Fock(2, 0)
print(f"tangent_invariant = {photon_invariant(state)}")
tangent_invariant = 0.8333333333333333
state = Fock(1, 1)
print(f"tangent_invariant = {photon_invariant(state)}")
tangent_invariant = 0.3333333333333332
state = Fock(0, 2)
print(f"tangent_invariant = {photon_invariant(state)}")
tangent_invariant = 0.8333333333333336
Hong-Ou-Mandel¶
We also compute the invariant of the a 50:50 beamsplitter (Hong-Ou-Mandel experiment output)
state = (Fock(2, 0) - Fock(0, 2)) / np.sqrt(2)
print(f"tangent_invariant = {photon_invariant(state)}")
tangent_invariant = 0.3333333333333336
NOON states¶
state_in = Fock(1, 1)
state_out = Fock(2, 0) + Fock(0, 2)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out)}")
In invariant = 0.3333333 Out invariant = 0.3333333 Forbidden transition? False
state_in = Fock(2, 2)
state_out = Fock(4, 0) + Fock(0, 4)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out)}")
In invariant = 0.2000000 Out invariant = 0.2000000 Forbidden transition? False
state_in = Fock(3, 3)
state_out = Fock(6, 0) + Fock(0, 6)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out)}")
In invariant = 0.1428571 Out invariant = 0.1428571 Forbidden transition? False
state_in = Fock(4, 4)
state_out = Fock(8, 0) + Fock(0, 8)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out)}")
In invariant = 0.1111111 Out invariant = 0.1111111 Forbidden transition? False
state_in = Fock(5, 5)
state_out = Fock(10, 0) + Fock(0, 10)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out)}")
In invariant = 0.0909091 Out invariant = 0.0909091 Forbidden transition? False
Reduced invariants¶
We can call the reduced invariant using the method='reduced'
option in the functions can_transition
and invariant
.
state = Fock(0, 2)
print(f"tangent_invariant = {photon_invariant(state, method='reduced')}")
tangent_invariant = 0.0
For pure states, we can recover the full invariant using theorem 2 without using the algebra basis:
state = Fock(0, 2)
print(f"tangent_invariant = {photon_invariant(state, method='no basis')}")
tangent_invariant = 0.8333333333333334
The reduced invariant allows us to apply the invariant criterion
state_in = Fock(4, 4)
state_out = Fock(8, 0) + Fock(0, 8)
print(f"\nForbidden transition? {forbidden_transition(state_in, state_out, method='reduced')}")
In reduced invariant = -16.0000000 Out reduced invariant = -16.0000000 Forbidden transition? False
Most transitions between fock states are forbidden¶
not_forbidden = []
random_draws = 1000
max_photons_per_mode = 14
rng = np.random.default_rng()
min_modes = 2
max_modes = 25
for modes in range(min_modes, max_modes):
for _ in range(random_draws):
while True:
photons_in = rng.integers(low=0, high=max_photons_per_mode, size=modes, endpoint=True)
photons_out = rng.integers(low=0, high=max_photons_per_mode, size=modes - 1, endpoint=True)
if sum(photons_in) >= sum(photons_out):
photons_out = np.r_[photons_out, sum(photons_in) - sum(photons_out)]
if (np.sort(photons_in) != np.sort(photons_out)).all():
state_in = Fock(*photons_in)
state_out = Fock(*photons_out)
break
if not forbidden_transition(state_in, state_out, method="reduced", print_invariant=False):
not_forbidden.append((state_in, state_out))
print(f"\nPercentage of not forbidden transitions = {len(not_forbidden) * 100 / (random_draws * (max_modes - min_modes))} %")
Percentage of not forbidden transitions = 0.16956521739130434 %
Forbidden (n, 0) to (n-k, k) transition¶
for n in range(5):
for k in range(1, n):
state_in = Fock(n, 0)
state_out = Fock(n - k, k)
print(
f"Forbidden transition? {forbidden_transition(state_in, state_out, method='reduced')}\n"
)
In reduced invariant = 0.0000000 Out reduced invariant = -1.0000000 Forbidden transition? True In reduced invariant = 0.0000000 Out reduced invariant = -2.0000000 Forbidden transition? True In reduced invariant = 0.0000000 Out reduced invariant = -2.0000000 Forbidden transition? True In reduced invariant = 0.0000000 Out reduced invariant = -3.0000000 Forbidden transition? True In reduced invariant = 0.0000000 Out reduced invariant = -4.0000000 Forbidden transition? True In reduced invariant = 0.0000000 Out reduced invariant = -3.0000000 Forbidden transition? True
Impossibility of Bell state with fock ancilla generation from a fock state¶
rng = np.random.default_rng()
bell_state = (Fock(1, 0, 1, 0) + Fock(0, 1, 0, 1)) / np.sqrt(2)
for modes in range(5, 8):
for max_photons_per_mode in range(1, 4):
while True:
photons_in = rng.integers(low=0, high=max_photons_per_mode, size=modes, endpoint=True)
photons_out = rng.integers(
low=0, high=max_photons_per_mode, size=modes - 4, endpoint=True
)
if sum(photons_in) - 2 == sum(photons_out) and sum(photons_in) != 0:
state_in = Fock(*photons_in)
state_out = bell_state * Fock(*photons_out)
break
print(
f"Forbidden transition? {forbidden_transition(state_in, state_out, method='reduced')}\n"
)
In reduced invariant = -1.0000000 Out reduced invariant = -1.5000000 Forbidden transition? True In reduced invariant = -3.0000000 Out reduced invariant = -3.5000000 Forbidden transition? True In reduced invariant = -8.0000000 Out reduced invariant = -7.5000000 Forbidden transition? True In reduced invariant = -3.0000000 Out reduced invariant = -3.5000000 Forbidden transition? True In reduced invariant = -13.0000000 Out reduced invariant = -13.5000000 Forbidden transition? True In reduced invariant = -11.0000000 Out reduced invariant = -12.5000000 Forbidden transition? True In reduced invariant = -6.0000000 Out reduced invariant = -6.5000000 Forbidden transition? True In reduced invariant = -18.0000000 Out reduced invariant = -19.5000000 Forbidden transition? True In reduced invariant = -29.0000000 Out reduced invariant = -30.5000000 Forbidden transition? True
Impossibility of exact transformation of GHZ into W (with fock ancillas)¶
rng = np.random.default_rng()
ghz_state = (Fock(1, 0, 1, 0, 1, 0) + Fock(0, 1, 0, 1, 0, 1)) / np.sqrt(2)
w_state = (Fock(1, 0, 0, 1, 0, 1) + Fock(0, 1, 1, 0, 0, 1) + Fock(0, 1, 0, 1, 1, 0)) / np.sqrt(3)
for modes in range(1, 5):
for max_photons_per_mode in range(1, 4):
while True:
photons_in = rng.integers(low=0, high=max_photons_per_mode, size=modes, endpoint=True)
photons_out = rng.integers(low=0, high=max_photons_per_mode, size=modes, endpoint=True)
if sum(photons_in) == sum(photons_out) and sum(photons_in) != 0:
state_in = ghz_state * Fock(*photons_in)
state_out = w_state * Fock(*photons_out)
break
print(
f"Forbidden transition? {forbidden_transition(state_in, state_out, method='reduced')}\n"
)
In reduced invariant = -6.7500000 Out reduced invariant = -6.6666667 Forbidden transition? True In reduced invariant = -9.7500000 Out reduced invariant = -9.6666667 Forbidden transition? True In reduced invariant = -6.7500000 Out reduced invariant = -6.6666667 Forbidden transition? True In reduced invariant = -6.7500000 Out reduced invariant = -6.6666667 Forbidden transition? True In reduced invariant = -6.7500000 Out reduced invariant = -6.6666667 Forbidden transition? True In reduced invariant = -30.7500000 Out reduced invariant = -30.6666667 Forbidden transition? True In reduced invariant = -6.7500000 Out reduced invariant = -6.6666667 Forbidden transition? True In reduced invariant = -9.7500000 Out reduced invariant = -10.6666667 Forbidden transition? True In reduced invariant = -19.7500000 Out reduced invariant = -20.6666667 Forbidden transition? True In reduced invariant = -10.7500000 Out reduced invariant = -10.6666667 Forbidden transition? True In reduced invariant = -20.7500000 Out reduced invariant = -20.6666667 Forbidden transition? True In reduced invariant = -70.7500000 Out reduced invariant = -70.6666667 Forbidden transition? True