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:
Qubit ordering: qubit
0is the least-significant bit of the computational-basis index, matching :meth:qamomile.observable.Hamiltonian.to_numpyand :class:qamomile.linalg.HermitianMatrix.Y-eigenstate phase:
|Y, 0⟩ = |+i⟩ = (|0⟩ + i|1⟩)/√2withY |+i⟩ = +|+i⟩. This is consistent with the basis-rotation pulseS† Hused to map a Y measurement onto the Z basis.Basis codes:
X = 0,Y = 1,Z = 2. Both raw integer codes andqamomile.observable.Paulienum members are accepted on input.Pauli.Iis rejected — every sample must lie in a definite X/Y/Z eigenbasis.
Overview¶
| Function | Description |
|---|---|
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. |
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:
| 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)