I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,558 @@
from sympy.testing.pytest import raises
from sympy.external.gmpy import GROUND_TYPES
from sympy.polys import ZZ, QQ
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.exceptions import (
DMShapeError, DMNonInvertibleMatrixError, DMDomainError,
DMBadInputError)
def test_DDM_init():
items = [[ZZ(0), ZZ(1), ZZ(2)], [ZZ(3), ZZ(4), ZZ(5)]]
shape = (2, 3)
ddm = DDM(items, shape, ZZ)
assert ddm.shape == shape
assert ddm.rows == 2
assert ddm.cols == 3
assert ddm.domain == ZZ
raises(DMBadInputError, lambda: DDM([[ZZ(2), ZZ(3)]], (2, 2), ZZ))
raises(DMBadInputError, lambda: DDM([[ZZ(1)], [ZZ(2), ZZ(3)]], (2, 2), ZZ))
def test_DDM_getsetitem():
ddm = DDM([[ZZ(2), ZZ(3)], [ZZ(4), ZZ(5)]], (2, 2), ZZ)
assert ddm[0][0] == ZZ(2)
assert ddm[0][1] == ZZ(3)
assert ddm[1][0] == ZZ(4)
assert ddm[1][1] == ZZ(5)
raises(IndexError, lambda: ddm[2][0])
raises(IndexError, lambda: ddm[0][2])
ddm[0][0] = ZZ(-1)
assert ddm[0][0] == ZZ(-1)
def test_DDM_str():
ddm = DDM([[ZZ(0), ZZ(1)], [ZZ(2), ZZ(3)]], (2, 2), ZZ)
if GROUND_TYPES == 'gmpy': # pragma: no cover
assert str(ddm) == '[[0, 1], [2, 3]]'
assert repr(ddm) == 'DDM([[mpz(0), mpz(1)], [mpz(2), mpz(3)]], (2, 2), ZZ)'
else: # pragma: no cover
assert repr(ddm) == 'DDM([[0, 1], [2, 3]], (2, 2), ZZ)'
assert str(ddm) == '[[0, 1], [2, 3]]'
def test_DDM_eq():
items = [[ZZ(0), ZZ(1)], [ZZ(2), ZZ(3)]]
ddm1 = DDM(items, (2, 2), ZZ)
ddm2 = DDM(items, (2, 2), ZZ)
assert (ddm1 == ddm1) is True
assert (ddm1 == items) is False
assert (items == ddm1) is False
assert (ddm1 == ddm2) is True
assert (ddm2 == ddm1) is True
assert (ddm1 != ddm1) is False
assert (ddm1 != items) is True
assert (items != ddm1) is True
assert (ddm1 != ddm2) is False
assert (ddm2 != ddm1) is False
ddm3 = DDM([[ZZ(0), ZZ(1)], [ZZ(3), ZZ(3)]], (2, 2), ZZ)
ddm3 = DDM(items, (2, 2), QQ)
assert (ddm1 == ddm3) is False
assert (ddm3 == ddm1) is False
assert (ddm1 != ddm3) is True
assert (ddm3 != ddm1) is True
def test_DDM_convert_to():
ddm = DDM([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
assert ddm.convert_to(ZZ) == ddm
ddmq = ddm.convert_to(QQ)
assert ddmq.domain == QQ
def test_DDM_zeros():
ddmz = DDM.zeros((3, 4), QQ)
assert list(ddmz) == [[QQ(0)] * 4] * 3
assert ddmz.shape == (3, 4)
assert ddmz.domain == QQ
def test_DDM_ones():
ddmone = DDM.ones((2, 3), QQ)
assert list(ddmone) == [[QQ(1)] * 3] * 2
assert ddmone.shape == (2, 3)
assert ddmone.domain == QQ
def test_DDM_eye():
ddmz = DDM.eye(3, QQ)
f = lambda i, j: QQ(1) if i == j else QQ(0)
assert list(ddmz) == [[f(i, j) for i in range(3)] for j in range(3)]
assert ddmz.shape == (3, 3)
assert ddmz.domain == QQ
def test_DDM_copy():
ddm1 = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
ddm2 = ddm1.copy()
assert (ddm1 == ddm2) is True
ddm1[0][0] = QQ(-1)
assert (ddm1 == ddm2) is False
ddm2[0][0] = QQ(-1)
assert (ddm1 == ddm2) is True
def test_DDM_transpose():
ddm = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
ddmT = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
assert ddm.transpose() == ddmT
ddm02 = DDM([], (0, 2), QQ)
ddm02T = DDM([[], []], (2, 0), QQ)
assert ddm02.transpose() == ddm02T
assert ddm02T.transpose() == ddm02
ddm0 = DDM([], (0, 0), QQ)
assert ddm0.transpose() == ddm0
def test_DDM_add():
A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
B = DDM([[ZZ(3)], [ZZ(4)]], (2, 1), ZZ)
C = DDM([[ZZ(4)], [ZZ(6)]], (2, 1), ZZ)
AQ = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
assert A + B == A.add(B) == C
raises(DMShapeError, lambda: A + DDM([[ZZ(5)]], (1, 1), ZZ))
raises(TypeError, lambda: A + ZZ(1))
raises(TypeError, lambda: ZZ(1) + A)
raises(DMDomainError, lambda: A + AQ)
raises(DMDomainError, lambda: AQ + A)
def test_DDM_sub():
A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
B = DDM([[ZZ(3)], [ZZ(4)]], (2, 1), ZZ)
C = DDM([[ZZ(-2)], [ZZ(-2)]], (2, 1), ZZ)
AQ = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
D = DDM([[ZZ(5)]], (1, 1), ZZ)
assert A - B == A.sub(B) == C
raises(TypeError, lambda: A - ZZ(1))
raises(TypeError, lambda: ZZ(1) - A)
raises(DMShapeError, lambda: A - D)
raises(DMShapeError, lambda: D - A)
raises(DMShapeError, lambda: A.sub(D))
raises(DMShapeError, lambda: D.sub(A))
raises(DMDomainError, lambda: A - AQ)
raises(DMDomainError, lambda: AQ - A)
raises(DMDomainError, lambda: A.sub(AQ))
raises(DMDomainError, lambda: AQ.sub(A))
def test_DDM_neg():
A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
An = DDM([[ZZ(-1)], [ZZ(-2)]], (2, 1), ZZ)
assert -A == A.neg() == An
assert -An == An.neg() == A
def test_DDM_mul():
A = DDM([[ZZ(1)]], (1, 1), ZZ)
A2 = DDM([[ZZ(2)]], (1, 1), ZZ)
assert A * ZZ(2) == A2
assert ZZ(2) * A == A2
raises(TypeError, lambda: [[1]] * A)
raises(TypeError, lambda: A * [[1]])
def test_DDM_matmul():
A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
B = DDM([[ZZ(3), ZZ(4)]], (1, 2), ZZ)
AB = DDM([[ZZ(3), ZZ(4)], [ZZ(6), ZZ(8)]], (2, 2), ZZ)
BA = DDM([[ZZ(11)]], (1, 1), ZZ)
assert A @ B == A.matmul(B) == AB
assert B @ A == B.matmul(A) == BA
raises(TypeError, lambda: A @ 1)
raises(TypeError, lambda: A @ [[3, 4]])
Bq = DDM([[QQ(3), QQ(4)]], (1, 2), QQ)
raises(DMDomainError, lambda: A @ Bq)
raises(DMDomainError, lambda: Bq @ A)
C = DDM([[ZZ(1)]], (1, 1), ZZ)
assert A @ C == A.matmul(C) == A
raises(DMShapeError, lambda: C @ A)
raises(DMShapeError, lambda: C.matmul(A))
Z04 = DDM([], (0, 4), ZZ)
Z40 = DDM([[]]*4, (4, 0), ZZ)
Z50 = DDM([[]]*5, (5, 0), ZZ)
Z05 = DDM([], (0, 5), ZZ)
Z45 = DDM([[0] * 5] * 4, (4, 5), ZZ)
Z54 = DDM([[0] * 4] * 5, (5, 4), ZZ)
Z00 = DDM([], (0, 0), ZZ)
assert Z04 @ Z45 == Z04.matmul(Z45) == Z05
assert Z45 @ Z50 == Z45.matmul(Z50) == Z40
assert Z00 @ Z04 == Z00.matmul(Z04) == Z04
assert Z50 @ Z00 == Z50.matmul(Z00) == Z50
assert Z00 @ Z00 == Z00.matmul(Z00) == Z00
assert Z50 @ Z04 == Z50.matmul(Z04) == Z54
raises(DMShapeError, lambda: Z05 @ Z40)
raises(DMShapeError, lambda: Z05.matmul(Z40))
def test_DDM_hstack():
A = DDM([[ZZ(1), ZZ(2), ZZ(3)]], (1, 3), ZZ)
B = DDM([[ZZ(4), ZZ(5)]], (1, 2), ZZ)
C = DDM([[ZZ(6)]], (1, 1), ZZ)
Ah = A.hstack(B)
assert Ah.shape == (1, 5)
assert Ah.domain == ZZ
assert Ah == DDM([[ZZ(1), ZZ(2), ZZ(3), ZZ(4), ZZ(5)]], (1, 5), ZZ)
Ah = A.hstack(B, C)
assert Ah.shape == (1, 6)
assert Ah.domain == ZZ
assert Ah == DDM([[ZZ(1), ZZ(2), ZZ(3), ZZ(4), ZZ(5), ZZ(6)]], (1, 6), ZZ)
def test_DDM_vstack():
A = DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)]], (3, 1), ZZ)
B = DDM([[ZZ(4)], [ZZ(5)]], (2, 1), ZZ)
C = DDM([[ZZ(6)]], (1, 1), ZZ)
Ah = A.vstack(B)
assert Ah.shape == (5, 1)
assert Ah.domain == ZZ
assert Ah == DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)], [ZZ(4)], [ZZ(5)]], (5, 1), ZZ)
Ah = A.vstack(B, C)
assert Ah.shape == (6, 1)
assert Ah.domain == ZZ
assert Ah == DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)], [ZZ(4)], [ZZ(5)], [ZZ(6)]], (6, 1), ZZ)
def test_DDM_applyfunc():
A = DDM([[ZZ(1), ZZ(2), ZZ(3)]], (1, 3), ZZ)
B = DDM([[ZZ(2), ZZ(4), ZZ(6)]], (1, 3), ZZ)
assert A.applyfunc(lambda x: 2*x, ZZ) == B
def test_DDM_rref():
A = DDM([], (0, 4), QQ)
assert A.rref() == (A, [])
A = DDM([[QQ(0), QQ(1)], [QQ(1), QQ(1)]], (2, 2), QQ)
Ar = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(1)]], (2, 2), QQ)
pivots = [0, 1]
assert A.rref() == (Ar, pivots)
A = DDM([[QQ(1), QQ(2), QQ(1)], [QQ(3), QQ(4), QQ(1)]], (2, 3), QQ)
Ar = DDM([[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]], (2, 3), QQ)
pivots = [0, 1]
assert A.rref() == (Ar, pivots)
A = DDM([[QQ(3), QQ(4), QQ(1)], [QQ(1), QQ(2), QQ(1)]], (2, 3), QQ)
Ar = DDM([[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]], (2, 3), QQ)
pivots = [0, 1]
assert A.rref() == (Ar, pivots)
A = DDM([[QQ(1), QQ(0)], [QQ(1), QQ(3)], [QQ(0), QQ(1)]], (3, 2), QQ)
Ar = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(1)], [QQ(0), QQ(0)]], (3, 2), QQ)
pivots = [0, 1]
assert A.rref() == (Ar, pivots)
A = DDM([[QQ(1), QQ(0), QQ(1)], [QQ(3), QQ(0), QQ(1)]], (2, 3), QQ)
Ar = DDM([[QQ(1), QQ(0), QQ(0)], [QQ(0), QQ(0), QQ(1)]], (2, 3), QQ)
pivots = [0, 2]
assert A.rref() == (Ar, pivots)
def test_DDM_nullspace():
# more tests are in test_nullspace.py
A = DDM([[QQ(1), QQ(1)], [QQ(1), QQ(1)]], (2, 2), QQ)
Anull = DDM([[QQ(-1), QQ(1)]], (1, 2), QQ)
nonpivots = [1]
assert A.nullspace() == (Anull, nonpivots)
def test_DDM_particular():
A = DDM([[QQ(1), QQ(0)]], (1, 2), QQ)
assert A.particular() == DDM.zeros((1, 1), QQ)
def test_DDM_det():
# 0x0 case
A = DDM([], (0, 0), ZZ)
assert A.det() == ZZ(1)
# 1x1 case
A = DDM([[ZZ(2)]], (1, 1), ZZ)
assert A.det() == ZZ(2)
# 2x2 case
A = DDM([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
assert A.det() == ZZ(-2)
# 3x3 with swap
A = DDM([[ZZ(1), ZZ(2), ZZ(3)], [ZZ(1), ZZ(2), ZZ(4)], [ZZ(1), ZZ(2), ZZ(5)]], (3, 3), ZZ)
assert A.det() == ZZ(0)
# 2x2 QQ case
A = DDM([[QQ(1, 2), QQ(1, 2)], [QQ(1, 3), QQ(1, 4)]], (2, 2), QQ)
assert A.det() == QQ(-1, 24)
# Nonsquare error
A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
raises(DMShapeError, lambda: A.det())
# Nonsquare error with empty matrix
A = DDM([], (0, 1), ZZ)
raises(DMShapeError, lambda: A.det())
def test_DDM_inv():
A = DDM([[QQ(1, 1), QQ(2, 1)], [QQ(3, 1), QQ(4, 1)]], (2, 2), QQ)
Ainv = DDM([[QQ(-2, 1), QQ(1, 1)], [QQ(3, 2), QQ(-1, 2)]], (2, 2), QQ)
assert A.inv() == Ainv
A = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
raises(DMShapeError, lambda: A.inv())
A = DDM([[ZZ(2)]], (1, 1), ZZ)
raises(DMDomainError, lambda: A.inv())
A = DDM([], (0, 0), QQ)
assert A.inv() == A
A = DDM([[QQ(1), QQ(2)], [QQ(2), QQ(4)]], (2, 2), QQ)
raises(DMNonInvertibleMatrixError, lambda: A.inv())
def test_DDM_lu():
A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
L, U, swaps = A.lu()
assert L == DDM([[QQ(1), QQ(0)], [QQ(3), QQ(1)]], (2, 2), QQ)
assert U == DDM([[QQ(1), QQ(2)], [QQ(0), QQ(-2)]], (2, 2), QQ)
assert swaps == []
A = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 1], [0, 0, 1, 2]]
Lexp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 1]]
Uexp = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]
to_dom = lambda rows, dom: [[dom(e) for e in row] for row in rows]
A = DDM(to_dom(A, QQ), (4, 4), QQ)
Lexp = DDM(to_dom(Lexp, QQ), (4, 4), QQ)
Uexp = DDM(to_dom(Uexp, QQ), (4, 4), QQ)
L, U, swaps = A.lu()
assert L == Lexp
assert U == Uexp
assert swaps == []
def test_DDM_lu_solve():
# Basic example
A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
x = DDM([[QQ(0)], [QQ(1, 2)]], (2, 1), QQ)
assert A.lu_solve(b) == x
# Example with swaps
A = DDM([[QQ(0), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
assert A.lu_solve(b) == x
# Overdetermined, consistent
A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)], [QQ(5), QQ(6)]], (3, 2), QQ)
b = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
assert A.lu_solve(b) == x
# Overdetermined, inconsistent
b = DDM([[QQ(1)], [QQ(2)], [QQ(4)]], (3, 1), QQ)
raises(DMNonInvertibleMatrixError, lambda: A.lu_solve(b))
# Square, noninvertible
A = DDM([[QQ(1), QQ(2)], [QQ(1), QQ(2)]], (2, 2), QQ)
b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
raises(DMNonInvertibleMatrixError, lambda: A.lu_solve(b))
# Underdetermined
A = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
b = DDM([[QQ(3)]], (1, 1), QQ)
raises(NotImplementedError, lambda: A.lu_solve(b))
# Domain mismatch
bz = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
raises(DMDomainError, lambda: A.lu_solve(bz))
# Shape mismatch
b3 = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
raises(DMShapeError, lambda: A.lu_solve(b3))
def test_DDM_charpoly():
A = DDM([], (0, 0), ZZ)
assert A.charpoly() == [ZZ(1)]
A = DDM([
[ZZ(1), ZZ(2), ZZ(3)],
[ZZ(4), ZZ(5), ZZ(6)],
[ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
Avec = [ZZ(1), ZZ(-15), ZZ(-18), ZZ(0)]
assert A.charpoly() == Avec
A = DDM([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
raises(DMShapeError, lambda: A.charpoly())
def test_DDM_getitem():
dm = DDM([
[ZZ(1), ZZ(2), ZZ(3)],
[ZZ(4), ZZ(5), ZZ(6)],
[ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
assert dm.getitem(1, 1) == ZZ(5)
assert dm.getitem(1, -2) == ZZ(5)
assert dm.getitem(-1, -3) == ZZ(7)
raises(IndexError, lambda: dm.getitem(3, 3))
def test_DDM_setitem():
dm = DDM.zeros((3, 3), ZZ)
dm.setitem(0, 0, 1)
dm.setitem(1, -2, 1)
dm.setitem(-1, -1, 1)
assert dm == DDM.eye(3, ZZ)
raises(IndexError, lambda: dm.setitem(3, 3, 0))
def test_DDM_extract_slice():
dm = DDM([
[ZZ(1), ZZ(2), ZZ(3)],
[ZZ(4), ZZ(5), ZZ(6)],
[ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
assert dm.extract_slice(slice(0, 3), slice(0, 3)) == dm
assert dm.extract_slice(slice(1, 3), slice(-2)) == DDM([[4], [7]], (2, 1), ZZ)
assert dm.extract_slice(slice(1, 3), slice(-2)) == DDM([[4], [7]], (2, 1), ZZ)
assert dm.extract_slice(slice(2, 3), slice(-2)) == DDM([[ZZ(7)]], (1, 1), ZZ)
assert dm.extract_slice(slice(0, 2), slice(-2)) == DDM([[1], [4]], (2, 1), ZZ)
assert dm.extract_slice(slice(-1), slice(-1)) == DDM([[1, 2], [4, 5]], (2, 2), ZZ)
assert dm.extract_slice(slice(2), slice(3, 4)) == DDM([[], []], (2, 0), ZZ)
assert dm.extract_slice(slice(3, 4), slice(2)) == DDM([], (0, 2), ZZ)
assert dm.extract_slice(slice(3, 4), slice(3, 4)) == DDM([], (0, 0), ZZ)
def test_DDM_extract():
dm1 = DDM([
[ZZ(1), ZZ(2), ZZ(3)],
[ZZ(4), ZZ(5), ZZ(6)],
[ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
dm2 = DDM([
[ZZ(6), ZZ(4)],
[ZZ(3), ZZ(1)]], (2, 2), ZZ)
assert dm1.extract([1, 0], [2, 0]) == dm2
assert dm1.extract([-2, 0], [-1, 0]) == dm2
assert dm1.extract([], []) == DDM.zeros((0, 0), ZZ)
assert dm1.extract([1], []) == DDM.zeros((1, 0), ZZ)
assert dm1.extract([], [1]) == DDM.zeros((0, 1), ZZ)
raises(IndexError, lambda: dm2.extract([2], [0]))
raises(IndexError, lambda: dm2.extract([0], [2]))
raises(IndexError, lambda: dm2.extract([-3], [0]))
raises(IndexError, lambda: dm2.extract([0], [-3]))
def test_DDM_flat():
dm = DDM([
[ZZ(6), ZZ(4)],
[ZZ(3), ZZ(1)]], (2, 2), ZZ)
assert dm.flat() == [ZZ(6), ZZ(4), ZZ(3), ZZ(1)]
def test_DDM_is_zero_matrix():
A = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(0)]], (2, 2), QQ)
Azero = DDM.zeros((1, 2), QQ)
assert A.is_zero_matrix() is False
assert Azero.is_zero_matrix() is True
def test_DDM_is_upper():
# Wide matrices:
A = DDM([
[QQ(1), QQ(2), QQ(3), QQ(4)],
[QQ(0), QQ(5), QQ(6), QQ(7)],
[QQ(0), QQ(0), QQ(8), QQ(9)]
], (3, 4), QQ)
B = DDM([
[QQ(1), QQ(2), QQ(3), QQ(4)],
[QQ(0), QQ(5), QQ(6), QQ(7)],
[QQ(0), QQ(7), QQ(8), QQ(9)]
], (3, 4), QQ)
assert A.is_upper() is True
assert B.is_upper() is False
# Tall matrices:
A = DDM([
[QQ(1), QQ(2), QQ(3)],
[QQ(0), QQ(5), QQ(6)],
[QQ(0), QQ(0), QQ(8)],
[QQ(0), QQ(0), QQ(0)]
], (4, 3), QQ)
B = DDM([
[QQ(1), QQ(2), QQ(3)],
[QQ(0), QQ(5), QQ(6)],
[QQ(0), QQ(0), QQ(8)],
[QQ(0), QQ(0), QQ(10)]
], (4, 3), QQ)
assert A.is_upper() is True
assert B.is_upper() is False
def test_DDM_is_lower():
# Tall matrices:
A = DDM([
[QQ(1), QQ(2), QQ(3), QQ(4)],
[QQ(0), QQ(5), QQ(6), QQ(7)],
[QQ(0), QQ(0), QQ(8), QQ(9)]
], (3, 4), QQ).transpose()
B = DDM([
[QQ(1), QQ(2), QQ(3), QQ(4)],
[QQ(0), QQ(5), QQ(6), QQ(7)],
[QQ(0), QQ(7), QQ(8), QQ(9)]
], (3, 4), QQ).transpose()
assert A.is_lower() is True
assert B.is_lower() is False
# Wide matrices:
A = DDM([
[QQ(1), QQ(2), QQ(3)],
[QQ(0), QQ(5), QQ(6)],
[QQ(0), QQ(0), QQ(8)],
[QQ(0), QQ(0), QQ(0)]
], (4, 3), QQ).transpose()
B = DDM([
[QQ(1), QQ(2), QQ(3)],
[QQ(0), QQ(5), QQ(6)],
[QQ(0), QQ(0), QQ(8)],
[QQ(0), QQ(0), QQ(10)]
], (4, 3), QQ).transpose()
assert A.is_lower() is True
assert B.is_lower() is False

View File

@ -0,0 +1,350 @@
from sympy.testing.pytest import raises
from sympy.polys import ZZ, QQ
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.dense import (
ddm_transpose,
ddm_iadd, ddm_isub, ddm_ineg, ddm_imatmul, ddm_imul, ddm_irref,
ddm_idet, ddm_iinv, ddm_ilu, ddm_ilu_split, ddm_ilu_solve, ddm_berk)
from sympy.polys.matrices.exceptions import (
DMDomainError,
DMNonInvertibleMatrixError,
DMNonSquareMatrixError,
DMShapeError,
)
def test_ddm_transpose():
a = [[1, 2], [3, 4]]
assert ddm_transpose(a) == [[1, 3], [2, 4]]
def test_ddm_iadd():
a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]
ddm_iadd(a, b)
assert a == [[6, 8], [10, 12]]
def test_ddm_isub():
a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]
ddm_isub(a, b)
assert a == [[-4, -4], [-4, -4]]
def test_ddm_ineg():
a = [[1, 2], [3, 4]]
ddm_ineg(a)
assert a == [[-1, -2], [-3, -4]]
def test_ddm_matmul():
a = [[1, 2], [3, 4]]
ddm_imul(a, 2)
assert a == [[2, 4], [6, 8]]
a = [[1, 2], [3, 4]]
ddm_imul(a, 0)
assert a == [[0, 0], [0, 0]]
def test_ddm_imatmul():
a = [[1, 2, 3], [4, 5, 6]]
b = [[1, 2], [3, 4], [5, 6]]
c1 = [[0, 0], [0, 0]]
ddm_imatmul(c1, a, b)
assert c1 == [[22, 28], [49, 64]]
c2 = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
ddm_imatmul(c2, b, a)
assert c2 == [[9, 12, 15], [19, 26, 33], [29, 40, 51]]
b3 = [[1], [2], [3]]
c3 = [[0], [0]]
ddm_imatmul(c3, a, b3)
assert c3 == [[14], [32]]
def test_ddm_irref():
# Empty matrix
A = []
Ar = []
pivots = []
assert ddm_irref(A) == pivots
assert A == Ar
# Standard square case
A = [[QQ(0), QQ(1)], [QQ(1), QQ(1)]]
Ar = [[QQ(1), QQ(0)], [QQ(0), QQ(1)]]
pivots = [0, 1]
assert ddm_irref(A) == pivots
assert A == Ar
# m < n case
A = [[QQ(1), QQ(2), QQ(1)], [QQ(3), QQ(4), QQ(1)]]
Ar = [[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]]
pivots = [0, 1]
assert ddm_irref(A) == pivots
assert A == Ar
# same m < n but reversed
A = [[QQ(3), QQ(4), QQ(1)], [QQ(1), QQ(2), QQ(1)]]
Ar = [[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]]
pivots = [0, 1]
assert ddm_irref(A) == pivots
assert A == Ar
# m > n case
A = [[QQ(1), QQ(0)], [QQ(1), QQ(3)], [QQ(0), QQ(1)]]
Ar = [[QQ(1), QQ(0)], [QQ(0), QQ(1)], [QQ(0), QQ(0)]]
pivots = [0, 1]
assert ddm_irref(A) == pivots
assert A == Ar
# Example with missing pivot
A = [[QQ(1), QQ(0), QQ(1)], [QQ(3), QQ(0), QQ(1)]]
Ar = [[QQ(1), QQ(0), QQ(0)], [QQ(0), QQ(0), QQ(1)]]
pivots = [0, 2]
assert ddm_irref(A) == pivots
assert A == Ar
# Example with missing pivot and no replacement
A = [[QQ(0), QQ(1)], [QQ(0), QQ(2)], [QQ(1), QQ(0)]]
Ar = [[QQ(1), QQ(0)], [QQ(0), QQ(1)], [QQ(0), QQ(0)]]
pivots = [0, 1]
assert ddm_irref(A) == pivots
assert A == Ar
def test_ddm_idet():
A = []
assert ddm_idet(A, ZZ) == ZZ(1)
A = [[ZZ(2)]]
assert ddm_idet(A, ZZ) == ZZ(2)
A = [[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]]
assert ddm_idet(A, ZZ) == ZZ(-2)
A = [[ZZ(1), ZZ(2), ZZ(3)], [ZZ(1), ZZ(2), ZZ(4)], [ZZ(1), ZZ(3), ZZ(5)]]
assert ddm_idet(A, ZZ) == ZZ(-1)
A = [[ZZ(1), ZZ(2), ZZ(3)], [ZZ(1), ZZ(2), ZZ(4)], [ZZ(1), ZZ(2), ZZ(5)]]
assert ddm_idet(A, ZZ) == ZZ(0)
A = [[QQ(1, 2), QQ(1, 2)], [QQ(1, 3), QQ(1, 4)]]
assert ddm_idet(A, QQ) == QQ(-1, 24)
def test_ddm_inv():
A = []
Ainv = []
ddm_iinv(Ainv, A, QQ)
assert Ainv == A
A = []
Ainv = []
raises(DMDomainError, lambda: ddm_iinv(Ainv, A, ZZ))
A = [[QQ(1), QQ(2)]]
Ainv = [[QQ(0), QQ(0)]]
raises(DMNonSquareMatrixError, lambda: ddm_iinv(Ainv, A, QQ))
A = [[QQ(1, 1), QQ(2, 1)], [QQ(3, 1), QQ(4, 1)]]
Ainv = [[QQ(0), QQ(0)], [QQ(0), QQ(0)]]
Ainv_expected = [[QQ(-2, 1), QQ(1, 1)], [QQ(3, 2), QQ(-1, 2)]]
ddm_iinv(Ainv, A, QQ)
assert Ainv == Ainv_expected
A = [[QQ(1, 1), QQ(2, 1)], [QQ(2, 1), QQ(4, 1)]]
Ainv = [[QQ(0), QQ(0)], [QQ(0), QQ(0)]]
raises(DMNonInvertibleMatrixError, lambda: ddm_iinv(Ainv, A, QQ))
def test_ddm_ilu():
A = []
Alu = []
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
A = [[]]
Alu = [[]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
A = [[QQ(1), QQ(2)], [QQ(3), QQ(4)]]
Alu = [[QQ(1), QQ(2)], [QQ(3), QQ(-2)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
A = [[QQ(0), QQ(2)], [QQ(3), QQ(4)]]
Alu = [[QQ(3), QQ(4)], [QQ(0), QQ(2)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == [(0, 1)]
A = [[QQ(1), QQ(2), QQ(3)], [QQ(4), QQ(5), QQ(6)], [QQ(7), QQ(8), QQ(9)]]
Alu = [[QQ(1), QQ(2), QQ(3)], [QQ(4), QQ(-3), QQ(-6)], [QQ(7), QQ(2), QQ(0)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
A = [[QQ(0), QQ(1), QQ(2)], [QQ(0), QQ(1), QQ(3)], [QQ(1), QQ(1), QQ(2)]]
Alu = [[QQ(1), QQ(1), QQ(2)], [QQ(0), QQ(1), QQ(3)], [QQ(0), QQ(1), QQ(-1)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == [(0, 2)]
A = [[QQ(1), QQ(2), QQ(3)], [QQ(4), QQ(5), QQ(6)]]
Alu = [[QQ(1), QQ(2), QQ(3)], [QQ(4), QQ(-3), QQ(-6)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
A = [[QQ(1), QQ(2)], [QQ(3), QQ(4)], [QQ(5), QQ(6)]]
Alu = [[QQ(1), QQ(2)], [QQ(3), QQ(-2)], [QQ(5), QQ(2)]]
swaps = ddm_ilu(A)
assert A == Alu
assert swaps == []
def test_ddm_ilu_split():
U = []
L = []
Uexp = []
Lexp = []
swaps = ddm_ilu_split(L, U, QQ)
assert U == Uexp
assert L == Lexp
assert swaps == []
U = [[]]
L = [[QQ(1)]]
Uexp = [[]]
Lexp = [[QQ(1)]]
swaps = ddm_ilu_split(L, U, QQ)
assert U == Uexp
assert L == Lexp
assert swaps == []
U = [[QQ(1), QQ(2)], [QQ(3), QQ(4)]]
L = [[QQ(1), QQ(0)], [QQ(0), QQ(1)]]
Uexp = [[QQ(1), QQ(2)], [QQ(0), QQ(-2)]]
Lexp = [[QQ(1), QQ(0)], [QQ(3), QQ(1)]]
swaps = ddm_ilu_split(L, U, QQ)
assert U == Uexp
assert L == Lexp
assert swaps == []
U = [[QQ(1), QQ(2), QQ(3)], [QQ(4), QQ(5), QQ(6)]]
L = [[QQ(1), QQ(0)], [QQ(0), QQ(1)]]
Uexp = [[QQ(1), QQ(2), QQ(3)], [QQ(0), QQ(-3), QQ(-6)]]
Lexp = [[QQ(1), QQ(0)], [QQ(4), QQ(1)]]
swaps = ddm_ilu_split(L, U, QQ)
assert U == Uexp
assert L == Lexp
assert swaps == []
U = [[QQ(1), QQ(2)], [QQ(3), QQ(4)], [QQ(5), QQ(6)]]
L = [[QQ(1), QQ(0), QQ(0)], [QQ(0), QQ(1), QQ(0)], [QQ(0), QQ(0), QQ(1)]]
Uexp = [[QQ(1), QQ(2)], [QQ(0), QQ(-2)], [QQ(0), QQ(0)]]
Lexp = [[QQ(1), QQ(0), QQ(0)], [QQ(3), QQ(1), QQ(0)], [QQ(5), QQ(2), QQ(1)]]
swaps = ddm_ilu_split(L, U, QQ)
assert U == Uexp
assert L == Lexp
assert swaps == []
def test_ddm_ilu_solve():
# Basic example
# A = [[QQ(1), QQ(2)], [QQ(3), QQ(4)]]
U = [[QQ(1), QQ(2)], [QQ(0), QQ(-2)]]
L = [[QQ(1), QQ(0)], [QQ(3), QQ(1)]]
swaps = []
b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
x = DDM([[QQ(0)], [QQ(0)]], (2, 1), QQ)
xexp = DDM([[QQ(0)], [QQ(1, 2)]], (2, 1), QQ)
ddm_ilu_solve(x, L, U, swaps, b)
assert x == xexp
# Example with swaps
# A = [[QQ(0), QQ(2)], [QQ(3), QQ(4)]]
U = [[QQ(3), QQ(4)], [QQ(0), QQ(2)]]
L = [[QQ(1), QQ(0)], [QQ(0), QQ(1)]]
swaps = [(0, 1)]
b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
x = DDM([[QQ(0)], [QQ(0)]], (2, 1), QQ)
xexp = DDM([[QQ(0)], [QQ(1, 2)]], (2, 1), QQ)
ddm_ilu_solve(x, L, U, swaps, b)
assert x == xexp
# Overdetermined, consistent
# A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)], [QQ(5), QQ(6)]], (3, 2), QQ)
U = [[QQ(1), QQ(2)], [QQ(0), QQ(-2)], [QQ(0), QQ(0)]]
L = [[QQ(1), QQ(0), QQ(0)], [QQ(3), QQ(1), QQ(0)], [QQ(5), QQ(2), QQ(1)]]
swaps = []
b = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
x = DDM([[QQ(0)], [QQ(0)]], (2, 1), QQ)
xexp = DDM([[QQ(0)], [QQ(1, 2)]], (2, 1), QQ)
ddm_ilu_solve(x, L, U, swaps, b)
assert x == xexp
# Overdetermined, inconsistent
b = DDM([[QQ(1)], [QQ(2)], [QQ(4)]], (3, 1), QQ)
raises(DMNonInvertibleMatrixError, lambda: ddm_ilu_solve(x, L, U, swaps, b))
# Square, noninvertible
# A = DDM([[QQ(1), QQ(2)], [QQ(1), QQ(2)]], (2, 2), QQ)
U = [[QQ(1), QQ(2)], [QQ(0), QQ(0)]]
L = [[QQ(1), QQ(0)], [QQ(1), QQ(1)]]
swaps = []
b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
raises(DMNonInvertibleMatrixError, lambda: ddm_ilu_solve(x, L, U, swaps, b))
# Underdetermined
# A = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
U = [[QQ(1), QQ(2)]]
L = [[QQ(1)]]
swaps = []
b = DDM([[QQ(3)]], (1, 1), QQ)
raises(NotImplementedError, lambda: ddm_ilu_solve(x, L, U, swaps, b))
# Shape mismatch
b3 = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
raises(DMShapeError, lambda: ddm_ilu_solve(x, L, U, swaps, b3))
# Empty shape mismatch
U = [[QQ(1)]]
L = [[QQ(1)]]
swaps = []
x = [[QQ(1)]]
b = []
raises(DMShapeError, lambda: ddm_ilu_solve(x, L, U, swaps, b))
# Empty system
U = []
L = []
swaps = []
b = []
x = []
ddm_ilu_solve(x, L, U, swaps, b)
assert x == []
def test_ddm_charpoly():
A = []
assert ddm_berk(A, ZZ) == [[ZZ(1)]]
A = [[ZZ(1), ZZ(2), ZZ(3)], [ZZ(4), ZZ(5), ZZ(6)], [ZZ(7), ZZ(8), ZZ(9)]]
Avec = [[ZZ(1)], [ZZ(-15)], [ZZ(-18)], [ZZ(0)]]
assert ddm_berk(A, ZZ) == Avec
A = DDM([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
raises(DMShapeError, lambda: ddm_berk(A, ZZ))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
from sympy.testing.pytest import raises
from sympy.core.symbol import S
from sympy.polys import ZZ, QQ
from sympy.polys.matrices.domainscalar import DomainScalar
from sympy.polys.matrices.domainmatrix import DomainMatrix
def test_DomainScalar___new__():
raises(TypeError, lambda: DomainScalar(ZZ(1), QQ))
raises(TypeError, lambda: DomainScalar(ZZ(1), 1))
def test_DomainScalar_new():
A = DomainScalar(ZZ(1), ZZ)
B = A.new(ZZ(4), ZZ)
assert B == DomainScalar(ZZ(4), ZZ)
def test_DomainScalar_repr():
A = DomainScalar(ZZ(1), ZZ)
assert repr(A) in {'1', 'mpz(1)'}
def test_DomainScalar_from_sympy():
expr = S(1)
B = DomainScalar.from_sympy(expr)
assert B == DomainScalar(ZZ(1), ZZ)
def test_DomainScalar_to_sympy():
B = DomainScalar(ZZ(1), ZZ)
expr = B.to_sympy()
assert expr.is_Integer and expr == 1
def test_DomainScalar_to_domain():
A = DomainScalar(ZZ(1), ZZ)
B = A.to_domain(QQ)
assert B == DomainScalar(QQ(1), QQ)
def test_DomainScalar_convert_to():
A = DomainScalar(ZZ(1), ZZ)
B = A.convert_to(QQ)
assert B == DomainScalar(QQ(1), QQ)
def test_DomainScalar_unify():
A = DomainScalar(ZZ(1), ZZ)
B = DomainScalar(QQ(2), QQ)
A, B = A.unify(B)
assert A.domain == B.domain == QQ
def test_DomainScalar_add():
A = DomainScalar(ZZ(1), ZZ)
B = DomainScalar(QQ(2), QQ)
assert A + B == DomainScalar(QQ(3), QQ)
raises(TypeError, lambda: A + 1.5)
def test_DomainScalar_sub():
A = DomainScalar(ZZ(1), ZZ)
B = DomainScalar(QQ(2), QQ)
assert A - B == DomainScalar(QQ(-1), QQ)
raises(TypeError, lambda: A - 1.5)
def test_DomainScalar_mul():
A = DomainScalar(ZZ(1), ZZ)
B = DomainScalar(QQ(2), QQ)
dm = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
assert A * B == DomainScalar(QQ(2), QQ)
assert A * dm == dm
assert B * 2 == DomainScalar(QQ(4), QQ)
raises(TypeError, lambda: A * 1.5)
def test_DomainScalar_floordiv():
A = DomainScalar(ZZ(-5), ZZ)
B = DomainScalar(QQ(2), QQ)
assert A // B == DomainScalar(QQ(-5, 2), QQ)
C = DomainScalar(ZZ(2), ZZ)
assert A // C == DomainScalar(ZZ(-3), ZZ)
raises(TypeError, lambda: A // 1.5)
def test_DomainScalar_mod():
A = DomainScalar(ZZ(5), ZZ)
B = DomainScalar(QQ(2), QQ)
assert A % B == DomainScalar(QQ(0), QQ)
C = DomainScalar(ZZ(2), ZZ)
assert A % C == DomainScalar(ZZ(1), ZZ)
raises(TypeError, lambda: A % 1.5)
def test_DomainScalar_divmod():
A = DomainScalar(ZZ(5), ZZ)
B = DomainScalar(QQ(2), QQ)
assert divmod(A, B) == (DomainScalar(QQ(5, 2), QQ), DomainScalar(QQ(0), QQ))
C = DomainScalar(ZZ(2), ZZ)
assert divmod(A, C) == (DomainScalar(ZZ(2), ZZ), DomainScalar(ZZ(1), ZZ))
raises(TypeError, lambda: divmod(A, 1.5))
def test_DomainScalar_pow():
A = DomainScalar(ZZ(-5), ZZ)
B = A**(2)
assert B == DomainScalar(ZZ(25), ZZ)
raises(TypeError, lambda: A**(1.5))
def test_DomainScalar_pos():
A = DomainScalar(QQ(2), QQ)
B = DomainScalar(QQ(2), QQ)
assert +A == B
def test_DomainScalar_neg():
A = DomainScalar(QQ(2), QQ)
B = DomainScalar(QQ(-2), QQ)
assert -A == B
def test_DomainScalar_eq():
A = DomainScalar(QQ(2), QQ)
assert A == A
B = DomainScalar(ZZ(-5), ZZ)
assert A != B
C = DomainScalar(ZZ(2), ZZ)
assert A != C
D = [1]
assert A != D
def test_DomainScalar_isZero():
A = DomainScalar(ZZ(0), ZZ)
assert A.is_zero() == True
B = DomainScalar(ZZ(1), ZZ)
assert B.is_zero() == False
def test_DomainScalar_isOne():
A = DomainScalar(ZZ(1), ZZ)
assert A.is_one() == True
B = DomainScalar(ZZ(0), ZZ)
assert B.is_one() == False

View File

@ -0,0 +1,90 @@
"""
Tests for the sympy.polys.matrices.eigen module
"""
from sympy.core.singleton import S
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.matrices.dense import Matrix
from sympy.polys.agca.extensions import FiniteExtension
from sympy.polys.domains import QQ
from sympy.polys.polytools import Poly
from sympy.polys.rootoftools import CRootOf
from sympy.polys.matrices.domainmatrix import DomainMatrix
from sympy.polys.matrices.eigen import dom_eigenvects, dom_eigenvects_to_sympy
def test_dom_eigenvects_rational():
# Rational eigenvalues
A = DomainMatrix([[QQ(1), QQ(2)], [QQ(1), QQ(2)]], (2, 2), QQ)
rational_eigenvects = [
(QQ, QQ(3), 1, DomainMatrix([[QQ(1), QQ(1)]], (1, 2), QQ)),
(QQ, QQ(0), 1, DomainMatrix([[QQ(-2), QQ(1)]], (1, 2), QQ)),
]
assert dom_eigenvects(A) == (rational_eigenvects, [])
# Test converting to Expr:
sympy_eigenvects = [
(S(3), 1, [Matrix([1, 1])]),
(S(0), 1, [Matrix([-2, 1])]),
]
assert dom_eigenvects_to_sympy(rational_eigenvects, [], Matrix) == sympy_eigenvects
def test_dom_eigenvects_algebraic():
# Algebraic eigenvalues
A = DomainMatrix([[QQ(1), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
Avects = dom_eigenvects(A)
# Extract the dummy to build the expected result:
lamda = Avects[1][0][1].gens[0]
irreducible = Poly(lamda**2 - 5*lamda - 2, lamda, domain=QQ)
K = FiniteExtension(irreducible)
KK = K.from_sympy
algebraic_eigenvects = [
(K, irreducible, 1, DomainMatrix([[KK((lamda-4)/3), KK(1)]], (1, 2), K)),
]
assert Avects == ([], algebraic_eigenvects)
# Test converting to Expr:
sympy_eigenvects = [
(S(5)/2 - sqrt(33)/2, 1, [Matrix([[-sqrt(33)/6 - S(1)/2], [1]])]),
(S(5)/2 + sqrt(33)/2, 1, [Matrix([[-S(1)/2 + sqrt(33)/6], [1]])]),
]
assert dom_eigenvects_to_sympy([], algebraic_eigenvects, Matrix) == sympy_eigenvects
def test_dom_eigenvects_rootof():
# Algebraic eigenvalues
A = DomainMatrix([
[0, 0, 0, 0, -1],
[1, 0, 0, 0, 1],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0]], (5, 5), QQ)
Avects = dom_eigenvects(A)
# Extract the dummy to build the expected result:
lamda = Avects[1][0][1].gens[0]
irreducible = Poly(lamda**5 - lamda + 1, lamda, domain=QQ)
K = FiniteExtension(irreducible)
KK = K.from_sympy
algebraic_eigenvects = [
(K, irreducible, 1,
DomainMatrix([
[KK(lamda**4-1), KK(lamda**3), KK(lamda**2), KK(lamda), KK(1)]
], (1, 5), K)),
]
assert Avects == ([], algebraic_eigenvects)
# Test converting to Expr (slow):
l0, l1, l2, l3, l4 = [CRootOf(lamda**5 - lamda + 1, i) for i in range(5)]
sympy_eigenvects = [
(l0, 1, [Matrix([-1 + l0**4, l0**3, l0**2, l0, 1])]),
(l1, 1, [Matrix([-1 + l1**4, l1**3, l1**2, l1, 1])]),
(l2, 1, [Matrix([-1 + l2**4, l2**3, l2**2, l2, 1])]),
(l3, 1, [Matrix([-1 + l3**4, l3**3, l3**2, l3, 1])]),
(l4, 1, [Matrix([-1 + l4**4, l4**3, l4**2, l4, 1])]),
]
assert dom_eigenvects_to_sympy([], algebraic_eigenvects, Matrix) == sympy_eigenvects

View File

@ -0,0 +1,193 @@
from sympy import ZZ, Matrix
from sympy.polys.matrices import DM, DomainMatrix
from sympy.polys.matrices.dense import ddm_iinv
from sympy.polys.matrices.exceptions import DMNonInvertibleMatrixError
from sympy.matrices.exceptions import NonInvertibleMatrixError
import pytest
from sympy.testing.pytest import raises
from sympy.core.numbers import all_close
from sympy.abc import x
# Examples are given as adjugate matrix and determinant adj_det should match
# these exactly but inv_den only matches after cancel_denom.
INVERSE_EXAMPLES = [
(
'zz_1',
DomainMatrix([], (0, 0), ZZ),
DomainMatrix([], (0, 0), ZZ),
ZZ(1),
),
(
'zz_2',
DM([[2]], ZZ),
DM([[1]], ZZ),
ZZ(2),
),
(
'zz_3',
DM([[2, 0],
[0, 2]], ZZ),
DM([[2, 0],
[0, 2]], ZZ),
ZZ(4),
),
(
'zz_4',
DM([[1, 2],
[3, 4]], ZZ),
DM([[ 4, -2],
[-3, 1]], ZZ),
ZZ(-2),
),
(
'zz_5',
DM([[2, 2, 0],
[0, 2, 2],
[0, 0, 2]], ZZ),
DM([[4, -4, 4],
[0, 4, -4],
[0, 0, 4]], ZZ),
ZZ(8),
),
(
'zz_6',
DM([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], ZZ),
DM([[-3, 6, -3],
[ 6, -12, 6],
[-3, 6, -3]], ZZ),
ZZ(0),
),
]
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_Matrix_inv(name, A, A_inv, den):
def _check(**kwargs):
if den != 0:
assert A.inv(**kwargs) == A_inv
else:
raises(NonInvertibleMatrixError, lambda: A.inv(**kwargs))
K = A.domain
A = A.to_Matrix()
A_inv = A_inv.to_Matrix() / K.to_sympy(den)
_check()
for method in ['GE', 'LU', 'ADJ', 'CH', 'LDL', 'QR']:
_check(method=method)
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_dm_inv_den(name, A, A_inv, den):
if den != 0:
A_inv_f, den_f = A.inv_den()
assert A_inv_f.cancel_denom(den_f) == A_inv.cancel_denom(den)
else:
raises(DMNonInvertibleMatrixError, lambda: A.inv_den())
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_dm_inv(name, A, A_inv, den):
A = A.to_field()
if den != 0:
A_inv = A_inv.to_field() / den
assert A.inv() == A_inv
else:
raises(DMNonInvertibleMatrixError, lambda: A.inv())
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_ddm_inv(name, A, A_inv, den):
A = A.to_field().to_ddm()
if den != 0:
A_inv = (A_inv.to_field() / den).to_ddm()
assert A.inv() == A_inv
else:
raises(DMNonInvertibleMatrixError, lambda: A.inv())
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_sdm_inv(name, A, A_inv, den):
A = A.to_field().to_sdm()
if den != 0:
A_inv = (A_inv.to_field() / den).to_sdm()
assert A.inv() == A_inv
else:
raises(DMNonInvertibleMatrixError, lambda: A.inv())
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_dense_ddm_iinv(name, A, A_inv, den):
A = A.to_field().to_ddm().copy()
K = A.domain
A_result = A.copy()
if den != 0:
A_inv = (A_inv.to_field() / den).to_ddm()
ddm_iinv(A_result, A, K)
assert A_result == A_inv
else:
raises(DMNonInvertibleMatrixError, lambda: ddm_iinv(A_result, A, K))
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_Matrix_adjugate(name, A, A_inv, den):
A = A.to_Matrix()
A_inv = A_inv.to_Matrix()
assert A.adjugate() == A_inv
for method in ["bareiss", "berkowitz", "bird", "laplace", "lu"]:
assert A.adjugate(method=method) == A_inv
@pytest.mark.parametrize('name, A, A_inv, den', INVERSE_EXAMPLES)
def test_dm_adj_det(name, A, A_inv, den):
assert A.adj_det() == (A_inv, den)
def test_inverse_inexact():
M = Matrix([[x-0.3, -0.06, -0.22],
[-0.46, x-0.48, -0.41],
[-0.14, -0.39, x-0.64]])
Mn = Matrix([[1.0*x**2 - 1.12*x + 0.1473, 0.06*x + 0.0474, 0.22*x - 0.081],
[0.46*x - 0.237, 1.0*x**2 - 0.94*x + 0.1612, 0.41*x - 0.0218],
[0.14*x + 0.1122, 0.39*x - 0.1086, 1.0*x**2 - 0.78*x + 0.1164]])
d = 1.0*x**3 - 1.42*x**2 + 0.4249*x - 0.0546540000000002
Mi = Mn / d
M_dm = M.to_DM()
M_dmd = M_dm.to_dense()
M_dm_num, M_dm_den = M_dm.inv_den()
M_dmd_num, M_dmd_den = M_dmd.inv_den()
# XXX: We don't check M_dm().to_field().inv() which currently uses division
# and produces a more complicate result from gcd cancellation failing.
# DomainMatrix.inv() over RR(x) should be changed to clear denominators and
# use DomainMatrix.inv_den().
Minvs = [
M.inv(),
(M_dm_num.to_field() / M_dm_den).to_Matrix(),
(M_dmd_num.to_field() / M_dmd_den).to_Matrix(),
M_dm_num.to_Matrix() / M_dm_den.as_expr(),
M_dmd_num.to_Matrix() / M_dmd_den.as_expr(),
]
for Minv in Minvs:
for Mi1, Mi2 in zip(Minv.flat(), Mi.flat()):
assert all_close(Mi2, Mi1)

View File

@ -0,0 +1,111 @@
#
# test_linsolve.py
#
# Test the internal implementation of linsolve.
#
from sympy.testing.pytest import raises
from sympy.core.numbers import I
from sympy.core.relational import Eq
from sympy.core.singleton import S
from sympy.abc import x, y, z
from sympy.polys.matrices.linsolve import _linsolve
from sympy.polys.solvers import PolyNonlinearError
def test__linsolve():
assert _linsolve([], [x]) == {x:x}
assert _linsolve([S.Zero], [x]) == {x:x}
assert _linsolve([x-1,x-2], [x]) is None
assert _linsolve([x-1], [x]) == {x:1}
assert _linsolve([x-1, y], [x, y]) == {x:1, y:S.Zero}
assert _linsolve([2*I], [x]) is None
raises(PolyNonlinearError, lambda: _linsolve([x*(1 + x)], [x]))
def test__linsolve_float():
# This should give the exact answer:
eqs = [
y - x,
y - 0.0216 * x
]
sol = {x:0.0, y:0.0}
assert _linsolve(eqs, (x, y)) == sol
# Other cases should be close to eps
def all_close(sol1, sol2, eps=1e-15):
close = lambda a, b: abs(a - b) < eps
assert sol1.keys() == sol2.keys()
return all(close(sol1[s], sol2[s]) for s in sol1)
eqs = [
0.8*x + 0.8*z + 0.2,
0.9*x + 0.7*y + 0.2*z + 0.9,
0.7*x + 0.2*y + 0.2*z + 0.5
]
sol_exact = {x:-29/42, y:-11/21, z:37/84}
sol_linsolve = _linsolve(eqs, [x,y,z])
assert all_close(sol_exact, sol_linsolve)
eqs = [
0.9*x + 0.3*y + 0.4*z + 0.6,
0.6*x + 0.9*y + 0.1*z + 0.7,
0.4*x + 0.6*y + 0.9*z + 0.5
]
sol_exact = {x:-88/175, y:-46/105, z:-1/25}
sol_linsolve = _linsolve(eqs, [x,y,z])
assert all_close(sol_exact, sol_linsolve)
eqs = [
0.4*x + 0.3*y + 0.6*z + 0.7,
0.4*x + 0.3*y + 0.9*z + 0.9,
0.7*x + 0.9*y,
]
sol_exact = {x:-9/5, y:7/5, z:-2/3}
sol_linsolve = _linsolve(eqs, [x,y,z])
assert all_close(sol_exact, sol_linsolve)
eqs = [
x*(0.7 + 0.6*I) + y*(0.4 + 0.7*I) + z*(0.9 + 0.1*I) + 0.5,
0.2*I*x + 0.2*I*y + z*(0.9 + 0.2*I) + 0.1,
x*(0.9 + 0.7*I) + y*(0.9 + 0.7*I) + z*(0.9 + 0.4*I) + 0.4,
]
sol_exact = {
x:-6157/7995 - 411/5330*I,
y:8519/15990 + 1784/7995*I,
z:-34/533 + 107/1599*I,
}
sol_linsolve = _linsolve(eqs, [x,y,z])
assert all_close(sol_exact, sol_linsolve)
# XXX: This system for x and y over RR(z) is problematic.
#
# eqs = [
# x*(0.2*z + 0.9) + y*(0.5*z + 0.8) + 0.6,
# 0.1*x*z + y*(0.1*z + 0.6) + 0.9,
# ]
#
# linsolve(eqs, [x, y])
# The solution for x comes out as
#
# -3.9e-5*z**2 - 3.6e-5*z - 8.67361737988404e-20
# x = ----------------------------------------------
# 3.0e-6*z**3 - 1.3e-5*z**2 - 5.4e-5*z
#
# The 8e-20 in the numerator should be zero which would allow z to cancel
# from top and bottom. It should be possible to avoid this somehow because
# the inverse of the matrix only has a quadratic factor (the determinant)
# in the denominator.
def test__linsolve_deprecated():
raises(PolyNonlinearError, lambda:
_linsolve([Eq(x**2, x**2 + y)], [x, y]))
raises(PolyNonlinearError, lambda:
_linsolve([(x + y)**2 - x**2], [x]))
raises(PolyNonlinearError, lambda:
_linsolve([Eq((x + y)**2, x**2)], [x]))

View File

@ -0,0 +1,145 @@
from sympy.polys.domains import ZZ, QQ
from sympy.polys.matrices import DM
from sympy.polys.matrices.domainmatrix import DomainMatrix
from sympy.polys.matrices.exceptions import DMRankError, DMValueError, DMShapeError, DMDomainError
from sympy.polys.matrices.lll import _ddm_lll, ddm_lll, ddm_lll_transform
from sympy.testing.pytest import raises
def test_lll():
normal_test_data = [
(
DM([[1, 0, 0, 0, -20160],
[0, 1, 0, 0, 33768],
[0, 0, 1, 0, 39578],
[0, 0, 0, 1, 47757]], ZZ),
DM([[10, -3, -2, 8, -4],
[3, -9, 8, 1, -11],
[-3, 13, -9, -3, -9],
[-12, -7, -11, 9, -1]], ZZ)
),
(
DM([[20, 52, 3456],
[14, 31, -1],
[34, -442, 0]], ZZ),
DM([[14, 31, -1],
[188, -101, -11],
[236, 13, 3443]], ZZ)
),
(
DM([[34, -1, -86, 12],
[-54, 34, 55, 678],
[23, 3498, 234, 6783],
[87, 49, 665, 11]], ZZ),
DM([[34, -1, -86, 12],
[291, 43, 149, 83],
[-54, 34, 55, 678],
[-189, 3077, -184, -223]], ZZ)
)
]
delta = QQ(5, 6)
for basis_dm, reduced_dm in normal_test_data:
reduced = _ddm_lll(basis_dm.rep.to_ddm(), delta=delta)[0]
assert reduced == reduced_dm.rep.to_ddm()
reduced = ddm_lll(basis_dm.rep.to_ddm(), delta=delta)
assert reduced == reduced_dm.rep.to_ddm()
reduced, transform = _ddm_lll(basis_dm.rep.to_ddm(), delta=delta, return_transform=True)
assert reduced == reduced_dm.rep.to_ddm()
assert transform.matmul(basis_dm.rep.to_ddm()) == reduced_dm.rep.to_ddm()
reduced, transform = ddm_lll_transform(basis_dm.rep.to_ddm(), delta=delta)
assert reduced == reduced_dm.rep.to_ddm()
assert transform.matmul(basis_dm.rep.to_ddm()) == reduced_dm.rep.to_ddm()
reduced = basis_dm.rep.lll(delta=delta)
assert reduced == reduced_dm.rep
reduced, transform = basis_dm.rep.lll_transform(delta=delta)
assert reduced == reduced_dm.rep
assert transform.matmul(basis_dm.rep) == reduced_dm.rep
reduced = basis_dm.rep.to_sdm().lll(delta=delta)
assert reduced == reduced_dm.rep.to_sdm()
reduced, transform = basis_dm.rep.to_sdm().lll_transform(delta=delta)
assert reduced == reduced_dm.rep.to_sdm()
assert transform.matmul(basis_dm.rep.to_sdm()) == reduced_dm.rep.to_sdm()
reduced = basis_dm.lll(delta=delta)
assert reduced == reduced_dm
reduced, transform = basis_dm.lll_transform(delta=delta)
assert reduced == reduced_dm
assert transform.matmul(basis_dm) == reduced_dm
def test_lll_linear_dependent():
linear_dependent_test_data = [
DM([[0, -1, -2, -3],
[1, 0, -1, -2],
[2, 1, 0, -1],
[3, 2, 1, 0]], ZZ),
DM([[1, 0, 0, 1],
[0, 1, 0, 1],
[0, 0, 1, 1],
[1, 2, 3, 6]], ZZ),
DM([[3, -5, 1],
[4, 6, 0],
[10, -4, 2]], ZZ)
]
for not_basis in linear_dependent_test_data:
raises(DMRankError, lambda: _ddm_lll(not_basis.rep.to_ddm()))
raises(DMRankError, lambda: ddm_lll(not_basis.rep.to_ddm()))
raises(DMRankError, lambda: not_basis.rep.lll())
raises(DMRankError, lambda: not_basis.rep.to_sdm().lll())
raises(DMRankError, lambda: not_basis.lll())
raises(DMRankError, lambda: _ddm_lll(not_basis.rep.to_ddm(), return_transform=True))
raises(DMRankError, lambda: ddm_lll_transform(not_basis.rep.to_ddm()))
raises(DMRankError, lambda: not_basis.rep.lll_transform())
raises(DMRankError, lambda: not_basis.rep.to_sdm().lll_transform())
raises(DMRankError, lambda: not_basis.lll_transform())
def test_lll_wrong_delta():
dummy_matrix = DomainMatrix.ones((3, 3), ZZ)
for wrong_delta in [QQ(-1, 4), QQ(0, 1), QQ(1, 4), QQ(1, 1), QQ(100, 1)]:
raises(DMValueError, lambda: _ddm_lll(dummy_matrix.rep, delta=wrong_delta))
raises(DMValueError, lambda: ddm_lll(dummy_matrix.rep, delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.rep.lll(delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.rep.to_sdm().lll(delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.lll(delta=wrong_delta))
raises(DMValueError, lambda: _ddm_lll(dummy_matrix.rep, delta=wrong_delta, return_transform=True))
raises(DMValueError, lambda: ddm_lll_transform(dummy_matrix.rep, delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.rep.lll_transform(delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.rep.to_sdm().lll_transform(delta=wrong_delta))
raises(DMValueError, lambda: dummy_matrix.lll_transform(delta=wrong_delta))
def test_lll_wrong_shape():
wrong_shape_matrix = DomainMatrix.ones((4, 3), ZZ)
raises(DMShapeError, lambda: _ddm_lll(wrong_shape_matrix.rep))
raises(DMShapeError, lambda: ddm_lll(wrong_shape_matrix.rep))
raises(DMShapeError, lambda: wrong_shape_matrix.rep.lll())
raises(DMShapeError, lambda: wrong_shape_matrix.rep.to_sdm().lll())
raises(DMShapeError, lambda: wrong_shape_matrix.lll())
raises(DMShapeError, lambda: _ddm_lll(wrong_shape_matrix.rep, return_transform=True))
raises(DMShapeError, lambda: ddm_lll_transform(wrong_shape_matrix.rep))
raises(DMShapeError, lambda: wrong_shape_matrix.rep.lll_transform())
raises(DMShapeError, lambda: wrong_shape_matrix.rep.to_sdm().lll_transform())
raises(DMShapeError, lambda: wrong_shape_matrix.lll_transform())
def test_lll_wrong_domain():
wrong_domain_matrix = DomainMatrix.ones((3, 3), QQ)
raises(DMDomainError, lambda: _ddm_lll(wrong_domain_matrix.rep))
raises(DMDomainError, lambda: ddm_lll(wrong_domain_matrix.rep))
raises(DMDomainError, lambda: wrong_domain_matrix.rep.lll())
raises(DMDomainError, lambda: wrong_domain_matrix.rep.to_sdm().lll())
raises(DMDomainError, lambda: wrong_domain_matrix.lll())
raises(DMDomainError, lambda: _ddm_lll(wrong_domain_matrix.rep, return_transform=True))
raises(DMDomainError, lambda: ddm_lll_transform(wrong_domain_matrix.rep))
raises(DMDomainError, lambda: wrong_domain_matrix.rep.lll_transform())
raises(DMDomainError, lambda: wrong_domain_matrix.rep.to_sdm().lll_transform())
raises(DMDomainError, lambda: wrong_domain_matrix.lll_transform())

View File

@ -0,0 +1,75 @@
from sympy.testing.pytest import raises
from sympy.core.symbol import Symbol
from sympy.polys.matrices.normalforms import (
invariant_factors, smith_normal_form,
hermite_normal_form, _hermite_normal_form, _hermite_normal_form_modulo_D)
from sympy.polys.domains import ZZ, QQ
from sympy.polys.matrices import DomainMatrix, DM
from sympy.polys.matrices.exceptions import DMDomainError, DMShapeError
def test_smith_normal():
m = DM([[12, 6, 4, 8], [3, 9, 6, 12], [2, 16, 14, 28], [20, 10, 10, 20]], ZZ)
smf = DM([[1, 0, 0, 0], [0, 10, 0, 0], [0, 0, -30, 0], [0, 0, 0, 0]], ZZ)
assert smith_normal_form(m).to_dense() == smf
x = Symbol('x')
m = DM([[x-1, 1, -1],
[ 0, x, -1],
[ 0, -1, x]], QQ[x])
dx = m.domain.gens[0]
assert invariant_factors(m) == (1, dx-1, dx**2-1)
zr = DomainMatrix([], (0, 2), ZZ)
zc = DomainMatrix([[], []], (2, 0), ZZ)
assert smith_normal_form(zr).to_dense() == zr
assert smith_normal_form(zc).to_dense() == zc
assert smith_normal_form(DM([[2, 4]], ZZ)).to_dense() == DM([[2, 0]], ZZ)
assert smith_normal_form(DM([[0, -2]], ZZ)).to_dense() == DM([[-2, 0]], ZZ)
assert smith_normal_form(DM([[0], [-2]], ZZ)).to_dense() == DM([[-2], [0]], ZZ)
m = DM([[3, 0, 0, 0], [0, 0, 0, 0], [0, 0, 2, 0]], ZZ)
snf = DM([[1, 0, 0, 0], [0, 6, 0, 0], [0, 0, 0, 0]], ZZ)
assert smith_normal_form(m).to_dense() == snf
raises(ValueError, lambda: smith_normal_form(DM([[1]], ZZ[x])))
def test_hermite_normal():
m = DM([[2, 7, 17, 29, 41], [3, 11, 19, 31, 43], [5, 13, 23, 37, 47]], ZZ)
hnf = DM([[1, 0, 0], [0, 2, 1], [0, 0, 1]], ZZ)
assert hermite_normal_form(m) == hnf
assert hermite_normal_form(m, D=ZZ(2)) == hnf
assert hermite_normal_form(m, D=ZZ(2), check_rank=True) == hnf
m = m.transpose()
hnf = DM([[37, 0, 19], [222, -6, 113], [48, 0, 25], [0, 2, 1], [0, 0, 1]], ZZ)
assert hermite_normal_form(m) == hnf
raises(DMShapeError, lambda: _hermite_normal_form_modulo_D(m, ZZ(96)))
raises(DMDomainError, lambda: _hermite_normal_form_modulo_D(m, QQ(96)))
m = DM([[8, 28, 68, 116, 164], [3, 11, 19, 31, 43], [5, 13, 23, 37, 47]], ZZ)
hnf = DM([[4, 0, 0], [0, 2, 1], [0, 0, 1]], ZZ)
assert hermite_normal_form(m) == hnf
assert hermite_normal_form(m, D=ZZ(8)) == hnf
assert hermite_normal_form(m, D=ZZ(8), check_rank=True) == hnf
m = DM([[10, 8, 6, 30, 2], [45, 36, 27, 18, 9], [5, 4, 3, 2, 1]], ZZ)
hnf = DM([[26, 2], [0, 9], [0, 1]], ZZ)
assert hermite_normal_form(m) == hnf
m = DM([[2, 7], [0, 0], [0, 0]], ZZ)
hnf = DM([[1], [0], [0]], ZZ)
assert hermite_normal_form(m) == hnf
m = DM([[-2, 1], [0, 1]], ZZ)
hnf = DM([[2, 1], [0, 1]], ZZ)
assert hermite_normal_form(m) == hnf
m = DomainMatrix([[QQ(1)]], (1, 1), QQ)
raises(DMDomainError, lambda: hermite_normal_form(m))
raises(DMDomainError, lambda: _hermite_normal_form(m))
raises(DMDomainError, lambda: _hermite_normal_form_modulo_D(m, ZZ(1)))

View File

@ -0,0 +1,209 @@
from sympy import ZZ, Matrix
from sympy.polys.matrices import DM, DomainMatrix
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.sdm import SDM
import pytest
zeros = lambda shape, K: DomainMatrix.zeros(shape, K).to_dense()
eye = lambda n, K: DomainMatrix.eye(n, K).to_dense()
#
# DomainMatrix.nullspace can have a divided answer or can return an undivided
# uncanonical answer. The uncanonical answer is not unique but we can make it
# unique by making it primitive (remove gcd). The tests here all show the
# primitive form. We test two things:
#
# A.nullspace().primitive()[1] == answer.
# A.nullspace(divide_last=True) == _divide_last(answer).
#
# The nullspace as returned by DomainMatrix and related classes is the
# transpose of the nullspace as returned by Matrix. Matrix returns a list of
# of column vectors whereas DomainMatrix returns a matrix whose rows are the
# nullspace vectors.
#
NULLSPACE_EXAMPLES = [
(
'zz_1',
DM([[ 1, 2, 3]], ZZ),
DM([[-2, 1, 0],
[-3, 0, 1]], ZZ),
),
(
'zz_2',
zeros((0, 0), ZZ),
zeros((0, 0), ZZ),
),
(
'zz_3',
zeros((2, 0), ZZ),
zeros((0, 0), ZZ),
),
(
'zz_4',
zeros((0, 2), ZZ),
eye(2, ZZ),
),
(
'zz_5',
zeros((2, 2), ZZ),
eye(2, ZZ),
),
(
'zz_6',
DM([[1, 2],
[3, 4]], ZZ),
zeros((0, 2), ZZ),
),
(
'zz_7',
DM([[1, 1],
[1, 1]], ZZ),
DM([[-1, 1]], ZZ),
),
(
'zz_8',
DM([[1],
[1]], ZZ),
zeros((0, 1), ZZ),
),
(
'zz_9',
DM([[1, 1]], ZZ),
DM([[-1, 1]], ZZ),
),
(
'zz_10',
DM([[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1]], ZZ),
DM([[ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[-1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[ 0, -1, 0, 0, 0, 0, 0, 1, 0, 0],
[ 0, 0, 0, -1, 0, 0, 0, 0, 1, 0],
[ 0, 0, 0, 0, -1, 0, 0, 0, 0, 1]], ZZ),
),
]
def _to_DM(A, ans):
"""Convert the answer to DomainMatrix."""
if isinstance(A, DomainMatrix):
return A.to_dense()
elif isinstance(A, DDM):
return DomainMatrix(list(A), A.shape, A.domain).to_dense()
elif isinstance(A, SDM):
return DomainMatrix(dict(A), A.shape, A.domain).to_dense()
else:
assert False # pragma: no cover
def _divide_last(null):
"""Normalize the nullspace by the rightmost non-zero entry."""
null = null.to_field()
if null.is_zero_matrix:
return null
rows = []
for i in range(null.shape[0]):
for j in reversed(range(null.shape[1])):
if null[i, j]:
rows.append(null[i, :] / null[i, j])
break
else:
assert False # pragma: no cover
return DomainMatrix.vstack(*rows)
def _check_primitive(null, null_ans):
"""Check that the primitive of the answer matches."""
null = _to_DM(null, null_ans)
cont, null_prim = null.primitive()
assert null_prim == null_ans
def _check_divided(null, null_ans):
"""Check the divided answer."""
null = _to_DM(null, null_ans)
null_ans_norm = _divide_last(null_ans)
assert null == null_ans_norm
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_Matrix_nullspace(name, A, A_null):
A = A.to_Matrix()
A_null_cols = A.nullspace()
# We have to patch up the case where the nullspace is empty
if A_null_cols:
A_null_found = Matrix.hstack(*A_null_cols)
else:
A_null_found = Matrix.zeros(A.cols, 0)
A_null_found = A_null_found.to_DM().to_field().to_dense()
# The Matrix result is the transpose of DomainMatrix result.
A_null_found = A_null_found.transpose()
_check_divided(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_dm_dense_nullspace(name, A, A_null):
A = A.to_field().to_dense()
A_null_found = A.nullspace(divide_last=True)
_check_divided(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_dm_sparse_nullspace(name, A, A_null):
A = A.to_field().to_sparse()
A_null_found = A.nullspace(divide_last=True)
_check_divided(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_ddm_nullspace(name, A, A_null):
A = A.to_field().to_ddm()
A_null_found, _ = A.nullspace()
_check_divided(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_sdm_nullspace(name, A, A_null):
A = A.to_field().to_sdm()
A_null_found, _ = A.nullspace()
_check_divided(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_dm_dense_nullspace_fracfree(name, A, A_null):
A = A.to_dense()
A_null_found = A.nullspace()
_check_primitive(A_null_found, A_null)
@pytest.mark.parametrize('name, A, A_null', NULLSPACE_EXAMPLES)
def test_dm_sparse_nullspace_fracfree(name, A, A_null):
A = A.to_sparse()
A_null_found = A.nullspace()
_check_primitive(A_null_found, A_null)

View File

@ -0,0 +1,737 @@
from sympy import ZZ, QQ, ZZ_I, EX, Matrix, eye, zeros, symbols
from sympy.polys.matrices import DM, DomainMatrix
from sympy.polys.matrices.dense import ddm_irref_den, ddm_irref
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.sdm import SDM, sdm_irref, sdm_rref_den
import pytest
#
# The dense and sparse implementations of rref_den are ddm_irref_den and
# sdm_irref_den. These can give results that differ by some factor and also
# give different results if the order of the rows is changed. The tests below
# show all results on lowest terms as should be returned by cancel_denom.
#
# The EX domain is also a case where the dense and sparse implementations
# can give results in different forms: the results should be equivalent but
# are not canonical because EX does not have a canonical form.
#
a, b, c, d = symbols('a, b, c, d')
qq_large_1 = DM([
[ (1,2), (1,3), (1,5), (1,7), (1,11), (1,13), (1,17), (1,19), (1,23), (1,29), (1,31)],
[ (1,37), (1,41), (1,43), (1,47), (1,53), (1,59), (1,61), (1,67), (1,71), (1,73), (1,79)],
[ (1,83), (1,89), (1,97),(1,101),(1,103),(1,107),(1,109),(1,113),(1,127),(1,131),(1,137)],
[(1,139),(1,149),(1,151),(1,157),(1,163),(1,167),(1,173),(1,179),(1,181),(1,191),(1,193)],
[(1,197),(1,199),(1,211),(1,223),(1,227),(1,229),(1,233),(1,239),(1,241),(1,251),(1,257)],
[(1,263),(1,269),(1,271),(1,277),(1,281),(1,283),(1,293),(1,307),(1,311),(1,313),(1,317)],
[(1,331),(1,337),(1,347),(1,349),(1,353),(1,359),(1,367),(1,373),(1,379),(1,383),(1,389)],
[(1,397),(1,401),(1,409),(1,419),(1,421),(1,431),(1,433),(1,439),(1,443),(1,449),(1,457)],
[(1,461),(1,463),(1,467),(1,479),(1,487),(1,491),(1,499),(1,503),(1,509),(1,521),(1,523)],
[(1,541),(1,547),(1,557),(1,563),(1,569),(1,571),(1,577),(1,587),(1,593),(1,599),(1,601)],
[(1,607),(1,613),(1,617),(1,619),(1,631),(1,641),(1,643),(1,647),(1,653),(1,659),(1,661)]],
QQ)
qq_large_2 = qq_large_1 + 10**100 * DomainMatrix.eye(11, QQ)
RREF_EXAMPLES = [
(
'zz_1',
DM([[1, 2, 3]], ZZ),
DM([[1, 2, 3]], ZZ),
ZZ(1),
),
(
'zz_2',
DomainMatrix([], (0, 0), ZZ),
DomainMatrix([], (0, 0), ZZ),
ZZ(1),
),
(
'zz_3',
DM([[1, 2],
[3, 4]], ZZ),
DM([[1, 0],
[0, 1]], ZZ),
ZZ(1),
),
(
'zz_4',
DM([[1, 0],
[3, 4]], ZZ),
DM([[1, 0],
[0, 1]], ZZ),
ZZ(1),
),
(
'zz_5',
DM([[0, 2],
[3, 4]], ZZ),
DM([[1, 0],
[0, 1]], ZZ),
ZZ(1),
),
(
'zz_6',
DM([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], ZZ),
DM([[1, 0, -1],
[0, 1, 2],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_7',
DM([[0, 0, 0],
[0, 0, 0],
[1, 0, 0]], ZZ),
DM([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_8',
DM([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]], ZZ),
DM([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_9',
DM([[1, 1, 0],
[0, 0, 2],
[0, 0, 0]], ZZ),
DM([[1, 1, 0],
[0, 0, 1],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_10',
DM([[2, 2, 0],
[0, 0, 2],
[0, 0, 0]], ZZ),
DM([[1, 1, 0],
[0, 0, 1],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_11',
DM([[2, 2, 0],
[0, 2, 2],
[0, 0, 2]], ZZ),
DM([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]], ZZ),
ZZ(1),
),
(
'zz_12',
DM([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]], ZZ),
DM([[1, 0, -1],
[0, 1, 2],
[0, 0, 0],
[0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_13',
DM([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 13]], ZZ),
DM([[ 1, 0, 0],
[ 0, 1, 0],
[ 0, 0, 1],
[ 0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_14',
DM([[1, 2, 4, 3],
[4, 5, 10, 6],
[7, 8, 16, 9]], ZZ),
DM([[1, 0, 0, -1],
[0, 1, 2, 2],
[0, 0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_15',
DM([[1, 2, 4, 3],
[4, 5, 10, 6],
[7, 8, 17, 9]], ZZ),
DM([[1, 0, 0, -1],
[0, 1, 0, 2],
[0, 0, 1, 0]], ZZ),
ZZ(1),
),
(
'zz_16',
DM([[1, 2, 0, 1],
[1, 1, 9, 0]], ZZ),
DM([[1, 0, 18, -1],
[0, 1, -9, 1]], ZZ),
ZZ(1),
),
(
'zz_17',
DM([[1, 1, 1],
[1, 2, 2]], ZZ),
DM([[1, 0, 0],
[0, 1, 1]], ZZ),
ZZ(1),
),
(
# Here the sparse implementation and dense implementation give very
# different denominators: 4061232 and -1765176.
'zz_18',
DM([[94, 24, 0, 27, 0],
[79, 0, 0, 0, 0],
[85, 16, 71, 81, 0],
[ 0, 0, 72, 77, 0],
[21, 0, 34, 0, 0]], ZZ),
DM([[ 1, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0],
[ 0, 0, 1, 0, 0],
[ 0, 0, 0, 1, 0],
[ 0, 0, 0, 0, 0]], ZZ),
ZZ(1),
),
(
# Let's have a denominator that cannot be cancelled.
'zz_19',
DM([[1, 2, 4],
[4, 5, 6]], ZZ),
DM([[3, 0, -8],
[0, 3, 10]], ZZ),
ZZ(3),
),
(
'zz_20',
DM([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 4]], ZZ),
DM([[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_21',
DM([[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1]], ZZ),
DM([[1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_22',
DM([[1, 1, 1, 0, 1],
[1, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
[1, 1, 0, 1, 0],
[1, 0, 0, 0, 0]], ZZ),
DM([[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 0]], ZZ),
ZZ(1),
),
(
'zz_large_1',
DM([
[ 0, 0, 0, 81, 0, 0, 75, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 86, 0, 92, 79, 54, 0, 7, 0, 0, 0, 0, 79, 0, 0, 0],
[89, 54, 81, 0, 0, 20, 0, 0, 0, 0, 0, 0, 51, 0, 94, 0, 0, 77, 0, 0],
[ 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 48, 29, 0, 0, 5, 0, 32, 0],
[ 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 11],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 43, 0, 0],
[ 0, 0, 0, 0, 0, 38, 91, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 26, 0, 0],
[69, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55],
[ 0, 13, 18, 49, 49, 88, 0, 0, 35, 54, 0, 0, 51, 0, 0, 0, 0, 0, 0, 87],
[ 0, 0, 0, 0, 31, 0, 40, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 88, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 15, 53, 0, 92, 0, 0, 0, 0],
[ 0, 0, 0, 95, 0, 0, 0, 36, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 73, 19],
[ 0, 65, 14, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 34, 0, 0],
[ 0, 0, 0, 16, 39, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0],
[ 0, 17, 0, 0, 0, 99, 84, 13, 50, 84, 0, 0, 0, 0, 95, 0, 43, 33, 20, 0],
[79, 0, 17, 52, 99, 12, 69, 0, 98, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 82, 0, 44, 0, 0, 0, 97, 0, 0, 0, 0, 0, 10, 0, 0, 31, 0],
[ 0, 0, 21, 0, 67, 0, 0, 0, 0, 0, 4, 0, 50, 0, 0, 0, 33, 0, 0, 0],
[ 0, 0, 0, 0, 9, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8],
[ 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 34, 93, 0, 0, 0, 0, 47, 0, 0, 0]],
ZZ),
DM([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], ZZ),
ZZ(1),
),
(
'zz_large_2',
DM([
[ 0, 0, 0, 0, 50, 0, 6, 81, 0, 1, 86, 0, 0, 98, 82, 94, 4, 0, 0, 29],
[ 0, 44, 43, 0, 62, 0, 0, 0, 60, 0, 0, 0, 0, 71, 9, 0, 57, 41, 0, 93],
[ 0, 0, 28, 0, 74, 89, 42, 0, 28, 0, 6, 0, 0, 0, 44, 0, 0, 0, 77, 19],
[ 0, 21, 82, 0, 30, 88, 0, 89, 68, 0, 0, 0, 79, 41, 0, 0, 99, 0, 0, 0],
[31, 0, 0, 0, 19, 64, 0, 0, 79, 0, 5, 0, 72, 10, 60, 32, 64, 59, 0, 24],
[ 0, 0, 0, 0, 0, 57, 0, 94, 0, 83, 20, 0, 0, 9, 31, 0, 49, 26, 58, 0],
[ 0, 65, 56, 31, 64, 0, 0, 0, 0, 0, 0, 52, 85, 0, 0, 0, 0, 51, 0, 0],
[ 0, 35, 0, 0, 0, 69, 0, 0, 64, 0, 0, 0, 0, 70, 0, 0, 90, 0, 75, 76],
[69, 7, 0, 90, 0, 0, 84, 0, 47, 69, 19, 20, 42, 0, 0, 32, 71, 35, 0, 0],
[39, 0, 90, 0, 0, 4, 85, 0, 0, 55, 0, 0, 0, 35, 67, 40, 0, 40, 0, 77],
[98, 63, 0, 71, 0, 50, 0, 2, 61, 0, 38, 0, 0, 0, 0, 75, 0, 40, 33, 56],
[ 0, 73, 0, 64, 0, 38, 0, 35, 61, 0, 0, 52, 0, 7, 0, 51, 0, 0, 0, 34],
[ 0, 0, 28, 0, 34, 5, 63, 45, 14, 42, 60, 16, 76, 54, 99, 0, 28, 30, 0, 0],
[58, 37, 14, 0, 0, 0, 94, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 8, 90, 53],
[86, 74, 94, 0, 49, 10, 60, 0, 40, 18, 0, 0, 0, 31, 60, 24, 0, 1, 0, 29],
[53, 0, 0, 97, 0, 0, 58, 0, 0, 39, 44, 47, 0, 0, 0, 12, 50, 0, 0, 11],
[ 4, 0, 92, 10, 28, 0, 0, 89, 0, 0, 18, 54, 23, 39, 0, 2, 0, 48, 0, 92],
[ 0, 0, 90, 77, 95, 33, 0, 0, 49, 22, 39, 0, 0, 0, 0, 0, 0, 40, 0, 0],
[96, 0, 0, 0, 0, 38, 86, 0, 22, 76, 0, 0, 0, 0, 83, 88, 95, 65, 72, 0],
[81, 65, 0, 4, 60, 0, 19, 0, 0, 68, 0, 0, 89, 0, 67, 22, 0, 0, 55, 33]],
ZZ),
DM([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]],
ZZ),
ZZ(1),
),
(
'zz_large_3',
DM([
[62,35,89,58,22,47,30,28,52,72,17,56,80,26,64,21,10,35,24,42,96,32,23,50,92,37,76,94,63,66],
[20,47,96,34,10,98,19,6,29,2,19,92,61,94,38,41,32,9,5,94,31,58,27,41,72,85,61,62,40,46],
[69,26,35,68,25,52,94,13,38,65,81,10,29,15,5,4,13,99,85,0,80,51,60,60,26,77,85,2,87,25],
[99,58,69,15,52,12,18,7,27,56,12,54,21,92,38,95,33,83,28,1,44,8,29,84,92,12,2,25,46,46],
[93,13,55,48,35,87,24,40,23,35,25,32,0,19,0,85,4,79,26,11,46,75,7,96,76,11,7,57,99,75],
[128,85,26,51,161,173,77,78,85,103,123,58,91,147,38,91,161,36,123,81,102,25,75,59,17,150,112,65,77,143],
[15,59,61,82,12,83,34,8,94,71,66,7,91,21,48,69,26,12,64,38,97,87,38,15,51,33,93,43,66,89],
[74,74,53,39,69,90,41,80,32,66,40,83,87,87,61,38,12,80,24,49,37,90,19,33,56,0,46,57,56,60],
[82,11,0,25,56,58,39,49,92,93,80,38,19,62,33,85,19,61,14,30,45,91,97,34,97,53,92,28,33,43],
[83,79,41,16,95,35,53,45,26,4,71,76,61,69,69,72,87,92,59,72,54,11,22,83,8,57,77,55,19,22],
[49,34,13,31,72,77,52,70,46,41,37,6,42,66,35,6,75,33,62,57,30,14,26,31,9,95,89,13,12,90],
[29,3,49,30,51,32,77,41,38,50,16,1,87,81,93,88,58,91,83,0,38,67,29,64,60,84,5,60,23,28],
[79,51,13,20,89,96,25,8,39,62,86,52,49,81,3,85,86,3,61,24,72,11,49,28,8,55,23,52,65,53],
[96,86,73,20,41,20,37,18,10,61,85,24,40,83,69,41,4,92,23,99,64,33,18,36,32,56,60,98,39,24],
[32,62,47,80,51,66,17,1,9,30,65,75,75,88,99,92,64,53,53,86,38,51,41,14,35,18,39,25,26,32],
[39,21,8,16,33,6,35,85,75,62,43,34,18,68,71,28,32,18,12,0,81,53,1,99,3,5,45,99,35,33],
[19,95,89,45,75,94,92,5,84,93,34,17,50,56,79,98,68,82,65,81,51,90,5,95,33,71,46,61,14,7],
[53,92,8,49,67,84,21,79,49,95,66,48,36,14,62,97,26,45,58,31,83,48,11,89,67,72,91,34,56,89],
[56,76,99,92,40,8,0,16,15,48,35,72,91,46,81,14,86,60,51,7,33,12,53,78,48,21,3,89,15,79],
[81,43,33,49,6,49,36,32,57,74,87,91,17,37,31,17,67,1,40,38,69,8,3,48,59,37,64,97,11,3],
[98,48,77,16,2,48,57,38,63,59,79,35,16,71,60,86,71,41,14,76,80,97,77,69,4,58,22,55,26,73],
[80,47,78,44,31,48,47,29,29,62,19,21,17,24,19,3,53,93,97,57,13,54,12,10,77,66,60,75,32,21],
[86,63,2,13,71,38,86,23,18,15,91,65,77,65,9,92,50,0,17,42,99,80,99,27,10,99,92,9,87,84],
[66,27,72,13,13,15,72,75,39,3,14,71,15,68,10,19,49,54,11,29,47,20,63,13,97,47,24,62,16,96],
[42,63,83,60,49,68,9,53,75,87,40,25,12,63,0,12,0,95,46,46,55,25,89,1,51,1,1,96,80,52],
[35,9,97,13,86,39,66,48,41,57,23,38,11,9,35,72,88,13,41,60,10,64,71,23,1,5,23,57,6,19],
[70,61,5,50,72,60,77,13,41,94,1,45,52,22,99,47,27,18,99,42,16,48,26,9,88,77,10,94,11,92],
[55,68,58,2,72,56,81,52,79,37,1,40,21,46,27,60,37,13,97,42,85,98,69,60,76,44,42,46,29,73],
[73,0,43,17,89,97,45,2,68,14,55,60,95,2,74,85,88,68,93,76,38,76,2,51,45,76,50,79,56,18],
[72,58,41,39,24,80,23,79,44,7,98,75,30,6,85,60,20,58,77,71,90,51,38,80,30,15,33,10,82,8]],
ZZ),
Matrix([
[eye(29) * 2028539767964472550625641331179545072876560857886207583101,
Matrix([ 4260575808093245475167216057435155595594339172099000182569,
169148395880755256182802335904188369274227936894862744452,
4915975976683942569102447281579134986891620721539038348914,
6113916866367364958834844982578214901958429746875633283248,
5585689617819894460378537031623265659753379011388162534838,
359776822829880747716695359574308645968094838905181892423,
-2800926112141776386671436511182421432449325232461665113305,
941642292388230001722444876624818265766384442910688463158,
3648811843256146649321864698600908938933015862008642023935,
-4104526163246702252932955226754097174212129127510547462419,
-704814955438106792441896903238080197619233342348191408078,
1640882266829725529929398131287244562048075707575030019335,
-4068330845192910563212155694231438198040299927120544468520,
136589038308366497790495711534532612862715724187671166593,
2544937011460702462290799932536905731142196510605191645593,
755591839174293940486133926192300657264122907519174116472,
-3683838489869297144348089243628436188645897133242795965021,
-522207137101161299969706310062775465103537953077871128403,
-2260451796032703984456606059649402832441331339246756656334,
-6476809325293587953616004856993300606040336446656916663680,
3521944238996782387785653800944972787867472610035040989081,
2270762115788407950241944504104975551914297395787473242379,
-3259947194628712441902262570532921252128444706733549251156,
-5624569821491886970999097239695637132075823246850431083557,
-3262698255682055804320585332902837076064075936601504555698,
5786719943788937667411185880136324396357603606944869545501,
-955257841973865996077323863289453200904051299086000660036,
-1294235552446355326174641248209752679127075717918392702116,
-3718353510747301598130831152458342785269166356215331448279,
]),],
[zeros(1, 29), zeros(1, 1)],
]).to_DM().to_dense(),
ZZ(2028539767964472550625641331179545072876560857886207583101),
),
(
'qq_1',
DM([[(1,2), 0], [0, 2]], QQ),
DM([[1, 0], [0, 1]], QQ),
QQ(1),
),
(
# Standard square case
'qq_2',
DM([[0, 1],
[1, 1]], QQ),
DM([[1, 0],
[0, 1]], QQ),
QQ(1),
),
(
# m < n case
'qq_3',
DM([[1, 2, 1],
[3, 4, 1]], QQ),
DM([[1, 0, -1],
[0, 1, 1]], QQ),
QQ(1),
),
(
# same m < n but reversed
'qq_4',
DM([[3, 4, 1],
[1, 2, 1]], QQ),
DM([[1, 0, -1],
[0, 1, 1]], QQ),
QQ(1),
),
(
# m > n case
'qq_5',
DM([[1, 0],
[1, 3],
[0, 1]], QQ),
DM([[1, 0],
[0, 1],
[0, 0]], QQ),
QQ(1),
),
(
# Example with missing pivot
'qq_6',
DM([[1, 0, 1],
[3, 0, 1]], QQ),
DM([[1, 0, 0],
[0, 0, 1]], QQ),
QQ(1),
),
(
# This is intended to trigger the threshold where we give up on
# clearing denominators.
'qq_large_1',
qq_large_1,
DomainMatrix.eye(11, QQ).to_dense(),
QQ(1),
),
(
# This is intended to trigger the threshold where we use rref_den over
# QQ.
'qq_large_2',
qq_large_2,
DomainMatrix.eye(11, QQ).to_dense(),
QQ(1),
),
(
# Example with missing pivot and no replacement
# This example is just enough to show a different result from the dense
# and sparse versions of the algorithm:
#
# >>> A = Matrix([[0, 1], [0, 2], [1, 0]])
# >>> A.to_DM().to_sparse().rref_den()[0].to_Matrix()
# Matrix([
# [1, 0],
# [0, 1],
# [0, 0]])
# >>> A.to_DM().to_dense().rref_den()[0].to_Matrix()
# Matrix([
# [2, 0],
# [0, 2],
# [0, 0]])
#
'qq_7',
DM([[0, 1],
[0, 2],
[1, 0]], QQ),
DM([[1, 0],
[0, 1],
[0, 0]], QQ),
QQ(1),
),
(
# Gaussian integers
'zz_i_1',
DM([[(0,1), 1, 1],
[ 1, 1, 1]], ZZ_I),
DM([[1, 0, 0],
[0, 1, 1]], ZZ_I),
ZZ_I(1),
),
(
# EX: test_issue_23718
'EX_1',
DM([
[a, b, 1],
[c, d, 1]], EX),
DM([[a*d - b*c, 0, -b + d],
[ 0, a*d - b*c, a - c]], EX),
EX(a*d - b*c),
),
]
def _to_DM(A, ans):
"""Convert the answer to DomainMatrix."""
if isinstance(A, DomainMatrix):
return A.to_dense()
elif isinstance(A, Matrix):
return A.to_DM(ans.domain).to_dense()
if not (hasattr(A, 'shape') and hasattr(A, 'domain')):
shape, domain = ans.shape, ans.domain
else:
shape, domain = A.shape, A.domain
if isinstance(A, (DDM, list)):
return DomainMatrix(list(A), shape, domain).to_dense()
elif isinstance(A, (SDM, dict)):
return DomainMatrix(dict(A), shape, domain).to_dense()
else:
assert False # pragma: no cover
def _pivots(A_rref):
"""Return the pivots from the rref of A."""
return tuple(sorted(map(min, A_rref.to_sdm().values())))
def _check_cancel(result, rref_ans, den_ans):
"""Check the cancelled result."""
rref, den, pivots = result
if isinstance(rref, (DDM, SDM, list, dict)):
assert type(pivots) is list
pivots = tuple(pivots)
rref = _to_DM(rref, rref_ans)
rref2, den2 = rref.cancel_denom(den)
assert rref2 == rref_ans
assert den2 == den_ans
assert pivots == _pivots(rref)
def _check_divide(result, rref_ans, den_ans):
"""Check the divided result."""
rref, pivots = result
if isinstance(rref, (DDM, SDM, list, dict)):
assert type(pivots) is list
pivots = tuple(pivots)
rref_ans = rref_ans.to_field() / den_ans
rref = _to_DM(rref, rref_ans)
assert rref == rref_ans
assert _pivots(rref) == pivots
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_Matrix_rref(name, A, A_rref, den):
K = A.domain
A = A.to_Matrix()
A_rref_found, pivots = A.rref()
if K.is_EX:
A_rref_found = A_rref_found.expand()
_check_divide((A_rref_found, pivots), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_dense_rref(name, A, A_rref, den):
A = A.to_field()
_check_divide(A.rref(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_dense_rref_den(name, A, A_rref, den):
_check_cancel(A.rref_den(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref(name, A, A_rref, den):
A = A.to_field().to_sparse()
_check_divide(A.rref(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den(name, A, A_rref, den):
A = A.to_sparse()
_check_cancel(A.rref_den(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain(name, A, A_rref, den):
A = A.to_sparse()
A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False)
A_rref_f = A_rref_f.to_field() / den_f
_check_divide((A_rref_f, pivots_f), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain_CD(name, A, A_rref, den):
A = A.to_sparse()
A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False, method='CD')
A_rref_f = A_rref_f.to_field() / den_f
_check_divide((A_rref_f, pivots_f), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain_GJ(name, A, A_rref, den):
A = A.to_sparse()
A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False, method='GJ')
A_rref_f = A_rref_f.to_field() / den_f
_check_divide((A_rref_f, pivots_f), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_rref_den(name, A, A_rref, den):
A = A.to_ddm()
_check_cancel(A.rref_den(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sdm_rref_den(name, A, A_rref, den):
A = A.to_sdm()
_check_cancel(A.rref_den(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_rref(name, A, A_rref, den):
A = A.to_field().to_ddm()
_check_divide(A.rref(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sdm_rref(name, A, A_rref, den):
A = A.to_field().to_sdm()
_check_divide(A.rref(), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_irref(name, A, A_rref, den):
A = A.to_field().to_ddm().copy()
pivots_found = ddm_irref(A)
_check_divide((A, pivots_found), A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_irref_den(name, A, A_rref, den):
A = A.to_ddm().copy()
(den_found, pivots_found) = ddm_irref_den(A, A.domain)
result = (A, den_found, pivots_found)
_check_cancel(result, A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sparse_sdm_rref(name, A, A_rref, den):
A = A.to_field().to_sdm()
_check_divide(sdm_irref(A)[:2], A_rref, den)
@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sparse_sdm_rref_den(name, A, A_rref, den):
A = A.to_sdm().copy()
K = A.domain
_check_cancel(sdm_rref_den(A, K), A_rref, den)

View File

@ -0,0 +1,428 @@
"""
Tests for the basic functionality of the SDM class.
"""
from itertools import product
from sympy.core.singleton import S
from sympy.external.gmpy import GROUND_TYPES
from sympy.testing.pytest import raises
from sympy.polys.domains import QQ, ZZ, EXRAW
from sympy.polys.matrices.sdm import SDM
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.exceptions import (DMBadInputError, DMDomainError,
DMShapeError)
def test_SDM():
A = SDM({0:{0:ZZ(1)}}, (2, 2), ZZ)
assert A.domain == ZZ
assert A.shape == (2, 2)
assert dict(A) == {0:{0:ZZ(1)}}
raises(DMBadInputError, lambda: SDM({5:{1:ZZ(0)}}, (2, 2), ZZ))
raises(DMBadInputError, lambda: SDM({0:{5:ZZ(0)}}, (2, 2), ZZ))
def test_DDM_str():
sdm = SDM({0:{0:ZZ(1)}, 1:{1:ZZ(1)}}, (2, 2), ZZ)
assert str(sdm) == '{0: {0: 1}, 1: {1: 1}}'
if GROUND_TYPES == 'gmpy': # pragma: no cover
assert repr(sdm) == 'SDM({0: {0: mpz(1)}, 1: {1: mpz(1)}}, (2, 2), ZZ)'
else: # pragma: no cover
assert repr(sdm) == 'SDM({0: {0: 1}, 1: {1: 1}}, (2, 2), ZZ)'
def test_SDM_new():
A = SDM({0:{0:ZZ(1)}}, (2, 2), ZZ)
B = A.new({}, (2, 2), ZZ)
assert B == SDM({}, (2, 2), ZZ)
def test_SDM_copy():
A = SDM({0:{0:ZZ(1)}}, (2, 2), ZZ)
B = A.copy()
assert A == B
A[0][0] = ZZ(2)
assert A != B
def test_SDM_from_list():
A = SDM.from_list([[ZZ(0), ZZ(1)], [ZZ(1), ZZ(0)]], (2, 2), ZZ)
assert A == SDM({0:{1:ZZ(1)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
raises(DMBadInputError, lambda: SDM.from_list([[ZZ(0)], [ZZ(0), ZZ(1)]], (2, 2), ZZ))
raises(DMBadInputError, lambda: SDM.from_list([[ZZ(0), ZZ(1)]], (2, 2), ZZ))
def test_SDM_to_list():
A = SDM({0:{1: ZZ(1)}}, (2, 2), ZZ)
assert A.to_list() == [[ZZ(0), ZZ(1)], [ZZ(0), ZZ(0)]]
A = SDM({}, (0, 2), ZZ)
assert A.to_list() == []
A = SDM({}, (2, 0), ZZ)
assert A.to_list() == [[], []]
def test_SDM_to_list_flat():
A = SDM({0:{1: ZZ(1)}}, (2, 2), ZZ)
assert A.to_list_flat() == [ZZ(0), ZZ(1), ZZ(0), ZZ(0)]
def test_SDM_to_dok():
A = SDM({0:{1: ZZ(1)}}, (2, 2), ZZ)
assert A.to_dok() == {(0, 1): ZZ(1)}
def test_SDM_from_ddm():
A = DDM([[ZZ(1), ZZ(0)], [ZZ(1), ZZ(0)]], (2, 2), ZZ)
B = SDM.from_ddm(A)
assert B.domain == ZZ
assert B.shape == (2, 2)
assert dict(B) == {0:{0:ZZ(1)}, 1:{0:ZZ(1)}}
def test_SDM_to_ddm():
A = SDM({0:{1: ZZ(1)}}, (2, 2), ZZ)
B = DDM([[ZZ(0), ZZ(1)], [ZZ(0), ZZ(0)]], (2, 2), ZZ)
assert A.to_ddm() == B
def test_SDM_to_sdm():
A = SDM({0:{1: ZZ(1)}}, (2, 2), ZZ)
assert A.to_sdm() == A
def test_SDM_getitem():
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
assert A.getitem(0, 0) == ZZ.zero
assert A.getitem(0, 1) == ZZ.one
assert A.getitem(1, 0) == ZZ.zero
assert A.getitem(-2, -2) == ZZ.zero
assert A.getitem(-2, -1) == ZZ.one
assert A.getitem(-1, -2) == ZZ.zero
raises(IndexError, lambda: A.getitem(2, 0))
raises(IndexError, lambda: A.getitem(0, 2))
def test_SDM_setitem():
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
A.setitem(0, 0, ZZ(1))
assert A == SDM({0:{0:ZZ(1), 1:ZZ(1)}}, (2, 2), ZZ)
A.setitem(1, 0, ZZ(1))
assert A == SDM({0:{0:ZZ(1), 1:ZZ(1)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
A.setitem(1, 0, ZZ(0))
assert A == SDM({0:{0:ZZ(1), 1:ZZ(1)}}, (2, 2), ZZ)
# Repeat the above test so that this time the row is empty
A.setitem(1, 0, ZZ(0))
assert A == SDM({0:{0:ZZ(1), 1:ZZ(1)}}, (2, 2), ZZ)
A.setitem(0, 0, ZZ(0))
assert A == SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
# This time the row is there but column is empty
A.setitem(0, 0, ZZ(0))
assert A == SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
raises(IndexError, lambda: A.setitem(2, 0, ZZ(1)))
raises(IndexError, lambda: A.setitem(0, 2, ZZ(1)))
def test_SDM_extract_slice():
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
B = A.extract_slice(slice(1, 2), slice(1, 2))
assert B == SDM({0:{0:ZZ(4)}}, (1, 1), ZZ)
def test_SDM_extract():
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
B = A.extract([1], [1])
assert B == SDM({0:{0:ZZ(4)}}, (1, 1), ZZ)
B = A.extract([1, 0], [1, 0])
assert B == SDM({0:{0:ZZ(4), 1:ZZ(3)}, 1:{0:ZZ(2), 1:ZZ(1)}}, (2, 2), ZZ)
B = A.extract([1, 1], [1, 1])
assert B == SDM({0:{0:ZZ(4), 1:ZZ(4)}, 1:{0:ZZ(4), 1:ZZ(4)}}, (2, 2), ZZ)
B = A.extract([-1], [-1])
assert B == SDM({0:{0:ZZ(4)}}, (1, 1), ZZ)
A = SDM({}, (2, 2), ZZ)
B = A.extract([0, 1, 0], [0, 0])
assert B == SDM({}, (3, 2), ZZ)
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
assert A.extract([], []) == SDM.zeros((0, 0), ZZ)
assert A.extract([1], []) == SDM.zeros((1, 0), ZZ)
assert A.extract([], [1]) == SDM.zeros((0, 1), ZZ)
raises(IndexError, lambda: A.extract([2], [0]))
raises(IndexError, lambda: A.extract([0], [2]))
raises(IndexError, lambda: A.extract([-3], [0]))
raises(IndexError, lambda: A.extract([0], [-3]))
def test_SDM_zeros():
A = SDM.zeros((2, 2), ZZ)
assert A.domain == ZZ
assert A.shape == (2, 2)
assert dict(A) == {}
def test_SDM_ones():
A = SDM.ones((1, 2), QQ)
assert A.domain == QQ
assert A.shape == (1, 2)
assert dict(A) == {0:{0:QQ(1), 1:QQ(1)}}
def test_SDM_eye():
A = SDM.eye((2, 2), ZZ)
assert A.domain == ZZ
assert A.shape == (2, 2)
assert dict(A) == {0:{0:ZZ(1)}, 1:{1:ZZ(1)}}
def test_SDM_diag():
A = SDM.diag([ZZ(1), ZZ(2)], ZZ, (2, 3))
assert A == SDM({0:{0:ZZ(1)}, 1:{1:ZZ(2)}}, (2, 3), ZZ)
def test_SDM_transpose():
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(1), 1:ZZ(3)}, 1:{0:ZZ(2), 1:ZZ(4)}}, (2, 2), ZZ)
assert A.transpose() == B
A = SDM({0:{1:ZZ(2)}}, (2, 2), ZZ)
B = SDM({1:{0:ZZ(2)}}, (2, 2), ZZ)
assert A.transpose() == B
A = SDM({0:{1:ZZ(2)}}, (1, 2), ZZ)
B = SDM({1:{0:ZZ(2)}}, (2, 1), ZZ)
assert A.transpose() == B
def test_SDM_mul():
A = SDM({0:{0:ZZ(2)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(4)}}, (2, 2), ZZ)
assert A*ZZ(2) == B
assert ZZ(2)*A == B
raises(TypeError, lambda: A*QQ(1, 2))
raises(TypeError, lambda: QQ(1, 2)*A)
def test_SDM_mul_elementwise():
A = SDM({0:{0:ZZ(2), 1:ZZ(2)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(4)}, 1:{0:ZZ(3)}}, (2, 2), ZZ)
C = SDM({0:{0:ZZ(8)}}, (2, 2), ZZ)
assert A.mul_elementwise(B) == C
assert B.mul_elementwise(A) == C
Aq = A.convert_to(QQ)
A1 = SDM({0:{0:ZZ(1)}}, (1, 1), ZZ)
raises(DMDomainError, lambda: Aq.mul_elementwise(B))
raises(DMShapeError, lambda: A1.mul_elementwise(B))
def test_SDM_matmul():
A = SDM({0:{0:ZZ(2)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(4)}}, (2, 2), ZZ)
assert A.matmul(A) == A*A == B
C = SDM({0:{0:ZZ(2)}}, (2, 2), QQ)
raises(DMDomainError, lambda: A.matmul(C))
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(7), 1:ZZ(10)}, 1:{0:ZZ(15), 1:ZZ(22)}}, (2, 2), ZZ)
assert A.matmul(A) == A*A == B
A22 = SDM({0:{0:ZZ(4)}}, (2, 2), ZZ)
A32 = SDM({0:{0:ZZ(2)}}, (3, 2), ZZ)
A23 = SDM({0:{0:ZZ(4)}}, (2, 3), ZZ)
A33 = SDM({0:{0:ZZ(8)}}, (3, 3), ZZ)
A22 = SDM({0:{0:ZZ(8)}}, (2, 2), ZZ)
assert A32.matmul(A23) == A33
assert A23.matmul(A32) == A22
# XXX: @ not supported by SDM...
#assert A32.matmul(A23) == A32 @ A23 == A33
#assert A23.matmul(A32) == A23 @ A32 == A22
#raises(DMShapeError, lambda: A23 @ A22)
raises(DMShapeError, lambda: A23.matmul(A22))
A = SDM({0: {0: ZZ(-1), 1: ZZ(1)}}, (1, 2), ZZ)
B = SDM({0: {0: ZZ(-1)}, 1: {0: ZZ(-1)}}, (2, 1), ZZ)
assert A.matmul(B) == A*B == SDM({}, (1, 1), ZZ)
def test_matmul_exraw():
def dm(d):
result = {}
for i, row in d.items():
row = {j:val for j, val in row.items() if val}
if row:
result[i] = row
return SDM(result, (2, 2), EXRAW)
values = [S.NegativeInfinity, S.NegativeOne, S.Zero, S.One, S.Infinity]
for a, b, c, d in product(*[values]*4):
Ad = dm({0: {0:a, 1:b}, 1: {0:c, 1:d}})
Ad2 = dm({0: {0:a*a + b*c, 1:a*b + b*d}, 1:{0:c*a + d*c, 1: c*b + d*d}})
assert Ad * Ad == Ad2
def test_SDM_add():
A = SDM({0:{1:ZZ(1)}, 1:{0:ZZ(2), 1:ZZ(3)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(1)}, 1:{0:ZZ(-2), 1:ZZ(3)}}, (2, 2), ZZ)
C = SDM({0:{0:ZZ(1), 1:ZZ(1)}, 1:{1:ZZ(6)}}, (2, 2), ZZ)
assert A.add(B) == B.add(A) == A + B == B + A == C
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(1)}, 1:{0:ZZ(-2), 1:ZZ(3)}}, (2, 2), ZZ)
C = SDM({0:{0:ZZ(1), 1:ZZ(1)}, 1:{0:ZZ(-2), 1:ZZ(3)}}, (2, 2), ZZ)
assert A.add(B) == B.add(A) == A + B == B + A == C
raises(TypeError, lambda: A + [])
def test_SDM_sub():
A = SDM({0:{1:ZZ(1)}, 1:{0:ZZ(2), 1:ZZ(3)}}, (2, 2), ZZ)
B = SDM({0:{0:ZZ(1)}, 1:{0:ZZ(-2), 1:ZZ(3)}}, (2, 2), ZZ)
C = SDM({0:{0:ZZ(-1), 1:ZZ(1)}, 1:{0:ZZ(4)}}, (2, 2), ZZ)
assert A.sub(B) == A - B == C
raises(TypeError, lambda: A - [])
def test_SDM_neg():
A = SDM({0:{1:ZZ(1)}, 1:{0:ZZ(2), 1:ZZ(3)}}, (2, 2), ZZ)
B = SDM({0:{1:ZZ(-1)}, 1:{0:ZZ(-2), 1:ZZ(-3)}}, (2, 2), ZZ)
assert A.neg() == -A == B
def test_SDM_convert_to():
A = SDM({0:{1:ZZ(1)}, 1:{0:ZZ(2), 1:ZZ(3)}}, (2, 2), ZZ)
B = SDM({0:{1:QQ(1)}, 1:{0:QQ(2), 1:QQ(3)}}, (2, 2), QQ)
C = A.convert_to(QQ)
assert C == B
assert C.domain == QQ
D = A.convert_to(ZZ)
assert D == A
assert D.domain == ZZ
def test_SDM_hstack():
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
B = SDM({1:{1:ZZ(1)}}, (2, 2), ZZ)
AA = SDM({0:{1:ZZ(1), 3:ZZ(1)}}, (2, 4), ZZ)
AB = SDM({0:{1:ZZ(1)}, 1:{3:ZZ(1)}}, (2, 4), ZZ)
assert SDM.hstack(A) == A
assert SDM.hstack(A, A) == AA
assert SDM.hstack(A, B) == AB
def test_SDM_vstack():
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
B = SDM({1:{1:ZZ(1)}}, (2, 2), ZZ)
AA = SDM({0:{1:ZZ(1)}, 2:{1:ZZ(1)}}, (4, 2), ZZ)
AB = SDM({0:{1:ZZ(1)}, 3:{1:ZZ(1)}}, (4, 2), ZZ)
assert SDM.vstack(A) == A
assert SDM.vstack(A, A) == AA
assert SDM.vstack(A, B) == AB
def test_SDM_applyfunc():
A = SDM({0:{1:ZZ(1)}}, (2, 2), ZZ)
B = SDM({0:{1:ZZ(2)}}, (2, 2), ZZ)
assert A.applyfunc(lambda x: 2*x, ZZ) == B
def test_SDM_inv():
A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
B = SDM({0:{0:QQ(-2), 1:QQ(1)}, 1:{0:QQ(3, 2), 1:QQ(-1, 2)}}, (2, 2), QQ)
assert A.inv() == B
def test_SDM_det():
A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
assert A.det() == QQ(-2)
def test_SDM_lu():
A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
L = SDM({0:{0:QQ(1)}, 1:{0:QQ(3), 1:QQ(1)}}, (2, 2), QQ)
#U = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(-2)}}, (2, 2), QQ)
#swaps = []
# This doesn't quite work. U has some nonzero elements in the lower part.
#assert A.lu() == (L, U, swaps)
assert A.lu()[0] == L
def test_SDM_lu_solve():
A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
b = SDM({0:{0:QQ(1)}, 1:{0:QQ(2)}}, (2, 1), QQ)
x = SDM({1:{0:QQ(1, 2)}}, (2, 1), QQ)
assert A.matmul(x) == b
assert A.lu_solve(b) == x
def test_SDM_charpoly():
A = SDM({0:{0:ZZ(1), 1:ZZ(2)}, 1:{0:ZZ(3), 1:ZZ(4)}}, (2, 2), ZZ)
assert A.charpoly() == [ZZ(1), ZZ(-5), ZZ(-2)]
def test_SDM_nullspace():
# More tests are in test_nullspace.py
A = SDM({0:{0:QQ(1), 1:QQ(1)}}, (2, 2), QQ)
assert A.nullspace()[0] == SDM({0:{0:QQ(-1), 1:QQ(1)}}, (1, 2), QQ)
def test_SDM_rref():
# More tests are in test_rref.py
A = SDM({0:{0:QQ(1), 1:QQ(2)},
1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
A_rref = SDM({0:{0:QQ(1)}, 1:{1:QQ(1)}}, (2, 2), QQ)
assert A.rref() == (A_rref, [0, 1])
A = SDM({0: {0: QQ(1), 1: QQ(2), 2: QQ(2)},
1: {0: QQ(3), 2: QQ(4)}}, (2, 3), ZZ)
A_rref = SDM({0: {0: QQ(1,1), 2: QQ(4,3)},
1: {1: QQ(1,1), 2: QQ(1,3)}}, (2, 3), QQ)
assert A.rref() == (A_rref, [0, 1])
def test_SDM_particular():
A = SDM({0:{0:QQ(1)}}, (2, 2), QQ)
Apart = SDM.zeros((1, 2), QQ)
assert A.particular() == Apart
def test_SDM_is_zero_matrix():
A = SDM({0: {0: QQ(1)}}, (2, 2), QQ)
Azero = SDM.zeros((1, 2), QQ)
assert A.is_zero_matrix() is False
assert Azero.is_zero_matrix() is True
def test_SDM_is_upper():
A = SDM({0: {0: QQ(1), 1: QQ(2), 2: QQ(3), 3: QQ(4)},
1: {1: QQ(5), 2: QQ(6), 3: QQ(7)},
2: {2: QQ(8), 3: QQ(9)}}, (3, 4), QQ)
B = SDM({0: {0: QQ(1), 1: QQ(2), 2: QQ(3), 3: QQ(4)},
1: {1: QQ(5), 2: QQ(6), 3: QQ(7)},
2: {1: QQ(7), 2: QQ(8), 3: QQ(9)}}, (3, 4), QQ)
assert A.is_upper() is True
assert B.is_upper() is False
def test_SDM_is_lower():
A = SDM({0: {0: QQ(1), 1: QQ(2), 2: QQ(3), 3: QQ(4)},
1: {1: QQ(5), 2: QQ(6), 3: QQ(7)},
2: {2: QQ(8), 3: QQ(9)}}, (3, 4), QQ
).transpose()
B = SDM({0: {0: QQ(1), 1: QQ(2), 2: QQ(3), 3: QQ(4)},
1: {1: QQ(5), 2: QQ(6), 3: QQ(7)},
2: {1: QQ(7), 2: QQ(8), 3: QQ(9)}}, (3, 4), QQ
).transpose()
assert A.is_lower() is True
assert B.is_lower() is False

View File

@ -0,0 +1,864 @@
#
# Test basic features of DDM, SDM and DFM.
#
# These three types are supposed to be interchangeable, so we should use the
# same tests for all of them for the most part.
#
# The tests here cover the basic part of the inerface that the three types
# should expose and that DomainMatrix should mostly rely on.
#
# More in-depth tests of the heavier algorithms like rref etc should go in
# their own test files.
#
# Any new methods added to the DDM, SDM or DFM classes should be tested here
# and added to all classes.
#
from sympy.external.gmpy import GROUND_TYPES
from sympy import ZZ, QQ, GF, ZZ_I, symbols
from sympy.polys.matrices.exceptions import (
DMBadInputError,
DMDomainError,
DMNonSquareMatrixError,
DMNonInvertibleMatrixError,
DMShapeError,
)
from sympy.polys.matrices.domainmatrix import DM, DomainMatrix, DDM, SDM, DFM
from sympy.testing.pytest import raises, skip
import pytest
def test_XXM_constructors():
"""Test the DDM, etc constructors."""
lol = [
[ZZ(1), ZZ(2)],
[ZZ(3), ZZ(4)],
[ZZ(5), ZZ(6)],
]
dod = {
0: {0: ZZ(1), 1: ZZ(2)},
1: {0: ZZ(3), 1: ZZ(4)},
2: {0: ZZ(5), 1: ZZ(6)},
}
lol_0x0 = []
lol_0x2 = []
lol_2x0 = [[], []]
dod_0x0 = {}
dod_0x2 = {}
dod_2x0 = {}
lol_bad = [
[ZZ(1), ZZ(2)],
[ZZ(3), ZZ(4)],
[ZZ(5), ZZ(6), ZZ(7)],
]
dod_bad = {
0: {0: ZZ(1), 1: ZZ(2)},
1: {0: ZZ(3), 1: ZZ(4)},
2: {0: ZZ(5), 1: ZZ(6), 2: ZZ(7)},
}
XDM_dense = [DDM]
XDM_sparse = [SDM]
if GROUND_TYPES == 'flint':
XDM_dense.append(DFM)
for XDM in XDM_dense:
A = XDM(lol, (3, 2), ZZ)
assert A.rows == 3
assert A.cols == 2
assert A.domain == ZZ
assert A.shape == (3, 2)
if XDM is not DFM:
assert ZZ.of_type(A[0][0]) is True
else:
assert ZZ.of_type(A.rep[0, 0]) is True
Adm = DomainMatrix(lol, (3, 2), ZZ)
if XDM is DFM:
assert Adm.rep == A
assert Adm.rep.to_ddm() != A
elif GROUND_TYPES == 'flint':
assert Adm.rep.to_ddm() == A
assert Adm.rep != A
else:
assert Adm.rep == A
assert Adm.rep.to_ddm() == A
assert XDM(lol_0x0, (0, 0), ZZ).shape == (0, 0)
assert XDM(lol_0x2, (0, 2), ZZ).shape == (0, 2)
assert XDM(lol_2x0, (2, 0), ZZ).shape == (2, 0)
raises(DMBadInputError, lambda: XDM(lol, (2, 3), ZZ))
raises(DMBadInputError, lambda: XDM(lol_bad, (3, 2), ZZ))
raises(DMBadInputError, lambda: XDM(dod, (3, 2), ZZ))
for XDM in XDM_sparse:
A = XDM(dod, (3, 2), ZZ)
assert A.rows == 3
assert A.cols == 2
assert A.domain == ZZ
assert A.shape == (3, 2)
assert ZZ.of_type(A[0][0]) is True
assert DomainMatrix(dod, (3, 2), ZZ).rep == A
assert XDM(dod_0x0, (0, 0), ZZ).shape == (0, 0)
assert XDM(dod_0x2, (0, 2), ZZ).shape == (0, 2)
assert XDM(dod_2x0, (2, 0), ZZ).shape == (2, 0)
raises(DMBadInputError, lambda: XDM(dod, (2, 3), ZZ))
raises(DMBadInputError, lambda: XDM(lol, (3, 2), ZZ))
raises(DMBadInputError, lambda: XDM(dod_bad, (3, 2), ZZ))
raises(DMBadInputError, lambda: DomainMatrix(lol, (2, 3), ZZ))
raises(DMBadInputError, lambda: DomainMatrix(lol_bad, (3, 2), ZZ))
raises(DMBadInputError, lambda: DomainMatrix(dod_bad, (3, 2), ZZ))
def test_XXM_eq():
"""Test equality for DDM, SDM, DFM and DomainMatrix."""
lol1 = [[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]]
dod1 = {0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(4)}}
lol2 = [[ZZ(1), ZZ(2)], [ZZ(3), ZZ(5)]]
dod2 = {0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(5)}}
A1_ddm = DDM(lol1, (2, 2), ZZ)
A1_sdm = SDM(dod1, (2, 2), ZZ)
A1_dm_d = DomainMatrix(lol1, (2, 2), ZZ)
A1_dm_s = DomainMatrix(dod1, (2, 2), ZZ)
A2_ddm = DDM(lol2, (2, 2), ZZ)
A2_sdm = SDM(dod2, (2, 2), ZZ)
A2_dm_d = DomainMatrix(lol2, (2, 2), ZZ)
A2_dm_s = DomainMatrix(dod2, (2, 2), ZZ)
A1_all = [A1_ddm, A1_sdm, A1_dm_d, A1_dm_s]
A2_all = [A2_ddm, A2_sdm, A2_dm_d, A2_dm_s]
if GROUND_TYPES == 'flint':
A1_dfm = DFM([[1, 2], [3, 4]], (2, 2), ZZ)
A2_dfm = DFM([[1, 2], [3, 5]], (2, 2), ZZ)
A1_all.append(A1_dfm)
A2_all.append(A2_dfm)
for n, An in enumerate(A1_all):
for m, Am in enumerate(A1_all):
if n == m:
assert (An == Am) is True
assert (An != Am) is False
else:
assert (An == Am) is False
assert (An != Am) is True
for n, An in enumerate(A2_all):
for m, Am in enumerate(A2_all):
if n == m:
assert (An == Am) is True
assert (An != Am) is False
else:
assert (An == Am) is False
assert (An != Am) is True
for n, A1 in enumerate(A1_all):
for m, A2 in enumerate(A2_all):
assert (A1 == A2) is False
assert (A1 != A2) is True
def test_to_XXM():
"""Test to_ddm etc. for DDM, SDM, DFM and DomainMatrix."""
lol = [[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]]
dod = {0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(4)}}
A_ddm = DDM(lol, (2, 2), ZZ)
A_sdm = SDM(dod, (2, 2), ZZ)
A_dm_d = DomainMatrix(lol, (2, 2), ZZ)
A_dm_s = DomainMatrix(dod, (2, 2), ZZ)
A_all = [A_ddm, A_sdm, A_dm_d, A_dm_s]
if GROUND_TYPES == 'flint':
A_dfm = DFM(lol, (2, 2), ZZ)
A_all.append(A_dfm)
for A in A_all:
assert A.to_ddm() == A_ddm
assert A.to_sdm() == A_sdm
if GROUND_TYPES != 'flint':
raises(NotImplementedError, lambda: A.to_dfm())
assert A.to_dfm_or_ddm() == A_ddm
# Add e.g. DDM.to_DM()?
# assert A.to_DM() == A_dm
if GROUND_TYPES == 'flint':
for A in A_all:
assert A.to_dfm() == A_dfm
for K in [ZZ, QQ, GF(5), ZZ_I]:
if isinstance(A, DFM) and not DFM._supports_domain(K):
raises(NotImplementedError, lambda: A.convert_to(K))
else:
A_K = A.convert_to(K)
if DFM._supports_domain(K):
A_dfm_K = A_dfm.convert_to(K)
assert A_K.to_dfm() == A_dfm_K
assert A_K.to_dfm_or_ddm() == A_dfm_K
else:
raises(NotImplementedError, lambda: A_K.to_dfm())
assert A_K.to_dfm_or_ddm() == A_ddm.convert_to(K)
def test_DFM_domains():
"""Test which domains are supported by DFM."""
x, y = symbols('x, y')
if GROUND_TYPES in ('python', 'gmpy'):
supported = []
flint_funcs = {}
not_supported = [ZZ, QQ, GF(5), QQ[x], QQ[x,y]]
elif GROUND_TYPES == 'flint':
import flint
supported = [ZZ, QQ]
flint_funcs = {
ZZ: flint.fmpz_mat,
QQ: flint.fmpq_mat,
}
not_supported = [
# This could be supported but not yet implemented in SymPy:
GF(5),
# Other domains could be supported but not implemented as matrices
# in python-flint:
QQ[x],
QQ[x,y],
QQ.frac_field(x,y),
# Others would potentially never be supported by python-flint:
ZZ_I,
]
else:
assert False, "Unknown GROUND_TYPES: %s" % GROUND_TYPES
for domain in supported:
assert DFM._supports_domain(domain) is True
assert DFM._get_flint_func(domain) == flint_funcs[domain]
for domain in not_supported:
assert DFM._supports_domain(domain) is False
raises(NotImplementedError, lambda: DFM._get_flint_func(domain))
def _DM(lol, typ, K):
"""Make a DM of type typ over K from lol."""
A = DM(lol, K)
if typ == 'DDM':
return A.to_ddm()
elif typ == 'SDM':
return A.to_sdm()
elif typ == 'DFM':
if GROUND_TYPES != 'flint':
skip("DFM not supported in this ground type")
return A.to_dfm()
else:
assert False, "Unknown type %s" % typ
def _DMZ(lol, typ):
"""Make a DM of type typ over ZZ from lol."""
return _DM(lol, typ, ZZ)
def _DMQ(lol, typ):
"""Make a DM of type typ over QQ from lol."""
return _DM(lol, typ, QQ)
def DM_ddm(lol, K):
"""Make a DDM over K from lol."""
return _DM(lol, 'DDM', K)
def DM_sdm(lol, K):
"""Make a SDM over K from lol."""
return _DM(lol, 'SDM', K)
def DM_dfm(lol, K):
"""Make a DFM over K from lol."""
return _DM(lol, 'DFM', K)
def DMZ_ddm(lol):
"""Make a DDM from lol."""
return _DMZ(lol, 'DDM')
def DMZ_sdm(lol):
"""Make a SDM from lol."""
return _DMZ(lol, 'SDM')
def DMZ_dfm(lol):
"""Make a DFM from lol."""
return _DMZ(lol, 'DFM')
def DMQ_ddm(lol):
"""Make a DDM from lol."""
return _DMQ(lol, 'DDM')
def DMQ_sdm(lol):
"""Make a SDM from lol."""
return _DMQ(lol, 'SDM')
def DMQ_dfm(lol):
"""Make a DFM from lol."""
return _DMQ(lol, 'DFM')
DM_all = [DM_ddm, DM_sdm, DM_dfm]
DMZ_all = [DMZ_ddm, DMZ_sdm, DMZ_dfm]
DMQ_all = [DMQ_ddm, DMQ_sdm, DMQ_dfm]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XDM_getitem(DM):
"""Test getitem for DDM, etc."""
lol = [[0, 1], [2, 0]]
A = DM(lol)
m, n = A.shape
indices = [-3, -2, -1, 0, 1, 2]
for i in indices:
for j in indices:
if -2 <= i < m and -2 <= j < n:
assert A.getitem(i, j) == ZZ(lol[i][j])
else:
raises(IndexError, lambda: A.getitem(i, j))
@pytest.mark.parametrize('DM', DMZ_all)
def test_XDM_setitem(DM):
"""Test setitem for DDM, etc."""
A = DM([[0, 1, 2], [3, 4, 5]])
A.setitem(0, 0, ZZ(6))
assert A == DM([[6, 1, 2], [3, 4, 5]])
A.setitem(0, 1, ZZ(7))
assert A == DM([[6, 7, 2], [3, 4, 5]])
A.setitem(0, 2, ZZ(8))
assert A == DM([[6, 7, 8], [3, 4, 5]])
A.setitem(0, -1, ZZ(9))
assert A == DM([[6, 7, 9], [3, 4, 5]])
A.setitem(0, -2, ZZ(10))
assert A == DM([[6, 10, 9], [3, 4, 5]])
A.setitem(0, -3, ZZ(11))
assert A == DM([[11, 10, 9], [3, 4, 5]])
raises(IndexError, lambda: A.setitem(0, 3, ZZ(12)))
raises(IndexError, lambda: A.setitem(0, -4, ZZ(13)))
A.setitem(1, 0, ZZ(14))
assert A == DM([[11, 10, 9], [14, 4, 5]])
A.setitem(1, 1, ZZ(15))
assert A == DM([[11, 10, 9], [14, 15, 5]])
A.setitem(-1, 1, ZZ(16))
assert A == DM([[11, 10, 9], [14, 16, 5]])
A.setitem(-2, 1, ZZ(17))
assert A == DM([[11, 17, 9], [14, 16, 5]])
raises(IndexError, lambda: A.setitem(2, 0, ZZ(18)))
raises(IndexError, lambda: A.setitem(-3, 0, ZZ(19)))
A.setitem(1, 2, ZZ(0))
assert A == DM([[11, 17, 9], [14, 16, 0]])
A.setitem(1, -2, ZZ(0))
assert A == DM([[11, 17, 9], [14, 0, 0]])
A.setitem(1, -3, ZZ(0))
assert A == DM([[11, 17, 9], [0, 0, 0]])
A.setitem(0, 0, ZZ(0))
assert A == DM([[0, 17, 9], [0, 0, 0]])
A.setitem(0, -1, ZZ(0))
assert A == DM([[0, 17, 0], [0, 0, 0]])
A.setitem(0, 0, ZZ(0))
assert A == DM([[0, 17, 0], [0, 0, 0]])
A.setitem(0, -2, ZZ(0))
assert A == DM([[0, 0, 0], [0, 0, 0]])
A.setitem(0, -3, ZZ(1))
assert A == DM([[1, 0, 0], [0, 0, 0]])
class _Sliced:
def __getitem__(self, item):
return item
_slice = _Sliced()
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_extract_slice(DM):
A = DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
assert A.extract_slice(*_slice[:,:]) == A
assert A.extract_slice(*_slice[1:,:]) == DM([[4, 5, 6], [7, 8, 9]])
assert A.extract_slice(*_slice[1:,1:]) == DM([[5, 6], [8, 9]])
assert A.extract_slice(*_slice[1:,:-1]) == DM([[4, 5], [7, 8]])
assert A.extract_slice(*_slice[1:,:-1:2]) == DM([[4], [7]])
assert A.extract_slice(*_slice[:,::2]) == DM([[1, 3], [4, 6], [7, 9]])
assert A.extract_slice(*_slice[::2,:]) == DM([[1, 2, 3], [7, 8, 9]])
assert A.extract_slice(*_slice[::2,::2]) == DM([[1, 3], [7, 9]])
assert A.extract_slice(*_slice[::2,::-2]) == DM([[3, 1], [9, 7]])
assert A.extract_slice(*_slice[::-2,::2]) == DM([[7, 9], [1, 3]])
assert A.extract_slice(*_slice[::-2,::-2]) == DM([[9, 7], [3, 1]])
assert A.extract_slice(*_slice[:,::-1]) == DM([[3, 2, 1], [6, 5, 4], [9, 8, 7]])
assert A.extract_slice(*_slice[::-1,:]) == DM([[7, 8, 9], [4, 5, 6], [1, 2, 3]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_extract(DM):
A = DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
assert A.extract([0, 1, 2], [0, 1, 2]) == A
assert A.extract([1, 2], [1, 2]) == DM([[5, 6], [8, 9]])
assert A.extract([1, 2], [0, 1]) == DM([[4, 5], [7, 8]])
assert A.extract([1, 2], [0, 2]) == DM([[4, 6], [7, 9]])
assert A.extract([1, 2], [0]) == DM([[4], [7]])
assert A.extract([1, 2], []) == DM([[1]]).zeros((2, 0), ZZ)
assert A.extract([], [0, 1, 2]) == DM([[1]]).zeros((0, 3), ZZ)
raises(IndexError, lambda: A.extract([1, 2], [0, 3]))
raises(IndexError, lambda: A.extract([1, 2], [0, -4]))
raises(IndexError, lambda: A.extract([3, 1], [0, 1]))
raises(IndexError, lambda: A.extract([-4, 2], [3, 1]))
B = DM([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
assert B.extract([1, 2], [1, 2]) == DM([[0, 0], [0, 0]])
def test_XXM_str():
A = DomainMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], (3, 3), ZZ)
assert str(A) == \
'DomainMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], (3, 3), ZZ)'
assert str(A.to_ddm()) == \
'[[1, 2, 3], [4, 5, 6], [7, 8, 9]]'
assert str(A.to_sdm()) == \
'{0: {0: 1, 1: 2, 2: 3}, 1: {0: 4, 1: 5, 2: 6}, 2: {0: 7, 1: 8, 2: 9}}'
assert repr(A) == \
'DomainMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], (3, 3), ZZ)'
assert repr(A.to_ddm()) == \
'DDM([[1, 2, 3], [4, 5, 6], [7, 8, 9]], (3, 3), ZZ)'
assert repr(A.to_sdm()) == \
'SDM({0: {0: 1, 1: 2, 2: 3}, 1: {0: 4, 1: 5, 2: 6}, 2: {0: 7, 1: 8, 2: 9}}, (3, 3), ZZ)'
B = DomainMatrix({0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3)}}, (2, 2), ZZ)
assert str(B) == \
'DomainMatrix({0: {0: 1, 1: 2}, 1: {0: 3}}, (2, 2), ZZ)'
assert str(B.to_ddm()) == \
'[[1, 2], [3, 0]]'
assert str(B.to_sdm()) == \
'{0: {0: 1, 1: 2}, 1: {0: 3}}'
assert repr(B) == \
'DomainMatrix({0: {0: 1, 1: 2}, 1: {0: 3}}, (2, 2), ZZ)'
if GROUND_TYPES != 'gmpy':
assert repr(B.to_ddm()) == \
'DDM([[1, 2], [3, 0]], (2, 2), ZZ)'
assert repr(B.to_sdm()) == \
'SDM({0: {0: 1, 1: 2}, 1: {0: 3}}, (2, 2), ZZ)'
else:
assert repr(B.to_ddm()) == \
'DDM([[mpz(1), mpz(2)], [mpz(3), mpz(0)]], (2, 2), ZZ)'
assert repr(B.to_sdm()) == \
'SDM({0: {0: mpz(1), 1: mpz(2)}, 1: {0: mpz(3)}}, (2, 2), ZZ)'
if GROUND_TYPES == 'flint':
assert str(A.to_dfm()) == \
'[[1, 2, 3], [4, 5, 6], [7, 8, 9]]'
assert str(B.to_dfm()) == \
'[[1, 2], [3, 0]]'
assert repr(A.to_dfm()) == \
'DFM([[1, 2, 3], [4, 5, 6], [7, 8, 9]], (3, 3), ZZ)'
assert repr(B.to_dfm()) == \
'DFM([[1, 2], [3, 0]], (2, 2), ZZ)'
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_list(DM):
T = type(DM([[0]]))
lol = [[1, 2, 4], [4, 5, 6]]
lol_ZZ = [[ZZ(1), ZZ(2), ZZ(4)], [ZZ(4), ZZ(5), ZZ(6)]]
lol_ZZ_bad = [[ZZ(1), ZZ(2), ZZ(4)], [ZZ(4), ZZ(5), ZZ(6), ZZ(7)]]
assert T.from_list(lol_ZZ, (2, 3), ZZ) == DM(lol)
raises(DMBadInputError, lambda: T.from_list(lol_ZZ_bad, (3, 2), ZZ))
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_to_list(DM):
lol = [[1, 2, 4], [4, 5, 6]]
assert DM(lol).to_list() == [[ZZ(1), ZZ(2), ZZ(4)], [ZZ(4), ZZ(5), ZZ(6)]]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_to_list_flat(DM):
lol = [[1, 2, 4], [4, 5, 6]]
assert DM(lol).to_list_flat() == [ZZ(1), ZZ(2), ZZ(4), ZZ(4), ZZ(5), ZZ(6)]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_list_flat(DM):
T = type(DM([[0]]))
flat = [ZZ(1), ZZ(2), ZZ(4), ZZ(4), ZZ(5), ZZ(6)]
assert T.from_list_flat(flat, (2, 3), ZZ) == DM([[1, 2, 4], [4, 5, 6]])
raises(DMBadInputError, lambda: T.from_list_flat(flat, (3, 3), ZZ))
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_to_flat_nz(DM):
M = DM([[1, 2, 0], [0, 0, 0], [0, 0, 3]])
elements = [ZZ(1), ZZ(2), ZZ(3)]
indices = ((0, 0), (0, 1), (2, 2))
assert M.to_flat_nz() == (elements, (indices, M.shape))
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_flat_nz(DM):
T = type(DM([[0]]))
elements = [ZZ(1), ZZ(2), ZZ(3)]
indices = ((0, 0), (0, 1), (2, 2))
data = (indices, (3, 3))
result = DM([[1, 2, 0], [0, 0, 0], [0, 0, 3]])
assert T.from_flat_nz(elements, data, ZZ) == result
raises(DMBadInputError, lambda: T.from_flat_nz(elements, (indices, (2, 3)), ZZ))
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_to_dod(DM):
dod = {0: {0: ZZ(1), 2: ZZ(4)}, 1: {0: ZZ(4), 1: ZZ(5), 2: ZZ(6)}}
assert DM([[1, 0, 4], [4, 5, 6]]).to_dod() == dod
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_dod(DM):
T = type(DM([[0]]))
dod = {0: {0: ZZ(1), 2: ZZ(4)}, 1: {0: ZZ(4), 1: ZZ(5), 2: ZZ(6)}}
assert T.from_dod(dod, (2, 3), ZZ) == DM([[1, 0, 4], [4, 5, 6]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_to_dok(DM):
dod = {(0, 0): ZZ(1), (0, 2): ZZ(4),
(1, 0): ZZ(4), (1, 1): ZZ(5), (1, 2): ZZ(6)}
assert DM([[1, 0, 4], [4, 5, 6]]).to_dok() == dod
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_dok(DM):
T = type(DM([[0]]))
dod = {(0, 0): ZZ(1), (0, 2): ZZ(4),
(1, 0): ZZ(4), (1, 1): ZZ(5), (1, 2): ZZ(6)}
assert T.from_dok(dod, (2, 3), ZZ) == DM([[1, 0, 4], [4, 5, 6]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_iter_values(DM):
values = [ZZ(1), ZZ(4), ZZ(4), ZZ(5), ZZ(6)]
assert sorted(DM([[1, 0, 4], [4, 5, 6]]).iter_values()) == values
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_iter_items(DM):
items = [((0, 0), ZZ(1)), ((0, 2), ZZ(4)),
((1, 0), ZZ(4)), ((1, 1), ZZ(5)), ((1, 2), ZZ(6))]
assert sorted(DM([[1, 0, 4], [4, 5, 6]]).iter_items()) == items
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_from_ddm(DM):
T = type(DM([[0]]))
ddm = DDM([[1, 2, 4], [4, 5, 6]], (2, 3), ZZ)
assert T.from_ddm(ddm) == DM([[1, 2, 4], [4, 5, 6]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_zeros(DM):
T = type(DM([[0]]))
assert T.zeros((2, 3), ZZ) == DM([[0, 0, 0], [0, 0, 0]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_ones(DM):
T = type(DM([[0]]))
assert T.ones((2, 3), ZZ) == DM([[1, 1, 1], [1, 1, 1]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_eye(DM):
T = type(DM([[0]]))
assert T.eye(3, ZZ) == DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
assert T.eye((3, 2), ZZ) == DM([[1, 0], [0, 1], [0, 0]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_diag(DM):
T = type(DM([[0]]))
assert T.diag([1, 2, 3], ZZ) == DM([[1, 0, 0], [0, 2, 0], [0, 0, 3]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_transpose(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
assert A.transpose() == DM([[1, 4], [2, 5], [3, 6]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_add(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[1, 2, 3], [4, 5, 6]])
C = DM([[2, 4, 6], [8, 10, 12]])
assert A.add(B) == C
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_sub(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[1, 2, 3], [4, 5, 6]])
C = DM([[0, 0, 0], [0, 0, 0]])
assert A.sub(B) == C
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_mul(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
b = ZZ(2)
assert A.mul(b) == DM([[2, 4, 6], [8, 10, 12]])
assert A.rmul(b) == DM([[2, 4, 6], [8, 10, 12]])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_matmul(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[1, 2], [3, 4], [5, 6]])
C = DM([[22, 28], [49, 64]])
assert A.matmul(B) == C
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_mul_elementwise(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[1, 2, 3], [4, 5, 6]])
C = DM([[1, 4, 9], [16, 25, 36]])
assert A.mul_elementwise(B) == C
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_neg(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
C = DM([[-1, -2, -3], [-4, -5, -6]])
assert A.neg() == C
@pytest.mark.parametrize('DM', DM_all)
def test_XXM_convert_to(DM):
A = DM([[1, 2, 3], [4, 5, 6]], ZZ)
B = DM([[1, 2, 3], [4, 5, 6]], QQ)
assert A.convert_to(QQ) == B
assert B.convert_to(ZZ) == A
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_scc(DM):
A = DM([
[0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0],
[0, 0, 0, 1, 0, 1]])
assert A.scc() == [[0, 1], [2], [3, 5], [4]]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_hstack(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[7, 8], [9, 10]])
C = DM([[1, 2, 3, 7, 8], [4, 5, 6, 9, 10]])
ABC = DM([[1, 2, 3, 7, 8, 1, 2, 3, 7, 8],
[4, 5, 6, 9, 10, 4, 5, 6, 9, 10]])
assert A.hstack(B) == C
assert A.hstack(B, C) == ABC
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_vstack(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[7, 8, 9]])
C = DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
ABC = DM([[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 8, 9]])
assert A.vstack(B) == C
assert A.vstack(B, C) == ABC
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_applyfunc(DM):
A = DM([[1, 2, 3], [4, 5, 6]])
B = DM([[2, 4, 6], [8, 10, 12]])
assert A.applyfunc(lambda x: 2*x, ZZ) == B
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_is_upper(DM):
assert DM([[1, 2, 3], [0, 5, 6]]).is_upper() is True
assert DM([[1, 2, 3], [4, 5, 6]]).is_upper() is False
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_is_lower(DM):
assert DM([[1, 0, 0], [4, 5, 0]]).is_lower() is True
assert DM([[1, 2, 3], [4, 5, 6]]).is_lower() is False
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_is_diagonal(DM):
assert DM([[1, 0, 0], [0, 5, 0]]).is_diagonal() is True
assert DM([[1, 2, 3], [4, 5, 6]]).is_diagonal() is False
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_diagonal(DM):
assert DM([[1, 0, 0], [0, 5, 0]]).diagonal() == [1, 5]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_is_zero_matrix(DM):
assert DM([[0, 0, 0], [0, 0, 0]]).is_zero_matrix() is True
assert DM([[1, 0, 0], [0, 0, 0]]).is_zero_matrix() is False
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_det_ZZ(DM):
assert DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).det() == 0
assert DM([[1, 2, 3], [4, 5, 6], [7, 8, 10]]).det() == -3
@pytest.mark.parametrize('DM', DMQ_all)
def test_XXM_det_QQ(DM):
dM1 = DM([[(1,2), (2,3)], [(3,4), (4,5)]])
assert dM1.det() == QQ(-1,10)
@pytest.mark.parametrize('DM', DMQ_all)
def test_XXM_inv_QQ(DM):
dM1 = DM([[(1,2), (2,3)], [(3,4), (4,5)]])
dM2 = DM([[(-8,1), (20,3)], [(15,2), (-5,1)]])
assert dM1.inv() == dM2
assert dM1.matmul(dM2) == DM([[1, 0], [0, 1]])
dM3 = DM([[(1,2), (2,3)], [(1,4), (1,3)]])
raises(DMNonInvertibleMatrixError, lambda: dM3.inv())
dM4 = DM([[(1,2), (2,3), (3,4)], [(1,4), (1,3), (1,2)]])
raises(DMNonSquareMatrixError, lambda: dM4.inv())
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_inv_ZZ(DM):
dM1 = DM([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
# XXX: Maybe this should return a DM over QQ instead?
# XXX: Handle unimodular matrices?
raises(DMDomainError, lambda: dM1.inv())
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_charpoly_ZZ(DM):
dM1 = DM([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
assert dM1.charpoly() == [1, -16, -12, 3]
@pytest.mark.parametrize('DM', DMQ_all)
def test_XXM_charpoly_QQ(DM):
dM1 = DM([[(1,2), (2,3)], [(3,4), (4,5)]])
assert dM1.charpoly() == [QQ(1,1), QQ(-13,10), QQ(-1,10)]
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_lu_solve_ZZ(DM):
dM1 = DM([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
dM2 = DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
raises(DMDomainError, lambda: dM1.lu_solve(dM2))
@pytest.mark.parametrize('DM', DMQ_all)
def test_XXM_lu_solve_QQ(DM):
dM1 = DM([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
dM2 = DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
dM3 = DM([[(-2,3),(-4,3),(1,1)],[(-2,3),(11,3),(-2,1)],[(1,1),(-2,1),(1,1)]])
assert dM1.lu_solve(dM2) == dM3 == dM1.inv()
dM4 = DM([[1, 2, 3], [4, 5, 6]])
dM5 = DM([[1, 0], [0, 1], [0, 0]])
raises(DMShapeError, lambda: dM4.lu_solve(dM5))
@pytest.mark.parametrize('DM', DMQ_all)
def test_XXM_nullspace_QQ(DM):
dM1 = DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# XXX: Change the signature to just return the nullspace. Possibly
# returning the rank or nullity makes sense but the list of nonpivots is
# not useful.
assert dM1.nullspace() == (DM([[1, -2, 1]]), [2])
@pytest.mark.parametrize('DM', DMZ_all)
def test_XXM_lll(DM):
M = DM([[1, 2, 3], [4, 5, 20]])
M_lll = DM([[1, 2, 3], [-1, -5, 5]])
T = DM([[1, 0], [-5, 1]])
assert M.lll() == M_lll
assert M.lll_transform() == (M_lll, T)
assert T.matmul(M) == M_lll