Skip to content

The determinant is computed by fraction-free elimination where it can be (#999) - #1129

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
a-fraction-free-determinant
Aug 31, 2026
Merged

The determinant is computed by fraction-free elimination where it can be (#999)#1129
Rafael-SOWNet merged 1 commit into
masterfrom
a-fraction-free-determinant

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Closes #999.

The ceiling, removed

Both arms built from source on one machine:

before now
numeric 8×8 382 ms under 1 ms
numeric 10×10 14 415 ms 2 ms
numeric 11×11 did not return in four minutes 2 ms
numeric 12×12 did not return 3 ms
numeric 20×20 did not return 11 ms
numeric 30×30 did not return 22 ms

Bareiss' fraction-free elimination runs wherever the entries are polynomials over the rationals; Laplace answers everything else. The switch is decided by the entries, not by n — as the issue asks, since a 12×12 with two variables and a 4×4 with sixteen are different problems. It is settled per matrix, by trying.

The elimination was already in the repository

PolynomialResultant.SylvesterDeterminant is a Bareiss elimination over MultivariatePolynomial, written for the Sylvester matrix and already carrying exact division, row swaps, sign tracking, a work budget and a cancellation check. It is split out into FractionFreeDeterminant and shared rather than written twice.

That is worth stating rather than doing quietly: a sign convention or an off-by-one in an elimination is a wrong answer that looks entirely plausible, and one implementation exercised by two callers is tested by both.

The condition question, which is the one the issue says to check rather than argue

exactness is a fact about the ring, not something Entity division notices: (a * b) / a stays written as a quotient unless something cancels it

Right — and the answer is that this arithmetic never touches Entity division. It happens in MultivariatePolynomial, which has no quotients to leave behind at all, and DivideExact returns null rather than a remainder. So a division that does not come out stops the elimination and sends the caller to Laplace. The answer is a polynomial in the entries, or it is Laplace's; there is no third outcome in which a quotient survives to exclude a point.

That is the property #992 was about, and it is why Determinant used Laplace in the first place: an ordinary Gaussian elimination leaves its pivots as literal divisions, so its answer is undefined wherever a pivot vanishes — at points where the determinant is perfectly well defined.

What it declines

Each is a refusal to try, not a wrong answer, and Laplace answers all of them exactly as before:

  • an entry that is not a polynomial over the rationals — sin(x), 1 / x, 2 ^ x
  • more than eight indeterminates, which is what the packed monomial holds
  • a matrix mentioning e or pi: a constant is a value, not an indeterminate, and this ring cannot hold one

Measured

The two algorithms agree on 300 generated matrices where both apply, with no disagreements — compared as a difference that simplifies to zero rather than as trees, since they group the same polynomial differently and that is not a disagreement. The test also asserts it read more than a third of what it generated, so a change that made the elimination decline everything fails here rather than passing vacuously.

The printed form of a symbolic determinant changes, because an elimination produces an expanded polynomial where Laplace produces a nested one. Recorded in BREAKING-CHANGES.md with both arms measured. The expression is no larger in any case measured:

before now
[[a, b], [c, d]] a * d + -b * c a * d - b * c
[[x, 1, 0], [1, x, 1], [0, 1, x]] x * (x ^ 2 + -1) + -x x ^ 3 - 2 * x
[[x, 1], [1, x]], [[1/2, 1/3], [1/4, 1/5]] unchanged

Failed: 0, Passed: 9154, Skipped: 14 locally on net10.0.

One thing found on the way

Collecting the matrix's variables by name and rebuilding them with MathS.Var throws on names a caller is entitled to have built — x2 parses back as x ^ 2, which is the implicit-power rule the grammar has for a reason. The Variable objects are kept instead.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

`Matrix.Determinant` expanded by Laplace, which is O(n!). For a fully symbolic matrix that is
optimal -- the determinant genuinely has n! terms and no algorithm returns it smaller in expanded
form. For a numeric one it is pure waste: the answer is a single number and O(n^3) work suffices.

Bareiss' fraction-free elimination now runs wherever the entries are polynomials over the rationals,
and Laplace answers everything else. What decides it is not the size but whether the entries can be
read, settled per matrix by trying -- a 12x12 with two variables and a 4x4 with sixteen are different
problems and n alone does not tell them apart.

Numeric, both arms built from source on one machine: 8x8 from 382 ms to under 1 ms, 10x10 from
14 415 ms to 2 ms, and 11x11, 12x12, 20x20 and 30x30 from not returning at all to 2, 3, 11 and 22 ms.

**The elimination was already here.** `PolynomialResultant.SylvesterDeterminant` is a Bareiss
elimination over `MultivariatePolynomial`, written for the Sylvester matrix and carrying everything
this needs: exact division, row swaps, sign tracking, a work budget and a cancellation check. It is
split into `FractionFreeDeterminant` and shared rather than written twice, which is worth saying out
loud: a sign convention or an off-by-one in an elimination is a wrong answer that looks entirely
plausible, and one implementation exercised by two callers is tested by both.

**No condition is introduced.** The issue is right that this is the thing to check rather than argue:
exactness is a fact about the ring, and `Entity` division would not notice it -- `(a * b) / a` stays
written as a quotient. The arithmetic here never touches `Entity` division. It happens in
`MultivariatePolynomial`, which has no quotients to leave behind, and `DivideExact` returns null
rather than a remainder, so a division that does not come out stops the elimination and sends the
caller to Laplace. The answer is a polynomial in the entries, or it is Laplace's.

Declined, and answered by Laplace exactly as before: an entry that is not a polynomial over the
rationals, a matrix in more than eight indeterminates, and a matrix mentioning `e` or `pi`, since a
constant is a value rather than an indeterminate and this ring cannot hold one.

The printed form of a symbolic determinant changes, because an elimination produces an expanded
polynomial where Laplace produces a nested one. Recorded in BREAKING-CHANGES.md with the shapes
measured on both arms; the expression is no larger in any case measured, and `x * (x ^ 2 + -1) + -x`
becomes `x ^ 3 - 2 * x`.

The two algorithms are compared on 300 generated matrices where both apply, as a difference
simplified to zero rather than as trees -- they group the same polynomial differently and that is not
a disagreement. No disagreements. The comparison also asserts that it read more than a third of what
it generated, so a change making the elimination decline everything fails rather than passes
vacuously.

Part of #999.
@Rafael-SOWNet
Rafael-SOWNet merged commit 5351d0c into master Aug 31, 2026
31 checks passed
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.

A fraction-free elimination (Bareiss) would raise the determinant's practical size ceiling from 10x10

1 participant