Operations#

This module provides functions and a mixin for performing quantum operations.

from qhronology.mechanics.operations import densify, columnify, dagger, simplify, apply, rewrite, normalize, coefficient, partial_trace, measure, postselect
from qhronology.mechanics.operations import OperationsMixin

Functions#

densify(
vector: mat | QuantumObject,
) mat[source]#

Convert vector to its corresponding matrix form via the outer product. If vector is a square matrix, it is unmodified.

Parameters:

vector (mat) – The input vector.

Returns:

mat – The outer product of vector with itself.

Examples

>>> vector = sp.Matrix([["a"], ["b"]])
>>> densify(vector)
Matrix([
[a*conjugate(a), a*conjugate(b)],
[b*conjugate(a), b*conjugate(b)]])
columnify(
vector: mat | QuantumObject,
) mat[source]#

Convert vector to its corresponding column vector form via transposition. If vector is a square matrix, it is unmodified.

Parameters:

vector (mat) – The input vector.

Returns:

mat – The column form of vector.

Examples

>>> vector = sp.Matrix([["a", "b"]])
>>> columnify(vector)
Matrix([
[a],
[b]])
dagger(
matrix: mat | QuantumObject,
) mat[source]#

Perform conjugate transposition on matrix.

Parameters:

matrix (mat) – The input matrix.

Returns:

mat – The conjugate transpose of matrix.

Examples

>>> matrix = sp.Matrix([["a"], ["b"]])
>>> dagger(matrix)
Matrix([[conjugate(a), conjugate(b)]])
>>> matrix = sp.Matrix([["a", "b"], ["c", "d"]])
>>> dagger(matrix)
Matrix([
[conjugate(a), conjugate(c)],
[conjugate(b), conjugate(d)]])
simplify(
matrix: mat | QuantumObject,
comprehensive: bool | None = None,
) mat[source]#

Simplify matrix using a powerful (albeit slow) algorithm.

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be simplified.

  • 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.

Returns:

mat – The simplified version of matrix.

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))"],
...     ]
... )
>>> simplify(matrix)
Matrix([
[a, b],
[c, d]])
>>> matrix = sp.Matrix(["2*cos(pi*x/2)**2"])
>>> simplify(matrix, comprehensive=False)
Matrix([[2*cos(pi*x/2)**2]])
>>> simplify(matrix, comprehensive=True)
Matrix([[cos(pi*x) + 1]])
apply(
matrix: mat | QuantumObject,
function: Callable,
arguments: dict[str, Any] | None = None,
) mat[source]#

Apply a Python function (function) to matrix.

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

  • apart()

  • cancel()

  • collect()

  • expand()

  • factor()

  • simplify()

More can be found at:

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be transformed.

  • 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 {}.

Returns:

mat – The transformed version of matrix.

Examples

>>> matrix = sp.Matrix([["(x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1)"]])
>>> apply(matrix, function=sp.cancel)
Matrix([[(y**2 - 2*y*z + z**2)/(x - 1)]])
>>> apply(matrix, function=sp.collect, arguments={"syms": "x"})
Matrix([[(x*(y**2 - 2*y*z + z**2) + y**2 - 2*y*z + z**2)/(x**2 - 1)]])
>>> apply(matrix, function=sp.collect, arguments={"syms": "y"})
Matrix([[(x*z**2 + y**2*(x + 1) + y*(-2*x*z - 2*z) + z**2)/(x**2 - 1)]])
>>> apply(matrix, function=sp.collect, arguments={"syms": "z"})
Matrix([[(x*y**2 + y**2 + z**2*(x + 1) + z*(-2*x*y - 2*y))/(x**2 - 1)]])
>>> apply(matrix, function=sp.expand)
Matrix([[x*y**2/(x**2 - 1) - 2*x*y*z/(x**2 - 1) + x*z**2/(x**2 - 1) + y**2/(x**2 - 1) - 2*y*z/(x**2 - 1) + z**2/(x**2 - 1)]])
>>> apply(matrix, function=sp.factor)
Matrix([[(y - z)**2/(x - 1)]])
rewrite(
matrix: mat | QuantumObject,
function: Callable,
) mat[source]#

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

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

  • exp()

  • log()

  • sin()

  • cos()

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be transformed.

  • function (Callable) – A SymPy mathematical function.

Returns:

mat – The transformed version of matrix.

Examples

>>> matrix = sp.Matrix([["cos(x)"], ["sin(x)"]])
>>> rewrite(matrix, function=sp.exp)
Matrix([
[   exp(I*x)/2 + exp(-I*x)/2],
[-I*(exp(I*x) - exp(-I*x))/2]])
normalize(
matrix: mat | QuantumObject,
norm: num | sym | str | None = None,
) mat[source]#

Normalize matrix to the value specified (norm).

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be normalized.

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

Returns:

mat – The normalized version of matrix.

Examples

>>> matrix = sp.Matrix([["a"], ["b"]])
>>> normalize(matrix, norm=1)
Matrix([
[a/sqrt(a*conjugate(a) + b*conjugate(b))],
[b/sqrt(a*conjugate(a) + b*conjugate(b))]])
>>> matrix = sp.Matrix([["a", "b"], ["c", "d"]])
>>> normalize(matrix, norm="n")
Matrix([
[a*n/(a + d), b*n/(a + d)],
[c*n/(a + d), d*n/(a + d)]])
coefficient(
matrix: mat | QuantumObject,
scalar: num | sym | str | None = None,
) mat[source]#

Multiply matrix by a scalar value (scalar).

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be scaled.

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

Returns:

mat – The scaled version of matrix.

Examples

>>> matrix = sp.Matrix([["1"], ["1"]])
>>> coefficient(matrix, scalar=1 / sp.sqrt(2))
Matrix([
[sqrt(2)/2],
[sqrt(2)/2]])
>>> matrix = sp.Matrix([["a"], ["b"]])
>>> coefficient(matrix, scalar="exp(I*x)")
Matrix([
[a*exp(I*x)],
[b*exp(I*x)]])
partial_trace(
matrix: mat | QuantumObject,
targets: int | list[int] | None = None,
discard: bool | None = None,
dim: int | None = None,
optimize: bool | None = None,
) num | sym | mat[source]#

Compute and return the partial trace of a matrix.

Parameters:
  • matrix (mat) – The matrix on which to perform the partial trace operation.

  • targets (int | list[int]) – The numerical index/indices of the subsystem(s) to be partially traced over. 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.

  • dim (int) – The dimensionality of the matrix. Must be a non-negative integer. Defaults to 2.

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

Returns:

mat – The reduced matrix.

Examples

>>> matrix = sp.Matrix([["a"], ["b"], ["c"], ["d"]])
>>> partial_trace(matrix, targets=[0], dim=2)
Matrix([
[a*conjugate(a) + c*conjugate(c), a*conjugate(b) + c*conjugate(d)],
[b*conjugate(a) + d*conjugate(c), b*conjugate(b) + d*conjugate(d)]])
>>> partial_trace(matrix, targets=[1], dim=2)
Matrix([
[a*conjugate(a) + b*conjugate(b), a*conjugate(c) + b*conjugate(d)],
[c*conjugate(a) + d*conjugate(b), c*conjugate(c) + d*conjugate(d)]])
>>> matrix = sp.Matrix([["a", 0, 0, 0], [0, "b", 0, 0], [0, 0, "c", 0], [0, 0, 0, "d"]])
>>> partial_trace(matrix, targets=[0], discard=True, dim=2)
Matrix([
[a + c,     0],
[    0, b + d]])
>>> partial_trace(matrix, targets=[1], discard=True, dim=2)
Matrix([
[a + b,     0],
[    0, c + d]])
measure(
matrix: mat | QuantumObject,
operators: list[mat | arr | QuantumObject],
targets: int | list[int],
observable: bool | None = None,
statistics: bool | None = None,
dim: int | None = None,
) mat | list[num | sym][source]#

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

This function 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:

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

    (321)#\[\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

(322)#\[\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:
  • matrix (mat | QuantumObject) – The matrix to be measured.

  • 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.

  • 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 transform matrix into a post-measurement probabilistic sum of all outcomes (False). Defaults to False.

  • dim (int) – The dimensionality of matrix and the item(s) of operators. Must be a non-negative integer. Defaults to 2.

Returns:

  • mat – The post-measurement matrix. 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

>>> matrix = sp.Matrix([["a"], ["b"]])
>>> plus = sp.Matrix([[1 / sp.sqrt(2)], [1 / sp.sqrt(2)]])
>>> minus = sp.Matrix([[1 / sp.sqrt(2)], [-1 / sp.sqrt(2)]])
>>> measure(matrix, operators=[plus, minus], targets=[0], observable=False, statistics=True)
[a*conjugate(a)/2 + a*conjugate(b)/2 + b*conjugate(a)/2 + b*conjugate(b)/2,
 a*conjugate(a)/2 - a*conjugate(b)/2 - b*conjugate(a)/2 + b*conjugate(b)/2]
>>> measure(matrix, operators=[plus, minus], targets=[0], observable=False, statistics=False)
Matrix([
[a*conjugate(a)/2 + b*conjugate(b)/2, a*conjugate(b)/2 + b*conjugate(a)/2],
[a*conjugate(b)/2 + b*conjugate(a)/2, a*conjugate(a)/2 + b*conjugate(b)/2]])
>>> matrix = sp.Matrix([["a"], ["b"]])
>>> I = sp.Matrix([[1, 0], [0, 1]])
>>> X = sp.Matrix([[0, 1], [1, 0]])
>>> Y = sp.Matrix([[0, -sp.I], [sp.I, 0]])
>>> Z = sp.Matrix([[1, 0], [0, -1]])
>>> measure(matrix, operators=[I, X, Y, Z], targets=[0], 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)]
>>> measure(matrix, operators=[I, X, Y, Z], targets=[0], observable=True, statistics=False)
Matrix([
[2*a*conjugate(a), 2*a*conjugate(b)],
[2*b*conjugate(a), 2*b*conjugate(b)]])
postselect(
matrix: mat | QuantumObject,
postselections: list[tuple[mat | arr | QuantumObject, int]],
dim: int | None = None,
) mat | list[num | sym][source]#

Perform postselection on matrix against the operator(s) specified in postselections.

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

(323)#\[\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 matrix \(\op{\rho}\) naturally generalizes to

(324)#\[\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, matrix will be successively postselected in the order in which they are given. If a vector matrix is postselected against a matrix form, it will automatically be transformed into its matrix form via the outer product as necessary.

Parameters:
  • matrix (mat | QuantumObject) – The matrix to be measured.

  • 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.

  • dim (int) – The dimensionality of matrix and the item(s) of postselections. Must be a non-negative integer. Defaults to 2.

Returns:

mat – The postselected form of matrix.

Examples

>>> matrix = sp.Matrix([["a"], [0], [0], ["b"]])
>>> zero = sp.Matrix([[1], [0]])
>>> one = sp.Matrix([[0], [1]])
>>> postselect(matrix, postselections=[(zero, [0])], dim=2)
Matrix([
[a],
[0]])
>>> postselect(matrix, postselections=[(one, [0])], dim=2)
Matrix([
[0],
[b]])

Mixin#

class OperationsMixin[source]#

A mixin for endowing classes with the ability to have their matrix property mutated by various quantum operations.

Note

The OperationsMixin mixin is used exclusively by the QuantumState class—please see the corresponding section (Operations) for documentation on its methods.