Skip to content

2.1.0

Choose a tag to compare

@Rafael-SOWNet Rafael-SOWNet released this 12 Aug 19:51
· 876 commits to master since this release
b8cf4dc

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 is pi - 3. Four rules had been wrong since 2020. (#884)
  • arctan(x) + arccotan(x) is no longer always pi/2 — this library's arccotan has range
    (-pi/2, pi/2], so the sum is -pi/2 for negative x. (#887)
  • ln(e^x) no longer simplifies to x, which is wrong wherever Im x leaves (-pi, pi]: at
    x = 3*pi*i the expression is pi*i. (#902)
  • log(1, 1) was 0 and is NaN — it is 0/0. log(1, 2) was +oo. (#890)
  • abs(sgn(x)) and sgn(abs(x)) were 1, and both are 0 at x = 0. (#892)
  • A logical connective is no longer strict in NaN. False and u is False for an operand with no
    truth value, which is what Simplify already answered while evaluation said NaN. (#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

  • abs folds where the sign of its argument is known. abs(-sqrt(6)) is sqrt(6); a concrete
    quadratic inequality no longer answers with abs(-sqrt(6)) / 2 as 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 — and Expand now descends into a matrix. A solved system's entries are shorter for
    it. (#882)
  • NaN is a literal. It printed as NaN and parsed back as a variable of that name, so
    NaN - NaN was 0. (#906)
  • MathS.ToSympyCode emits Python that runs and stays exact. Any non-integer rational produced a
    SyntaxError, NaN and the infinities were unbound names, and 1/2 arrived 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)) and lim x->+oo x^x / e^(x*ln(x) - ln(x)) are unevaluated
    where they used to be answered. They need ln(a^c) = c*ln(a), which is false in general; on the way to
    +oo the base really is positive, and there is currently no way to tell the simplifier so. Unevaluated
    rather than NaN, so the caller is told nothing was settled rather than told the limit does not exist.
  • false and 0 and true or 0 were False and True by 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.