Skip to content
This repository was archived by the owner on Feb 1, 2023. It is now read-only.

Commit f0ab074

Browse files
author
Release Manager
committed
Trac #30892: Characteristic polynomials over complete discrete valuation rings/fields
We implement Hessenberg algorithm (with choice of pivot) for computing the characteristic polynomial of a matrix with coefficients in a complete discrete valuation ring/field. URL: https://trac.sagemath.org/30892 Reported by: caruso Ticket author(s): Xavier Caruso, Raphaël Pagès Reviewer(s): Travis Scrimshaw
2 parents 4415f75 + e3432b4 commit f0ab074

File tree

8 files changed

+246
-72
lines changed

8 files changed

+246
-72
lines changed

src/sage/categories/discrete_valuation.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,48 @@ def residue_field(self):
6464
Rational Field
6565
"""
6666

67+
def _matrix_charpoly(self, M, var):
68+
r"""
69+
Return the characteristic polynomial of `M`.
70+
71+
EXAMPLES::
72+
73+
sage: R.<t> = PowerSeriesRing(GF(5))
74+
sage: M = matrix(4, 4, [ (t^(i+j)).add_bigoh(10)
75+
....: for i in range(4) for j in range(4) ])
76+
sage: M
77+
[ 1 + O(t^10) t + O(t^10) t^2 + O(t^10) t^3 + O(t^10)]
78+
[ t + O(t^10) t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)]
79+
[t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10)]
80+
[t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10) t^6 + O(t^10)]
81+
sage: M.charpoly() # indirect doctest
82+
x^4 + (4 + 4*t^2 + 4*t^4 + 4*t^6 + O(t^10))*x^3
83+
84+
Note that this function uses a Hessenberg-like algorithm
85+
that performs divisions. Hence, truncations may show up
86+
even if the input matrix is exact::
87+
88+
sage: M = matrix(3, 3, [ 1, t, t^2, 1+t, t^2, t^3, t^2, t^3, t^4 ])
89+
sage: M
90+
[ 1 t t^2]
91+
[1 + t t^2 t^3]
92+
[ t^2 t^3 t^4]
93+
sage: M.charpoly()
94+
x^3 + (4 + 4*t^2 + 4*t^4 + O(t^25))*x^2 + (4*t + O(t^24))*x
95+
96+
Another example over the p-adics::
97+
98+
sage: R = Zp(5, print_mode="digits", prec=5)
99+
sage: M = matrix(R, 3, 3, range(9))
100+
sage: M
101+
[ 0 ...00001 ...00002]
102+
[ ...00003 ...00004 ...000010]
103+
[ ...00011 ...00012 ...00013]
104+
sage: M.charpoly()
105+
...00001*x^3 + ...44423*x^2 + ...44412*x + ...00000
106+
"""
107+
return M._charpoly_hessenberg(var)
108+
67109
class ElementMethods:
68110
@abstract_method
69111
def valuation(self):
@@ -200,7 +242,7 @@ def uniformizer(self):
200242
@abstract_method
201243
def residue_field(self):
202244
"""
203-
Return the residue field of the ring of integers of
245+
Return the residue field of the ring of integers of
204246
this discrete valuation field.
205247
206248
EXAMPLES::
@@ -213,6 +255,45 @@ def residue_field(self):
213255
Rational Field
214256
"""
215257

258+
def _matrix_hessenbergize(self, H):
259+
r"""
260+
Replace `H` with an Hessenberg form of it.
261+
262+
EXAMPLES::
263+
264+
sage: R.<t> = PowerSeriesRing(GF(5))
265+
sage: K = R.fraction_field()
266+
sage: H = matrix(K, 4, 4, [ (t^(i+j)).add_bigoh(10)
267+
....: for i in range(4) for j in range(4) ])
268+
sage: H
269+
[ 1 + O(t^10) t + O(t^10) t^2 + O(t^10) t^3 + O(t^10)]
270+
[ t + O(t^10) t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)]
271+
[t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10)]
272+
[t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10) t^6 + O(t^10)]
273+
sage: H.hessenbergize()
274+
sage: H
275+
[ 1 + O(t^10) t + t^3 + t^5 + O(t^10) t^2 + O(t^10) t^3 + O(t^10)]
276+
[ t + O(t^10) t^2 + t^4 + t^6 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)]
277+
[ O(t^10) O(t^10) O(t^10) O(t^10)]
278+
[ O(t^10) O(t^10) O(t^10) O(t^10)]
279+
280+
Another example over the p-adics::
281+
282+
sage: K = Qp(5, print_mode="digits", prec=5)
283+
sage: H = matrix(K, 3, 3, range(9))
284+
sage: H
285+
[ 0 ...00001 ...00002]
286+
[ ...00003 ...00004 ...000010]
287+
[ ...00011 ...00012 ...00013]
288+
sage: H.hessenbergize()
289+
sage: H
290+
[ 0 ...00010 ...00002]
291+
[ ...00003 ...00024 ...000010]
292+
[ ...00000 ...44440 ...44443]
293+
"""
294+
from sage.matrix.matrix_cdv import hessenbergize_cdvf
295+
hessenbergize_cdvf(H)
296+
216297
class ElementMethods:
217298
@abstract_method
218299
def valuation(self):

src/sage/matrix/matrix2.pyx

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2700,6 +2700,8 @@ cdef class Matrix(Matrix1):
27002700

27012701
ALGORITHM:
27022702

2703+
If the base ring has a method `_matrix_charpoly`, we use it.
2704+
27032705
In the generic case of matrices over a ring (commutative and with
27042706
unity), there is a division-free algorithm, which can be accessed
27052707
using ``"df"``, with complexity `O(n^4)`. Alternatively, by
@@ -2862,18 +2864,16 @@ cdef class Matrix(Matrix1):
28622864
if f is not None:
28632865
return f.change_variable_name(var)
28642866

2865-
if algorithm is None:
2866-
from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing
2867+
R = self._base_ring
28672868

2868-
R = self._base_ring
2869-
if is_NumberField(R):
2870-
f = self._charpoly_over_number_field(var)
2871-
elif is_IntegerModRing(R):
2872-
f = self.lift().charpoly(var).change_ring(R)
2873-
elif R in _Fields and R.is_exact():
2874-
f = self._charpoly_hessenberg(var)
2875-
else:
2876-
f = self._charpoly_df(var)
2869+
if algorithm is None:
2870+
if hasattr(R, '_matrix_charpoly'):
2871+
f = R._matrix_charpoly(self, var)
2872+
if f is None:
2873+
if R in _Fields and R.is_exact():
2874+
f = self._charpoly_hessenberg(var)
2875+
else:
2876+
f = self._charpoly_df(var)
28772877
else:
28782878
if algorithm == "hessenberg":
28792879
f = self._charpoly_hessenberg(var)
@@ -3049,53 +3049,6 @@ cdef class Matrix(Matrix1):
30493049

30503050
return f
30513051

3052-
def _charpoly_over_number_field(self, var='x'):
3053-
r"""
3054-
Use PARI to compute the characteristic polynomial of self as a
3055-
polynomial over the base ring.
3056-
3057-
EXAMPLES::
3058-
3059-
sage: x = QQ['x'].gen()
3060-
sage: K.<a> = NumberField(x^2 - 2)
3061-
sage: m = matrix(K, [[a-1, 2], [a, a+1]])
3062-
sage: m._charpoly_over_number_field('Z')
3063-
Z^2 - 2*a*Z - 2*a + 1
3064-
sage: m._charpoly_over_number_field('a')(m) == 0
3065-
True
3066-
sage: m = matrix(K, [[0, a, 0], [-a, 0, 0], [0, 0, 0]])
3067-
sage: m._charpoly_over_number_field('Z')
3068-
Z^3 + 2*Z
3069-
3070-
The remaining tests are indirect::
3071-
3072-
sage: L.<b> = K.extension(x^3 - a)
3073-
sage: m = matrix(L, [[b+a, 1], [a, b^2-2]])
3074-
sage: m.charpoly('Z')
3075-
Z^2 + (-b^2 - b - a + 2)*Z + a*b^2 - 2*b - 2*a
3076-
sage: m.charpoly('a')
3077-
a^2 + (-b^2 - b - a + 2)*a + a*b^2 - 2*b - 2*a
3078-
sage: m.charpoly('a')(m) == 0
3079-
True
3080-
3081-
::
3082-
3083-
sage: M.<c> = L.extension(x^2 - a*x + b)
3084-
sage: m = matrix(M, [[a+b+c, 0, b], [0, c, 1], [a-1, b^2+1, 2]])
3085-
sage: f = m.charpoly('Z'); f
3086-
Z^3 + (-2*c - b - a - 2)*Z^2 + ((b + 2*a + 4)*c - b^2 + (-a + 2)*b + 2*a - 1)*Z + (b^2 + (a - 3)*b - 4*a + 1)*c + a*b^2 + 3*b + 2*a
3087-
sage: f(m) == 0
3088-
True
3089-
sage: f.base_ring() is M
3090-
True
3091-
"""
3092-
K = self.base_ring()
3093-
if not is_NumberField(K):
3094-
raise ValueError("_charpoly_over_number_field called with base ring (%s) not a number field" % K)
3095-
3096-
paripoly = self.__pari__().charpoly()
3097-
return K[var](paripoly)
3098-
30993052
def fcp(self, var='x'):
31003053
"""
31013054
Return the factorization of the characteristic polynomial of self.
@@ -3373,11 +3326,16 @@ cdef class Matrix(Matrix1):
33733326
if not self.is_square():
33743327
raise TypeError("self must be square")
33753328

3329+
self.check_mutability()
3330+
3331+
base = self._base_ring
3332+
if hasattr(base, '_matrix_hessenbergize'):
3333+
base._matrix_hessenbergize(self)
3334+
return
3335+
33763336
if self._base_ring not in _Fields:
33773337
raise TypeError("Hessenbergize only possible for matrices over a field")
33783338

3379-
self.check_mutability()
3380-
33813339
zero = self._base_ring(0)
33823340
one = self._base_ring(1)
33833341
for m from 1 <= m < n-1:

src/sage/matrix/matrix_cdv.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense
2+
3+
cpdef hessenbergize_cdvf(Matrix_generic_dense)

src/sage/matrix/matrix_cdv.pyx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
r"""
2+
Special methods for matrices over discrete valuation rings/fields.
3+
"""
4+
5+
# ****************************************************************************
6+
# Copyright (C) 2020 Xavier Caruso <[email protected]>
7+
# Raphaël Pagès <[email protected]>
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 2 of the License, or
12+
# (at your option) any later version.
13+
# https://www.gnu.org/licenses/
14+
# ****************************************************************************
15+
16+
17+
from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense
18+
from sage.structure.element cimport RingElement
19+
from sage.rings.infinity import Infinity
20+
21+
22+
# We assume that H is square
23+
cpdef hessenbergize_cdvf(Matrix_generic_dense H):
24+
r"""
25+
Replace `H` with an Hessenberg form of it.
26+
27+
.. NOTE::
28+
29+
This function assumes that H is a matrix over
30+
a complete discrete valuation field.
31+
32+
The pivot on each column is always chosen
33+
with maximal relative precision, which ensures
34+
the numerical stability of the algorithm.
35+
36+
TESTS::
37+
38+
sage: K = Qp(5, print_mode="digits", prec=5)
39+
sage: H = matrix(K, 3, 3, range(9))
40+
sage: H
41+
[ 0 ...00001 ...00002]
42+
[ ...00003 ...00004 ...000010]
43+
[ ...00011 ...00012 ...00013]
44+
sage: H.hessenbergize()
45+
sage: H
46+
[ 0 ...00010 ...00002]
47+
[ ...00003 ...00024 ...000010]
48+
[ ...00000 ...44440 ...44443]
49+
50+
::
51+
52+
sage: M = random_matrix(K, 6, 6)
53+
sage: M.charpoly()[0] == M.determinant()
54+
True
55+
"""
56+
cdef Py_ssize_t n, m, i, j, k
57+
cdef Matrix_generic_dense c
58+
cdef RingElement pivot, inv, scalar
59+
60+
n = H.nrows()
61+
for j in range(n-1):
62+
k = j + 1
63+
maxi = H.get_unsafe(k, j).precision_relative()
64+
i = j + 2
65+
while maxi is not Infinity and i < n:
66+
entry = H.get_unsafe(i, j)
67+
if entry:
68+
m = entry.precision_relative()
69+
if m > maxi:
70+
maxi = m
71+
k = i
72+
i += 1
73+
74+
if k != j + 1:
75+
H.swap_rows_c(j+1, k)
76+
H.swap_columns_c(j+1, k)
77+
pivot = H.get_unsafe(j+1, j)
78+
if pivot:
79+
inv = ~pivot
80+
for i in range(j+2, n):
81+
scalar = inv * H.get_unsafe(i, j)
82+
H.add_multiple_of_row_c(i, j+1, -scalar, j)
83+
H.add_multiple_of_column_c(j+1, i, scalar, 0)

src/sage/matrix/matrix_cyclo_dense.pyx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1285,7 +1285,8 @@ cdef class Matrix_cyclo_dense(Matrix_dense):
12851285
if algorithm == 'multimodular':
12861286
f = self._charpoly_multimodular(var, proof=proof)
12871287
elif algorithm == 'pari':
1288-
f = self._charpoly_over_number_field(var)
1288+
paripoly = self.__pari__().charpoly()
1289+
f = self.base_ring()[var](paripoly)
12891290
elif algorithm == 'hessenberg':
12901291
f = self._charpoly_hessenberg(var)
12911292
else:

src/sage/rings/laurent_series_ring_element.pyx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,15 @@ cdef class LaurentSeries(AlgebraElement):
155155

156156
# self is that t^n * u:
157157
if not f:
158-
if n == infinity:
158+
if n is infinity:
159159
self.__n = 0
160160
self.__u = parent._power_series_ring.zero()
161161
else:
162162
self.__n = n
163163
self.__u = f
164164
else:
165165
val = f.valuation()
166-
if val == infinity:
166+
if val is infinity:
167167
self.__n = 0
168168
self.__u = f
169169
elif val == 0:
@@ -328,7 +328,7 @@ cdef class LaurentSeries(AlgebraElement):
328328
'2 + 2/3*t^3'
329329
"""
330330
if self.is_zero():
331-
if self.prec() == infinity:
331+
if self.prec() is infinity:
332332
return "0"
333333
else:
334334
return "O(%s^%s)"%(self._parent.variable_name(),self.prec())
@@ -451,7 +451,7 @@ cdef class LaurentSeries(AlgebraElement):
451451
\left(a + b\right)x
452452
"""
453453
if self.is_zero():
454-
if self.prec() == infinity:
454+
if self.prec() is infinity:
455455
return "0"
456456
else:
457457
return "0 + \\cdots"
@@ -835,7 +835,7 @@ cdef class LaurentSeries(AlgebraElement):
835835
sage: (t^(-2)).add_bigoh(-3)
836836
O(t^-3)
837837
"""
838-
if prec == infinity or prec >= self.prec():
838+
if prec is infinity or prec >= self.prec():
839839
return self
840840
P = self._parent
841841
if not self or prec < self.__n:
@@ -1691,7 +1691,7 @@ cdef class LaurentSeries(AlgebraElement):
16911691
"""
16921692
if prec is None:
16931693
prec = self.prec()
1694-
if prec == infinity:
1694+
if prec is infinity:
16951695
prec = self.parent().default_prec()
16961696
else:
16971697
prec = min(self.prec(), prec)

0 commit comments

Comments
 (0)