fix: deferred-simp context manager for Ga.build in curvi_linear_latex (#576)#592
Open
utiberious wants to merge 3 commits intopygae:masterfrom
Open
fix: deferred-simp context manager for Ga.build in curvi_linear_latex (#576)#592utiberious wants to merge 3 commits intopygae:masterfrom
utiberious wants to merge 3 commits intopygae:masterfrom
Conversation
Replaces the `Simp.profile([simplify])` default with a two-phase strategy to avoid the SymPy 1.13 TR3 O(N·M) traversal (PR #26390): 1. **Build phase** — `_no_simp_build()` context manager sets an identity simplifier (`Simp.profile([lambda e: e])`) around each `Ga.build()` call. The ~70 intermediate `Simp.apply` calls during metric normalisation now do nothing, cutting the build time from ~25 s/call to < 0.01 s/call. 2. **Output phase** — each result expression (gradient, divergence, curl, Laplacian, grad-of-bivector) is explicitly simplified once with `_ts()` = `Mv.simplify(modes=lambda e: trigsimp(e, method='old'))` before formatting. `trigsimp(method='old')` avoids `fu.py`/TR3 entirely. Alternative considered: replacing the explicit `trigsimp(method='old')` in `_ts()` with plain `simplify()` to recover the pre-SymPy-1.13 canonical output form. This does **not** work — the un-simplified metric components folded into the gradient/divergence/curl expressions are large enough that TR3 is just as slow on the final result as it was on the intermediate metric components, causing the same > 10 min hang. Closes pygae#576 (alongside pygae#590)
…pygae#576) Refines the PR pygae#590 fix with a two-phase simplification strategy: 1. **Build phase** — `_no_simp_build()` context manager temporarily sets an identity simplifier (`Simp.profile([lambda e: e])`) inside each `Ga.build()` call, skipping ~70 `Simp.apply` calls during metric normalisation. These calls were triggering the slow O(N·M) TR3 traversal introduced in SymPy 1.13 PR #26390 for every intermediate metric component. 2. **Print phase** — the `trigsimp(method='old')` profile set in `main()` remains active when `_sympystr` calls `Simp.apply` once per output expression at formatting time. **Output** is identical to the PR pygae#590 approach (`Simp.profile` wrapping all function calls without `_no_simp_build`). Both approaches produce the same ~4 s total run time. The structural difference: this branch makes the build/output separation explicit in code rather than relying on a single outer profile. Alternative investigated: replacing `trigsimp(method='old')` in `_ts()` with plain `simplify()` to recover the pre-SymPy-1.13 canonical output. This does **not** work — the unsimplified metric components folded into the output expressions are large enough that TR3 remains slow on them, causing the same > 10 min hang as the original unpatched code. Closes pygae#576 (alongside pygae#590)
Updated stored outputs reflect the deferred-simp implementation: - identity simplifier during Ga.build (metric components unsimplified) - trigsimp(method='old') active during output formatting (_sympystr) Output is identical to the PR pygae#590 (fix/issue-576-notebook-simp) approach since both ultimately apply trigsimp(method='old') once per printed expression.
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion PR to #590, exploring an alternative code structure for the same SymPy 1.13 fix.
Root cause (same as #590)
SymPy 1.13 PR #26390 added a
.replace(Mul, ...)traversal insideTR3/futriginsympy.simplify.fu. TheGa.build(norm=True)call in curvilinear-coordinate examples invokesSimp.apply~70 times during metric normalisation, each triggeringsimplify → trigsimp → futrig → TR3. This scales as O(N·M) for mixed trig+hyperbolic expressions and causes > 10 min hang.Alternative approach explored here
Two-phase strategy, making the build/output separation explicit in code:
Phase 1 — build: A
_no_simp_build()context manager temporarily replacesSimp.modeswith an identity simplifier[lambda e: e]around eachGa.build()call. This skips all ~70 redundantSimp.applytraversals during metric normalisation (the intermediate metric components are never printed, so unsimplified storage is fine).Phase 2 — output: The
trigsimp(method='old')profile set inmain()is still active when_sympystrcallsSimp.applyonce per expression at formatting time, avoiding TR3 on the final results.Outcome
trigsimp(method='old')canonical formBoth approaches produce the same output and both fix the CI timeout.
Alternative investigated but does not work
Replacing
trigsimp(method='old')with plainsimplify()in the output phase (to recover the pre-SymPy-1.13 canonical form) does not help. The unsimplified metric components folded into the gradient/divergence/curl expressions make them large enough that TR3 is just as slow on the final result as on the intermediate metric components. The call hangs after > 10 min on prolate spheroidalgrad|A.Comparison
The structural difference from #590 is that
_no_simp_build()makes explicit whichSimp.applycalls are skipped (the ~70 build-phase calls) vs which are kept (the ~5 per-output formatting calls). PR #590 achieves the same result via a single outer profile but relies ontrigsimp(method='old')being idempotent on already-simplified metric components during build.Both approaches are correct. This PR exists to let us compare and decide which code structure to land.
Closes #576