qarp.operators¶
Operators. Public depth: flat for the operator, mapping and grouping types,
plus seven qualified namespaces. Function-first fermionic surface —
models, integrals, ucc, onv (bare ucc_singles /
active_space_integrals do not carry their domain). Interop and aliases —
functions (raw C++ transforms: a bare jordan_wigner would sit beside
the JordanWigner Mapping class), compat (openfermion MSB interop, §1)
and pyscf (the pyscf recipes). All other submodules are private.
- class qarp.operators.BravyiKitaev(n_qubits: int | None = None)[source]¶
Bases:
MappingA class for mapping FermionOperators to qubit operators using the Bravyi-Kitaev mapping.
The BK transform of an operator depends on the register width: the update, parity and remainder sets of orbital
jchange withn_qubits. A list is therefore mapped on one width —n_qubitswhen given, else the largestcount_qubitsover the list — so its members compose (e.g. UCC generators against a Hamiltonian). A single operator withn_qubits=Nonekeeps the per-operator inference.- Parameters:
n_qubits – register width for every
encode_operatorcall, orNoneto infer it (per call, as described above).
- encode_operator(op: list[FermionOperator] | FermionOperator)[source]¶
Perform a Bravyi-Kitaev mapping of the provided operator(s).
- Parameters:
op – A list of, or single, FermionOperator object.
- Returns:
The result of the mapping as a list of, or single, QubitOperator.
- class qarp.operators.FermionOperator(*args, **kwargs)¶
Bases:
objectopenfermion-compatible FermionOperator backed by a packed ladder-sequence C++ core.
- coefficient(self, term: object) object¶
Fast single-term coefficient lookup (qarpx extra, not openfermion).
- property constant¶
(self) -> complex
- get_operators(self) list[qarpx.FermionOperator]¶
- hermitian_conjugated(self) qarpx.FermionOperator¶
- identity = <nanobind.nb_func object>¶
- isclose(self, other: qarpx.FermionOperator, tol: float = 1e-08) bool¶
- sparse_matrix(n_qubits=None)¶
qarpx-LSB
scipy.sparse.csc_matrixof the Jordan-Wigner image (qubit q ↔ bit q).The only matrix API (conventions §1) — directly contractable with qarpx statevectors / unitaries. openfermion’s MSB layout, for interop only, is
qarp.operators.compat.get_sparse_operator().
- substitute(self, values: object) qarpx.FermionOperator¶
Substitute named symbols with numeric values; demotes to the numeric backend when no free symbols remain (qarpx extra).
- property terms¶
Term dictionary (read-only view; assign a full dict to replace).
- zero = <nanobind.nb_func object>¶
- class qarp.operators.FullyCommuting[source]¶
Bases:
GroupingStrategyGreedy first-fit under general multi-qubit commutation.
Uses
qx.commutes(two Paulis commute iff they disagree on an even number of qubits where both are non-identity) — a strict superset of QWC, so each group holds more terms and fewer measurement circuits are needed. Diagonalisation needs an entangling Clifford (diagonalise_group()).
- class qarp.operators.GroupingStrategy[source]¶
Bases:
ABCPartition Pauli terms into groups measurable/exponentiable together.
- Contract:
group()returns an index partition ofterms— every index appears in exactly one group; group and within-group order must be deterministic given the input order (circuit identity depends on it).Partitioning strategies must be order-insensitive: permuting
termsmust permute the partition, not change it. Greedy first-fit is order-sensitive, so built-in strategies route throughcanonical_term_order(). Without this the same Hamiltonian written two ways gives two circuits — and for symmetry-carrying operators, two different physics (seetest_grouping_is_order_insensitive).NoGroupingis exempt: it makes no grouping decision and deliberately preserves input order.Strategies never see coefficients — those stay positionally attached in the consumer, so one strategy serves measurement (real coeffs), Trotter (angles), and imaginary-time paths alike.
qubit_wise = Trueguarantees every group is qubit-wise commuting, i.e. diagonalisable by single-qubit rotations only (group_basis()applies). Consumers that cannot insert an entangling Clifford (circuit cutting) require this and must check it.
- class qarp.operators.JordanWigner[source]¶
Bases:
MappingA class for mapping FermionOperators to qubit operators using the Jordan-Wigner mapping.
- encode_operator(op: list[FermionOperator] | FermionOperator)[source]¶
Perform a Jordan-Wigner mapping of the provided operator(s).
- Parameters:
op – A list of, or single, FermionOperator object.
- Returns:
The result of the mapping as a list of, or single, QubitOperator.
- class qarp.operators.LinearCombinationUnitaries(A: ndarray, *, pad: bool = True, tol: float = 1e-12)[source]¶
Bases:
objectPauli/LCU decomposition of a matrix.
The decomposition represents a square power-of-two matrix as
A = sum_P c_P P
where P is a tensor product of single-qubit Pauli matrices. If the input matrix is not square power-of-two sized and
pad=True, it is decomposed after zero-padding to the next square power-of-two dimension.Ais read in the qarpx LSB basis (row/column bitk↔ qubitk, conventions §1) and Pauli strings are qubit-ordered: characterkacts on qubitk, so"XZ"isZ_1 X_0 = kron(Z, X). This is the same indexing asQubitOperatorterms andPauliBlockstrings, andto_QubitOperator().sparse_matrix()reproducesA.- adjust_padding(matrix: ndarray | None = None) ndarray[source]¶
Return a zero-padded square power-of-two copy of
matrix.
- assert_qubitlike_shape(matrix: ndarray | None = None) bool[source]¶
Return True when the matrix is square with dimension
2**n.
- decomposition(tol: float | None = None) List[Tuple[str, complex]][source]¶
Return the Pauli decomposition as
[(pauli_string, coefficient), ...].
- itpd_core(matrix: ndarray) ndarray[source]¶
Inverse TPD transform: restore the standard matrix representation.
- pauli_basis2ppoly(cmat: ndarray, tol: float | None = None) List[Tuple[str, complex]][source]¶
Transform a Pauli-basis coefficient matrix into Pauli-string terms.
- pauli_str_to_matrix(pauli_string: str) ndarray[source]¶
Convert a qubit-ordered Pauli string such as
'XXYI'to its LSB matrixP_{n-1} ⊗ … ⊗ P_0.
- ppoly2pauli_basis(ppoly: List[Tuple[str, complex]], *, nqubits: int | None = None) ndarray[source]¶
Convert weighted Pauli strings into the TPD coefficient matrix.
- pstr2sym_code(pstr: str, sim_code: dict[str, Tuple[int, int]] | None = None) Tuple[int, int][source]¶
- reconstruct(ppoly: List[Tuple[str, complex]], *, nqubits: int | None = None, crop: bool = False) ndarray[source]¶
Reconstruct a matrix from a Pauli decomposition.
If
crop=True, the reconstructed padded matrix is cropped back to the original input shape.
- class qarp.operators.Mapping[source]¶
Bases:
ABCAbstract base class for fermion-to-qubit mappings.
- abstractmethod encode_operator(op: list[FermionOperator] | FermionOperator) list[QubitOperator] | QubitOperator[source]¶
Map the provided FermionOperator(s) to QubitOperator(s).
- class qarp.operators.NoGrouping[source]¶
Bases:
GroupingStrategyOne term per group — the termwise (circuit-per-term) behaviour.
- class qarp.operators.Parity(n_qubits: int)[source]¶
Bases:
MappingA class for mapping FermionOperators to qubit operators using the parity mapping. :param n_qubits: number of qubits in the qubit register.
- encode_operator(op: list[FermionOperator] | FermionOperator)[source]¶
Perform a parity mapping of the provided operator(s).
The transform runs in the qarpx C++ kernel (a verbatim port of the previous hand-rolled Python implementation, term order included).
- Parameters:
op – A list of, or single, FermionOperator object.
- Returns:
The result of the mapping as a list of, or single, QubitOperator.
- class qarp.operators.QubitOperator(*args, **kwargs)¶
Bases:
objectopenfermion-compatible QubitOperator backed by a packed binary-symplectic C++ core (PauliEngine design, arXiv:2601.02233).
- coefficient(self, term: object) object¶
Fast single-term coefficient lookup (qarpx extra, not openfermion).
- property constant¶
(self) -> complex
- get_operators(self) list[qarpx.QubitOperator]¶
- hermitian_conjugated(self) qarpx.QubitOperator¶
- identity = <nanobind.nb_func object>¶
- isclose(self, other: qarpx.QubitOperator, tol: float = 1e-08) bool¶
- sparse_matrix(n_qubits=None)¶
qarpx-LSB
scipy.sparse.csc_matrixof the operator (qubit q ↔ bit q).The only matrix API (conventions §1) — directly contractable with qarpx statevectors / unitaries. openfermion’s MSB layout, for interop only, is
qarp.operators.compat.get_sparse_operator().
- substitute(self, values: object) qarpx.QubitOperator¶
Substitute named symbols with numeric values; demotes to the numeric backend when no free symbols remain (qarpx extra).
- property terms¶
Term dictionary (read-only view; assign a full dict to replace).
- zero = <nanobind.nb_func object>¶
- class qarp.operators.QubitWiseCommuting[source]¶
Bases:
GroupingStrategyGreedy first-fit under qubit-wise commutation.
QWC is the special case of commutation where every shared qubit carries the same Pauli, so a group diagonalises with a per-qubit basis change (H for X, Sdg·H for Y) — no entangling Clifford needed.
- qarp.operators.basis_rotation_grouping(integrals_1e: NDArray, integrals_2e: NDArray, tolerance: float = 1e-08) tuple[list[float], list[FermionOperator], list[NDArray]][source]¶
Group a molecular Hamiltonian by basis-rotated number-operator sets.
Uses the spin-summed excitation operators
E_pq = Σ_σ a†_pσ a_qσ:H = const + Σ_pq h'_pq E_pq + ½ Σ_r λ_r (Σ_p ν_r[p] ñ_p)²with
h'_ps = h_ps − ½ Σ_q (pq|qs)absorbing the reordering term andñ_p = n_2p + n_2p+1the spin-summed number operator. Each group is therefore diagonal once the spatial rotation is applied to both spin sublattices.- Parameters:
integrals_1e – Spatial one-electron matrix, chemists’ MO basis.
integrals_2e – Spatial two-electron order-4 tensor, chemists’ (pq|rs).
tolerance – Relative factor-discard threshold (see
double_factorization()) — higher discards more factors (fewer groups, larger deterministic bias).
- Returns:
per-group coefficients
c_ℓ(c₀ = 1for the corrected one-body group, then the descending-|λ| two-electron factors), diagonal number-operatorFermionOperator``s ``G_ℓ, and real orthogonal spin-orbital (abab) rotation matricesu_ℓ. Reconstruction contract, pinned by the re-summation test:H = constant + Σ_ℓ c_ℓ · U(u_ℓ) G_ℓ U(u_ℓ)†.- Return type:
(coefficients, groups, rotations)
- qarp.operators.diagonal_group_to_masks(operator: FermionOperator, n_qubits: int) tuple[float, dict[int, float]][source]¶
Expand a number-operator polynomial into Z-parity masks.
n_p = (1 − Z_p)/2— the Jordan-Wigner occupation identity, matching the JW-only rotation circuit — so a diagonal group becomes a constant plus{z_mask: coefficient}. This is the bridge from the fermionic groups to the shot post-processing:run()consumes the masks directly on computational-basis counts.- Raises:
ValueError – for any term that is not (), a†_p a_p, or a†_p a_p a†_q a_q.
- qarp.operators.double_factorization(tensor: NDArray, tolerance: float = 1e-08) tuple[NDArray, NDArray, NDArray][source]¶
Double low-rank factorization
T[p,q,r,s] = Σ_r λ_r L_r[p,q] L_r[r,s]withL_r = u_r diag(ν_r) u_rᵀ.- Parameters:
tensor – Order-4 tensor, square in every axis and symmetric under
(pq)↔(qp),(rs)↔(sr)and(pq)↔(rs). Both eigendecompositions need that symmetry, and neither reports its absence:eighreads one triangle and returns a wrong answer.tolerance – First-stage factors with
|λ| <= tolerance · max|λ|are discarded — a deterministic truncation bias, not noise. The cut is relative because the scale ofλis system-dependent (max|λ|spans ~1–15 across STO-3G molecules), so no absolute threshold is portable. Exact-null factors are always dropped: they are the tensor’s null space, not truncation.
References
PennyLane
qchem.factorize; InQuanto’s double-factorization manual page — both document the same two-eigendecomposition construction.- Returns:
surviving eigenvalues sorted by descending
|λ|(shape(L,)), their factors’ eigenvalue vectors ((L, n)) and eigenvector matrices ((L, n, n), eigenvectors in columns).- Return type:
(λ, ν, u)
- qarp.operators.fermion_operator_from_tensor(tensor: NDArray, threshold: float = 1e-12) FermionOperator[source]¶
Build the k-body FermionOperator of a 2k-index spin-orbital tensor.
T[p1..pk, q1..qk]contributesT · a†_p1 … a†_pk · a_q1 … a_qk.- Parameters:
tensor – A 2k-index numpy tensor over spin orbitals (k = 1, 2, 3, …).
threshold – Entries with absolute value below this are dropped.
- Returns:
The corresponding FermionOperator.
- qarp.operators.group_basis(group_indices: Sequence[int], pauli_dicts: Sequence[Dict[int, str]]) Dict[int, str][source]¶
Per-qubit basis (
'X'/'Y'/'Z') for a QWC group.By the QWC property every term agrees on each shared qubit, so the union over the group’s terms gives the basis-change recipe. Qubits absent from every term are omitted (no basis change needed).
- qarp.operators.orbital_rotation_generator(parameters: NDArray, n: int) NDArray[source]¶
Real skew-symmetric generator
kappafrom itsn(n-1)/2free entries.The
(n, n)matrixorbital_rotation_matrix()exponentiates.kappa[p, q] = parameters[k]for(p, q)innp.tril_indices(n, -1)order,kappa[q, p] = -kappa[p, q], zero diagonal — the flat real vector an optimiser drives.- Parameters:
parameters – Real vector of length
n * (n - 1) // 2.n – Number of orbitals.
- Returns:
The real skew-symmetric
(n, n)generator.- Raises:
ValueError – wrong parameter count, or complex parameters.
- qarp.operators.orbital_rotation_matrix(kappa: NDArray) NDArray[source]¶
U = exp(-kappa)for an anti-Hermitian generatorkappa.New orbitals are the columns of
U, soC_new = C_old @ Uandrotate_tensor(U, tensor)expressestensorin the new basis. Real skew-symmetrickappagives a real orthogonalU, which may be applied to chemists’-notation integrals; complex anti-Hermitiankappagives a unitaryUthat is only valid on creators-first spin-orbital tensors (the caveat ofrotate_tensor()). For the JW circuit of a spatial rotation expand first withqarp.operators.integrals.spatial_to_spin_orbital().- Parameters:
kappa – Square anti-Hermitian matrix,
kappa == -kappa.conj().T.- Returns:
The
(n, n)rotationexpm(-kappa); real whenkappais real.- Raises:
ValueError –
kappais not a square 2-D array, or is not anti-Hermitian (checked withrtol=0, atol=1e-12).
- qarp.operators.orbital_rotation_parameters(kappa: NDArray) NDArray[source]¶
Inverse of
orbital_rotation_generator().The packed parameter vector of a real skew-symmetric generator: strict-lower-triangle entries in
np.tril_indices(n, -1)order.- Parameters:
kappa – Real skew-symmetric
(n, n)matrix.- Returns:
Real vector of length
n * (n - 1) // 2.- Raises:
ValueError –
kappais not a real square skew-symmetric matrix (checked withrtol=0, atol=1e-12).
- class qarp.operators.qDRIFT(H: QubitOperator, samples: int, ratio: float | None = None, verbose: bool = False, seed: int | None = None)[source]¶
Bases:
object- partially_randomized() QubitOperator[source]¶
Apply partially randomized qDRIFT approximation.
Keeps the most significant terms deterministically and samples the remaining terms randomly according to their weights.
- Returns:
Approximate QubitOperator with deterministic + sampled terms
- Raises:
ValueError – If ratio was not provided during initialization
- qdrift() QubitOperator[source]¶
Apply fully randomized qDRIFT approximation.
Samples Pauli terms according to their weight distribution and constructs an approximate Hamiltonian.
- Returns:
Approximate QubitOperator with sampled terms
- Raises:
ValueError – If samples is 0 (at least 1 sample required)
- qarp.operators.rotate_tensor(u: NDArray, tensor: NDArray) NDArray[source]¶
Change of single-particle basis for a 2k-index coefficient tensor.
In the creators-first convention of
fermion_operator_from_tensor(), creator slots contract withu.conj()and annihilator slots withu— the rank-2k generalization of \(u^\dagger h u\), valid for complex unitaries. The new modes are the columns ofu; a rectangular(n, m)matrix projects into an m-mode basis (truncation).For real orthogonal
uevery slot transforms identically, so the same function also rotates chemists’-notation integral tensors (where creator/annihilator slots alternate).- Parameters:
u – The (n, m) basis-change matrix; new mode p is column p.
tensor – A 2k-index tensor over the n old modes.
- Returns:
The tensor over the m new modes.