Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,36 @@ the ends are about, `n >= 0`. With two names it is left as it was, `n >= m - 1`
|---|---|---|
| `"sum(2 k + 1, k, 0, n - 1)".Simplify()` | `piecewise((2 * n + (n - 1) ^ 2 - 1) provided (n - 1 >= -1), 0 provided True)` | `piecewise((n ^ 2) provided (n >= 0), 0 provided True)` |

### A sum over a set: `sum(f, x in S)`

`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 four arguments of the range form. `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: spelt `sum(f, x in S)`, printed `\sum_{x \in S} f` in LaTeX, and built by
`MathS.Sum(f, x, S)`. The range form `sum(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.

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 every one is rational, a root of a quadratic
or a root of a binomial. That is checked by counting them against the degree of the square-free
part, so a root the solver missed leaves the sum as written rather than one term short. The roots of
an irreducible cubic or quartic are not written out in radicals, which read no more simply than the
sum. Evaluated to a number, the roots it cannot
write are found to the working precision by the Durand–Kerner iteration, all of them or none. A set
with a symbol among its members, whose members may coincide, and a set that is not finite are left
as written. [#1285](https://github.com/asc-community/AngouriMath/issues/1285). Both columns
measured on a build, `v2.5.0` against this change.

| | Was (2.5.0) | Is |
|---|---|---|
| `"sum(x^2, x in {1, 2, 3})".ToEntity()` | `FunctionArgumentCountException`: *sum should have exactly 4 arguments but 2 arguments are provided* | `14` once simplified |
| `"sum(w^2, w in { w : w^3 - 2w + 1 = 0 })".ToEntity().Simplify()` | the same exception | `4` |
| `"sum(w^5, w in { w : w^5 + w + 3 = 0 })".ToEntity().Evaled` | the same exception | `-15`, from the five roots found numerically |
| `"sum(k, k in ZZ intersect [1; 10])".ToEntity().Simplify()` | the same exception | `55`, as `sum(k, k, 1, 10)` is |
| `"sum(x, x in {a, b})".ToEntity().Simplify()` | the same exception | `sum(x, x in { a, b })`, since `a` and `b` may be equal |
| `"sum(k, k, 1, 10)".ToEntity().Simplify()` | `55` | the same |

### `(a + b asech(c x))/(d + e x)^2` is integrated, and a root written apart with `|x|` no longer needs a parity

Rubi's 7.5.1 with a symbolic linear below the bar ran for ten minutes without an answer. By
Expand Down
6 changes: 6 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[AngouriMath/Functions/Continuous/ExtremumOverSet.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[AngouriMath/Functions/Algebra/SumOverSet.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[AngouriMath/Functions/Continuous/Solvers/NumericalSolving/DurandKerner.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Core/Transformations/LogarithmSpellingsTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

Expand Down
34 changes: 34 additions & 0 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7476,6 +7476,40 @@ public static Entity Limit(Entity expr, Entity var, Entity dest, ApproachFrom ap
public static Entity Sum(Entity expr, Entity var, Entity from, Entity to)
=> new Summationf(expr, var, from, to);

/// <summary>
/// The sum of <paramref name="expr"/> over the members of <paramref name="over"/> as
/// <paramref name="var"/> ranges over it, each member counted once: written
/// <c>sum(expr, var in over)</c>. A sum over the roots of a polynomial is a sum over its
/// set of roots, <c>sum(f(w), w in { w : p(w) = 0 })</c>, with no index and no order.
/// </summary>
/// <param name="expr">The term, as a function of <paramref name="var"/>.</param>
/// <param name="var">The name that ranges over the set, bound by the sum.</param>
/// <param name="over">The set.</param>
/// <returns>
/// The terms added up where the set is finite and its members are known: a listed set of
/// numbers, or roots of a polynomial that are rational, roots of a quadratic or roots of a
/// binomial -- those of an irreducible cubic or quartic are kept as the sum, radicals
/// reading no more simply than it. Evaluated to a
/// number, the roots it cannot write are found numerically, all of them or none. Left as
/// written otherwise: a set with a symbol among its members, which may coincide, or a set
/// that is not finite.
/// </returns>
/// <remarks><a href="https://github.com/asc-community/AngouriMath/issues/1285">#1285</a></remarks>
/// <example>
/// <code>
/// using System;
/// using static AngouriMath.MathS;
///
/// Console.WriteLine(Sum("w^2", "w", "{ w : w^3 - 2w + 1 = 0 }").Simplify());
/// </code>
/// Prints
/// <code>
/// 4
/// </code>
/// </example>
public static Entity Sum(Entity expr, Entity var, Entity over)
=> new SumOverSetf(expr, var, over);

/// <summary>
/// The largest value <paramref name="expr"/> takes as <paramref name="var"/> ranges over
/// <paramref name="over"/>, written <c>max(expr, var in over)</c>. Answered over a finite
Expand Down
2 changes: 1 addition & 1 deletion Sources/AngouriMath/Core/Antlr/AngouriMath.g
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ atom returns[Entity value]
| 'limit(' args = function_arguments ')' { Assert("limit", 3, $args.list.Count); $value = MathS.Limit($args.list[0], $args.list[1], $args.list[2]); }
| 'limitleft(' args = function_arguments ')' { Assert("limitleft", 3, $args.list.Count); $value = MathS.Limit($args.list[0], $args.list[1], $args.list[2], AngouriMath.Core.ApproachFrom.Left); }
| 'limitright(' args = function_arguments ')' { Assert("limitright", 3, $args.list.Count); $value = MathS.Limit($args.list[0], $args.list[1], $args.list[2], AngouriMath.Core.ApproachFrom.Right); }
| 'sum(' args = function_arguments ')' { Assert("sum", 4, $args.list.Count); $value = MathS.Sum($args.list[0], $args.list[1], $args.list[2], $args.list[3]); }
| 'sum(' args = function_arguments ')' { if ($args.list.Count == 2 && AngouriMath.Functions.ExtremumOverSet.AsRange($args.list[1]) is var (sumVar, sumOver)) $value = MathS.Sum($args.list[0], sumVar, sumOver); else { Assert("sum", 4, $args.list.Count); $value = MathS.Sum($args.list[0], $args.list[1], $args.list[2], $args.list[3]); } }
| 'product(' args = function_arguments ')' { Assert("product", 4, $args.list.Count); $value = MathS.Product($args.list[0], $args.list[1], $args.list[2], $args.list[3]); }
| 'signum(' args = function_arguments ')' { Assert("signum", 1, $args.list.Count); $value = MathS.Signum($args.list[0]); }
| 'sgn(' args = function_arguments ')' { Assert("sgn", 1, $args.list.Count); $value = MathS.Signum($args.list[0]); }
Expand Down
2 changes: 1 addition & 1 deletion Sources/AngouriMath/Core/Antlr/AngouriMathParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4449,7 +4449,7 @@ public AtomContext atom() {
_localctx.args = function_arguments();
State = 916;
Match(T__30);
Assert("sum", 4, _localctx.args.list.Count); _localctx.value = MathS.Sum(_localctx.args.list[0], _localctx.args.list[1], _localctx.args.list[2], _localctx.args.list[3]);
if (_localctx.args.list.Count == 2 && AngouriMath.Functions.ExtremumOverSet.AsRange(_localctx.args.list[1]) is var (sumVar, sumOver)) _localctx.value = MathS.Sum(_localctx.args.list[0], sumVar, sumOver); else { Assert("sum", 4, _localctx.args.list.Count); _localctx.value = MathS.Sum(_localctx.args.list[0], _localctx.args.list[1], _localctx.args.list[2], _localctx.args.list[3]); }
}
break;
case 100:
Expand Down
8 changes: 8 additions & 0 deletions Sources/AngouriMath/Core/Domains.Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ partial record Productf
internal override Domain DefaultCodomain => Domain.Complex;
}

partial record SumOverSetf
{
/// <inheritdoc/>
public override Domain Codomain { get; protected init; } = Domain.Complex;
/// <inheritdoc/>
internal override Domain DefaultCodomain => Domain.Complex;
}

partial record Maximumf
{
/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ public override Entity Replace(Func<Entity, Entity> func) =>
protected override Entity[] InitDirectChildren() => new[] { Expression, Var, From, To };
}

/// <summary>
/// A sum over a set: <c>sum(f(x), x in S)</c> is the sum of <c>f(x)</c> over the members
/// <c>x</c> of <c>S</c>, each counted once. A binder, like <see cref="Summationf"/>, whose
/// range is a set rather than two bounds, and so has no index and no order: the sum over
/// the roots of a polynomial is <c>sum(f(w), w in { w : p(w) = 0 })</c>, which needs no
/// first root.
/// https://github.com/asc-community/AngouriMath/issues/1285
/// </summary>
public sealed partial record SumOverSetf(Entity Expression, Entity Var, Entity Over) : CalculusOperator(Expression, Var)
{
/// <summary>The set the variable ranges over.</summary>
public Entity Over { get; init; } = Binding.Of(Var).In(Over);

private SumOverSetf New(Entity expression, Entity var, Entity over) =>
ReferenceEquals(Expression, expression) && ReferenceEquals(Var, var) && ReferenceEquals(Over, over)
? this : new(expression, var, over) { Codomain = Codomain };
/// <inheritdoc/>
public override Entity Replace(Func<Entity, Entity> func) =>
func(New(Expression.Replace(func), Var, Over.Replace(func)));
/// <inheritdoc/>
protected override Entity[] InitDirectChildren() => new[] { Expression, Var, Over };
}

/// <summary>
/// A node of limit
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions Sources/AngouriMath/Core/Entity/Entity.Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,9 @@ public IReadOnlyList<Variable> Vars
=> BoundBy(index, body, from, to),
Productf(var body, var index, var from, var to)
=> BoundBy(index, body, from, to),
// A sum over a set binds the name that ranges over the set:
// sum(w * a, w in { w : w^2 = 2 }) is a function of a alone.
SumOverSetf(var body, var bound, var over) => BoundBy(bound, body, over),
// An extremum over a set binds the variable that ranges over the set:
// max(t^2 + a, t in [0; 1]) is a function of a alone.
Maximumf(var body, var bound, var over) => BoundBy(bound, body, over),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ [EntityJsonConverter] partial record SpecialSet
[EntityJsonConverter] partial record Sumf;
[EntityJsonConverter] partial record Summationf;
[EntityJsonConverter] partial record Maximumf;
[EntityJsonConverter] partial record SumOverSetf;
[EntityJsonConverter] partial record Minimumf;
[EntityJsonConverter] partial record Argmaxf;
[EntityJsonConverter] partial record Argminf;
Expand Down
17 changes: 15 additions & 2 deletions Sources/AngouriMath/Docs/Usage/Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,9 @@ undefined at `0`; `Si(+oo)` is `pi / 2` and `Ci(+oo)` is `0`.

**Calculus** — `derivative(expr, var, order)`, `integral(expr, var)`,
`integral(expr, var, from, to)`, `limit(expr, var, dest)`, `limitleft(...)`, `limitright(...)`;
`max(expr, var in set)` and `min` for the extremum of an expression over a set, `argmax` and
`argmin` for the set of points where it is taken — `max(a, b)` of two values is still the larger.
`sum(expr, var in set)` for a sum over a set; `max(expr, var in set)` and `min` for the extremum of
an expression over a set, `argmax` and `argmin` for the set of points where it is taken —
`max(a, b)` of two values is still the larger.
`min(S)` with one argument that is a set is its least member, `min(x, x in S)`, and the range
may carry conditions, `min(x, x in PP and x > 14)` being the least of `{ x in PP : x > 14 }`;
decided for `PP`, `ZZ+`, `ZZ*` and their cuts by an interval or bounds (`min(ZZ+)` is `1`,
Expand Down Expand Up @@ -487,6 +488,18 @@ at `c = 0`, where the empty product is `1`. Where the index appears in the body
must be a concrete integer of at least one, `factorial` having no value at the negative integers;
`product(k, k, 0, n)` therefore stays as written.

`sum(body, name in set)` is a sum **over a set**: the body at each member of the set, each member
counted once, added up. `sum(x^2, x in {1, 2, 3})` is `14`. A sum over the roots of a polynomial is a
sum over its set of roots, `sum(w^2, w in { w : w^3 - 2w + 1 = 0 })`, which is `4`, and it needs no
index and no order, a set having neither. The terms are added up where the set is finite and its
members are known: a listed set of numbers, or roots that are rational, roots of a quadratic or roots
of a binomial, which is checked by counting them against the polynomial's number of distinct roots.
The roots of an irreducible cubic or quartic are not written out in radicals, which read no more
simply than the sum. A set whose members might coincide,
such as `{ a, b }`, and a set that is not finite are left as written. Evaluated to a number, roots
the solver cannot write are found numerically, all of them to the working precision or none, so
`sum(w^5, w in { w : w^5 + w + 3 = 0 })` evaluates to `-15`.

**The declared name means the name, and only inside the operator that declares it.** Two names
show this, because both mean something else in this language:

Expand Down
Loading
Loading