diff --git a/src/content/quickstart/index.html b/src/content/quickstart/index.html index 20319332f..918083dd1 100644 --- a/src/content/quickstart/index.html +++ b/src/content/quickstart/index.html @@ -39,12 +39,14 @@

Install for F#

dotnet add package AngouriMath.FSharp

- The current release is 2.1.0, and both it and 2.0.0 change answers that earlier versions + The current release is 2.5.0, and every 2.x release changes answers that earlier versions got wrong. Whichever version you are coming from, read BREAKING-CHANGES.md first: it lists every input whose result is now different, with the old value and the new one, - under a heading per release. 2.1.0 is a drop-in replacement for 2.0.0 — the assembly version is - pinned at 2.0.0.0 for the whole of 2.x. + under a heading per release. The assembly version is pinned at 2.0.0.0 for the whole + of 2.x, so 2.5.0 binds wherever a 2.x release did. One rename needs a recompile: 2.2.0 spelled + Transformation.Rationalisation and RewriteRules.RationaliseDenominator + with a z, and code calling either throws MissingMethodException until it is rebuilt.

The library targets netstandard2.0, net8.0 and net10.0. diff --git a/src/content/whatsnew/index.html b/src/content/whatsnew/index.html index 5ef8f61fe..59632be91 100644 --- a/src/content/whatsnew/index.html +++ b/src/content/whatsnew/index.html @@ -34,7 +34,214 @@

What's new

--> -
2.1.0 +
2.5.0 +

+ Tier 2 of the Math OS roadmap — the rewrite graph — is finished in this release. An e-graph now lives in the kernel rather than in a measurement harness, every one of the thirty registered rule sets runs as data through a real e-matcher, and each rule declares its own soundness tier and how much it grows what it matches. The honest result of that work is the part worth reading: Simplify still does not run on the graph, because the tier asked for equality saturation to be evaluated against memory cost on real expressions and the evaluation came back negative. A measurement that says "not by default" is the deliverable, not a substitute for it. +

+ Every changed answer is in BREAKING-CHANGES.md under 2.5.0 — since 2.4.0, with the old value, the new one and why, each measured on a build of both sides. Read that first if you have code depending on an answer. Seventy-six entries at a glance and thirty-seven worked sections; twenty-nine are marked Silent — the call still succeeds and quietly returns something else — and two are Loud, where input that parsed now raises. +

+ AssemblyVersion stays 2.0.0.0. The recorded public surface has 136 additions and no removals, so this is source- and binary-compatible: nothing that compiled against 2.4.0 stops compiling, and nothing bound against it stops binding. +

+ Wrong answers fixed:
+

+ The rewrite graph:
+ The e-graph moved out of the harness and into the kernel behind an explicit opt-in (#1101), and what matches against it is a genuine e-matcher — a pattern against an e-class, not against a term (#1103). A cost model is a record taken as a parameter rather than read from an ambient setting (#1102), so SmallestTree, FewestDivisions and FewestRadicals are things you pass rather than things you set. Canonicalisation runs over the graph (#1105), rule priorities are computed from the patterns instead of typed by hand (#1106), and all thirty registered sets run as data (#1192). +

+ The parts that make that safe are tests rather than assertions by an author: a rule's declared growth is checked against a corpus (#1158), a set that rewrites back to where it started is caught (#1165), and so is one that never settles over a whole tree (#1168). The runaway that used to be blamed on a whole soundness tier turned out to be one inverse pair across two sets (#1193), and the growth ceiling turned out to already be the scheduling policy the documents kept deferring (#1194, #1203). +

+ Speed:
+ Five changes, each measured at the entry point rather than in a microbenchmark, and each answer-identical: +

+ + + + + + + +
SolveHard−98.6% allocation — the solver now tries the equation as written before asking for its alternatives (#1209)
SimplifyHard−64% — a candidate already registered is not registered again (#1207)
SimplifyHard−44% — a candidate is registered once and a subtree costed once (#1205)
SimplifyHard−35% — the sort key of a node is spelt once (#1210)
SimplifyHard−25% — a candidate is re-simplified at the default level whatever level it was offered at (#1211)
+ Extraction from the graph became a fixed point computed from the leaves up rather than a top-down walk whose cycle guard defeated its own memo: 1,918 ms to 4 ms on the case that exposed it, and a budget now honoured to the millisecond (#1202). +

+ New notation:
+ + Closed forms:
+ A summation with a closed form now has one, and each carries the condition it holds under rather than asserting it unconditionally: +

+
sum(k, k, 1, n)              →  (n + n^2)/2, with the condition on n
+sum(x^k, k, 0, +oo)          →  1 / (1 - x) provided abs(x) < 1
+sum(2^k, k, 0, +oo)          →  +oo, by the nth-term test, where it used to be left as written
+ Polynomial summands (#1152), monomial products (#1153), a power over a factorial (#1213), binomial sums with a power or trigonometric weight (#1214), geometric series (#1218) and divergent ones (#1224). +

+ Integration reached further too: a biquadratic denominator decomposed over the reals (#1145), fractional powers and the tangent as substitutions (#1146), improper quotients divided out before decomposition (#1147), repeated quadratic denominators (#1155), and an integral across a jump split at the jumps rather than taken through an antiderivative that treats a floor as a constant (#1215). There is also an analytical solver for first-order linear ODEs (#1137). +

+ Integration speed, and a correction to an earlier version of these notes:
+ An earlier version of this page said the integrator was left much slower than 2.4.0 on hard integrands. That was wrong, and it was my measurement error: the figures behind it came from a corpus run that had the full unit suite running beside it on the same machine, which inflated the sections being compared. Measured properly — the seventeen hardest integrands from that part of the corpus, one process, nothing else running, two runs of each arm — 2.5.0 is about 30% faster: 72,017 and 71,390 ms for 2.4.0 against 53,159 and 47,996 ms here. Several individual integrands halve. The details are on #1232, which is closed. +

+ The honest caveat that survives the correction: partial fractions over repeated factors with high powers is slow in both versions — 1/((2+x)^3*(3+x)^4) takes about half a minute either way. That is a standing limitation rather than anything new. +

+ No Rubi coverage figure is claimed for 2.5.0, because that run was never completed on a quiet machine. The last complete one is 2.4.0's, taken the same day: 599 of 1774, 0 wrong. +

+ Documentation that is checked:
+ 254 worked examples in the XML documentation say what they print, and nothing checked any of them; 67 were wrong — 56 printed something else and 11 did not compile or threw. They are compiled and run against what they claim now (#1184), so a changed answer fails a test instead of leaving a wrong promise standing in the docs a user reads. +

+
+ +
2.4.0 +

+ Tier 1 of the Math OS roadmap is finished, and the thing that finished it kept finding wrong answers. Writing a rewrite rule out as data — a pattern and a replacement, rather than an arm of a switch — makes the correspondence between the two something you have to state. Four times this cycle, a rule did not survive stating it. +

+ Every changed answer is in BREAKING-CHANGES.md under 2.4.0 — since 2.3.0, with the old value, the new one and why, each measured on a build of both sides. Read that first if you have code depending on an answer. Twenty-seven entries, and more than half are marked Silent — the call still succeeds and quietly returns something else. +

+ AssemblyVersion stays 2.0.0.0. The recorded public surface shows 68 additions and 130 removals, and none of the removals is one a caller can hit: Stringize() and Latexize() stopped being abstract on Entity and each node's override became a private helper, so the member is still public on Entity and inherited by every node. Source- and binary-compatible. +

+ Wrong answers fixed:
+

+ Factorisation:
+ Factorize was built entirely out of rewrite rules, so it factored what someone had written a rule for and handed everything else back whole — while square-free decomposition, Zassenhaus over ℚ and the multivariate GCD sat in the tree unused by it. +

+
"x3 - 1".Factorize()          x ^ 3 - 1              →  (x - 1) * (x ^ 2 + x + 1)
+"x4 - 5x2 + 4".Factorize()    unchanged              →  (x + 1) * (x + 2) * (x - 2) * (x - 1)
+"x2 + 2x + 1".Factorize()     unchanged              →  (x + 1) ^ 2
+ The layer speaks only where the rules said nothing, so every answer they already gave is unchanged — the order two factors come out in is arbitrary and theirs is the one on record. (#1018) +

+ And the layer itself reaches further. Hensel lifting along an evaluation homomorphism (#1088, #1089) factors bivariate polynomials that Kronecker's substitution refuses — not because its image is too large but because it over-factors: x⁷ − y⁷ maps to t⁷(1 − t⁴⁹), whose factors are cyclotomic. An evaluation image inflates nothing. +

+
Factor("x12 - y12", "x")      null                   →  six factors, the full cyclotomic split
+Factor("x7 - y7",   "x")      null                   →  (x - y)(x⁶ + x⁵y + ⋯ + y⁶)
+ Where the substitution gives up entirely, an evaluation image can still prove a polynomial irreducible — which since #1059 is an answer rather than a refusal, so Factor("x2 + y2 + z2 + w2 + 1", "x") returns the polynomial instead of null. (#1087) +

+ The rule sets are data:
+ Thirty of thirty rule sets now have a form in which each rule is a value — a pattern, a replacement, a soundness tier and a direction — proven to agree with the switch it replaces over thousands of generated expressions. Twenty-seven run it. The three that do not are the canonical orders, and that is a measurement rather than an omission. +

+ It buys: every rule individually addressable (407 entries), per-rule soundness where only the set had a tier before (181 Sound, 141 SoundUnderAssumptions), and 26 rules that can be read backwards. +

+ Performance, honestly:
+ SimplifyEasy is about 13% slower than before the exchange — 82,676 ns to 93,732 ns on one desktop, both arms, standard deviation under half a per cent. Every conversion was measured against the commit in front of it and every one came back free or better; nothing was measured against the start, and the sum of a run of free steps is +13%. +

+ It would have been worse. Indexing each set's rules by node type recovered most of it, and a bounded pattern is now walked by index rather than enumerated — 165.05 MB to 163.40 MB of SolveMediumHard on its own. +

+ The work shape is unchanged: 4,914 rule-set invocations on that input at both ends, so this is per-operation overhead rather than extra work. version_performance_control.md records where it goes, what three attempts to remove it measured, and the roughly 40% still unattributed. +

+ That file also gained a measurement worth more than the column: the Kernel Benchmark run twice on one commit is 30–57% apart on every benchmark, while allocation over the same pair agrees to 0.03%. Timing comparisons between its columns are evidence only above about 50%. +

+
+ +
2.3.0 +

+ Correctness release, and the first one whose claim to that is measured on an outside corpus rather than only on our own tests. Against Rubi's integration suite — 1774 problems that each carry an antiderivative known to exist — this version answers 604 where 2.2.0-era master answered 536, and gets 0 wrong where that answered 7. Six of those seven were NaN: a definite claim that no value exists, made about integrals that have one. +

+ Every changed answer is in BREAKING-CHANGES.md under 2.3.0 — since 2.2.0, with the old value, the new one and why, each measured on a build of both sides. Read that first if you have code depending on an answer. +

+ AssemblyVersion stays 2.0.0.0. This release removes and renames nothing — 103 additions and 0 removals against the 2.2.0 public API baseline — so it is a drop-in replacement in both the binding and the source sense, which 2.2.0 was not. +

+ Wrong answers fixed:
+

+ Interoperability:
+ + Names and reporting:
+ + Under the hood:
+ The rule registry grew to cover every addressable rule set, with confluence and termination checked by tooling rather than asserted — #746 tier 2 asks for exactly that. Core.Binding makes binder resolution happen at construction, which is what let i, pi and e become bindable names. +

+ Performance:
+ The 1769th column of version_performance_control.md, taken on the same runner class as the 1620th and 1671st so the three are comparable. Every row faster or flat. SolveEasy is 21.3 ms to 8.5 ms — a 2.5× speedup with a 2.3× allocation drop beside it, which is how you tell work from machine noise. +

+ What this release is not:
+ It does not complete a #746 tier. It advances tier 1 and tier 2 and finishes neither, which is why it is a minor version. The rewrite graph that v2.0 is reserved for is still ahead. +

+
+ +
2.2.0 +

+ Infrastructure release. 2.1.0 was mostly wrong answers becoming right ones; this one is mostly the layer underneath them — a real polynomial layer, and a written specification of what canonical form means here with both halves implemented. The corrected answers in it are largely consequences of those two rather than separate fixes. +

+ Every changed answer is in BREAKING-CHANGES.md under 2.2.0 — since 2.1.0, with the old value, the new one, and why, measured on a build of each version. Read that first if you have code depending on an answer. +

+ AssemblyVersion stays 2.0.0.0. See the rename note at the bottom before dropping the DLL in without recompiling. +

+ A polynomial layer:
+ The single item that #746 names as unblocking a large fraction of the tracker. Multivariate GCD, resultants, square-free decomposition, and factorisation over ℚ and finite fields. (#918, #920/#923, #921/#927) +

+ It is not shipped for its own sake — three things in this release are consequences of having it: +

+

+ Canonical form: specified, measured, and offered:
+ Docs/Contributing/CanonicalForm.md states the position rather than leaving it to be inferred: canonical is about identity, simplest is about presentation, and there is no canonical form for the whole language — zero-equivalence is undecidable once pi, exp, the trigonometric functions and abs are in play (Richardson 1968). So the specification is a canonical form on a decidable sublanguage, a normalisation elsewhere that must not be mistaken for one, and a search that is not required to be canonical at all. (#928) +

+ Both halves are now reachable from Entity, beside Simplify and Factorize: +

+
Entity Canonicalize()                     // the commutative structure: 0 idempotence and
+                                          // 0 order-independence failures over 834 expressions
+Entity? CanonicalizeAsRationalFunction()  // rational functions over Q -- or null, which is the
+                                          // library saying it has no answer rather than guessing
+ x/x canonicalises to 1 provided not x = 0 and is deliberately not equal to the canonical form of 1: the quotient is undefined where the polynomial is not. (#933, #935, #940) +

+ Nothing applies either by default. Simplify and InnerSimplified return exactly what they returned before. Turning canonical ordering on by default would change every commutative operand order in every printed answer, and that is a decision for a release that says so. +

+ Wrong answers fixed:
+ + Under the hood:
+ + One rename:
+ Five members were spelled -ise on a surface that is otherwise Factorize, Latexize, Normalization. Three had not shipped; two had: +

+ + + + +
Was (2.1.0)Is
Transformation.RationalisationTransformation.Rationalization
RewriteRules.RationaliseDenominatorRewriteRules.RationalizeDenominator
+ Recompiling turns a stale reference into a compile error. Swapping the DLL without recompiling does not — AssemblyVersion is pinned at 2.0.0.0 so the assembly still binds, and the call throws MissingMethodException when it is reached. Documentation prose keeps British spelling; the convention is about identifiers. (#940) +

+
+ +
2.1.0

A correctness release. Almost everything below is a wrong answer becoming a right one, and most of it was found by harnesses rather than reported — boundary points where a rule's assumption fails,