Skip to content

fix: deferred-simp context manager for Ga.build in curvi_linear_latex (#576)#592

Open
utiberious wants to merge 3 commits intopygae:masterfrom
utiberious:fix/issue-576-deferred-simp
Open

fix: deferred-simp context manager for Ga.build in curvi_linear_latex (#576)#592
utiberious wants to merge 3 commits intopygae:masterfrom
utiberious:fix/issue-576-deferred-simp

Conversation

@utiberious
Copy link
Copy Markdown
Contributor

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 inside TR3/futrig in sympy.simplify.fu. The Ga.build(norm=True) call in curvilinear-coordinate examples invokes Simp.apply ~70 times during metric normalisation, each triggering simplify → 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 replaces Simp.modes with an identity simplifier [lambda e: e] around each Ga.build() call. This skips all ~70 redundant Simp.apply traversals during metric normalisation (the intermediate metric components are never printed, so unsimplified storage is fine).

Phase 2 — output: The trigsimp(method='old') profile set in main() is still active when _sympystr calls Simp.apply once per expression at formatting time, avoiding TR3 on the final results.

@contextmanager
def _no_simp_build():
    orig = Simp.modes[:]
    Simp.profile([lambda e: e])  # identity during build
    try:
        yield
    finally:
        Simp.profile(orig)          # restore trigsimp(method='old')

# In each function:
with _no_simp_build():
    (sp3d, er, eth, ephi) = Ga.build(...)

Outcome

#590 (trigsimp-old profile only) This PR (deferred build + trigsimp-old)
Build time (prolate spheroidal) ~1.5 s ~0.5 s
Total run time ~5 s ~3.7 s
Output trigsimp(method='old') canonical form Identical

Both approaches produce the same output and both fix the CI timeout.

Alternative investigated but does not work

Replacing trigsimp(method='old') with plain simplify() 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 spheroidal grad|A.

Comparison

The structural difference from #590 is that _no_simp_build() makes explicit which Simp.apply calls 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 on trigsimp(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

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.
@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

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.

Investigate CI slowdown since Python 3.12 bump

1 participant