From 19af1b973e8b0ff8b9789bc0f5e6206ca5882cd1 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Thu, 1 Oct 2026 18:27:59 +0000 Subject: [PATCH] Move to AngouriMath 2.5.0 Built against the published 2.5.0 package (-p:UseLocalAngouriMath=false), not the sibling checkout. All identities hold; two documented defects no longer reproduce and leave the docs: the integral of x^4*(1-x)^4/(1+x^2), which 2.5.0 divides out itself (its value on [0, 1], exactly 22/7 - pi, is now a selftest identity), and the determinant's pivot guards, which 2.5.0 no longer leaves. The grammar was re-probed by parsing name(x) for every name the 2.5.0 grammar declares. card, sum, product, argmax and argmin are new and join the whitelist, and so do short spellings the grammar has accepted since at least 2.1.0 and the list missed -- asin, acos, atan, sh, ch, th and their kin -- on which it warned about an unknown function on correct input. The version is set once, and the line the build prints reads it. Part of asc-community/AngouriMath#1649. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- AGENTS.md | 3 ++- README.md | 15 ++++++------ UPSTREAM.md | 21 ++++++++++------- src/AngouriMath.Mcp/AngouriMath.Mcp.csproj | 7 ++++-- src/AngouriMath.Mcp/Matrices.cs | 15 ++++++------ src/AngouriMath.Mcp/Parsing.cs | 8 +++++++ src/AngouriMath.Mcp/Resources.cs | 9 ++++---- src/AngouriMath.Mcp/SelfTest.cs | 27 +++++++++------------- 8 files changed, 57 insertions(+), 48 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9bd1ff7..8e10baa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,8 @@ every documented defect. ## Which AngouriMath you built against The build uses a **sibling AngouriMath checkout at `../AngouriMath` when one exists, and the -released 2.1.0 NuGet package when it does not**. It prints which; read the line. +released NuGet package (2.5.0, set once as `AngouriMathPackageVersion`) when it does not**. +It prints which, and the version; read the line. This used to be the first trap in the repo — the fallback was 1.4.0, which behaves differently enough that two assertions in `test/smoke.sh` failed against it legitimately diff --git a/README.md b/README.md index c77fc42..542d3eb 100644 --- a/README.md +++ b/README.md @@ -264,14 +264,13 @@ Limits worth knowing: there are no eigenvectors, no SVD, and no matrix exponenti and that is Abel–Ruffini rather than a defect — no general radical solution exists. Ten qubits would be a 1024×1024 symbolic matrix; this dies long before that. -**A defect it works around.** `Entity.Matrix.Determinant` calls GenericTensor's -`DeterminantGaussianSafeDivision`, which divides by pivots and leaves a `provided` guard for -each. Those guards are artefacts, not mathematics: the raw output claims -`det([[a,b],[c,d]]) = a*d - b*c provided not a = 0`, and eigenvalues of `[[0,J],[J,0]]` come -back as `J provided not J = 0` — excluding a perfectly valid case. This server simplifies -*first* (the guard is what licenses cancelling `a/a`), then strips the guard and reports it -under `dropped_guards`. The real fix belongs upstream: GenericTensor already ships a -division-free `DeterminantLaplace`, which emits none of this. +**Pivot guards.** Before AngouriMath 2.5.0 the determinant divided by pivots and left a +`provided` guard for each — `det([[a,b],[c,d]]) = a*d - b*c provided not a = 0`, and +eigenvalues of `[[0,J],[J,0]]` came back as `J provided not J = 0` — artefacts rather than +mathematics. 2.5.0's determinant leaves none: `a*d - b*c`, and `lambda^2 - J^2` for that +characteristic polynomial. This server still simplifies *first* and strips any guard that +remains, reporting it under `dropped_guards`, so a guard from any other pivoting routine is +handled the same way. ## Step-by-step, and knowing what to distrust diff --git a/UPSTREAM.md b/UPSTREAM.md index ce4cb00..4dca690 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -42,27 +42,32 @@ reports them under `dropped_guards` in the meantime. ## Defects worth reporting upstream -Re-verified against AngouriMath 2.1.0 — a claim measured on an older build is not worth +Re-verified against AngouriMath 2.5.0 — a claim measured on an older build is not worth reporting. `--selftest` re-checks each row on every run; three entries were dropped at the 2.0.0 upgrade because the release fixed them, which is the whole reason that check exists. -**Nothing was dropped at 2.1.0**, and that is a measurement rather than an assumption: the -build was made against the *published* 2.1.0 package rather than the sibling checkout, which -still sat at 2.0.0 while this was written. All eleven identities hold and all five rows below -still reproduce. 2.1.0 is a correctness release, and none of what it fixed is on this list. +**Two were dropped at 2.5.0**, measured against the *published* 2.5.0 package rather than the +sibling checkout: the integral of `x^4*(1-x)^4/(1+x^2)` and the determinant's pivot guards, +both under "Fixed upstream" below. Nothing was dropped at 2.1.0. All identities hold, the +integral is now one of them, and the four rows below still reproduce. | Observed | Note | |---|---| | `Simplify(sqrt(x^2))` is left as written, not reduced to `abs(x)` | No longer the soundness bug it was — 2.0.0 stopped answering `x`, which was wrong for every negative. What remains is a gap: writing `abs` needs to know the expression is real, which the codomain of [#719](https://github.com/asc-community/AngouriMath/issues/719) can now say and the simplifier does not yet read. | | `MathS.Equations(...)` throws on an equality | It wants each equation in `= 0` form; passing an `Equalsf` raises `NotSufficientlySupportedException` rather than normalising `a = b` to `a - b`, which is a rewrite it could do itself. This server does it instead. Re-checked on 2.0.0 by `--selftest`; the exception type changed with the release, the behaviour did not. | -| `Integrate` declines `x^4*(1-x)^4/(1+x^2)` | It handles the same function once the polynomial division is done by hand, so the gap is dividing a rational function whose numerator outranks its denominator. | | `Entity.DefiniteIntegral` is a first-order rule | New in 2.0.0, and it is a rectangle rule: the error halves per doubling of the step count, so 4000 steps buy about four digits of `∫[0,1] e^(x^2)` at ~150 ms. Simpson's rule is the same amount of code and would give roughly eight. It also samples both endpoints, so a convergent integral with a singular endpoint — `∫[0,1] sin(x)/x`, `∫[0,1] ln(x)` — returns `NaN` rather than a value. Both are worth raising; this server runs it twice and reports only the agreed digits in the meantime. | | An unknown identifier still becomes implicit multiplication silently | 2.0.0 closed most of this: `exp`, `log10`, `log2`, `pow`, `floor`, `ceil`, `round`, `min`, `max` and `gcd` became real functions, and eleven names the library does not have are now refused by name. The general case remains — `im(z)` is `im * z` — and cannot be closed without refusing `a(b + c)`, so a warning or a strict default is still the only answer. This server warns. | ## Fixed upstream, kept here as a record -Each of these was on the list above and reproduced no longer at the 2.0.0 upgrade. Listed -so that nobody re-reports them, and so the cost of not re-measuring is visible. +Each of these was on the list above and reproduced no longer at an upgrade. Listed so that +nobody re-reports them, and so the cost of not re-measuring is visible. + +- **`Integrate` declining `x^4*(1-x)^4/(1+x^2)`** (at 2.5.0). It divides the rational + function out itself now, and the integral over `[0, 1]` is exactly `22/7 - pi`, which + `--selftest` checks as an identity. +- **The determinant's pivot guards** (at 2.5.0). `det([[a,b],[c,d]])` was + `a*d - b*c provided not a = 0`; it is `a*d - b*c`. - **`Factorize(x^2 - 1)` emitting `sqrt(1)`.** Dropped before 2.0.0: true of 1.4.0, false of the branch, and it went stale unnoticed. This is why `--selftest` exists. diff --git a/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj b/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj index fe13d64..b0f81ae 100644 --- a/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj +++ b/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj @@ -31,6 +31,9 @@ $(MSBuildThisFileDirectory)../../../AngouriMath/Sources/AngouriMath/AngouriMath.csproj true + + 2.5.0 @@ -40,13 +43,13 @@ - + + Text="AngouriMathMCP: building against the released $(AngouriMathPackageVersion) package." /> diff --git a/src/AngouriMath.Mcp/Matrices.cs b/src/AngouriMath.Mcp/Matrices.cs index 6382167..3e3ce64 100644 --- a/src/AngouriMath.Mcp/Matrices.cs +++ b/src/AngouriMath.Mcp/Matrices.cs @@ -134,15 +134,14 @@ public static (Matrix Value, List Guards) Clean(Matrix m) /// /// Eigenvalues via the characteristic polynomial. /// - /// The guard-dropping is not cosmetic. Entity.Matrix.Determinant calls GenericTensor's - /// DeterminantGaussianSafeDivision, which divides by pivots and leaves a `provided` guard - /// for each one. On a symbolic matrix those guards are artefacts of the algorithm, not - /// mathematics: the characteristic polynomial of the Pauli X matrix comes back as - /// `lambda^2 - 1 provided not lambda = 0`, and [[0,J],[J,0]] yields eigenvalues + /// The guard-dropping is not cosmetic. Until AngouriMath 2.5.0, Entity.Matrix.Determinant + /// called GenericTensor's DeterminantGaussianSafeDivision, which divides by pivots and left + /// a `provided` guard for each one. On a symbolic matrix those guards are artefacts of the + /// algorithm, not mathematics: the characteristic polynomial of the Pauli X matrix came + /// back as `lambda^2 - 1 provided not lambda = 0`, and [[0,J],[J,0]] yielded eigenvalues /// `{J provided not J = 0, -J provided not J = 0}` — both wrong, since those values are - /// perfectly valid. GenericTensor also ships the division-free DeterminantLaplace, which - /// would emit none of this; until AngouriMath uses it for symbolic entries, we strip the - /// guards here and report what was dropped. + /// perfectly valid. 2.5.0's determinant leaves none (`lambda^2 - 1`, `lambda^2 - J^2`); the + /// stripping stays, and still reports what it dropped, for any routine that pivots. /// public static Eigen Compute(Matrix a, Variable lambda) { diff --git a/src/AngouriMath.Mcp/Parsing.cs b/src/AngouriMath.Mcp/Parsing.cs index 270d9e9..3699606 100644 --- a/src/AngouriMath.Mcp/Parsing.cs +++ b/src/AngouriMath.Mcp/Parsing.cs @@ -58,6 +58,14 @@ public sealed record Outcome(Entity? Entity, List Warnings, string? Erro "floor", "ceil", "ceiling", "round", "min", "max", "gcd", "factorial", // `pow(x, y)` parses to x^y as of 2.0.0. "pow", + // Short and alternative spellings the grammar has accepted at least since 2.1.0, and + // which this list missed until the 2.5.0 probe read every name back: `asin(x)` was + // being warned about as an unknown function. + "asin", "acos", "atan", "asec", "acsc", "acosec", "acot", "acotan", + "sh", "ch", "th", "cth", "sch", + "acoth", "acsch", "arcoth", "arcsch", "arsch", + // New by 2.5.0: a set's cardinality, sums and products, and arg-extrema. + "card", "sum", "product", "argmax", "argmin", "gamma", "phi", "derivative", "integral", "limit", "limitleft", "limitright", "piecewise", "provided", "apply", "lambda", "domain", "intersect", "and", "or", "not", "xor", diff --git a/src/AngouriMath.Mcp/Resources.cs b/src/AngouriMath.Mcp/Resources.cs index 3a5f4c4..e43c5fc 100644 --- a/src/AngouriMath.Mcp/Resources.cs +++ b/src/AngouriMath.Mcp/Resources.cs @@ -131,12 +131,11 @@ exactly what to run — so this doubles as a self-test corpus. If one of these s ## A machine-checked proof that 22/7 > π The integrand is positive on (0,1), so the integral is positive — which proves the - inequality. It needs the polynomial division done by hand first, because the - integrator declines the unexpanded rational form: + inequality: - 1. `am_verify_equal x^4*(1-x)^4/(1+x^2) vs x^6 - 4*x^5 + 5*x^4 - 4*x^2 + 4 - 4/(1+x^2)` - 2. `am_integrate x^6 - 4*x^5 + 5*x^4 - 4*x^2 + 4 - 4/(1+x^2) dx` (verifies) - 3. `am_evaluate 1/7 - 4/6 + 1 - 4/3 + 4 - 4*arctan(1)` → **exactly `22/7 - pi`** + 1. `am_integrate x^4*(1-x)^4/(1+x^2) dx` (verifies; the integrator divides the + rational function out itself as of AngouriMath 2.5.0) + 2. `am_evaluate 1/7 - 4/6 + 1 - 4/3 + 4 - 4*arctan(1)` → **exactly `22/7 - pi`** ## Near misses — where floating point would lie to you diff --git a/src/AngouriMath.Mcp/SelfTest.cs b/src/AngouriMath.Mcp/SelfTest.cs index 954d79a..b1b0666 100644 --- a/src/AngouriMath.Mcp/SelfTest.cs +++ b/src/AngouriMath.Mcp/SelfTest.cs @@ -45,6 +45,17 @@ private static readonly (string Name, Func Holds)[] Identities = ("42 is 101010 in binary", () => MathS.ToBaseN(42, 2) == "101010"), ("42 is the 5th Catalan number", () => Value("10! / (6! * 5!)") == "42"), ("Pythagoras: sin^2 + cos^2 = 1", () => IsZero("sin(x)^2 + cos(x)^2 - 1")), + ("22/7 - pi is the integral of x^4*(1-x)^4/(1+x^2) over [0, 1]", () => + { + // Documented as declined until 2.5.0 integrated it whole; kept as an identity so + // the proof the reliability resource walks through cannot silently stop working. + var outcome = Parsing.Parse("x^4*(1-x)^4/(1+x^2)"); + if (outcome.Entity is null) return false; + var x = MathS.Var("x"); + var antiderivative = outcome.Entity.Integrate(x); + var value = antiderivative.Substitute(x, 1) - antiderivative.Substitute(x, 0); + return Numeric.IsZero((value - (MathS.FromString("22/7") - MathS.pi)).Simplify()); + }), ("d/dx integral of x*ln(x) returns the integrand", () => { var outcome = Parsing.Parse("x*ln(x)"); @@ -85,22 +96,6 @@ private static readonly (string Name, Func StillBroken, string Documented) var ratio = Math.Abs(fine - coarse) / Math.Abs(finer - fine); return ratio is > 1.7 and < 2.3; }, "error halves per doubling, so ~4 digits at 4000 steps; Simpson would give more"), - ("integral of x^4*(1-x)^4/(1+x^2)", () => - { - var outcome = Parsing.Parse("x^4*(1-x)^4/(1+x^2)"); - if (outcome.Entity is null) return false; - var result = outcome.Entity.Integrate(MathS.Var("x")).Stringize(); - return Guard.IsDeclined(result); - }, "declined, though it succeeds once divided out by hand"), - ("determinant leaves a pivot guard", () => - { - var m = MathS.Matrix(new Entity[,] - { - { MathS.Var("a"), MathS.Var("b") }, - { MathS.Var("c"), MathS.Var("d") }, - }); - return m.Determinant is { } det && Analysis.Conditions(det.Simplify()).Count > 0; - }, "det([[a,b],[c,d]]) carries `provided not a = 0`"), ]; public static int Run(TextWriter output)