feat: add get_algebra_basis for defining-rep matrix bases of classifi…#212
feat: add get_algebra_basis for defining-rep matrix bases of classifi…#212Adithyaphani wants to merge 8 commits into
Conversation
|
@AmanieOxana looking forward to your response on this PR and it looks cleanly merged with the base branch with no conflicts.I hope this branch gets merged , moreover I successfully registered for UnitaryHack 2026 . |
|
@AmanieOxana looking forward to your response and is any review is done from your side ? and happy to make any changes if needed. |
|
Hi, some of your tests seems very inefficient, also you may want to probe element-wise reachability ( I've heard people are confused about this because of the scope description but that's what we need for testing) |
|
@AmanieOxana Thanks for the feedback. The latest commit (b28d129) addresses both points. On efficiency — the closure tests now use a pre-allocated orthonormal buffer On element-wise reachability — added TestElementWiseReachability which checks |
| Returns | ||
| ------- | ||
| list[np.ndarray] | ||
| One array per direct summand. Shapes follow the module-level |
There was a problem hiding this comment.
Hi, I am unsure if that works. So you are currently just stacking basis of each block in an array.
The idea was to have a basis for the complete operator.
There was a problem hiding this comment.
@AmanieOxana Fixed. algebra_basis_from_label now returns a single ndarray with block-diagonal
embedding for k-summand algebras: shape (kdim, kM, k*M) where summand i
occupies the i-th M×M diagonal block. The old code was returning k identical
copies of the summand basis which didn't represent the complete operator.
Also fixed the dispatch bug in classification.py: the morph-based path was
calling u1_basis() for all TypeAlgebra.U morphs regardless of the actual
algebra. get_algebra_basis() now delegates to algebra_basis_from_label(self.get_algebra())
which correctly parses the label returned by get_algebra().
| # so(N) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
|
|
There was a problem hiding this comment.
The same issue starts here. You have a direct sum, so the entire operator is not in so(N) for example and does not satisfies this condition
There was a problem hiding this comment.
@AmanieOxana the test was using p(["XY","YX"], n=2) which classifies as 2u(1),
not a type A so algebra. Changed to p(["XY","YX","ZI","IZ"], n=2) which
gives 2so(3). After the block-diagonal fix each basis element is a 6×6
real antisymmetric matrix and the condition holds correctly.
| _check_bracket_closure(b) | ||
|
|
||
| @pytest.mark.parametrize("gens,n", [ | ||
| (["XY", "XZ"], 4), |
There was a problem hiding this comment.
we have build in code for this Glie
There was a problem hiding this comment.
@AmanieOxana Thanks for pointing that out. Replaced the matrix-based _lie_closure with
PauLie's native approach using PauliString.commutes_with() and the @
operator, matching the pattern in docs/examples/benchmark_classification.py.
The DLA dimension check now uses this directly in test_basis_dim_matches_lie_closure_rank.
|
@AmanieOxana Pushed aec83f1 addressing all three points. |
|
@AmanieOxana resolved the merge conflict with upstream in Looking forward to your response and happy to make further changes if needed. |
|
Thanks, there are some changes required:
|
|
It is necessary to do a rebase to resolve conflicts |
…ed DLAs Closes QPauLie#200 Adds get_algebra_basis() alongside get_algebra(). Table-driven: output is fully determined by the classification label, no input-dependent work. Constructors (src/paulie/application/algebra_basis.py): so(N): {E_ij - E_ji : i<j}, lex order, float64 su(N): traceless anti-Hermitian (Gell-Mann x i), complex128 sp(N): 2Nx2N symplectic J=[[0,I],[-I,0]], three block families, float64 u(1): [[i]], complex128 Tests (66 new, 905 total passing): - Defining condition per family (antisymmetry / symplectic / anti-Hermitian) - Full rank (linear independence) - Lie bracket closure: every [B_i,B_j] in span(basis) for all i<j -- direct algebraic soundness, not a dimension proxy - Total dim == get_dla_dim() for all canonical types - Summand count consistent with algebra label (handles k*algebra(N) format) - Basis dim == rank of brute-force DLA matrix stack via adjoint_map + get_matrix() -- the reachability check the maintainer asked for
…-based dispatch in get_algebra_basis
8a10fe2 to
59bcd19
Compare
|
@AmanieOxana and @gksmail rebased on upstream/main (59bcd19), uv.lock regenerated, 990 tests passing. |
|
@AmanieOxana and @gksmail looking forward to your responses and happy to make further changes if needed. |
|
Thanks.
|
|
@AmanieOxana and @gksmail Fixed the C0415 lint failure in d66e9b8 — moved algebra_basis_from_label import to the top of classification.py. All checks passing locally: ruff clean, pylint 10/10 across all files, mypy clean, 990 tests |
…rror, Majorana test uses get_algebra_basis() output
|
@AmanieOxana and @gksmail addressed the review points in the latest commit: Looking forward to your response and happy to make further changes if needed. |
|
Hi, I appreciate the effort but I become a bit suspicious of the amount of LLM use at this point. It's not the purpose that we pingpong until the LLM is converging to a correct solution. You should see that as an opportunity to understand a problem well enough to come up with a correct solution. |
Closes #200
get_algebrareturns an isomorphism label. This addsget_algebra_basis()at the same API level — it returns the named algebra as concrete matrices in its defining representation, onenp.ndarrayper direct summand.Architecture
PauliStringCollectiondelegates toClassification, which reads(TypeAlgebra, nc, n)from eachMorphand dispatches to the four constructors in the newalgebra_basis.pymodule.What was implemented
Table-driven: output is fully determined by the classification label — no input-dependent computation. Three constructors in a new module
src/paulie/application/algebra_basis.py:Dispatch is via
TypeAlgebradirectly frommorph.get_algebra_properties()— no string parsing. Thenparameter returned byget_algebra_properties()is passed unchanged to each constructor.Canonical types covered
[
UPLOAD IMAGE: d3_algebra_types.png]
All four canonical DLA families (A, B1, B2, B3) with their algebra family,
parameter formula, output shape, and constructor.
Quick example
Basis conventions (ordering, sign choices, choice of
Jforsp) are documented insrc/paulie/application/algebra_basis.py.Tests — 66 new, 905 total passing, 0 regressions
Three verification levels
1. Defining condition — 48 tests
Per-constructor checks run for
N ∈ {2, 3, 4, 5}:so(N): every matrix satisfiesm + m.T == 0(antisymmetry)sp(N): every matrix satisfiesX.T @ J + J @ X == 0withJ = [[0,I],[−I,0]]su(N): every matrix satisfiesm + m.conj().T == 0(anti-Hermitian) andtrace(m) == 02. Lie bracket closure — 9 tests
_check_bracket_closureverifies[B_i, B_j] ∈ span(basis)for every pairi < jvia least-squares residual. This is the direct algebraic completeness check — a correct Lie algebra basis must be closed under the bracket. Checking only the dimension is not sufficient; this test verifies the basis actually generates a valid Lie algebra.3. DLA reachability — 3 tests
test_basis_dim_matches_lie_closure_rankbuilds the DLA via brute-forceadjoint_mapclosure, represents each element as a2^n × 2^nmatrix viaget_matrix(), computesrank(matrix stack), and asserts it equals the total basis dimension. This cross-checks the abstract defining-rep basis against the embedded DLA.I used Claude to help brainstorm the bracket-closure test strategy and the basis constructor structure, which I then ran locally, verified against the brute-force DLA closure, and confirmed across all 905 test cases.