Skip to content

Commit 2b11cf0

Browse files
committed
Use nmod_ctx consistently in nmod_poly and nmod_mat
1 parent 2637c9b commit 2b11cf0

7 files changed

Lines changed: 284 additions & 163 deletions

File tree

src/flint/test/test_all.py

Lines changed: 119 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,17 +3533,34 @@ def factor_sqf(p):
35333533

35343534
def _all_matrices():
35353535
"""Return a list of matrix types and scalar types."""
3536+
# Prime modulus
35363537
R163 = flint.fmpz_mod_ctx(163)
35373538
R127 = flint.fmpz_mod_ctx(2**127 - 1)
35383539
R255 = flint.fmpz_mod_ctx(2**255 - 19)
3540+
3541+
# Composite modulus
3542+
R164_C = flint.fmpz_mod_ctx(164)
3543+
R127_C = flint.fmpz_mod_ctx(2**127)
3544+
R255_C = flint.fmpz_mod_ctx(2**255)
3545+
35393546
return [
3540-
# (matrix_type, scalar_type, is_field)
3541-
(flint.fmpz_mat, flint.fmpz, False),
3542-
(flint.fmpq_mat, flint.fmpq, True),
3543-
(lambda *a: flint.nmod_mat(*a, 17), lambda x: flint.nmod(x, 17), True),
3544-
(lambda *a: flint.fmpz_mod_mat(*a, R163), lambda x: flint.fmpz_mod(x, R163), True),
3545-
(lambda *a: flint.fmpz_mod_mat(*a, R127), lambda x: flint.fmpz_mod(x, R127), True),
3546-
(lambda *a: flint.fmpz_mod_mat(*a, R255), lambda x: flint.fmpz_mod(x, R255), True),
3547+
# (matrix_type, scalar_type, is_field, characteristic)
3548+
3549+
# Z and Q
3550+
(flint.fmpz_mat, flint.fmpz, False, 0),
3551+
(flint.fmpq_mat, flint.fmpq, True, 0),
3552+
3553+
# Z/pZ
3554+
(lambda *a: flint.nmod_mat(*a, 17), lambda x: flint.nmod(x, 17), True, 17),
3555+
(lambda *a: flint.fmpz_mod_mat(*a, R163), lambda x: flint.fmpz_mod(x, R163), True, 163),
3556+
(lambda *a: flint.fmpz_mod_mat(*a, R127), lambda x: flint.fmpz_mod(x, R127), True, 2**127 - 1),
3557+
(lambda *a: flint.fmpz_mod_mat(*a, R255), lambda x: flint.fmpz_mod(x, R255), True, 2**255 - 19),
3558+
3559+
# Z/nZ (n composite)
3560+
(lambda *a: flint.nmod_mat(*a, 16), lambda x: flint.nmod(x, 16), False, 16),
3561+
(lambda *a: flint.fmpz_mod_mat(*a, R164_C), lambda x: flint.fmpz_mod(x, R164_C), False, 164),
3562+
(lambda *a: flint.fmpz_mod_mat(*a, R127_C), lambda x: flint.fmpz_mod(x, R127_C), False, 2**127),
3563+
(lambda *a: flint.fmpz_mod_mat(*a, R255_C), lambda x: flint.fmpz_mod(x, R255_C), False, 2**255),
35473564
]
35483565

35493566

@@ -3664,7 +3681,7 @@ def _poly_type_from_matrix_type(mat_type):
36643681

36653682

36663683
def test_matrices_eq():
3667-
for M, S, is_field in _all_matrices():
3684+
for M, S, is_field, characteristic in _all_matrices():
36683685
A1 = M([[1, 2], [3, 4]])
36693686
A2 = M([[1, 2], [3, 4]])
36703687
B = M([[5, 6], [7, 8]])
@@ -3689,7 +3706,7 @@ def test_matrices_eq():
36893706

36903707

36913708
def test_matrices_constructor():
3692-
for M, S, is_field in _all_matrices():
3709+
for M, S, is_field, characteristic in _all_matrices():
36933710
assert raises(lambda: M(), TypeError)
36943711

36953712
# Empty matrices
@@ -3761,7 +3778,7 @@ def _matrix_repr(M):
37613778

37623779

37633780
def test_matrices_strrepr():
3764-
for M, S, is_field in _all_matrices():
3781+
for M, S, is_field, characteristic in _all_matrices():
37653782
A = M([[1, 2], [3, 4]])
37663783
A_str = "[1, 2]\n[3, 4]"
37673784
A_repr = _matrix_repr(A)
@@ -3784,7 +3801,7 @@ def test_matrices_strrepr():
37843801

37853802

37863803
def test_matrices_getitem():
3787-
for M, S, is_field in _all_matrices():
3804+
for M, S, is_field, characteristic in _all_matrices():
37883805
M1234 = M([[1, 2], [3, 4]])
37893806
assert M1234[0, 0] == S(1)
37903807
assert M1234[0, 1] == S(2)
@@ -3800,7 +3817,7 @@ def test_matrices_getitem():
38003817

38013818

38023819
def test_matrices_setitem():
3803-
for M, S, is_field in _all_matrices():
3820+
for M, S, is_field, characteristic in _all_matrices():
38043821
M1234 = M([[1, 2], [3, 4]])
38053822

38063823
assert M1234[0, 0] == S(1)
@@ -3826,7 +3843,7 @@ def setbad(obj, key, val):
38263843

38273844

38283845
def test_matrices_bool():
3829-
for M, S, is_field in _all_matrices():
3846+
for M, S, is_field, characteristic in _all_matrices():
38303847
assert bool(M([])) is False
38313848
assert bool(M([[0]])) is False
38323849
assert bool(M([[1]])) is True
@@ -3837,14 +3854,14 @@ def test_matrices_bool():
38373854

38383855

38393856
def test_matrices_pos_neg():
3840-
for M, S, is_field in _all_matrices():
3857+
for M, S, is_field, characteristic in _all_matrices():
38413858
M1234 = M([[1, 2], [3, 4]])
38423859
assert +M1234 == M1234
38433860
assert -M1234 == M([[-1, -2], [-3, -4]])
38443861

38453862

38463863
def test_matrices_add():
3847-
for M, S, is_field in _all_matrices():
3864+
for M, S, is_field, characteristic in _all_matrices():
38483865
M1234 = M([[1, 2], [3, 4]])
38493866
M5678 = M([[5, 6], [7, 8]])
38503867
assert M1234 + M5678 == M([[6, 8], [10, 12]])
@@ -3864,7 +3881,7 @@ def test_matrices_add():
38643881

38653882

38663883
def test_matrices_sub():
3867-
for M, S, is_field in _all_matrices():
3884+
for M, S, is_field, characteristic in _all_matrices():
38683885
M1234 = M([[1, 2], [3, 4]])
38693886
M5678 = M([[5, 6], [7, 8]])
38703887
assert M1234 - M5678 == M([[-4, -4], [-4, -4]])
@@ -3884,7 +3901,7 @@ def test_matrices_sub():
38843901

38853902

38863903
def test_matrices_mul():
3887-
for M, S, is_field in _all_matrices():
3904+
for M, S, is_field, characteristic in _all_matrices():
38883905
M1234 = M([[1, 2], [3, 4]])
38893906
M5678 = M([[5, 6], [7, 8]])
38903907
assert M1234 * M5678 == M([[19, 22], [43, 50]])
@@ -3910,18 +3927,24 @@ def test_matrices_mul():
39103927

39113928

39123929
def test_matrices_pow():
3913-
for M, S, is_field in _all_matrices():
3930+
for M, S, is_field, characteristic in _all_matrices():
39143931
M1234 = M([[1, 2], [3, 4]])
3932+
39153933
assert M1234**0 == M([[1, 0], [0, 1]])
39163934
assert M1234**1 == M1234
39173935
assert M1234**2 == M([[7, 10], [15, 22]])
39183936
assert M1234**3 == M([[37, 54], [81, 118]])
3937+
39193938
if is_field:
39203939
assert M1234**-1 == M([[-4, 2], [3, -1]]) / 2
39213940
assert M1234**-2 == M([[22, -10], [-15, 7]]) / 4
39223941
assert M1234**-3 == M([[-118, 54], [81, -37]]) / 8
39233942
Ms = M([[1, 2], [3, 6]])
39243943
assert raises(lambda: Ms**-1, ZeroDivisionError)
3944+
else:
3945+
# XXX: Allow unimodular matrices?
3946+
assert raises(lambda: M1234**-1, DomainError)
3947+
39253948
Mr = M([[1, 2, 3], [4, 5, 6]])
39263949
assert raises(lambda: Mr**0, ValueError)
39273950
assert raises(lambda: Mr**1, ValueError)
@@ -3931,31 +3954,49 @@ def test_matrices_pow():
39313954

39323955

39333956
def test_matrices_div():
3934-
for M, S, is_field in _all_matrices():
3957+
3958+
for M, S, is_field, characteristic in _all_matrices():
39353959
M1234 = M([[1, 2], [3, 4]])
3960+
39363961
if is_field:
39373962
assert M1234 / 2 == M([[S(1)/2, S(1)], [S(3)/2, 2]])
39383963
assert M1234 / S(2) == M([[S(1)/2, S(1)], [S(3)/2, 2]])
39393964
assert raises(lambda: M1234 / 0, ZeroDivisionError)
39403965
assert raises(lambda: M1234 / S(0), ZeroDivisionError)
3966+
else:
3967+
assert raises(lambda: M1234 / 2, DomainError)
3968+
if characteristic == 0:
3969+
assert (2*M1234) / 2 == M1234
3970+
else:
3971+
assert raises(lambda: (2*M1234) / 2, DomainError)
3972+
39413973
raises(lambda: M1234 / None, TypeError)
39423974
raises(lambda: None / M1234, TypeError)
39433975

39443976

39453977
def test_matrices_inv():
3946-
for M, S, is_field in _all_matrices():
3947-
if is_field:
3948-
M1234 = M([[1, 2], [3, 4]])
3978+
3979+
for M, S, is_field, characteristic in _all_matrices():
3980+
3981+
M1234 = M([[1, 2], [3, 4]])
3982+
M1236 = M([[1, 2], [3, 6]])
3983+
Mr = M([[1, 2, 3], [4, 5, 6]])
3984+
3985+
if characteristic > 0 and not is_field:
3986+
assert raises(lambda: M([[1, 2], [3, 4]]).inv(), DomainError)
3987+
elif is_field:
39493988
assert M1234.inv() == M([[-2, 1], [S(3)/2, -S(1)/2]])
3950-
M1236 = M([[1, 2], [3, 6]])
39513989
assert raises(lambda: M1236.inv(), ZeroDivisionError)
3952-
Mr = M([[1, 2, 3], [4, 5, 6]])
39533990
assert raises(lambda: Mr.inv(), ValueError)
3954-
# XXX: Test non-field matrices. unimodular?
3991+
else:
3992+
# assert M1234.inv() == (M([[-4, 2], [3, -1]]), 2)
3993+
# assert M1236.inv() == (M([[-6, 2], [3, -1]]), 3)
3994+
# XXX: fmpz_mat.inv() return fmpq_mat...
3995+
assert M1234.inv() * M1234.det() == M([[4, -2], [-3, 1]])
39553996

39563997

39573998
def test_matrices_det():
3958-
for M, S, is_field in _all_matrices():
3999+
for M, S, is_field, characteristic in _all_matrices():
39594000
M1234 = M([[1, 2], [3, 4]])
39604001
assert M1234.det() == S(-2)
39614002
M9 = M([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
@@ -3965,7 +4006,7 @@ def test_matrices_det():
39654006

39664007

39674008
def test_matrices_charpoly():
3968-
for M, S, is_field in _all_matrices():
4009+
for M, S, is_field, characteristic in _all_matrices():
39694010
P = _poly_type_from_matrix_type(M)
39704011
M1234 = M([[1, 2], [3, 4]])
39714012
assert M1234.charpoly() == P([-2, -5, 1])
@@ -3976,18 +4017,21 @@ def test_matrices_charpoly():
39764017

39774018

39784019
def test_matrices_minpoly():
3979-
for M, S, is_field in _all_matrices():
4020+
for M, S, is_field, characteristic in _all_matrices():
4021+
if characteristic > 0 and not is_field:
4022+
assert raises(lambda: M([[1, 2], [3, 4]]).minpoly(), DomainError)
4023+
continue
39804024
P = _poly_type_from_matrix_type(M)
3981-
M1234 = M([[1, 2], [3, 4]])
3982-
assert M1234.minpoly() == P([-2, -5, 1])
3983-
M9 = M([[2, 1, 0], [0, 2, 0], [0, 0, 2]])
3984-
assert M9.minpoly() == P([4, -4, 1])
3985-
Mr = M([[1, 2, 3], [4, 5, 6]])
3986-
assert raises(lambda: Mr.minpoly(), ValueError)
4025+
assert M([[1, 2], [3, 4]]).minpoly() == P([-2, -5, 1])
4026+
assert M([[2, 1, 0], [0, 2, 0], [0, 0, 2]]).minpoly() == P([4, -4, 1])
4027+
assert raises(lambda: M([[1, 2, 3], [4, 5, 6]]).minpoly(), ValueError)
39874028

39884029

39894030
def test_matrices_rank():
3990-
for M, S, is_field in _all_matrices():
4031+
for M, S, is_field, characteristic in _all_matrices():
4032+
if characteristic > 0 and not is_field:
4033+
assert raises(lambda: M([[1, 2], [3, 4]]).rank(), DomainError)
4034+
continue
39914035
M1234 = M([[1, 2], [3, 4]])
39924036
assert M1234.rank() == 2
39934037
Mr = M([[1, 2, 3], [4, 5, 6]])
@@ -3999,37 +4043,57 @@ def test_matrices_rank():
39994043

40004044

40014045
def test_matrices_rref():
4002-
for M, S, is_field in _all_matrices():
4003-
if is_field:
4004-
Mr = M([[1, 2, 3], [4, 5, 6]])
4005-
Mr_rref = M([[1, 0, -1], [0, 1, 2]])
4046+
for M, S, is_field, characteristic in _all_matrices():
4047+
4048+
Mr = M([[1, 2, 3], [4, 5, 6]])
4049+
Mr_rref = M([[1, 0, -1], [0, 1, 2]])
4050+
4051+
if characteristic > 0 and not is_field:
4052+
# Z/nZ (n composite) raises
4053+
assert raises(lambda: Mr.rref(), DomainError)
4054+
elif is_field:
4055+
# Q, Z/pZ and GF(p^d) return usual RREF
40064056
assert Mr.rref() == (Mr_rref, 2)
40074057
assert Mr == M([[1, 2, 3], [4, 5, 6]])
40084058
assert Mr.rref(inplace=True) == (Mr_rref, 2)
40094059
assert Mr == Mr_rref
4060+
else:
4061+
# Z returns RREF with divisor -3
4062+
d = -3
4063+
assert Mr.rref() == (d*Mr_rref, d, 2)
4064+
assert Mr == M([[1, 2, 3], [4, 5, 6]])
4065+
assert Mr.rref(inplace=True) == (d*Mr_rref, d, 2)
4066+
assert Mr == d*Mr_rref
40104067

40114068

40124069
def test_matrices_solve():
4013-
for M, S, is_field in _all_matrices():
4014-
if is_field:
4015-
A = M([[1, 2], [3, 4]])
4016-
x = M([[1], [2]])
4017-
b = M([[5], [11]])
4018-
assert A*x == b
4070+
for M, S, is_field, characteristic in _all_matrices():
4071+
4072+
A = M([[1, 2], [3, 4]])
4073+
x = M([[1], [2]])
4074+
b = M([[5], [11]])
4075+
assert A*x == b
4076+
4077+
A2 = M([[1, 2], [2, 4]])
4078+
4079+
if characteristic > 0 and not is_field:
4080+
assert raises(lambda: A.solve(b), DomainError)
4081+
assert raises(lambda: A2.solve(b), DomainError)
4082+
else:
40194083
assert A.solve(b) == x
4020-
A22 = M([[1, 2], [3, 4]])
4021-
A23 = M([[1, 2, 3], [4, 5, 6]])
4022-
b2 = M([[5], [11]])
4023-
b3 = M([[5], [11], [17]])
4024-
assert raises(lambda: A22.solve(b3), ValueError)
4025-
assert raises(lambda: A23.solve(b2), ValueError)
4026-
assert raises(lambda: A.solve(None), TypeError)
4027-
A = M([[1, 2], [2, 4]])
4028-
assert raises(lambda: A.solve(b), ZeroDivisionError)
4084+
assert raises(lambda: A2.solve(b), ZeroDivisionError)
4085+
4086+
A22 = M([[1, 2], [3, 4]])
4087+
A23 = M([[1, 2, 3], [4, 5, 6]])
4088+
b2 = M([[5], [11]])
4089+
b3 = M([[5], [11], [17]])
4090+
assert raises(lambda: A22.solve(b3), ValueError)
4091+
assert raises(lambda: A23.solve(b2), ValueError)
4092+
assert raises(lambda: A.solve(None), TypeError)
40294093

40304094

40314095
def test_matrices_transpose():
4032-
for M, S, is_field in _all_matrices():
4096+
for M, S, is_field, characteristic in _all_matrices():
40334097
M1234 = M([[1, 2, 3], [4, 5, 6]])
40344098
assert M1234.transpose() == M([[1, 4], [2, 5], [3, 6]])
40354099

src/flint/types/fmpz_mat.pyx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,11 @@ cdef class fmpz_mat(flint_mat):
308308
raise ValueError("matrix must be square")
309309
if m is not None:
310310
raise NotImplementedError("modular matrix exponentiation")
311+
if e < 0:
312+
raise DomainError("negative power of integer matrix: M**%i" % e)
311313
ee = e
312-
t = fmpz_mat(self) # XXX
314+
t = fmpz_mat.__new__(fmpz_mat)
315+
fmpz_mat_init_set(t.val, (<fmpz_mat>self).val)
313316
fmpz_mat_pow(t.val, t.val, ee)
314317
return t
315318

0 commit comments

Comments
 (0)