From 3672d91b693a4e76e82eef9913535354968ad69d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 1 Mar 2024 11:24:35 -0800 Subject: [PATCH 1/9] MatrixSpace: When row_keys or column_keys are given, use Hom(CombinatorialFreeModule) --- src/sage/matrix/matrix_space.py | 186 ++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 69 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 7f93cceb6ad..a3336a846b9 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -434,9 +434,78 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): class MatrixSpace(UniqueRepresentation, Parent): """ - The space of matrices of given size and base ring + The space of matrices of given size and base ring. - EXAMPLES: + INPUT: + + - ``base_ring`` -- a ring + + - ``nrows`` -- (nonnegative integer) the number of rows + + - ``row_keys`` -- a finite or enumerated family of arbitrary objects + that index the rows of the matrix + + - ``ncols`` -- (nonnegative integer, default ``nrows``) the number of + columns + + - ``column_keys`` -- a finite or enumerated family of arbitrary objects + that index the columns of the matrix + + - ``sparse`` -- (boolean, default ``False``) whether or not matrices + are given a sparse representation + + - ``implementation`` -- (optional, a string or a matrix class) a possible + implementation. Depending on the base ring, the string can be + + - ``'generic'`` -- on any base rings + + - ``'flint'`` -- for integers and rationals + + - ``'meataxe'`` -- finite fields using the optional package :ref:`spkg_meataxe` + + - ``'m4ri'`` -- for characteristic 2 using the :ref:`spkg_m4ri` library + + - ``'linbox-float'`` -- for integer mod rings up to `2^8 = 256` + + - ``'linbox-double'`` -- for integer mod rings up to + `floor(2^26*sqrt(2) + 1/2) = 94906266` + + - ``'numpy'`` -- for real and complex floating point numbers + + OUTPUT: a matrix space or, more generally, a homspace between free modules. + + This factory function creates instances of various specialized classes + depending on the input. Not all combinations of options are + implemented. + + - If the parameters ``row_keys`` or ``column_keys`` are provided, + they must be finite or enumerated families of objects. In this + case, instances of :class:`CombinatorialFreeModule` are created + via the factory function :func:`FreeModule`. Then the homspace + between these modules is returned. + + EXAMPLES:: + + sage: MatrixSpace(QQ, 2) + Full MatrixSpace of 2 by 2 dense matrices over Rational Field + sage: MatrixSpace(ZZ, 3, 2) + Full MatrixSpace of 3 by 2 dense matrices over Integer Ring + sage: MatrixSpace(ZZ, 3, sparse=False) + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + + sage: MatrixSpace(ZZ, 10, 5) + Full MatrixSpace of 10 by 5 dense matrices over Integer Ring + sage: MatrixSpace(ZZ, 10, 5).category() + Category of infinite enumerated finite dimensional modules with basis over + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(ZZ, 10, 10).category() + Category of infinite enumerated finite dimensional algebras with basis over + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(QQ, 10).category() + Category of infinite finite dimensional algebras with basis over + (number fields and quotient fields and metric spaces) Some examples of square 2 by 2 rational matrices:: @@ -463,8 +532,7 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: B[1,1] [0 0] [0 1] - sage: A = MS.matrix([1,2,3,4]) - sage: A + sage: A = MS.matrix([1,2,3,4]); A [1 2] [3 4] @@ -476,19 +544,27 @@ class MatrixSpace(UniqueRepresentation, Parent): [ 9 12 15] [19 26 33] + Using ``row_keys`` and ``column_keys``:: + + sage: MS = MatrixSpace(ZZ, ['u', 'v'], ['a', 'b', 'c']); MS + Set of Morphisms + from Free module generated by {'a', 'b', 'c'} over Integer Ring + to Free module generated by {'u', 'v'} over Integer Ring + in Category of finite dimensional modules with basis over Integer Ring + Check categories:: - sage: MatrixSpace(ZZ,10,5) + sage: MatrixSpace(ZZ, 10, 5) Full MatrixSpace of 10 by 5 dense matrices over Integer Ring - sage: MatrixSpace(ZZ,10,5).category() + sage: MatrixSpace(ZZ, 10, 5).category() Category of infinite enumerated finite dimensional modules with basis over (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) - sage: MatrixSpace(ZZ,10,10).category() + sage: MatrixSpace(ZZ, 10, 10).category() Category of infinite enumerated finite dimensional algebras with basis over (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) - sage: MatrixSpace(QQ,10).category() + sage: MatrixSpace(QQ, 10).category() Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) @@ -539,11 +615,14 @@ class MatrixSpace(UniqueRepresentation, Parent): """ @staticmethod - def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementation=None, **kwds): + def __classcall__(cls, base_ring, + nrows_or_row_keys=None, ncols_or_column_keys=None, + sparse=False, implementation=None, *, + nrows=None, ncols=None, + row_keys=None, column_keys=None, + **kwds): """ - Normalize the arguments to call the ``__init__`` constructor. - - See the documentation in ``__init__``. + Normalize the arguments to call the ``__init__`` constructor or delegate to another class. TESTS:: @@ -588,13 +667,35 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio """ if base_ring not in _Rings: raise TypeError("base_ring (=%s) must be a ring" % base_ring) - nrows = int(nrows) - if ncols is None: + + if ncols_or_column_keys is not None: + try: + ncols = int(ncols_or_column_keys) + except (TypeError, ValueError): + column_keys = ncols_or_column_keys + + if nrows_or_row_keys is not None: + try: + nrows = int(nrows_or_row_keys) + except (TypeError, ValueError): + row_keys = nrows_or_row_keys + + if ncols is None and column_keys is None: ncols = nrows - else: - ncols = int(ncols) + column_keys = row_keys + sparse = bool(sparse) + if row_keys is not None or column_keys is not None: + from sage.categories.homset import Hom + from sage.modules.free_module import FreeModule + + domain = FreeModule(base_ring, rank=ncols, basis_keys=column_keys, + sparse=sparse, **kwds) + codomain = FreeModule(base_ring, rank=nrows, basis_keys=row_keys, + sparse=sparse, **kwds) + return Hom(domain, codomain) + if nrows < 0: raise ArithmeticError("nrows must be nonnegative") if ncols < 0: @@ -608,59 +709,6 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio def __init__(self, base_ring, nrows, ncols, sparse, implementation): r""" - INPUT: - - - ``base_ring`` - - - ``nrows`` - (positive integer) the number of rows - - - ``ncols`` - (positive integer, default nrows) the number of - columns - - - ``sparse`` - (boolean, default false) whether or not matrices - are given a sparse representation - - - ``implementation`` -- (optional, a string or a matrix class) a possible - implementation. Depending on the base ring the string can be - - - ``'generic'`` - on any base rings - - - ``'flint'`` - for integers and rationals - - - ``'meataxe'`` - finite fields, needs to install the optional package meataxe - - - ``m4ri`` - for characteristic 2 using M4RI library - - - ``linbox-float`` - for integer mod rings up to `2^8 = 256` - - - ``linbox-double`` - for integer mod rings up to - `floor(2^26*sqrt(2) + 1/2) = 94906266` - - - ``numpy`` - for real and complex floating point numbers - - EXAMPLES:: - - sage: MatrixSpace(QQ, 2) - Full MatrixSpace of 2 by 2 dense matrices over Rational Field - sage: MatrixSpace(ZZ, 3, 2) - Full MatrixSpace of 3 by 2 dense matrices over Integer Ring - sage: MatrixSpace(ZZ, 3, sparse=False) - Full MatrixSpace of 3 by 3 dense matrices over Integer Ring - - sage: MatrixSpace(ZZ,10,5) - Full MatrixSpace of 10 by 5 dense matrices over Integer Ring - sage: MatrixSpace(ZZ,10,5).category() - Category of infinite enumerated finite dimensional modules with basis over - (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) - sage: MatrixSpace(ZZ,10,10).category() - Category of infinite enumerated finite dimensional algebras with basis over - (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) - sage: MatrixSpace(QQ,10).category() - Category of infinite finite dimensional algebras with basis over - (number fields and quotient fields and metric spaces) - TESTS: We test that in the real or complex double dense case, From b27e6e1455b2696ec055c08bfdb6c9b3e10eb711 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 1 Mar 2024 11:49:31 -0800 Subject: [PATCH 2/9] src/sage/matrix/args.pyx: Doctest cosmetics --- src/sage/matrix/args.pyx | 64 ++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 6b247595059..b39f92986f7 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -156,24 +156,29 @@ cdef class MatrixArgs: sage: ma = MatrixArgs(2, 2, (x for x in range(4))); ma > sage: ma.finalized() - + Many types of input are possible:: sage: ma = MatrixArgs(2, 2, entries=None); ma.finalized(); ma.matrix() - + [0 0] [0 0] sage: ma = MatrixArgs(2, 2, entries={}); ma.finalized(); ma.matrix() - + [0 0] [0 0] sage: ma = MatrixArgs(2, 2, entries=[1,2,3,4]); ma.finalized(); ma.matrix() - + [1 2] [3 4] sage: ma = MatrixArgs(2, 2, entries=math.pi); ma.finalized(); ma.matrix() - + [3.141592653589793 0.0] [ 0.0 3.141592653589793] sage: ma = MatrixArgs(2, 2, entries=pi); ma.finalized() # needs sage.symbolic @@ -183,15 +188,18 @@ cdef class MatrixArgs: [pi 0] [ 0 pi] sage: ma = MatrixArgs(ZZ, 2, 2, entries={(0,0):7}); ma.finalized(); ma.matrix() - + [7 0] [0 0] sage: ma = MatrixArgs(ZZ, 2, 2, entries=((1,2),(3,4))); ma.finalized(); ma.matrix() - + [1 2] [3 4] sage: ma = MatrixArgs(ZZ, 2, 2, entries=(1,2,3,4)); ma.finalized(); ma.matrix() - + [1 2] [3 4] @@ -216,16 +224,19 @@ cdef class MatrixArgs: [ 0 3/5] sage: ma = MatrixArgs(entries=matrix(2,2)); ma.finalized(); ma.matrix() - [0 0] [0 0] sage: ma = MatrixArgs(2, 2, entries=lambda i,j: 1+2*i+j); ma.finalized(); ma.matrix() - + [1 2] [3 4] sage: ma = MatrixArgs(ZZ, 2, 2, entries=lambda i,j: 1+2*i+j); ma.finalized(); ma.matrix() - > + > [1 2] [3 4] @@ -263,9 +274,11 @@ cdef class MatrixArgs: [1 0 1] [1 1 0] - sage: ma = MatrixArgs([vector([0,1], sparse=True), vector([0,0], sparse=True)], sparse=True) + sage: ma = MatrixArgs([vector([0,1], sparse=True), vector([0,0], sparse=True)], + ....: sparse=True) sage: ma.finalized(); ma.matrix() - + [0 1] [0 0] @@ -615,8 +628,8 @@ cdef class MatrixArgs: INPUT: - - ``convert`` -- if True, the matrix is guaranteed to have - the correct parent matrix space. If False, the input matrix + - ``convert`` -- if ``True``, the matrix is guaranteed to have + the correct parent matrix space. If ``False``, the input matrix may be returned even if it lies in the wrong space. .. NOTE:: @@ -633,7 +646,8 @@ cdef class MatrixArgs: :: sage: ma = MatrixArgs(M); ma.finalized() - sage: ma.matrix() [0 1 2] @@ -642,7 +656,8 @@ cdef class MatrixArgs: :: sage: ma = MatrixArgs(M, sparse=False); ma.finalized() - sage: ma.matrix() [0 1 2] @@ -651,7 +666,8 @@ cdef class MatrixArgs: :: sage: ma = MatrixArgs(RDF, M); ma.finalized() - sage: ma.matrix(convert=False) [0 1 2] @@ -810,7 +826,8 @@ cdef class MatrixArgs: sage: S = MatrixSpace(QQ, 3, 2, sparse=True) sage: _ = ma.set_space(S) sage: ma.finalized() - + sage: M = ma.matrix(); M [0 0] [0 0] @@ -848,7 +865,8 @@ cdef class MatrixArgs: ... TypeError: the dimensions of the matrix must be specified sage: MatrixArgs(2, 3, 0.0).finalized() - + sage: MatrixArgs(RR, 2, 3, 1.0).finalized() Traceback (most recent call last): ... @@ -1023,10 +1041,12 @@ cdef class MatrixArgs: sage: ma = MatrixArgs({(2,5):1/2, (4,-3):1/3}) sage: ma = MatrixArgs(2, 2, {(-1,0):2, (0,-1):1}, sparse=True) sage: ma.finalized() - + sage: ma = MatrixArgs(2, 2, {(-1,0):2, (0,-1):1}, sparse=False) sage: ma.finalized() - + sage: ma = MatrixArgs(2, 1, {(1,0):88, (0,1):89}) sage: ma.finalized() Traceback (most recent call last): From 139d37ede9034c6c83de0b141e7e9b32085b90ea Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 1 Mar 2024 14:06:47 -0800 Subject: [PATCH 3/9] src/sage/matrix/matrix_space.py: Docstring edits --- src/sage/matrix/matrix_space.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index a3336a846b9..1df9674ebd0 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -440,15 +440,12 @@ class MatrixSpace(UniqueRepresentation, Parent): - ``base_ring`` -- a ring - - ``nrows`` -- (nonnegative integer) the number of rows + - ``nrows`` or ``row_keys`` -- (nonnegative integer) the number of rows, or + a finite or enumerated family of arbitrary objects that index the rows + of the matrix - - ``row_keys`` -- a finite or enumerated family of arbitrary objects - that index the rows of the matrix - - - ``ncols`` -- (nonnegative integer, default ``nrows``) the number of - columns - - - ``column_keys`` -- a finite or enumerated family of arbitrary objects + - ``ncols`` or ``column_keys`` -- (nonnegative integer, default ``nrows``) + the number of columns, or a finite or enumerated family of arbitrary objects that index the columns of the matrix - ``sparse`` -- (boolean, default ``False``) whether or not matrices From 42fabd4bc43a24a8fd082f956a0cd4cfd8fc4443 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 1 Mar 2024 14:08:31 -0800 Subject: [PATCH 4/9] src/sage/matrix/{args,constructor}.pyx: Docstring cosmetics --- src/sage/matrix/args.pyx | 29 ++++++++++++++++++----------- src/sage/matrix/constructor.pyx | 24 ++++++++++++------------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index b39f92986f7..56e79e70e45 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -313,21 +313,27 @@ cdef class MatrixArgs: sage: from sage.matrix.args import MatrixArgs sage: MatrixArgs().finalized() - + sage: MatrixArgs(1).finalized() - + sage: MatrixArgs(1, 1, 3).finalized() - + sage: MatrixArgs(1, 1, 1, 1).finalized() Traceback (most recent call last): ... TypeError: too many arguments in matrix constructor sage: MatrixArgs(3, nrows=1, ncols=1).finalized() - + sage: MatrixArgs(3, nrows=1).finalized() - + sage: MatrixArgs(3, ncols=1).finalized() - + """ self.base = ring if nrows is not None: @@ -719,7 +725,7 @@ cdef class MatrixArgs: INPUT: - - ``convert`` -- If True, the entries are converted to the base + - ``convert`` -- If ``True``, the entries are converted to the base ring. Otherwise, the entries are returned as given. .. NOTE:: @@ -779,11 +785,12 @@ cdef class MatrixArgs: cpdef dict dict(self, bint convert=True) noexcept: """ - Return the entries of the matrix as a dict. The keys of this - dict are the non-zero positions ``(i,j)``. The corresponding - value is the entry at that position. Zero values are skipped. + Return the entries of the matrix as a :class:`dict`. - If ``convert`` is True, the entries are converted to the base + The keys of this :class:`dict` are the non-zero positions ``(i,j)``. The + corresponding value is the entry at that position. Zero values are skipped. + + If ``convert`` is ``True``, the entries are converted to the base ring. Otherwise, the entries are returned as given. EXAMPLES:: diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 665ae4fbde9..f1d6a72f133 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -37,7 +37,7 @@ def matrix(*args, **kwds): INPUT: - The matrix command takes the entries of a matrix, optionally + The :func:`matrix` command takes the entries of a matrix, optionally preceded by a ring and the dimensions of the matrix, and returns a matrix. @@ -49,11 +49,11 @@ def matrix(*args, **kwds): columns. You can create a matrix of zeros by passing an empty list or the integer zero for the entries. To construct a multiple of the identity (`cI`), you can specify square dimensions and pass in - `c`. Calling matrix() with a Sage object may return something that - makes sense. Calling matrix() with a NumPy array will convert the + `c`. Calling :func:`matrix` with a Sage object may return something that + makes sense. Calling :func:`matrix` with a NumPy array will convert the array to a matrix. - All arguments (even the positional) are optional. + All arguments (even the positional ones) are optional. Positional and keyword arguments: @@ -105,7 +105,7 @@ def matrix(*args, **kwds): :: - sage: m = matrix(QQ,[[1,2,3],[4,5,6]]); m; m.parent() + sage: m = matrix(QQ, [[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field @@ -135,8 +135,8 @@ def matrix(*args, **kwds): :: - sage: v1=vector((1,2,3)) - sage: v2=vector((4,5,6)) + sage: v1 = vector((1,2,3)) + sage: v2 = vector((4,5,6)) sage: m = matrix([v1,v2]); m; m.parent() [1 2 3] [4 5 6] @@ -144,28 +144,28 @@ def matrix(*args, **kwds): :: - sage: m = matrix(QQ,2,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ, 2, [1,2,3,4,5,6]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field :: - sage: m = matrix(QQ,2,3,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ, 2, 3, [1,2,3,4,5,6]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field :: - sage: m = matrix({(0,1): 2, (1,1):2/5}); m; m.parent() + sage: m = matrix({(0,1): 2, (1,1): 2/5}); m; m.parent() [ 0 2] [ 0 2/5] Full MatrixSpace of 2 by 2 sparse matrices over Rational Field :: - sage: m = matrix(QQ,2,3,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ, 2, 3, {(1,1): 2}); m; m.parent() [0 0 0] [0 2 0] Full MatrixSpace of 2 by 3 sparse matrices over Rational Field @@ -234,7 +234,7 @@ def matrix(*args, **kwds): :: - sage: M = Matrix([[1,2,3],[4,5,6],[7,8,9]], immutable=True) + sage: M = Matrix([[1,2,3], [4,5,6], [7,8,9]], immutable=True) sage: M[0] = [9,9,9] Traceback (most recent call last): ... From 0c1dfd0b84e58122ca0989759fe0f806e1b241db Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 7 Mar 2024 09:25:29 -0800 Subject: [PATCH 5/9] src/sage/matrix/matrix_space.py: Do not mention enumerated families as row/column keys --- src/sage/matrix/matrix_space.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 1df9674ebd0..95c099d5379 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -441,12 +441,11 @@ class MatrixSpace(UniqueRepresentation, Parent): - ``base_ring`` -- a ring - ``nrows`` or ``row_keys`` -- (nonnegative integer) the number of rows, or - a finite or enumerated family of arbitrary objects that index the rows - of the matrix + a finite family of arbitrary objects that index the rows of the matrix - ``ncols`` or ``column_keys`` -- (nonnegative integer, default ``nrows``) - the number of columns, or a finite or enumerated family of arbitrary objects - that index the columns of the matrix + the number of columns, or a finite family of arbitrary objects that index + the columns of the matrix - ``sparse`` -- (boolean, default ``False``) whether or not matrices are given a sparse representation @@ -475,11 +474,10 @@ class MatrixSpace(UniqueRepresentation, Parent): depending on the input. Not all combinations of options are implemented. - - If the parameters ``row_keys`` or ``column_keys`` are provided, - they must be finite or enumerated families of objects. In this - case, instances of :class:`CombinatorialFreeModule` are created - via the factory function :func:`FreeModule`. Then the homspace - between these modules is returned. + - If the parameters ``row_keys`` or ``column_keys`` are provided, they + must be finite families of objects. In this case, instances of + :class:`CombinatorialFreeModule` are created via the factory function + :func:`FreeModule`. Then the homspace between these modules is returned. EXAMPLES:: From f2c9efbd4b837082631eb89ce11191d22e9e00da Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Apr 2024 21:48:36 -0700 Subject: [PATCH 6/9] src/sage/matrix/matrix_space.py: Add doctests and input checking --- src/sage/matrix/matrix_space.py | 73 ++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index cc3133046e5..cef1c416385 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -607,6 +607,57 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: M1 = MatrixSpace(GF(2), 5) sage: M1(m * m) == M1(m) * M1(m) True + + Check various combinations of dimensions and row/column keys:: + + sage: MatrixSpace(QQ, ['a','b'], 4) + Set of Morphisms (Linear Transformations) + from Vector space of dimension 4 over Rational Field + to Free module generated by {'a', 'b'} over Rational Field + sage: MatrixSpace(QQ, 4, ['a','b']) + Set of Morphisms + from Free module generated by {'a', 'b'} over Rational Field + to Vector space of dimension 4 over Rational Field + in Category of finite dimensional vector spaces with basis + over (number fields and quotient fields and metric spaces) + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2) + Set of Morphisms + from Free module generated by {'x', 'y'} over Rational Field + to Vector space of dimension 2 over Rational Field + in Category of finite dimensional vector spaces with basis + over (number fields and quotient fields and metric spaces) + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=4) + Traceback (most recent call last): + ... + ValueError: inconsistent number of rows: + should be cardinality of ['a', 'b'] but got 4 + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], ncols=2) + Set of Morphisms (Linear Transformations) + from Vector space of dimension 2 over Rational Field + to Free module generated by {'a', 'b'} over Rational Field + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], ncols=4) + Traceback (most recent call last): + ... + ValueError: inconsistent number of columns: + should be cardinality of ['x', 'y'] but got 4 + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2, ncols=2) + Set of Morphisms (Linear Transformations) + from Vector space of dimension 2 over Rational Field + to Vector space of dimension 2 over Rational Field + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2, ncols=4) + Traceback (most recent call last): + ... + ValueError: inconsistent number of columns: + should be cardinality of ['x', 'y'] but got 4 + sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=4, ncols=4) + Traceback (most recent call last): + ... + ValueError: inconsistent number of columns: + should be cardinality of ['x', 'y'] but got 4 + sage: MatrixSpace(QQ, 4, ['a','b'], nrows=4, ncols=2) + Traceback (most recent call last): + ... + ValueError: duplicate values for nrows """ @staticmethod @@ -665,15 +716,33 @@ def __classcall__(cls, base_ring, if ncols_or_column_keys is not None: try: - ncols = int(ncols_or_column_keys) + n = int(ncols_or_column_keys) except (TypeError, ValueError): + if column_keys is not None: + raise ValueError("duplicate values for column_keys") column_keys = ncols_or_column_keys + else: + if ncols is not None: + raise ValueError("duplicate values for ncols") + ncols = n + if column_keys is not None and ncols is not None and ncols != len(column_keys): + raise ValueError(f"inconsistent number of columns: should be cardinality of {column_keys} " + f"but got {ncols}") if nrows_or_row_keys is not None: try: - nrows = int(nrows_or_row_keys) + n = int(nrows_or_row_keys) except (TypeError, ValueError): + if row_keys is not None: + raise ValueError("duplicate values for row_keys") row_keys = nrows_or_row_keys + else: + if nrows is not None: + raise ValueError("duplicate values for nrows") + nrows = n + if row_keys is not None and nrows is not None and nrows != len(row_keys): + raise ValueError(f"inconsistent number of rows: should be cardinality of {row_keys} " + f"but got {nrows}") if ncols is None and column_keys is None: ncols = nrows From 8bd73349f0b08ef43d47d8e9d2123864fc821a90 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 6 Apr 2024 11:06:33 -0700 Subject: [PATCH 7/9] src/sage/matrix/matrix_space.py: Add TestSuite(...).run() # known bug --- src/sage/matrix/matrix_space.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index cef1c416385..ec3ede933bc 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -610,22 +610,25 @@ class MatrixSpace(UniqueRepresentation, Parent): Check various combinations of dimensions and row/column keys:: - sage: MatrixSpace(QQ, ['a','b'], 4) + sage: M_ab_4 = MatrixSpace(QQ, ['a','b'], 4); M_ab_4 Set of Morphisms (Linear Transformations) from Vector space of dimension 4 over Rational Field to Free module generated by {'a', 'b'} over Rational Field - sage: MatrixSpace(QQ, 4, ['a','b']) + sage: TestSuite(M_ab_4).run() # known bug + sage: M_4_ab = MatrixSpace(QQ, 4, ['a','b']); M_4_ab Set of Morphisms from Free module generated by {'a', 'b'} over Rational Field to Vector space of dimension 4 over Rational Field in Category of finite dimensional vector spaces with basis over (number fields and quotient fields and metric spaces) - sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2) + sage: TestSuite(M_4_ab).run() # known bug + sage: M_ab_xy = MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2); M_ab_xy Set of Morphisms from Free module generated by {'x', 'y'} over Rational Field to Vector space of dimension 2 over Rational Field in Category of finite dimensional vector spaces with basis over (number fields and quotient fields and metric spaces) + sage: TestSuite(M_ab_xy).run() # known bug sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=4) Traceback (most recent call last): ... From bc20ab28bc425374e751d78e4baba3e3ec3da7e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 7 Apr 2024 20:08:40 -0700 Subject: [PATCH 8/9] FreeModule: Fix handling of overspecified rank and basis_keys --- src/sage/modules/free_module.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 6e2c9e4dc1d..0cb8a0be8fd 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -514,12 +514,32 @@ def FreeModule(base_ring, rank_or_basis_keys=None, sparse=False, inner_product_m Traceback (most recent call last): ... NotImplementedError: FiniteRankFreeModule only supports integer ranges as basis_keys, got [1, 3, 5] + + sage: FreeModule(QQ, ['a', 'b'], rank=2) + Free module generated by {'a', 'b'} over Rational Field + sage: FreeModule(QQ, 2, basis_keys=['a', 'b']) + Free module generated by {'a', 'b'} over Rational Field + sage: FreeModule(QQ, ['a', 'b'], rank=3) + Traceback (most recent call last): + ... + ValueError: inconsistent rank: should be cardinality of ['a', 'b'] but got 3 """ if rank_or_basis_keys is not None: try: - rank = sage.rings.integer_ring.ZZ(rank_or_basis_keys) + n = sage.rings.integer_ring.ZZ(rank_or_basis_keys) except (TypeError, ValueError): + if basis_keys is not None: + raise ValueError("duplicate values for basis_keys") basis_keys = rank_or_basis_keys + else: + if rank is not None: + raise ValueError("duplicate values for rank") + rank = n + + if rank is not None and basis_keys is not None and rank != len(basis_keys): + raise ValueError(f"inconsistent rank: should be cardinality of {basis_keys} " + f"but got {rank}") + if not with_basis: if inner_product_matrix is not None: raise NotImplementedError @@ -539,7 +559,7 @@ def FreeModule(base_ring, rank_or_basis_keys=None, sparse=False, inner_product_m return FiniteRankFreeModule(base_ring, rank, start_index=start_index, **args) return FiniteRankFreeModule(base_ring, rank, **args) elif with_basis == 'standard': - if rank is not None: + if basis_keys is None: return FreeModuleFactory_with_standard_basis(base_ring, rank, sparse, inner_product_matrix, **args) else: From 36a56d30756450110acd22ff5ea171791b70e3b4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 7 Apr 2024 20:12:59 -0700 Subject: [PATCH 9/9] MatrixSpace: Update doctests for overspecified rows/cols --- src/sage/matrix/matrix_space.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index ec3ede933bc..f7efb5432c8 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -625,9 +625,8 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: M_ab_xy = MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2); M_ab_xy Set of Morphisms from Free module generated by {'x', 'y'} over Rational Field - to Vector space of dimension 2 over Rational Field - in Category of finite dimensional vector spaces with basis - over (number fields and quotient fields and metric spaces) + to Free module generated by {'a', 'b'} over Rational Field + in Category of finite dimensional vector spaces with basis over Rational Field sage: TestSuite(M_ab_xy).run() # known bug sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=4) Traceback (most recent call last): @@ -635,18 +634,20 @@ class MatrixSpace(UniqueRepresentation, Parent): ValueError: inconsistent number of rows: should be cardinality of ['a', 'b'] but got 4 sage: MatrixSpace(QQ, ['a','b'], ['x','y'], ncols=2) - Set of Morphisms (Linear Transformations) - from Vector space of dimension 2 over Rational Field + Set of Morphisms + from Free module generated by {'x', 'y'} over Rational Field to Free module generated by {'a', 'b'} over Rational Field + in Category of finite dimensional vector spaces with basis over Rational Field sage: MatrixSpace(QQ, ['a','b'], ['x','y'], ncols=4) Traceback (most recent call last): ... ValueError: inconsistent number of columns: should be cardinality of ['x', 'y'] but got 4 sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2, ncols=2) - Set of Morphisms (Linear Transformations) - from Vector space of dimension 2 over Rational Field - to Vector space of dimension 2 over Rational Field + Set of Morphisms + from Free module generated by {'x', 'y'} over Rational Field + to Free module generated by {'a', 'b'} over Rational Field + in Category of finite dimensional vector spaces with basis over Rational Field sage: MatrixSpace(QQ, ['a','b'], ['x','y'], nrows=2, ncols=4) Traceback (most recent call last): ...