StateVector

The StateVector class is the mean to create superposed pure states in the Fock space.

State vector arithmetics

A StateVector can be built using arithmetic. While only applying arithmetic operations to a state vector, no automatic normalization is called, allowing the composition of state vectors through multiple Python statements.

>>> from exqalibur import StateVector
>>> sv = StateVector("|1>") + StateVector("|2>")
>>> sv += StateVector("|3>")
>>> print(sv)  # All components of sv have the same amplitude
0.577*|1>+0.577*|2>+0.577*|3>

StateVector can be built with great freedom:

>>> import math
>>> from exqalibur import FockState, StateVector
>>> sv = 0.5j * FockState([1, 1]) - math.sqrt(2) * StateVector("|2,0>") + StateVector([0, 2]) * 0.45
>>> print(sv)
0.319I*|1,1>-0.903*|2,0>+0.287*|0,2>

Warning

When multiplying a state by a numpy scalar (such as one returned by a numpy function), numpy takes precedence over the state arithmetics and tries to convert the state to a numpy array. This results in an exception with potentially obscure message. Two solutions exist: putting the numpy number on the right of the * operand, or converting the numpy scalar to a python type using the .item() method.

  • Comparison operators

Comparing two StateVector with operator == or != compare normalised copies of each. probability amplitudes are compared strictly (they have to be exactly the same to be considered equal).

Note

StateVector will normalize themselves only at usage (iteration, sampling, measurement), and not during state arithmetics operations.

StateVector can also be multiplied with a tensor product:

>>> import exqalibur as xq
>>> sv0 = xq.StateVector([1,0]) + xq.StateVector([0,1])
>>> sv1 = 1j*xq.StateVector([2]) - xq.StateVector([0])
>>> bs = xq.FockState([0])
>>> print(sv0 * sv1 * bs)
0.5I*|0,1,2,0>-0.5*|0,1,0,0>+0.5I*|1,0,2,0>-0.5*|1,0,0,0>

Exponentiation is also built-in:

>>> print(sv1 ** 3) # equivalent to sv1 * sv1 * sv1
-0.354I*|2,2,2>+0.354I*|2,0,0>+0.354*|2,2,0>+0.354*|2,0,2>+0.354I*|0,2,0>+0.354*|0,2,2>-0.354*|0,0,0>+0.354I*|0,0,2>

StateVector code reference

class exqalibur.StateVector

State vector is a superposition of Fock states

Parameters:

fock_state

Optional initial state. Can either be:

  • a constructed FockState

  • the string representation of a state

  • vector of integers representing the photon positioning in modes

>>> import exqalibur as xq
>>> empty_sv = xq.StateVector()  # creates an empty state vector
>>> s = xq.FockState("|1,0,1,0>")
>>> sv1 = xq.StateVector(s)  # creates a state vector containing only |1,0,1,0> with amplitude 1
>>> sv2 = xq.StateVector([1, 0, 1, 0])  # same
>>> sv3 = xq.StateVector("|1,0,1,0>")  # same
>>> assert sv1 == sv2 and sv1 == sv3
property is_normalized
Returns:

True if the state vector remained unchanged since the previous normalisation, False otherwise.

keys(self: exqalibur.StateVector) collections.abc.Iterator[exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState]

Iterates over the states composing the state vector without normalising the amplitudes

property m
Returns:

The number of modes in the state

>>> import exqalibur as xq
>>> sv = xq.StateVector("|1,0>")
>>> sv.m
2
measure(self: exqalibur.StateVector, modes: collections.abc.Sequence[SupportsInt]) dict[exqalibur.FockState, tuple[float, exqalibur.StateVector]]

Measure the state on a set of modes, actually collapsing part of the wave function.

>>> import exqalibur as xq
>>> sv = xq.StateVector("|0,1,1>") + xq.StateVector("|1,1,0>")
>>> map_measure_sv = sv.measure([0])
>>> for s, (p, sv) in map_measure_sv.items():
>>>    print(f"For detection {s} with probability {p}, we get the remaining state: {sv}")
For detection |0> with probability 0.4999999999999999, we get the remaining state: |1,1>
For detection |1> with probability 0.4999999999999999, we get the remaining state: |1,0>
Parameters:

modes – A list of modes where the measurement is performed

Returns:

A dictionary of the different possible mesurement results, with their probability and the remaining state vector

property n
Returns:

A set of all different photon count in the states composing the vector

>>> import exqalibur as xq
>>> sv = 0.1*xq.StateVector([0,1]) + 0.2*xq.StateVector([2,0]) + 0.5*xq.StateVector([1,0])
>>> print(sv.n)
{1, 2}
normalize(self: exqalibur.StateVector) None

Normalize the amplitudes

sample(self: exqalibur.StateVector) exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState

Retrieve one sample following the normalised probability amplitudes

Returns:

The sampled state

samples(self: exqalibur.StateVector, count: SupportsInt) list[exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState]

Generate an ordered list of samples following the normalised probability amplitudes

Parameters:

count – The number of expected samples

Returns:

A list of samples of size count

unnormalized_iterator(self: exqalibur.StateVector) collections.abc.Iterator[tuple[exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState, complex]]

Iterates over (state, amplitude) composing the state vector without normalising the amplitudes.

>>> import exqalibur as xq
>>> sv = 0.1*xq.StateVector([0,1]) + 0.2*xq.StateVector([1,0])
>>> for state, amplitude in sv.unnormalized_iterator():
>>>     print(state, amplitude)
|0,1> (0.1+0j)
|1,0> (0.2+0j)

SVDistribution

class exqalibur.SVDistribution

State vector distribution holding pure states (i.e. state vectors). This class is one way of representing a mixed state.

The SVDistribution can be build via any of the following parameters:

Parameters:
  • fs – (optional) build from a single Fock state which gets a probability of 1.

  • sv – (optional) build from a single state vector which gets a probability of 1.

  • bsd – (optional) build from an existing dictionary or distribution where keys are perfect Fock states.

  • svd – (optional) build from an existing dictionary or distribution where keys are state vectors.

add(*args, **kwargs)

Overloaded function.

  1. add(self: exqalibur.SVDistribution, sv: exqalibur.StateVector, value: typing.SupportsFloat) -> None

Increment the probability of a given state. If the state doesn’t exist beforehand, use the given probability. Probabilities that are too low (1e-16) are discarded.

Parameters:
  • sv – State vector

  • value – Probability

  1. add(self: exqalibur.SVDistribution, fs: exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState, value: typing.SupportsFloat) -> None

get(*args, **kwargs)

Overloaded function.

  1. get(self: exqalibur.SVDistribution, sv: exqalibur.StateVector, default: typing.SupportsFloat | None = None) -> float | None

  2. get(self: exqalibur.SVDistribution, sv: exqalibur.StateVector, default: typing.SupportsFloat | None = None) -> float | None

Retrieve the probability for a given state, with a default value if the state doesn’t exist in the distribution.

Parameters:
  • sv – State to search

  • default – Default probability value (defaults to None)

Returns:

The state probability, if found, the default value otherwise

items(self: exqalibur.SVDistribution) collections.abc.Iterator[tuple[exqalibur.StateVector, float]]

Iterate over tuples of (state vector, probability) contained in the distribution

keys(self: exqalibur.SVDistribution) collections.abc.Iterator[exqalibur.StateVector]

Iterate over state vectors contained in the distribution

static list_tensor_product(distributions: collections.abc.Sequence[exqalibur.SVDistribution], prob_threshold: SupportsFloat = 0.0) exqalibur.SVDistribution

Compute a series of tensor product between distributions

Parameters:
  • distributions – List of distributions

  • prob_threshold – Threshold under which probabilities are discarded during the tensor product (defaults to 0., i.e. no probability is discarded).

Returns:

The result of the tensor product

property m
Returns:

The number of modes of all states in the distribution

property n_max
Returns:

The maximum number of photons in any state of the distribution

normalize(self: exqalibur.SVDistribution) None

Normalize the probabilities in place (i.e. their sum is equal to 1)

sample(self: exqalibur.SVDistribution, count: SupportsInt, non_null: bool = True) list[exqalibur.StateVector]

Generate an ordered list of state vectors from the distribution.

Parameters:
  • count – Number of expected state vectors

  • non_null – If True avoids returning in void state (i.e. state containing 0 photon). Defaults to True.

Returns:

A list of state vectors following the probability distribution

static tensor_product(svd1: exqalibur.SVDistribution, svd2: exqalibur.SVDistribution, prob_threshold: SupportsFloat = 0.0) exqalibur.SVDistribution

Compute the tensor product of two distributions

Parameters:
  • svd1 – Left hand-side distribution

  • svd2 – Right hand-side distribution

  • prob_threshold – Threshold under which probabilities are discarded during the tensor product (defaults to 0., i.e. no probability is discarded).

Returns:

The result of the tensor product

values(self: exqalibur.SVDistribution) collections.abc.Iterator[float]

Iterate over the probabilities contained in the distribution