Z2 = Zp(2, prec=30, print_mode="digits")
Q2 = Z2.fraction_field()
Z3 = Zp(3, prec=20, print_mode="digits")
x = random_element(Z3,5)
x
When we multiply by $p$, we gain one digit of absolute precision:
3*x
The ZpL package sees this gain of precision even if the computation is split into several steps:
x + x + x
y = 2*x
y
x + y
The same with multiplication:
x^3
x * x * x
y = x^2
y
x * y
x = random_element(Z2,10)
y = random_element(Z2,5)
u = x+y
u
v = x-y
v
Now we compute $u + v$, which is also $2x$. Here is what we get:
u+v
2*x
The SOMOS 4 sequence is the sequence defined by the recurrence
$$u_{n+4} = \frac{u_{n+1}u_{n+3} + u_{n+2}^2}{u_n}.$$
It turns out that its computation is highly unstable.
For this reason, using the ZpL package can here be decisive.
def somos(u0, u1, u2, u3, n):
a, b, c, d = u0, u1, u2, u3
for _ in range(4, n+1):
a, b, c, d = b, c, d, (b*d + c*c) / a
return d
somos(Z2(1,15), Z2(1,15), Z2(1,15), Z2(3,15), 18)
somos(Z2(1,15), Z2(1,15), Z2(1,15), Z2(3,15), 100)
MS = MatrixSpace(Z2,2)
M = MS(1)
for _ in range(60):
M *= random_element(MS, prec=8)
M
D = diagonal_matrix([ Z3(1,5), Z3(9,5), Z3(27,5), Z3(81,5) ])
D
The determinant of $D$ is known at higher precision. This is due to the particular shape of $D$.
D.determinant()
This phenomenon is preserved when $D$ is modified by (left and right) multiplication by some matrix. However the software may or may not see it, depending on the method it uses for tracking precision
MS = D.parent()
P = random_element(MS, prec=5)
Q = random_element(MS, prec=5)
M = P*D*Q
M
M.determinant()
P.determinant() * D.determinant() * Q.determinant()
Notice that ZpCR cannot decide whether the determinant $M$ vanishes. This may have important consequences if, at some point, we will test if $M$ is invertible or not and jump to one branch or another one depending on the result.
M.charpoly()
def dodgson(M): # M must be a square matrix
n = M.nrows() - 1
A = M
B = matrix(n, n,
[ [ M[i,j]*M[i+1,j+1] - M[i+1,j]*M[i,j+1]
for j in range(n) ] for i in range(n) ])
for m in range(n-1, 0, -1):
A, B = B, matrix(m, m,
[ [ (B[i,j]*B[i+1,j+1] - B[i+1,j]*B[i,j+1]) / A[i+1,j+1]
for j in range(m) ] for i in range(m) ])
return B[0,0]
MS = MatrixSpace(Z2, 6)
M = random_element(MS, prec=10)
dodgson(M)
M.determinant()
S.<x> = PolynomialRing(Q2)
P = random_element(S, degree=10, prec=5)
Q = random_element(S, degree=10, prec=5)
D = x^5 + random_element(S, degree=4, prec=8)
D
With high probability, $P$ and $Q$ are coprime, implying that the gcd of $DP$ is $DQ$ is $D$.
However, we observe that we do not always get this expected result:
def euclidean(A,B):
while B != 0:
A, B = B, A % B
return A.monic()
euclidean(D*P, D*Q)
More precisely, ZpCR often found a gcd of higher degree.
Indeed the Euclidean algorithm stops prematurely because the software believes that some early remainder vanishes due to the lack of precision.
The situation with ZpFP is the exact opposite:
since the precision is too high (always the maximal cap), the software does not notice that the remainder vanishes. Therefore the Euclidean algorithm does not stop until it obtains a remainder of degree $0$.
R.<x,y,z> = PolynomialRing(Q2, order = 'invlex')
F = [Q2(2,10)*x + Q2(1,10)*z,
Q2(1,10)*x^2 + Q2(1,10)*y^2 - Q2(2,10)*z^2,
Q2(4,10)*y^2 + Q2(1,10)*y*z + Q2(8,10)*z^2]
from sage.rings.polynomial.toy_buchberger import buchberger_improved
g = buchberger_improved(ideal(F)); g.sort()
g
def edp_simple(g, h, N):
S = g.parent()
u = S.gen()
for i in range(N):
u -= h(u) * (u.derivative()/h(u) - g).integral()
u = u[:2^(i+1)]
return u
N = 4
S.<t> = PowerSeriesRing(Q2, 2^N)
h = 1 + t + t^3
y = t + t^2 * random_element(S,10)
g = y.derivative() / h(y)
u = edp_simple(g, h, N)
u[15]