1792 lines
47 KiB
Python
1792 lines
47 KiB
Python
|
"""
|
|||
|
|
|||
|
Module for the DomainMatrix class.
|
|||
|
|
|||
|
A DomainMatrix represents a matrix with elements that are in a particular
|
|||
|
Domain. Each DomainMatrix internally wraps a DDM which is used for the
|
|||
|
lower-level operations. The idea is that the DomainMatrix class provides the
|
|||
|
convenience routines for converting between Expr and the poly domains as well
|
|||
|
as unifying matrices with different domains.
|
|||
|
|
|||
|
"""
|
|||
|
from functools import reduce
|
|||
|
from typing import Union as tUnion, Tuple as tTuple
|
|||
|
|
|||
|
from sympy.core.sympify import _sympify
|
|||
|
|
|||
|
from ..domains import Domain
|
|||
|
|
|||
|
from ..constructor import construct_domain
|
|||
|
|
|||
|
from .exceptions import (DMNonSquareMatrixError, DMShapeError,
|
|||
|
DMDomainError, DMFormatError, DMBadInputError,
|
|||
|
DMNotAField)
|
|||
|
|
|||
|
from .ddm import DDM
|
|||
|
|
|||
|
from .sdm import SDM
|
|||
|
|
|||
|
from .domainscalar import DomainScalar
|
|||
|
|
|||
|
from sympy.polys.domains import ZZ, EXRAW, QQ
|
|||
|
|
|||
|
|
|||
|
def DM(rows, domain):
|
|||
|
"""Convenient alias for DomainMatrix.from_list
|
|||
|
|
|||
|
Examples
|
|||
|
=======
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DM
|
|||
|
>>> DM([[1, 2], [3, 4]], ZZ)
|
|||
|
DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
|
|||
|
|
|||
|
See also
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix.from_list
|
|||
|
"""
|
|||
|
return DomainMatrix.from_list(rows, domain)
|
|||
|
|
|||
|
|
|||
|
class DomainMatrix:
|
|||
|
r"""
|
|||
|
Associate Matrix with :py:class:`~.Domain`
|
|||
|
|
|||
|
Explanation
|
|||
|
===========
|
|||
|
|
|||
|
DomainMatrix uses :py:class:`~.Domain` for its internal representation
|
|||
|
which makes it faster than the SymPy Matrix class (currently) for many
|
|||
|
common operations, but this advantage makes it not entirely compatible
|
|||
|
with Matrix. DomainMatrix are analogous to numpy arrays with "dtype".
|
|||
|
In the DomainMatrix, each element has a domain such as :ref:`ZZ`
|
|||
|
or :ref:`QQ(a)`.
|
|||
|
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
Creating a DomainMatrix from the existing Matrix class:
|
|||
|
|
|||
|
>>> from sympy import Matrix
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> Matrix1 = Matrix([
|
|||
|
... [1, 2],
|
|||
|
... [3, 4]])
|
|||
|
>>> A = DomainMatrix.from_Matrix(Matrix1)
|
|||
|
>>> A
|
|||
|
DomainMatrix({0: {0: 1, 1: 2}, 1: {0: 3, 1: 4}}, (2, 2), ZZ)
|
|||
|
|
|||
|
Directly forming a DomainMatrix:
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> A
|
|||
|
DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
DDM
|
|||
|
SDM
|
|||
|
Domain
|
|||
|
Poly
|
|||
|
|
|||
|
"""
|
|||
|
rep: tUnion[SDM, DDM]
|
|||
|
shape: tTuple[int, int]
|
|||
|
domain: Domain
|
|||
|
|
|||
|
def __new__(cls, rows, shape, domain, *, fmt=None):
|
|||
|
"""
|
|||
|
Creates a :py:class:`~.DomainMatrix`.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
rows : Represents elements of DomainMatrix as list of lists
|
|||
|
shape : Represents dimension of DomainMatrix
|
|||
|
domain : Represents :py:class:`~.Domain` of DomainMatrix
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
TypeError
|
|||
|
If any of rows, shape and domain are not provided
|
|||
|
|
|||
|
"""
|
|||
|
if isinstance(rows, (DDM, SDM)):
|
|||
|
raise TypeError("Use from_rep to initialise from SDM/DDM")
|
|||
|
elif isinstance(rows, list):
|
|||
|
rep = DDM(rows, shape, domain)
|
|||
|
elif isinstance(rows, dict):
|
|||
|
rep = SDM(rows, shape, domain)
|
|||
|
else:
|
|||
|
msg = "Input should be list-of-lists or dict-of-dicts"
|
|||
|
raise TypeError(msg)
|
|||
|
|
|||
|
if fmt is not None:
|
|||
|
if fmt == 'sparse':
|
|||
|
rep = rep.to_sdm()
|
|||
|
elif fmt == 'dense':
|
|||
|
rep = rep.to_ddm()
|
|||
|
else:
|
|||
|
raise ValueError("fmt should be 'sparse' or 'dense'")
|
|||
|
|
|||
|
return cls.from_rep(rep)
|
|||
|
|
|||
|
def __getnewargs__(self):
|
|||
|
rep = self.rep
|
|||
|
if isinstance(rep, DDM):
|
|||
|
arg = list(rep)
|
|||
|
elif isinstance(rep, SDM):
|
|||
|
arg = dict(rep)
|
|||
|
else:
|
|||
|
raise RuntimeError # pragma: no cover
|
|||
|
return arg, self.shape, self.domain
|
|||
|
|
|||
|
def __getitem__(self, key):
|
|||
|
i, j = key
|
|||
|
m, n = self.shape
|
|||
|
if not (isinstance(i, slice) or isinstance(j, slice)):
|
|||
|
return DomainScalar(self.rep.getitem(i, j), self.domain)
|
|||
|
|
|||
|
if not isinstance(i, slice):
|
|||
|
if not -m <= i < m:
|
|||
|
raise IndexError("Row index out of range")
|
|||
|
i = i % m
|
|||
|
i = slice(i, i+1)
|
|||
|
if not isinstance(j, slice):
|
|||
|
if not -n <= j < n:
|
|||
|
raise IndexError("Column index out of range")
|
|||
|
j = j % n
|
|||
|
j = slice(j, j+1)
|
|||
|
|
|||
|
return self.from_rep(self.rep.extract_slice(i, j))
|
|||
|
|
|||
|
def getitem_sympy(self, i, j):
|
|||
|
return self.domain.to_sympy(self.rep.getitem(i, j))
|
|||
|
|
|||
|
def extract(self, rowslist, colslist):
|
|||
|
return self.from_rep(self.rep.extract(rowslist, colslist))
|
|||
|
|
|||
|
def __setitem__(self, key, value):
|
|||
|
i, j = key
|
|||
|
if not self.domain.of_type(value):
|
|||
|
raise TypeError
|
|||
|
if isinstance(i, int) and isinstance(j, int):
|
|||
|
self.rep.setitem(i, j, value)
|
|||
|
else:
|
|||
|
raise NotImplementedError
|
|||
|
|
|||
|
@classmethod
|
|||
|
def from_rep(cls, rep):
|
|||
|
"""Create a new DomainMatrix efficiently from DDM/SDM.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
Create a :py:class:`~.DomainMatrix` with an dense internal
|
|||
|
representation as :py:class:`~.DDM`:
|
|||
|
|
|||
|
>>> from sympy.polys.domains import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy.polys.matrices.ddm import DDM
|
|||
|
>>> drep = DDM([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> dM = DomainMatrix.from_rep(drep)
|
|||
|
>>> dM
|
|||
|
DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
|
|||
|
|
|||
|
Create a :py:class:`~.DomainMatrix` with a sparse internal
|
|||
|
representation as :py:class:`~.SDM`:
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy.polys.matrices.sdm import SDM
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> drep = SDM({0:{1:ZZ(1)},1:{0:ZZ(2)}}, (2, 2), ZZ)
|
|||
|
>>> dM = DomainMatrix.from_rep(drep)
|
|||
|
>>> dM
|
|||
|
DomainMatrix({0: {1: 1}, 1: {0: 2}}, (2, 2), ZZ)
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
rep: SDM or DDM
|
|||
|
The internal sparse or dense representation of the matrix.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
A :py:class:`~.DomainMatrix` wrapping *rep*.
|
|||
|
|
|||
|
Notes
|
|||
|
=====
|
|||
|
|
|||
|
This takes ownership of rep as its internal representation. If rep is
|
|||
|
being mutated elsewhere then a copy should be provided to
|
|||
|
``from_rep``. Only minimal verification or checking is done on *rep*
|
|||
|
as this is supposed to be an efficient internal routine.
|
|||
|
|
|||
|
"""
|
|||
|
if not isinstance(rep, (DDM, SDM)):
|
|||
|
raise TypeError("rep should be of type DDM or SDM")
|
|||
|
self = super().__new__(cls)
|
|||
|
self.rep = rep
|
|||
|
self.shape = rep.shape
|
|||
|
self.domain = rep.domain
|
|||
|
return self
|
|||
|
|
|||
|
|
|||
|
@classmethod
|
|||
|
def from_list(cls, rows, domain):
|
|||
|
r"""
|
|||
|
Convert a list of lists into a DomainMatrix
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
rows: list of lists
|
|||
|
Each element of the inner lists should be either the single arg,
|
|||
|
or tuple of args, that would be passed to the domain constructor
|
|||
|
in order to form an element of the domain. See examples.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix containing elements defined in rows
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import FF, QQ, ZZ
|
|||
|
>>> A = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], ZZ)
|
|||
|
>>> A
|
|||
|
DomainMatrix([[1, 0, 1], [0, 0, 1]], (2, 3), ZZ)
|
|||
|
>>> B = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], FF(7))
|
|||
|
>>> B
|
|||
|
DomainMatrix([[1 mod 7, 0 mod 7, 1 mod 7], [0 mod 7, 0 mod 7, 1 mod 7]], (2, 3), GF(7))
|
|||
|
>>> C = DomainMatrix.from_list([[(1, 2), (3, 1)], [(1, 4), (5, 1)]], QQ)
|
|||
|
>>> C
|
|||
|
DomainMatrix([[1/2, 3], [1/4, 5]], (2, 2), QQ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
from_list_sympy
|
|||
|
|
|||
|
"""
|
|||
|
nrows = len(rows)
|
|||
|
ncols = 0 if not nrows else len(rows[0])
|
|||
|
conv = lambda e: domain(*e) if isinstance(e, tuple) else domain(e)
|
|||
|
domain_rows = [[conv(e) for e in row] for row in rows]
|
|||
|
return DomainMatrix(domain_rows, (nrows, ncols), domain)
|
|||
|
|
|||
|
|
|||
|
@classmethod
|
|||
|
def from_list_sympy(cls, nrows, ncols, rows, **kwargs):
|
|||
|
r"""
|
|||
|
Convert a list of lists of Expr into a DomainMatrix using construct_domain
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
nrows: number of rows
|
|||
|
ncols: number of columns
|
|||
|
rows: list of lists
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix containing elements of rows
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy.abc import x, y, z
|
|||
|
>>> A = DomainMatrix.from_list_sympy(1, 3, [[x, y, z]])
|
|||
|
>>> A
|
|||
|
DomainMatrix([[x, y, z]], (1, 3), ZZ[x,y,z])
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
sympy.polys.constructor.construct_domain, from_dict_sympy
|
|||
|
|
|||
|
"""
|
|||
|
assert len(rows) == nrows
|
|||
|
assert all(len(row) == ncols for row in rows)
|
|||
|
|
|||
|
items_sympy = [_sympify(item) for row in rows for item in row]
|
|||
|
|
|||
|
domain, items_domain = cls.get_domain(items_sympy, **kwargs)
|
|||
|
|
|||
|
domain_rows = [[items_domain[ncols*r + c] for c in range(ncols)] for r in range(nrows)]
|
|||
|
|
|||
|
return DomainMatrix(domain_rows, (nrows, ncols), domain)
|
|||
|
|
|||
|
@classmethod
|
|||
|
def from_dict_sympy(cls, nrows, ncols, elemsdict, **kwargs):
|
|||
|
"""
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
nrows: number of rows
|
|||
|
ncols: number of cols
|
|||
|
elemsdict: dict of dicts containing non-zero elements of the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix containing elements of elemsdict
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy.abc import x,y,z
|
|||
|
>>> elemsdict = {0: {0:x}, 1:{1: y}, 2: {2: z}}
|
|||
|
>>> A = DomainMatrix.from_dict_sympy(3, 3, elemsdict)
|
|||
|
>>> A
|
|||
|
DomainMatrix({0: {0: x}, 1: {1: y}, 2: {2: z}}, (3, 3), ZZ[x,y,z])
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
from_list_sympy
|
|||
|
|
|||
|
"""
|
|||
|
if not all(0 <= r < nrows for r in elemsdict):
|
|||
|
raise DMBadInputError("Row out of range")
|
|||
|
if not all(0 <= c < ncols for row in elemsdict.values() for c in row):
|
|||
|
raise DMBadInputError("Column out of range")
|
|||
|
|
|||
|
items_sympy = [_sympify(item) for row in elemsdict.values() for item in row.values()]
|
|||
|
domain, items_domain = cls.get_domain(items_sympy, **kwargs)
|
|||
|
|
|||
|
idx = 0
|
|||
|
items_dict = {}
|
|||
|
for i, row in elemsdict.items():
|
|||
|
items_dict[i] = {}
|
|||
|
for j in row:
|
|||
|
items_dict[i][j] = items_domain[idx]
|
|||
|
idx += 1
|
|||
|
|
|||
|
return DomainMatrix(items_dict, (nrows, ncols), domain)
|
|||
|
|
|||
|
@classmethod
|
|||
|
def from_Matrix(cls, M, fmt='sparse',**kwargs):
|
|||
|
r"""
|
|||
|
Convert Matrix to DomainMatrix
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
M: Matrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
Returns DomainMatrix with identical elements as M
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import Matrix
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> M = Matrix([
|
|||
|
... [1.0, 3.4],
|
|||
|
... [2.4, 1]])
|
|||
|
>>> A = DomainMatrix.from_Matrix(M)
|
|||
|
>>> A
|
|||
|
DomainMatrix({0: {0: 1.0, 1: 3.4}, 1: {0: 2.4, 1: 1.0}}, (2, 2), RR)
|
|||
|
|
|||
|
We can keep internal representation as ddm using fmt='dense'
|
|||
|
>>> from sympy import Matrix, QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix.from_Matrix(Matrix([[QQ(1, 2), QQ(3, 4)], [QQ(0, 1), QQ(0, 1)]]), fmt='dense')
|
|||
|
>>> A.rep
|
|||
|
[[1/2, 3/4], [0, 0]]
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
Matrix
|
|||
|
|
|||
|
"""
|
|||
|
if fmt == 'dense':
|
|||
|
return cls.from_list_sympy(*M.shape, M.tolist(), **kwargs)
|
|||
|
|
|||
|
return cls.from_dict_sympy(*M.shape, M.todod(), **kwargs)
|
|||
|
|
|||
|
@classmethod
|
|||
|
def get_domain(cls, items_sympy, **kwargs):
|
|||
|
K, items_K = construct_domain(items_sympy, **kwargs)
|
|||
|
return K, items_K
|
|||
|
|
|||
|
def copy(self):
|
|||
|
return self.from_rep(self.rep.copy())
|
|||
|
|
|||
|
def convert_to(self, K):
|
|||
|
r"""
|
|||
|
Change the domain of DomainMatrix to desired domain or field
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
K : Represents the desired domain or field.
|
|||
|
Alternatively, ``None`` may be passed, in which case this method
|
|||
|
just returns a copy of this DomainMatrix.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix with the desired domain or field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ, ZZ_I
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.convert_to(ZZ_I)
|
|||
|
DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ_I)
|
|||
|
|
|||
|
"""
|
|||
|
if K is None:
|
|||
|
return self.copy()
|
|||
|
return self.from_rep(self.rep.convert_to(K))
|
|||
|
|
|||
|
def to_sympy(self):
|
|||
|
return self.convert_to(EXRAW)
|
|||
|
|
|||
|
def to_field(self):
|
|||
|
r"""
|
|||
|
Returns a DomainMatrix with the appropriate field
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix with the appropriate field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.to_field()
|
|||
|
DomainMatrix([[1, 2], [3, 4]], (2, 2), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
K = self.domain.get_field()
|
|||
|
return self.convert_to(K)
|
|||
|
|
|||
|
def to_sparse(self):
|
|||
|
"""
|
|||
|
Return a sparse DomainMatrix representation of *self*.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> A = DomainMatrix([[1, 0],[0, 2]], (2, 2), QQ)
|
|||
|
>>> A.rep
|
|||
|
[[1, 0], [0, 2]]
|
|||
|
>>> B = A.to_sparse()
|
|||
|
>>> B.rep
|
|||
|
{0: {0: 1}, 1: {1: 2}}
|
|||
|
"""
|
|||
|
if self.rep.fmt == 'sparse':
|
|||
|
return self
|
|||
|
|
|||
|
return self.from_rep(SDM.from_ddm(self.rep))
|
|||
|
|
|||
|
def to_dense(self):
|
|||
|
"""
|
|||
|
Return a dense DomainMatrix representation of *self*.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> A = DomainMatrix({0: {0: 1}, 1: {1: 2}}, (2, 2), QQ)
|
|||
|
>>> A.rep
|
|||
|
{0: {0: 1}, 1: {1: 2}}
|
|||
|
>>> B = A.to_dense()
|
|||
|
>>> B.rep
|
|||
|
[[1, 0], [0, 2]]
|
|||
|
|
|||
|
"""
|
|||
|
if self.rep.fmt == 'dense':
|
|||
|
return self
|
|||
|
|
|||
|
return self.from_rep(SDM.to_ddm(self.rep))
|
|||
|
|
|||
|
@classmethod
|
|||
|
def _unify_domain(cls, *matrices):
|
|||
|
"""Convert matrices to a common domain"""
|
|||
|
domains = {matrix.domain for matrix in matrices}
|
|||
|
if len(domains) == 1:
|
|||
|
return matrices
|
|||
|
domain = reduce(lambda x, y: x.unify(y), domains)
|
|||
|
return tuple(matrix.convert_to(domain) for matrix in matrices)
|
|||
|
|
|||
|
@classmethod
|
|||
|
def _unify_fmt(cls, *matrices, fmt=None):
|
|||
|
"""Convert matrices to the same format.
|
|||
|
|
|||
|
If all matrices have the same format, then return unmodified.
|
|||
|
Otherwise convert both to the preferred format given as *fmt* which
|
|||
|
should be 'dense' or 'sparse'.
|
|||
|
"""
|
|||
|
formats = {matrix.rep.fmt for matrix in matrices}
|
|||
|
if len(formats) == 1:
|
|||
|
return matrices
|
|||
|
if fmt == 'sparse':
|
|||
|
return tuple(matrix.to_sparse() for matrix in matrices)
|
|||
|
elif fmt == 'dense':
|
|||
|
return tuple(matrix.to_dense() for matrix in matrices)
|
|||
|
else:
|
|||
|
raise ValueError("fmt should be 'sparse' or 'dense'")
|
|||
|
|
|||
|
def unify(self, *others, fmt=None):
|
|||
|
"""
|
|||
|
Unifies the domains and the format of self and other
|
|||
|
matrices.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
others : DomainMatrix
|
|||
|
|
|||
|
fmt: string 'dense', 'sparse' or `None` (default)
|
|||
|
The preferred format to convert to if self and other are not
|
|||
|
already in the same format. If `None` or not specified then no
|
|||
|
conversion if performed.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
Tuple[DomainMatrix]
|
|||
|
Matrices with unified domain and format
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
Unify the domain of DomainMatrix that have different domains:
|
|||
|
|
|||
|
>>> from sympy import ZZ, QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([[QQ(1, 2), QQ(2)]], (1, 2), QQ)
|
|||
|
>>> Aq, Bq = A.unify(B)
|
|||
|
>>> Aq
|
|||
|
DomainMatrix([[1, 2]], (1, 2), QQ)
|
|||
|
>>> Bq
|
|||
|
DomainMatrix([[1/2, 2]], (1, 2), QQ)
|
|||
|
|
|||
|
Unify the format (dense or sparse):
|
|||
|
|
|||
|
>>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
|
|||
|
>>> B = DomainMatrix({0:{0: ZZ(1)}}, (2, 2), ZZ)
|
|||
|
>>> B.rep
|
|||
|
{0: {0: 1}}
|
|||
|
|
|||
|
>>> A2, B2 = A.unify(B, fmt='dense')
|
|||
|
>>> B2.rep
|
|||
|
[[1, 0], [0, 0]]
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
convert_to, to_dense, to_sparse
|
|||
|
|
|||
|
"""
|
|||
|
matrices = (self,) + others
|
|||
|
matrices = DomainMatrix._unify_domain(*matrices)
|
|||
|
if fmt is not None:
|
|||
|
matrices = DomainMatrix._unify_fmt(*matrices, fmt=fmt)
|
|||
|
return matrices
|
|||
|
|
|||
|
def to_Matrix(self):
|
|||
|
r"""
|
|||
|
Convert DomainMatrix to Matrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
Matrix
|
|||
|
MutableDenseMatrix for the DomainMatrix
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.to_Matrix()
|
|||
|
Matrix([
|
|||
|
[1, 2],
|
|||
|
[3, 4]])
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
from_Matrix
|
|||
|
|
|||
|
"""
|
|||
|
from sympy.matrices.dense import MutableDenseMatrix
|
|||
|
elemlist = self.rep.to_list()
|
|||
|
elements_sympy = [self.domain.to_sympy(e) for row in elemlist for e in row]
|
|||
|
return MutableDenseMatrix(*self.shape, elements_sympy)
|
|||
|
|
|||
|
def to_list(self):
|
|||
|
return self.rep.to_list()
|
|||
|
|
|||
|
def to_list_flat(self):
|
|||
|
return self.rep.to_list_flat()
|
|||
|
|
|||
|
def to_dok(self):
|
|||
|
return self.rep.to_dok()
|
|||
|
|
|||
|
def __repr__(self):
|
|||
|
return 'DomainMatrix(%s, %r, %r)' % (str(self.rep), self.shape, self.domain)
|
|||
|
|
|||
|
def transpose(self):
|
|||
|
"""Matrix transpose of ``self``"""
|
|||
|
return self.from_rep(self.rep.transpose())
|
|||
|
|
|||
|
def flat(self):
|
|||
|
rows, cols = self.shape
|
|||
|
return [self[i,j].element for i in range(rows) for j in range(cols)]
|
|||
|
|
|||
|
@property
|
|||
|
def is_zero_matrix(self):
|
|||
|
return self.rep.is_zero_matrix()
|
|||
|
|
|||
|
@property
|
|||
|
def is_upper(self):
|
|||
|
"""
|
|||
|
Says whether this matrix is upper-triangular. True can be returned
|
|||
|
even if the matrix is not square.
|
|||
|
"""
|
|||
|
return self.rep.is_upper()
|
|||
|
|
|||
|
@property
|
|||
|
def is_lower(self):
|
|||
|
"""
|
|||
|
Says whether this matrix is lower-triangular. True can be returned
|
|||
|
even if the matrix is not square.
|
|||
|
"""
|
|||
|
return self.rep.is_lower()
|
|||
|
|
|||
|
@property
|
|||
|
def is_square(self):
|
|||
|
return self.shape[0] == self.shape[1]
|
|||
|
|
|||
|
def rank(self):
|
|||
|
rref, pivots = self.rref()
|
|||
|
return len(pivots)
|
|||
|
|
|||
|
def hstack(A, *B):
|
|||
|
r"""Horizontally stack the given matrices.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
B: DomainMatrix
|
|||
|
Matrices to stack horizontally.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix by stacking horizontally.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
|
|||
|
>>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ)
|
|||
|
>>> A.hstack(B)
|
|||
|
DomainMatrix([[1, 2, 5, 6], [3, 4, 7, 8]], (2, 4), ZZ)
|
|||
|
|
|||
|
>>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ)
|
|||
|
>>> A.hstack(B, C)
|
|||
|
DomainMatrix([[1, 2, 5, 6, 9, 10], [3, 4, 7, 8, 11, 12]], (2, 6), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
unify
|
|||
|
"""
|
|||
|
A, *B = A.unify(*B, fmt='dense')
|
|||
|
return DomainMatrix.from_rep(A.rep.hstack(*(Bk.rep for Bk in B)))
|
|||
|
|
|||
|
def vstack(A, *B):
|
|||
|
r"""Vertically stack the given matrices.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
B: DomainMatrix
|
|||
|
Matrices to stack vertically.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix by stacking vertically.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
|
|||
|
>>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ)
|
|||
|
>>> A.vstack(B)
|
|||
|
DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8]], (4, 2), ZZ)
|
|||
|
|
|||
|
>>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ)
|
|||
|
>>> A.vstack(B, C)
|
|||
|
DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], (6, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
unify
|
|||
|
"""
|
|||
|
A, *B = A.unify(*B, fmt='dense')
|
|||
|
return DomainMatrix.from_rep(A.rep.vstack(*(Bk.rep for Bk in B)))
|
|||
|
|
|||
|
def applyfunc(self, func, domain=None):
|
|||
|
if domain is None:
|
|||
|
domain = self.domain
|
|||
|
return self.from_rep(self.rep.applyfunc(func, domain))
|
|||
|
|
|||
|
def __add__(A, B):
|
|||
|
if not isinstance(B, DomainMatrix):
|
|||
|
return NotImplemented
|
|||
|
A, B = A.unify(B, fmt='dense')
|
|||
|
return A.add(B)
|
|||
|
|
|||
|
def __sub__(A, B):
|
|||
|
if not isinstance(B, DomainMatrix):
|
|||
|
return NotImplemented
|
|||
|
A, B = A.unify(B, fmt='dense')
|
|||
|
return A.sub(B)
|
|||
|
|
|||
|
def __neg__(A):
|
|||
|
return A.neg()
|
|||
|
|
|||
|
def __mul__(A, B):
|
|||
|
"""A * B"""
|
|||
|
if isinstance(B, DomainMatrix):
|
|||
|
A, B = A.unify(B, fmt='dense')
|
|||
|
return A.matmul(B)
|
|||
|
elif B in A.domain:
|
|||
|
return A.scalarmul(B)
|
|||
|
elif isinstance(B, DomainScalar):
|
|||
|
A, B = A.unify(B)
|
|||
|
return A.scalarmul(B.element)
|
|||
|
else:
|
|||
|
return NotImplemented
|
|||
|
|
|||
|
def __rmul__(A, B):
|
|||
|
if B in A.domain:
|
|||
|
return A.rscalarmul(B)
|
|||
|
elif isinstance(B, DomainScalar):
|
|||
|
A, B = A.unify(B)
|
|||
|
return A.rscalarmul(B.element)
|
|||
|
else:
|
|||
|
return NotImplemented
|
|||
|
|
|||
|
def __pow__(A, n):
|
|||
|
"""A ** n"""
|
|||
|
if not isinstance(n, int):
|
|||
|
return NotImplemented
|
|||
|
return A.pow(n)
|
|||
|
|
|||
|
def _check(a, op, b, ashape, bshape):
|
|||
|
if a.domain != b.domain:
|
|||
|
msg = "Domain mismatch: %s %s %s" % (a.domain, op, b.domain)
|
|||
|
raise DMDomainError(msg)
|
|||
|
if ashape != bshape:
|
|||
|
msg = "Shape mismatch: %s %s %s" % (a.shape, op, b.shape)
|
|||
|
raise DMShapeError(msg)
|
|||
|
if a.rep.fmt != b.rep.fmt:
|
|||
|
msg = "Format mismatch: %s %s %s" % (a.rep.fmt, op, b.rep.fmt)
|
|||
|
raise DMFormatError(msg)
|
|||
|
|
|||
|
def add(A, B):
|
|||
|
r"""
|
|||
|
Adds two DomainMatrix matrices of the same Domain
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A, B: DomainMatrix
|
|||
|
matrices to add
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after Addition
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
DMShapeError
|
|||
|
If the dimensions of the two DomainMatrix are not equal
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of the two DomainMatrix are not same
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [ZZ(4), ZZ(3)],
|
|||
|
... [ZZ(2), ZZ(1)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.add(B)
|
|||
|
DomainMatrix([[5, 5], [5, 5]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
sub, matmul
|
|||
|
|
|||
|
"""
|
|||
|
A._check('+', B, A.shape, B.shape)
|
|||
|
return A.from_rep(A.rep.add(B.rep))
|
|||
|
|
|||
|
|
|||
|
def sub(A, B):
|
|||
|
r"""
|
|||
|
Subtracts two DomainMatrix matrices of the same Domain
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A, B: DomainMatrix
|
|||
|
matrices to subtract
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after Subtraction
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
DMShapeError
|
|||
|
If the dimensions of the two DomainMatrix are not equal
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of the two DomainMatrix are not same
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [ZZ(4), ZZ(3)],
|
|||
|
... [ZZ(2), ZZ(1)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.sub(B)
|
|||
|
DomainMatrix([[-3, -1], [1, 3]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
add, matmul
|
|||
|
|
|||
|
"""
|
|||
|
A._check('-', B, A.shape, B.shape)
|
|||
|
return A.from_rep(A.rep.sub(B.rep))
|
|||
|
|
|||
|
def neg(A):
|
|||
|
r"""
|
|||
|
Returns the negative of DomainMatrix
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A : Represents a DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after Negation
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.neg()
|
|||
|
DomainMatrix([[-1, -2], [-3, -4]], (2, 2), ZZ)
|
|||
|
|
|||
|
"""
|
|||
|
return A.from_rep(A.rep.neg())
|
|||
|
|
|||
|
def mul(A, b):
|
|||
|
r"""
|
|||
|
Performs term by term multiplication for the second DomainMatrix
|
|||
|
w.r.t first DomainMatrix. Returns a DomainMatrix whose rows are
|
|||
|
list of DomainMatrix matrices created after term by term multiplication.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A, B: DomainMatrix
|
|||
|
matrices to multiply term-wise
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after term by term multiplication
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(1)],
|
|||
|
... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.mul(B)
|
|||
|
DomainMatrix([[DomainMatrix([[1, 1], [0, 1]], (2, 2), ZZ),
|
|||
|
DomainMatrix([[2, 2], [0, 2]], (2, 2), ZZ)],
|
|||
|
[DomainMatrix([[3, 3], [0, 3]], (2, 2), ZZ),
|
|||
|
DomainMatrix([[4, 4], [0, 4]], (2, 2), ZZ)]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
matmul
|
|||
|
|
|||
|
"""
|
|||
|
return A.from_rep(A.rep.mul(b))
|
|||
|
|
|||
|
def rmul(A, b):
|
|||
|
return A.from_rep(A.rep.rmul(b))
|
|||
|
|
|||
|
def matmul(A, B):
|
|||
|
r"""
|
|||
|
Performs matrix multiplication of two DomainMatrix matrices
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A, B: DomainMatrix
|
|||
|
to multiply
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after multiplication
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(1)],
|
|||
|
... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.matmul(B)
|
|||
|
DomainMatrix([[1, 3], [3, 7]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
mul, pow, add, sub
|
|||
|
|
|||
|
"""
|
|||
|
|
|||
|
A._check('*', B, A.shape[1], B.shape[0])
|
|||
|
return A.from_rep(A.rep.matmul(B.rep))
|
|||
|
|
|||
|
def _scalarmul(A, lamda, reverse):
|
|||
|
if lamda == A.domain.zero:
|
|||
|
return DomainMatrix.zeros(A.shape, A.domain)
|
|||
|
elif lamda == A.domain.one:
|
|||
|
return A.copy()
|
|||
|
elif reverse:
|
|||
|
return A.rmul(lamda)
|
|||
|
else:
|
|||
|
return A.mul(lamda)
|
|||
|
|
|||
|
def scalarmul(A, lamda):
|
|||
|
return A._scalarmul(lamda, reverse=False)
|
|||
|
|
|||
|
def rscalarmul(A, lamda):
|
|||
|
return A._scalarmul(lamda, reverse=True)
|
|||
|
|
|||
|
def mul_elementwise(A, B):
|
|||
|
assert A.domain == B.domain
|
|||
|
return A.from_rep(A.rep.mul_elementwise(B.rep))
|
|||
|
|
|||
|
def __truediv__(A, lamda):
|
|||
|
""" Method for Scalar Division"""
|
|||
|
if isinstance(lamda, int) or ZZ.of_type(lamda):
|
|||
|
lamda = DomainScalar(ZZ(lamda), ZZ)
|
|||
|
|
|||
|
if not isinstance(lamda, DomainScalar):
|
|||
|
return NotImplemented
|
|||
|
|
|||
|
A, lamda = A.to_field().unify(lamda)
|
|||
|
if lamda.element == lamda.domain.zero:
|
|||
|
raise ZeroDivisionError
|
|||
|
if lamda.element == lamda.domain.one:
|
|||
|
return A.to_field()
|
|||
|
|
|||
|
return A.mul(1 / lamda.element)
|
|||
|
|
|||
|
def pow(A, n):
|
|||
|
r"""
|
|||
|
Computes A**n
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A : DomainMatrix
|
|||
|
|
|||
|
n : exponent for A
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix on computing A**n
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
NotImplementedError
|
|||
|
if n is negative.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(1)],
|
|||
|
... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.pow(2)
|
|||
|
DomainMatrix([[1, 2], [0, 1]], (2, 2), ZZ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
matmul
|
|||
|
|
|||
|
"""
|
|||
|
nrows, ncols = A.shape
|
|||
|
if nrows != ncols:
|
|||
|
raise DMNonSquareMatrixError('Power of a nonsquare matrix')
|
|||
|
if n < 0:
|
|||
|
raise NotImplementedError('Negative powers')
|
|||
|
elif n == 0:
|
|||
|
return A.eye(nrows, A.domain)
|
|||
|
elif n == 1:
|
|||
|
return A
|
|||
|
elif n % 2 == 1:
|
|||
|
return A * A**(n - 1)
|
|||
|
else:
|
|||
|
sqrtAn = A ** (n // 2)
|
|||
|
return sqrtAn * sqrtAn
|
|||
|
|
|||
|
def scc(self):
|
|||
|
"""Compute the strongly connected components of a DomainMatrix
|
|||
|
|
|||
|
Explanation
|
|||
|
===========
|
|||
|
|
|||
|
A square matrix can be considered as the adjacency matrix for a
|
|||
|
directed graph where the row and column indices are the vertices. In
|
|||
|
this graph if there is an edge from vertex ``i`` to vertex ``j`` if
|
|||
|
``M[i, j]`` is nonzero. This routine computes the strongly connected
|
|||
|
components of that graph which are subsets of the rows and columns that
|
|||
|
are connected by some nonzero element of the matrix. The strongly
|
|||
|
connected components are useful because many operations such as the
|
|||
|
determinant can be computed by working with the submatrices
|
|||
|
corresponding to each component.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
Find the strongly connected components of a matrix:
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> M = DomainMatrix([[ZZ(1), ZZ(0), ZZ(2)],
|
|||
|
... [ZZ(0), ZZ(3), ZZ(0)],
|
|||
|
... [ZZ(4), ZZ(6), ZZ(5)]], (3, 3), ZZ)
|
|||
|
>>> M.scc()
|
|||
|
[[1], [0, 2]]
|
|||
|
|
|||
|
Compute the determinant from the components:
|
|||
|
|
|||
|
>>> MM = M.to_Matrix()
|
|||
|
>>> MM
|
|||
|
Matrix([
|
|||
|
[1, 0, 2],
|
|||
|
[0, 3, 0],
|
|||
|
[4, 6, 5]])
|
|||
|
>>> MM[[1], [1]]
|
|||
|
Matrix([[3]])
|
|||
|
>>> MM[[0, 2], [0, 2]]
|
|||
|
Matrix([
|
|||
|
[1, 2],
|
|||
|
[4, 5]])
|
|||
|
>>> MM.det()
|
|||
|
-9
|
|||
|
>>> MM[[1], [1]].det() * MM[[0, 2], [0, 2]].det()
|
|||
|
-9
|
|||
|
|
|||
|
The components are given in reverse topological order and represent a
|
|||
|
permutation of the rows and columns that will bring the matrix into
|
|||
|
block lower-triangular form:
|
|||
|
|
|||
|
>>> MM[[1, 0, 2], [1, 0, 2]]
|
|||
|
Matrix([
|
|||
|
[3, 0, 0],
|
|||
|
[0, 1, 2],
|
|||
|
[6, 4, 5]])
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
List of lists of integers
|
|||
|
Each list represents a strongly connected component.
|
|||
|
|
|||
|
See also
|
|||
|
========
|
|||
|
|
|||
|
sympy.matrices.matrices.MatrixBase.strongly_connected_components
|
|||
|
sympy.utilities.iterables.strongly_connected_components
|
|||
|
|
|||
|
"""
|
|||
|
rows, cols = self.shape
|
|||
|
assert rows == cols
|
|||
|
return self.rep.scc()
|
|||
|
|
|||
|
def rref(self):
|
|||
|
r"""
|
|||
|
Returns reduced-row echelon form and list of pivots for the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
(DomainMatrix, list)
|
|||
|
reduced-row echelon form and list of pivots for the DomainMatrix
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of DomainMatrix not a Field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(2), QQ(-1), QQ(0)],
|
|||
|
... [QQ(-1), QQ(2), QQ(-1)],
|
|||
|
... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ)
|
|||
|
|
|||
|
>>> rref_matrix, rref_pivots = A.rref()
|
|||
|
>>> rref_matrix
|
|||
|
DomainMatrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]], (3, 3), QQ)
|
|||
|
>>> rref_pivots
|
|||
|
(0, 1, 2)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
convert_to, lu
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
rref_ddm, pivots = self.rep.rref()
|
|||
|
return self.from_rep(rref_ddm), tuple(pivots)
|
|||
|
|
|||
|
def columnspace(self):
|
|||
|
r"""
|
|||
|
Returns the columnspace for the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
The columns of this matrix form a basis for the columnspace.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(1), QQ(-1)],
|
|||
|
... [QQ(2), QQ(-2)]], (2, 2), QQ)
|
|||
|
>>> A.columnspace()
|
|||
|
DomainMatrix([[1], [2]], (2, 1), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
rref, pivots = self.rref()
|
|||
|
rows, cols = self.shape
|
|||
|
return self.extract(range(rows), pivots)
|
|||
|
|
|||
|
def rowspace(self):
|
|||
|
r"""
|
|||
|
Returns the rowspace for the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
The rows of this matrix form a basis for the rowspace.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(1), QQ(-1)],
|
|||
|
... [QQ(2), QQ(-2)]], (2, 2), QQ)
|
|||
|
>>> A.rowspace()
|
|||
|
DomainMatrix([[1, -1]], (1, 2), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
rref, pivots = self.rref()
|
|||
|
rows, cols = self.shape
|
|||
|
return self.extract(range(len(pivots)), range(cols))
|
|||
|
|
|||
|
def nullspace(self):
|
|||
|
r"""
|
|||
|
Returns the nullspace for the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
The rows of this matrix form a basis for the nullspace.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(1), QQ(-1)],
|
|||
|
... [QQ(2), QQ(-2)]], (2, 2), QQ)
|
|||
|
>>> A.nullspace()
|
|||
|
DomainMatrix([[1, 1]], (1, 2), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
return self.from_rep(self.rep.nullspace()[0])
|
|||
|
|
|||
|
def inv(self):
|
|||
|
r"""
|
|||
|
Finds the inverse of the DomainMatrix if exists
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
DomainMatrix after inverse
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of DomainMatrix not a Field
|
|||
|
|
|||
|
DMNonSquareMatrixError
|
|||
|
If the DomainMatrix is not a not Square DomainMatrix
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(2), QQ(-1), QQ(0)],
|
|||
|
... [QQ(-1), QQ(2), QQ(-1)],
|
|||
|
... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ)
|
|||
|
>>> A.inv()
|
|||
|
DomainMatrix([[2/3, 1/3, 1/6], [1/3, 2/3, 1/3], [0, 0, 1/2]], (3, 3), QQ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
neg
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
m, n = self.shape
|
|||
|
if m != n:
|
|||
|
raise DMNonSquareMatrixError
|
|||
|
inv = self.rep.inv()
|
|||
|
return self.from_rep(inv)
|
|||
|
|
|||
|
def det(self):
|
|||
|
r"""
|
|||
|
Returns the determinant of a Square DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
S.Complexes
|
|||
|
determinant of Square DomainMatrix
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of DomainMatrix not a Field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.det()
|
|||
|
-2
|
|||
|
|
|||
|
"""
|
|||
|
m, n = self.shape
|
|||
|
if m != n:
|
|||
|
raise DMNonSquareMatrixError
|
|||
|
return self.rep.det()
|
|||
|
|
|||
|
def lu(self):
|
|||
|
r"""
|
|||
|
Returns Lower and Upper decomposition of the DomainMatrix
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
(L, U, exchange)
|
|||
|
L, U are Lower and Upper decomposition of the DomainMatrix,
|
|||
|
exchange is the list of indices of rows exchanged in the decomposition.
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of DomainMatrix not a Field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(1), QQ(-1)],
|
|||
|
... [QQ(2), QQ(-2)]], (2, 2), QQ)
|
|||
|
>>> A.lu()
|
|||
|
(DomainMatrix([[1, 0], [2, 1]], (2, 2), QQ), DomainMatrix([[1, -1], [0, 0]], (2, 2), QQ), [])
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
lu_solve
|
|||
|
|
|||
|
"""
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
L, U, swaps = self.rep.lu()
|
|||
|
return self.from_rep(L), self.from_rep(U), swaps
|
|||
|
|
|||
|
def lu_solve(self, rhs):
|
|||
|
r"""
|
|||
|
Solver for DomainMatrix x in the A*x = B
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
rhs : DomainMatrix B
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
DomainMatrix
|
|||
|
x in A*x = B
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
DMShapeError
|
|||
|
If the DomainMatrix A and rhs have different number of rows
|
|||
|
|
|||
|
ValueError
|
|||
|
If the domain of DomainMatrix A not a Field
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [QQ(1), QQ(2)],
|
|||
|
... [QQ(3), QQ(4)]], (2, 2), QQ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [QQ(1), QQ(1)],
|
|||
|
... [QQ(0), QQ(1)]], (2, 2), QQ)
|
|||
|
|
|||
|
>>> A.lu_solve(B)
|
|||
|
DomainMatrix([[-2, -1], [3/2, 1]], (2, 2), QQ)
|
|||
|
|
|||
|
See Also
|
|||
|
========
|
|||
|
|
|||
|
lu
|
|||
|
|
|||
|
"""
|
|||
|
if self.shape[0] != rhs.shape[0]:
|
|||
|
raise DMShapeError("Shape")
|
|||
|
if not self.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
sol = self.rep.lu_solve(rhs.rep)
|
|||
|
return self.from_rep(sol)
|
|||
|
|
|||
|
def _solve(A, b):
|
|||
|
# XXX: Not sure about this method or its signature. It is just created
|
|||
|
# because it is needed by the holonomic module.
|
|||
|
if A.shape[0] != b.shape[0]:
|
|||
|
raise DMShapeError("Shape")
|
|||
|
if A.domain != b.domain or not A.domain.is_Field:
|
|||
|
raise DMNotAField('Not a field')
|
|||
|
Aaug = A.hstack(b)
|
|||
|
Arref, pivots = Aaug.rref()
|
|||
|
particular = Arref.from_rep(Arref.rep.particular())
|
|||
|
nullspace_rep, nonpivots = Arref[:,:-1].rep.nullspace()
|
|||
|
nullspace = Arref.from_rep(nullspace_rep)
|
|||
|
return particular, nullspace
|
|||
|
|
|||
|
def charpoly(self):
|
|||
|
r"""
|
|||
|
Returns the coefficients of the characteristic polynomial
|
|||
|
of the DomainMatrix. These elements will be domain elements.
|
|||
|
The domain of the elements will be same as domain of the DomainMatrix.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
list
|
|||
|
coefficients of the characteristic polynomial
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
DMNonSquareMatrixError
|
|||
|
If the DomainMatrix is not a not Square DomainMatrix
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
|
|||
|
>>> A.charpoly()
|
|||
|
[1, -5, -2]
|
|||
|
|
|||
|
"""
|
|||
|
m, n = self.shape
|
|||
|
if m != n:
|
|||
|
raise DMNonSquareMatrixError("not square")
|
|||
|
return self.rep.charpoly()
|
|||
|
|
|||
|
@classmethod
|
|||
|
def eye(cls, shape, domain):
|
|||
|
r"""
|
|||
|
Return identity matrix of size n
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> DomainMatrix.eye(3, QQ)
|
|||
|
DomainMatrix({0: {0: 1}, 1: {1: 1}, 2: {2: 1}}, (3, 3), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
if isinstance(shape, int):
|
|||
|
shape = (shape, shape)
|
|||
|
return cls.from_rep(SDM.eye(shape, domain))
|
|||
|
|
|||
|
@classmethod
|
|||
|
def diag(cls, diagonal, domain, shape=None):
|
|||
|
r"""
|
|||
|
Return diagonal matrix with entries from ``diagonal``.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> DomainMatrix.diag([ZZ(5), ZZ(6)], ZZ)
|
|||
|
DomainMatrix({0: {0: 5}, 1: {1: 6}}, (2, 2), ZZ)
|
|||
|
|
|||
|
"""
|
|||
|
if shape is None:
|
|||
|
N = len(diagonal)
|
|||
|
shape = (N, N)
|
|||
|
return cls.from_rep(SDM.diag(diagonal, domain, shape))
|
|||
|
|
|||
|
@classmethod
|
|||
|
def zeros(cls, shape, domain, *, fmt='sparse'):
|
|||
|
"""Returns a zero DomainMatrix of size shape, belonging to the specified domain
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> DomainMatrix.zeros((2, 3), QQ)
|
|||
|
DomainMatrix({}, (2, 3), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
return cls.from_rep(SDM.zeros(shape, domain))
|
|||
|
|
|||
|
@classmethod
|
|||
|
def ones(cls, shape, domain):
|
|||
|
"""Returns a DomainMatrix of 1s, of size shape, belonging to the specified domain
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> from sympy import QQ
|
|||
|
>>> DomainMatrix.ones((2,3), QQ)
|
|||
|
DomainMatrix([[1, 1, 1], [1, 1, 1]], (2, 3), QQ)
|
|||
|
|
|||
|
"""
|
|||
|
return cls.from_rep(DDM.ones(shape, domain))
|
|||
|
|
|||
|
def __eq__(A, B):
|
|||
|
r"""
|
|||
|
Checks for two DomainMatrix matrices to be equal or not
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
A, B: DomainMatrix
|
|||
|
to check equality
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
Boolean
|
|||
|
True for equal, else False
|
|||
|
|
|||
|
Raises
|
|||
|
======
|
|||
|
|
|||
|
NotImplementedError
|
|||
|
If B is not a DomainMatrix
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy import ZZ
|
|||
|
>>> from sympy.polys.matrices import DomainMatrix
|
|||
|
>>> A = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(2)],
|
|||
|
... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
|
|||
|
>>> B = DomainMatrix([
|
|||
|
... [ZZ(1), ZZ(1)],
|
|||
|
... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
|
|||
|
>>> A.__eq__(A)
|
|||
|
True
|
|||
|
>>> A.__eq__(B)
|
|||
|
False
|
|||
|
|
|||
|
"""
|
|||
|
if not isinstance(A, type(B)):
|
|||
|
return NotImplemented
|
|||
|
return A.domain == B.domain and A.rep == B.rep
|
|||
|
|
|||
|
def unify_eq(A, B):
|
|||
|
if A.shape != B.shape:
|
|||
|
return False
|
|||
|
if A.domain != B.domain:
|
|||
|
A, B = A.unify(B)
|
|||
|
return A == B
|
|||
|
|
|||
|
def lll(A, delta=QQ(3, 4)):
|
|||
|
"""
|
|||
|
Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm.
|
|||
|
See [1]_ and [2]_.
|
|||
|
|
|||
|
Parameters
|
|||
|
==========
|
|||
|
|
|||
|
delta : QQ, optional
|
|||
|
The Lovász parameter. Must be in the interval (0.25, 1), with larger
|
|||
|
values producing a more reduced basis. The default is 0.75 for
|
|||
|
historical reasons.
|
|||
|
|
|||
|
Returns
|
|||
|
=======
|
|||
|
|
|||
|
The reduced basis as a DomainMatrix over ZZ.
|
|||
|
|
|||
|
Throws
|
|||
|
======
|
|||
|
|
|||
|
DMValueError: if delta is not in the range (0.25, 1)
|
|||
|
DMShapeError: if the matrix is not of shape (m, n) with m <= n
|
|||
|
DMDomainError: if the matrix domain is not ZZ
|
|||
|
DMRankError: if the matrix contains linearly dependent rows
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.domains import ZZ, QQ
|
|||
|
>>> from sympy.polys.matrices import DM
|
|||
|
>>> x = DM([[1, 0, 0, 0, -20160],
|
|||
|
... [0, 1, 0, 0, 33768],
|
|||
|
... [0, 0, 1, 0, 39578],
|
|||
|
... [0, 0, 0, 1, 47757]], ZZ)
|
|||
|
>>> y = DM([[10, -3, -2, 8, -4],
|
|||
|
... [3, -9, 8, 1, -11],
|
|||
|
... [-3, 13, -9, -3, -9],
|
|||
|
... [-12, -7, -11, 9, -1]], ZZ)
|
|||
|
>>> assert x.lll(delta=QQ(5, 6)) == y
|
|||
|
|
|||
|
Notes
|
|||
|
=====
|
|||
|
|
|||
|
The implementation is derived from the Maple code given in Figures 4.3
|
|||
|
and 4.4 of [3]_ (pp.68-69). It uses the efficient method of only calculating
|
|||
|
state updates as they are required.
|
|||
|
|
|||
|
See also
|
|||
|
========
|
|||
|
|
|||
|
lll_transform
|
|||
|
|
|||
|
References
|
|||
|
==========
|
|||
|
|
|||
|
.. [1] https://en.wikipedia.org/wiki/Lenstra–Lenstra–Lovász_lattice_basis_reduction_algorithm
|
|||
|
.. [2] https://web.archive.org/web/20221029115428/https://web.cs.elte.hu/~lovasz/scans/lll.pdf
|
|||
|
.. [3] Murray R. Bremner, "Lattice Basis Reduction: An Introduction to the LLL Algorithm and Its Applications"
|
|||
|
|
|||
|
"""
|
|||
|
return DomainMatrix.from_rep(A.rep.lll(delta=delta))
|
|||
|
|
|||
|
def lll_transform(A, delta=QQ(3, 4)):
|
|||
|
"""
|
|||
|
Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm
|
|||
|
and returns the reduced basis and transformation matrix.
|
|||
|
|
|||
|
Explanation
|
|||
|
===========
|
|||
|
|
|||
|
Parameters, algorithm and basis are the same as for :meth:`lll` except that
|
|||
|
the return value is a tuple `(B, T)` with `B` the reduced basis and
|
|||
|
`T` a transformation matrix. The original basis `A` is transformed to
|
|||
|
`B` with `T*A == B`. If only `B` is needed then :meth:`lll` should be
|
|||
|
used as it is a little faster.
|
|||
|
|
|||
|
Examples
|
|||
|
========
|
|||
|
|
|||
|
>>> from sympy.polys.domains import ZZ, QQ
|
|||
|
>>> from sympy.polys.matrices import DM
|
|||
|
>>> X = DM([[1, 0, 0, 0, -20160],
|
|||
|
... [0, 1, 0, 0, 33768],
|
|||
|
... [0, 0, 1, 0, 39578],
|
|||
|
... [0, 0, 0, 1, 47757]], ZZ)
|
|||
|
>>> B, T = X.lll_transform(delta=QQ(5, 6))
|
|||
|
>>> T * X == B
|
|||
|
True
|
|||
|
|
|||
|
See also
|
|||
|
========
|
|||
|
|
|||
|
lll
|
|||
|
|
|||
|
"""
|
|||
|
reduced, transform = A.rep.lll_transform(delta=delta)
|
|||
|
return DomainMatrix.from_rep(reduced), DomainMatrix.from_rep(transform)
|