diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 7a602ae02..ded9f0f7b 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -870,6 +870,23 @@ reals, to `1` and `0` ([#2195](https://github.com/asc-community/AngouriMath/issu | `"arg(x)".ToEntity().Compile("x").Call(-2)` | throws `UncompilableNodeException`, `arg` being a variable | `pi` | | `"conj(x)".ToEntity().Compile("x")(3 + 4i)` | throws `UncompilableNodeException`, `conj` being a variable | `3 - 4i` | +### A Taylor expansion simplifies each derivative before taking the next + +**Silent: `MathS.Series.Taylor`, `Maclaurin` and `TaylorTerms` print simplified coefficients, an expansion where the +function has no value is `NaN`, and no terms are `0`.** Each derivative was taken from the one before it as it stood, +and only the term built from it was simplified. A quotient's derivatives grew about eightfold a step: the ninth of +`sin(x) / x` had 7,797,110 nodes, and 87 simplified. One derivative more than the terms kept was also taken, then +dropped. An expansion around a point where the function has no value summed a `NaN` for each term +([#2291](https://github.com/asc-community/AngouriMath/issues/2291)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `MathS.Series.Taylor("sin(x) / x", 11, ("x", 1))` | `OutOfMemoryException` after 32 s with a 2 GB heap | 529 characters, 0.9 s | +| `MathS.Series.Taylor("sin(x) / x", 7, ("x", 1))` | 36,421 characters | `sin(1) + (cos(1) - sin(1)) * (x - 1) + ((-2) * cos(1) + sin(1)) * (x - 1) ^ 2 / 2! + ...` | +| `MathS.Series.Taylor("sin(x) / x", 4, ("x", 0))` | `NaN + NaN + NaN / 2! + NaN / 3!` | `NaN` | +| `MathS.Series.Taylor("sin(x)", 0, ("x", 1))` | throws `AngouriBugException` | `0` | +| `MathS.Series.Taylor("sin(x) + cos(y)", 6, ("x", 1))` | `sin(1) + cos(y) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + cos(1) * (-1) * (x - 1) ^ 3 / 3! + ...` | `cos(y) + sin(1) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + -cos(1) * (x - 1) ^ 3 / 3! + ...` | + ### A logarithm to base 0 has no value **Silent: `log(0, x)` is `NaN`, where it was `0` for a positive `x`.** Evaluation and both compilers took it as diff --git a/Sources/AngouriMath/Convenience/MathS.cs b/Sources/AngouriMath/Convenience/MathS.cs index 2cfee6d72..9ea198721 100644 --- a/Sources/AngouriMath/Convenience/MathS.cs +++ b/Sources/AngouriMath/Convenience/MathS.cs @@ -2681,23 +2681,23 @@ public static class Series /// x ^ 9 / 362880 - x ^ 7 / 5040 + x ^ 5 / 120 - x ^ 3 / 6 + x /// ---------------------- /// sin(x) + cos(y) - /// 1 + (6 * x ^ 5 - 120 * x ^ 3) / 720 + x + (2 * y ^ 4 - 24 * y ^ 2) / 48 + /// x ^ 5 / 120 - x ^ 3 / 6 + x + 1 + y ^ 4 / 24 - y ^ 2 / 2 /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(y) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + cos(1) * (-1) * (x - 1) ^ 3 / 3! + -sin(1) * (-1) * (x - 1) ^ 4 / 4! + cos(1) * (-1) * (-1) * (x - 1) ^ 5 / 5! + /// cos(y) + sin(1) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + -cos(1) * (x - 1) ^ 3 / 3! + sin(1) * (x - 1) ^ 4 / 4! + cos(1) * (x - 1) ^ 5 / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + cos(5) * (-1) * (y - 5) ^ 2) / 2! + (cos(1) * (-1) * (x - 1) ^ 3 + -sin(5) * (-1) * (y - 5) ^ 3) / 3! + (-sin(1) * (-1) * (x - 1) ^ 4 + cos(5) * (-1) * (-1) * (y - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (x - 1) ^ 5 + -sin(5) * (-1) * (-1) * (y - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + -cos(5) * (y - 5) ^ 2) / 2! + (-cos(1) * (x - 1) ^ 3 + sin(5) * (y - 5) ^ 3) / 3! + (sin(1) * (x - 1) ^ 4 + cos(5) * (y - 5) ^ 4) / 4! + (cos(1) * (x - 1) ^ 5 + -sin(5) * (y - 5) ^ 5) / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + cos(5) * (-1) * (z_2 - 5) ^ 2) / 2! + (cos(1) * (-1) * (z_1 - 1) ^ 3 + -sin(5) * (-1) * (z_2 - 5) ^ 3) / 3! + (-sin(1) * (-1) * (z_1 - 1) ^ 4 + cos(5) * (-1) * (-1) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (z_1 - 1) ^ 5 + -sin(5) * (-1) * (-1) * (z_2 - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + -cos(5) * (z_2 - 5) ^ 2) / 2! + (-cos(1) * (z_1 - 1) ^ 3 + sin(5) * (z_2 - 5) ^ 3) / 3! + (sin(1) * (z_1 - 1) ^ 4 + cos(5) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (z_1 - 1) ^ 5 + -sin(5) * (z_2 - 5) ^ 5) / 5! /// ---------------------- /// Received cos(1) /// Received x + -sin(1) * (y - 1) - /// Received cos(1) * (-1) * (y - 1) ^ 2 / 2! - /// Received (-x ^ 3 + -sin(1) * (-1) * (y - 1) ^ 3) / 3! - /// Received cos(1) * (-1) * (-1) * (y - 1) ^ 4 / 4! - /// Received (x ^ 5 + -sin(1) * (-1) * (-1) * (y - 1) ^ 5) / 5! + /// Received -cos(1) * (y - 1) ^ 2 / 2! + /// Received (-x ^ 3 + sin(1) * (y - 1) ^ 3) / 3! + /// Received cos(1) * (y - 1) ^ 4 / 4! + /// Received (x ^ 5 + -sin(1) * (y - 1) ^ 5) / 5! /// /// public static Entity Maclaurin(Entity expr, int degree, params Variable[] exprVariables) @@ -2769,23 +2769,23 @@ public static Entity Maclaurin(Entity expr, int degree, params Variable[] exprVa /// x ^ 9 / 362880 - x ^ 7 / 5040 + x ^ 5 / 120 - x ^ 3 / 6 + x /// ---------------------- /// sin(x) + cos(y) - /// 1 + (6 * x ^ 5 - 120 * x ^ 3) / 720 + x + (2 * y ^ 4 - 24 * y ^ 2) / 48 + /// x ^ 5 / 120 - x ^ 3 / 6 + x + 1 + y ^ 4 / 24 - y ^ 2 / 2 /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(y) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + cos(1) * (-1) * (x - 1) ^ 3 / 3! + -sin(1) * (-1) * (x - 1) ^ 4 / 4! + cos(1) * (-1) * (-1) * (x - 1) ^ 5 / 5! + /// cos(y) + sin(1) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + -cos(1) * (x - 1) ^ 3 / 3! + sin(1) * (x - 1) ^ 4 / 4! + cos(1) * (x - 1) ^ 5 / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + cos(5) * (-1) * (y - 5) ^ 2) / 2! + (cos(1) * (-1) * (x - 1) ^ 3 + -sin(5) * (-1) * (y - 5) ^ 3) / 3! + (-sin(1) * (-1) * (x - 1) ^ 4 + cos(5) * (-1) * (-1) * (y - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (x - 1) ^ 5 + -sin(5) * (-1) * (-1) * (y - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + -cos(5) * (y - 5) ^ 2) / 2! + (-cos(1) * (x - 1) ^ 3 + sin(5) * (y - 5) ^ 3) / 3! + (sin(1) * (x - 1) ^ 4 + cos(5) * (y - 5) ^ 4) / 4! + (cos(1) * (x - 1) ^ 5 + -sin(5) * (y - 5) ^ 5) / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + cos(5) * (-1) * (z_2 - 5) ^ 2) / 2! + (cos(1) * (-1) * (z_1 - 1) ^ 3 + -sin(5) * (-1) * (z_2 - 5) ^ 3) / 3! + (-sin(1) * (-1) * (z_1 - 1) ^ 4 + cos(5) * (-1) * (-1) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (z_1 - 1) ^ 5 + -sin(5) * (-1) * (-1) * (z_2 - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + -cos(5) * (z_2 - 5) ^ 2) / 2! + (-cos(1) * (z_1 - 1) ^ 3 + sin(5) * (z_2 - 5) ^ 3) / 3! + (sin(1) * (z_1 - 1) ^ 4 + cos(5) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (z_1 - 1) ^ 5 + -sin(5) * (z_2 - 5) ^ 5) / 5! /// ---------------------- /// Received cos(1) /// Received x + -sin(1) * (y - 1) - /// Received cos(1) * (-1) * (y - 1) ^ 2 / 2! - /// Received (-x ^ 3 + -sin(1) * (-1) * (y - 1) ^ 3) / 3! - /// Received cos(1) * (-1) * (-1) * (y - 1) ^ 4 / 4! - /// Received (x ^ 5 + -sin(1) * (-1) * (-1) * (y - 1) ^ 5) / 5! + /// Received -cos(1) * (y - 1) ^ 2 / 2! + /// Received (-x ^ 3 + sin(1) * (y - 1) ^ 3) / 3! + /// Received cos(1) * (y - 1) ^ 4 / 4! + /// Received (x ^ 5 + -sin(1) * (y - 1) ^ 5) / 5! /// /// public static Entity Taylor(Entity expr, int degree, params (Variable exprVariable, Entity point)[] exprVariables) @@ -2858,23 +2858,23 @@ public static Entity Taylor(Entity expr, int degree, params (Variable exprVariab /// x ^ 9 / 362880 - x ^ 7 / 5040 + x ^ 5 / 120 - x ^ 3 / 6 + x /// ---------------------- /// sin(x) + cos(y) - /// 1 + (6 * x ^ 5 - 120 * x ^ 3) / 720 + x + (2 * y ^ 4 - 24 * y ^ 2) / 48 + /// x ^ 5 / 120 - x ^ 3 / 6 + x + 1 + y ^ 4 / 24 - y ^ 2 / 2 /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(y) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + cos(1) * (-1) * (x - 1) ^ 3 / 3! + -sin(1) * (-1) * (x - 1) ^ 4 / 4! + cos(1) * (-1) * (-1) * (x - 1) ^ 5 / 5! + /// cos(y) + sin(1) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + -cos(1) * (x - 1) ^ 3 / 3! + sin(1) * (x - 1) ^ 4 / 4! + cos(1) * (x - 1) ^ 5 / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + cos(5) * (-1) * (y - 5) ^ 2) / 2! + (cos(1) * (-1) * (x - 1) ^ 3 + -sin(5) * (-1) * (y - 5) ^ 3) / 3! + (-sin(1) * (-1) * (x - 1) ^ 4 + cos(5) * (-1) * (-1) * (y - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (x - 1) ^ 5 + -sin(5) * (-1) * (-1) * (y - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + -cos(5) * (y - 5) ^ 2) / 2! + (-cos(1) * (x - 1) ^ 3 + sin(5) * (y - 5) ^ 3) / 3! + (sin(1) * (x - 1) ^ 4 + cos(5) * (y - 5) ^ 4) / 4! + (cos(1) * (x - 1) ^ 5 + -sin(5) * (y - 5) ^ 5) / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + cos(5) * (-1) * (z_2 - 5) ^ 2) / 2! + (cos(1) * (-1) * (z_1 - 1) ^ 3 + -sin(5) * (-1) * (z_2 - 5) ^ 3) / 3! + (-sin(1) * (-1) * (z_1 - 1) ^ 4 + cos(5) * (-1) * (-1) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (z_1 - 1) ^ 5 + -sin(5) * (-1) * (-1) * (z_2 - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + -cos(5) * (z_2 - 5) ^ 2) / 2! + (-cos(1) * (z_1 - 1) ^ 3 + sin(5) * (z_2 - 5) ^ 3) / 3! + (sin(1) * (z_1 - 1) ^ 4 + cos(5) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (z_1 - 1) ^ 5 + -sin(5) * (z_2 - 5) ^ 5) / 5! /// ---------------------- /// Received cos(1) /// Received x + -sin(1) * (y - 1) - /// Received cos(1) * (-1) * (y - 1) ^ 2 / 2! - /// Received (-x ^ 3 + -sin(1) * (-1) * (y - 1) ^ 3) / 3! - /// Received cos(1) * (-1) * (-1) * (y - 1) ^ 4 / 4! - /// Received (x ^ 5 + -sin(1) * (-1) * (-1) * (y - 1) ^ 5) / 5! + /// Received -cos(1) * (y - 1) ^ 2 / 2! + /// Received (-x ^ 3 + sin(1) * (y - 1) ^ 3) / 3! + /// Received cos(1) * (y - 1) ^ 4 / 4! + /// Received (x ^ 5 + -sin(1) * (y - 1) ^ 5) / 5! /// /// public static Entity Taylor(Entity expr, int degree, params (Variable exprVariable, Variable polyVariable, Entity point)[] exprToPolyVars) @@ -2948,23 +2948,23 @@ public static Entity Taylor(Entity expr, int degree, params (Variable exprVariab /// x ^ 9 / 362880 - x ^ 7 / 5040 + x ^ 5 / 120 - x ^ 3 / 6 + x /// ---------------------- /// sin(x) + cos(y) - /// 1 + (6 * x ^ 5 - 120 * x ^ 3) / 720 + x + (2 * y ^ 4 - 24 * y ^ 2) / 48 + /// x ^ 5 / 120 - x ^ 3 / 6 + x + 1 + y ^ 4 / 24 - y ^ 2 / 2 /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(y) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + cos(1) * (-1) * (x - 1) ^ 3 / 3! + -sin(1) * (-1) * (x - 1) ^ 4 / 4! + cos(1) * (-1) * (-1) * (x - 1) ^ 5 / 5! + /// cos(y) + sin(1) + cos(1) * (x - 1) + -sin(1) * (x - 1) ^ 2 / 2! + -cos(1) * (x - 1) ^ 3 / 3! + sin(1) * (x - 1) ^ 4 / 4! + cos(1) * (x - 1) ^ 5 / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + cos(5) * (-1) * (y - 5) ^ 2) / 2! + (cos(1) * (-1) * (x - 1) ^ 3 + -sin(5) * (-1) * (y - 5) ^ 3) / 3! + (-sin(1) * (-1) * (x - 1) ^ 4 + cos(5) * (-1) * (-1) * (y - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (x - 1) ^ 5 + -sin(5) * (-1) * (-1) * (y - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (x - 1) + -sin(5) * (y - 5) + (-sin(1) * (x - 1) ^ 2 + -cos(5) * (y - 5) ^ 2) / 2! + (-cos(1) * (x - 1) ^ 3 + sin(5) * (y - 5) ^ 3) / 3! + (sin(1) * (x - 1) ^ 4 + cos(5) * (y - 5) ^ 4) / 4! + (cos(1) * (x - 1) ^ 5 + -sin(5) * (y - 5) ^ 5) / 5! /// ---------------------- /// sin(x) + cos(y) - /// sin(1) + cos(5) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + cos(5) * (-1) * (z_2 - 5) ^ 2) / 2! + (cos(1) * (-1) * (z_1 - 1) ^ 3 + -sin(5) * (-1) * (z_2 - 5) ^ 3) / 3! + (-sin(1) * (-1) * (z_1 - 1) ^ 4 + cos(5) * (-1) * (-1) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (-1) * (-1) * (z_1 - 1) ^ 5 + -sin(5) * (-1) * (-1) * (z_2 - 5) ^ 5) / 5! + /// cos(5) + sin(1) + cos(1) * (z_1 - 1) + -sin(5) * (z_2 - 5) + (-sin(1) * (z_1 - 1) ^ 2 + -cos(5) * (z_2 - 5) ^ 2) / 2! + (-cos(1) * (z_1 - 1) ^ 3 + sin(5) * (z_2 - 5) ^ 3) / 3! + (sin(1) * (z_1 - 1) ^ 4 + cos(5) * (z_2 - 5) ^ 4) / 4! + (cos(1) * (z_1 - 1) ^ 5 + -sin(5) * (z_2 - 5) ^ 5) / 5! /// ---------------------- /// Received cos(1) /// Received x + -sin(1) * (y - 1) - /// Received cos(1) * (-1) * (y - 1) ^ 2 / 2! - /// Received (-x ^ 3 + -sin(1) * (-1) * (y - 1) ^ 3) / 3! - /// Received cos(1) * (-1) * (-1) * (y - 1) ^ 4 / 4! - /// Received (x ^ 5 + -sin(1) * (-1) * (-1) * (y - 1) ^ 5) / 5! + /// Received -cos(1) * (y - 1) ^ 2 / 2! + /// Received (-x ^ 3 + sin(1) * (y - 1) ^ 3) / 3! + /// Received cos(1) * (y - 1) ^ 4 / 4! + /// Received (x ^ 5 + -sin(1) * (y - 1) ^ 5) / 5! /// /// public static IEnumerable TaylorTerms(Entity expr, params (Variable exprVariable, Variable polyVariable, Entity point)[] exprToPolyVars) diff --git a/Sources/AngouriMath/Functions/Series.cs b/Sources/AngouriMath/Functions/Series.cs index 5a192f51e..20474a096 100644 --- a/Sources/AngouriMath/Functions/Series.cs +++ b/Sources/AngouriMath/Functions/Series.cs @@ -24,24 +24,53 @@ internal static Entity SingleTaylorExpansion(Entity expr, int termCount, Variabl { if (termCount < 0) throw new InvalidNumberException($"{nameof(termCount)} is supposed to be at least 0, and {termCount} was given"); + return Sum(SingleTaylorExpansionTerms(expr, exprVariable, polyVariable, point), termCount); + } + + /// + /// The first terms added up, 0 where there are none, and + /// where a term has no value. + /// + /// + /// The term after the last is never asked for, since it costs a derivative more. The + /// expansion of sin(x) / x around 0 has no value, as the function has none there, + /// and neither has any derivative of it; it was the sum of the terms, NaN + NaN + .... + /// https://github.com/asc-community/AngouriMath/issues/2291 + /// + private static Entity Sum(IEnumerable series, int termCount) + { var terms = new List(); - foreach (var term in SingleTaylorExpansionTerms(expr, exprVariable, polyVariable, point)) + using var enumerator = series.GetEnumerator(); + while (terms.Count < termCount && enumerator.MoveNext()) { - if (terms.Count >= termCount) - return TreeAnalyzer.MultiHangBinary(terms, (a, b) => a + b); - terms.Add(term); + if (enumerator.Current.IsNaN) + return Number.Real.NaN; + terms.Add(enumerator.Current); } - throw new AngouriBugException($"We cannot get here, as {nameof(SingleTaylorExpansionTerms)} is supposed to be endless"); + return terms.Count == 0 ? Number.Integer.Zero : TreeAnalyzer.MultiHangBinary(terms, (a, b) => a + b); } + /// + /// Each derivative is simplified before the next is taken from it, and each coefficient + /// once it is evaluated at the point. A quotient's derivative taken as it stands grows about + /// eightfold a step, so the ninth of sin(x) / x had 7,797,110 nodes, and 87 + /// simplified, and the eleventh ran out of memory. Where a coefficient has no value, neither + /// has any after it, so the derivatives stop there. + /// https://github.com/asc-community/AngouriMath/issues/2291 + /// internal static IEnumerable SingleTaylorExpansionTerms(Entity expr, Variable exprVariable, Variable polyVariable, Entity point) { var currExpr = expr; var i = 0; while (true) { - var coef = currExpr.Substitute(exprVariable, point); + var coef = currExpr.Substitute(exprVariable, point).Simplify(); var num = (i == 0 ? coef : coef * (polyVariable - point).Pow(i)).InnerSimplified; + if (num.IsNaN) + { + while (true) + yield return Number.Real.NaN; + } if (num != Number.Integer.Zero) if (i > 1) yield return num / ((Entity)i).Factorial(); @@ -49,7 +78,7 @@ internal static IEnumerable SingleTaylorExpansionTerms(Entity expr, Vari yield return num; else yield return Number.Integer.Zero; - currExpr = currExpr.Differentiate(exprVariable); + currExpr = currExpr.Differentiate(exprVariable).Simplify(); i++; } } @@ -58,14 +87,7 @@ internal static Entity MultivariableTaylorExpansion(Entity expr, int termCount, { if (termCount < 0) throw new InvalidNumberException($"{nameof(termCount)} is supposed to be at least 0, and {termCount} was given"); - var terms = new List(); - foreach (var term in MultivariableTaylorExpansionTerms(expr, exprToPolyVars)) - { - if (terms.Count >= termCount) - return TreeAnalyzer.MultiHangBinary(terms, (a, b) => a + b); - terms.Add(term); - } - throw new AngouriBugException($"We cannot get here, as {nameof(MultivariableTaylorExpansionTerms)} is supposed to be endless"); + return Sum(MultivariableTaylorExpansionTerms(expr, exprToPolyVars), termCount); } internal static IEnumerable MultivariableTaylorExpansionTerms(Entity expr, params (Variable exprVariable, Variable polyVariable, Entity point)[] exprToPolyVars) @@ -119,7 +141,8 @@ internal static IEnumerable MultivariableTaylorExpansionTerms(Entity exp // If no match is found, we add it to the new term list. if (!foundRepeatFlag) { - var newPartialDerivative = partialDerivative.Differentiate(exprVariable); + // Simplified before the next is taken from it, as in SingleTaylorExpansionTerms. + var newPartialDerivative = partialDerivative.Differentiate(exprVariable).Simplify(); newTerms.Add((coefficient, newPointCoeffs, newPartialDerivative)); } @@ -146,6 +169,7 @@ internal static IEnumerable MultivariableTaylorExpansionTerms(Entity exp var solvedPartialDerivative = partialDerivative; foreach (var (exprVariable, polyVariable, point) in exprToPolyVars) solvedPartialDerivative = solvedPartialDerivative.Substitute(exprVariable, point); + solvedPartialDerivative = solvedPartialDerivative.Simplify(); var fullTerm = solvedPartialDerivative * coefficient * pointCoefficients; diff --git a/Sources/Tests/UnitTests/Calculus/SeriesTest.cs b/Sources/Tests/UnitTests/Calculus/SeriesTest.cs index 829b62585..9f1f76aba 100644 --- a/Sources/Tests/UnitTests/Calculus/SeriesTest.cs +++ b/Sources/Tests/UnitTests/Calculus/SeriesTest.cs @@ -17,7 +17,10 @@ public sealed class SeriesTest [InlineData("sin(t)", "(((0) + (x)) + ((0) + ((((-1) * ((x) ^ (3))) / ((3)!)) + (0)))) + (((((x) ^ (5)) / ((5)!)) + (0)) + ((((-1) * ((x) ^ (7))) / ((7)!)) + ((0) + (((x) ^ (9)) / ((9)!)))))", 10)] [InlineData("cos(t)", "(((1) + (0)) + ((((-1) * ((x) ^ (2))) / ((2)!)) + ((0) + (((x) ^ (4)) / ((4)!))))) + (((0) + (((-1) * ((x) ^ (6))) / ((6)!))) + ((0) + ((((x) ^ (8)) / ((8)!)) + (0))))", 10)] [InlineData("tan(t)", "((0) + ((x) + (0))) + ((((2) * ((x) ^ (3))) / ((3)!)) + ((0) + (((16) * ((x) ^ (5))) / ((5)!))))", 6)] - [InlineData("cotan(1 - t)", "(cotan(1)) + (((((-1) / ((sin(1)) ^ (2))) * (-1)) * (x)) + ((((((-1) * ((((2) * (sin(1))) * ((cos(1)) * (-1))) * (-1))) / (((sin(1)) ^ (2)) ^ (2))) * (-1)) * ((x) ^ (2))) / ((2)!)))", 3)] + // The derivatives are simplified as they are taken, so the coefficients are csc(1)^2 and + // 2 cot(1) csc(1)^2, where they were the unsimplified quotient rule's. + // https://github.com/asc-community/AngouriMath/issues/2291 + [InlineData("cotan(1 - t)", "(cotan(1)) + ((((1) / ((sin(1)) ^ (2))) * (x)) + ((((2) * ((cos(1)) / ((sin(1)) ^ (3)))) * ((x) ^ (2))) / ((2)!)))", 3)] [InlineData("a t3 + b t2 + c t + d", "((d) + ((c) * (x))) + (((((2) * (b)) * ((x) ^ (2))) / ((2)!)) + ((((6) * (a)) * ((x) ^ (3))) / ((3)!)))", 4)] public void TaylorDirect(string funcOverTRaw, string expectedRaw, int termCount) { @@ -31,7 +34,10 @@ public void TaylorDirect(string funcOverTRaw, string expectedRaw, int termCount) [InlineData("sin(t)", "(((0) + (x)) + ((0) + ((((-1) * ((x) ^ (3))) / ((3)!)) + (0)))) + (((((x) ^ (5)) / ((5)!)) + (0)) + ((((-1) * ((x) ^ (7))) / ((7)!)) + ((0) + (((x) ^ (9)) / ((9)!)))))", 10)] [InlineData("cos(t)", "(((1) + (0)) + ((((-1) * ((x) ^ (2))) / ((2)!)) + ((0) + (((x) ^ (4)) / ((4)!))))) + (((0) + (((-1) * ((x) ^ (6))) / ((6)!))) + ((0) + ((((x) ^ (8)) / ((8)!)) + (0))))", 10)] [InlineData("tan(t)", "((0) + ((x) + (0))) + ((((2) * ((x) ^ (3))) / ((3)!)) + ((0) + (((16) * ((x) ^ (5))) / ((5)!))))", 6)] - [InlineData("cotan(1 - t)", "(cotan(1)) + (((((-1) / ((sin(1)) ^ (2))) * (-1)) * (x)) + ((((((-1) * ((((2) * (sin(1))) * ((cos(1)) * (-1))) * (-1))) / (((sin(1)) ^ (2)) ^ (2))) * (-1)) * ((x) ^ (2))) / ((2)!)))", 3)] + // The derivatives are simplified as they are taken, so the coefficients are csc(1)^2 and + // 2 cot(1) csc(1)^2, where they were the unsimplified quotient rule's. + // https://github.com/asc-community/AngouriMath/issues/2291 + [InlineData("cotan(1 - t)", "(cotan(1)) + ((((1) / ((sin(1)) ^ (2))) * (x)) + ((((2) * ((cos(1)) / ((sin(1)) ^ (3)))) * ((x) ^ (2))) / ((2)!)))", 3)] [InlineData("a t3 + b s2 + c u + d", "((d) + ((c) * (z))) + (((((2) * (b)) * ((y) ^ (2))) / ((2)!)) + ((((6) * (a)) * ((x) ^ (3))) / ((3)!)))", 4)] [InlineData("(e^t)ln(1 + s)", "(0) + ((y) + ((((2) * ((x) * (y))) + ((-1) * ((y) ^ (2)))) / ((2)!)))", 3)] [InlineData("sin(t) + cos(s) + tan(u)", "((1) + ((x) + (z)))", 2)] @@ -45,6 +51,53 @@ public void MultivariableTaylorDirect(string funcOverTRaw, string expectedRaw, i actual.ShouldBe(expected); } + /// + /// Each derivative is simplified before the next is taken from it, so a quotient's + /// expansion stays the size of its terms. Taken as they stood, the derivatives of + /// sin(x) / x grew about eightfold a step: 7 terms around 1 printed in 36,421 + /// characters, and 11 ran out of memory. + /// https://github.com/asc-community/AngouriMath/issues/2291 + /// + [Theory] + [InlineData("sin(x) / x", 7)] + [InlineData("sin(x) / x", 11)] + [InlineData("e^x / (1 + x^2)", 7)] + public void AQuotientIsExpandedWithoutItsDerivativesGrowing(string func, int termCount) + { + Entity expr = func; + var taylor = MathS.Series.Taylor(expr, termCount, ("x", 1)); + Assert.True(taylor.Stringize().Length < 300 * termCount, taylor.Stringize()); + var expected = expr.Substitute("x", 1.1).EvalNumerical(); + var actual = taylor.Substitute("x", 1.1).EvalNumerical(); + var error = (expected - actual).Abs(); + Assert.True(error < 1e-6, $"{actual} where the function is {expected}"); + } + + /// + /// A function with no value at the point has no expansion there, nor has any of its + /// derivatives, so the expansion has none, once. It was the sum of the terms, each + /// NaN, after differentiating as many times as there were terms. + /// https://github.com/asc-community/AngouriMath/issues/2291 + /// + [Fact] + public void AnExpansionWhereTheFunctionHasNoValueHasNone() + { + Assert.Equal(Entity.Number.Real.NaN, MathS.Series.Taylor("sin(x) / x", 4, ("x", 0))); + Assert.Equal(Entity.Number.Real.NaN, MathS.Series.Taylor("sin(x) / x", 11, ("x", 0))); + Assert.Equal(Entity.Number.Real.NaN, MathS.Series.Taylor("sin(x) / x + y", 3, ("x", 0), ("y", 0))); + } + + /// + /// No terms add up to 0. It threw AngouriBugException, since the sum of no terms was + /// asked of a method that needs one. https://github.com/asc-community/AngouriMath/issues/2291 + /// + [Fact] + public void NoTermsAreZero() + { + Assert.Equal(Entity.Number.Integer.Zero, MathS.Series.Taylor("sin(x)", 0, ("x", 1))); + Assert.Equal(Entity.Number.Integer.Zero, MathS.Series.Taylor("sin(x) + y", 0, ("x", 1), ("y", 2))); + } + [Theory] [InlineData(0, 6, "sin(x)", -0.5)] [InlineData(0, 6, "sin(x)", -0.4)] diff --git a/Sources/Tests/UnitTests/Convenience/DocumentationExamplesRunTest.cs b/Sources/Tests/UnitTests/Convenience/DocumentationExamplesRunTest.cs index 81587a9c5..1ead75d21 100644 --- a/Sources/Tests/UnitTests/Convenience/DocumentationExamplesRunTest.cs +++ b/Sources/Tests/UnitTests/Convenience/DocumentationExamplesRunTest.cs @@ -105,10 +105,6 @@ public sealed class DocumentationExamplesRunTest "M:AngouriMath.MathS.Piecewise(System.Collections.Generic.IEnumerable{AngouriMath.Entity.Providedf},AngouriMath.Entity)", "M:AngouriMath.MathS.Piecewise(System.ValueTuple{AngouriMath.Entity,AngouriMath.Entity}[])", "M:AngouriMath.MathS.Pow(AngouriMath.Entity,AngouriMath.Entity)", - "M:AngouriMath.MathS.Series.Maclaurin(AngouriMath.Entity,System.Int32,AngouriMath.Entity.Variable[])", - "M:AngouriMath.MathS.Series.Taylor(AngouriMath.Entity,System.Int32,System.ValueTuple{AngouriMath.Entity.Variable,AngouriMath.Entity.Variable,AngouriMath.Entity}[])", - "M:AngouriMath.MathS.Series.Taylor(AngouriMath.Entity,System.Int32,System.ValueTuple{AngouriMath.Entity.Variable,AngouriMath.Entity}[])", - "M:AngouriMath.MathS.Series.TaylorTerms(AngouriMath.Entity,System.ValueTuple{AngouriMath.Entity.Variable,AngouriMath.Entity.Variable,AngouriMath.Entity}[])", "M:AngouriMath.MathS.Signum(AngouriMath.Entity)", "M:AngouriMath.MathS.SolveOde(AngouriMath.Entity,AngouriMath.Entity.Variable,AngouriMath.Entity.Variable)", "M:AngouriMath.MathS.Sqr(AngouriMath.Entity)",