Skip to content
This repository was archived by the owner on Oct 2, 2026. It is now read-only.
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
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 13 additions & 8 deletions UPSTREAM.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions src/AngouriMath.Mcp/AngouriMath.Mcp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
<PropertyGroup>
<LocalAngouriMath>$(MSBuildThisFileDirectory)../../../AngouriMath/Sources/AngouriMath/AngouriMath.csproj</LocalAngouriMath>
<UseLocalAngouriMath Condition="Exists('$(LocalAngouriMath)')">true</UseLocalAngouriMath>
<!-- One place for the version, so the reference and the line the build prints cannot
disagree again: the message said 2.0.0 through the whole of the 2.1.0 bump. -->
<AngouriMathPackageVersion>2.5.0</AngouriMathPackageVersion>
</PropertyGroup>

<ItemGroup Condition="'$(UseLocalAngouriMath)' == 'true'">
Expand All @@ -40,13 +43,13 @@
</ItemGroup>

<ItemGroup Condition="'$(UseLocalAngouriMath)' != 'true'">
<PackageReference Include="AngouriMath" Version="2.1.0" />
<PackageReference Include="AngouriMath" Version="$(AngouriMathPackageVersion)" />
</ItemGroup>

<Target Name="ReportAngouriMathSource" BeforeTargets="Build">
<Message Condition="'$(UseLocalAngouriMath)' == 'true'" Importance="high"
Text="AngouriMathMCP: building against the local AngouriMath checkout." />
<Message Condition="'$(UseLocalAngouriMath)' != 'true'" Importance="high"
Text="AngouriMathMCP: no sibling AngouriMath checkout found, building against the released 2.0.0 package." />
Text="AngouriMathMCP: building against the released $(AngouriMathPackageVersion) package." />
</Target>
</Project>
15 changes: 7 additions & 8 deletions src/AngouriMath.Mcp/Matrices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,14 @@ public static (Matrix Value, List<string> Guards) Clean(Matrix m)
/// <summary>
/// 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.
/// </summary>
public static Eigen Compute(Matrix a, Variable lambda)
{
Expand Down
8 changes: 8 additions & 0 deletions src/AngouriMath.Mcp/Parsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ public sealed record Outcome(Entity? Entity, List<string> 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",
Expand Down
9 changes: 4 additions & 5 deletions src/AngouriMath.Mcp/Resources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 11 additions & 16 deletions src/AngouriMath.Mcp/SelfTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ private static readonly (string Name, Func<bool> 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)");
Expand Down Expand Up @@ -85,22 +96,6 @@ private static readonly (string Name, Func<bool> 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)
Expand Down
Loading