Skip to content

Add the Modulus option to RowReduce, NullSpace, MatrixRank, Inverse and LinearSolve - #2010

Open
tombouctou wants to merge 2 commits into
Mathics3:masterfrom
tombouctou:fix-linalg-modulus
Open

tombouctou wants to merge 2 commits into
Mathics3:masterfrom
tombouctou:fix-linalg-modulus

Conversation

@tombouctou

@tombouctou tombouctou commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

RowReduce, NullSpace, MatrixRank, Inverse and LinearSolve did not accept the Modulus option, so for example NullSpace[{{1, 1}, {1, 1}}, Modulus -> 2] stayed unevaluated. This adds it for a prime modulus, as described in the WMA docs ("If p is zero, ordinary arithmetic is used").

What it does. With Modulus -> p the matrix entries are reduced mod p and the computation is plain Gaussian elimination on Python ints (new module mathics/eval/numbers/linalg_modular.py). Entries may be integers, or rationals whose denominator is invertible mod p. Results are integers in 0..p-1:

RowReduce[{{1, 0, 4}, {2, 0, 3}, {2, 1, 2}}, Modulus -> 5]    (* {{1, 0, 4}, {0, 1, 4}, {0, 0, 0}} *)
MatrixRank[{{1, 0, 4}, {2, 0, 3}, {2, 1, 2}}, Modulus -> 5]   (* 2 *)
NullSpace[{{1, 1, 0}, {1, 0, 1}, {0, 1, 1}}, Modulus -> 2]     (* {{1, 1, 1}} *)
Inverse[{{12, -2}, {1, 3}}, Modulus -> 5]                     (* {{1, 4}, {3, 4}} *)
LinearSolve[{{1, 1, 1}, {1, 2, 3}, {1, 4, 9}}, {1, 2, 3}, Modulus -> 47]   (* {23, 2, 23} *)

Conventions:

  • RowReduce gives the reduced row echelon form, as without Modulus.
  • NullSpace gives one vector per non-pivot column, with 1 in that column and 0 in the other non-pivot columns, in decreasing order of that column. That order is what WMA gives: running Strickland's LambdaAlgebra.m (below) reproduces his WMA-computed homology bases exactly only with this order. The exact NullSpace (without Modulus) currently lists them in increasing order; I left that alone here.
  • LinearSolve sets the free variables to 0.
  • A singular matrix and an inconsistent system give the existing Inverse::sing and LinearSolve::nosol messages.

What it does not do. A modulus that is not a prime (WMA says the computation "may fail" then), a non-integer modulus, a symbolic entry, or a denominator divisible by p leave the expression unevaluated. Modulus -> 0 keeps the current code path.

Tests. Doc examples, plus test_linalg_modulus and test_linalg_modulus_random in test/builtin/numbers/test_linalg.py; the random test checks the defining properties (rank + nullity, m . ns == 0, m . x == b, m . Inverse[m] == 1 mod p) on 60 random matrices. pytest and the doctests give the same results as on master; the only failures are environmental ones that also fail on master. mypy is clean.

Motivation and a larger check: Strickland's LambdaAlgebra.m (https://strickland1.org/toda/) computes the homology of the mod-2 lambda algebra with all five functions and Modulus -> 2, and his LambdaData.m stores the results computed in WMA. With this PR (and the Transpose and MapAt fixes in #2007 and #2008) Mathics3 reproduces exactly, in the same order, his homology, boundary and non-boundary bases and his canonical forms: 218 of the 296 tridegrees in LambdaData.m (4225 canonical forms), with no difference. The remaining 78 are too slow to finish under Mathics3 in reasonable time; the largest would take days. For comparison, under Wolfram Engine 15 the same script computes all 296 in under a minute and matches LambdaData.m exactly.

🤖 Generated with Claude Code

tombouctou and others added 2 commits October 16, 2026 12:04
…nd LinearSolve

With Modulus -> p for a prime p, these compute over the integers mod p
with plain Gaussian elimination on Python ints. Entries may be integers
or rationals whose denominator is invertible mod p. Modulus -> 0 keeps
ordinary arithmetic; a modulus that is not a prime, or a symbolic entry,
leaves the expression unevaluated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Strickland's LambdaAlgebra.m, run in WMA, shows that NullSpace with
Modulus gives the vectors in decreasing order of their free column, so
do the same.

The random tests assigned x, m and p in the shared test session, which
broke later tests that use x as a symbol; use Module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant