Linear algebra#

Linear-algebra building blocks of quantum-like models.

States are unit vectors (real or complex) or density matrices. A “yes” answer to a question is an orthogonal projector \(P\), and answering applies the Lüders rule

\[p(\text{yes}) = \lVert P\psi \rVert^2, \qquad \psi \mapsto P\psi / \lVert P\psi \rVert .\]

Nothing here assumes a particular domain: the model families, the robotics layer and the circuit builders are all written on top of these functions.

Functions

normalize

Scale a vector to unit length.

projector

Orthogonal projector onto the span of a set of vectors.

complement

Projector onto the orthogonal complement, \(I - P\).

luders

Apply the Lüders rule for one projective measurement outcome.

sequence_probabilities

Probabilities of every yes/no answer sequence to a sequence of questions.

angles_to_unit

Unit vector from hyperspherical angles.

givens_frame

Orthonormal frame built from Givens rotations.

density

Density matrix of a pure state.

dephase

Partial dephasing channel.

lindblad_superoperator

Lindblad generator as a matrix acting on the row-stacked density matrix.

evolve_density

Evolve a density matrix under a Lindblad generator.

unitary

Time-evolution operator \(e^{-iHt}\).

is_projector

Test whether a matrix is an orthogonal projector.

normalize(v)[source]#

Scale a vector to unit length.

Parameters:

v (array_like) – Real or complex vector.

Returns:

numpy.ndarray – v / ||v||, real if v is real and complex otherwise.

Raises:

ValueError – If v is the zero vector.

projector(basis)[source]#

Orthogonal projector onto the span of a set of vectors.

The vectors are orthonormalised first (QR decomposition), so any spanning set may be passed.

Parameters:

basis (array_like) – A single vector, or a 2D array whose rows span the subspace.

Returns:

numpy.ndarray – Hermitian, idempotent matrix \(QQ^\dagger\) of shape (d, d).

Examples

>>> from quantum_mind.core import projector
>>> projector([1, 1]).round(2)
array([[0.5, 0.5],
       [0.5, 0.5]])
complement(P)[source]#

Projector onto the orthogonal complement, \(I - P\).

Parameters:

P (numpy.ndarray) – Orthogonal projector of shape (d, d).

Returns:

numpy.ndarray – The “no” projector belonging to the “yes” projector P.

luders(psi, P)[source]#

Apply the Lüders rule for one projective measurement outcome.

Parameters:
Returns:

  • probability (float) – \(\lVert P\psi \rVert^2\).

  • state (numpy.ndarray) – Post-measurement state \(P\psi / \lVert P\psi \rVert\); the unnormalised vector is returned when the probability is numerically zero.

sequence_probabilities(psi, projectors, order)[source]#

Probabilities of every yes/no answer sequence to a sequence of questions.

Each question is answered in turn with the Lüders rule, so the result depends on the order whenever the projectors do not commute (the source of question-order effects).

Parameters:
  • psi (numpy.ndarray) – Initial normalised state.

  • projectors (dict) – Maps a question name to the projector of its “yes” answer.

  • order (sequence of str) – Question names in the order they are asked.

Returns:

dict – {(a1, a2, ...): probability} with a = 1 for yes and 0 for no. The values sum to 1.

Examples

>>> import numpy as np
>>> from quantum_mind.core import projector, sequence_probabilities
>>> P = {'A': projector([1, 0]), 'B': projector([1, 1])}
>>> p = sequence_probabilities(np.array([1.0, 0.0]), P, ['A', 'B'])
>>> round(p[(1, 1)], 3)
0.5
angles_to_unit(angles)[source]#

Unit vector from hyperspherical angles.

Parameters:

angles (array_like) – d - 1 angles \(\theta_1, \dots, \theta_{d-1}\).

Returns:

numpy.ndarray – Real unit vector in \(\mathbb{R}^d\) with \(v_i = \cos\theta_i \prod_{j<i} \sin\theta_j\) (the last entry has no cosine).

givens_frame(angles, d)[source]#

Orthonormal frame built from Givens rotations.

Parameters:
  • angles (array_like) – d (d - 1) / 2 rotation angles, one per coordinate plane (i, j) with i < j.

  • d (int) – Dimension.

Returns:

numpy.ndarray – Orthogonal (d, d) matrix whose columns form an orthonormal basis. Every rotation of \(\mathbb{R}^d\) with determinant 1 can be written this way.

density(psi)[source]#

Density matrix of a pure state.

Parameters:

psi (array_like) – Normalised state vector.

Returns:

numpy.ndarray – \(\rho = |\psi\rangle\langle\psi|\).

dephase(rho, strength, basis_projectors=None)[source]#

Partial dephasing channel.

\[\rho \mapsto (1 - s)\,\rho + s \sum_k P_k \rho P_k\]
Parameters:
  • rho (numpy.ndarray) – Density matrix.

  • strength (float) – Dephasing strength s in [0, 1]; s = 1 removes all coherences between the subspaces.

  • basis_projectors (sequence of numpy.ndarray, optional) – Projectors \(P_k\) that sum to the identity. Defaults to the computational basis.

Returns:

numpy.ndarray – The dephased density matrix.

lindblad_superoperator(H, jumps, rates)[source]#

Lindblad generator as a matrix acting on the row-stacked density matrix.

\[\dot\rho = -i[H, \rho] + \sum_k \gamma_k \left(L_k \rho L_k^\dagger - \tfrac12 \{L_k^\dagger L_k, \rho\}\right)\]
Parameters:
  • H (array_like) – Hamiltonian, shape (n, n).

  • jumps (sequence of array_like) – Jump operators \(L_k\).

  • rates (sequence of float) – Rates \(\gamma_k \ge 0\), one per jump operator.

Returns:

numpy.ndarray – Superoperator of shape (n**2, n**2); use it with evolve_density().

evolve_density(rho, superop, t)[source]#

Evolve a density matrix under a Lindblad generator.

Parameters:
Returns:

numpy.ndarray – \(\rho(t)\), computed with a matrix exponential.

unitary(H, t)[source]#

Time-evolution operator \(e^{-iHt}\).

Parameters:
  • H (array_like) – Hermitian Hamiltonian.

  • t (float) – Time.

Returns:

numpy.ndarray – Unitary matrix.

is_projector(P, tol=1e-09)[source]#

Test whether a matrix is an orthogonal projector.

Parameters:
  • P (numpy.ndarray) – Square matrix.

  • tol (float, optional) – Absolute tolerance for \(P^2 = P\) and \(P = P^\dagger\).

Returns:

bool – True if P is Hermitian and idempotent within tol.