From a129824bea1d789ea7518f24b74e2f913e127019 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 21 Jun 2025 12:28:28 +0700 Subject: [PATCH 1/3] Check that vectors are not passed to matrix.block --- src/sage/matrix/special.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index f1de08e87d5..0c45efc7a5b 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -1940,6 +1940,22 @@ def block_matrix(*args, **kwds): [-----+-----] [ 1 0| 3 5] [ 0 1| 8 13] + + Ensure errors are raised if vectors are passed in:: + + sage: matrix.block([ + ....: [matrix.zero(2), vector([0]*2)], + ....: ]) + Traceback (most recent call last): + ... + ValueError: block_matrix only accept matrices, not vectors + sage: matrix.block([ + ....: [matrix.zero(2), vector([0]*2)], + ....: [0, matrix.zero(2)], + ....: ]) + Traceback (most recent call last): + ... + ValueError: block_matrix only accept matrices, not vectors """ args = list(args) sparse = kwds.get('sparse', None) @@ -2050,6 +2066,12 @@ def block_matrix(*args, **kwds): # At this point sub_matrices is a list of lists + from sage.structure.element import Vector + for row in sub_matrices: + for M in row: + if isinstance(M, Vector): + raise ValueError("block_matrix only accept matrices, not vectors") + # determine the base ring and sparsity if ring is None: ring = ZZ From fb82da9a17b1ab48806ff21fed73ed6a18498d65 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 27 Jun 2025 19:22:50 +0700 Subject: [PATCH 2/3] Interpret vectors as column vectors --- src/sage/matrix/special.py | 49 +++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 0c45efc7a5b..375cb0c3234 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -1789,13 +1789,13 @@ def block_matrix(*args, **kwds): INPUT: - The block_matrix command takes a list of submatrices to add + The :func:`block_matrix` function takes a list of submatrices to add as blocks, optionally preceded by a ring and the number of block rows and block columns, and returns a matrix. The submatrices can be specified as a list of matrices (using ``nrows`` and ``ncols`` to determine their layout), or a list - of lists of matrices, where each list forms a row. + of lists of matrices/column vectors, where each list forms a row. - ``ring`` -- the base ring @@ -1928,6 +1928,31 @@ def block_matrix(*args, **kwds): ... ValueError: must specify either nrows or ncols + Vectors are interpreted as column vectors:: + + sage: m = matrix([[1, 2], [3, 4]]) + sage: v = vector([5, 6]) + sage: matrix.block([ + ....: [m, v], + ....: [0, 1], + ....: ]) + [1 2|5] + [3 4|6] + [---+-] + [0 0|1] + + To interpret vectors as row vectors, :meth:`~sage.modules.free_module_element.FreeModuleElement.row` + can be used:: + + sage: matrix.block([ + ....: [m, 0], + ....: [v.row(), 1], + ....: ]) + [1 2|0] + [3 4|0] + [---+-] + [5 6|1] + TESTS:: sage: A = matrix(ZZ, 2, 2, [3,5,8,13]) @@ -1941,21 +1966,16 @@ def block_matrix(*args, **kwds): [ 1 0| 3 5] [ 0 1| 8 13] - Ensure errors are raised if vectors are passed in:: + This is not implemented for now, but it might be implemented in the future + if there is no ambiguity:: sage: matrix.block([ - ....: [matrix.zero(2), vector([0]*2)], - ....: ]) - Traceback (most recent call last): - ... - ValueError: block_matrix only accept matrices, not vectors - sage: matrix.block([ - ....: [matrix.zero(2), vector([0]*2)], - ....: [0, matrix.zero(2)], + ....: [m, 0], + ....: [v, 1], ....: ]) Traceback (most recent call last): ... - ValueError: block_matrix only accept matrices, not vectors + ValueError: incompatible submatrix widths """ args = list(args) sparse = kwds.get('sparse', None) @@ -2067,10 +2087,7 @@ def block_matrix(*args, **kwds): # At this point sub_matrices is a list of lists from sage.structure.element import Vector - for row in sub_matrices: - for M in row: - if isinstance(M, Vector): - raise ValueError("block_matrix only accept matrices, not vectors") + sub_matrices = [[M.column() if isinstance(M, Vector) else M for M in row] for row in sub_matrices] # determine the base ring and sparsity if ring is None: From 0b29b37a808e8a403252c2ab68e7697b8499dec0 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 2 Jul 2025 18:55:12 +0700 Subject: [PATCH 3/3] Better error checking --- src/sage/matrix/special.py | 54 +++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 375cb0c3234..1d2302579fe 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -1965,6 +1965,12 @@ def block_matrix(*args, **kwds): [-----+-----] [ 1 0| 3 5] [ 0 1| 8 13] + sage: block_matrix([[A, 3r+1jr], [1r, A]]) + [ 3.0 5.0|3.0 + 1.0*I 0.0] + [ 8.0 13.0| 0.0 3.0 + 1.0*I] + [-----------------------+-----------------------] + [ 1.0 0.0| 3.0 5.0] + [ 0.0 1.0| 8.0 13.0] This is not implemented for now, but it might be implemented in the future if there is no ambiguity:: @@ -1976,6 +1982,38 @@ def block_matrix(*args, **kwds): Traceback (most recent call last): ... ValueError: incompatible submatrix widths + + Error reporting when non-ring elements are passed in:: + + sage: matrix.block([ + ....: ["abc"], + ....: ]) + Traceback (most recent call last): + ... + ValueError: an element of parent was passed in, + but only matrices, vectors and ring elements are accepted + sage: matrix.block([ + ....: [(1, 2)], + ....: ]) + Traceback (most recent call last): + ... + ValueError: an element of parent was passed in, + but only matrices, vectors and ring elements are accepted + sage: matrix.block([ + ....: [[1, 2]], + ....: ]) + Traceback (most recent call last): + ... + ValueError: an element of parent was passed in, + but only matrices, vectors and ring elements are accepted + sage: matrix.block([ + ....: [EllipticCurve('37a1').0], + ....: ]) + Traceback (most recent call last): + ... + ValueError: an element of parent Abelian group of points on + Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field was passed in, + but only matrices, vectors and ring elements are accepted """ args = list(args) sparse = kwds.get('sparse', None) @@ -2087,16 +2125,18 @@ def block_matrix(*args, **kwds): # At this point sub_matrices is a list of lists from sage.structure.element import Vector - sub_matrices = [[M.column() if isinstance(M, Vector) else M for M in row] for row in sub_matrices] + from sage.structure.coerce import py_scalar_to_element + sub_matrices = [[M.column() if isinstance(M, Vector) else M if isinstance(M, Matrix) else py_scalar_to_element(M) + for M in row] for row in sub_matrices] # determine the base ring and sparsity if ring is None: - ring = ZZ - for row in sub_matrices: - for M in row: - R = M.base_ring() if isinstance(M, Matrix) else parent(M) - if R is not ZZ: - ring = sage.categories.pushout.pushout(ring, R) + from sage.structure.element import get_coercion_model + parents = [M.base_ring() if isinstance(M, Matrix) else parent(M) for row in sub_matrices for M in row] + for p in parents: + if p not in Rings(): + raise ValueError(f"an element of parent {p} was passed in, but only matrices, vectors and ring elements are accepted") + ring = get_coercion_model().common_parent(*parents) if parents else ZZ if sparse is None: sparse = True