From 78d34778bedf4c181911d76e62773b9096828432 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 31 Oct 2026 18:42:27 +0000 Subject: [PATCH] am_series keeps the power it is asked for Catalogue.Series and the MCP tool am_series document their degree as the highest power kept, and passed it to MathS.Series.Maclaurin and Taylor, whose second argument is the number of terms. So the highest power kept was one less than asked: sin(x) to degree 7 stopped at x ^ 5, and degree 1, which am_series offers for linearising a model, gave the constant term alone, 0 for sin(x) and 1 for e^x. The catalogue now passes one term more than the degree. 2.6.0's am_series called MathS.Series directly and answered the same. MathS.Series is unchanged. Its documentation called the argument the degree of the polynomial, and its own examples show it counts terms, Maclaurin(Sin(x), 4, x) stopping at x ^ 3, so the documentation now says that. CatalogueTest asks for the highest power at degrees 0, 1, 2 and 7, and the pin beside it takes one term more. ToolsTest.Series, which pinned degree 7 at x ^ 5, takes x ^ 7 and degree 1. The suite, the trio, F#, AOT and the eight harnesses pass. Fixes #2299. Co-Authored-By: Claude Opus 5.5 (1M context) --- BREAKING-CHANGES.md | 14 +++++++++++++ Sources/AngouriMath/Convenience/MathS.cs | 9 +++++--- .../AngouriMath/Core/Operations/Catalogue.cs | 8 ++++--- Sources/Tests/McpUnitTests/ToolsTest.cs | 11 +++++++--- .../Core/Operations/CatalogueTest.cs | 21 +++++++++++++++++-- 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 7a602ae02..eef54d33e 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -911,6 +911,20 @@ no value at `i`, where it is 1 ([#2256](https://github.com/asc-community/Angouri | `"x! = 0".ToEntity().Simplify()` | `False provided x in RR and (x >= 0 or not x in ZZ)` | `False provided x in ZZ implies x >= 0` | | `"x!".ToEntity().DomainCondition` | `x in RR and (x >= 0 or not x in ZZ)` | `x in ZZ implies x >= 0` | +### `am_series` keeps the power it is asked for + +**Silent: `am_series` and `Catalogue.Series` keep the powers up to the degree given, where they kept one fewer.** +The degree, documented as the highest power kept, was passed to `MathS.Series.Maclaurin` and `Taylor` as their +number of terms. So degree 1 gave the constant term alone, and linearising a model gave its value at the point. +`MathS.Series` is unchanged, and its documentation now says that its argument counts terms +([#2299](https://github.com/asc-community/AngouriMath/issues/2299)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `am_series`, `sin(x)`, degree 7 | `x ^ 5 / 120 - x ^ 3 / 6 + x` | `x ^ 5 / 120 - x ^ 7 / 5040 - x ^ 3 / 6 + x` | +| `am_series`, `sin(x)`, degree 1 | `0` | `x` | +| `am_series`, `e^x`, degree 1 | `1` | `1 + x` | + ### A number's conjugate is made under the setting in force **Silent: `Complex.Conjugate` is made under the downcasting setting of the caller, where the first caller's setting was diff --git a/Sources/AngouriMath/Convenience/MathS.cs b/Sources/AngouriMath/Convenience/MathS.cs index 2cfee6d72..72a9eb5a8 100644 --- a/Sources/AngouriMath/Convenience/MathS.cs +++ b/Sources/AngouriMath/Convenience/MathS.cs @@ -2624,7 +2624,8 @@ public static class Series /// The function to find the Maclaurin expansion of /// /// - /// The degree of the resulting Maclaurin polynomial (and the variable in the resulting series) + /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the + /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299 /// /// /// The variable/s to take the series over (and the variable the series will be over) @@ -2711,7 +2712,8 @@ public static Entity Maclaurin(Entity expr, int degree, params Variable[] exprVa /// The function to find the Taylor expansion of /// /// - /// The degree of the resulting taylor polynomial + /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the + /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299 /// /// /// The variable/s to take the series over (and the variable in the resulting series), @@ -2799,7 +2801,8 @@ public static Entity Taylor(Entity expr, int degree, params (Variable exprVariab /// The function to find the Taylor expansion of /// /// - /// The degree of the resulting taylor polynomial + /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the + /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299 /// /// /// The variable/s to take the series over, the variable the series will be over, diff --git a/Sources/AngouriMath/Core/Operations/Catalogue.cs b/Sources/AngouriMath/Core/Operations/Catalogue.cs index 6dfb2e3a3..4390c8465 100644 --- a/Sources/AngouriMath/Core/Operations/Catalogue.cs +++ b/Sources/AngouriMath/Core/Operations/Catalogue.cs @@ -146,13 +146,15 @@ internal static class Catalogue /// /// , or - /// where there is no point. + /// where there is no point, + /// with one term more than the degree, since those count terms: sin(x) to degree 1 is + /// x, and was 0. https://github.com/asc-community/AngouriMath/issues/2299 /// public static Query> Series { get; } = new( "series", "Expands as a Taylor series in a variable, up to a degree, around a point or around zero.", static (e, variable, degree, around) => around is null - ? MathS.Series.Maclaurin(e, degree, variable) - : MathS.Series.Taylor(e, degree, (variable, around)), + ? MathS.Series.Maclaurin(e, degree + 1, variable) + : MathS.Series.Taylor(e, degree + 1, (variable, around)), TheVariable, new Parameter("degree", "The highest power kept."), new Parameter("around", "The point, or none for zero.")); // ---------------------------------------------------------------- queries on a number diff --git a/Sources/Tests/McpUnitTests/ToolsTest.cs b/Sources/Tests/McpUnitTests/ToolsTest.cs index c8fb608a5..7c57762ee 100644 --- a/Sources/Tests/McpUnitTests/ToolsTest.cs +++ b/Sources/Tests/McpUnitTests/ToolsTest.cs @@ -175,9 +175,14 @@ public void EigenvaluesHoldAtZeroToo() Assert.DoesNotContain("provided", answer.Field("eigenvalues")); } - [Fact] - public void Series() => - Assert.Equal("x ^ 5 / 120 - x ^ 3 / 6 + x", Mcp.Call("am_series", """{"expression":"sin(x)","variable":"x","degree":7}""").Field("result")); + // The degree is the highest power kept, x ^ 7 at 7. It was passed on as the number of terms, so + // degree 7 stopped at x ^ 5 and degree 1 was the constant. https://github.com/asc-community/AngouriMath/issues/2299 + [Theory] + [InlineData("sin(x)", 7, "x ^ 5 / 120 - x ^ 7 / 5040 - x ^ 3 / 6 + x")] + [InlineData("sin(x)", 1, "x")] + [InlineData("e^x", 1, "1 + x")] + public void Series(string expression, int degree, string expected) => + Assert.Equal(expected, Mcp.Call("am_series", $$"""{"expression":"{{expression}}","variable":"x","degree":{{degree}}}""").Field("result")); [Fact] public void Factorize() => diff --git a/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs b/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs index 288aa6743..996d6094f 100644 --- a/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs +++ b/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs @@ -171,6 +171,23 @@ public void AnEntryClaimsWhatItsTransformationClaims(Transformation transformati Assert.Equal(transformation.Relation, relation); } + /// + /// The series is taken to the degree asked for, which is the highest power it keeps. It + /// was passed on as the number of terms, one fewer: sin(x) to degree 1 was 0. + /// https://github.com/asc-community/AngouriMath/issues/2299 + /// + [Theory] + [InlineData("sin(x)", 1, null, "x")] + [InlineData("e^x", 1, null, "1 + x")] + [InlineData("sin(x)", 7, null, "x - x^3/6 + x^5/120 - x^7/5040")] + [InlineData("e^x", 2, "1", "e + e (x - 1) + e (x - 1)^2 / 2")] + [InlineData("cos(x)", 0, null, "1")] + public void ASeriesKeepsTheHighestPowerItIsAskedFor(string expression, int degree, string? around, string expected) + { + var series = Catalogue.Series.Ask(expression, MathS.Var("x"), degree, around is null ? null : MathS.FromString(around)); + Assert.Equal(Entity.Number.Integer.Zero, (series - MathS.FromString(expected)).Simplify()); + } + /// /// One answer from each entry, against the method the entry names, on an input where /// that method and its neighbours answer differently -- so that an entry holding the @@ -203,8 +220,8 @@ public void EachEntryAnswersAsTheMethodItNames() Assert.Equal(MathS.Boolean.BuildTruthTable("a and not b", "a", "b"), Catalogue.TruthTable.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")])); Assert.Equal(MathS.SolveBooleanTable("a and not b", "a", "b"), Catalogue.SatisfyingAssignments.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")])); Assert.NotEqual(Catalogue.TruthTable.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")]), Catalogue.SatisfyingAssignments.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")])); - Assert.Equal(MathS.Series.Maclaurin("e^x", 3, x), Catalogue.Series.Ask("e^x", x, 3, null)); - Assert.Equal(MathS.Series.Taylor("e^x", 3, (x, 1)), Catalogue.Series.Ask("e^x", x, 3, 1)); + Assert.Equal(MathS.Series.Maclaurin("e^x", 4, x), Catalogue.Series.Ask("e^x", x, 3, null)); + Assert.Equal(MathS.Series.Taylor("e^x", 4, (x, 1)), Catalogue.Series.Ask("e^x", x, 3, 1)); Assert.NotEqual(Catalogue.Series.Ask("e^x", x, 3, null), Catalogue.Series.Ask("e^x", x, 3, 1)); Assert.Equal(MathS.ToBaseN(10.5m, 2), Catalogue.ToBase.Ask(10.5m, 2));