Repository navigation
A sum over a set: sum(f, x in S) - #1624
Merged
Merged
Conversation
sum took a range and nothing else, so a sum over a set had no spelling:
sum(f, x in S) was a parse error asking for the range form's four
arguments. SumOverSetf reads the second argument as `variable in set` and
binds the variable over the term and the set, as max(f, t in S) does; it
prints \sum_{x \in S} f, and MathS.Sum(f, x, S) builds it. The range form
is untouched. A sum over the roots of a polynomial is a sum over its set
of roots, sum(f(w), w in { w : p(w) = 0 }), with no index and no order.
The terms are added up where the set is finite and its members are known:
a listed set of numbers, distinct by value, and the roots of a polynomial
where the solver writes all of them -- counted against the degree of the
square-free part, so a root the solver missed leaves the sum as written
rather than a term short. The solver is asked only where every
irreducible factor has degree at most four or is a binomial. Evaluated
to a number, the roots it cannot write are found to the working
precision by Durand-Kerner, one square-free factor at a time, all of
them or none; the polynomial is read off the tree, so that holds with
downcasting off. A set whose members may coincide, and one that is not
finite, are left as written.
Termwise differentiation where the set does not depend on the variable,
zero in the bound name; SymPy export as a sum of substitutions or a
RootSum of the square-free part.
Part of #1285.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rational roots, the roots of a quadratic and the roots of a binomial are
written and added; the roots of an irreducible cubic or quartic are not.
In radicals they read no more simply than the sum over them, and for a
real cubic with three real roots Cardano's formula writes them with
complex radicals, so sum(w^2, w in { w : w^3 + w + 1 = 0 }) is kept as it
is and evaluates to -2.
A numeric value goes to the numeric roots at once rather than asking the
solver first, since a check evaluates at many points and the roots do not
depend on them. SquareFreeParts and NumericRoots are internal, for the
interval evaluation that reads them next.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet
added a commit
that referenced
this pull request
Sep 30, 2026
The Rothstein-Trager resultant declined a rational function whose residues lie in a field of degree above two: 1/(x^3 + x + 1) and #1285's sqrt(x)/(1 + x + x^4), 2u^2/(1 + u^2 + u^8) under u = sqrt(x), were left unevaluated. Their logarithms are written now as a sum over the roots of the factor of the denominator they belong to, sum(A(r)/D'(r) ln(x - r), r in { r : E(r) = 0 }), the form #1285 asked for, with the node #1624 added. The poles whose residues are roots of R are the common roots of D and D'^n R(A/D'), so E is their gcd over the rationals, and no arithmetic in the residues' field is needed. Only as a last resort. Asked beside the other rules, the sum answered each of Jeffrey's three terms of (-1 + 4 cos(x) + 5 cos(x)^2)/(-1 - 4 cos(x) - 3 cos(x)^2 + 4 cos(x)^3) with a sum over the roots of a sextic, where the whole is 2 arctan(tan(x/2)^3 - 2 tan(x/2)). So the rule declines as before and notes that a sum would answer, and only where the question comes back unanswered and that was noted is it asked again with sums allowed, under a memo of its own; the answer found then is remembered for the question. The integrator checks an answer in double intervals and then in decimals, and neither read the sum node, so every such answer would have been declined by its own check. PreciseEvaluation encloses each root now: Durand-Kerner's approximations, each widened by its Weierstrass correction in intervals, n |w_k| about z_k, which holds exactly one root where the rectangles are apart (Braess and Hadeler), and the summand worked out on each rectangle. Measured on the Rubi corpus, master at ad05a79 and this change on it, run side by side: the independent suites 1753 -> 1756 of 1814 (Bondarenko 22 and 23, and Hearn 66, 1/(1 - x^4 + x^8)); family 1 at ten problems a file, 295 -> 301 of 377 (1/(x^2 (1 - x^3 + x^6)), (1 + x^4)/(1 - x^4 + x^8), (1 + x^4)/(1 - 5x^4 + x^8), (-1 + 2x^4 + sqrt(3))/(1 - x^4 + x^8), (1 - x^4)/(1 - x^4 + x^8) and x^3 (5 + x + 3x^2 + 2x^3)/(2 + x + 5x^2 + x^3 + 2x^4)); families 2 to 7 at the usual sample, 663 of 722 on both. 0 wrong everywhere. Run alone, those nine are 0 of 9 on master and 9 of 9 here, and the tenth problem that moved, 1.1.4.3 #231, is 17.9 s unevaluated on both: its master timeout was the load of the side-by-side run. A question still declined costs what it did: the problems unevaluated in both arms took 14.2 s against 13.4 s in the independent suites, 65.6 s against 64.7 s in families 2 to 7 and 89.9 s against 92.3 s in the family 1 sample. The performance gate passes on 34c548a8, whose library code is this commit's: allocation is what the baseline says on all 19 gated benchmarks. RothsteinTragerTest: the cubic, quartic and quintic rows that were pinned as declined are sums now, and #1285's integral in PowerSubstitutionIntegralTest; that test's declined witnesses are the elliptic x^2/sqrt(x^4 + x + 1) and x^2/sqrt(x^4 + x^3 + 1), which nothing answers. BinomialDenominatorIntegralTest, PartialFractionsTest and RationalIntegralsTest pinned 1/(x^3 + x + 1), 1/(x^3 + x^2 + x + 2), 1/(x^4 + x + 1) and 1/(x^4 + x^3 + 1) as declined: each is the sum now, checked by differentiating back, and the boundary pinned in their place is the same kind of denominator with a symbol among its coefficients, which the sum is not written for. DecliningStaysCheap keeps its ten-second bound as FindingThatNothingSplitsStaysCheap: the second pass starts only once the first has declined, so the decline it guards is still inside the bound. Closes #1285. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
sum(f, x in S)is the sum offover the members of a set, each member counted once. It is a binder, likemax(f, t in S)(#1222), and is built byMathS.Sum(f, x, S). The range formsum(f, k, from, to)is untouched. A sum over the roots of a polynomial is a sum over its set of roots,sum(f(w), w in { w : p(w) = 0 }), with no index and no order. That is the shape agreed on #1285, and it lets the rational integrator write an answer over the roots of an irreducible denominator.What it answers. The terms are added up where the set is finite and its members are known:
sum(w^2, w in { w : w^3 + w + 1 = 0 })stays as it is, and evaluates to-2. (Changed in review, second commit.)Evaluated to a number, the roots of a root set are found to the working precision by the Durand–Kerner iteration, without asking the solver (
NumericalSolving/DurandKerner.cs, one square-free factor at a time), all of them or none. It respects the precision scope and downcasting off. A set with a symbol among its members, which may coincide, and a set that is not finite are left as written.Measured on this branch and on
v2.5.0, where every set form threw sum should have exactly 4 arguments but 2 arguments are provided:sum(x^2, x in {1, 2, 3}), simplified14sum(w^2, w in { w : w^3 - 2w + 1 = 0 }), simplified4sum(w^5, w in { w : w^5 + w + 3 = 0 }), evaluated-15, from the five roots found numericallysum(k, k in ZZ intersect [1; 10]), simplified55, assum(k, k, 1, 10)issum(x, x in {a, b}), simplifiedaandbmay be equalEvaluated with downcasting off,
sum(1/w, w in { w : w^5 + w + 3 = 0 })is-0.333…3336 + 4E-100iat 100 digits:-1/3, with rounding at the last digit.The name is bound over the term and the set, as in the other binders: it is not a free variable of the sum, and substitution renames it first. Differentiation is termwise where the set does not depend on the variable, and gives zero in the bound name. SymPy export: a listed set goes over as a sum of substitutions, and a root set as
RootSumof the square-free part, sinceRootSumcounts a repeated root as often as it is repeated. Anything else is refused.The node's interval evaluation, and the Rothstein–Trager rule that answers
1/(x^3 + x + 1), come next in a separate PR by the session working on the integrator.BREAKING-CHANGES.md:
sum(f, x in S)was a parse error in 2.5.0. Measured rows are in the entry.Suite, net10.0, at
7b7f5e11on master8cb650e2: 14100 passed, 0 failed. Every target framework builds.Gate: allocation is what the baseline says on all 19 gated benchmarks.
Part of #1285: the Rothstein–Trager rule in the integrator closes it.
🤖 Generated with Claude Code
https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura