Skip to content

refactor(ga): simplify ReciprocalFrame with clearer loop structure#553

Merged
utensil merged 3 commits intopygae:masterfrom
utiberious:fix/issue-249-reciprocal-frame
Mar 31, 2026
Merged

refactor(ga): simplify ReciprocalFrame with clearer loop structure#553
utensil merged 3 commits intopygae:masterfrom
utiberious:fix/issue-249-reciprocal-frame

Conversation

@utiberious
Copy link
Copy Markdown
Contributor

Summary

Simplifies the ReciprocalFrame implementation with a cleaner loop structure while preserving the mathematical correctness of the reciprocal frame computation.

Fixes #249

Changes

  • galgebra/ga.py: Restructure ReciprocalFrame with clearer variable naming and simplified loop
  • examples/ipython/Old Format.ipynb: Update notebook outputs to match corrected reciprocal frame (reciprocal vectors now satisfy e_i . e^j = delta_ij)

Test plan

  • Unit tests pass
  • Full CI with nbval notebooks passes (Old Format notebook updated)

Replace the convoluted index/combinations approach with a clearer
loop that directly computes E as the wedge of all basis vectors, then
forms each reciprocal vector by wedging all basis vectors except the
i-th and multiplying by E with the appropriate sign.

Fixes #5
The reciprocal frame fix (issue pygae#249) changes the signs of the reciprocal
vectors, making eu.eu_r = 1 instead of -1. Update the saved notebook
output to reflect the mathematically correct results.
@review-notebook-app
Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@utensil
Copy link
Copy Markdown
Member

utensil commented Mar 31, 2026

Thanks for the cleanup — the loop structure is much cleaner.

One math issue: the sign formula (-1)^(n-1-i) is wrong for even-dimensional frames. The notebook diff shows this: the 2D example gives eu.eu_r = -1 instead of 1, violating $e_i \cdot e^j = \delta_{ij}$. The 3D unit test passes accidentally — for odd $n$, $(-1)^{n-1-i} = (-1)^i$, so it's correct by coincidence. For $n=2$ (even), all signs flip.

The correct sign is $(-1)^i$ (moving basis[i] to position 0 requires exactly $i$ transpositions):

sgn = S.NegativeOne ** i

Please also add a 2D test case to test_ReciprocalFrame so this stays covered:

ga2, e1, e2 = Ga.build('e*1|2', g=[1, 1])
basis2 = [e1 + e2, e1 - e2]
r_basis2 = ga2.ReciprocalFrame(basis2)
for i, base in enumerate(basis2):
    for j, rbase in enumerate(r_basis2):
        assert (base | rbase).simplify() == (1 if i == j else 0)

The sign (-1)^(n-1-i) was wrong for even-dimensional frames (e.g. 2D),
giving eu.eu_r = -1 instead of 1. The correct sign is (-1)^i (moving
basis[i] to position 0 requires exactly i transpositions).

Add 2D test case to catch even-dimension sign bugs.
@utiberious
Copy link
Copy Markdown
Contributor Author

Good catch on the sign formula. Fixed:

  • Changed (-1)^(n-1-i) to (-1)^i for the reciprocal frame sign. The old formula worked for odd n by coincidence but was wrong for even n.
  • Added 2D test case with non-orthogonal basis [e1+e2, e1-e2] to cover even dimensions.
  • Re-executed Old Format notebook with corrected outputs.

Pushed in 741a586.

@utensil utensil added the component: core Ga, Mv, Metric, etc label Mar 31, 2026
@utensil utensil merged commit 721df72 into pygae:master Mar 31, 2026
6 checks passed
@utensil utensil added this to the 0.6.0 milestone Mar 31, 2026
utiberious added a commit to utiberious/galgebra that referenced this pull request Apr 1, 2026
Groups new entries by: features, bug fixes, examples/docs, tests/maintenance.

Features: Cl() kingdon interface (pygae#550, closes pygae#524), Mv.__rtruediv__
(pygae#543, closes pygae#512), shirokov_inverse/hitzer_inverse (pygae#530).

Bugs: interop dual mode contamination (pygae#556, closes pygae#555), norm() Abs
wrapping (pygae#554, closes pygae#522), is_versor() improvement (pygae#536, closes pygae#533).

Examples/docs: sundial + cheatsheet tests (pygae#549+pygae#557, closes pygae#506),
coords tutorial (pygae#551), README ops (pygae#548, closes pygae#523).

Tests/maintenance: lt.matrix() regression tests (pygae#558, closes pygae#461),
extra-cdot regression test (pygae#545), er_blade + ReciprocalFrame refactors
(pygae#552+pygae#553), CI fix (pygae#535).
utensil pushed a commit that referenced this pull request Apr 1, 2026
* docs: add 0.6.0 changelog entries

Groups new entries by: features, bug fixes, examples/docs, tests/maintenance.

Features: Cl() kingdon interface (#550, closes #524), Mv.__rtruediv__
(#543, closes #512), shirokov_inverse/hitzer_inverse (#530).

Bugs: interop dual mode contamination (#556, closes #555), norm() Abs
wrapping (#554, closes #522), is_versor() improvement (#536, closes #533).

Examples/docs: sundial + cheatsheet tests (#549+#557, closes #506),
coords tutorial (#551), README ops (#548, closes #523).

Tests/maintenance: lt.matrix() regression tests (#558, closes #461),
extra-cdot regression test (#545), er_blade + ReciprocalFrame refactors
(#552+#553), CI fix (#535).

* docs: add missing issue link for #551 entry

* docs: add changelog entry for #560 (Lt callable zero fix)

* docs: move Lt zero fix into bug group, use issue #540 as reference
utiberious added a commit to utiberious/galgebra that referenced this pull request Apr 2, 2026
utiberious added a commit to utiberious/galgebra that referenced this pull request Apr 2, 2026
* docs: add 0.6.0 changelog entries

Groups new entries by: features, bug fixes, examples/docs, tests/maintenance.

Features: Cl() kingdon interface (pygae#550, closes pygae#524), Mv.__rtruediv__
(pygae#543, closes pygae#512), shirokov_inverse/hitzer_inverse (pygae#530).

Bugs: interop dual mode contamination (pygae#556, closes pygae#555), norm() Abs
wrapping (pygae#554, closes pygae#522), is_versor() improvement (pygae#536, closes pygae#533).

Examples/docs: sundial + cheatsheet tests (pygae#549+pygae#557, closes pygae#506),
coords tutorial (pygae#551), README ops (pygae#548, closes pygae#523).

Tests/maintenance: lt.matrix() regression tests (pygae#558, closes pygae#461),
extra-cdot regression test (pygae#545), er_blade + ReciprocalFrame refactors
(pygae#552+pygae#553), CI fix (pygae#535).

* docs: add missing issue link for pygae#551 entry

* docs: add changelog entry for pygae#560 (Lt callable zero fix)

* docs: move Lt zero fix into bug group, use issue pygae#540 as reference
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: core Ga, Mv, Metric, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants