The rational canonical form keeps the points where the expression has no value - #1740
Merged
Rafael-SOWNet merged 1 commit intoOct 4, 2026
Merged
Conversation
… no value Dividing by a quotient moves its denominator into the numerator, and the form gave the result a value where the expression had none: 1/(1/x) was x. The denominators a quotient turns over are kept as a condition now, with the factors a cancellation removes and the denominator under a vanishing numerator, written square-free and without what the reduced denominator still excludes. The form gathers through the step AsSingleFraction takes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
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.
Fixes #1618.
CanonicalizeAsRationalFunctiongave an expression a value where it has none: dividing by a quotient moves that quotient's denominator into the numerator, and nothing said the denominator had to be nonzero. On master6bbed69fagainst this branch:….CanonicalizeAsRationalFunction()of1/(1/x)xx provided not x = 01/(1 + 1/x)x / (x + 1)x / (x + 1) provided not x = 0(a/b)/(c/d)a * d / (b * c)a * d / (b * c) provided not d = 0(1/x)^(-1)xx provided not x = 0(x/y)^(-2)y ^ 2 / x ^ 2y ^ 2 / x ^ 2 provided not y = 0x/(y/x)x ^ 2 / yx ^ 2 / y provided not x = 00/x00 provided not x = 0x^2/x^21 provided not x ^ 2 = 01 provided not x = 0(x^2 - 1)/(x^2 + 2x + 1) + 1/(x + 1)x / (x + 1) provided not x ^ 2 + 2 * x + 1 = 0x / (x + 1)What changes. The form gathers through the step
AsSingleFractiontakes (#1622),SingleQuotient.OverLeastCommonDenominator, which collects each denominator it turns over, so the two agree on what a single quotient of an expression is and on where it has a value. Those denominators are kept as a condition beside the factors a cancellation removes, and so is the denominator under a numerator that vanishes. The condition is part of a form whose point is that equal trees mean equal functions, so it is written canonically too: the square-free part of what was excluded --E / gcd(E, dE/dx_1, …, dE/dx_n)-- without the factors the reduced denominator still excludes. Sox^2/x^2andx/xmeet, and the last row says nothing the denominator does not already say. A sum widens nothing: it is defined exactly where its terms are, and the common denominator vanishes exactly where one of theirs does.Tests:
RationalCanonicalFormTest--1/(1/x)meetsx^2/x,x^2/x^2meetsx/xand(a/b)/(c/d)meetsa d^2/(b c d);1/(1/x),1/(1 + 1/x)and0/xno longer collide withx,x/(x + 1)and0; seven expressions evaluated at the point where they have no value, form and expression bothNaN; and the denominator's exclusion not repeated. Each of the new rows fails on master.Measured. The suite passes, 14,760 tests with 13 skipped, on this change over #1622's last head; cut from master after #1622 merged, the canonical-form, single-fraction, transformation and corpus tests pass, 928 of them.
Simplifyreaches the form inside the interval-membership rule, so the corpus gate is among them.BREAKING-CHANGES.mdhas the entry, both columns measured on a build,v2.5.0against this change.The allocation gate run here failed on
EvalPolynomialFresh, 13,672 against 12,536 B/op, with the machine at a load of thirteen and the benchmark at 28 times its baseline time. That benchmark substitutes a decimal intox^3 + 2 x^2 - x + 1and evaluates it, which this change does not reach, and measured directly -- the same call twenty thousand times after a warm-up -- it allocates 12,536 B/op on this branch, on master6bbed69fand on8f3757cdalike. CI's gate is the one to read.🤖 Generated with Claude Code
https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura