Skip to content

A constant comes out of a fractional power beside another power of the same function - #1481

Merged
Rafael-SOWNet merged 2 commits into
masterfrom
distribute-a-constant
Sep 23, 2026
Merged

Rafael-SOWNet merged 2 commits into
masterfrom
distribute-a-constant

Conversation

@Rafael-SOWNet

@Rafael-SOWNet Rafael-SOWNet commented Sep 23, 2026

Copy link
Copy Markdown
Member

sqrt(b sec(x))/sec(x)^(7/2) was left as written. The two powers of the secant are the same function to a total power of -3, which the rules for a power of a secant answer — and they cannot see it while one of them is written over b sec(x) rather than over sec(x).

(c f)^b is c^b f^b for a positive real c and any f: both sides pick up the same phase where f is negative, so the rewrite needs nothing assumed about f, and a symbolic c gives the answer provided c > 0.

That condition is not decoration. 2.5.0 answered sec(x)^(3/2)/(b sec(x))^(5/2) without it, and that answer is wrong wherever b and the secant are both negative — at b = -3 its derivative is +0.0267i against the integrand's -0.0267i at x = 2, while the two agree at x = 0.5. #1388 withdrew the unconditional reading for exactly that reason, and this brings the shape back with the condition it owes.

Where it is asked, and what it takes, keeps it from costing anything elsewhere:

  • After the rules that answer the same shapes for any real constant. A function comes out of its even power with its sign — (a sin(x)^2)^(5/2) for every real a — and sqrt(a + a sin(x)) is integrated by the half angle, again for every real a. Asked before them this answered both provided a > 0; it is asked after them, and before the substitution search. The monomial split from A power of the variable times a sine or cosine of a logarithm, and a power of a monomial #1479 keeps its own, earlier place.
  • A single factor in which x enters only through trigonometric functions, with a constant written in every term of such a sum read too (a - a sin(x)^2 is a (1 - sin(x)^2)). The rewritten question is asked again at the same depth, so a rewrite that brings nothing closer multiplies the search at every level: over a product of trigonometric powers it cost 10 s → 47 s, and over csch(x), which is written in exponentials, it hung the test run — see the comment below.
  • Never the question it was asked. A rewrite that simplifies back to its input is declined rather than asked again.

Measured

before after
Rubi family 4 290/422 309/422, no row lost, 27 timeouts against 32
Rubi family 6 377/417 378/417
Rubi families 1, 5, 7 158, 212, 215 158, 212, 215, no row lost
Rubi corpus (1774) 1707, 0 wrong, 0 error, 0 timeout 1707, 0 wrong, 0 error, 0 timeout
suite 12,675 12,677, 0 failed, run to completion
allocation gate PASSED on all 19 gated benchmarks

Six tests added, and BREAKING-CHANGES.md records both changed answers with their 2.5.0 values.

Part of #718.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

Rafael-SOWNet and others added 2 commits September 23, 2026 13:43
…e same function

`sqrt(b sec(x))/sec(x)^(7/2)` was left as written. The two powers of the secant are the
same function to a total power of -3, which the rules for a power of a secant answer, and
they cannot see it while one of the two is written over `b sec(x)` rather than over
`sec(x)`.

`(c f)^b` is `c^b f^b` for a positive real `c` and any `f` -- both sides pick up the same
phase where `f` is negative, so nothing need be assumed about `f`, and a symbolic `c` gives
the answer `provided c > 0`. That condition is not decoration: 2.5.0 answered
`sec(x)^(3/2)/(b sec(x))^(5/2)` without it, and its answer is wrong wherever `b` and the
secant are both negative -- at `b = -3` its derivative is `+0.0267i` against the
integrand's `-0.0267i` at `x = 2`, the two agreeing at `x = 0.5`. PR #1388 withdrew the
unconditional reading for that reason; this brings the shape back with the condition it
owes.

Two restrictions keep the rewrite from costing anything elsewhere. The constant comes out
only where the rest of the base is a *single* factor, which is where it pays, the power it
leaves being free to meet another power of the same function beside it; over a product it
would rewrite the question into one no easier and pay a whole descent to find out, which
`(cos(x)^11 sin(x)^13)^(-1/4)` measured at ten seconds against forty-seven. And an even
whole power of a function is left to the rule that takes the function out of it with its
sign, which answers `(a sin(x)^2)^(5/2)` for any real `a` -- read through the nesting and
past the sign, since below the tangent substitution the factor arrives as `(u^2)^(-1)`.

Rubi's family 4 goes from 290 to 309 of 422, no row lost and two fewer timeouts; the
corpus stays at 1707 of 1774 with no wrong answer, no error and no timeout.

Part of #718.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
…he rules that need none

The first head of this change hung the test run: HalfAngleSquareTest's
`x/csch(x)^(3/2) + x*sqrt(csch(x))/3` started and never finished, and CI's three C# Test
legs sat in the Test step for three and a half hours before they were cancelled.

The library writes `csch(x)` as `1/((e^x - e^(-x))/2)`, and the rule re-asks through
ComputeAsTheSameQuestion, which keeps the rewritten question at the same depth. `1/s`
splits into the factors `1` and `s^(-1)`, and a constant accumulated as `1 * 1` is not
`One` as a tree, so the rule believed it had taken something out and asked the identical
integrand again at every level -- 38 of the first 40 traced firings rewrote an integrand
into itself. With that fixed, the genuine `2` in `csch(x)` was still taken out again under
each exponential substitution below it: `x/csch(x)^(3/2)`, declined in 7.4 s on master,
did not return in ninety.

- The rule declines where its rewrite, once simplified, is the question it was asked, and
  its products are built without a leading `1 *`.
- A constant is taken out only of a single factor in which `x` enters only through
  trigonometric functions, which is the family it was written for; below a substitution
  those are gone, which keeps it off the levels where it compounded. A constant written in
  every term of such a sum counts -- `a - a sin(x)^2` is `a (1 - sin(x)^2)` -- which was the
  difference between `sqrt(a - a sin(c + d x)^2)` answering and `sqrt(a - a sin(x)^2)` not.
- The monomial split keeps its place; the constant pull is asked after the rules that
  answer its shapes for any real constant, since asked first it answered
  `sqrt(a + a sin(x))` for a positive `a` only.

The shapes that hung now take what they take on master (16.96 s against 17.12 s, 7.39 s
against 7.42 s). Family 4 goes from 290 to 309 of 422 and family 6 from 377 to 378, with no
row lost in families 1, 4, 5, 6 or 7; the corpus stays at 1707 of 1774 with no wrong answer,
error or timeout, and the suite runs to completion, 12,677 tests.

Part of #718.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet

Copy link
Copy Markdown
Member Author

The first head of this PR hung the test run, and I cancelled its CI. The three C# Test legs sat in the Test step for 3½ hours; master's runs of that workflow finish in about 26 minutes. Fixed in 2de06de. What happened, for the record:

Where. HalfAngleSquareTest.ThePairOfPowersTwoApartIsElementary on x/csch(x)^(3/2) + x*sqrt(csch(x))/3 started and never finished — named by --blame-hang with the dump turned off, from the entry in the Sequence file that never reached Completed. Locally the run looked green at first: after Blame kills a hung host, vstest still prints Passed! Failed: 0 over the tests that finished, and its total was 12,658 against a complete run's 12,675.

Why. The library writes csch(x) as 1/((e^x - e^(-x))/2), and the rule re-asks through ComputeAsTheSameQuestion, which keeps the rewritten question at the same depth. Two things then went unbounded:

  1. An identity rewrite. 1/s splits into the factors 1 and s^(-1); the constant accumulated as 1 * 1, which is not One as a tree, so the rule believed it had taken something out and asked the identical integrand again — 38 of the first 40 traced firings were expr ==> expr.
  2. A real rewrite at every level. With that fixed, csch's genuine 2 was still taken out again under each exponential substitution below it: x/csch(x)^(3/2), declined in 7.4 s on master, did not return in 90.

What changed.

  • A same-question rewrite must change the question: the rule declines when its rewrite, after InnerSimplified, is what it was asked. Products are built without a leading 1 *.
  • The constant is taken out only of a single factor in which x enters only through trigonometric functions — the family it was written and measured for. Below a substitution the trigonometric functions are gone, so this also keeps it off the levels where it compounded. A constant written in every term of such a sum counts too (a - a sin(x)^2 is a (1 - sin(x)^2)), which was the difference between sqrt(a - a sin(pe + f x)^2) answering and sqrt(a - a sin(x)^2) not.
  • Split into two registrations. The monomial split from A power of the variable times a sine or cosine of a logarithm, and a power of a monomial #1479 stays where it was. The constant pull now runs after the rules that answer the same shapes for any real constant — the even power with its sign, and the half angle of a ± a sin — because asked first it answered sqrt(a + a sin(x)) provided a > 0, which HalfAngleSquareTest pins at a = -3.

Measured on the new head:

master this PR
Rubi family 4 290/422 309/422, 27 timeouts against 32
Rubi family 6 377/417 378/417
families 1, 5, 7 158, 212, 215 158, 212, 215 — no row lost in any of the five
corpus (1774) 1707, 0 wrong 1707, 0 wrong, 0 error, 0 timeout
suite 12,675 12,677 (two cases added), 0 failed, run to completion — Blame: "All tests finished running"
allocation gate PASSED on all 19 gated benchmarks

The shapes that hung now match master: x/csch(x)^(3/2) + x sqrt(csch(x))/3 16.96 s against 17.12 s, x/csch(x)^(3/2) 7.39 s against 7.42 s.

🤖 Generated with Claude Code

@Rafael-SOWNet
Rafael-SOWNet merged commit d1f4cab into master Sep 23, 2026
31 checks passed
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.

1 participant