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…)