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.
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
add(self: exqalibur.SVDistribution, fs: exqalibur.FockState | exqalibur.NoisyFockState | exqalibur.AnnotatedFockState, value: typing.SupportsFloat) -> None
- get(*args, **kwargs)
Overloaded function.
get(self: exqalibur.SVDistribution, sv: exqalibur.StateVector, default: typing.SupportsFloat | None = None) -> float | None
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 toTrue
.
- 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