An expression can be written as a single fraction: AsSingleFraction - #1622
Merged
Merged
Conversation
Entity.AsSingleFraction(), with the string extension and Transformation.AsSingleFraction, writes an expression as one numerator over one denominator, nothing divided inside either: 1 + 2/(1+t^2) is (t^2 + 3)/(t^2 + 1). Any expression, functions included, and nothing cancelled or multiplied out, so the denominator's factors stay and x/x stays x/x; a rational number counts as the fraction it is written as. Simplify never does it, and for a rational function in lowest terms Transformation.RationalCanonicalization is the canonical form. The name is the textbook's, "as a single fraction", as asked on #1239. Closes #1239. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Dividing by a fraction moves its denominator into the numerator, where it no longer stops the answer having a value: 1/(1/x) gathers to x, which is 0 at x = 0 where the expression has no value, and (a/b)/(c/d) to a d/(b c), which is 0 at d = 0. That is the one thing the simplification contract forbids (O4), so the answer now says so: x provided not x = 0. SingleQuotient.Of gains an overload that records each denominator it carries across; the one-argument form passes nothing and is unchanged for its callers. A denominator the new one still contains is not repeated, so (1/x)/(1/x) stays x / x. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
As it is by hand: 1/x + 1/x^2 is (x + 1)/x^2, where the product of the denominators put it over x^3 with a factor x in both halves. The common denominator is taken from the denominators as they are written -- each factor to the highest power any of them has it, and the whole numbers by their least common multiple -- so nothing is factorised to find it, and x^2 - 1 and x + 1 share nothing. SingleQuotient.OverLeastCommonDenominator is the entry AsSingleFraction uses; SingleQuotient.Of keeps the product for its callers, which divide out afterwards, and is unchanged for them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
The tidying pass writes x^2 - 1 as x ^ 2 + -1, and Simplify's last step, NumericNeat, writes it back. So 1/(x^2 - 1) + 1/(x + 1) is (x + x ^ 2) / ((x ^ 2 - 1) * (x + 1)), and every other written form here is as it was. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
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.
Entity.AsSingleFraction(),Transformation.AsSingleFractionand"…".AsSingleFraction()write an expression as one numerator over one denominator, with nothing divided inside either. That is what a textbook calls writing it as a single fraction. The name is the one Happypig375 chose on #1239.sin(x) + 1/cos(x)is(1 + cos(x) * sin(x)) / cos(x).1/x + 1/x^2is(x + 1) / x ^ 2, not overx^3, and2/3 + x/2is over6. Nothing is factorised to find it, sox^2 - 1andx + 1share no factor.x/xstaysx / x, and1/(t^2+1) + 1/(t+1)keeps its denominator as(t ^ 2 + 1) * (t + 1). A rational number counts as the fraction it is:2/3 + x/2is(4 + 3 * x) / 6. Each half is tidied the waySimplifytidies, without its search: like terms are collected and operands put in order, so1/(x^2 - 1) + 1/(x + 1)is(x + x ^ 2) / ((x ^ 2 - 1) * (x + 1)).1/(1/x)isx provided not x = 0, and(a/b)/(c/d)isa * d / (b * c) provided not d = 0. This is O4 of the simplification contract. Where the new denominator still contains it, nothing is added:(1/x)/(1/x)isx / x.CanonicalizeAsRationalFunctionremains the canonical form for a rational function (lowest terms, denominator multiplied out and monic), and the docs point there. On #1239 I proposed modelling the two as one pipeline: single fraction, then lowest terms, then normalise, rather than one operation with a cost setting. The canonical form has the same domain gap on division by a fraction, filed as #1618, and the fix proposed there is to gather through this first step.SingleQuotient.Of(expr)keeps the product of the denominators, so its callers see no change: the integrator, partial fractions, the inequality solver and the quantifiers.SingleQuotient.OverLeastCommonDenominator(expr, carried)is the new entry this uses.The API is additive, so there is no BREAKING entry (as for #934).
Suite, net10.0, at
9e23f639on master8cb650e2: 14106 passed, 0 failed.Gate: allocation is what the baseline says on all 19 gated benchmarks.
Closes #1239.
🤖 Generated with Claude Code
https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura