Skip to content

Inverse, RowReduce, MatrixPower, ...: keep a one-column matrix a matrix - #2041

Merged
rocky merged 3 commits into
Mathics3:masterfrom
tombouctou:audit-matrixshape
Oct 18, 2026
Merged

rocky merged 3 commits into
Mathics3:masterfrom
tombouctou:audit-matrixshape

Conversation

@tombouctou

Copy link
Copy Markdown
Collaborator

from_sympy turns any SymPy matrix with one column into a vector. That is
right for a solution of a linear system, but Inverse, RowReduce,
MatrixPower, MatrixExp, PseudoInverse and QRDecomposition return matrices,
and lost a level whenever the result had one column:

Inverse[{{2}}]           gave {1/2}        instead of {{1/2}}
RowReduce[{{1}, {2}}]    gave {1, 0}       instead of {{1}, {0}}
MatrixPower[{{2}}, 3]    gave {8}          instead of {{8}}
PseudoInverse[{{1, 2}}]  gave {1/5, 2/5}   instead of {{1/5}, {2/5}}

These now convert their result row by row. LinearSolve and LeastSquares
still return vectors.

To reproduce on master:

Inverse[{{2}}]          (* {1/2} *)
RowReduce[{{1}, {2}}]   (* {1, 0} *)

Tests: new cases in test/builtin/numbers/test_linalg.py (test_linalg). They fail on current master and pass with this change.

The full pytest suite and the doctests pass with this change (run together with the other fixes from the same audit; the only pytest failures, in test_main.py and test_returncode.py, are the same as on master and come from the mathics script not being on PATH in our environment).

Found in a correctness audit of Mathics3 (differential checks against SymPy/mpmath/exact computation, and calls of every builtin with edge-case arguments).

🤖 Generated with Claude Code

from_sympy turns any SymPy matrix with one column into a vector. That is
right for a solution of a linear system, but Inverse, RowReduce,
MatrixPower, MatrixExp, PseudoInverse and QRDecomposition return matrices,
and lost a level whenever the result had one column:

    Inverse[{{2}}]           gave {1/2}        instead of {{1/2}}
    RowReduce[{{1}, {2}}]    gave {1, 0}       instead of {{1}, {0}}
    MatrixPower[{{2}}, 3]    gave {8}          instead of {{8}}
    PseudoInverse[{{1, 2}}]  gave {1/5, 2/5}   instead of {{1/5}, {2/5}}

These now convert their result row by row. LinearSolve and LeastSquares
still return vectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread mathics/builtin/numbers/linalg.py Outdated
)


def from_sympy_matrix_as_matrix(matrix):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not belong here, but instead belongs under mathics.core.convert

Comment thread test/builtin/numbers/test_linalg.py Outdated
None, # "Sympy issue #1156",
),
# A matrix with one column stays a matrix.
("Inverse[{{2}}]", None, "{{1 / 2}}", None),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please enhance the final "None", parameter.

tombouctou and others added 2 commits October 18, 2026 20:04
Address review: the shape-keeping SymPy-matrix conversion belongs with
the other SymPy conversions, next to from_sympy_matrix, not in
mathics.builtin.numbers.linalg. Give each new one-column test case a
specific failure message instead of None.

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

rocky commented Oct 18, 2026

Copy link
Copy Markdown
Member

LGTM

@rocky
rocky merged commit 59671dc into Mathics3:master Oct 18, 2026
15 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.

2 participants