Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

qamomile.linalg

Matrix types upstream of :mod:qamomile.observable.

Exposes:

Overview

FunctionDescription
compute_mottonen_amplitude_encoding_ry_anglesPre-compute the flat Ry angle vector for a Möttönen encoding.
compute_mottonen_amplitude_encoding_rz_anglesPre-compute the flat Rz angle vector for the phase-restoration stage.
generalized_subspace_matricesBuild (H_sub, S_sub) for samples in mixed X/Y/Z eigenbases.
solve_subspaceSolve the (generalized) subspace eigenvalue problem.
subspace_hamiltonianBuild H_sub[i, j] = ⟨s_i| H |s_j⟩ for Z-basis bitstring samples.
validate_and_normalize_amplitudesValidate an amplitude vector and return its normalised form.
ClassDescription
HermitianMatrixDense Hermitian operator on n qubits (a 2**n x 2**n matrix).
PauliLCUStore an immutable Pauli linear-combination decomposition.
PauliLCUTermStore one nonzero complex coefficient and one sparse Pauli word.
PeriodicShiftLCUStore an immutable periodic-shift linear-combination decomposition.
PeriodicShiftLCUTermStore one nonzero coefficient and one canonical periodic offset.

Functions

compute_mottonen_amplitude_encoding_ry_angles [source]

def compute_mottonen_amplitude_encoding_ry_angles(amplitudes: Sequence[float] | Sequence[complex] | np.ndarray) -> np.ndarray

Pre-compute the flat Ry angle vector for a Möttönen encoding.

Returns the Gray-walk Ry angles for the magnitude stage of the encoding. For real inputs (or complex inputs with zero imaginary part) this corresponds to the single-stage signed-Ry encoding. For complex inputs with non-zero imaginary part, these are the magnitude-stage angles only — pair with :func:compute_mottonen_amplitude_encoding_rz_angles to obtain the phase-stage angles needed to reproduce the full complex state.

Parameters:

NameTypeDescription
amplitudesSequence[float] | Sequence[complex] | np.ndarrayAmplitude vector of length 2**n. Real or complex; it is normalised automatically.

Returns:

np.ndarray — np.ndarray: 1-D array of length 2**n - 1 holding the Gray-walk Ry angles laid out level by level.

Raises:


compute_mottonen_amplitude_encoding_rz_angles [source]

def compute_mottonen_amplitude_encoding_rz_angles(amplitudes: Sequence[float] | Sequence[complex] | np.ndarray) -> np.ndarray

Pre-compute the flat Rz angle vector for the phase-restoration stage.

Returns the Gray-walk Rz angles for the phase stage of the Möttönen encoding. For real inputs (or complex inputs with zero imaginary part) the phase stage is unnecessary and the returned array is all zeros. For complex inputs with non-zero imaginary part, the returned angles together with the Ry angles from :func:compute_mottonen_amplitude_encoding_ry_angles reproduce the full complex state.

Parameters:

NameTypeDescription
amplitudesSequence[float] | Sequence[complex] | np.ndarrayAmplitude vector of length 2**n. Real or complex; it is normalised automatically.

Returns:

np.ndarray — np.ndarray: 1-D array of length 2**n - 1 holding the Gray-walk Rz angles laid out level by level (all zeros for real inputs).

Raises:


generalized_subspace_matrices [source]

def generalized_subspace_matrices(
    samples: Sequence[Sequence[int]] | np.ndarray,
    bases: Sequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarray,
    hamiltonian: qm_o.Hamiltonian,
) -> tuple[np.ndarray, np.ndarray]

Build (H_sub, S_sub) for samples in mixed X/Y/Z eigenbases.

Each sample |s_i; β_i⟩ = ⊗_q |β_{i,q}, s_{i,q}⟩ is a tensor product of single-qubit Pauli eigenstates with per-qubit basis β_{i,q} and bit s_{i,q}. The returned matrices are

H_sub[i, j] = ⟨s_i; β_i| H |s_j; β_j⟩,
S_sub[i, j] = ⟨s_i; β_i|     s_j; β_j⟩,

so the variational eigenpairs are recovered by solving the generalized eigenvalue problem H_sub v = λ S_sub v (see :func:solve_subspace).

Parameters:

NameTypeDescription
samplesSequence[Sequence[int]] | np.ndarrayIterable of bitstrings, with samples[k][q] the eigenvalue index for qubit q in basis bases[k][q] (0 for the +1 eigenstate, 1 for the -1 eigenstate).
basesSequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarrayPer-qubit basis labels. Either a 1-D sequence of length num_qubits (broadcast across all samples) or a 2-D (n_samples, num_qubits) array. Each entry is either a Pauli.X / Y / Z member or the corresponding integer code 0 / 1 / 2.
hamiltonianqm_o.HamiltonianThe Hamiltonian whose matrix elements are taken.

Returns:

np.ndarray — Tuple (H_sub, S_sub) of complex ndarray s with shape np.ndarray(n_samples, n_samples).

Raises:

Example:

Mixed-basis subspace with one Z-basis sample (|0⟩) and one
X-basis sample (|+⟩) for H = Z:

>>> import qamomile.observable as qm_o
>>> from qamomile.linalg import generalized_subspace_matrices
>>> H = qm_o.Z(0)
>>> samples = [(0,), (0,)]  # bit=0 in each basis
>>> bases = [(qm_o.Pauli.Z,), (qm_o.Pauli.X,)]  # |0⟩ and |+⟩
>>> H_sub, S_sub = generalized_subspace_matrices(samples, bases, H)
>>> S_sub  # off-diagonal ⟨0|+⟩ = 1/√2, not identity
array([[1.    +0.j, 0.7071+0.j],
       [0.7071+0.j, 1.    +0.j]])
>>> H_sub  # ⟨0|Z|0⟩=1, ⟨+|Z|+⟩=0, ⟨0|Z|+⟩=1/√2
array([[1.    +0.j, 0.7071+0.j],
       [0.7071+0.j, 0.    +0.j]])

solve_subspace [source]

def solve_subspace(
    samples: Sequence[Sequence[int]] | np.ndarray,
    hamiltonian: qm_o.Hamiltonian,
    *,
    bases: Sequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarray | None = None,
    overlap_tol: float = 1e-12,
) -> tuple[np.ndarray, np.ndarray]

Solve the (generalized) subspace eigenvalue problem.

Every input, including the default Z-basis path, is solved as the generalised eigenvalue problem H_sub v = λ S_sub v. When bases is None, Z labels are synthesized and :func:generalized_subspace_matrices builds both matrices. Whitening S_sub via its eigendecomposition reduces the usable subspace to a standard Hermitian problem, with overlap_tol as the cutoff. This removes null directions caused by duplicate or near-duplicate samples instead of treating repeated Z samples as independent basis states.

Parameters:

NameTypeDescription
samplesSequence[Sequence[int]] | np.ndarrayIterable of length-num_qubits bitstrings.
hamiltonianqm_o.HamiltonianThe Hamiltonian to project.
basesSequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarray | NoneOptional per-qubit or per-sample basis labels (see :func:generalized_subspace_matrices). Defaults to None, which means every qubit of every sample is in the Z basis.
overlap_tolfloatThreshold below which an eigenvalue of S_sub is treated as zero in the rank-deficient fallback. Must be non-negative. Defaults to 1e-12.

Returns:

np.ndarray — Tuple (eigvals, eigvecs). eigvals is a real 1-D array np.ndarray — sorted in ascending order; eigvecs[:, k] is the variational tuple[np.ndarray, np.ndarray] — coefficient vector of the k-th eigenstate in the basis of tuple[np.ndarray, np.ndarray] — the supplied samples. In the rank-deficient fallback path the tuple[np.ndarray, np.ndarray] — number of returned eigenpairs is the effective rank of tuple[np.ndarray, np.ndarray]S_sub.

Raises:

Example:

>>> import qamomile.observable as qm_o
>>> from qamomile.linalg import solve_subspace
>>> H = -qm_o.X(0)
>>> # Sampling the |+⟩ eigenstate of X recovers the ground state.
>>> eigvals, _ = solve_subspace(
...     [(0,)], H, bases=(qm_o.Pauli.X,)
... )
>>> float(eigvals[0])
-1.0

subspace_hamiltonian [source]

def subspace_hamiltonian(
    samples: Sequence[Sequence[int]] | np.ndarray,
    hamiltonian: qm_o.Hamiltonian,
) -> np.ndarray

Build H_sub[i, j] = ⟨s_i| H |s_j⟩ for Z-basis bitstring samples.

Vectorised over all (i, j) pairs using XOR masks to detect the X/Y support of each Pauli term and Z/Y parities for the sign and phase. Identical bitstrings produce a degenerate row/column; dedupe upstream if the resulting subspace must be non-degenerate.

Parameters:

NameTypeDescription
samplesSequence[Sequence[int]] | np.ndarrayIterable of length-num_qubits bitstrings, with samples[k][q] the Z-eigenvalue index (0 for |0⟩, 1 for |1⟩) of qubit q. Excess columns are truncated.
hamiltonianqm_o.HamiltonianThe Hamiltonian whose matrix elements are taken. hamiltonian.constant contributes c · I on the diagonal of the projected matrix.

Returns:

np.ndarray — Complex ndarray of shape (n_samples, n_samples) holding np.ndarray⟨s_i|H|s_j⟩.

Raises:

Example:

>>> import qamomile.observable as qm_o
>>> from qamomile.linalg import subspace_hamiltonian
>>> H = qm_o.Z(0) + qm_o.X(1)
>>> # All four Z-basis bitstrings on 2 qubits
>>> samples = [(0, 0), (1, 0), (0, 1), (1, 1)]
>>> H_sub = subspace_hamiltonian(samples, H)
>>> H_sub.shape
(4, 4)

validate_and_normalize_amplitudes [source]

def validate_and_normalize_amplitudes(
    amplitudes: Sequence[float] | Sequence[complex] | np.ndarray,
) -> tuple[np.ndarray, int, bool]

Validate an amplitude vector and return its normalised form.

The Möttönen construction (arXiv:quant-ph/0407010, Section III) works on a unit-norm amplitude vector indexed by computational basis states; this helper enforces that pre-condition so the downstream angle computation can assume a well-formed input.

Inputs may be real or complex. A complex input whose imaginary part is identically zero (within np.allclose tolerance) is coerced to a real array; this preserves the cheaper signed-RY fast path for vectors that happen to arrive boxed as complex.

Parameters:

NameTypeDescription
amplitudesSequence[float] | Sequence[complex] | np.ndarrayAmplitude vector. Must be a 1-D sequence whose length is a power of two and at least 2, with at least one non-zero entry.

Returns:

tuple[np.ndarray, int, bool] — tuple[np.ndarray, int, bool]: (normalized, num_qubits, is_complex) where normalized is a unit-norm np.ndarray, num_qubits is log2(len(amplitudes)), and is_complex is True iff the input has a non-zero imaginary component (and therefore needs the phase-restoration stage downstream). When is_complex is False, normalized.dtype is float; otherwise it is complex.

Raises:

Classes

HermitianMatrix [source]

class HermitianMatrix

Dense Hermitian operator on n qubits (a 2**n x 2**n matrix).

Wraps a NumPy array and validates on construction that it is 2D square, has a power-of-two dimension, and is Hermitian up to atol. The Hermitian check can be skipped via validate=False for internal use (for example, when the caller already knows a result is Hermitian by construction).

The main entry point for downstream quantum code is :meth:to_hamiltonian, which returns the exact Pauli decomposition as a :class:qamomile.observable.Hamiltonian via the Fast Walsh-Hadamard Transform in O(n * 4**n) time.

Example:

>>> import numpy as np
>>> from qamomile.linalg import HermitianMatrix
>>> X = np.array([[0, 1], [1, 0]], dtype=complex)
>>> Z = np.array([[1, 0], [0, -1]], dtype=complex)
>>> H = HermitianMatrix(np.kron(X, Z) + 0.5 * np.kron(Z, np.eye(2)))
>>> H.num_qubits
2
>>> ham = H.to_hamiltonian()

Constructor

def __init__(
    self,
    matrix: np.ndarray,
    *,
    validate: bool = True,
    atol: float = 1e-10,
) -> None

Attributes

Methods

to_hamiltonian
def to_hamiltonian(self, *, tol: float = 1e-12) -> Hamiltonian

Return the exact Pauli decomposition as a Hamiltonian.

Uses FWHT internally. Pauli terms with |coeff| <= tol are dropped. The all-identity component is accumulated into :attr:qamomile.observable.Hamiltonian.constant. Qubit 0 corresponds to the least-significant bit of the matrix’s computational-basis indices.

Parameters:

NameTypeDescription
tolfloatAbsolute tolerance below which a Pauli coefficient is treated as zero and omitted from the result.

Returns:

Hamiltonian — class:~qamomile.observable.Hamiltonian containing the Hamiltonian — Pauli decomposition.


PauliLCU [source]

class PauliLCU

Store an immutable Pauli linear-combination decomposition.

Parameters:

NameTypeDescription
num_qubitsintNumber of system qubits represented by the Pauli words. Zero is valid for a scalar 1 x 1 matrix.
termstuple[PauliLCUTerm, ...]Ordered nonzero Pauli terms. The empty tuple represents the zero operator.
truncation_error_boundfloatUpper bound on the spectral-norm error caused only by coefficients intentionally removed by :meth:from_matrix. Defaults to 0.0. It does not include floating-point FWHT roundoff.

Raises:

Constructor

def __init__(
    self,
    num_qubits: int,
    terms: tuple[PauliLCUTerm, ...],
    truncation_error_bound: float = 0.0,
) -> None

Attributes

Methods

from_matrix
@classmethod
def from_matrix(cls, matrix: ArrayLike, *, atol: float = 0.0) -> PauliLCU

Decompose a general complex matrix into the Pauli basis.

Qubit zero is the least-significant bit of the matrix’s computational basis index. Coefficients whose magnitude is at most atol are omitted, and their absolute values are accumulated into :attr:truncation_error_bound.

Parameters:

NameTypeDescription
matrixArrayLikeFinite square matrix with dimension 2**n. Hermiticity is not required.
atolfloatNon-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only.

Returns:

PauliLCU — Immutable ordered Pauli decomposition.

Raises:


PauliLCUTerm [source]

class PauliLCUTerm

Store one nonzero complex coefficient and one sparse Pauli word.

The empty operator tuple denotes the identity. Non-identity operators are canonicalized into increasing qubit-index order.

Parameters:

NameTypeDescription
coefficientcomplexFinite, nonzero coefficient multiplying the Pauli word.
operatorstuple[PauliOperator, ...]Sparse Pauli word. Identity operators must be omitted; use an empty tuple for the all-identity word.

Raises:

Constructor

def __init__(self, coefficient: complex, operators: tuple[PauliOperator, ...]) -> None

Attributes


PeriodicShiftLCU [source]

class PeriodicShiftLCU

Store an immutable periodic-shift linear-combination decomposition.

Axis zero occupies the least-significant bits of the flattened system basis. Each term represents coefficient * T_offset, where every axis of T_offset wraps modulo 2**register_sizes[axis].

Parameters:

NameTypeDescription
register_sizestuple[int, ...]Positive qubit width of every periodic axis. The empty tuple represents a scalar 1 x 1 matrix at the linalg layer.
termstuple[PeriodicShiftLCUTerm, ...]Canonical nonzero terms. Terms are stored in lexicographic offset order. The empty tuple represents the zero operator.
truncation_error_boundfloatUpper bound on the spectral-norm error caused only by coefficients intentionally removed by :meth:from_matrix or :meth:from_coefficients. Defaults to 0.0 and excludes host floating-point roundoff.

Raises:

Constructor

def __init__(
    self,
    register_sizes: tuple[int, ...],
    terms: tuple[PeriodicShiftLCUTerm, ...],
    truncation_error_bound: float = 0.0,
) -> None

Attributes

Methods

from_coefficients
@classmethod
def from_coefficients(
    cls,
    coefficients: Mapping[Offset, complex],
    *,
    register_sizes: Sequence[int],
    atol: float = 0.0,
) -> PeriodicShiftLCU

Build a canonical decomposition from periodic-shift coefficients.

Equivalent signed or unsigned offsets are combined before pruning. Coefficients whose magnitude is at most atol are omitted, and the sum of their magnitudes is stored in truncation_error_bound.

Parameters:

NameTypeDescription
coefficientsMapping[int | tuple[int, ...], complex]Finite coefficients keyed by a one-dimensional integer offset or one integer component per axis.
register_sizesSequence[int]Positive qubit width of each periodic axis. The empty sequence describes a scalar decomposition; any supplied offset must then be the empty tuple.
atolfloatNon-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only.

Returns:

PeriodicShiftLCU — Immutable canonical decomposition.

Raises:

from_matrix
@classmethod
def from_matrix(
    cls,
    matrix: ArrayLike,
    *,
    register_sizes: Sequence[int],
    atol: float = 0.0,
) -> PeriodicShiftLCU

Decompose an exact constant-coefficient periodic-shift matrix.

After conversion to complex128, the structural check is exact: every column must be the periodic translation of the first column implied by register_sizes. The atol argument has the same meaning as in :meth:from_coefficients; it prunes extracted coefficients but does not relax the structural check. Hermiticity is not required.

Parameters:

NameTypeDescription
matrixArrayLikeFinite square matrix with dimension 2**sum(register_sizes).
register_sizesSequence[int]Positive qubit width of each axis in flattened least-significant-axis-first order. The empty sequence describes a scalar 1 x 1 matrix.
atolfloatNon-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only.

Returns:

PeriodicShiftLCU — Immutable canonical shift decomposition.

Raises:


PeriodicShiftLCUTerm [source]

class PeriodicShiftLCUTerm

Store one nonzero coefficient and one canonical periodic offset.

Parameters:

NameTypeDescription
coefficientcomplexFinite nonzero coefficient multiplying the periodic shift.
offsettuple[int, ...]Non-negative offset residue for every axis. The enclosing :class:PeriodicShiftLCU validates each component against its corresponding axis size.

Raises:

Constructor

def __init__(self, coefficient: complex, offset: tuple[int, ...]) -> None

Attributes

Submodules