Matrix types upstream of :mod:qamomile.observable.
Exposes:
:class:
HermitianMatrix, which wraps a dense Hermitian NumPy matrix and produces an exact Pauli decomposition as a :class:qamomile.observable.Hamiltonianvia the Fast Walsh-Hadamard Transform.Sample-based partial-diagonalization helpers (:func:
subspace_hamiltonian, :func:generalized_subspace_matrices, :func:solve_subspace) that build Hamiltonian / overlap matrices on a subspace spanned by a set of computational-basis or mixed-Pauli-basis product states and (optionally) solve the resulting (generalised) eigenvalue problem.Möttönen amplitude-encoding angle precomputation (:func:
compute_mottonen_amplitude_encoding_ry_angles, :func:compute_mottonen_amplitude_encoding_rz_angles, :func:validate_and_normalize_amplitudes). The actual quantum gate emission lives under :mod:qamomile.circuit.stdlib.state_preparation; this module provides only the classical angle math so that hybrid loops can pre-compute angle vectors outside any kernel and feed them tomottonen_amplitude_encoding_from_anglesviaparameters=[...].:class:
PauliLCU, which decomposes any finite complex power-of-two square matrix into immutable complex-weighted Pauli terms for LCU algorithms.:class:
PeriodicShiftLCU, which validates constant-coefficient periodic shift structure and stores its immutable complex-weighted shift terms.
Overview¶
| Function | Description |
|---|---|
compute_mottonen_amplitude_encoding_ry_angles | Pre-compute the flat Ry angle vector for a Möttönen encoding. |
compute_mottonen_amplitude_encoding_rz_angles | Pre-compute the flat Rz angle vector for the phase-restoration stage. |
generalized_subspace_matrices | Build (H_sub, S_sub) for samples in mixed X/Y/Z eigenbases. |
solve_subspace | Solve the (generalized) subspace eigenvalue problem. |
subspace_hamiltonian | Build H_sub[i, j] = ⟨s_i| H |s_j⟩ for Z-basis bitstring samples. |
validate_and_normalize_amplitudes | Validate an amplitude vector and return its normalised form. |
| Class | Description |
|---|---|
HermitianMatrix | Dense Hermitian operator on n qubits (a 2**n x 2**n matrix). |
PauliLCU | Store an immutable Pauli linear-combination decomposition. |
PauliLCUTerm | Store one nonzero complex coefficient and one sparse Pauli word. |
PeriodicShiftLCU | Store an immutable periodic-shift linear-combination decomposition. |
PeriodicShiftLCUTerm | Store 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.ndarrayPre-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:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude 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:
ValueError— If the input is not a 1-D vector, the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
compute_mottonen_amplitude_encoding_rz_angles [source]¶
def compute_mottonen_amplitude_encoding_rz_angles(amplitudes: Sequence[float] | Sequence[complex] | np.ndarray) -> np.ndarrayPre-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:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude 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:
ValueError— If the input is not a 1-D vector, the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
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:
| Name | Type | Description |
|---|---|---|
samples | Sequence[Sequence[int]] | np.ndarray | Iterable 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). |
bases | Sequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarray | Per-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. |
hamiltonian | qm_o.Hamiltonian | The 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:
ValueError— On shape, range, or label mismatches insamplesorbases.
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:
| Name | Type | Description |
|---|---|---|
samples | Sequence[Sequence[int]] | np.ndarray | Iterable of length-num_qubits bitstrings. |
hamiltonian | qm_o.Hamiltonian | The Hamiltonian to project. |
bases | Sequence[Sequence[BasisLike]] | Sequence[BasisLike] | np.ndarray | None | Optional 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_tol | float | Threshold 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:
ValueError— Ifoverlap_tolis negative, any input fails validation in :func:subspace_hamiltonian/ :func:generalized_subspace_matrices, or every overlap eigenvalue is belowoverlap_tol(the supplied basis spans no usable directions; loweroverlap_tolor pass a better-conditioned set of samples).
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.0subspace_hamiltonian [source]¶
def subspace_hamiltonian(
samples: Sequence[Sequence[int]] | np.ndarray,
hamiltonian: qm_o.Hamiltonian,
) -> np.ndarrayBuild 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:
| Name | Type | Description |
|---|---|---|
samples | Sequence[Sequence[int]] | np.ndarray | Iterable 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. |
hamiltonian | qm_o.Hamiltonian | The 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:
ValueError— Ifsampleshas the wrong rank, fewer columns thanhamiltonian.num_qubits, or out-of-range entries.
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:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude 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:
ValueError— If the input is not a 1-D vector (e.g., a nested sequence or a 2-Dnp.ndarray), the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
Classes¶
HermitianMatrix [source]¶
class HermitianMatrixDense 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,
) -> NoneAttributes¶
matrix: np.ndarray Return a read-only view of the underlying array.num_qubits: int Number of qubits (log2of the matrix dimension).shape: tuple[int, int] Shape of the underlying matrix as(2**n, 2**n).
Methods¶
to_hamiltonian¶
def to_hamiltonian(self, *, tol: float = 1e-12) -> HamiltonianReturn 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:
| Name | Type | Description |
|---|---|---|
tol | float | Absolute 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 PauliLCUStore an immutable Pauli linear-combination decomposition.
Parameters:
| Name | Type | Description |
|---|---|---|
num_qubits | int | Number of system qubits represented by the Pauli words. Zero is valid for a scalar 1 x 1 matrix. |
terms | tuple[PauliLCUTerm, ...] | Ordered nonzero Pauli terms. The empty tuple represents the zero operator. |
truncation_error_bound | float | Upper 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:
TypeError— Ifnum_qubits,terms, ortruncation_error_boundhas an invalid type.ValueError— If a term addresses a qubit outside the system, duplicate Pauli words are present, the LCU normalization exceeds the finite float range, or the error bound is negative or non-finite.
Constructor¶
def __init__(
self,
num_qubits: int,
terms: tuple[PauliLCUTerm, ...],
truncation_error_bound: float = 0.0,
) -> NoneAttributes¶
alpha: float Return the LCU one-norm normalization.num_qubits: intnum_terms: int Return the number of retained Pauli terms.terms: tuple[PauliLCUTerm, ...]truncation_error_bound: float
Methods¶
from_matrix¶
@classmethod
def from_matrix(cls, matrix: ArrayLike, *, atol: float = 0.0) -> PauliLCUDecompose 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:
| Name | Type | Description |
|---|---|---|
matrix | ArrayLike | Finite square matrix with dimension 2**n. Hermiticity is not required. |
atol | float | Non-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only. |
Returns:
PauliLCU — Immutable ordered Pauli decomposition.
Raises:
TypeError— Ifatolis not a real number.ValueError— Ifatolis negative or non-finite, ormatrixis not a finite square numeric matrix with power-of-two dimension, or the resulting LCU normalization exceeds the finite float range.
PauliLCUTerm [source]¶
class PauliLCUTermStore 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:
| Name | Type | Description |
|---|---|---|
coefficient | complex | Finite, nonzero coefficient multiplying the Pauli word. |
operators | tuple[PauliOperator, ...] | Sparse Pauli word. Identity operators must be omitted; use an empty tuple for the all-identity word. |
Raises:
TypeError— If the coefficient is not a numeric scalar, an operator is not aPauliOperator, its Pauli value is invalid, or its index is not an integer.ValueError— If the coefficient is zero or non-finite, an identity operator is stored explicitly, or multiple operators address the same qubit.
Constructor¶
def __init__(self, coefficient: complex, operators: tuple[PauliOperator, ...]) -> NoneAttributes¶
coefficient: complexoperators: tuple[PauliOperator, ...]
PeriodicShiftLCU [source]¶
class PeriodicShiftLCUStore 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:
| Name | Type | Description |
|---|---|---|
register_sizes | tuple[int, ...] | Positive qubit width of every periodic axis. The empty tuple represents a scalar 1 x 1 matrix at the linalg layer. |
terms | tuple[PeriodicShiftLCUTerm, ...] | Canonical nonzero terms. Terms are stored in lexicographic offset order. The empty tuple represents the zero operator. |
truncation_error_bound | float | Upper 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:
TypeError— If a field has an invalid runtime type.ValueError— If an axis width, term, normalization, or truncation bound is invalid, or terms contain duplicate offsets.
Constructor¶
def __init__(
self,
register_sizes: tuple[int, ...],
terms: tuple[PeriodicShiftLCUTerm, ...],
truncation_error_bound: float = 0.0,
) -> NoneAttributes¶
alpha: float Return the retained LCU one-norm normalization.num_qubits: int Return the flattened system-register width.num_terms: int Return the number of retained periodic-shift terms.register_sizes: tuple[int, ...]terms: tuple[PeriodicShiftLCUTerm, ...]truncation_error_bound: float
Methods¶
from_coefficients¶
@classmethod
def from_coefficients(
cls,
coefficients: Mapping[Offset, complex],
*,
register_sizes: Sequence[int],
atol: float = 0.0,
) -> PeriodicShiftLCUBuild 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:
| Name | Type | Description |
|---|---|---|
coefficients | Mapping[int | tuple[int, ...], complex] | Finite coefficients keyed by a one-dimensional integer offset or one integer component per axis. |
register_sizes | Sequence[int] | Positive qubit width of each periodic axis. The empty sequence describes a scalar decomposition; any supplied offset must then be the empty tuple. |
atol | float | Non-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only. |
Returns:
PeriodicShiftLCU — Immutable canonical decomposition.
Raises:
TypeError— If coefficients, offsets, widths, oratolhave invalid types.ValueError— If a value is non-finite, an offset has the wrong dimension, a width is invalid, or normalization overflows.
from_matrix¶
@classmethod
def from_matrix(
cls,
matrix: ArrayLike,
*,
register_sizes: Sequence[int],
atol: float = 0.0,
) -> PeriodicShiftLCUDecompose 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:
| Name | Type | Description |
|---|---|---|
matrix | ArrayLike | Finite square matrix with dimension 2**sum(register_sizes). |
register_sizes | Sequence[int] | Positive qubit width of each axis in flattened least-significant-axis-first order. The empty sequence describes a scalar 1 x 1 matrix. |
atol | float | Non-negative absolute coefficient threshold. Defaults to 0.0, which removes exact zeros only. |
Returns:
PeriodicShiftLCU — Immutable canonical shift decomposition.
Raises:
TypeError— If widths oratolhave invalid types.ValueError— If the matrix is nonnumeric, non-finite, nonsquare, has the wrong dimension, violates the periodic-shift structure, or produces an unrepresentable normalization.
PeriodicShiftLCUTerm [source]¶
class PeriodicShiftLCUTermStore one nonzero coefficient and one canonical periodic offset.
Parameters:
| Name | Type | Description |
|---|---|---|
coefficient | complex | Finite nonzero coefficient multiplying the periodic shift. |
offset | tuple[int, ...] | Non-negative offset residue for every axis. The enclosing :class:PeriodicShiftLCU validates each component against its corresponding axis size. |
Raises:
TypeError— If the coefficient is not numeric or the offset is not an iterable of non-Boolean integers.ValueError— If the coefficient is zero or non-finite, or an offset component is negative.
Constructor¶
def __init__(self, coefficient: complex, offset: tuple[int, ...]) -> NoneAttributes¶
coefficient: complexoffset: tuple[int, ...]