Spaces:
Running
Running
from sympy.core.expr import Expr | |
from sympy.core.symbol import Dummy | |
from sympy.core.sympify import _sympify | |
from sympy.polys.polyerrors import CoercionFailed | |
from sympy.polys.polytools import Poly, parallel_poly_from_expr | |
from sympy.polys.domains import QQ | |
from sympy.polys.matrices import DomainMatrix | |
from sympy.polys.matrices.domainscalar import DomainScalar | |
class MutablePolyDenseMatrix: | |
""" | |
A mutable matrix of objects from poly module or to operate with them. | |
Examples | |
======== | |
>>> from sympy.polys.polymatrix import PolyMatrix | |
>>> from sympy import Symbol, Poly | |
>>> x = Symbol('x') | |
>>> pm1 = PolyMatrix([[Poly(x**2, x), Poly(-x, x)], [Poly(x**3, x), Poly(-1 + x, x)]]) | |
>>> v1 = PolyMatrix([[1, 0], [-1, 0]], x) | |
>>> pm1*v1 | |
PolyMatrix([ | |
[ x**2 + x, 0], | |
[x**3 - x + 1, 0]], ring=QQ[x]) | |
>>> pm1.ring | |
ZZ[x] | |
>>> v1*pm1 | |
PolyMatrix([ | |
[ x**2, -x], | |
[-x**2, x]], ring=QQ[x]) | |
>>> pm2 = PolyMatrix([[Poly(x**2, x, domain='QQ'), Poly(0, x, domain='QQ'), Poly(1, x, domain='QQ'), \ | |
Poly(x**3, x, domain='QQ'), Poly(0, x, domain='QQ'), Poly(-x**3, x, domain='QQ')]]) | |
>>> v2 = PolyMatrix([1, 0, 0, 0, 0, 0], x) | |
>>> v2.ring | |
QQ[x] | |
>>> pm2*v2 | |
PolyMatrix([[x**2]], ring=QQ[x]) | |
""" | |
def __new__(cls, *args, ring=None): | |
if not args: | |
# PolyMatrix(ring=QQ[x]) | |
if ring is None: | |
raise TypeError("The ring needs to be specified for an empty PolyMatrix") | |
rows, cols, items, gens = 0, 0, [], () | |
elif isinstance(args[0], list): | |
elements, gens = args[0], args[1:] | |
if not elements: | |
# PolyMatrix([]) | |
rows, cols, items = 0, 0, [] | |
elif isinstance(elements[0], (list, tuple)): | |
# PolyMatrix([[1, 2]], x) | |
rows, cols = len(elements), len(elements[0]) | |
items = [e for row in elements for e in row] | |
else: | |
# PolyMatrix([1, 2], x) | |
rows, cols = len(elements), 1 | |
items = elements | |
elif [type(a) for a in args[:3]] == [int, int, list]: | |
# PolyMatrix(2, 2, [1, 2, 3, 4], x) | |
rows, cols, items, gens = args[0], args[1], args[2], args[3:] | |
elif [type(a) for a in args[:3]] == [int, int, type(lambda: 0)]: | |
# PolyMatrix(2, 2, lambda i, j: i+j, x) | |
rows, cols, func, gens = args[0], args[1], args[2], args[3:] | |
items = [func(i, j) for i in range(rows) for j in range(cols)] | |
else: | |
raise TypeError("Invalid arguments") | |
# PolyMatrix([[1]], x, y) vs PolyMatrix([[1]], (x, y)) | |
if len(gens) == 1 and isinstance(gens[0], tuple): | |
gens = gens[0] | |
# gens is now a tuple (x, y) | |
return cls.from_list(rows, cols, items, gens, ring) | |
def from_list(cls, rows, cols, items, gens, ring): | |
# items can be Expr, Poly, or a mix of Expr and Poly | |
items = [_sympify(item) for item in items] | |
if items and all(isinstance(item, Poly) for item in items): | |
polys = True | |
else: | |
polys = False | |
# Identify the ring for the polys | |
if ring is not None: | |
# Parse a domain string like 'QQ[x]' | |
if isinstance(ring, str): | |
ring = Poly(0, Dummy(), domain=ring).domain | |
elif polys: | |
p = items[0] | |
for p2 in items[1:]: | |
p, _ = p.unify(p2) | |
ring = p.domain[p.gens] | |
else: | |
items, info = parallel_poly_from_expr(items, gens, field=True) | |
ring = info['domain'][info['gens']] | |
polys = True | |
# Efficiently convert when all elements are Poly | |
if polys: | |
p_ring = Poly(0, ring.symbols, domain=ring.domain) | |
to_ring = ring.ring.from_list | |
convert_poly = lambda p: to_ring(p.unify(p_ring)[0].rep.to_list()) | |
elements = [convert_poly(p) for p in items] | |
else: | |
convert_expr = ring.from_sympy | |
elements = [convert_expr(e.as_expr()) for e in items] | |
# Convert to domain elements and construct DomainMatrix | |
elements_lol = [[elements[i*cols + j] for j in range(cols)] for i in range(rows)] | |
dm = DomainMatrix(elements_lol, (rows, cols), ring) | |
return cls.from_dm(dm) | |
def from_dm(cls, dm): | |
obj = super().__new__(cls) | |
dm = dm.to_sparse() | |
R = dm.domain | |
obj._dm = dm | |
obj.ring = R | |
obj.domain = R.domain | |
obj.gens = R.symbols | |
return obj | |
def to_Matrix(self): | |
return self._dm.to_Matrix() | |
def from_Matrix(cls, other, *gens, ring=None): | |
return cls(*other.shape, other.flat(), *gens, ring=ring) | |
def set_gens(self, gens): | |
return self.from_Matrix(self.to_Matrix(), gens) | |
def __repr__(self): | |
if self.rows * self.cols: | |
return 'Poly' + repr(self.to_Matrix())[:-1] + f', ring={self.ring})' | |
else: | |
return f'PolyMatrix({self.rows}, {self.cols}, [], ring={self.ring})' | |
def shape(self): | |
return self._dm.shape | |
def rows(self): | |
return self.shape[0] | |
def cols(self): | |
return self.shape[1] | |
def __len__(self): | |
return self.rows * self.cols | |
def __getitem__(self, key): | |
def to_poly(v): | |
ground = self._dm.domain.domain | |
gens = self._dm.domain.symbols | |
return Poly(v.to_dict(), gens, domain=ground) | |
dm = self._dm | |
if isinstance(key, slice): | |
items = dm.flat()[key] | |
return [to_poly(item) for item in items] | |
elif isinstance(key, int): | |
i, j = divmod(key, self.cols) | |
e = dm[i,j] | |
return to_poly(e.element) | |
i, j = key | |
if isinstance(i, int) and isinstance(j, int): | |
return to_poly(dm[i, j].element) | |
else: | |
return self.from_dm(dm[i, j]) | |
def __eq__(self, other): | |
if not isinstance(self, type(other)): | |
return NotImplemented | |
return self._dm == other._dm | |
def __add__(self, other): | |
if isinstance(other, type(self)): | |
return self.from_dm(self._dm + other._dm) | |
return NotImplemented | |
def __sub__(self, other): | |
if isinstance(other, type(self)): | |
return self.from_dm(self._dm - other._dm) | |
return NotImplemented | |
def __mul__(self, other): | |
if isinstance(other, type(self)): | |
return self.from_dm(self._dm * other._dm) | |
elif isinstance(other, int): | |
other = _sympify(other) | |
if isinstance(other, Expr): | |
Kx = self.ring | |
try: | |
other_ds = DomainScalar(Kx.from_sympy(other), Kx) | |
except (CoercionFailed, ValueError): | |
other_ds = DomainScalar.from_sympy(other) | |
return self.from_dm(self._dm * other_ds) | |
return NotImplemented | |
def __rmul__(self, other): | |
if isinstance(other, int): | |
other = _sympify(other) | |
if isinstance(other, Expr): | |
other_ds = DomainScalar.from_sympy(other) | |
return self.from_dm(other_ds * self._dm) | |
return NotImplemented | |
def __truediv__(self, other): | |
if isinstance(other, Poly): | |
other = other.as_expr() | |
elif isinstance(other, int): | |
other = _sympify(other) | |
if not isinstance(other, Expr): | |
return NotImplemented | |
other = self.domain.from_sympy(other) | |
inverse = self.ring.convert_from(1/other, self.domain) | |
inverse = DomainScalar(inverse, self.ring) | |
dm = self._dm * inverse | |
return self.from_dm(dm) | |
def __neg__(self): | |
return self.from_dm(-self._dm) | |
def transpose(self): | |
return self.from_dm(self._dm.transpose()) | |
def row_join(self, other): | |
dm = DomainMatrix.hstack(self._dm, other._dm) | |
return self.from_dm(dm) | |
def col_join(self, other): | |
dm = DomainMatrix.vstack(self._dm, other._dm) | |
return self.from_dm(dm) | |
def applyfunc(self, func): | |
M = self.to_Matrix().applyfunc(func) | |
return self.from_Matrix(M, self.gens) | |
def eye(cls, n, gens): | |
return cls.from_dm(DomainMatrix.eye(n, QQ[gens])) | |
def zeros(cls, m, n, gens): | |
return cls.from_dm(DomainMatrix.zeros((m, n), QQ[gens])) | |
def rref(self, simplify='ignore', normalize_last='ignore'): | |
# If this is K[x] then computes RREF in ground field K. | |
if not (self.domain.is_Field and all(p.is_ground for p in self)): | |
raise ValueError("PolyMatrix rref is only for ground field elements") | |
dm = self._dm | |
dm_ground = dm.convert_to(dm.domain.domain) | |
dm_rref, pivots = dm_ground.rref() | |
dm_rref = dm_rref.convert_to(dm.domain) | |
return self.from_dm(dm_rref), pivots | |
def nullspace(self): | |
# If this is K[x] then computes nullspace in ground field K. | |
if not (self.domain.is_Field and all(p.is_ground for p in self)): | |
raise ValueError("PolyMatrix nullspace is only for ground field elements") | |
dm = self._dm | |
K, Kx = self.domain, self.ring | |
dm_null_rows = dm.convert_to(K).nullspace(divide_last=True).convert_to(Kx) | |
dm_null = dm_null_rows.transpose() | |
dm_basis = [dm_null[:,i] for i in range(dm_null.shape[1])] | |
return [self.from_dm(dmvec) for dmvec in dm_basis] | |
def rank(self): | |
return self.cols - len(self.nullspace()) | |
MutablePolyMatrix = PolyMatrix = MutablePolyDenseMatrix | |