Skip to content

Paulie bridge - #5

Open
AmanieOxana wants to merge 7 commits into
dwierichs:mainfrom
QPauLie:paulie-bridge
Open

Paulie bridge#5
AmanieOxana wants to merge 7 commits into
dwierichs:mainfrom
QPauLie:paulie-bridge

Conversation

@AmanieOxana

Copy link
Copy Markdown

No description provided.

claude added 7 commits August 5, 2026 22:25
recursive_bdi splits a block of width w into p = w // 2 and q = w - p, and the
resulting Cartan element rotates index i against index i + q. angles_to_reducible
used i + p instead. The two agree whenever w is even, which is every block the
transverse-field XY model on a power-of-two number of qubits produces, so the
existing tests never saw it.

For odd widths -- so(6), so(10), so(12), so(14) and up -- the angles were
assigned to the wrong Pauli words, and the failure was silent: no exception, just
a decomposition that no longer multiplies back to the unitary it came from. The
odd case is the "to do: debug odd-n case" on TestFDHSCompilation.

Same offset in angles_to_reducible_str.
For a horizontal u the decomposition u = k1 a k2 satisfies k1 = k2.T, but scipy's
cossin does not know that and returns a representative fixed only up to a
diagonal sign D = diag(+-1):

    (k1, a, k2) -> (k1, a diag(D, D), diag(D, D) k2)

which leaves k1 @ a @ k2 alone and shifts the affected angles by pi. bdi checked
for the mismatch and raised a bare ValueError, with unreachable repair code after
it. Whether it triggered depended on the Hamiltonian: at so(4) roughly three
quarters of random coefficient draws hit it.

The mismatch is now repaired for p == q by flipping the affected rows of k21 and
k22 and shifting theta by pi. A column that matches neither +k21[i] nor -k21[i]
still raises, but now says what it means -- u does not look horizontal -- and
suggests is_horizontal=False.
group_matrix_to_reducible discovered its rotation planes from the non-zero
off-diagonal entries and read each angle as arcsin of one of them, patching the
sign from the diagonal. A 2x2 block equal to diag(-1, -1) is a rotation by pi: it
has no off-diagonal entry at all, so np.where never found it and the factor was
dropped without complaint.

Angles now come from arctan2(matrix[i, j], matrix[i, i]), which is the same value
where the old code worked and correct where it did not, and indices left over in
the diagonal block are paired up and emitted as pi rotations. An odd number of
-1s means determinant -1, which is not a product of rotations, and is now
asserted against rather than silently mapped to something wrong.

A translation-invariant Hamiltonian -- uniform coefficients -- is what lands on
this.
Two things in the closure loop.

`warnings` was never imported, so reaching max_iterations raised NameError from
the line meant to warn about it.

`com not in dla` tested against a list, which is O(dim) per commutator and makes
the whole closure O(dim^3). A set alongside the list keeps the returned order and
makes membership O(1). The full_size hint now also returns as soon as the algebra
is complete, rather than finishing the sweep first.

The iteration range is bounded by new_length rather than the open-ended
dla[old_length:], so appending during the loop cannot extend it mid-pass.
map_simple_to_irrep returns (mapping, signs) with mapping sending nodes to Pauli
words, but irrep_dot expected mapping to send nodes to (word, sign) pairs and
took no signs argument. Every call in full_workflows passed signs, so
complete_workflow_tfXY and workflow_tfXY_known_algebra raised

    TypeError: irrep_dot() got multiple values for argument 'n'

on every invocation. irrep_dot now takes both forms, and the old positional
signature still works.

While there: the full_workflows entry points took a `coefficients` argument and
then passed the literal "random" to make_tfXY_hamiltonian_qubits, so asking for
uniform coefficients silently gave random ones.
pennylane.labs.dla.structure_constants_dense has moved; it is now
qml.structure_constants(..., matrix=True). The import failed at collection time,
so the whole module was skipped. A small shim tries the old location first and
falls back, so the test runs on either.
kak_tools needs to know which algebra it is decomposing -- the irrep size n and a
Cartan involution. That was either hard-coded (n_so = 2 * n in full_workflows) or
guessed from the dimension by identify_algebra, which is deliberately non-unique:
a 21-dimensional simple DLA is consistent with both so(7) and sp(3), and those
need different involutions.

PauLie (https://github.com/QPauLie/PauLie) classifies the dynamical Lie algebra
of a Pauli generator set exactly and in polynomial time. kak_tools/paulie_bridge.py
feeds that classification into the existing pipeline:

  classify_dla          PauLie's classification, wrapped as a DLAInfo
  dla_pauli_basis       Pauli-word basis, dimension known before the closure runs
  labelled_matrix_basis PauLie's so(m) basis, labelled with the Pauli words
  map_dla_to_irrep      map_simple_to_irrep with n from the classification
  kak_decomposition     generators in, verified Pauli rotations out

DLAInfo holds PauLie's Classification and forwards to it -- get_algebra,
get_dla_dim, get_subalgebras, get_algebra_basis, is_algebra -- rather than
recomputing any of it. The so(m) identification is is_algebra plus kak_tools'
own get_simple_dim, not a local table of isomorphisms, so the low-rank
coincidences come out right: a DLA PauLie names 2*so(3) is decomposed as so(4).

get_so_basis(m)[k] is exactly E(triu_indices(m, 1)[k], m, "BDI") / 2, so the two
packages agree on the basis element for element; a test asserts it rather than
trusting it. Note orthogonal_basis is not get_algebra_basis(): the latter follows
PauLie's naming, so 2*so(3) is based in 6x6 while kak_tools decomposes the
isomorphic so(4) in 4x4.

Scope: so(m) with a BDI involution, which is what map_simple_to_irrep implements.
Odd m is refused with an explanation -- the top-level split is BDI(p, p+1), whose
horizontal cosine-sine decomposition has a gauge freedom bdi does not fix.
kak_decomposition validates its own output by recomposing exp(t H) from the Pauli
rotations.

paulie becomes a dependency. Also adds tests, a runnable example over several
models, and a README section.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants