Representing partially dinstinguishable states
In this notebook, we use the different kinds of BasicState
to present how the user can handle complex inputs using perceval.
[1]:
import numpy as np
from perceval import BasicState, AnnotatedFockState, StateVector, Annotation
BasicState insight
A BasicState
is a class that embodies three different kinds of Fock states. The class init chooses the correct representation according to its arguments, and most methods are common to all the classes.
The
FockState
represents indistinguishable photons. The representation shows the number of photon in each mode (e.g. \(|0, 1, 0, 2\rangle\))The
NoisyFockState
represents groups of indistinguishable photons, where each group is represented using an integer (the ‘noise tag’) from 0 to 255. The photons from different groups are totally distinguishable and do not interact at all. The representation shows the noise tag between brackets (e.g. \(|0, \{0\}, 0, \{0\}\{1\}\rangle\))The
AnnotatedFockState
is a generic state class where photons can be given string tags associated with values. The representation shows the tag and the value separated by a semicolon, all between brackets (e.g. \(|0, \{P:0\}, 0, \{P:1.57\}\{P:3.14\}\rangle\)).
In the general case, it is not possible to simulate the results of an AnnotatedFockState
. The user can provide a way to compute distinguishability from annotations comparison, which allows to handle some physical properties. Here we show an example of computations with photon wavelength, by converting an AnnotatedFockState
into a superposition of FockState
and NoisyFockState
.
Example: wavelength
Suppose you have two photons of different wavelength arriving at a perfect beam splitter. We want to study how a difference in wavelength will affect the HOM effect.
[2]:
# This creates two AnnotatedFockStates with an associated wavelength
state_1 = BasicState("|{wavelength:625e-9}>") # The first source produced state
state_2 = BasicState("|{wavelength:615e-9}>") # The second source produced state
# The whole input state arriving at the chip is then
input_state = state_1 * state_2
print(input_state)
|{wavelength:6.25e-07},{wavelength:6.15e-07}>
The problem now is that perceval doesn’t know how distinguishable are these photons. So, as a user, we first need to convert this to something that Perceval can simulate.
For that, we introduce a model where the indistinguishability depends on the wavelength of the two photons.
[3]:
def compute_indistinguishability(photon_1: Annotation, photon_2: Annotation):
# To make sense, this method must at least verify:
# - compute_indistinguishability(a, b) == compute_indistinguishability(b, a)
# - compute_indistinguishability(a, a) == 1
lambda_1 = photon_1["wavelength"]
lambda_2 = photon_2["wavelength"]
return np.exp(- 2 * (lambda_2 - lambda_1) ** 2 / (lambda_1 * lambda_2))
def convert_state(state: AnnotatedFockState) -> StateVector:
assert state.n == 2 # This method would fail otherwise
photon1 = state.get_photon_annotation(0) # Get the annotation from the first photon
photon2 = state.get_photon_annotation(1) # Get the annotation from the second photon
indist = compute_indistinguishability(photon1, photon2)
indist_state = state.clear_annotations() # This creates a FockState with photons at the same place
# Same result than BasicState(list(state))
noise = (1 - indist ** 2) ** 0.5 # So the final result is normalized
noisy_state = BasicState(list(state), [0, 1]) # Creates a NoisyFockState with tags 0 and 1 so the two photons are distinguishable
return indist_state * indist + noisy_state * noise # Creates a StateVector containing only state types that Perceval can simulate
converted_input_state = convert_state(input_state)
print(converted_input_state)
0.999*|1,1>+0.032*|{0},{1}>
We can now simulate using our state as usual.
[4]:
from perceval import BS, Processor
p = Processor("SLAP", BS())
p.min_detected_photons_filter(0)
p.with_input(converted_input_state)
print(p.probs()["results"])
{
|0,2>: 0.4997399722916615
|2,0>: 0.4997399722916615
|1,1>: 0.0005200554166770454
}
Great! You now know how to create and use states defining (partially) distinguishable photons. Knowing how to use BasicStates
that are more than just photon positions is a valuable tool for bigger and tougher problems, especially when state arithmetics is involved.
The AnnotatedFockState
can also be used to define polarization using the tag “P”, that Perceval can natively handle, and that requires a more complex conversion (duplication of the circuit and state size, conversion back at the end…)