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

Sample-based partial diagonalization in a Pauli-eigenbasis subspace.

Given a finite collection of computational- or Pauli-basis product states {|s_i; β_i⟩} and a Hamiltonian H, build the projected Hamiltonian and overlap matrices

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

and (optionally) solve the generalized eigenvalue problem H_sub v = λ S_sub v to recover variational eigenpairs in the sampled subspace. When every sample is in the Z computational basis, S_sub reduces to the identity (modulo duplicates) and the function subspace_hamiltonian provides a vectorised XOR/parity fast path.

Conventions:

Overview

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

Functions

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)