Repository navigation
2.1.0
AngouriMath 2.1.0
Correctness release. Almost everything here 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, one child
process per case so a stack overflow is a result rather than the end of the run, every documented code
sample compiled and run, and the generated SymPy code executed rather than read.
Every changed answer is in BREAKING-CHANGES.md under 2.1.0 — since 2.0.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, so this is a drop-in replacement for 2.0.0 on a strong-named reference.
Wrong answers fixed
arcsin(sin(x))and three siblings no longer cancel off the principal branch.arcsin(sin(3))was
3; it ispi - 3. Four rules had been wrong since 2020. (#884)arctan(x) + arccotan(x)is no longer alwayspi/2— this library'sarccotanhas range
(-pi/2, pi/2], so the sum is-pi/2for negativex. (#887)ln(e^x)no longer simplifies tox, which is wrong whereverIm xleaves(-pi, pi]: at
x = 3*pi*ithe expression ispi*i. (#902)log(1, 1)was0and isNaN— it is0/0.log(1, 2)was+oo. (#890)abs(sgn(x))andsgn(abs(x))were1, and both are0atx = 0. (#892)- A logical connective is no longer strict in
NaN.False and uisFalsefor an operand with no
truth value, which is whatSimplifyalready answered while evaluation saidNaN. (#880) - A connective over a number now declines rather than reporting the same type error three
different ways. A number is not a truth value. (#897) - A conditional set's bound variable was named from a hash of the predicate, which could spell
true
and throw — an intermittent CI failure that would not reproduce. (#891)
Better answers
absfolds where the sign of its argument is known.abs(-sqrt(6))issqrt(6); a concrete
quadratic inequality no longer answers withabs(-sqrt(6)) / 2as an endpoint. (#881)-(a - b)is turned round wherever it sits — inside a power, a function's argument or a matrix, not
only at the root — andExpandnow descends into a matrix. A solved system's entries are shorter for
it. (#882)NaNis a literal. It printed asNaNand parsed back as a variable of that name, so
NaN - NaNwas0. (#906)MathS.ToSympyCodeemits Python that runs and stays exact. Any non-integer rational produced a
SyntaxError,NaNand the infinities were unbound names, and1/2arrived in SymPy as the float
0.5. (#909, #911)
Two answers withdrawn on purpose
Both are recorded in the changelog rather than quietly dropped.
lim x->+oo (x^2)^x / e^(2*x*ln(x))andlim x->+oo x^x / e^(x*ln(x) - ln(x))are unevaluated
where they used to be answered. They needln(a^c) = c*ln(a), which is false in general; on the way to
+oothe base really is positive, and there is currently no way to tell the simplifier so. Unevaluated
rather thanNaN, so the caller is told nothing was settled rather than told the limit does not exist.false and 0andtrue or 0wereFalseandTrueby short-circuit. Whether an operand is admissible
cannot depend on whether the operator happened to need it.
One word reserved
NaN is now a keyword, so it can no longer be a variable name — the same trade mod took in 2.0. Only
the exact spelling: NaNx and NaN_1 are still variables.
Performance
Measured, not hoped for: v2.0.0 and this release benchmarked minutes apart on one machine, published as
a pair in
Docs/WhatsNew/version_performance_control.md.
No regression — the largest real move is SolveMedium at +4.6%, the rest sits inside ±3%, and
allocation is flat with three rows byte-identical.
Known, and not fixed here
ln(x) + ln(x+1) -> ln(x*(1+x)) is still unsound off the positive reals, and log(x, x) -> 1 provided x > 0 declares itself undefined at x = -3 where it evaluates to 1. Both wait on
#721 — whether a domain is a property a node
carries or a query with the reading passed in — because guarding either one costs coverage that an
assumption travelling with the expression would not.