Skip to content

Algebra and States#

Beyond the metrics computed from a Model, the algebra and states modules provide dynamical Lie algebra (DLA) tooling and state-preparation utilities used for trainability and barren-plateau analysis.

DLA and g-purity#

Beyond circuit execution, the algebra module provides dynamical Lie algebra (DLA) helpers used for trainability and barren-plateau analysis. The matchgate algebra \(\mathfrak{so}(2n)\) is available both as a generating set and as an explicit Pauli-string basis, and the latter must match the Lie closure of the former:

from qml_essentials.algebra import (
    matchgate_generators,
    matchgate_basis,
    dim_so2n,
    lie_closure_paulis,
    g_purity_from_basis,
)

n = 3
gens = matchgate_generators(n)         # {Z_k} u {X_k X_{k+1}}
basis = matchgate_basis(n)             # the n(2n-1) Pauli strings of so(2n)
assert len(basis) == dim_so2n(n)
assert {pw.to_pauli_string() for pw in lie_closure_paulis(gens)} == set(basis)

An ansatz that saturates \(\mathfrak{su}(2^n)\) has a closure of \(4^n - 1\) words, which quickly becomes intractable to enumerate. Pass max_dim to stop the growth early; a result of that length means \(\dim \mathfrak{g} \geq\) max_dim and the basis is partial, so it is only meaningful as a dimension bound:

capped = lie_closure_paulis(gens, max_dim=dim_so2n(n))
assert len(capped) == dim_so2n(n)      # so(2n) is reached exactly

The g-purity \(P_g = \sum_B \langle\psi\lvert B\rvert\psi\rangle^2\) of a statevector with respect to a DLA basis measures how much of the state lies in the algebra:

import numpy as np

psi = np.zeros(2**n, dtype=complex)
psi[0] = 1.0                           # |0...0>
print(g_purity_from_basis(psi, basis)) # 3.0 (only the on-site Z_k contribute)

g_purity_from_basis takes a Pauli-word basis (strings or PauliWord objects). For a Hilbert-Schmidt-orthonormal Hermitian matrix basis, for example the output of lie_closure_matrices, use g_purity_matrix instead.

Permutation-symmetric operators and input states#

The symmetric_pauli_sum constructor sums a Pauli over all subsets of a given size, e.g. \(\sum_k X_k\) (locality=1) or \(\sum_{j<k} X_j X_k\) (locality=2). The \(S_n\)-equivariant generators \(\{\sum_k X_k, \sum_k Y_k, \sum_{j<k} Z_j Z_k\}\) and observable \(O = \tfrac{2}{n(n-1)} \sum_{j<k} X_j X_k\) build on it, and the generators feed the matrix DLA:

import numpy as np
from qml_essentials.algebra import (
    sn_equivariant_generators,
    lie_closure_matrices,
    g_purity_matrix,
)
from qml_essentials.states import dicke_state, haar_state, graph_state_vector, path_edges

n = 4
basis = lie_closure_matrices(sn_equivariant_generators(n))   # HS-orthonormal DLA basis

for psi in (dicke_state(n, 2), haar_state(n, seed=0), graph_state_vector(n, path_edges(n))):
    print(g_purity_matrix(psi, basis))

The same statevectors can be evolved through a circuit by passing them as the initial state:

from jaqsi import Script
from qml_essentials.ansaetze import Ansaetze
import jaqsi

def circ():
    Ansaetze.Permutation_Equivariant.build(np.array([0.7, 1.1, 0.5]), n)

script = Script(circ, n_qubits=n)
zs = script.execute(type="expval", obs=[jaqsi.PauliZ(q) for q in range(n)], initial_state=dicke_state(n, 2))