fix(num): Num.equal/compare report distinct close roots as equal #23

Closed
opened 2026-07-02 14:03:37 +00:00 by toph · 1 comment
Owner

Num.equal/Num.compare decide two distinct algebraic numbers as equal when they lie close together. Repro: among the four real roots of x⁴ − 5x² + ε for ε = 5e-8 are x ≈ +10⁻⁴ and y ≈ −10⁻⁴; Num.equal x y returns true and Num.compare x y returns 0 (must be false / +1). Exact sign determination is the one guarantee the real-algebraic kernel (ADR 0012) exists to provide — everything downstream (incidence checks, solution selection, dedup) leans on it.

Likely cause: comparison decided by an interval/precision cutoff without an exact fallback. Equality should be decided exactly (sign of the difference via the minimal-polynomial machinery), with intervals only as a fast path refined until separation or an exact decision.

Related: Num.real_roots hits a wall as root separation shrinks — 0.07s → 0.39s → 3.47s → timeout (>150s) for ε = 5e-12 … 5e-20 on the same quartic. Root isolation likely needs Descartes/VCA instead of plain bisection.

Stages:

  • Check in the failing repro as a test (close-roots quartic)
  • Exact equality/compare with interval fast path
  • Profile and fix the real_roots isolation wall
`Num.equal`/`Num.compare` decide two *distinct* algebraic numbers as equal when they lie close together. Repro: among the four real roots of x⁴ − 5x² + ε for ε = 5e-8 are x ≈ +10⁻⁴ and y ≈ −10⁻⁴; `Num.equal x y` returns `true` and `Num.compare x y` returns `0` (must be `false` / `+1`). Exact sign determination is the one guarantee the real-algebraic kernel (ADR 0012) exists to provide — everything downstream (incidence checks, solution selection, dedup) leans on it. Likely cause: comparison decided by an interval/precision cutoff without an exact fallback. Equality should be decided exactly (sign of the difference via the minimal-polynomial machinery), with intervals only as a fast path refined until separation or an exact decision. Related: `Num.real_roots` hits a wall as root separation shrinks — 0.07s → 0.39s → 3.47s → timeout (>150s) for ε = 5e-12 … 5e-20 on the same quartic. Root isolation likely needs Descartes/VCA instead of plain bisection. ### Stages: - [ ] Check in the failing repro as a test (close-roots quartic) - [ ] Exact equality/compare with interval fast path - [ ] Profile and fix the `real_roots` isolation wall
toph closed this issue 2026-07-02 22:26:04 +00:00
Author
Owner

All three stages landed on main (325bc90..1ae7cac): select_root decides zero via isolating intervals now (the close-roots repro is checked in as a regression test), the rational-root search replaces trial division with a continued-fraction search below the 1/an^2 separation bound, and isolate_roots uses Descartes/VCA instead of Sturm bisection. The e-ladder (5e-8 … 5e-20) runs at <1s per rung, was >150s at the last rung — the final second turned out to be real_roots re-sorting already-ascending roots with exact compare, also removed.

All three stages landed on main (`325bc90..1ae7cac`): `select_root` decides zero via isolating intervals now (the close-roots repro is checked in as a regression test), the rational-root search replaces trial division with a continued-fraction search below the 1/an^2 separation bound, and `isolate_roots` uses Descartes/VCA instead of Sturm bisection. The e-ladder (5e-8 … 5e-20) runs at <1s per rung, was >150s at the last rung — the final second turned out to be `real_roots` re-sorting already-ascending roots with exact compare, also removed.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
toph/beloch#23
No description provided.