States#

In Qhronology, quantum states are both represented and constructed natively in the computational basis (also known as the standard or \(z\)-basis). This is achieved primarily through the use of the QuantumState class,

from qhronology.quantum.states import QuantumState

which itself relies chiefly on functionality provided by the matrix-generating quantum_state() function:

from qhronology.mechanics.matrices import quantum_state

Characterization of quantum states is facilitated by two properties: form distinguishes between states which are "vector" or "matrix", while kind distinguishes those which are "pure" or "mixed". Of the four combinations (pairs) of these properties, all are valid except for the pairing of "vector" and "mixed". Therefore, to expedite and simplify state instantiation, the following subclasses of the base class QuantumState are provided:

from qhronology.quantum.states import VectorState, MatrixState, PureState, MixedState

These classes are specialized (or restrictive) subclasses, meaning that they do not extend the base class in any way, and instead merely constrain its functionality in order to enforce the desired behaviour. They therefore allow for quantum state objects to be initialized in ways that are more concise than the general QuantumState class.

Main class#

class QuantumState(
spec: MutableDenseMatrix | ndarray | list[list[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str]] | list[tuple[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str, int | list[int]]],
form: str | None = None,
kind: str | None = None,
dim: int | None = None,
symbols: dict[MatrixSymbol | MatrixElement | Symbol | str, dict[str, Any]] | None = None,
conditions: list[tuple[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str, Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str]] | None = None,
conjugate: bool | None = None,
norm: bool | Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str | None = None,
label: str | None = None,
notation: str | None = None,
family: str | None = None,
debug: bool | None = None,
)[source]#

Bases: QuantitiesMixin, OperationsMixin, QuantumObject

A class for creating quantum states and storing their metadata.

Instances provide complete descriptions of both vector and matrix quantum states, along with various associated attributes (such as mathematical conditions, including normalization). The internal state of the class is expressly mutable, and a selection of useful methods are provided with which the state can be manipulated and otherwise transformed in various quantum-mechanically significant ways. This includes:

  • normalization

  • (partial) trace

  • measurement

  • postselection

Parameters:
  • spec

    The specification of the quantum state. Provides a complete description of the state’s values in a standard dim-dimensional basis. Can be one of:

    • a SymPy matrix (mat)

    • a NumPy array (arr)

    • a list of lists of numerical, symbolic, or string expressions (that collectively specify a matrix) (list[list[num | sym | str]])

    • a list of 2-tuples of numerical, symbolic, or string coefficients and their respective number-basis specifications (list[tuple[num | sym | str, int | list[int]]])

  • form (str) – A string specifying the form for the quantum state to take. Can be either of "vector" or "matrix". Defaults to "matrix".

  • kind (str) – A string specifying the kind for the quantum state to take. Can be either of "mixed" or "pure". Defaults to "mixed".

  • dim (int) – The dimensionality of the quantum state’s Hilbert space. Must be a non-negative integer. Defaults to 2.

  • symbols (dict[sym | str, dict[str, Any]]) – A dictionary in which the keys are individual symbols (usually found within the state specification spec) and the values are dictionaries of their respective SymPy keyword-argument assumptions. Defaults to {}.

  • conditions (list[tuple[num | sym | str, num | sym | str]]) – A list of \(2\)-tuples of conditions to be applied to the state. All instances of the expression in each tuple’s first element are replaced by the expression in the respective second element. This uses the same format as the SymPy subs() method. The order in which they are applied is simply their order in the list. Defaults to [].

  • conjugate (bool) – Whether to perform Hermitian conjugation on the state when it is called. Defaults to False.

  • norm (bool | num | sym | str) – The value to which the state is normalized. If True, normalizes to a value of \(1\). If False, does not normalize. Defaults to False.

  • label (str) – The unformatted string used to represent the state in mathematical expressions. Must have a non-zero length. Defaults to "ρ" (if form == "matrix") or "ψ" (if form == "vector").

  • notation (str) – The formatted string used to represent the state in mathematical expressions. When not None, overrides the value passed to label. Must have a non-zero length. Not intended to be set by the user in most cases. Defaults to None.

  • family (str) – A string expressing the kind of block element for which the object is to be visualized. Not intended to be set by the user. Defaults to "LSTICK".

  • debug (bool) – Whether to print the internal state (held in matrix) on change. Defaults to False.

Examples

>>> qubit_vector = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", "1")],
...     norm=1,
...     label="ψ",
... )
>>> qubit_vector.output()
Matrix([
[a],
[b]])
>>> qubit_vector.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> qubit_vector.diagram()
>>> qutrit_vector = QuantumState(
...     spec=[("a", [0]), ("b", [1]), ("c", [2])],
...     form="vector",
...     dim=3,
...     symbols={
...         "a": {"complex": True},
...         "b": {"complex": True},
...         "c": {"complex": True},
...     },
...     conditions=[("a*conjugate(a) + b*conjugate(b) + c*conjugate(c)", "1")],
...     norm=1,
...     label="φ",
... )
>>> qutrit_vector.output()
Matrix([
[a],
[b],
[c]])
>>> qutrit_vector.print()
|φ⟩ = a|0⟩ + b|1⟩ + c|2⟩
>>> qutrit_vector.diagram()
>>> qubit_pure = QuantumState(
...     spec=[("α", [0]), ("β", [1])],
...     form="matrix",
...     kind="pure",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("α*conjugate(α) + β*conjugate(β)", 1)],
...     norm=1,
...     label="ξ",
... )
>>> qubit_pure.output()
Matrix([
[α*conjugate(α), α*conjugate(β)],
[β*conjugate(α), β*conjugate(β)]])
>>> qubit_pure.print()
|ξ⟩⟨ξ| = α*conjugate(α)|0⟩⟨0| + α*conjugate(β)|0⟩⟨1| + β*conjugate(α)|1⟩⟨0| + β*conjugate(β)|1⟩⟨1|
>>> qubit_pure.diagram()
>>> qubit_mixed = QuantumState(
...     spec=[("p", [0]), ("1 - p", [1])],
...     form="matrix",
...     kind="mixed",
...     symbols={"p": {"real": True}},
...     norm=1,
...     label="τ",
... )
>>> qubit_mixed.output()
Matrix([
[p,     0],
[0, 1 - p]])
>>> qubit_mixed.print()
τ = p|0⟩⟨0| + (1 - p)|1⟩⟨1|
>>> qubit_mixed.diagram()
>>> custom_vector = QuantumState(spec=[["μ"], ["ν"]], kind="mixed", label="η")
>>> custom_vector.output()
Matrix([
[μ*conjugate(μ), μ*conjugate(ν)],
[ν*conjugate(μ), ν*conjugate(ν)]])
>>> custom_vector.print()
η = μ*conjugate(μ)|0⟩⟨0| + μ*conjugate(ν)|0⟩⟨1| + ν*conjugate(μ)|1⟩⟨0| + ν*conjugate(ν)|1⟩⟨1|
>>> custom_vector.diagram()
>>> custom_matrix = QuantumState(spec=[["w", "x"], ["y", "z"]], kind="mixed", label="ω")
>>> custom_matrix.output()
Matrix([
[w, x],
[y, z]])
>>> custom_matrix.print()
ω = w|0⟩⟨0| + x|0⟩⟨1| + y|1⟩⟨0| + z|1⟩⟨1|
>>> custom_matrix.diagram()
>>> bell_state = QuantumState(spec=[(1, [0, 0]), (1, [1, 1])], form="vector", norm=1, label="Φ")
>>> bell_state.output()
Matrix([
[sqrt(2)/2],
[        0],
[        0],
[sqrt(2)/2]])
>>> bell_state.print()
|Φ⟩ = sqrt(2)/2|0,0⟩ + sqrt(2)/2|1,1⟩
>>> bell_state.diagram()
>>> tripartite_zero = QuantumState(spec=[(1, [0, 0, 0])], form="vector", label="0,0,0")
>>> tripartite_zero.output()
Matrix([
[1],
[0],
[0],
[0],
[0],
[0],
[0],
[0]])
>>> tripartite_zero.print()
|0,0,0⟩ = |0,0,0⟩
>>> tripartite_zero.diagram()

Constructor argument properties#

property QuantumState.spec: MutableDenseMatrix | ndarray | list[list[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str]] | list[tuple[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str, int | list[int]]]#

The matrix representation of the quantum state. Provides a complete description of the state in a standard dim-dimensional basis.

property QuantumState.form: str#

The form of the object. Can be either of "vector" or "matrix". Only QuantumState objects can be "vector".

property QuantumState.kind: str#

The kind of quantum state. Can be either of "mixed" or "pure".

property QuantumState.dim: int#

The dimensionality of the quantum object. Must be a non-negative integer.

property QuantumState.symbols: dict[MatrixSymbol | MatrixElement | Symbol | str, dict[str, Any]]#

A dictionary in which the keys are individual symbols (contained within the object’s matrix representation) and the values are dictionaries of their respective SymPy keyword-argument assumptions (“predicates”). A full list of currently supported predicates, and their defaults, is as follows:

  • "algebraic": True

  • "commutative": True

  • "complex": True

  • "extended_negative": False

  • "extended_nonnegative": True

  • "extended_nonpositive": False

  • "extended_nonzero": True

  • "extended_positive": True

  • "extended_real": True

  • "finite": True

  • "hermitian": True

  • "imaginary": False

  • "infinite": False

  • "integer": True

  • "irrational": False

  • "negative": False

  • "noninteger": False

  • "nonnegative": True

  • "nonpositive": False

  • "nonzero": True

  • "positive": True

  • "rational": True

  • "real": True

  • "transcendental": False

  • "zero": False

property QuantumState.conditions: list[tuple[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str, Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str]]#

A list of \(2\)-tuples of conditions to be applied to the object’s matrix representation.

property QuantumState.norm: bool | Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str#

The value to which the state is normalized. If True, normalizes to a value of \(1\). If False, does not normalize.

Examples of valid values include:

  • 1/2

  • "1/d"

  • "a*conjugate(a) + b*conjugate(b)"

property QuantumState.conjugate: bool#

Whether to perform Hermitian conjugation on the object when it is called.

property QuantumState.label: str#

The unformatted string used to represent the object in mathematical expressions. Must have a non-zero length.

property QuantumState.notation: str#

The formatted string used to represent the object in mathematical expressions. When set, overrides the value of the label property. Must have a non-zero length. Not intended to be set by the user in most cases.

property QuantumState.family: str#

The code of the block element that the object is to be visualized as. Not intended to be set by the user.

property QuantumState.debug: bool#

Whether to print the object’s matrix representation (stored in the matrix property) on mutation.

Read-only properties#

property QuantumState.systems: list[int]#

Read-only property containing an ordered list of the numerical indices of the object’s systems.

property QuantumState.num_systems: int#

Read-only property containing the number of systems which the state spans. The current value is calculated from the state’s matrix representation and its dimensionality dim.

property QuantumState.is_vector: bool#

Test for whether the object is a vector. Returns True if so, otherwise False.

property QuantumState.matrix: MutableDenseMatrix#

The matrix representation of the object.

Considered read-only (this is strictly enforced by QuantumGate class and its derivatives), though can be (indirectly) mutated by some derived classes (such as QuantumState). Not intended to be set directly by the user.

Methods#

QuantumState.output(
conditions: list[tuple[Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str, Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str]] | None = None,
simplify: bool | None = None,
conjugate: bool | None = None,
norm: bool | Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str | None = None,
) MutableDenseMatrix[source]#

Construct the state’s matrix representation, perform any necessary transformations on it, and return it.

Parameters:
  • conditions (list[tuple[num | sym | str, num | sym | str]]) – Algebraic conditions to be applied to the state. Defaults to the value of self.conditions.

  • simplify (bool) – Whether to perform algebraic simplification on the state. Defaults to False.

  • conjugate (bool) – Whether to perform Hermitian conjugation on the state. If False, does not conjugate. Defaults to the value of self.conjugate.

  • norm (bool | num | sym | str) – The value to which the state is normalized. If False, does not normalize. Defaults to the value of self.norm.

Returns:

mat – The matrix or vector representation of the quantum state.

QuantumState.print(
delimiter: str | None = None,
product: bool | None = None,
return_string: bool | None = None,
) None | str#

Print or return a mathematical expression of the quantum object as a string.

Parameters:
  • delimiter (str) – A string containing the character(s) with which to delimit (i.e., separate) the values in the ket and/or bra terms in the mathematical expression. Defaults to ",".

  • product (bool) – Whether to represent the mathematical expression using tensor products. Only applies if the object is a multipartite composition. Defaults to False.

  • return_string (bool) – Whether to return the mathematical expression as a string. Defaults to False.

Returns:

  • None – Returned only if return_string is False.

  • str – The constructed mathematical expression. Returned only if return_string is True.

Examples

>>> vector_state = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> vector_state.output()
Matrix([
[a],
[b]])
>>> vector_state.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> mixed_matrix_state = QuantumState(
...     spec=[("a", [0]), ("b", [1])], form="matrix", label="ρ"
... )
>>> mixed_matrix_state.output()
Matrix([
[a, 0],
[0, b]])
>>> mixed_matrix_state.print()
ρ = a|0⟩⟨0| + b|1⟩⟨1|
>>> pure_matrix_state = QuantumState(
...     spec=[("a", [0]), ("b", [1])], form="matrix", kind="pure", label="ψ"
... )
>>> pure_matrix_state.output()
Matrix([
[a*conjugate(a), a*conjugate(b)],
[b*conjugate(a), b*conjugate(b)]])
>>> pure_matrix_state.print()
|ψ⟩⟨ψ| = a*conjugate(a)|0⟩⟨0| + a*conjugate(b)|0⟩⟨1| + b*conjugate(a)|1⟩⟨0| + b*conjugate(b)|1⟩⟨1|
>>> composite_vector_state = QuantumState(
...     spec=[("a", [0, 0]), ("b", [1, 1])], form="vector", label="ψ"
... )
>>> composite_vector_state.output()
Matrix([
[a],
[0],
[0],
[b]])
>>> composite_vector_state.print()
|ψ⟩ = a|0,0⟩ + b|1,1⟩
>>> composite_vector_state.print(delimiter="")
|ψ⟩ = a|00⟩ + b|11⟩
>>> composite_vector_state.print(product=True)
|ψ⟩ = a|0⟩⊗|0⟩ + b|1⟩⊗|1⟩
>>> composite_mixed_matrix_state = QuantumState(
...     spec=[("a", [0, 0]), ("b", [1, 1])], form="matrix", label="ρ"
... )
>>> composite_mixed_matrix_state.output()
Matrix([
[a, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, b]])
>>> composite_mixed_matrix_state.print()
ρ = a|0,0⟩⟨0,0| + b|1,1⟩⟨1,1|
>>> composite_mixed_matrix_state.print(delimiter="")
ρ = a|00⟩⟨00| + b|11⟩⟨11|
>>> composite_mixed_matrix_state.print(product=True)
ρ = a|0⟩⟨0|⊗|0⟩⟨0| + b|1⟩⟨1|⊗|1⟩⟨1|
>>> composite_pure_matrix_state = QuantumState(
...     spec=[("a", [0, 0]), ("b", [1, 1])], form="matrix", kind="pure", label="ψ"
... )
>>> composite_pure_matrix_state.output()
Matrix([
[a*conjugate(a), 0, 0, a*conjugate(b)],
[             0, 0, 0,              0],
[             0, 0, 0,              0],
[b*conjugate(a), 0, 0, b*conjugate(b)]])
>>> composite_pure_matrix_state.print()
|ψ⟩⟨ψ| = a*conjugate(a)|0,0⟩⟨0,0| + a*conjugate(b)|0,0⟩⟨1,1| + b*conjugate(a)|1,1⟩⟨0,0| + b*conjugate(b)|1,1⟩⟨1,1|
>>> composite_pure_matrix_state.print(delimiter="")
|ψ⟩⟨ψ| = a*conjugate(a)|00⟩⟨00| + a*conjugate(b)|00⟩⟨11| + b*conjugate(a)|11⟩⟨00| + b*conjugate(b)|11⟩⟨11|
>>> composite_pure_matrix_state.print(product=True)
|ψ⟩⟨ψ| = a*conjugate(a)|0⟩⟨0|⊗|0⟩⟨0| + a*conjugate(b)|0⟩⟨1|⊗|0⟩⟨1| + b*conjugate(a)|1⟩⟨0|⊗|1⟩⟨0| + b*conjugate(b)|1⟩⟨1|⊗|1⟩⟨1|
QuantumState.diagram(
pad: tuple[int, int] | None = None,
sep: tuple[int, int] | None = None,
style: str | None = None,
return_string: bool | None = None,
) None | str#

Print or return a circuit diagram of the quantum object as a multiline string.

Parameters:
  • pad (tuple[int, int]) – A two-tuple describing the horizontal and vertical interior paddings between the content at the centre of the object (e.g., its label) and its outer edge (e.g., block border). Both integers must be non-negative. Defaults to (0, 0).

  • sep (tuple[int, int]) – A two-tuple describing the horizontal and vertical exterior separation distances at the object’s edges. Both integers must be non-negative. Defaults to (1, 1).

  • style (str) – A string specifying the style for the circuit visualization to take. Can be any of "ascii", "unicode", or "unicode_alt". Defaults to "unicode".

  • return_string (bool) – Whether to return the assembled diagram as a multiline string. Defaults to False.

Returns:

  • None – Returned only if return_string is False.

  • str – The rendered circuit diagram. Returned only if return_string is True.

Note

The quality of the visualization depends greatly on the output’s configuration. For best results, the terminal should have a monospace font with good Unicode coverage.

Examples

For usage examples, please see those of the QuantumState class itself.

Operations#

All of these methods (except for reset()) are inherited from OperationsMixin.

QuantumState.reset()[source]#

Reset the quantum state’s internal matrix state (specifically its matrix property) to its original value at instantiation.

Note

This reset only the matrix property of the instance. All other attributes and properties are unchanged.

QuantumState.densify()#

Convert the state to its equivalent (density) matrix representation.

States that are already in density matrix form are unmodified.

Examples

>>> psi = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> psi.densify()
>>> psi.print()
|ψ⟩⟨ψ| = a*conjugate(a)|0⟩⟨0| + a*conjugate(b)|0⟩⟨1| + b*conjugate(a)|1⟩⟨0| + b*conjugate(b)|1⟩⟨1|
QuantumState.dagger()#

Perform conjugate transposition on the state.

Examples

>>> psi = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> psi.dagger()
>>> psi.print()
⟨ψ| = conjugate(a)⟨0| + conjugate(b)⟨1|
QuantumState.simplify(
comprehensive: bool | None = None,
)#

Apply a forced simplification to the state using the values of its symbols and conditions properties.

Useful if intermediate simplification is required during a sequence of mutating operations in order to process the state into a more desirable form.

Parameters:

comprehensive (bool) – Whether the simplifying algorithm should use a relatively efficient subset of simplifying operations (False), or alternatively use a larger, more powerful (but slower) set (True). Defaults to False.

Note

If comprehensive is True, the simplification algorithm will likely take far longer to execute than if comprehensive were False.

Examples

>>> matrix = sp.Matrix(
...     [
...         ["(a**2 - 1)/(a - 1) - 1", "log(cos(b) + I*sin(b))/I"],
...         ["acos((exp(I*c) + exp(-I*c))/2)", "d**log(E*(sin(d)**2 + cos(d)**2))"],
...     ]
... )
>>> rho = QuantumState(spec=matrix, form="matrix", label="ρ")
>>> rho.print()
ρ = (-1 + (a**2 - 1)/(a - 1))|0⟩⟨0| + -I*log(I*sin(b) + cos(b))|0⟩⟨1| + acos(exp(I*c)/2 + exp(-I*c)/2)|1⟩⟨0| + d**log(E*(sin(d)**2 + cos(d)**2))|1⟩⟨1|
>>> rho.simplify()
>>> rho.print()
ρ = a|0⟩⟨0| + b|0⟩⟨1| + c|1⟩⟨0| + d|1⟩⟨1|
QuantumState.apply(
function: Callable,
arguments: dict[str, Any] | None = None,
)#

Apply a Python function (function) to the state.

Useful when used with SymPy’s symbolic-manipulation functions, such as:

  • simplify()

  • expand()

  • factor()

  • collect()

  • cancel()

  • apart()

More can be found at:

Parameters:
  • function (Callable) – A Python function. Its first non-keyword argument must be able to take a mathematical expression or a matrix/array of such types.

  • arguments (dict[str, str]) – A dictionary of keyword arguments (both keys and values as strings) to pass to the function call. Defaults to {}.

Examples

>>> psi = QuantumState(
...     spec=[("a*b + b*c + c*a", [0]), ("x*y + y*z + z*x", [1])], form="vector", label="ψ"
... )
>>> psi.print()
|ψ⟩ = (a*b + a*c + b*c)|0⟩ + (x*y + x*z + y*z)|1⟩
>>> psi.apply(sp.collect, {"syms": ["a", "x"]})
>>> psi.print()
|ψ⟩ = (a*(b + c) + b*c)|0⟩ + (x*(y + z) + y*z)|1⟩
>>> psi.apply(sp.expand)
>>> psi.print()
|ψ⟩ = (a*b + a*c + b*c)|0⟩ + (x*y + x*z + y*z)|1⟩
QuantumState.rewrite(
function: Callable,
)#

Rewrite the elements of the state using the given mathematical function (function).

Useful when used with SymPy’s mathematical functions, such as:

  • exp()

  • log()

  • sin()

  • cos()

Parameters:

function (Callable) – A SymPy mathematical function.

Examples

>>> psi = QuantumState(spec=[("cos(θ)", [0]), ("sin(θ)", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = cos(θ)|0⟩ + sin(θ)|1⟩
>>> psi.rewrite(sp.exp)
>>> psi.print()
|ψ⟩ = (exp(I*θ)/2 + exp(-I*θ)/2)|0⟩ + -I*(exp(I*θ) - exp(-I*θ))/2|1⟩
QuantumState.normalize(
norm: Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str | None = None,
)#

Perform a forced (re)normalization on the state to the value specified (norm).

Useful when applied to the current quantum state both before and after mutating operations, prior to any simplification (such as renormalization) performed on the processed output (obtained via the state() method).

Parameters:

norm (num | sym | str) – The value to which the state is normalized. Defaults to 1.

Examples

>>> identity = QuantumState(spec=[("1", [0]), ("1", [1])], label="I")
>>> identity.print()
I = |0⟩⟨0| + |1⟩⟨1|
>>> identity.normalize()
>>> identity.print()
I = 1/2|0⟩⟨0| + 1/2|1⟩⟨1|
>>> psi = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> psi.normalize()
>>> psi.print()
|ψ⟩ = a/sqrt(a*conjugate(a) + b*conjugate(b))|0⟩ + b/sqrt(a*conjugate(a) + b*conjugate(b))|1⟩
QuantumState.coefficient(
scalar: Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol | str | None = None,
)#

Multiply the state by a scalar value (scalar).

Can be useful to manually (re)normalize states, or introduce a phase factor.

Parameters:

scalar (num | sym | str) – The value by which the state is multiplied. Defaults to 1.

Examples

>>> psi = QuantumState(spec=[("1", [0]), ("1", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = |0⟩ + |1⟩
>>> psi.coefficient(1 / sp.sqrt(2))
>>> psi.print()
|ψ⟩ = sqrt(2)/2|0⟩ + sqrt(2)/2|1⟩
QuantumState.partial_trace(
targets: int | list[int] | None = None,
discard: bool | None = None,
optimize: bool | None = None,
)#

Perform a partial trace operation on the state.

Performs a total trace if targets is unspecified.

Parameters:
  • targets (int | list[int]) – The numerical index/indices of the subsystem(s) to be partially traced over. Indexing begins at 0. Defaults to [].

  • discard (bool) – Whether the systems corresponding to the indices given in targets are to be discarded (True) or kept (False). Defaults to True.

  • optimize (bool) – Whether to optimize the partial trace implementation’s algorithm. Can greatly increase the computational efficiency at the cost of a larger memory footprint during computation. Defaults to True.

Examples

>>> psi = QuantumState(
...     spec=[("a*u", [0, 0]), ("b*u", [1, 0]), ("a*v", [0, 1]), ("b*v", [1, 1])],
...     form="vector",
...     conditions=[
...         ("a*conjugate(a) + b*conjugate(b)", "1"),
...         ("u*conjugate(u) + v*conjugate(v)", "1"),
...     ],
...     label="Ψ",
... )
>>> psi.print()
|Ψ⟩ = a*u|0,0⟩ + a*v|0,1⟩ + b*u|1,0⟩ + b*v|1,1⟩
>>> psi.partial_trace([1])
>>> psi.simplify()
>>> psi.notation = "ρ"
>>> psi.print()
ρ = a*conjugate(a)|0⟩⟨0| + a*conjugate(b)|0⟩⟨1| + b*conjugate(a)|1⟩⟨0| + b*conjugate(b)|1⟩⟨1|
>>> bell = QuantumState(
...     spec=[("1", [0, 0]), ("1", [1, 1])], form="vector", norm=1, label="Φ"
... )
>>> bell.print()
|Φ⟩ = sqrt(2)/2|0,0⟩ + sqrt(2)/2|1,1⟩
>>> bell.partial_trace([0])
>>> bell.notation = "ρ"
>>> bell.print()
ρ = 1/2|0⟩⟨0| + 1/2|1⟩⟨1|
QuantumState.measure(
operators: list[mat | arr | QuantumObject],
targets: int | list[int] | None = None,
observable: bool | None = None,
statistics: bool | None = None,
) None | list[num | sym]#

Perform a quantum measurement on one or more systems (indicated in targets) of the state.

This method has two main modes of operation:

  • When statistics is True, the (reduced) state (\(\op{\rho}\)) (residing on the systems indicated in targets) is measured and the set of resulting statistics is returned. This takes the form of an ordered list of values \(\{p_i\}_i\) associated with each given operator, where:

    • \(p_i = \trace[\Kraus_i^\dagger \Kraus_i \op{\rho}]\) (measurement probabilities) when observable is False (operators is a list of Kraus operators or projectors \(\Kraus_i\))

    • \(p_i = \trace[\Observable_i \op{\rho}]\) (expectation values) when observable is True (operators is a list of observables \(\Observable_i\))

  • When statistics is False, the (reduced) state (\(\op{\rho}\)) (residing on the systems indicated in targets) is measured and mutated it according to its predicted post-measurement form (i.e., the sum of all possible measurement outcomes). This yields the transformed states:

    • When observable is False:

    (275)#\[\op{\rho}^\prime = \sum_i \Kraus_i \op{\rho} \Kraus_i^\dagger.\]
    • When observable is True:

    (276)#\[\op{\rho}^\prime = \sum_i \trace[\Observable_i \op{\rho}]\Observable_i.\]

In the case where operators contains only a single item (\(\Kraus\)) and the current state (\(\ket{\psi}\)) is a vector form, the transformation of the state is in accordance with the rule

(277)#\[\ket{\psi^\prime} = \frac{\Kraus \ket{\psi}} {\sqrt{\bra{\psi} \Kraus^\dagger \Kraus \ket{\psi}}}\]

when observable is False. In all other mutation cases, the post-measurement state is a matrix, even if the pre-measurement state was a vector.

The items in the list operators can also be vectors (e.g., \(\ket{\xi_i}\)), in which case each is converted into its corresponding operator matrix representation (e.g., \(\ket{\xi_i}\bra{\xi_i}\)) prior to any measurements.

Parameters:
  • operators (list[mat | arr | QuantumObject]) – The operator(s) with which to perform the measurement. These would typically be a (complete) set of Kraus operators forming a POVM, a (complete) set of (orthogonal) projectors forming a PVM, or a set of observables constituting a complete basis for the relevant state space.

  • targets (int | list[int]) – The numerical indices of the subsystem(s) to be measured. They must be consecutive, and their number must match the number of systems spanned by all given operators. Indexing begins at 0. All other systems are discarded (traced over) in the course of performing the measurement. Defaults to the value of self.systems.

  • observable (bool) – Whether to treat the items in operators as observables instead of Kraus operators or projectors. Defaults to False.

  • statistics (bool) – Whether to return a list of probabilities (True) or mutate the state into a post-measurement probabilistic sum of all outcomes (False). Defaults to False.

Returns:

  • None – Returned only if statistics is False.

  • num | sym | list[num | sym] – A list of probabilities corresponding to each operator given in operators. Returned only if statistics is True.

Note

This method does not check for validity of supplied POVMs or the completeness of sets of observables, nor does it renormalize the post-measurement state.

Examples

>>> psi = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> I = Pauli(index=0)
>>> X = Pauli(index=1)
>>> Y = Pauli(index=2)
>>> Z = Pauli(index=3)
>>> psi.measure(operators=[I, X, Y, Z], observable=True, statistics=True)
[a*conjugate(a) + b*conjugate(b),
 a*conjugate(b) + b*conjugate(a),
 I*(a*conjugate(b) - b*conjugate(a)),
 a*conjugate(a) - b*conjugate(b)]
>>> psi.measure(operators=[I, X, Y, Z], observable=True, statistics=False)
>>> psi.simplify()
>>> psi.coefficient(sp.Rational(1, 2))
>>> psi.print()
|ψ⟩⟨ψ| = a*conjugate(a)|0⟩⟨0| + a*conjugate(b)|0⟩⟨1| + b*conjugate(a)|1⟩⟨0| + b*conjugate(b)|1⟩⟨1|
>>> from qhronology.mechanics.matrices import ket
>>> psi = QuantumState(spec=[("a", [0]), ("b", [1])], form="vector", label="ψ")
>>> psi.print()
|ψ⟩ = a|0⟩ + b|1⟩
>>> psi.measure(operators=[ket(0), ket(1)], observable=False, statistics=True)
[a*conjugate(a), b*conjugate(b)]
>>> psi.measure(operators=[ket(0), ket(1)], observable=False, statistics=False)
>>> psi.notation = "ρ"
>>> psi.print()
ρ = a*conjugate(a)|0⟩⟨0| + b*conjugate(b)|1⟩⟨1|
QuantumState.postselect(
postselections: list[tuple[mat | arr | QuantumObject, int]],
)#

Perform postselection on the state against the operators(s) specified in postselections.

The postselections can be given in either vector or matrix form. For the former, the transformation of the vector state \(\ket{\Psi}\) follows the standard rule

(278)#\[\ket{\Psi^\prime} = \braket{\phi}{\Psi}\]

where \(\ket{\phi}\) is the postselection vector. In the case of a matrix form \(\op{\omega}\), the notion of postselection of a density matrix state \(\op{\rho}\) naturally generalizes to

(279)#\[\op{\rho}^\prime = \trace_{\{i\}}[\op{\omega} \op{\rho}]\]

where \(\{i\}\) is the set of indices corresponding to the subsystem(s) upon which the postselection is performed.

If multiple postselections are supplied, the state will be successively postselected in the order in which they are given. If a vector state is postselected against a matrix form, it will automatically be transformed into its matrix form as necessary.

Parameters:

postselections (list[tuple[mat | arr | QuantumObject, int]]) – A list of 2-tuples of vectors or matrix operators paired with the first (smallest) index of their postselection target systems.

Note

Any classes given in postselections that are derived from the QuantumObject base class (such as QuantumState and QuantumGate) will have their symbols and conditions properties merged into the current QuantumState instance.

Examples

>>> psi = QuantumState(spec=[("a", [0, 0]), ("b", [1, 1])], form="vector", label="Ψ")
>>> phi = QuantumState(spec=[("c", [0]), ("d", [1])], form="vector", label="φ")
>>> psi.print()
|Ψ⟩ = a|0,0⟩ + b|1,1⟩
>>> phi.print()
|φ⟩ = c|0⟩ + d|1⟩
>>> psi.postselect([(phi, [0])])
>>> psi.label = "Ψ'"
>>> psi.print()
|Ψ'⟩ = a*conjugate(c)|0⟩ + b*conjugate(d)|1⟩
>>> from qhronology.mechanics.matrices import ket
>>> psi = QuantumState(spec=[("a", [0, 0]), ("b", [1, 1])], form="vector", label="Ψ")
>>> psi.print()
|Ψ⟩ = a|0,0⟩ + b|1,1⟩
>>> psi.label = "Ψ'"
>>> psi.postselect([(ket(0), [0])])
>>> psi.print()
|Ψ'⟩ = a|0⟩
>>> psi.reset()
>>> psi.postselect([(ket(1), [0])])
>>> psi.print()
|Ψ'⟩ = b|1⟩

Quantities#

All of these methods are inherited from QuantitiesMixin.

QuantumState.trace() Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol#

Calculate the (complete) trace \(\trace[\op{\rho}]\) of the internal state (\(\op{\rho}\)).

Returns:

num | sym – The trace of the internal state.

Examples

>>> state = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state.trace()
1
>>> state = QuantumState(
...     spec=[(1, [0]), (1, [1])],
...     kind="mixed",
...     symbols={"d": {"real": True}},
...     norm="1/d",
... )
>>> state.trace()
1/d
QuantumState.purity() Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol#

Calculate the purity (\(\Purity\)) of the internal state (\(\op{\rho}\)):

(280)#\[\Purity(\op{\rho}) = \trace[\op{\rho}^2].\]
Returns:

num | sym – The purity of the internal state.

Examples

>>> state = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state.purity()
1
>>> state = QuantumState(spec=[("p", [0]), ("1 - p", [1])], kind="mixed", norm=1)
>>> state.purity()
p**2 + (1 - p)**2
QuantumState.distance(
state: mat | QuantumObject,
) num | sym#

Calculate the trace distance (\(\TraceDistance\)) between the internal state (\(\op{\rho}\)) and the given state (\(\op{\tau}\)):

(281)#\[\TraceDistance(\op{\rho}, \op{\tau}) = \frac{1}{2}\trace{\abs{\op{\rho} - \op{\tau}}}.\]
Parameters:

state (mat | QuantumObject) – The given state.

Returns:

num | sym – The trace distance between the internal state and state.

Examples

>>> state_A = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("c", [0]), ("d", [1])],
...     form="vector",
...     symbols={"c": {"complex": True}, "d": {"complex": True}},
...     conditions=[("c*conjugate(c) + d*conjugate(d)", 1)],
...     norm=1,
... )
>>> state_A.distance(state_A)
0
>>> state_B.distance(state_B)
0
>>> state_A.distance(state_B)
sqrt((a*conjugate(b) - c*conjugate(d))*(b*conjugate(a) - d*conjugate(c)) + (b*conjugate(b) - d*conjugate(d))**2)/2 + sqrt((a*conjugate(a) - c*conjugate(c))**2 + (a*conjugate(b) - c*conjugate(d))*(b*conjugate(a) - d*conjugate(c)))/2
>>> state_A = QuantumState(
...     spec=[("p", [0]), ("1 - p", [1])],
...     kind="mixed",
...     symbols={"p": {"positive": True}},
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("q", [0]), ("1 - q", [1])],
...     kind="mixed",
...     symbols={"q": {"positive": True}},
...     norm=1,
... )
>>> state_A.distance(state_B)
Abs(p - q)
>>> plus_state = QuantumState(spec=[(1, [0]), (1, [1])], form="vector", norm=1)
>>> minus_state = QuantumState(spec=[(1, [0]), (-1, [1])], form="vector", norm=1)
>>> plus_state.distance(minus_state)
1
QuantumState.fidelity(
state: mat | QuantumObject,
) num | sym#

Calculate the fidelity (\(\Fidelity\)) between the internal state (\(\op{\rho}\)) and the given state (\(\op{\tau}\)):

(282)#\[\Fidelity(\op{\rho}, \op{\tau}) = \left(\trace{\sqrt{\sqrt{\op{\rho}}\,\op{\tau}\sqrt{\op{\rho}}}}\right)^2.\]
Parameters:

state (mat | QuantumObject) – The given state.

Returns:

num | sym – The fidelity between the internal state and state.

Examples

>>> state_A = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("c", [0]), ("d", [1])],
...     form="vector",
...     symbols={"c": {"complex": True}, "d": {"complex": True}},
...     conditions=[("c*conjugate(c) + d*conjugate(d)", 1)],
...     norm=1,
... )
>>> state_A.fidelity(state_A)
1
>>> state_B.fidelity(state_B)
1
>>> state_A.fidelity(state_B)
(a*conjugate(c) + b*conjugate(d))*(c*conjugate(a) + d*conjugate(b))
>>> state_A = QuantumState(
...     spec=[("p", [0]), ("1 - p", [1])],
...     kind="mixed",
...     symbols={"p": {"positive": True}},
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("q", [0]), ("1 - q", [1])],
...     kind="mixed",
...     symbols={"q": {"positive": True}},
...     norm=1,
... )
>>> state_A.fidelity(state_B)
(sqrt(p)*sqrt(q) + sqrt((1 - p)*(1 - q)))**2
>>> plus_state = QuantumState(spec=[(1, [0]), (1, [1])], form="vector", norm=1)
>>> minus_state = QuantumState(spec=[(1, [0]), (-1, [1])], form="vector", norm=1)
>>> plus_state.fidelity(minus_state)
0
QuantumState.entropy(
state: mat | QuantumObject = None,
base: num | None = None,
) num | sym#

Calculate the relative von Neumann entropy (\(\Entropy\)) between the internal state (\(\op{\rho}\)) and the given state (\(\op{\tau}\)):

(283)#\[\Entropy(\op{\rho} \Vert \op{\tau}) = \trace\bigl[\op{\rho} (\log_\Base\op{\rho} - \log_\Base\op{\tau})\bigr].\]

If state is not specified (i.e., None), calculate the ordinary von Neumann entropy of the internal state (\(\op{\rho}\)) instead:

(284)#\[\Entropy(\op{\rho}) = \trace[\op{\rho}\log_\Base\op{\rho}].\]

Here, \(\Base\) represents base, which is the dimensionality of the unit of information with which the entropy is measured.

Parameters:
  • state (mat | QuantumObject) – The given state.

  • base (num) – The dimensionality of the unit of information with which the entropy is measured. Defaults to 2.

Returns:

num | sym – The (relative) von Neumann entropy.

Examples

>>> state_A = QuantumState(
...     spec=[("a", [0]), ("b", [1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("c", [0]), ("d", [1])],
...     form="vector",
...     symbols={"c": {"complex": True}, "d": {"complex": True}},
...     conditions=[("c*conjugate(c) + d*conjugate(d)", 1)],
...     norm=1,
... )
>>> state_A.entropy()
0
>>> state_B.entropy()
0
>>> state_A.entropy(state_B)
0
>>> state_A = QuantumState(
...     spec=[("p", [0]), ("1 - p", [1])],
...     kind="mixed",
...     symbols={"p": {"positive": True}},
...     norm=1,
... )
>>> state_B = QuantumState(
...     spec=[("q", [0]), ("1 - q", [1])],
...     kind="mixed",
...     symbols={"q": {"positive": True}},
...     norm=1,
... )
>>> state_A.entropy()
(-p*log(p) + (p - 1)*log(1 - p))/log(2)
>>> state_B.entropy()
(-q*log(q) + (q - 1)*log(1 - q))/log(2)
>>> state_A.entropy(state_B)
(-(p - 1)*(log(1 - p) - log(1 - q)) + log((p/q)**p))/log(2)
QuantumState.mutual(
systems_A: int | list[int],
systems_B: int | list[int] | None = None,
) Number | generic | Basic | MatrixSymbol | MatrixElement | Symbol#

Calculate the mutual information (\(\MutualInformation\)) between two subsystems systems_A (\(A\)) and systems_B (\(B\)) of the internal state (\(\rho^{A,B}\)):

(285)#\[\MutualInformation(A : B) = \Entropy(\op{\rho}^A) + \Entropy(\op{\rho}^B) - \Entropy(\op{\rho}^{A,B})\]

where \(\Entropy(\op{\rho})\) is the von Neumann entropy of a state \(\op{\rho}\).

Parameters:
  • systems_A (int | list[int]) – The indices of the first subsystem. Defaults to [0].

  • systems_B (int | list[int]) – The indices of the second subsystem. Defaults to the complement of systems_A with respect to the entire composition of subsystems of state.

  • dim (int) – The dimensionality of the composite quantum system (and its subsystems). Must be a non-negative integer. Defaults to 2.

Returns:

num | sym – The mutual information between the subsystems systems_A and systems_B of the internal state.

Examples

>>> state_AB = QuantumState(
...     spec=[("a", [0, 0]), ("b", [1, 1])],
...     form="vector",
...     symbols={"a": {"complex": True}, "b": {"complex": True}},
...     conditions=[("a*conjugate(a) + b*conjugate(b)", 1)],
...     norm=1,
... )
>>> state_AB.mutual([0], [1])
2*(-a*log(a*conjugate(a))*conjugate(a) - b*log(b*conjugate(b))*conjugate(b))/log(2)
>>> state_AB = QuantumState(
...     spec=[("a", [0, 0]), ("b", [1, 1])],
...     kind="mixed",
...     symbols={"a": {"positive": True}, "b": {"positive": True}},
...     conditions=[("a + b", 1)],
...     norm=1,
... )
>>> state_AB.mutual([0], [1])
(-a*log(a) - b*log(b))/log(2)

Subclasses#

class VectorState(
*args,
**kwargs,
)[source]#

A specialized subclass for creating vector states and storing their metadata.

This is a wrapper on the QuantumState class, and so inherits all of its attributes, properties, and methods. The distinction is that this VectorState class fixes both the form and kind arguments to the values of "vector" and "pure", respectively, at instantiation. This means that neither *args or **kwargs must contain these arguments.

Examples

>>> qubit_vector = VectorState(spec=[(1, [0]), (1, [1])], norm=1)
>>> qubit_vector.print()
|ψ⟩ = sqrt(2)/2|0⟩ + sqrt(2)/2|1⟩
class MatrixState(
*args,
**kwargs,
)[source]#

A specialized subclass for creating matrix states and storing their metadata.

This is a wrapper on the QuantumState class, and so inherits all of its attributes, properties, and methods. The distinction is that this MatrixState class fixes the form argument to a value of "matrix" at instantiation. This means that neither *args or **kwargs must contain this argument.

Examples

>>> qubit_matrix_pure = MatrixState(spec=[(1, [0]), (1, [1])], kind="pure", norm=1)
>>> qubit_matrix_pure.print()
|ψ⟩⟨ψ| = 1/2|0⟩⟨0| + 1/2|0⟩⟨1| + 1/2|1⟩⟨0| + 1/2|1⟩⟨1|
>>> qubit_matrix_mixed = MatrixState(spec=[(1, [0]), (1, [1])], kind="mixed", norm=1)
>>> qubit_matrix_mixed.print()
ρ = 1/2|0⟩⟨0| + 1/2|1⟩⟨1|
class PureState(
*args,
**kwargs,
)[source]#

A specialized subclass for creating pure states and storing their metadata.

This is a wrapper on the QuantumState class, and so inherits all of its attributes, properties, and methods. The distinction is that this PureState class fixes the kind argument to a value of "pure" at instantiation. This means that neither *args or **kwargs must contain this argument.

Examples

>>> qubit_pure_vector = PureState(spec=[(1, [0]), (1, [1])], form="vector", norm=1)
>>> qubit_pure_vector.print()
|ψ⟩ = sqrt(2)/2|0⟩ + sqrt(2)/2|1⟩
>>> qubit_pure_matrix = PureState(spec=[(1, [0]), (1, [1])], form="matrix", norm=1)
>>> qubit_pure_matrix.print()
|ψ⟩⟨ψ| = 1/2|0⟩⟨0| + 1/2|0⟩⟨1| + 1/2|1⟩⟨0| + 1/2|1⟩⟨1|
class MixedState(
*args,
**kwargs,
)[source]#

A specialized subclass for creating mixed states and storing their metadata.

This is a wrapper on the QuantumState class, and so inherits all of its attributes, properties, and methods. The distinction is that this MixedState class fixes both the form and kind arguments to the values of "matrix" and "mixed", respectively, at instantiation. This means that neither *args or **kwargs must contain these arguments.

Examples

>>> qubit_mixed = MixedState(spec=[(1, [0]), (1, [1])], norm=1)
>>> qubit_mixed.print()
ρ = 1/2|0⟩⟨0| + 1/2|1⟩⟨1|