diff --git a/src/doc/en/reference/matrices/index.rst b/src/doc/en/reference/matrices/index.rst index 5029f4eec2b..6dc2a83a821 100644 --- a/src/doc/en/reference/matrices/index.rst +++ b/src/doc/en/reference/matrices/index.rst @@ -47,23 +47,16 @@ objects like operation tables (e.g. the multiplication table of a group). sage/matrix/matrix_space - sage/matrix/constructor - - sage/matrix/docs - - sage/matrix/matrix_misc - sage/matrix/special + sage/matrix/args + sage/matrix/docs sage/matrix/matrix0 - sage/matrix/matrix1 - sage/matrix/matrix2 sage/matrix/strassen - sage/matrix/berlekamp_massey sage/matrix/matrix_dense @@ -72,38 +65,32 @@ objects like operation tables (e.g. the multiplication table of a group). sage/matrix/matrix_generic_dense sage/matrix/matrix_generic_sparse - sage/matrix/matrix_modn_sparse - - sage/matrix/matrix_symbolic_dense - sage/matrix/matrix_integer_dense - + sage/matrix/matrix_integer_sparse + sage/matrix/matrix_integer_dense_hnf + sage/matrix/matrix_integer_dense_saturation sage/matrix/matrix_rational_dense - + sage/matrix/matrix_rational_sparse sage/matrix/matrix_double_dense - sage/matrix/matrix_real_double_dense - + sage/matrix/matrix_mod2_dense + sage/matrix/matrix_gf2e_dense + sage/matrix/matrix_modn_dense_double + sage/matrix/matrix_modn_dense_float + sage/matrix/matrix_modn_sparse + sage/matrix/matrix_symbolic_dense sage/matrix/matrix_complex_double_dense sage/matrix/matrix_complex_ball_dense - sage/matrix/matrix_polynomial_dense sage/matrix/matrix_mpolynomial_dense + sage/matrix/matrix_cyclo_dense sage/matrix/operation_table sage/matrix/action sage/matrix/change_ring sage/matrix/echelon_matrix - sage/matrix/matrix_cyclo_dense - sage/matrix/matrix_integer_dense_hnf - sage/matrix/matrix_integer_dense_saturation - sage/matrix/matrix_integer_sparse - sage/matrix/matrix_mod2_dense - sage/matrix/matrix_gf2e_dense - sage/matrix/matrix_modn_dense_double - sage/matrix/matrix_modn_dense_float - sage/matrix/matrix_rational_sparse + sage/matrix/matrix_misc sage/matrix/matrix_window sage/matrix/misc sage/matrix/symplectic_basis diff --git a/src/module_list.py b/src/module_list.py index c661ee06212..1af81db6c87 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -723,6 +723,9 @@ def uname_specific(name, value, alternative): Extension('sage.matrix.action', sources = ['sage/matrix/action.pyx']), + Extension('sage.matrix.args', + sources = ['sage/matrix/args.pyx']), + Extension('sage.matrix.echelon_matrix', sources = ['sage/matrix/echelon_matrix.pyx']), diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index bd1f9108898..1ee00582609 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -249,7 +249,7 @@ def _element_constructor_(self, x, **args): sage: W(s1*s2) Traceback (most recent call last): ... - ValueError: ... + ValueError: inconsistent number of rows: should be 1 but got 3 """ P = parent(x) if P in CoxeterGroups(): diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 1e9a447f1af..41292e7d5d2 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -1823,7 +1823,7 @@ def straighten_input(t, k): sage: WeakTableau_factorized_permutation.straighten_input([W.an_element(),W.an_element()], 3) Traceback (most recent call last): ... - ValueError: a matrix from Full MatrixSpace of 5 by 5 dense matrices over Rational Field cannot be converted to a matrix in Full MatrixSpace of 4 by 4 dense matrices over Rational Field! + ValueError: inconsistent number of rows: should be 4 but got 5 """ W = WeylGroup(['A', k, 1], prefix='s') if len(t) > 0: diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx index 2876d43cbdd..acff37f7358 100644 --- a/src/sage/groups/matrix_gps/group_element.pyx +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -310,6 +310,21 @@ cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): """ return self._matrix + def _matrix_(self, base=None): + """ + Method used by the :func:`matrix` constructor. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A', 3], base_ring=ZZ) + sage: g = W.gen(0) + sage: matrix(RDF, g) + [-1.0 1.0 0.0] + [ 0.0 1.0 0.0] + [ 0.0 0.0 1.0] + """ + return self.matrix() + cpdef _mul_(self, other): """ Return the product of ``self`` and`` other``, which must @@ -590,6 +605,22 @@ cdef class MatrixGroupElement_gap(ElementLibGAP): m.set_immutable() return m + def _matrix_(self, base=None): + """ + Method used by the :func:`matrix` constructor. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: G = MatrixGroup([MS([1,1,0,1])]) + sage: g = G.gen(0) + sage: M = matrix(GF(9), g); M; parent(M) + [1 1] + [0 1] + Full MatrixSpace of 2 by 2 dense matrices over Finite Field in z2 of size 3^2 + """ + return self.matrix() + cpdef list list(self): """ Return list representation of this matrix. diff --git a/src/sage/matrix/__init__.py b/src/sage/matrix/__init__.py index 7719f53628b..58b5ef12b66 100644 --- a/src/sage/matrix/__init__.py +++ b/src/sage/matrix/__init__.py @@ -1,5 +1,2 @@ -""" -Classes for matrix algebra. - -See docs.py for more information -""" +# Resolve a cyclic import +import sage.matrix.args diff --git a/src/sage/matrix/args.pxd b/src/sage/matrix/args.pxd new file mode 100644 index 00000000000..581f89b0cc5 --- /dev/null +++ b/src/sage/matrix/args.pxd @@ -0,0 +1,122 @@ +from cpython.object cimport PyObject +from sage.structure.element cimport Element, Matrix +from sage.structure.parent cimport Parent + + +cdef enum entries_type: + # flags appearing in the types below + MA_FLAG_ASSUME_SQUARE = 0x01_00 # Assume a square matrix if only 1 dimension given + MA_FLAG_DIM_REQUIRED = 0x02_00 # Dimensions must be given + MA_FLAG_BASE_REQUIRED = 0x04_00 # Base must be given or is trivial to determine + MA_FLAG_FINAL = 0x10_00 # Can occur after finalize() + MA_FLAG_SPARSE = 0x20_00 # Sparse by default + + # types of input entries + MA_ENTRIES_UNKNOWN = 0 # anything + MA_ENTRIES_ZERO = 0x17_01 # zero matrix + MA_ENTRIES_SCALAR = 0x17_02 # single scalar value + MA_ENTRIES_SEQ_SEQ = 0x10_03 # list of lists + MA_ENTRIES_SEQ_FLAT = 0x10_04 # flat list + MA_ENTRIES_SEQ_SPARSE = 0x37_05 # list of SparseEntry instances + MA_ENTRIES_CALLABLE = 0x13_06 # function for the entries + MA_ENTRIES_MATRIX = 0x14_07 # Sage matrix + MA_ENTRIES_MAPPING = 0x01_08 # dict + MA_ENTRIES_METHOD = 0x00_09 # function returning the matrix + MA_ENTRIES_NDARRAY = 0x00_0A # numpy array + + # value for exception raised + MA_EXCEPT = -1 + + +cdef class SparseEntry: + cdef public long i, j + cdef public object entry + + +cdef inline SparseEntry make_SparseEntry(long i, long j, entry): + e = SparseEntry.__new__(SparseEntry) + e.i = i + e.j = j + e.entry = entry + return e + + +cdef class MatrixArgs: + # For integers, -1 means unknown + # For Python objects, None means unknown + cdef public Parent space # parent of matrix + cdef public Parent base # parent of entries + cdef public long nrows, ncols + cdef public object entries + cdef entries_type typ + cdef public bint sparse + cdef public dict kwds # **kwds for MatrixSpace() + cdef bint is_finalized + + cpdef Matrix matrix(self, bint convert=?) + cpdef list list(self, bint convert=?) + cpdef dict dict(self, bint convert=?) + + cdef inline bint ref_safe(self): + """ + Can we safely return self.entries without making a copy? + A refcount of 1 means that self.entries is the only reference. + """ + return (self.entries).ob_refcnt == 1 + + cdef inline bint need_to_convert(self, x): + """Is ``x`` not an element of ``self.base``?""" + if not isinstance(x, Element): + return True + return (x)._parent is not self.base + + cdef int set_base_from_entries(self, entries) except -1 + + cdef inline int setdefault_base(self, B) except -1: + """ + Set the base ring if not previously set. + """ + if self.base is not None: + return 0 + self.base = B + + cdef inline int set_ncols(self, long n) except -1: + """ + Set the number of columns with consistency checking: if the + value was previously set, it must remain the same. + """ + if n < 0: + raise ArithmeticError("number of columns must be non-negative") + cdef long p = self.ncols + if p != -1 and p != n: + raise ValueError(f"inconsistent number of columns: should be {p} but got {n}") + self.ncols = n + + cdef inline int set_nrows(self, long n) except -1: + """ + Set the number of rows with consistency checking: if the + value was previously set, it must remain the same. + """ + if n < 0: + raise ArithmeticError("number of rows must be non-negative") + cdef long p = self.nrows + if p != -1 and p != n: + raise ValueError(f"inconsistent number of rows: should be {p} but got {n}") + self.nrows = n + + cpdef int set_space(self, space) except -1 + + cdef int finalize(self) except -1 + cdef int process_mapping(self) except -1 + cdef int process_method(self) except -1 + cdef int process_ndarray(self) except -1 + cdef int finalize_seq_seq(self) except -1 + cdef int finalize_seq_scalar(self) except -1 + cdef int finalize_callable(self) except -1 + cdef entries_type get_type(self) except MA_EXCEPT + cdef entries_type sequence_type(self) except MA_EXCEPT + + cdef int set_seq_flat(self, entries) except -1 + + +cpdef MatrixArgs MatrixArgs_init(space, entries) diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx new file mode 100644 index 00000000000..3fa9068e985 --- /dev/null +++ b/src/sage/matrix/args.pyx @@ -0,0 +1,1300 @@ +# cython: wraparound=False +# cython: boundscheck=False +""" +Helpers for creating matrices +""" + +#***************************************************************************** +# Copyright (C) 2018 Jeroen Demeyer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import absolute_import, generator_stop + +cimport cython +from cpython.sequence cimport PySequence_Fast +from cysignals.signals cimport sig_check +from cypari2.gen cimport Gen +from cypari2.types cimport typ, t_MAT, t_VEC, t_COL, t_VECSMALL, t_LIST, t_STR, t_CLOSURE + +from .matrix_space import MatrixSpace +from sage.rings.all import ZZ, RDF, CDF +from sage.structure.coerce cimport is_numpy_type, py_scalar_parent +from sage.structure.element cimport (coercion_model, + Element, RingElement, coercion_model) +from sage.arith.long cimport pyobject_to_long +from sage.misc.misc_c import sized_iter +from sage.categories import monoids + + +CommutativeMonoids = monoids.Monoids().Commutative() + + +cdef inline bint element_is_scalar(Element x): + """ + Should this element be considered a scalar (as opposed to a vector)? + """ + # This is mostly guesswork, but after some experimentation the + # checks below turned out to work well in practice... + if isinstance(x, RingElement): + return True + if x._parent in CommutativeMonoids: + return True + return False + + +# We disable cyclic garbage collection and subclassing for efficiency. +# This class is mainly meant to be used internally by MatrixArgs and +# typically not in user code. +@cython.final +@cython.no_gc +cdef class SparseEntry: + """ + Specialized class for dealing with sparse input in + :class:`MatrixArgs`. An instance of ``SparseEntry`` represents + one position in a matrix to be constructed. To construct a sparse + matrix, one would typically make a list of such. + + Previous versions of Sage used a ``dict`` as data structure for + sparse input, but that is not so suitable because the keys are not + guaranteed to be of the correct format. There is also the + performance cost of creating tuples of Python integers. + + Users of this class are expected to know what they are doing, so + the indices are not checked when constructing a matrix. + + INPUT: + + - ``i``, ``j`` -- row and column index + + - ``entry`` -- value to be put at position `(i,j)` + + EXAMPLES:: + + sage: from sage.matrix.args import SparseEntry + sage: SparseEntry(123, 456, "abc") + SparseEntry(123, 456, 'abc') + sage: SparseEntry(1/3, 2/3, x) + Traceback (most recent call last): + ... + TypeError: rational is not an integer + """ + + def __init__(self, i, j, entry): + self.i = pyobject_to_long(i) + self.j = pyobject_to_long(j) + self.entry = entry + + def __iter__(self): + """ + Iteration for convenience, such as converting to a tuple. + + EXAMPLES:: + + sage: from sage.matrix.args import SparseEntry + sage: e = SparseEntry(123, 456, "abc") + sage: tuple(e) + (123, 456, 'abc') + """ + yield self.i + yield self.j + yield self.entry + + def __repr__(self): + return f"{type(self).__name__}({self.i}, {self.j}, {self.entry!r})" + + +# We disable cyclic garbage collection for efficiency. This is not a +# problem because instances should exist only for a short time. +@cython.no_gc +cdef class MatrixArgs: + """ + Collect arguments for constructing a matrix. + + This class is meant to pass around arguments, for example from the + global :func:`matrix` constructor to the matrix space or to the + element class constructor. + + A typical use case is first creating a ``MatrixArgs`` instance, + possibly adjusting the attributes. This instance can then be passed + around and a matrix can be constructed from it using the + :meth:`matrix` method. Also, a flat list can be constructed using + :meth:`list` or a sparse dict using :meth:`dict`. It is safe to + construct multiple objects (of the same or a different kind) from + the same ``MatrixArgs`` instance. + + ``MatrixArgs`` also supports iteration using the :meth:`iter` + method. This is a more low-level interface. + + When ``MatrixArgs`` produces output, it is first *finalized*. This + means that all missing attributes are derived or guessed. After + finalization, you should no longer change the attributes or it will + end up in an inconsistent state. You can also finalize explicitly by + calling the :meth:`finalized` method. + + A ``MatrixArgs`` can contain invalid input. This is not checked when + constructing the ``MatrixArgs`` instance, but it is checked either + when finalizing or when constructing an object from it. + + .. WARNING:: + + Instances of this class should only be temporary, they are not + meant to be stored long-term. + + EXAMPLES:: + + sage: from sage.matrix.args import 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(); ma.matrix() + + [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] + sage: ma = MatrixArgs(QQ, entries=pari("[1,2;3,4]")); ma.finalized(); ma.matrix() + + [1 2] + [3 4] + sage: ma = MatrixArgs(QQ, 2, 2, entries=pari("[1,2,3,4]")); ma.finalized(); ma.matrix() + + [1 2] + [3 4] + sage: ma = MatrixArgs(QQ, 2, 2, entries=pari("3/5")); ma.finalized(); ma.matrix() + + [3/5 0] + [ 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] + sage: from numpy import array + sage: ma = MatrixArgs(array([[1,2],[3,4]])); ma.finalized(); ma.matrix() + + [1 2] + [3 4] + sage: ma = MatrixArgs(array([[1.,2.],[3.,4.]])); ma.finalized(); ma.matrix() + + [1.0 2.0] + [3.0 4.0] + sage: ma = MatrixArgs(RealField(20), array([[1.,2.],[3.,4.]])); ma.finalized(); ma.matrix() + + [1.0000 2.0000] + [3.0000 4.0000] + sage: ma = MatrixArgs(graphs.CycleGraph(3)); ma.finalized(); ma.matrix() + + [0 1 1] + [1 0 1] + [1 1 0] + + Test invalid input:: + + sage: MatrixArgs(ZZ, 2, 2, entries="abcd").finalized() + Traceback (most recent call last): + ... + TypeError: unable to convert 'abcd' to a matrix + sage: MatrixArgs(ZZ, 2, 2, entries=MatrixArgs()).finalized() + Traceback (most recent call last): + ... + TypeError: unable to convert to a matrix + """ + + def __cinit__(self): + self.nrows = -1 + self.ncols = -1 + self.sparse = -1 + self.kwds = {} + + def __init__(self, *args, ring=None, nrows=None, ncols=None, entries=None, sparse=None, space=None, **kwds): + """ + Parse arguments for creating a new matrix. + + See :func:`matrix` for documentation. + + This typically does not raise errors for invalid input, except + when the arguments cannot be parsed. + + EXAMPLES:: + + 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: + self.set_nrows(pyobject_to_long(nrows)) + if ncols is not None: + self.set_ncols(pyobject_to_long(ncols)) + self.entries = entries + if sparse is not None: + self.sparse = sparse + if space is not None: + self.set_space(space) + self.kwds.update(kwds) + + # Parse positional arguments (base, nrows, ncols, entries) + # where each of them is optional. + cdef Py_ssize_t argi = 0, argc = len(args) + if argi == argc: return + + # fast check for certain types of entries which cannot be + # confused with a base ring or a number. + arg = args[argc - 1] + if self.entries is None and isinstance(arg, (list, tuple, dict)): + self.entries = arg + argc -= 1 + if argi == argc: return + + # check for base ring argument + if self.base is None and isinstance(args[argi], Parent): + self.base = args[argi] + argi += 1 + if argi == argc: return + + # check nrows and ncols argument + cdef int k + cdef long v + if self.nrows == -1 and self.ncols == -1: + for k in range(2): + arg = args[argi] + if is_numpy_type(type(arg)): + import numpy + if isinstance(arg, numpy.ndarray): + break + try: + v = pyobject_to_long(arg) + except TypeError: + break + else: + if k == 0: + self.set_nrows(v) + else: + self.set_ncols(v) + argi += 1 + if argi == argc: return + + # check for entries argument + if self.entries is None: + self.entries = args[argi] + argi += 1 + if argi == argc: return + + raise TypeError("too many arguments in matrix constructor") + + def __repr__(self): + """Print representation for debugging""" + t = "(invalid)" + if self.typ == MA_ENTRIES_UNKNOWN: + t = "UNKNOWN" + elif self.typ == MA_ENTRIES_ZERO: + t = "ZERO" + elif self.typ == MA_ENTRIES_SCALAR: + t = "SCALAR" + elif self.typ == MA_ENTRIES_SEQ_SEQ: + t = "SEQ_SEQ" + elif self.typ == MA_ENTRIES_SEQ_FLAT: + t = "SEQ_FLAT" + elif self.typ == MA_ENTRIES_SEQ_SPARSE: + t = "SEQ_SPARSE" + elif self.typ == MA_ENTRIES_CALLABLE: + t = "CALLABLE" + elif self.typ == MA_ENTRIES_MATRIX: + t = "MATRIX" + elif self.typ == MA_ENTRIES_MAPPING: + t = "MAPPING" + elif self.typ == MA_ENTRIES_METHOD: + t = "METHOD" + elif self.typ == MA_ENTRIES_NDARRAY: + t = "NDARRAY" + return f"<{type(self).__name__} for {self.space}; typ={t}; entries={self.entries!r}>" + + def __reduce__(self): + """ + We intentionally do not support pickling because there should + not be any use case for it. + + TESTS:: + + sage: from sage.matrix.args import MatrixArgs + sage: dumps(MatrixArgs()) + Traceback (most recent call last): + ... + RuntimeError: pickling MatrixArgs instances is not allowed + sage: copy(MatrixArgs()) + Traceback (most recent call last): + ... + RuntimeError: pickling MatrixArgs instances is not allowed + """ + raise RuntimeError(f"pickling {type(self).__name__} instances is not allowed") + + def __iter__(self): + """ + Default iteration (dense with conversion) + + EXAMPLES:: + + sage: from sage.matrix.args import SparseEntry, MatrixArgs + sage: ma = MatrixArgs(ZZ, 2, 3, iter(range(6))) + sage: list(ma) + [0, 1, 2, 3, 4, 5] + """ + return self.iter() + + def iter(self, bint convert=True, bint sparse=False): + """ + Iteration over the entries in the matrix + + INPUT: + + - ``convert`` -- If ``True``, the entries are converted to the + base right. If ``False``, the entries are returned as given. + + - ``sparse`` -- See OUTPUT below. + + OUTPUT: iterator + + - If ``sparse`` is False: yield all entries of the matrix in + the following order:: + + [1 2 3] + [4 5 6] + + - If ``sparse`` is True: yield instances of + :class:`SparseEntry`. The indices ``(i, j)`` are guaranteed to + lie within the matrix. Zero entries in the input are *not* + skipped. + + .. WARNING:: + + If an iterator is given as input to :class:`MatrixArgs`, it + may be exhausted breaking any further usage. Otherwise, it + is safe to iterate multiple times. + + EXAMPLES:: + + sage: from sage.matrix.args import SparseEntry, MatrixArgs + sage: ma = MatrixArgs(ZZ, 2, 3, iter(range(6))) + sage: list(ma.iter()) + [0, 1, 2, 3, 4, 5] + sage: ma = MatrixArgs(ZZ, 3, 3, [SparseEntry(0, 0, 0)]) + sage: list(ma.iter()) + Traceback (most recent call last): + ... + TypeError: dense iteration is not supported for sparse input + + Sparse examples:: + + sage: ma = MatrixArgs(3, 3, pi) + sage: list(ma.iter(sparse=True)) + [SparseEntry(0, 0, pi), SparseEntry(1, 1, pi), SparseEntry(2, 2, pi)] + sage: ma = MatrixArgs(2, 3) + sage: list(ma.iter(sparse=True)) + [] + sage: ma = MatrixArgs(2, 2, lambda i, j: i > j) + sage: list(ma.iter(convert=False, sparse=True)) + [SparseEntry(0, 0, False), + SparseEntry(0, 1, False), + SparseEntry(1, 0, True), + SparseEntry(1, 1, False)] + sage: ma = MatrixArgs(2, 2, {(1,0):88, (0,1):89}) + sage: sorted(tuple(x) for x in ma.iter(sparse=True)) + [(0, 1, 89), (1, 0, 88)] + sage: ma = MatrixArgs(QQ, 2, 1, {(1,0):88, (0,1):89}) + sage: ma.finalized() + Traceback (most recent call last): + ... + IndexError: invalid column index 1 + sage: ma = MatrixArgs(QQ, 1, 2, {(1,0):88, (0,1):89}) + sage: ma.finalized() + Traceback (most recent call last): + ... + IndexError: invalid row index 1 + """ + self.finalize() + + cdef long i, j + cdef SparseEntry se + if self.typ == MA_ENTRIES_ZERO: + if sparse: + pass + else: + zero = self.base.zero() + for i in range(self.nrows): + for j in range(self.ncols): + sig_check() + yield zero + elif self.typ == MA_ENTRIES_SCALAR: + diag = self.entries + if convert and self.need_to_convert(diag): + diag = self.base(diag) + if sparse: + for i in range(self.nrows): + sig_check() + yield make_SparseEntry(i, i, diag) + else: + zero = self.base.zero() + for i in range(self.nrows): + for j in range(self.ncols): + sig_check() + yield diag if (i == j) else zero + elif self.typ == MA_ENTRIES_SEQ_SEQ: + row_iter = sized_iter(self.entries, self.nrows) + for i in range(self.nrows): + row = sized_iter(next(row_iter), self.ncols) + for j in range(self.ncols): + sig_check() + x = next(row) + if convert and self.need_to_convert(x): + x = self.base(x) + if sparse: + yield make_SparseEntry(i, j, x) + else: + yield x + elif self.typ == MA_ENTRIES_SEQ_FLAT: + it = sized_iter(self.entries, self.nrows * self.ncols) + for i in range(self.nrows): + for j in range(self.ncols): + sig_check() + x = next(it) + if convert and self.need_to_convert(x): + x = self.base(x) + if sparse: + yield make_SparseEntry(i, j, x) + else: + yield x + elif self.typ == MA_ENTRIES_SEQ_SPARSE: + if sparse: + for t in self.entries: + sig_check() + se = t + if convert and self.need_to_convert(se.entry): + se = make_SparseEntry(se.i, se.j, self.base(se.entry)) + yield se + else: + raise TypeError("dense iteration is not supported for sparse input") + elif self.typ == MA_ENTRIES_CALLABLE: + f = self.entries + for i in range(self.nrows): + for j in range(self.ncols): + sig_check() + x = f(i, j) + if convert and self.need_to_convert(x): + x = self.base(x) + if sparse: + yield make_SparseEntry(i, j, x) + else: + yield x + elif self.typ == MA_ENTRIES_MATRIX: + m = self.entries + for i in range(self.nrows): + for j in range(self.ncols): + sig_check() + x = m[i, j] + if convert and self.need_to_convert(x): + x = self.base(x) + if sparse: + yield make_SparseEntry(i, j, x) + else: + yield x + else: + raise AssertionError(f"unknown MatrixArgs type {self.typ}") + + def __len__(self): + """ + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: len(MatrixArgs(3, 14)) + 42 + """ + self.finalize() + return self.nrows * self.ncols + + cpdef Matrix matrix(self, bint convert=True): + """ + Return the entries of the matrix as a Sage Matrix. + + If possible, this returns a direct reference to + ``self.entries`` without copying. + + INPUT: + + - ``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:: + + This changes ``self.entries`` to the returned matrix. This + means that it is unsafe to access the ``self.entries`` + attribute after calling this method. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: M = matrix(2, 3, range(6), sparse=True) + + :: + + sage: ma = MatrixArgs(M); ma.finalized() + + sage: ma.matrix() + [0 1 2] + [3 4 5] + + :: + + sage: ma = MatrixArgs(M, sparse=False); ma.finalized() + + sage: ma.matrix() + [0 1 2] + [3 4 5] + + :: + + sage: ma = MatrixArgs(RDF, M); ma.finalized() + + sage: ma.matrix(convert=False) + [0 1 2] + [3 4 5] + sage: ma.matrix() + [0.0 1.0 2.0] + [3.0 4.0 5.0] + + If we remove our reference to the input ``M``, no copy is made:: + + sage: idM = id(M) + sage: ma = MatrixArgs(M) + sage: del M + sage: M = ma.matrix() + sage: id(M) == idM + True + + Immutable matrices are never copied:: + + sage: M.set_immutable() + sage: matrix(M) is M + True + """ + self.finalize() + + cdef Matrix M + for _ in range(1): + if self.typ == MA_ENTRIES_MATRIX: + safe = self.ref_safe() # Check this before assigning M + M = self.entries + if M._parent is self.space or not convert: + if not (safe or M.is_immutable()): + M = M.__copy__() + break + else: + M = self.space(self, coerce=convert) + + # Also store the matrix to support multiple calls of matrix() + self.entries = M + self.typ = MA_ENTRIES_MATRIX + return M + + cpdef list list(self, bint convert=True): + """ + Return the entries of the matrix as a flat list of scalars. + + If possible and ``convert=False``, this returns a direct + reference to ``self.entries`` without copying. + + INPUT: + + - ``convert`` -- If True, the entries are converted to the base + ring. Otherwise, the entries are returned as given. + + .. NOTE:: + + This changes ``self.entries`` to the returned list. This + means that it is unsafe to access the ``self.entries`` + attribute after calling this method. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: L = list(range(6)) + sage: MatrixArgs(2, 3, L).list() + [0, 1, 2, 3, 4, 5] + + :: + + sage: ma = MatrixArgs(RDF, 2, 3, L) + sage: ma.list(convert=False) + [0, 1, 2, 3, 4, 5] + sage: ma.list() + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] + + If we remove our reference to the input ``L`` and + ``convert=False``, no copy is made:: + + sage: idL = id(L) + sage: ma = MatrixArgs(2, 3, L) + sage: del L + sage: L = ma.list(convert=False) + sage: id(L) == idL + True + """ + self.finalize() + + cdef long i + cdef list L + if self.typ == MA_ENTRIES_SEQ_FLAT and not convert: + # Try to re-use existing list + if type(self.entries) is not list: + L = list(self.entries) + else: + safe = self.ref_safe() # Check this before assigning L + L = self.entries + if not safe: + L = L[:] + else: + L = list(self.iter(convert)) + + cdef long N = self.nrows * self.ncols + if len(L) != N: + raise ValueError(f"entries has the wrong length (expected {N}, got {len(L)})") + + # Also store the list to support multiple calls of list() + self.entries = L + self.typ = MA_ENTRIES_SEQ_FLAT + return L + + cpdef dict dict(self, bint convert=True): + """ + 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. + + If ``convert`` is True, the entries are converted to the base + ring. Otherwise, the entries are returned as given. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: L = list(range(6)) + sage: MatrixArgs(2, 3, L).dict() + {(0, 1): 1, (0, 2): 2, (1, 0): 3, (1, 1): 4, (1, 2): 5} + + :: + + sage: ma = MatrixArgs(GF(2), 2, 3, L) + sage: ma.dict(convert=False) + {(0, 1): 1, (0, 2): 2, (1, 0): 3, (1, 1): 4, (1, 2): 5} + sage: ma.dict() + {(0, 1): 1, (1, 0): 1, (1, 2): 1} + """ + self.finalize() + + R = self.base + cdef dict D = {} + for t in self.iter(convert, True): + se = t + x = se.entry + if x: + D[se.i, se.j] = x + return D + + cpdef int set_space(self, space) except -1: + """ + Set inputs from a given matrix space. + + INPUT: + + - ``space`` -- a :class:`MatrixSpace` + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: ma = 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] + [0 0] + sage: M.parent() is S + True + """ + self.space = space + self.set_nrows(space.nrows()) + self.set_ncols(space.ncols()) + self.base = space._base + self.sparse = space.is_sparse() + + def finalized(self): + """ + Determine all missing values. + + Depending on the input, this might be a non-trivial operation. + In some cases, this will do most of the work of constructing the + matrix. That is actually not a problem since we eventually want + to construct the matrix anyway. However, care is taken to avoid + double work or needless checking or copying. + + OUTPUT: ``self`` + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: MatrixArgs(pi).finalized() + Traceback (most recent call last): + ... + TypeError: the dimensions of the matrix must be specified + sage: MatrixArgs(RR, pi).finalized() + Traceback (most recent call last): + ... + 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): + ... + TypeError: nonzero scalar matrix must be square + + Check :trac:`19134`:: + + sage: matrix(2, 3, []) + Traceback (most recent call last): + ... + ValueError: sequence too short (expected length 6, got 0) + sage: matrix(ZZ, 2, 3, []) + Traceback (most recent call last): + ... + ValueError: sequence too short (expected length 6, got 0) + sage: matrix(2, 3, [1]) + Traceback (most recent call last): + ... + ValueError: sequence too short (expected length 6, got 1) + """ + self.finalize() + return self + + cdef int finalize(self) except -1: + # See finalized() for doc + if self.is_finalized: + return 0 + + if self.typ == MA_ENTRIES_UNKNOWN: + self.typ = self.get_type() + if self.typ == MA_ENTRIES_UNKNOWN: + raise TypeError(f"unable to convert {self.entries!r} to a matrix") + + # Can we assume a square matrix? + if self.typ & MA_FLAG_ASSUME_SQUARE: + if self.ncols == -1: + if self.nrows != -1: + self.ncols = self.nrows + elif self.typ == MA_ENTRIES_ZERO: + # Special case for the zero matrix: + # assume zero rows and columns + self.nrows = self.ncols = 0 + elif self.nrows == -1: + self.nrows = self.ncols + + # Process some types to other types + if not self.typ & MA_FLAG_FINAL: + if self.typ == MA_ENTRIES_MAPPING: + self.process_mapping() + elif self.typ == MA_ENTRIES_METHOD: + self.process_method() + elif self.typ == MA_ENTRIES_NDARRAY: + self.process_ndarray() + else: + raise AssertionError(f"unhandled MatrixArgs type {self.typ}") + + # Handle trivial MATRIX type + if self.typ == MA_ENTRIES_MATRIX: + m = self.entries + self.set_nrows(m._nrows) + self.set_ncols(m._ncols) + self.setdefault_base(m._parent._base) + if self.sparse == -1: + self.sparse = m.is_sparse() + + # Error if size is required + if self.typ & MA_FLAG_DIM_REQUIRED: + if self.nrows == -1 or self.ncols == -1: + raise TypeError("the dimensions of the matrix must be specified") + + # Determine base in easy cases + if self.base is None and self.typ & MA_FLAG_BASE_REQUIRED: + if self.typ == MA_ENTRIES_ZERO: + self.base = ZZ + elif self.typ == MA_ENTRIES_SCALAR: + if isinstance(self.entries, Element): + self.base = (self.entries)._parent + else: + # Can be None if input is bogus + self.base = py_scalar_parent(type(self.entries)) + if self.base is None: + raise TypeError(f"unable to determine base of {self.entries!r}") + + if self.nrows == -1 or self.ncols == -1 or self.base is None: + # Determine dimensions or base in the cases where we + # really need to look at the entries. + if self.typ == MA_ENTRIES_SEQ_SEQ: + self.finalize_seq_seq() + elif self.typ == MA_ENTRIES_SEQ_FLAT: + self.finalize_seq_scalar() + elif self.typ == MA_ENTRIES_CALLABLE: + self.finalize_callable() + else: + raise AssertionError(f"nrows={self.nrows} ncols={self.ncols} base={self.base} type={self.typ}") + + # Non-zero scalar matrices must be square + if self.typ == MA_ENTRIES_SCALAR: + if self.nrows != self.ncols: + if self.entries: + raise TypeError("nonzero scalar matrix must be square") + self.typ = MA_ENTRIES_ZERO + + if self.sparse == -1: + self.sparse = (self.typ & MA_FLAG_SPARSE) != 0 + + if self.space is None: + self.space = MatrixSpace(self.base, self.nrows, self.ncols, + sparse=self.sparse, **self.kwds) + + self.is_finalized = True + + cdef int set_base_from_entries(self, entries) except -1: + """ + Set base ring from the given list of entries. + """ + if entries: + B = coercion_model.common_parent(*entries) + if isinstance(B, type): + B = py_scalar_parent(B) + self.base = B + else: + self.base = ZZ + + cdef int set_seq_flat(self, entries) except -1: + """ + Set ``self.entries`` to ``entries``, which should be a flat + list of entries. Also determine base ring if unknown. + """ + if self.base is None: + self.set_base_from_entries(entries) + self.entries = entries + self.typ = MA_ENTRIES_SEQ_FLAT + + cdef int process_mapping(self) except -1: + """ + Convert type ``MAPPING`` to ``SEQ_SPARSE`` or ``SEQ_FLAT`` + (the latter if ``sparse`` is ``False``). This also does the rest + of finalization, such as determining dimensions and base ring. + + EXAMPLES:: + + sage: from sage.matrix.args import 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): + ... + IndexError: invalid column index 1 + sage: ma = MatrixArgs(1, 2, {(1,0):88, (0,1):89}) + sage: ma.finalized() + Traceback (most recent call last): + ... + IndexError: invalid row index 1 + """ + cdef list seqsparse = [] + cdef list values = [] + cdef long maxrow = -1, maxcol = -1 # Maximum occuring value for indices + cdef long i, j + for (i0, j0), x in self.entries.items(): + sig_check() + i = pyobject_to_long(i0) + j = pyobject_to_long(j0) + if i > maxrow: + maxrow = i + elif i < 0: # Negative indices to index from the end + if self.nrows > 0: + i += self.nrows + if i < 0: + raise IndexError(f"invalid row index {i0}") + if j > maxcol: + maxcol = j + elif j < 0: # Negative indices to index from the end + if self.ncols > 0: + j += self.ncols + if j < 0: + raise IndexError(f"invalid column index {j0}") + seqsparse.append(make_SparseEntry(i, j, x)) + values.append(x) + if self.nrows == -1: + self.nrows = maxrow + 1 + elif maxrow >= self.nrows: + raise IndexError(f"invalid row index {maxrow}") + if self.ncols == -1: + self.ncols = maxcol + 1 + elif maxcol >= self.ncols: + raise IndexError(f"invalid column index {maxcol}") + + if self.base is None: + self.set_base_from_entries(values) + + if self.sparse == False: + # If we actually want a dense result, convert to MA_ENTRIES_SEQ_FLAT + self.entries = [self.base.zero()] * (self.nrows * self.ncols) + self.typ = MA_ENTRIES_SEQ_FLAT + for t in seqsparse: + se = t + self.entries[se.i*self.ncols + se.j] = se.entry + else: + self.entries = seqsparse + self.typ = MA_ENTRIES_SEQ_SPARSE + + cdef int process_method(self) except -1: + """ + Convert type ``METHOD`` to ``MATRIX``. + """ + args = () + if self.base is not None: + args += (self.base,) + m = self.entries(*args) + if not isinstance(m, Matrix): + raise TypeError(f"{self.entries!r} did not return a matrix") + self.entries = m + self.typ = MA_ENTRIES_MATRIX + + cdef int process_ndarray(self) except -1: + """ + Convert type ``NDARRAY`` to ``MATRIX`` or ``SEQ_SEQ``. + """ + # TODO: this is old code which should be cleaned up and made + # more efficient + e = self.entries + cdef long inrows, incols + (inrows, incols) = e.shape + self.set_nrows(inrows) + self.set_ncols(incols) + str_dtype = str(e.dtype) + + if not (e.flags.c_contiguous is True or e.flags.f_contiguous is True): + raise TypeError('numpy matrix must be either c_contiguous or f_contiguous') + + from .constructor import matrix + if 'float32' in str_dtype: + m = matrix(RDF, inrows, incols, 0) + m._replace_self_with_numpy32(e) + self.typ = MA_ENTRIES_MATRIX + self.setdefault_base(RDF) + elif 'float64' in str_dtype: + m = matrix(RDF, inrows, incols, 0) + m._replace_self_with_numpy(e) + self.typ = MA_ENTRIES_MATRIX + self.setdefault_base(RDF) + elif 'complex64' in str_dtype: + m = matrix(CDF, inrows, incols, 0) + m._replace_self_with_numpy32(e) + self.typ = MA_ENTRIES_MATRIX + self.setdefault_base(CDF) + elif 'complex128' in str_dtype: + m = matrix(CDF, inrows, incols, 0) + m._replace_self_with_numpy(e) + self.typ = MA_ENTRIES_MATRIX + self.setdefault_base(CDF) + else: + if 'int' in str_dtype: + self.setdefault_base(ZZ) + m = self.entries + self.typ = MA_ENTRIES_SEQ_SEQ + self.entries = m + + cdef int finalize_seq_seq(self) except -1: + """ + Determine missing input (dimensions, base ring) for type + ``SEQ_SEQ`` and convert to ``SEQ_FLAT`` in the process. + """ + e = PySequence_Fast(self.entries, "not a sequence") + self.set_nrows(len(e)) + if self.nrows == 0: + if self.ncols == -1: self.ncols = 0 + self.setdefault_base(ZZ) + return 0 + elif self.ncols != -1 and self.base is not None: + # Everything known => OK + return 0 + + # Process everything and convert to SEQ_FLAT + cdef list entries = [] + cdef long c + for row in e: + c = 0 + for entry in row: + sig_check() + c += 1 + entries.append(entry) + self.set_ncols(c) + + self.set_seq_flat(entries) + + cdef int finalize_seq_scalar(self) except -1: + """ + Determine missing input (dimensions, base ring) for type + ``SEQ_FLAT``. + """ + entries = PySequence_Fast(self.entries, "not a sequence") + cdef long N = len(entries) + + if self.ncols == 0 or self.nrows == 0 or N == 0: + # If there are no entries, fill in missing dimensions as 0 + if self.nrows == -1: + self.nrows = 0 + if self.ncols == -1: + self.ncols = 0 + elif self.ncols == -1: + if self.nrows == -1: + # Assume row matrix + self.nrows = 1 + self.ncols = N + else: + self.ncols = N // self.nrows + elif self.nrows == -1: + self.nrows = N // self.ncols + + self.set_seq_flat(entries) + + cdef int finalize_callable(self) except -1: + """ + Determine base ring for type ``CALLABLE`` and convert to + ``SEQ_FLAT`` in the process. + """ + # Dimensions are required, so we must determine the base ring. + # We do this by converting to the SEQ_FLAT type + f = self.entries + cdef list entries = [] + cdef long i, j + for i in range(self.nrows): + row = i + for j in range(self.ncols): + sig_check() + entries.append(f(row, j)) + + self.set_seq_flat(entries) + + cdef inline entries_type get_type(self) except MA_EXCEPT: + """ + Return the type of ``self.entries``. In some cases, this might + change ``self.entries``. + + If the entries are invalid, return ``MA_ENTRIES_UNKNOWN``. + """ + # Check basic Python types. This is very fast, so it doesn't + # hurt to do these first. + if self.entries is None: + return MA_ENTRIES_ZERO + if isinstance(self.entries, (list, tuple)): + return self.sequence_type() + if isinstance(self.entries, dict): + return MA_ENTRIES_MAPPING + if isinstance(self.entries, (int, long, float, complex)): + return MA_ENTRIES_SCALAR + + # Note: some objects are callable, iterable and act like a + # scalar, e.g. polynomials. So the order of these checks + # matters a lot. Also the more efficient checks should be + # done first. + + # We check for Element first because that speeds up some + # other checks. + cdef bint is_elt = isinstance(self.entries, Element) + if is_elt and isinstance(self.entries, Matrix): + return MA_ENTRIES_MATRIX + t = type(self.entries) + try: + f = t._matrix_ + except AttributeError: + pass + else: + self.entries = f.__get__(self.entries, t) + return MA_ENTRIES_METHOD + if is_elt and element_is_scalar(self.entries): + return MA_ENTRIES_SCALAR + if not is_elt and is_numpy_type(t): + import numpy + if isinstance(self.entries, numpy.ndarray): + # Convert to a numpy array if it was a matrix. + if t is not numpy.ndarray: + self.entries = numpy.array(self.entries) + return MA_ENTRIES_NDARRAY + return MA_ENTRIES_SCALAR + if isinstance(self.entries, Gen): # PARI object + t = typ((self.entries).g) + if t == t_MAT: + self.entries = self.entries.Col().sage() + return MA_ENTRIES_SEQ_SEQ + elif t in [t_VEC, t_COL, t_VECSMALL, t_LIST]: + self.entries = self.entries.sage() + return MA_ENTRIES_SEQ_FLAT + elif t == t_CLOSURE: + return MA_ENTRIES_CALLABLE + elif t == t_STR: + return MA_ENTRIES_UNKNOWN + else: + self.entries = self.entries.sage() + return MA_ENTRIES_SCALAR + if isinstance(self.entries, MatrixArgs): + # Prevent recursion + return MA_ENTRIES_UNKNOWN + if isinstance(self.entries, basestring): + # Blacklist strings, we don't want them to be considered a sequence + return MA_ENTRIES_UNKNOWN + try: + self.entries = list(self.entries) + except TypeError: + pass + else: + return self.sequence_type() + if callable(self.entries): + return MA_ENTRIES_CALLABLE + if is_elt: # Last resort + return MA_ENTRIES_SCALAR + return MA_ENTRIES_UNKNOWN + + cdef entries_type sequence_type(self) except MA_EXCEPT: + """ + Return the type of ``self.entries``, where ``self.entries`` + is a sequence. + + If the entries are invalid, return ``MA_ENTRIES_UNKNOWN``. + """ + if not self.entries: + return MA_ENTRIES_SEQ_FLAT + x = self.entries[0] + if isinstance(x, (list, tuple)): + return MA_ENTRIES_SEQ_SEQ + if type(x) is SparseEntry: + return MA_ENTRIES_SEQ_SPARSE + if self.nrows != -1 and self.ncols != -1 and self.ncols != 1: + # Determine type from the number of entries. Unfortunately, + # this only works if the number of columns is not 1. + if len(self.entries) == self.nrows: + return MA_ENTRIES_SEQ_SEQ + else: + return MA_ENTRIES_SEQ_FLAT + if isinstance(x, (int, long, float, complex)): + return MA_ENTRIES_SEQ_FLAT + if isinstance(x, Element) and element_is_scalar(x): + return MA_ENTRIES_SEQ_FLAT + if isinstance(x, basestring): + # Blacklist strings, we don't want them to be considered a sequence + return MA_ENTRIES_UNKNOWN + try: + iter(x) + except TypeError: + return MA_ENTRIES_SEQ_FLAT + else: + return MA_ENTRIES_SEQ_SEQ + + +cpdef MatrixArgs MatrixArgs_init(space, entries): + """ + Construct a :class:`MatrixArgs` object from a matrix space and + entries. This is the typical use in a matrix constructor. + + If the given entries is already an instance of :class:`MatrixArgs`, + then just set the space and return the same object. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs_init + sage: S = MatrixSpace(GF(2), 2, 4) + sage: ma = MatrixArgs_init(S, {(1,3):7}) + sage: M = ma.matrix(); M + [0 0 0 0] + [0 0 0 1] + sage: parent(M) is S + True + """ + cdef MatrixArgs ret + if isinstance(entries, MatrixArgs): + ret = entries + else: + ret = MatrixArgs.__new__(MatrixArgs) + ret.entries = entries + ret.set_space(space) + ret.finalize() + return ret diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 19a1d37df04..81054152318 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -1,10 +1,11 @@ +# cython: binding=True """ General matrix Constructor """ #***************************************************************************** # Copyright (C) 2005 William Stein -# Copyright (C) 2016 Jeroen Demeyer +# Copyright (C) 2016 Jeroen Demeyer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -12,17 +13,10 @@ General matrix Constructor # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import absolute_import -import types -from .matrix_space import MatrixSpace -from sage.rings.ring import is_Ring -from sage.rings.all import ZZ, RDF, CDF -from sage.arith.srange import srange -from sage.structure.coerce cimport is_numpy_type, py_scalar_parent -from sage.structure.element cimport Vector -from sage.structure.sequence import Sequence -from sage.arith.long cimport pyobject_to_long +from .args cimport MatrixArgs class MatrixFactory(object): @@ -60,29 +54,34 @@ class MatrixFactory(object): makes sense. Calling matrix() with a NumPy array will convert the array to a matrix. - The ring, number of rows, and number of columns of the matrix can - be specified by setting the ``ring``, ``nrows``, or ``ncols`` - keyword parameters or by passing them as the first arguments to the - function in specified order. The ring defaults to ``ZZ`` if it is - not specified and cannot be determined from the entries. If the - number of rows and columns are not specified and cannot be - determined, then an empty 0x0 matrix is returned. - - INPUT: + All arguments (even the positional) are optional. - - ``ring`` -- the base ring for the entries of the - matrix. + Positional and keyword arguments: - - ``nrows`` -- the number of rows in the matrix. + - ``ring`` -- parent of the entries of the matrix (despite the + name, this is not a priori required to be a ring). By default, + determine this from the given entries, falling back to ``ZZ`` if + no entries are given. - - ``ncols`` -- the number of columns in the matrix. + - ``nrows`` -- the number of rows in the matrix. - - ``sparse`` -- create a sparse matrix. This defaults to ``True`` - when the entries are given as a dictionary, otherwise defaults to - ``False``. + - ``ncols`` -- the number of columns in the matrix. - ``entries`` -- see examples below. + If either ``nrows`` or ``ncols`` is given as keyword argument, then + no positional arguments ``nrows`` and ``ncols`` may be given. + + Keyword-only arguments: + + - ``sparse`` -- (boolean) create a sparse matrix. This defaults to + ``True`` when the entries are given as a dictionary, otherwise + defaults to ``False``. + + - ``space`` -- matrix space which will be the parent of the output + matrix. This determines ``ring``, ``nrows``, ``ncols`` and + ``sparse``. + OUTPUT: a matrix @@ -186,6 +185,18 @@ class MatrixFactory(object): sage: m = matrix(GF(7), v); m; m.parent() [1 3 2] Full MatrixSpace of 1 by 3 dense matrices over Finite Field of size 7 + sage: m = matrix(GF(7), 3, 1, v); m; m.parent() + [1] + [3] + [2] + Full MatrixSpace of 3 by 1 dense matrices over Finite Field of size 7 + + :: + + sage: matrix(pari.mathilbert(3)) + [ 1 1/2 1/3] + [1/2 1/3 1/4] + [1/3 1/4 1/5] :: @@ -289,7 +300,7 @@ class MatrixFactory(object): sage: matrix(QQ,2,3,1) Traceback (most recent call last): ... - TypeError: identity matrix must be square + TypeError: nonzero scalar matrix must be square sage: matrix(QQ,2,3,5) Traceback (most recent call last): ... @@ -308,7 +319,7 @@ class MatrixFactory(object): sage: m = matrix(QQ,3,[[1,2,3],[4,5,6]]); m; m.parent() Traceback (most recent call last): ... - ValueError: number of rows does not match up with specified number + ValueError: inconsistent number of rows: should be 3 but got 2 sage: m = matrix(QQ,2,3,[[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] @@ -316,7 +327,7 @@ class MatrixFactory(object): sage: m = matrix(QQ,2,4,[[1,2,3],[4,5,6]]); m; m.parent() Traceback (most recent call last): ... - ValueError: number of columns does not match up with specified number + ValueError: sequence too short (expected length 4, got 3) sage: m = matrix([(1,2,3),(4,5,6)]); m; m.parent() [1 2 3] [4 5 6] @@ -338,13 +349,11 @@ class MatrixFactory(object): sage: m = matrix(QQ,2,4,[1,2,3,4,5,6]); m; m.parent() Traceback (most recent call last): ... - ValueError: entries has the wrong length + ValueError: sequence too short (expected length 8, got 6) sage: m = matrix(QQ,5,[1,2,3,4,5,6]); m; m.parent() Traceback (most recent call last): ... - TypeError: cannot construct an element of - Full MatrixSpace of 5 by 1 dense matrices over Rational Field - from [1, 2, 3, 4, 5, 6]! + ValueError: sequence too long (expected length 5, got more) Matrices specified by a dict of entries:: @@ -377,7 +386,7 @@ class MatrixFactory(object): sage: m = matrix(QQ,1,{(1,1): 2}); m; m.parent() Traceback (most recent call last): ... - IndexError: invalid entries list + IndexError: invalid row index 1 sage: m = matrix({}); m; m.parent() [] Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring @@ -412,7 +421,7 @@ class MatrixFactory(object): sage: m = matrix(0,[1]); m; m.parent() Traceback (most recent call last): ... - ValueError: entries has the wrong length + ValueError: sequence too long (expected length 0, got more) sage: m = matrix(1,0,[]); m; m.parent() [] Full MatrixSpace of 1 by 0 dense matrices over Integer Ring @@ -428,11 +437,11 @@ class MatrixFactory(object): sage: m = matrix(0,{(1,1):2}); m; m.parent() Traceback (most recent call last): ... - IndexError: invalid entries list + IndexError: invalid row index 1 sage: m = matrix(2,0,{(1,1):2}); m; m.parent() Traceback (most recent call last): ... - IndexError: invalid entries list + IndexError: invalid column index 1 Check conversion from numpy:: @@ -502,7 +511,11 @@ class MatrixFactory(object): The dimensions of a matrix may be given as numpy types:: - sage: matrix(numpy.int32(2), ncols=numpy.int32(3)) + sage: matrix(numpy.int32(2), numpy.int32(3)) + [0 0 0] + [0 0 0] + + sage: matrix(nrows=numpy.int32(2), ncols=numpy.int32(3)) [0 0 0] [0 0 0] @@ -511,7 +524,7 @@ class MatrixFactory(object): sage: matrix(RR, 2.0, 2.0) Traceback (most recent call last): ... - TypeError: invalid matrix constructor: type matrix? for help + TypeError: too many arguments in matrix constructor More tests:: @@ -537,11 +550,11 @@ class MatrixFactory(object): sage: matrix([[1],[2,3]]) Traceback (most recent call last): ... - ValueError: list of rows is not valid (rows are wrong types or lengths) + ValueError: inconsistent number of columns: should be 1 but got 2 sage: matrix([[1],2]) Traceback (most recent call last): ... - ValueError: list of rows is not valid (rows are wrong types or lengths) + TypeError: 'sage.rings.integer.Integer' object is not iterable sage: matrix(vector(RR,[1,2,3])).parent() Full MatrixSpace of 1 by 3 dense matrices over Real Field with 53 bits of precision @@ -550,27 +563,40 @@ class MatrixFactory(object): sage: matrix(ZZ, [[0] for i in range(10^5)]).is_zero() True - Test conversion using a ``_matrix_`` method:: + Test a simple ``_matrix_`` method. Note that we are ignoring + ``base`` which is inefficient but allowed:: - sage: A = gap(MatrixSpace(QQ, 2, 2)(range(4))) - sage: matrix(QQ, A) - [0 1] - [2 3] - sage: matrix(A, ring=QQ) - [0 1] - [2 3] + sage: class MatrixTest(object): + ....: def _matrix_(self, base=None): + ....: return matrix(ZZ, 2, 2, [1,2,3,4]) + sage: e = MatrixTest() + sage: matrix(e) + [1 2] + [3 4] + sage: S = MatrixSpace(ZZ, 2, 2) + sage: M = S(e); M + [1 2] + [3 4] + sage: parent(M) is S + True + sage: matrix(RDF, e) + [1.0 2.0] + [3.0 4.0] + sage: S = MatrixSpace(RDF, 2, 2) + sage: M = S(e); M + [1.0 2.0] + [3.0 4.0] + sage: parent(M) is S + True A redundant ``ring`` argument:: sage: matrix(ZZ, 3, 3, ring=ZZ) Traceback (most recent call last): ... - TypeError: invalid matrix constructor: type matrix? for help - - TESTS: + TypeError: too many arguments in matrix constructor - Some calls using an iterator (note that xrange is no longer available - in Python 3):: + Some calls using an iterator:: sage: from six.moves import range sage: matrix(QQ, 3, 6, range(18), sparse=true) @@ -592,337 +618,14 @@ class MatrixFactory(object): - Jeroen Demeyer (2016-02-05): major clean up, see :trac:`20015` and :trac:`20016` - """ - def __call__(self, *Args, ring=None, nrows=None, ncols=None, sparse=None): - cdef list args = list(Args) - - # ring argument - if ring is None and args and is_Ring(args[0]): - ring = args.pop(0) - - # object with _matrix_ method - if args: - try: - makematrix = args[0]._matrix_ - except AttributeError: - pass - else: - if ring is None: - args.pop(0) - else: - args[0] = ring - if sparse is None: - return makematrix(*args) - else: - return makematrix(*args, sparse=sparse) - - # nrows argument - if nrows is None and args: - arg = args[0] - try: - if is_numpy_type(type(arg)): - import numpy - if isinstance(arg, numpy.ndarray): - raise TypeError - nrows = pyobject_to_long(arg) - except TypeError: - pass - else: - args.pop(0) - - # ncols argument - if ncols is None and args: - arg = args[0] - try: - if is_numpy_type(type(arg)): - import numpy - if isinstance(arg, numpy.ndarray): - raise TypeError - ncols = pyobject_to_long(arg) - except TypeError: - pass - else: - args.pop(0) - - # Now we've taken care of initial ring, nrows, and ncols arguments. - # We've also taken care of the Sage object case. - - # Now the rest of the arguments are a list of rows, a flat list of - # entries, a callable, a dict, a numpy array, or a single value. - entry_ring = ZZ - if not args: - # If no entries are specified, pass back a zero matrix - entries = 0 - elif len(args) == 1: - arg = args[0] - if isinstance(arg, (types.FunctionType, types.LambdaType, types.MethodType)): - if ncols is None and nrows is None: - raise TypeError("when passing in a callable, the dimensions of the matrix must be specified") - if ncols is None: - ncols = nrows - elif nrows is None: - nrows = ncols - - irange = srange(nrows) - jrange = srange(ncols) - arg = [[arg(i, j) for j in jrange] for i in irange] - - if isinstance(arg, xrange): - arg = list(arg) - if isinstance(arg, (list, tuple)): - if not arg: - # no entries are specified, pass back the zero matrix - entries = 0 - elif isinstance(arg[0], (list, tuple)) or isinstance(arg[0], Vector): - # Ensure we have a list of lists, each inner list having the same number of elements - first_len = len(arg[0]) - if not all( (isinstance(v, (list, tuple)) or isinstance(v, Vector)) and len(v) == first_len for v in arg): - raise ValueError("list of rows is not valid (rows are wrong types or lengths)") - # We have a list of rows or vectors - if nrows is None: - nrows = len(arg) - elif nrows != len(arg): - raise ValueError("number of rows does not match up with specified number") - if ncols is None: - ncols = len(arg[0]) - elif ncols != len(arg[0]): - raise ValueError("number of columns does not match up with specified number") - - entries = [] - for v in arg: - entries.extend(v) - - else: - # We have a flat list; figure out nrows and ncols - if nrows is None: - nrows = 1 - - if nrows > 0: - if ncols is None: - ncols = len(arg) // nrows - elif ncols != len(arg) // nrows: - raise ValueError("entries has the wrong length") - elif len(arg) > 0: - raise ValueError("entries has the wrong length") - - entries = arg - - if nrows > 0 and ncols > 0 and ring is None: - entries, ring = prepare(entries) - - elif isinstance(arg, dict): - # We have a dictionary: default to sparse - if sparse is None: - sparse = True - if not arg: - # no entries are specified, pass back the zero matrix - entries = 0 - else: - entries, entry_ring = prepare_dict(arg) - if nrows is None: - nrows = nrows_from_dict(entries) - ncols = ncols_from_dict(entries) - # note that ncols can still be None if nrows is set -- - # it will be assigned nrows down below. - - # See the construction after the numpy case below. - else: - if is_numpy_type(type(arg)): - import numpy - if isinstance(arg, numpy.ndarray): - # Convert to a numpy array if it was a matrix. - if type(arg) is not numpy.ndarray: - arg = numpy.array(arg) - - str_dtype = str(arg.dtype) - - if not (arg.flags.c_contiguous is True or arg.flags.f_contiguous is True): - raise TypeError('numpy matrix must be either c_contiguous or f_contiguous') - - if str_dtype.count('float32') == 1: - m = matrix(RDF, arg.shape[0], arg.shape[1], 0) - m._replace_self_with_numpy32(arg) - elif str_dtype.count('float64') == 1: - m = matrix(RDF, arg.shape[0], arg.shape[1], 0) - m._replace_self_with_numpy(arg) - elif str_dtype.count('complex64') == 1: - m = matrix(CDF, arg.shape[0], arg.shape[1], 0) - m._replace_self_with_numpy32(arg) - elif str_dtype.count('complex128') == 1: - m = matrix(CDF, arg.shape[0], arg.shape[1], 0) - m._replace_self_with_numpy(arg) - elif str_dtype.count('int') == 1: - m = matrix(ZZ, [list(row) for row in list(arg)]) - elif str_dtype.count('object') == 1: - # Get the raw nested list from the numpy array - # and feed it back into matrix - m = matrix([list(row) for row in list(arg)]) - else: - raise TypeError("cannot convert NumPy matrix to Sage matrix") - - if ring is not None and m.base_ring() is not ring: - m = m.change_ring(ring) - - return m - elif nrows is not None and ncols is not None: - # assume that we should just pass the thing into the - # MatrixSpace constructor and hope for the best - # This is not documented, but it is doctested - if ring is None: - entry_ring = arg.parent() - entries = arg - else: - raise TypeError("invalid matrix constructor: type matrix? for help") - else: # len(args) >= 2 - raise TypeError("invalid matrix constructor: type matrix? for help") - - if ring is None: - ring = entry_ring - if nrows is None: - nrows = 0 - if ncols is None: - ncols = nrows - - return MatrixSpace(ring, nrows, ncols, sparse=sparse)(entries) - -Matrix = matrix = MatrixFactory() - -def prepare(w): + - Jeroen Demeyer (2018-02-20): completely rewritten using + :class:`MatrixArgs`, see :trac:`24742` """ - Given a list w of numbers, find a common ring that they all - canonically map to, and return the list of images of the elements - of w in that ring along with the ring. - - This is for internal use by the matrix function. - - INPUT: - - - ``w`` - list - - OUTPUT: + def __call__(self, *args, **kwds): + return MatrixArgs(*args, **kwds).matrix() - list, ring - - EXAMPLES:: - - sage: sage.matrix.constructor.prepare([-2, Mod(1,7)]) - ([5, 1], Ring of integers modulo 7) - - Notice that the elements must all canonically coerce to a common - ring (since Sequence is called):: - - sage: sage.matrix.constructor.prepare([2/1, Mod(1,7)]) - Traceback (most recent call last): - ... - TypeError: unable to find a common ring for all elements - - TESTS: - - Check that :trac:`19920` is fixed:: - - sage: import numpy - sage: matrix([[numpy.int8(1)]]) - [1] - """ - if not w: - return Sequence([], ZZ), ZZ - entries = Sequence(w) - ring = entries.universe() - if isinstance(ring,type): - ring = py_scalar_parent(ring) - if not is_Ring(ring): - raise TypeError("unable to find a common ring for all elements") - return entries, ring - -def prepare_dict(w): - """ - Given a dictionary w of numbers, find a common ring that they all - canonically map to, and return the dictionary of images of the - elements of w in that ring along with the ring. - - This is for internal use by the matrix function. - - INPUT: - - - ``w`` -- dict - - OUTPUT: - - dict, ring - - EXAMPLES:: - - sage: sage.matrix.constructor.prepare_dict({(0,1):2, (4,10):Mod(1,7)}) - ({(0, 1): 2, (4, 10): 1}, Ring of integers modulo 7) - """ - Z = list(w.items()) - X = [x for _, x in Z] - entries, ring = prepare(X) - return {Z[i][0]: ent for i, ent in enumerate(entries)}, ring - - -def nrows_from_dict(d): - """ - Given a dictionary that defines a sparse matrix, return the number - of rows that matrix should have. - - This is for internal use by the matrix function. - - INPUT: - - - ``d`` - dict - - OUTPUT: - - integer - - EXAMPLES:: - - sage: sage.matrix.constructor.nrows_from_dict({}) - 0 - - Here the answer is 301 not 300, since there is a 0-th row. - - :: - - sage: sage.matrix.constructor.nrows_from_dict({(300,4):10}) - 301 - """ - if 0 == len(d): - return 0 - return max([0] + [ij[0] for ij in d.keys()]) + 1 - -def ncols_from_dict(d): - """ - Given a dictionary that defines a sparse matrix, return the number - of columns that matrix should have. - - This is for internal use by the matrix function. - - INPUT: - - - ``d`` - dict - - OUTPUT: - - integer - - EXAMPLES:: - - sage: sage.matrix.constructor.ncols_from_dict({}) - 0 - - Here the answer is 301 not 300, since there is a 0-th row. - - :: - - sage: sage.matrix.constructor.ncols_from_dict({(4,300):10}) - 301 - """ - if 0 == len(d): - return 0 - return max([0] + [ij[1] for ij in d.keys()]) + 1 +Matrix = matrix = MatrixFactory() from .special import * diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 1e10430d537..8d490918857 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -2178,6 +2178,10 @@ cdef class Matrix(Matrix0): sage: B = A.dense_matrix() sage: B.is_sparse() False + sage: A == B + True + sage: B.dense_matrix() is B + True sage: A*B [1 4] [0 1] @@ -2218,10 +2222,8 @@ cdef class Matrix(Matrix0): if self.is_dense(): return self cdef Matrix A - A = self.new_matrix(self._nrows, self._ncols, 0, coerce=False, - copy=False, sparse=False) - for i,j in self.nonzero_positions(): - A.set_unsafe(i,j,self.get_unsafe(i,j)) + A = self.new_matrix(self._nrows, self._ncols, self, + coerce=False, sparse=False) A.subdivide(self.subdivisions()) return A @@ -2245,12 +2247,10 @@ cdef class Matrix(Matrix0): sage: B = A.sparse_matrix() sage: B.is_sparse() True - sage: A - [1 2] - [0 1] - sage: B - [1 2] - [0 1] + sage: A == B + True + sage: B.sparse_matrix() is B + True sage: A*B [1 4] [0 1] @@ -2265,8 +2265,8 @@ cdef class Matrix(Matrix0): """ if self.is_sparse(): return self - A = self.new_matrix(self._nrows, self._ncols, entries = self.dict(), coerce=False, - copy = False, sparse=True) + A = self.new_matrix(self._nrows, self._ncols, self, + coerce=False, sparse=True) A.subdivide(self.subdivisions()) return A diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx index f2b69597ca6..8921bd821c5 100644 --- a/src/sage/matrix/matrix_complex_ball_dense.pyx +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -36,6 +36,7 @@ from sage.libs.arb.acb_mat cimport * from sage.matrix.matrix cimport Matrix from sage.matrix.constructor import matrix from sage.matrix.matrix_generic_sparse cimport Matrix_generic_sparse +from .args cimport SparseEntry, MatrixArgs_init from sage.rings.complex_interval_field import ComplexIntervalField_class, ComplexIntervalField from sage.rings.complex_interval cimport ComplexIntervalFieldElement from sage.rings.complex_arb cimport ( @@ -159,30 +160,20 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): """ acb_mat_clear(self.value) - - def __init__(self, - parent, - entries, - copy, - coerce): + def __init__(self, parent, entries=None, copy=None, bint coerce=True): r""" Initialize a dense matrix over the complex ball field. INPUT: - - ``parent`` - a matrix space - - - ``entries`` - list - create the matrix with those - entries along the rows. + - ``parent`` -- a matrix space over a complex ball field - - ``other`` - a scalar; entries is coerced to a complex ball - and the diagonal entries of this matrix are set to that - complex ball. + - ``entries`` -- see :func:`matrix` - - ``coerce`` - whether need to coerce entries to the - complex ball field (program may crash if you get this wrong) + - ``copy`` -- ignored (for backwards compatibility) - - ``copy`` - ignored (since complex balls are immutable) + - ``coerce`` -- if False, assume without checking that the + entries lie in the base ring EXAMPLES: @@ -245,56 +236,12 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): 100 x 0 dense matrix over Complex ball field with 53 bits of precision (use the '.str()' method to see the entries) """ - cdef Py_ssize_t i, j, k - cdef bint is_list - cdef ComplexBall x - - if entries is None: - x = self._base_ring.zero() - is_list = False - elif isinstance(entries, (int, long, Element)): - try: - x = self._base_ring(entries) - except TypeError: - raise TypeError("unable to convert entry to a complex ball") - is_list = False - else: - entries = list(entries) - is_list = True - - if is_list: - # Create the matrix whose entries are in the given entry list. - if len(entries) != self._nrows * self._ncols: - raise TypeError("entries has the wrong length") - if coerce: - k = 0 - for i in range(self._nrows): - for j in range(self._ncols): - x = self._base_ring(entries[k]) - acb_set(acb_mat_entry(self.value, i, j), - x.value) - k += 1 - else: - k = 0 - for i in range(self._nrows): - for j in range(self._ncols): - acb_set(acb_mat_entry(self.value, i, j), - ( entries[k]).value) - k += 1 - else: - # If x is zero, make the zero matrix and be done. - if acb_is_zero(x.value): - acb_mat_zero(self.value) - return - - # the matrix must be square: - if self._nrows != self._ncols: - raise TypeError("nonzero scalar matrix must be square") - - # Now we set all the diagonal entries to x and all other entries to 0. - acb_mat_zero(self.value) - for i in range(self._nrows): - acb_set(acb_mat_entry(self.value, i, i), x.value) + ma = MatrixArgs_init(parent, entries) + cdef ComplexBall z + for t in ma.iter(coerce, True): + se = t + z = se.entry + acb_set(acb_mat_entry(self.value, se.i, se.j), z.value) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x): """ diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index 9056c6929df..0335568b79c 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -51,6 +51,7 @@ from sage.libs.flint.fmpz cimport fmpz_init, fmpz_clear, fmpz_set, fmpz_set_mpz, from sage.libs.flint.fmpq cimport fmpq_is_zero, fmpq_get_mpq, fmpq_set_mpq, fmpq_canonicalise from sage.libs.flint.fmpq_mat cimport fmpq_mat_entry_num, fmpq_mat_entry_den, fmpq_mat_entry +from .args cimport MatrixArgs_init from .constructor import matrix from .matrix_space import MatrixSpace from .matrix cimport Matrix @@ -113,26 +114,20 @@ cdef class Matrix_cyclo_dense(Matrix_dense): self._degree = self._base_ring.degree() self._n = int(self._base_ring._n()) - # This is not necessary, since we do not (yet) explicitly allocate - # any memory. - #def __dealloc__(self): - # pass - - def __init__(self, parent, entries=None, copy=True, coerce=True): + def __init__(self, parent, entries=None, copy=None, bint coerce=True): """ Initialize a newly created cyclotomic matrix. INPUT: - - ``parent`` -- a matrix space over a cyclotomic field + - ``parent`` -- a matrix space over a cyclotomic number field - - ``entries`` -- a list of entries or scalar + - ``entries`` -- see :func:`matrix` - - ``coerce`` -- boolean; if true entries are coerced to base - ring + - ``copy`` -- ignored (for backwards compatibility) - - ``copy`` -- boolean; ignored due to underlying data - structure + - ``coerce`` -- if False, assume without checking that the + entries lie in the base ring EXAMPLES: @@ -161,32 +156,15 @@ cdef class Matrix_cyclo_dense(Matrix_dense): sage: A [0 1] [2 3] - """ - cdef int i - z = None - if (entries is None) or (entries == 0): - pass - elif isinstance(entries, list): - # This code could be made much faster using Cython, etc. - if coerce: - K = parent.base_ring() - entries = [K(a) for a in entries] - entries = sum([a.list() for a in entries], []) - else: - K = self._base_ring - z = K(entries) - entries = 0 - - self._n = int(self._base_ring._n()) - self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._nrows*self._ncols, self._degree), - entries, copy=False, coerce=False).transpose() - # This could also be made much faster. - if z is not None: - if self._nrows != self._ncols: - raise TypeError("nonzero scalar matrix must be square") - for i in range(self._nrows): - self.set_unsafe(i,i,z) + ma = MatrixArgs_init(parent, entries) + cdef list L = [] + for x in ma.iter(coerce): + L += x.list() + + QQspace = MatrixSpace(QQ, self._nrows * self._ncols, self._degree) + QQmat = Matrix_rational_dense(QQspace, L, False, False) + self._matrix = QQmat.transpose() cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): """ diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 48e0a8a97ff..8014dc5ad8d 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -47,12 +47,12 @@ TESTS:: from __future__ import absolute_import import math -from collections import Iterator, Sequence import sage.rings.real_double import sage.rings.complex_double from .matrix cimport Matrix +from .args cimport MatrixArgs_init from sage.structure.element cimport ModuleElement,Vector from .constructor import matrix from sage.modules.free_module_element import vector @@ -155,12 +155,24 @@ cdef class Matrix_double_dense(Matrix_dense): """ return self.fetch('PLU_factors') is not None - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Fill the matrix with entries. The numpy matrix must have already been allocated. + INPUT: + + - ``parent`` -- a matrix space over ``RDF`` + + - ``entries`` -- see :func:`matrix` + + - ``copy`` -- ignored (for backwards compatibility) + + - ``coerce`` -- if True (the default), convert elements to the + base ring before passing them to NumPy. If False, pass the + elements to NumPy as given. + EXAMPLES:: sage: matrix(RDF,3,range(9)) @@ -206,43 +218,12 @@ cdef class Matrix_double_dense(Matrix_dense): [ 0.0 1.0 + 1.0*I] [2.0 + 2.0*I 3.0 + 3.0*I] """ - cdef Py_ssize_t i,j - cdef cnumpy.npy_intp dims[2] - dims[0] = self._nrows - dims[1] = self._ncols - if isinstance(entries, (Iterator, Sequence)): - if not isinstance(entries, (list, tuple)): - entries = list(entries) - - if len(entries) != self._nrows * self._ncols: - raise TypeError("entries has wrong length") - - if coerce: - for i from 0<=i= self._nrows or j < 0 or j >= self._ncols): - raise IndexError("matrix indices {} out of range".format(key)) - w = R(x) - if w: - v[(i,j)] = w - entries = v - else: - # Here we do not pay attention to the indices. We just check that it - # *converts* to a pair of Py_ssize_t. In particular it is possible - # to do: - # - # sage: R = QQ['a','b'] - # sage: M = MatrixSpace(R, 3, 3, sparse=True) - # sage: m = M({(Zmod(3)(1), Zmod(6)(2)): R.one()}, coerce=False) - # - # and this is bad since: - # - # sage: list(map(type,m.dict().keys()[0])) - # [, - # ] - # - # But not that setting coerce=False is advanced usage and we assume - # that in such case the user knows what he/she is doing. - if copy: - entries = entries.copy() - for key in entries.keys(): - i,j = key - if i < 0: i += self._nrows - if j < 0: j += self._ncols - if (i < 0 or i >= self._nrows or j < 0 or j >= self._ncols): - raise IndexError("matrix indices {} out of range".format(key)) - if not entries[key]: - del entries[key] - - self._entries = entries + self._zero = self.base_ring().zero() + ma = MatrixArgs_init(parent, entries) + self._entries = ma.dict(coerce) def __nonzero__(self): r""" diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index 65ffdc6d812..83f6853df46 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -93,6 +93,7 @@ from sage.rings.all import FiniteField as GF from sage.misc.randstate cimport randstate, current_randstate from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense +from .args cimport SparseEntry, MatrixArgs_init from sage.libs.m4ri cimport m4ri_word, mzd_copy, mzd_init from sage.libs.m4rie cimport * @@ -213,16 +214,20 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): mzed_free(self._entries) self._entries = NULL - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Create new matrix over `GF(2^e)` for 2<=e<=10. INPUT: - - ``parent`` - a :class:`MatrixSpace`. - - ``entries`` - may be list or a finite field element. - - ``copy`` - ignored, elements are always copied - - ``coerce`` - ignored, elements are always coerced + - ``parent`` -- a matrix space over ``GF(2^e)`` + + - ``entries`` -- see :func:`matrix` + + - ``copy`` -- ignored (for backwards compatibility) + + - ``coerce`` -- if False, assume without checking that the + entries lie in the base ring EXAMPLES:: @@ -246,34 +251,10 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): [0 a 0] [0 0 a] """ - cdef int i,j - - if entries is None: - return - - R = self.base_ring() - - # scalar ? - if not isinstance(entries, list): - if entries != 0: - if self.nrows() != self.ncols(): - raise TypeError("self must be a square matrices for scalar assignment") - for i in range(self.nrows()): - self.set_unsafe(i,i, R(entries)) - return - - # all entries are given as a long list - if len(entries) != self._nrows * self._ncols: - raise IndexError("The vector of entries has the wrong length.") - - k = 0 - - for i from 0 <= i < self._nrows: - sig_check() - for j from 0 <= j < self._ncols: - e = R(entries[k]) - mzed_write_elem(self._entries, i, j, poly_to_word(e)) - k = k + 1 + ma = MatrixArgs_init(parent, entries) + for t in ma.iter(coerce, True): + se = t + mzed_write_elem(self._entries, se.i, se.j, poly_to_word(se.entry)) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): """ diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index 95c47e5a9e0..68b3c95d9b5 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -49,6 +49,7 @@ from sage.misc.randstate import current_randstate from sage.misc.randstate cimport randstate from sage.misc.cachefunc import cached_method, cached_function from sage.structure.element cimport Element, ModuleElement, RingElement, Matrix +from .args cimport MatrixArgs_init from libc.string cimport memset, memcpy @@ -301,34 +302,22 @@ cdef class Matrix_gfpn_dense(Matrix_dense): MatFree(self.Data) self.Data = NULL - def __init__(self, parent, entries=None, bint copy=False, bint coerce=False, *, bint mutable=True): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=False, *, bint mutable=True): + r""" Matrix extension class using libmeataxe as backend. INPUT: - Instances of this class can be created by providing one of - the following input data, where ``q<255`` is a prime power, - ``m,n`` are non-negative integers, and `a_{11},...,a_{mn}` - can be coerced into ``GF(q)``. Note that a user should - create these instances via the matrix constructors; what - we explain here is for internal use only! + - ``parent`` -- a matrix space over ``GF(q)`` with `q < 255` - - A string ``f`` ==> load matrix from the file named ``f`` - - A matrix space of `m\\times n` matrices over GF(q) and either + - ``entries`` -- see :func:`matrix` - - a list `[a_{11},a_{12},...,a_{1n},a_{21},...,a_{m1},...,a_{mn}]`, - which results in a matrix with the given marks - - ``None``, which is the fastest way to creata a zero matrix. - - an element of GF(q), which results in a diagonal matrix with the - given element on the diagonal. + - ``copy`` -- ignored (for backwards compatibility) - If the optional parameter ``mutable`` is ``False`` (by default, - it is ``True``), the resulting matrix can not be changed, and - it can be used as key in a Python dictionary. + - ``coerce`` -- ignored - The arguments ``copy`` and ``coerce`` are ignored, they are only - here for a common interface with other matrix constructors. + - ``mutable`` -- if False, the resulting matrix can not be + changed, and it can be used as key in a Python dictionary EXAMPLES:: @@ -385,7 +374,6 @@ cdef class Matrix_gfpn_dense(Matrix_dense): Basis matrix: [1 0] [0 1] - """ if isinstance(parent, basestring): # load from file if not parent: @@ -404,63 +392,29 @@ cdef class Matrix_gfpn_dense(Matrix_dense): self._cache = {} return - data = entries - cdef int fl = parent.base_ring().order() - cdef int nr = parent.nrows() - cdef int nc = parent.ncols() - self.Data = MatAlloc(fl, nr, nc) - Matrix_dense.__init__(self, parent) - self._is_immutable = not mutable - B = self._base_ring - self._converter = FieldConverter(B) - if data is None: - return + ma = MatrixArgs_init(parent, entries) + Matrix_dense.__init__(self, ma.space) + cdef long fl = ma.base.order() + cdef long nr = ma.nrows + cdef long nc = ma.ncols - cdef int i,j - cdef FEL f - cdef PTR x - if not isinstance(data,list): - if not data: - return - if self._nrows != self._ncols: - raise ValueError("Cannot initialise non-square matrix from {}".format(data)) - f = FfFromInt(self._converter.field_to_int(self._coerce_element(data))) - x = self.Data.Data - for j in range(self.Data.Noc): - FfInsert(x,j,f) - FfStepPtr(&x) - sig_check() - return + self.Data = MatAlloc(fl, nr, nc) + self._converter = FieldConverter(ma.base) - x = self.Data.Data + cdef PTR x = self.Data.Data assert self.Data.Noc == nc assert self.Data.Nor == nr - if nr==0 or nc==0: - return - if len(data) < nr: - raise ValueError("Expected a list of size at least the number of rows") - cdef list dt, dt_i FfSetField(fl) FfSetNoc(nc) - if isinstance(data[0],list): - # The matrix is given by a list of rows - dt = data - for i in range(nr): - idx = 0 - dt_i = dt[i] - for j in range(nc): - FfInsert(x, j, FfFromInt(self._converter.field_to_int(self._coerce_element(dt_i[j])))) - sig_check() - FfStepPtr(&(x)) - else: - # It is supposed to be a flat list of all entries, sorted by rows - dtnext = data.__iter__().next - for i in range(nr): - for j in range(nc): - bla = self._converter.field_to_int(self._coerce_element(dtnext())) - FfInsert(x, j, FfFromInt(bla)) - sig_check() - FfStepPtr(&(x)) + it = ma.iter(False) + cdef long i,j + for i in range(nr): + for j in range(nc): + v = self._converter.field_to_int(self._coerce_element(next(it))) + FfInsert(x, j, FfFromInt(v)) + FfStepPtr(&x) + + self._is_immutable = not mutable cdef Matrix_gfpn_dense _new(self, Py_ssize_t nrows, Py_ssize_t ncols): r""" diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 0e4c188b64b..e8a1b1cecd9 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -81,6 +81,7 @@ from sage.structure.proof.proof import get_flag as get_proof_flag from sage.misc.randstate cimport randstate, current_randstate from sage.matrix.matrix_rational_dense cimport Matrix_rational_dense +from .args cimport SparseEntry, MatrixArgs_init ######################################################### # PARI C library @@ -245,27 +246,20 @@ cdef class Matrix_integer_dense(Matrix_dense): """ fmpz_mat_clear(self._matrix) - def __init__(self, parent, entries, copy, coerce): + def __init__(self, parent, entries=None, copy=None, bint coerce=True): r""" Initialize a dense matrix over the integers. INPUT: + - ``parent`` -- a matrix space over ``ZZ`` - - ``parent`` - a matrix space + - ``entries`` -- see :func:`matrix` - - ``entries`` - list - create the matrix with those - entries along the rows. - - - ``other`` - a scalar; entries is coerced to an - integer and the diagonal entries of this matrix are set to that - integer. - - - ``coerce`` - whether need to coerce entries to the - integers (program may crash if you get this wrong) - - - ``copy`` - ignored (since integers are immutable) + - ``copy`` -- ignored (for backwards compatibility) + - ``coerce`` -- if False, assume without checking that the + entries are of type :class:`Integer`. EXAMPLES: @@ -318,59 +312,12 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: v.parent() Full MatrixSpace of 1 by 100000 dense matrices over Integer Ring """ - cdef Py_ssize_t i, j, k - cdef bint is_list - cdef Integer x - cdef list entries_list - - if entries is None: - x = ZZ.zero() - is_list = False - elif isinstance(entries, (int,long,Element)): - try: - x = ZZ(entries) - except TypeError: - raise TypeError("unable to coerce entry to an integer") - is_list = False - elif type(entries) is list: - entries_list = entries - is_list = True - else: - entries_list = list(entries) - is_list = True - if is_list: - # Create the matrix whose entries are in the given entry list. - if len(entries_list) != self._nrows * self._ncols: - raise TypeError("entries has the wrong length") - if coerce: - k = 0 - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: - x = ZZ(entries_list[k]) - k += 1 - # todo -- see integer.pyx and the TODO there; perhaps this could be - # sped up by creating a mpz_init_set_sage function. - fmpz_set_mpz(fmpz_mat_entry(self._matrix, i, j),(x).value) - else: - k = 0 - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: - fmpz_set_mpz(fmpz_mat_entry(self._matrix, i,j),( entries_list[k]).value) - k += 1 - else: - # If x is zero, make the zero matrix and be done. - if mpz_sgn(x.value) == 0: - fmpz_mat_zero(self._matrix) - return - - # the matrix must be square: - if self._nrows != self._ncols: - raise TypeError("nonzero scalar matrix must be square") - - # Now we set all the diagonal entries to x and all other entries to 0. - fmpz_mat_zero(self._matrix) - for i from 0 <= i < self._nrows: - fmpz_set_mpz(fmpz_mat_entry(self._matrix,i,i), x.value) + ma = MatrixArgs_init(parent, entries) + cdef Integer z + for t in ma.iter(coerce, True): + se = t + z = se.entry + fmpz_set_mpz(fmpz_mat_entry(self._matrix, se.i, se.j), z.value) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x): """ @@ -1467,10 +1414,8 @@ cdef class Matrix_integer_dense(Matrix_dense): return self._mod_int_c(modulus) cdef _mod_two(self): - cdef Matrix_mod2_dense res - res = Matrix_mod2_dense.__new__(Matrix_mod2_dense, matrix_space.MatrixSpace(IntegerModRing(2), self._nrows, self._ncols, sparse=False), None, None, None) - res.__init__(matrix_space.MatrixSpace(IntegerModRing(2), self._nrows, self._ncols, sparse=False), self.list(), None, None) - return res + MS = matrix_space.MatrixSpace(IntegerModRing(2), self._nrows, self._ncols) + return Matrix_mod2_dense(MS, self, True, True) cdef _mod_int_c(self, mod_int p): from .matrix_modn_dense_float import MAX_MODULUS as MAX_MODULUS_FLOAT diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index e5d088a5404..2876117c669 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -27,17 +27,15 @@ TESTS:: from __future__ import absolute_import from cysignals.memory cimport check_calloc, sig_free -from collections import Iterator, Sequence - + from sage.data_structures.binary_search cimport * from sage.modules.vector_integer_sparse cimport * from sage.modules.vector_modn_sparse cimport * -from cpython.sequence cimport * - from sage.libs.gmp.mpz cimport * from sage.rings.integer cimport Integer from .matrix cimport Matrix +from .args cimport SparseEntry, MatrixArgs_init from .matrix_modn_sparse cimport Matrix_modn_sparse from sage.structure.element cimport ModuleElement, RingElement, Element, Vector @@ -69,77 +67,28 @@ cdef class Matrix_integer_sparse(Matrix_sparse): mpz_vector_clear(&self._matrix[i]) sig_free(self._matrix) - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Create a sparse matrix over the integers. INPUT: - - ``parent`` -- a matrix space - - - ``entries`` -- can be one of the following: + - ``parent`` -- a matrix space over ``ZZ`` - * a Python dictionary whose items have the - form ``(i, j): x``, where ``0 <= i < nrows``, - ``0 <= j < ncols``, and ``x`` is coercible to - an integer. The ``i,j`` entry of ``self`` is - set to ``x``. The ``x``'s can be ``0``. - * Alternatively, entries can be a list of *all* - the entries of the sparse matrix, read - row-by-row from top to bottom (so they would - be mostly 0). + - ``entries`` -- see :func:`matrix` - - ``copy`` -- ignored + - ``copy`` -- ignored (for backwards compatibility) - - ``coerce`` -- ignored + - ``coerce`` -- if False, assume without checking that the + entries are of type :class:`Integer`. """ - cdef Py_ssize_t i, j, k + ma = MatrixArgs_init(parent, entries) cdef Integer z - cdef PyObject** X - - # fill in entries in the dict case - if entries is None: - return - if isinstance(entries, dict): - R = self.base_ring() - for ij, x in entries.iteritems(): - z = R(x) - if z != 0: - i, j = ij # nothing better to do since this is user input, which may be bogus. - if i < 0 or j < 0 or i >= self._nrows or j >= self._ncols: - raise IndexError("invalid entries list") - mpz_vector_set_entry(&self._matrix[i], j, z.value) - - elif isinstance(entries, (Iterator, Sequence)): - if not isinstance(entries, (list, tuple)): - entries = list(entries) - - # Dense input format -- fill in entries - if len(entries) != self._nrows * self._ncols: - raise TypeError("list of entries must be a dictionary of (i,j):x or a dense list of n * m elements") - seq = PySequence_Fast(entries,"expected a list") - X = PySequence_Fast_ITEMS(seq) - k = 0 - R = self.base_ring() - # Get fast access to the entries list. - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: - z = R(X[k]) - if z != 0: - mpz_vector_set_entry(&self._matrix[i], j, z.value) - k = k + 1 - - else: - - # fill in entries in the scalar case - z = Integer(entries) - if z == 0: - return - if self._nrows != self._ncols: - raise TypeError("matrix must be square to initialize with a scalar.") - for i from 0 <= i < self._nrows: - mpz_vector_set_entry(&self._matrix[i], i, z.value) - + for t in ma.iter(coerce, True): + se = t + z = se.entry + if z: + mpz_vector_set_entry(&self._matrix[se.i], se.j, z.value) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, x): mpz_vector_set_entry(&self._matrix[i], j, ( x).value) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index ea0925cde92..482f9dbab24 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -104,8 +104,8 @@ from __future__ import absolute_import from cysignals.memory cimport check_malloc, sig_free from cysignals.signals cimport sig_check, sig_on, sig_str, sig_off -from collections import Iterator, Sequence cimport sage.matrix.matrix_dense as matrix_dense +from .args cimport SparseEntry, MatrixArgs_init from libc.stdio cimport * from sage.structure.element cimport (Matrix, Vector, ModuleElement, Element) @@ -222,16 +222,20 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse mzd_free(self._entries) self._entries = NULL - def __init__(self, parent, entries, copy, coerce): + def __init__(self, parent, entries=None, copy=None, bint coerce=True): """ - Dense matrix over GF(2) constructor. + Construct a dense matrix over GF(2). INPUT: - - ``parent`` - MatrixSpace. - - ``entries`` - may be list or 0 or 1 - - ``copy`` - ignored, elements are always copied - - ``coerce`` - ignored, elements are always coerced to ints % 2 + - ``parent`` -- a matrix space over ``GF(2)`` + + - ``entries`` -- see :func:`matrix` + + - ``copy`` -- ignored (for backwards compatibility) + + - ``coerce`` -- if False, assume without checking that the + entries lie in the base ring EXAMPLES:: @@ -265,32 +269,10 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: Matrix(GF(2),0,2) [] """ - cdef int i,j,e - - if entries is None: - return - - R = self.base_ring() - - # scalar ? - if not isinstance(entries, (Iterator, Sequence)): - if self._nrows and self._ncols and R(entries) == 1: - mzd_set_ui(self._entries, 1) - return - - # all entries are given as a long iterable - if not isinstance(entries, (list, tuple)): - entries = list(entries) - if len(entries) != self._nrows * self._ncols: - raise IndexError("The vector of entries has the wrong length.") - - k = 0 - - for i from 0 <= i < self._nrows: - sig_check() - for j from 0 <= j < self._ncols: - mzd_write_bit(self._entries,i,j, R(entries[k])) - k = k + 1 + ma = MatrixArgs_init(parent, entries) + for t in ma.iter(coerce, True): + se = t + mzd_write_bit(self._entries, se.i, se.j, se.entry) cdef long _hash_(self) except -1: r""" diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index e8f43d89e15..27ac8942815 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -93,8 +93,6 @@ from cpython.bytes cimport * from cysignals.memory cimport check_malloc, check_allocarray, sig_malloc, sig_free from cysignals.signals cimport sig_check, sig_on, sig_off -from collections import Iterator, Sequence - from sage.libs.gmp.mpz cimport * from sage.libs.linbox.fflas cimport fflas_trans_enum, fflas_no_trans, fflas_trans, \ fflas_right, vector, list as std_list @@ -121,6 +119,8 @@ from sage.rings.integer_ring import ZZ from sage.structure.proof.proof import get_flag as get_proof_flag from sage.misc.randstate cimport randstate, current_randstate import sage.matrix.matrix_space as matrix_space +from .args cimport MatrixArgs_init + cdef long num = 1 cdef bint little_endian = ((&num))[0] @@ -417,16 +417,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): """ Create a new matrix. - INPUT: - - - ``parent`` - a matrix space - - - ``entries`` - a list of entries or a scalar - - - ``copy`` - ignroed - - - ``coerce`` - perform modular reduction first? - EXAMPLES:: sage: A = random_matrix(GF(3),1000,1000) @@ -471,24 +461,21 @@ cdef class Matrix_modn_dense_template(Matrix_dense): ....: del C ....: del D ....: _ = gc.collect() - """ - if self._entries == NULL: - return sig_free(self._entries) sig_free(self._matrix) - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Create a new matrix. INPUT: - - ``parent`` - a matrix space + - ``parent`` -- a matrix space - - ``entries`` - a list of entries or a scalar + - ``entries`` -- see :func:`matrix` - - ``copy`` - ignroed + - ``copy`` -- ignored (for backwards compatibility) - ``coerce`` - perform modular reduction first? @@ -518,44 +505,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): [4618989 4618988] [ 4 2639423] """ - cdef celement e - cdef Py_ssize_t i, j, k - cdef celement *v - cdef long p - p = self._base_ring.characteristic() - - R = self.base_ring() - - # scalar? - if not isinstance(entries, (Iterator, Sequence)): - sig_on() - for i in range(self._nrows*self._ncols): - self._entries[i] = 0 - sig_off() - if entries is None: - # zero matrix - pass - else: - e = R(entries) - if e != 0: - for i in range(min(self._nrows, self._ncols)): - self._matrix[i][i] = e - return - - # all entries are given as a long iterable - if not isinstance(entries, (list, tuple)): - entries = list(entries) - if len(entries) != self._nrows * self._ncols: - raise IndexError("The vector of entries has the wrong length.") - - k = 0 - cdef long tmp - - for i in range(self._nrows): - sig_check() + ma = MatrixArgs_init(parent, entries) + cdef long i, j + it = ma.iter(False) + R = ma.base + p = R.characteristic() + for i in range(ma.nrows): v = self._matrix[i] - for j in range(self._ncols): - x = entries[k] + for j in range(ma.ncols): + x = next(it) if type(x) is int: tmp = (x) % p v[j] = tmp + (tmp<0)*p @@ -567,10 +525,9 @@ cdef class Matrix_modn_dense_template(Matrix_dense): else: v[j] = mpz_get_ui((x).value) elif coerce: - v[j] = R(entries[k]) + v[j] = R(x) else: - v[j] = (entries[k]) - k = k + 1 + v[j] = x cdef long _hash_(self) except -1: """ diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index f13537a0d72..f87e984be6b 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -80,15 +80,11 @@ TESTS:: from __future__ import absolute_import -from collections import Iterator, Sequence - from cysignals.memory cimport check_calloc, sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off from sage.modules.vector_modn_sparse cimport * -from cpython.sequence cimport * - from sage.libs.gmp.mpz cimport mpz_init_set_si cimport sage.matrix.matrix as matrix cimport sage.matrix.matrix_sparse as matrix_sparse @@ -100,6 +96,7 @@ from sage.misc.misc import verbose, get_verbose import sage.rings.all as rings from sage.matrix.matrix2 import Matrix as Matrix2 +from .args cimport SparseEntry, MatrixArgs_init from sage.arith.all import is_prime from sage.structure.element import is_Vector @@ -154,76 +151,27 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): clear_c_vector_modint(&self.rows[i]) sig_free(self.rows) - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Create a sparse matrix over the integers modulo ``n``. INPUT: - - ``parent`` -- a matrix space + - ``parent`` -- a matrix space over the integers modulo ``n`` - - ``entries`` -- can be one of the following: + - ``entries`` -- see :func:`matrix` - * a Python dictionary whose items have the - form ``(i, j): x``, where ``0 <= i < nrows``, - ``0 <= j < ncols``, and ``x`` is coercible to - an element of the integers modulo ``n``. - The ``i,j`` entry of ``self`` is - set to ``x``. The ``x``'s can be ``0``. - * Alternatively, entries can be a list of *all* - the entries of the sparse matrix, read - row-by-row from top to bottom (so they would - be mostly 0). + - ``copy`` -- ignored (for backwards compatibility) - - ``copy`` -- ignored - - - ``coerce`` -- ignored + - ``coerce`` -- if False, assume without checking that the + entries lie in the base ring """ - cdef int s, z, p - cdef Py_ssize_t i, j, k - - cdef PyObject** X - - if entries is None: - return - - if isinstance(entries, dict): - # Sparse input format. - R = self._base_ring - for ij, x in entries.iteritems(): - z = R(x) - if z != 0: - i, j = ij # nothing better to do since this is user input, which may be bogus. - if i < 0 or j < 0 or i >= self._nrows or j >= self._ncols: - raise IndexError("invalid entries list") - set_entry(&self.rows[i], j, z) - elif isinstance(entries, (Iterator, Sequence)): - if not isinstance(entries, (list, tuple)): - entries = list(entries) - # Dense input format - if len(entries) != self._nrows * self._ncols: - raise TypeError("list of entries must be a dictionary of (i,j):x or a dense list of n * m elements") - seq = PySequence_Fast(entries,"expected a list") - X = PySequence_Fast_ITEMS(seq) - k = 0 - R = self._base_ring - # Get fast access to the entries list. - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: - z = R(X[k]) - if z != 0: - set_entry(&self.rows[i], j, z) - k = k + 1 - else: - # scalar? - s = int(self._base_ring(entries)) - if s == 0: - return - if self._nrows != self._ncols: - raise TypeError("matrix must be square to initialize with a scalar.") - for i from 0 <= i < self._nrows: - set_entry(&self.rows[i], i, s) - + ma = MatrixArgs_init(parent, entries) + for t in ma.iter(coerce, True): + se = t + z = se.entry + if z: + set_entry(&self.rows[se.i], se.j, z) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): set_entry(&self.rows[i], j, ( value).ivalue) @@ -309,7 +257,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): def _unpickle(self, data, version): if version == 1: - self.__init__(self.parent(), data, copy=False, coerce=False) + self.__init__(self.parent(), data, coerce=False) else: raise ValueError("unknown matrix format") @@ -386,7 +334,6 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): set_entry(&ans.rows[i], j, s) return ans - def _matrix_times_matrix_dense(self, sage.structure.element.Matrix _right): """ Multiply self by the sparse matrix _right, and return the diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 3443189611f..9ba0e68c90e 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -97,6 +97,7 @@ cimport sage.structure.element from sage.structure.sequence import Sequence from sage.rings.rational cimport Rational from .matrix cimport Matrix +from .args cimport SparseEntry, MatrixArgs_init from .matrix_integer_dense cimport Matrix_integer_dense, _lift_crt from sage.structure.element cimport ModuleElement, RingElement, Element, Vector from sage.rings.integer cimport Integer @@ -154,12 +155,21 @@ cdef class Matrix_rational_dense(Matrix_dense): return Matrix_rational_dense.__new__(Matrix_rational_dense, parent, None, None, None) def __dealloc__(self): - sig_on() fmpq_mat_clear(self._matrix) - sig_off() - def __init__(self, parent, entries=None, coerce=True, copy=True): + def __init__(self, parent, entries=None, copy=None, bint coerce=True): r""" + INPUT: + + - ``parent`` -- a matrix space over ``QQ`` + + - ``entries`` -- see :func:`matrix` + + - ``copy`` -- ignored (for backwards compatibility) + + - ``coerce`` -- if False, assume without checking that the + entries are of type :class:`Rational`. + TESTS:: sage: matrix(QQ, 2, 2, 1/4) @@ -169,49 +179,16 @@ cdef class Matrix_rational_dense(Matrix_dense): [ 1/2] [-3/4] [ 0] + sage: matrix(QQ, 2, 2, 0.5) + [1/2 0] + [ 0 1/2] """ - cdef Py_ssize_t i, j, k + ma = MatrixArgs_init(parent, entries) cdef Rational z - - if entries is None: return - if isinstance(entries, xrange): - entries = list(entries) - if isinstance(entries, (list, tuple)): - if len(entries) != self._nrows * self._ncols: - raise TypeError("entries has the wrong length") - - if coerce: - k = 0 - for i in range(self._nrows): - for j in range(self._ncols): - # TODO: Should use an unsafe un-bounds-checked array access here. - sig_check() - z = Rational(entries[k]) - k += 1 - fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), z.value) - else: - k = 0 - for i in range(self._nrows): - for j in range(self._ncols): - # TODO: Should use an unsafe un-bounds-checked array access here. - sig_check() - fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), ( entries[k]).value) - k += 1 - - else: - # is it a scalar? - try: - # Try to coerce entries to a scalar (an integer) - z = Rational(entries) - is_list = False - except TypeError: - raise TypeError("entries must be coercible to a list or integer") - - if z: - if self._nrows != self._ncols: - raise TypeError("nonzero scalar matrix must be square") - for i in range(self._nrows): - fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, i), z.value) + for t in ma.iter(coerce, True): + se = t + z = se.entry + fmpq_set_mpq(fmpq_mat_entry(self._matrix, se.i, se.j), z.value) def matrix_from_columns(self, columns): """ diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index ed2542499ac..f92d8b65b4c 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -29,8 +29,6 @@ from __future__ import absolute_import from cysignals.signals cimport sig_on, sig_off from cysignals.memory cimport sig_malloc, sig_free -from collections import Iterator, Sequence - from sage.data_structures.binary_search cimport * from sage.modules.vector_integer_sparse cimport * from sage.modules.vector_rational_sparse cimport * @@ -40,6 +38,7 @@ from cpython.sequence cimport * from sage.rings.rational cimport Rational from sage.rings.integer cimport Integer from .matrix cimport Matrix +from .args cimport SparseEntry, MatrixArgs_init from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * @@ -86,73 +85,28 @@ cdef class Matrix_rational_sparse(Matrix_sparse): if self._matrix != NULL: sig_free(self._matrix) - def __init__(self, parent, entries, copy, coerce): - """ + def __init__(self, parent, entries=None, copy=None, bint coerce=True): + r""" Create a sparse matrix over the rational numbers. INPUT: - - ``parent`` -- a matrix space - - - ``entries`` -- can be one of the following: + - ``parent`` -- a matrix space over ``QQ`` - * a Python dictionary whose items have the - form ``(i, j): x``, where ``0 <= i < nrows``, - ``0 <= j < ncols``, and ``x`` is coercible to - a rational. The ``i,j`` entry of ``self`` is - set to ``x``. The ``x``'s can be ``0``. - * Alternatively, entries can be a list of *all* - the entries of the sparse matrix, read - row-by-row from top to bottom (so they would - be mostly 0). + - ``entries`` -- see :func:`matrix` - - ``copy`` -- ignored + - ``copy`` -- ignored (for backwards compatibility) - - ``coerce`` -- ignored + - ``coerce`` -- if False, assume without checking that the + entries are of type :class:`Rational`. """ - cdef Py_ssize_t i, j, k + ma = MatrixArgs_init(parent, entries) cdef Rational z - cdef PyObject** X - - if entries is None: - return - # fill in entries in the dict case - if isinstance(entries, dict): - R = self.base_ring() - for ij, x in entries.iteritems(): - z = R(x) - if z != 0: - i, j = ij # nothing better to do since this is user input, which may be bogus. - if i < 0 or j < 0 or i >= self._nrows or j >= self._ncols: - raise IndexError("invalid entries list") - mpq_vector_set_entry(&self._matrix[i], j, z.value) - elif isinstance(entries, (Iterator, Sequence)): - if not isinstance(entries, (list, tuple)): - entries = list(entries) - # Dense input format -- fill in entries - if len(entries) != self._nrows * self._ncols: - raise TypeError("list of entries must be a dictionary of (i,j):x or a dense list of n * m elements") - seq = PySequence_Fast(entries,"expected a list") - X = PySequence_Fast_ITEMS(seq) - k = 0 - R = self.base_ring() - # Get fast access to the entries list. - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: - z = R(X[k]) - if z != 0: - mpq_vector_set_entry(&self._matrix[i], j, z.value) - k = k + 1 - else: - # fill in entries in the scalar case - z = Rational(entries) - if z == 0: - return - if self._nrows != self._ncols: - raise TypeError("matrix must be square to initialize with a scalar.") - for i from 0 <= i < self._nrows: - mpq_vector_set_entry(&self._matrix[i], i, z.value) - + for t in ma.iter(coerce, True): + se = t + z = se.entry + if z: + mpq_vector_set_entry(&self._matrix[se.i], se.j, z.value) cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, x): mpq_vector_set_entry(&self._matrix[i], j, ( x).value) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 51b3473b12a..a526c64316c 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -37,7 +37,6 @@ # System imports import sys -import types import operator # Sage matrix imports @@ -769,7 +768,7 @@ def _copy_zero(self): else: return True - def __call__(self, entries=None, coerce=True, copy=True, sparse = False): + def __call__(self, entries=None, coerce=True, copy=None): """ EXAMPLES:: @@ -849,17 +848,11 @@ def __call__(self, entries=None, coerce=True, copy=True, sparse = False): sage: M(m) Traceback (most recent call last): ... - ValueError: a matrix from - Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - cannot be converted to a matrix in - Full MatrixSpace of 3 by 5 dense matrices over Integer Ring! + ValueError: inconsistent number of rows: should be 3 but got 2 sage: M.matrix(m) Traceback (most recent call last): ... - ValueError: a matrix from - Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - cannot be converted to a matrix in - Full MatrixSpace of 3 by 5 dense matrices over Integer Ring! + ValueError: inconsistent number of rows: should be 3 but got 2 Check that :trac:`15110` is fixed:: @@ -872,7 +865,8 @@ def __call__(self, entries=None, coerce=True, copy=True, sparse = False): sage: MS(t) # given as a scalar matrix [t] """ - return self.matrix(entries, coerce, copy) + MC = self._matrix_class + return MC(self, entries, copy, coerce) def change_ring(self, R): """ @@ -1678,31 +1672,16 @@ def ngens(self): """ return self.dimension() - def matrix(self, x=0, coerce=True, copy=True): + def matrix(self, x=None, **kwds): r""" Create a matrix in ``self``. INPUT: - - ``x`` -- (default: 0) data to construct a new matrix from. Can be one - of the following: - - * 0, corresponding to the zero matrix; - - * 1, corresponding to the identity_matrix; - - * a matrix, whose dimensions must match ``self`` and whose base ring - must be convertible to the base ring of ``self``; - - * a list of entries corresponding to all elements of the new matrix; - - * a list of rows with each row given as an iterable; - - - ``coerce`` -- (default: ``True``) whether to coerce ``x`` into self; + - ``x`` -- data to construct a new matrix from. See :func:`matrix` - - ``copy`` -- (default: ``True``) whether to copy ``x`` during - construction (makes a difference only if ``x`` is a matrix in - ``self``). + - ``coerce`` -- (default: ``True``) if False, assume without + checking that the values in ``x`` lie in the base ring OUTPUT: @@ -1737,10 +1716,7 @@ def matrix(self, x=0, coerce=True, copy=True): sage: M(projection) Traceback (most recent call last): ... - ValueError: a matrix from - Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - cannot be converted to a matrix in - Full MatrixSpace of 3 by 2 dense matrices over Integer Ring! + ValueError: inconsistent number of rows: should be 3 but got 2 If you really want to make from a matrix another matrix of different dimensions, use either transpose method or explicit conversion to a @@ -1774,21 +1750,14 @@ def matrix(self, x=0, coerce=True, copy=True): sage: MS.matrix([MS0([1,2,3,4]), MS0([5,6,7,8])]) Traceback (most recent call last): ... - TypeError: cannot construct an element of - Full MatrixSpace of 4 by 2 dense matrices over Integer Ring - from [[1 2] - [3 4], [5 6] - [7 8]]! + TypeError: unable to coerce to an integer A mixed list of matrices and vectors is prohibited as well:: sage: MS.matrix( [MS0([1,2,3,4])] + list(MS0([5,6,7,8])) ) Traceback (most recent call last): ... - TypeError: cannot construct an element of - Full MatrixSpace of 4 by 2 dense matrices over Integer Ring - from [[1 2] - [3 4], (5, 6), (7, 8)]! + TypeError: unable to coerce to an integer Check that :trac:`13302` is fixed:: @@ -1817,76 +1786,7 @@ def matrix(self, x=0, coerce=True, copy=True): sage: md.parent() is MS True """ - if x is None or isinstance(x, (int, integer.Integer)) and x == 0: - if self._copy_zero: # faster to copy than to create a new one. - return self.zero_matrix().__copy__() - else: - return self._matrix_class(self, None, False, False) - if isinstance(x, (int, integer.Integer)) and x == 1: - return self.identity_matrix().__copy__() - m, n, sparse = self.__nrows, self.__ncols, self.__is_sparse - if is_Matrix(x): - if x.parent() is self: - if x.is_immutable(): - return x - else: - return x.__copy__() - else: - if x.nrows() == m and x.ncols() == n: - if (x.base_ring() == self.base_ring() - and x.is_sparse() and not sparse): - # If x is sparse and large, calling x.dense_matrix() - # is much faster than calling x.list(). See #20470. - return x.dense_matrix() - x = x.list() - else: - raise ValueError("a matrix from %s cannot be converted to " - "a matrix in %s!" % (x.parent(), self)) - if is_MatrixGroupElement(x) or isinstance(x, ArithmeticSubgroupElement): - return self(x.matrix(), copy=False) - if isinstance(x, (types.GeneratorType, range)): - x = list(x) - if not sparse and isinstance(x, dict): - x = dict_to_list(x, m, n) - coerce = True - copy = False - MC = self._matrix_class - if isinstance(x, (list, tuple)) and x: - if len(x) == m: # Try unpacking elements - unpacked = True - new_x = [] - for v in x: - l = len(new_x) - try: - from sage.structure.element import is_Vector - if isinstance(v, (list, tuple)) or is_Vector(v): - # The isinstance check should prevent the "flattening" - # of v if v is an iterable but not meant to be - # iterated (e.g., an element of a combinatorial free - # module). - new_x.extend(v) - else: - raise TypeError - if len(new_x) - l != n: - raise TypeError - except TypeError: - unpacked = False - if unpacked: - try: - if sparse: - return MC(self, list_to_dict(new_x, m, n), - copy=False, coerce=coerce) - else: - return MC(self, new_x, copy=False, coerce=coerce) - except (TypeError, ValueError): - pass - if len(x) != m * n: - raise TypeError("cannot construct an element of {} from {}!" - .format(self, x)) - if sparse: - x = list_to_dict(x, m, n) - copy = False - return MC(self, x, copy=copy, coerce=coerce) + return self(x, **kwds) def matrix_space(self, nrows=None, ncols=None, sparse=False): """ diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 46a15e53eb4..a832dfb3fcd 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -1,10 +1,6 @@ """ Symbolic matrices -Matrices with symbolic entries. The underlying representation is a -pointer to a Maxima object. - - EXAMPLES:: sage: matrix(SR, 2, 2, range(4)) @@ -146,6 +142,16 @@ Conversion to Maxima:: sage: m._maxima_() matrix([sqrt(2),3],[%pi,%e]) +TESTS: + +Check that :trac:`12778` is fixed:: + + sage: M = Matrix([[1, 0.9, 1/5, x^2], [2, 1.9, 2/5, x^3], [3, 2.9, 3/5, x^4]]); M + [ 1 0.900000000000000 1/5 x^2] + [ 2 1.90000000000000 2/5 x^3] + [ 3 2.90000000000000 3/5 x^4] + sage: parent(M) + Full MatrixSpace of 3 by 4 dense matrices over Symbolic Ring """ from __future__ import absolute_import diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index c698ba5e97e..c31888b05fd 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -3354,7 +3354,7 @@ def vector_on_axis_rotation_matrix(v, i, ring=None): [ 0.00 -0.93 0.22 0.30] [ 0.00 0.00 -0.80 0.60] sage: vector_on_axis_rotation_matrix(v, 0, ring=RealField(10)) * v - (5.5, 0.00098, 0.00098, 0.00) + (5.5, 0.0020, 0.00098, 0.00) AUTHORS: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 575ad98b797..652b9322fd3 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2004,7 +2004,7 @@ def sage_getsourcelines(obj): sage: from sage.misc.sageinspect import sage_getsourcelines sage: sage_getsourcelines(matrix)[1] - 28 + 22 sage: sage_getsourcelines(matrix)[0][0][6:] 'MatrixFactory(object):\n' diff --git a/src/sage/modular/arithgroup/congroup_sl2z.py b/src/sage/modular/arithgroup/congroup_sl2z.py index a4ed774813b..cdb6da3e6c6 100644 --- a/src/sage/modular/arithgroup/congroup_sl2z.py +++ b/src/sage/modular/arithgroup/congroup_sl2z.py @@ -105,11 +105,10 @@ def _element_constructor_(self, x, check=True): sage: SL2Z([2, 0, 0, 2], check=False) # don't do this! [2 0] [0 2] - sage: SL2Z([1, QQ, False], check=False) # don't do this either! + sage: SL2Z([1, QQ, False]) Traceback (most recent call last): ... - TypeError: cannot construct an element of Full MatrixSpace of 2 by 2 - dense matrices over Integer Ring from [1, Rational Field, False]! + TypeError: unable to coerce to an integer """ return ArithmeticSubgroupElement(self, x, check=check) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 230f1421170..1c0fa0fd133 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -95,6 +95,11 @@ TESTS:: sage: p = matrix(R,[[84, 97, 55, 58, 51]]) sage: 2*p.row(0) (168, 194, 110, 116, 102) + +This is a test from :trac:`20211`:: + + sage: MatrixSpace(ZZ, 1, 1)(vector([1])) + [1] """ #***************************************************************************** @@ -1061,33 +1066,6 @@ cdef class FreeModuleElement(Vector): # abstract base class return self return self.change_ring(R) - def _matrix_(self, R=None): - r""" - Return self as a row matrix. - - EXAMPLES:: - - sage: v = vector(ZZ, [2, 12, 22]) - sage: v._matrix_() - [ 2 12 22] - sage: v._matrix_(GF(7)) - [2 5 1] - sage: v._matrix_(ZZ['x', 'y']) - [ 2 12 22] - sage: v = ((ZZ^3)*(1/2))( (1/2, -1, 3/2) ) - sage: v._matrix_() - [1/2 -1 3/2] - sage: v._matrix_(ZZ) - Traceback (most recent call last): - ... - TypeError: no conversion of this rational to integer - """ - if R is None: - R = self.coordinate_ring() - sparse = self.is_sparse() - from sage.matrix.constructor import matrix - return matrix(R, [list(self)], sparse=sparse) - def _sage_input_(self, sib, coerce): r""" Produce an expression which will reproduce this value when evaluated. @@ -1336,7 +1314,10 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: w.parent() Full MatrixSpace of 1 by 0 dense matrices over Integer Ring """ - return self._matrix_(R=None) + from sage.matrix.args import MatrixArgs + ma = MatrixArgs(self._parent._base, 1, self.degree(), + list(self), sparse=self.is_sparse()) + return ma.matrix() def column(self): r""" @@ -1405,23 +1386,10 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: w.parent() Full MatrixSpace of 0 by 1 dense matrices over Integer Ring """ - return self._matrix_(R=None).transpose() - - def _hash(self): - """ - Return hash of an immutable form of self (works even if self - is mutable). - - EXAMPLES:: - - sage: v = vector([1,2/3,pi]) - sage: v.__hash__() - Traceback (most recent call last): - ... - TypeError: mutable vectors are unhashable - sage: v._hash() # random output - """ - return hash(tuple(list(self))) + from sage.matrix.args import MatrixArgs + ma = MatrixArgs(self._parent._base, self.degree(), 1, + [(x,) for x in self], sparse=self.is_sparse()) + return ma.matrix() def __copy__(self): """ diff --git a/src/sage/modules/matrix_morphism.py b/src/sage/modules/matrix_morphism.py index 17a1d6e2900..e91c4970440 100644 --- a/src/sage/modules/matrix_morphism.py +++ b/src/sage/modules/matrix_morphism.py @@ -534,10 +534,7 @@ def __add__(self, right): sage: phi + psi Traceback (most recent call last): ... - ValueError: a matrix from - Full MatrixSpace of 3 by 3 dense matrices over Integer Ring - cannot be converted to a matrix in - Full MatrixSpace of 2 by 2 dense matrices over Integer Ring! + ValueError: inconsistent number of rows: should be 2 but got 3 """ # TODO: move over to any coercion model! if not isinstance(right, MatrixMorphism): diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 29692f3a25d..e31619c8339 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1758,7 +1758,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): sage: G(p) Traceback (most recent call last): ... - TypeError: entries must be coercible to a list or integer + TypeError: unable to convert (1,3,2) to a rational sage: phi = S3.hom(lambda p: G(p.matrix()), codomain = G) sage: phi(p) [0 0 1]