Skip to content

Stop Expand losing a term that takes no power - #852

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
fix/set-plus-number-nan
Aug 9, 2026
Merged

Stop Expand losing a term that takes no power#852
Rafael-SOWNet merged 1 commit into
masterfrom
fix/set-plus-number-nan

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Closes #851.

The bug

Expand turned RR + 1 into NaN — and with it ZZ + 1, CC + 1, QQ + 1, BB + 1 and true + 1. Because Simplify offers res.Expand() as a candidate and NaN rates as the simplest thing on offer, Simplify returned NaN too.

A 2.0 regression, measured against the published packages:

1.4.0 2.0.0-preview.1
"RR + 1".Simplify() RR + 1 NaN
"ZZ + 1".Simplify() ZZ + 1 NaN

Expand is public, so this is not only a Simplify artefact — "RR + 1".Expand() returned NaN directly.

The cause

CollectLikeTerms is what makes expansion finish: it reduces every factor to a base and an exponent, adds up the terms whose monomials agree, and puts them back together. The reassembly wrote a factor that appeared once as base^1.

Raising to the first power is an identity only where the power is defined at all. It is not, for these:

RR^1 -> NaN     ZZ^1 -> NaN     true^1 -> NaN
{1,2}^1 -> {1,2}   [0;1]^1 -> [0;1]   x^1 -> x   [[1,2],[3,4]]^1 -> [[1,2],[3,4]]

So the decomposition was lossless for everything that takes a power and destroyed everything that does not. That also explains why the sets that can be shifted were never affected: a finite set and an interval both take a power, so they round-tripped.

The fix

A factor that appeared once goes back as itself rather than as base^1.

A second guard covers the same fault above the first power, where the identity does not apply and the factor cannot be written back at all (RR * RR): collecting is an improvement rather than a requirement, so a collection that introduced NaN is discarded and the expanded form kept. That one is deliberately written against the symptom rather than a list of node types, so a node added later cannot reintroduce it.

Measured

Fixed, and the shiftable sets still shift:

RR + 1      Expand: RR + 1      Simplify: RR + 1
true + 1    Expand: True + 1    Simplify: True + 1
{ 1, 2 } + 1                    Simplify: { 2, 3 }
[0; 1] + 1                      Simplify: [1; 2]

Suite: 6061 passed, 0 failed, 14 skipped (11 new cases).

Verified downstream, which is where it came from

Found by running CSharpMath's suite against 2.0.0-preview.1 — the consumer #822 is about. It is 955 green on 1.4.0 and two red on the preview. Rebuilt against this branch:

before: Total: 955, Failed: 2
after:  Total: 955, Failed: 1

The one that remains is 1+\sqrt{2x} expanding to 1+\sqrt{2}\sqrt{x} — the documented 2.0 radical change, sound and theirs to absorb, not a defect here.

Noted, not fixed here

(x+1)^2 * (x+1)^2 expands to ... + 2*x^2 + 4*x^2 + ... with those two left uncollected, which is what CollectLikeTerms exists to prevent. Pre-existing — reproduced on master without this change, so it is not a regression from it and wants its own issue rather than being folded in.

🤖 Generated with Claude Code

Expand turned RR + 1 into NaN, and with it ZZ + 1, CC + 1, QQ + 1, BB + 1
and true + 1. A 2.0 regression: 1.4.0 leaves all of them alone.

CollectLikeTerms, which is what makes expansion finish, reduces every factor
to a base and an exponent and then puts the term back together. A factor that
appeared once came back as base^1 -- and raising to the first power is an
identity only where the power is defined at all. RR^1, ZZ^1 and true^1 are
NaN, so the reassembly turned an expression that had a value into one that
did not. A factor that appeared once now goes back as itself.

Sets that can be shifted are unaffected and still are: { 1, 2 } + 1 is
{ 2, 3 } and [0; 1] + 1 is [1; 2]. They survived before because a finite set
and an interval both do take a power.

A second guard covers the same fault at any exponent above the first, where
the identity does not apply and the factor cannot be written back at all:
collecting is an improvement rather than a requirement, so a collection that
introduced NaN is discarded and the expanded form kept.

Found by running CSharpMath's suite against 2.0.0-preview.1 -- 955 green on
1.4.0, two red on the preview. This closes one of the two; the other is the
documented radical change and is theirs to absorb.

Closes #851.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 5a55cb5 into master Aug 9, 2026
25 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the fix/set-plus-number-nan branch August 9, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Simplify turns a special set plus anything into NaN — a 2.0 regression found downstream

1 participant