123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 |
- from collections import defaultdict
- from operator import index as index_
- from sympy.core.expr import Expr
- from sympy.core.kind import Kind, NumberKind, UndefinedKind
- from sympy.core.numbers import Integer, Rational
- from sympy.core.sympify import _sympify, SympifyError
- from sympy.core.singleton import S
- from sympy.polys.domains import ZZ, QQ, EXRAW
- from sympy.polys.matrices import DomainMatrix
- from sympy.utilities.exceptions import sympy_deprecation_warning
- from sympy.utilities.iterables import is_sequence
- from sympy.utilities.misc import filldedent
- from .common import classof
- from .matrices import MatrixBase, MatrixKind, ShapeError
- class RepMatrix(MatrixBase):
- """Matrix implementation based on DomainMatrix as an internal representation.
- The RepMatrix class is a superclass for Matrix, ImmutableMatrix,
- SparseMatrix and ImmutableSparseMatrix which are the main usable matrix
- classes in SymPy. Most methods on this class are simply forwarded to
- DomainMatrix.
- """
- #
- # MatrixBase is the common superclass for all of the usable explicit matrix
- # classes in SymPy. The idea is that MatrixBase is an abstract class though
- # and that subclasses will implement the lower-level methods.
- #
- # RepMatrix is a subclass of MatrixBase that uses DomainMatrix as an
- # internal representation and delegates lower-level methods to
- # DomainMatrix. All of SymPy's standard explicit matrix classes subclass
- # RepMatrix and so use DomainMatrix internally.
- #
- # A RepMatrix uses an internal DomainMatrix with the domain set to ZZ, QQ
- # or EXRAW. The EXRAW domain is equivalent to the previous implementation
- # of Matrix that used Expr for the elements. The ZZ and QQ domains are used
- # when applicable just because they are compatible with the previous
- # implementation but are much more efficient. Other domains such as QQ[x]
- # are not used because they differ from Expr in some way (e.g. automatic
- # expansion of powers and products).
- #
- _rep: DomainMatrix
- def __eq__(self, other):
- # Skip sympify for mutable matrices...
- if not isinstance(other, RepMatrix):
- try:
- other = _sympify(other)
- except SympifyError:
- return NotImplemented
- if not isinstance(other, RepMatrix):
- return NotImplemented
- return self._rep.unify_eq(other._rep)
- @classmethod
- def _unify_element_sympy(cls, rep, element):
- domain = rep.domain
- element = _sympify(element)
- if domain != EXRAW:
- # The domain can only be ZZ, QQ or EXRAW
- if element.is_Integer:
- new_domain = domain
- elif element.is_Rational:
- new_domain = QQ
- else:
- new_domain = EXRAW
- # XXX: This converts the domain for all elements in the matrix
- # which can be slow. This happens e.g. if __setitem__ changes one
- # element to something that does not fit in the domain
- if new_domain != domain:
- rep = rep.convert_to(new_domain)
- domain = new_domain
- if domain != EXRAW:
- element = new_domain.from_sympy(element)
- if domain == EXRAW and not isinstance(element, Expr):
- sympy_deprecation_warning(
- """
- non-Expr objects in a Matrix is deprecated. Matrix represents
- a mathematical matrix. To represent a container of non-numeric
- entities, Use a list of lists, TableForm, NumPy array, or some
- other data structure instead.
- """,
- deprecated_since_version="1.9",
- active_deprecations_target="deprecated-non-expr-in-matrix",
- stacklevel=4,
- )
- return rep, element
- @classmethod
- def _dod_to_DomainMatrix(cls, rows, cols, dod, types):
- if not all(issubclass(typ, Expr) for typ in types):
- sympy_deprecation_warning(
- """
- non-Expr objects in a Matrix is deprecated. Matrix represents
- a mathematical matrix. To represent a container of non-numeric
- entities, Use a list of lists, TableForm, NumPy array, or some
- other data structure instead.
- """,
- deprecated_since_version="1.9",
- active_deprecations_target="deprecated-non-expr-in-matrix",
- stacklevel=6,
- )
- rep = DomainMatrix(dod, (rows, cols), EXRAW)
- if all(issubclass(typ, Rational) for typ in types):
- if all(issubclass(typ, Integer) for typ in types):
- rep = rep.convert_to(ZZ)
- else:
- rep = rep.convert_to(QQ)
- return rep
- @classmethod
- def _flat_list_to_DomainMatrix(cls, rows, cols, flat_list):
- elements_dod = defaultdict(dict)
- for n, element in enumerate(flat_list):
- if element != 0:
- i, j = divmod(n, cols)
- elements_dod[i][j] = element
- types = set(map(type, flat_list))
- rep = cls._dod_to_DomainMatrix(rows, cols, elements_dod, types)
- return rep
- @classmethod
- def _smat_to_DomainMatrix(cls, rows, cols, smat):
- elements_dod = defaultdict(dict)
- for (i, j), element in smat.items():
- if element != 0:
- elements_dod[i][j] = element
- types = set(map(type, smat.values()))
- rep = cls._dod_to_DomainMatrix(rows, cols, elements_dod, types)
- return rep
- def flat(self):
- return self._rep.to_sympy().to_list_flat()
- def _eval_tolist(self):
- return self._rep.to_sympy().to_list()
- def _eval_todok(self):
- return self._rep.to_sympy().to_dok()
- def _eval_values(self):
- return list(self.todok().values())
- def copy(self):
- return self._fromrep(self._rep.copy())
- @property
- def kind(self) -> MatrixKind:
- domain = self._rep.domain
- element_kind: Kind
- if domain in (ZZ, QQ):
- element_kind = NumberKind
- elif domain == EXRAW:
- kinds = set(e.kind for e in self.values())
- if len(kinds) == 1:
- [element_kind] = kinds
- else:
- element_kind = UndefinedKind
- else: # pragma: no cover
- raise RuntimeError("Domain should only be ZZ, QQ or EXRAW")
- return MatrixKind(element_kind)
- def _eval_has(self, *patterns):
- # if the matrix has any zeros, see if S.Zero
- # has the pattern. If _smat is full length,
- # the matrix has no zeros.
- zhas = False
- dok = self.todok()
- if len(dok) != self.rows*self.cols:
- zhas = S.Zero.has(*patterns)
- return zhas or any(value.has(*patterns) for value in dok.values())
- def _eval_is_Identity(self):
- if not all(self[i, i] == 1 for i in range(self.rows)):
- return False
- return len(self.todok()) == self.rows
- def _eval_is_symmetric(self, simpfunc):
- diff = (self - self.T).applyfunc(simpfunc)
- return len(diff.values()) == 0
- def _eval_transpose(self):
- """Returns the transposed SparseMatrix of this SparseMatrix.
- Examples
- ========
- >>> from sympy import SparseMatrix
- >>> a = SparseMatrix(((1, 2), (3, 4)))
- >>> a
- Matrix([
- [1, 2],
- [3, 4]])
- >>> a.T
- Matrix([
- [1, 3],
- [2, 4]])
- """
- return self._fromrep(self._rep.transpose())
- def _eval_col_join(self, other):
- return self._fromrep(self._rep.vstack(other._rep))
- def _eval_row_join(self, other):
- return self._fromrep(self._rep.hstack(other._rep))
- def _eval_extract(self, rowsList, colsList):
- return self._fromrep(self._rep.extract(rowsList, colsList))
- def __getitem__(self, key):
- return _getitem_RepMatrix(self, key)
- @classmethod
- def _eval_zeros(cls, rows, cols):
- rep = DomainMatrix.zeros((rows, cols), ZZ)
- return cls._fromrep(rep)
- @classmethod
- def _eval_eye(cls, rows, cols):
- rep = DomainMatrix.eye((rows, cols), ZZ)
- return cls._fromrep(rep)
- def _eval_add(self, other):
- return classof(self, other)._fromrep(self._rep + other._rep)
- def _eval_matrix_mul(self, other):
- return classof(self, other)._fromrep(self._rep * other._rep)
- def _eval_matrix_mul_elementwise(self, other):
- selfrep, otherrep = self._rep.unify(other._rep)
- newrep = selfrep.mul_elementwise(otherrep)
- return classof(self, other)._fromrep(newrep)
- def _eval_scalar_mul(self, other):
- rep, other = self._unify_element_sympy(self._rep, other)
- return self._fromrep(rep.scalarmul(other))
- def _eval_scalar_rmul(self, other):
- rep, other = self._unify_element_sympy(self._rep, other)
- return self._fromrep(rep.rscalarmul(other))
- def _eval_Abs(self):
- return self._fromrep(self._rep.applyfunc(abs))
- def _eval_conjugate(self):
- rep = self._rep
- domain = rep.domain
- if domain in (ZZ, QQ):
- return self.copy()
- else:
- return self._fromrep(rep.applyfunc(lambda e: e.conjugate()))
- def equals(self, other, failing_expression=False):
- """Applies ``equals`` to corresponding elements of the matrices,
- trying to prove that the elements are equivalent, returning True
- if they are, False if any pair is not, and None (or the first
- failing expression if failing_expression is True) if it cannot
- be decided if the expressions are equivalent or not. This is, in
- general, an expensive operation.
- Examples
- ========
- >>> from sympy import Matrix
- >>> from sympy.abc import x
- >>> A = Matrix([x*(x - 1), 0])
- >>> B = Matrix([x**2 - x, 0])
- >>> A == B
- False
- >>> A.simplify() == B.simplify()
- True
- >>> A.equals(B)
- True
- >>> A.equals(2)
- False
- See Also
- ========
- sympy.core.expr.Expr.equals
- """
- if self.shape != getattr(other, 'shape', None):
- return False
- rv = True
- for i in range(self.rows):
- for j in range(self.cols):
- ans = self[i, j].equals(other[i, j], failing_expression)
- if ans is False:
- return False
- elif ans is not True and rv is True:
- rv = ans
- return rv
- class MutableRepMatrix(RepMatrix):
- """Mutable matrix based on DomainMatrix as the internal representation"""
- #
- # MutableRepMatrix is a subclass of RepMatrix that adds/overrides methods
- # to make the instances mutable. MutableRepMatrix is a superclass for both
- # MutableDenseMatrix and MutableSparseMatrix.
- #
- is_zero = False
- def __new__(cls, *args, **kwargs):
- return cls._new(*args, **kwargs)
- @classmethod
- def _new(cls, *args, copy=True, **kwargs):
- if copy is False:
- # The input was rows, cols, [list].
- # It should be used directly without creating a copy.
- if len(args) != 3:
- raise TypeError("'copy=False' requires a matrix be initialized as rows,cols,[list]")
- rows, cols, flat_list = args
- else:
- rows, cols, flat_list = cls._handle_creation_inputs(*args, **kwargs)
- flat_list = list(flat_list) # create a shallow copy
- rep = cls._flat_list_to_DomainMatrix(rows, cols, flat_list)
- return cls._fromrep(rep)
- @classmethod
- def _fromrep(cls, rep):
- obj = super().__new__(cls)
- obj.rows, obj.cols = rep.shape
- obj._rep = rep
- return obj
- def copy(self):
- return self._fromrep(self._rep.copy())
- def as_mutable(self):
- return self.copy()
- def __setitem__(self, key, value):
- """
- Examples
- ========
- >>> from sympy import Matrix, I, zeros, ones
- >>> m = Matrix(((1, 2+I), (3, 4)))
- >>> m
- Matrix([
- [1, 2 + I],
- [3, 4]])
- >>> m[1, 0] = 9
- >>> m
- Matrix([
- [1, 2 + I],
- [9, 4]])
- >>> m[1, 0] = [[0, 1]]
- To replace row r you assign to position r*m where m
- is the number of columns:
- >>> M = zeros(4)
- >>> m = M.cols
- >>> M[3*m] = ones(1, m)*2; M
- Matrix([
- [0, 0, 0, 0],
- [0, 0, 0, 0],
- [0, 0, 0, 0],
- [2, 2, 2, 2]])
- And to replace column c you can assign to position c:
- >>> M[2] = ones(m, 1)*4; M
- Matrix([
- [0, 0, 4, 0],
- [0, 0, 4, 0],
- [0, 0, 4, 0],
- [2, 2, 4, 2]])
- """
- rv = self._setitem(key, value)
- if rv is not None:
- i, j, value = rv
- self._rep, value = self._unify_element_sympy(self._rep, value)
- self._rep.rep.setitem(i, j, value)
- def _eval_col_del(self, col):
- self._rep = DomainMatrix.hstack(self._rep[:,:col], self._rep[:,col+1:])
- self.cols -= 1
- def _eval_row_del(self, row):
- self._rep = DomainMatrix.vstack(self._rep[:row,:], self._rep[row+1:, :])
- self.rows -= 1
- def _eval_col_insert(self, col, other):
- other = self._new(other)
- return self.hstack(self[:,:col], other, self[:,col:])
- def _eval_row_insert(self, row, other):
- other = self._new(other)
- return self.vstack(self[:row,:], other, self[row:,:])
- def col_op(self, j, f):
- """In-place operation on col j using two-arg functor whose args are
- interpreted as (self[i, j], i).
- Examples
- ========
- >>> from sympy import eye
- >>> M = eye(3)
- >>> M.col_op(1, lambda v, i: v + 2*M[i, 0]); M
- Matrix([
- [1, 2, 0],
- [0, 1, 0],
- [0, 0, 1]])
- See Also
- ========
- col
- row_op
- """
- for i in range(self.rows):
- self[i, j] = f(self[i, j], i)
- def col_swap(self, i, j):
- """Swap the two given columns of the matrix in-place.
- Examples
- ========
- >>> from sympy import Matrix
- >>> M = Matrix([[1, 0], [1, 0]])
- >>> M
- Matrix([
- [1, 0],
- [1, 0]])
- >>> M.col_swap(0, 1)
- >>> M
- Matrix([
- [0, 1],
- [0, 1]])
- See Also
- ========
- col
- row_swap
- """
- for k in range(0, self.rows):
- self[k, i], self[k, j] = self[k, j], self[k, i]
- def row_op(self, i, f):
- """In-place operation on row ``i`` using two-arg functor whose args are
- interpreted as ``(self[i, j], j)``.
- Examples
- ========
- >>> from sympy import eye
- >>> M = eye(3)
- >>> M.row_op(1, lambda v, j: v + 2*M[0, j]); M
- Matrix([
- [1, 0, 0],
- [2, 1, 0],
- [0, 0, 1]])
- See Also
- ========
- row
- zip_row_op
- col_op
- """
- for j in range(self.cols):
- self[i, j] = f(self[i, j], j)
- def row_swap(self, i, j):
- """Swap the two given rows of the matrix in-place.
- Examples
- ========
- >>> from sympy import Matrix
- >>> M = Matrix([[0, 1], [1, 0]])
- >>> M
- Matrix([
- [0, 1],
- [1, 0]])
- >>> M.row_swap(0, 1)
- >>> M
- Matrix([
- [1, 0],
- [0, 1]])
- See Also
- ========
- row
- col_swap
- """
- for k in range(0, self.cols):
- self[i, k], self[j, k] = self[j, k], self[i, k]
- def zip_row_op(self, i, k, f):
- """In-place operation on row ``i`` using two-arg functor whose args are
- interpreted as ``(self[i, j], self[k, j])``.
- Examples
- ========
- >>> from sympy import eye
- >>> M = eye(3)
- >>> M.zip_row_op(1, 0, lambda v, u: v + 2*u); M
- Matrix([
- [1, 0, 0],
- [2, 1, 0],
- [0, 0, 1]])
- See Also
- ========
- row
- row_op
- col_op
- """
- for j in range(self.cols):
- self[i, j] = f(self[i, j], self[k, j])
- def copyin_list(self, key, value):
- """Copy in elements from a list.
- Parameters
- ==========
- key : slice
- The section of this matrix to replace.
- value : iterable
- The iterable to copy values from.
- Examples
- ========
- >>> from sympy import eye
- >>> I = eye(3)
- >>> I[:2, 0] = [1, 2] # col
- >>> I
- Matrix([
- [1, 0, 0],
- [2, 1, 0],
- [0, 0, 1]])
- >>> I[1, :2] = [[3, 4]]
- >>> I
- Matrix([
- [1, 0, 0],
- [3, 4, 0],
- [0, 0, 1]])
- See Also
- ========
- copyin_matrix
- """
- if not is_sequence(value):
- raise TypeError("`value` must be an ordered iterable, not %s." % type(value))
- return self.copyin_matrix(key, type(self)(value))
- def copyin_matrix(self, key, value):
- """Copy in values from a matrix into the given bounds.
- Parameters
- ==========
- key : slice
- The section of this matrix to replace.
- value : Matrix
- The matrix to copy values from.
- Examples
- ========
- >>> from sympy import Matrix, eye
- >>> M = Matrix([[0, 1], [2, 3], [4, 5]])
- >>> I = eye(3)
- >>> I[:3, :2] = M
- >>> I
- Matrix([
- [0, 1, 0],
- [2, 3, 0],
- [4, 5, 1]])
- >>> I[0, 1] = M
- >>> I
- Matrix([
- [0, 0, 1],
- [2, 2, 3],
- [4, 4, 5]])
- See Also
- ========
- copyin_list
- """
- rlo, rhi, clo, chi = self.key2bounds(key)
- shape = value.shape
- dr, dc = rhi - rlo, chi - clo
- if shape != (dr, dc):
- raise ShapeError(filldedent("The Matrix `value` doesn't have the "
- "same dimensions "
- "as the in sub-Matrix given by `key`."))
- for i in range(value.rows):
- for j in range(value.cols):
- self[i + rlo, j + clo] = value[i, j]
- def fill(self, value):
- """Fill self with the given value.
- Notes
- =====
- Unless many values are going to be deleted (i.e. set to zero)
- this will create a matrix that is slower than a dense matrix in
- operations.
- Examples
- ========
- >>> from sympy import SparseMatrix
- >>> M = SparseMatrix.zeros(3); M
- Matrix([
- [0, 0, 0],
- [0, 0, 0],
- [0, 0, 0]])
- >>> M.fill(1); M
- Matrix([
- [1, 1, 1],
- [1, 1, 1],
- [1, 1, 1]])
- See Also
- ========
- zeros
- ones
- """
- value = _sympify(value)
- if not value:
- self._rep = DomainMatrix.zeros(self.shape, EXRAW)
- else:
- elements_dod = {i: {j: value for j in range(self.cols)} for i in range(self.rows)}
- self._rep = DomainMatrix(elements_dod, self.shape, EXRAW)
- def _getitem_RepMatrix(self, key):
- """Return portion of self defined by key. If the key involves a slice
- then a list will be returned (if key is a single slice) or a matrix
- (if key was a tuple involving a slice).
- Examples
- ========
- >>> from sympy import Matrix, I
- >>> m = Matrix([
- ... [1, 2 + I],
- ... [3, 4 ]])
- If the key is a tuple that doesn't involve a slice then that element
- is returned:
- >>> m[1, 0]
- 3
- When a tuple key involves a slice, a matrix is returned. Here, the
- first column is selected (all rows, column 0):
- >>> m[:, 0]
- Matrix([
- [1],
- [3]])
- If the slice is not a tuple then it selects from the underlying
- list of elements that are arranged in row order and a list is
- returned if a slice is involved:
- >>> m[0]
- 1
- >>> m[::2]
- [1, 3]
- """
- if isinstance(key, tuple):
- i, j = key
- try:
- return self._rep.getitem_sympy(index_(i), index_(j))
- except (TypeError, IndexError):
- if (isinstance(i, Expr) and not i.is_number) or (isinstance(j, Expr) and not j.is_number):
- if ((j < 0) is True) or ((j >= self.shape[1]) is True) or\
- ((i < 0) is True) or ((i >= self.shape[0]) is True):
- raise ValueError("index out of boundary")
- from sympy.matrices.expressions.matexpr import MatrixElement
- return MatrixElement(self, i, j)
- if isinstance(i, slice):
- i = range(self.rows)[i]
- elif is_sequence(i):
- pass
- else:
- i = [i]
- if isinstance(j, slice):
- j = range(self.cols)[j]
- elif is_sequence(j):
- pass
- else:
- j = [j]
- return self.extract(i, j)
- else:
- # Index/slice like a flattened list
- rows, cols = self.shape
- # Raise the appropriate exception:
- if not rows * cols:
- return [][key]
- rep = self._rep.rep
- domain = rep.domain
- is_slice = isinstance(key, slice)
- if is_slice:
- values = [rep.getitem(*divmod(n, cols)) for n in range(rows * cols)[key]]
- else:
- values = [rep.getitem(*divmod(index_(key), cols))]
- if domain != EXRAW:
- to_sympy = domain.to_sympy
- values = [to_sympy(val) for val in values]
- if is_slice:
- return values
- else:
- return values[0]
|