Skip to content

Commit 6e2aece

Browse files
authored
More tests. Added tarce und norm for AlgebraicNumbers and NumberFields (#23)
1 parent c6b1c8a commit 6e2aece

File tree

5 files changed

+131
-24
lines changed

5 files changed

+131
-24
lines changed

src/CommutativeRings.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export groebnerbase, SPOL, lextend
3838
export varnames, varname, factors
3939

4040
export characteristic_polynomial, minimal_polynomial, field_polynomial, adjugate, companion
41-
41+
export field_matrix, field_polynomial, tr, norm
4242
export compose_inv, Li, Ein, lin1p, lin1pe, ver
4343

4444
export rational_normal_form, matrix, transformation, polynomials
@@ -52,7 +52,7 @@ import Base: copy, show, promote_rule, convert, abs, isless, length, iterate, el
5252
import Base: Rational, numerator, denominator, precision
5353

5454
import Primes: factor, isprime
55-
import LinearAlgebra: checksquare, det
55+
import LinearAlgebra: checksquare, det, norm, tr
5656

5757
# Re-exports (of non-Base functions)
5858
export det, isprime, factor

src/algebraic.jl

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function AlgebraicNumber(a::Complex)
4444
id = 2
4545
ca = conj(ca)
4646
end
47-
AlgebraicNumber(x^2 - 2*ra * x + ia^2 + ra^2, [ca, conj(ca)], id, a, NOCHECK)
47+
AlgebraicNumber(x^2 - 2 * ra * x + ia^2 + ra^2, [ca, conj(ca)], id, a, NOCHECK)
4848
end
4949

5050
function AlgebraicNumber(p::UnivariatePolynomial, a = -Inf)
@@ -215,7 +215,7 @@ function lincomb(a::T, b::T, aa, bb) where T<:UnivariatePolynomial
215215
lincomb(a, aa)
216216
elseif iszero(aa)
217217
lincomb(b, bb)
218-
elseif a == b
218+
elseif a == b # there should be better performance possible in this case - also check if a and b are collinear
219219
ca = companion(a)
220220
ea = I(deg(a))
221221
characteristic_polynomial(kron(ca, ea * aa) + kron(ea * bb, ca))
@@ -231,6 +231,8 @@ end
231231
function lincomb(p::T, aa) where T<:UnivariatePolynomial
232232
if isone(aa)
233233
p
234+
elseif iszero(aa)
235+
monom(T)
234236
else
235237
q = copy(p)
236238
qq = q.coeff
@@ -465,8 +467,15 @@ function rationalconst(expr::Expr)
465467
p = rationalconst(args[2])
466468
isnothing(p) && return p
467469
return inv(p)
470+
elseif fun == :sqrt
471+
p = rationalconst(args[2])
472+
isnothing(p) && return p
473+
sn = isqrt(abs(numerator(p)))
474+
sd = isqrt(denominator(p))
475+
q = sn // sd
476+
return q^2 == p ? q : nothing
468477
end
469-
if fun (:(+), :(-), :(*), :(//), :(inv), (:sqrt))
478+
if fun (:(+), :(-), :(*), :(//))
470479
return false
471480
end
472481
n = length(args)
@@ -516,21 +525,26 @@ function isalgebraic(expr::Expr)
516525
end
517526
end
518527

519-
function AlgebraicNumber(expr::Expr)
528+
AlgebraicNumber(expr::Expr) = AlgebraicNumber(to_algebraic_or_rational(expr))
529+
530+
function to_algebraic_or_rational(expr::Expr)
531+
x = rationalconst(expr)
532+
!isnothing(x) && return x
520533
isalgebraic(expr) || throw(ArgumentError("expression is not an algebraic number"))
521534
head = expr.head
522535
if head == :call
523536
args = expr.args
524537
fun = args[1]
525538
if fun == :(^)
526-
a = AlgebraicNumber(args[2])
539+
a = to_algebraic_or_rational(args[2])
527540
q = rationalconst(args[3])
541+
denominator(q) != 1 && (a = AlgebraicNumber(a))
528542
return a^q
529543
elseif fun (:(+), :(-), :(*), :(//), :(/), :inv, :sqrt)
530544
n = length(args)
531545
eargs = Vector{Any}(undef, n - 1)
532546
for j = 2:n
533-
eargs[j-1] = AlgebraicNumber(args[j])
547+
eargs[j-1] = to_algebraic_or_rational(args[j])
534548
end
535549
return evaluate(Val(fun), eargs)
536550
end
@@ -539,11 +553,58 @@ function AlgebraicNumber(expr::Expr)
539553
throw(ArgumentError("no call, but $head"))
540554
end
541555
end
556+
to_algebraic_or_rational(a::Integer) = a
542557

543558
evaluate(::Val{:(+)}, args) = +(args...)
544559
evaluate(::Val{:(-)}, args) = -(args...)
545560
evaluate(::Val{:(*)}, args) = *(args...)
546-
evaluate(::Val{:(/)}, args) = //(args...)
547-
evaluate(::Val{:(//)}, args) = //(args...)
561+
evaluate(::Val{:(/)}, args) = /(args...)
562+
evaluate(::Val{:(//)}, args) = /(args...)
548563
evaluate(::Val{:(inv)}, args) = inv(args...)
549-
evaluate(::Val{:(sqrt)}, args) = sqrt(args...)
564+
evaluate(::Val{:(sqrt)}, args) = sqrt(AlgebraicNumber(args[1]))
565+
566+
"""
567+
com2(p, q)
568+
569+
calculates `mod(q^i, p)` for i = 0:n and stores coeffs in n*n matrix M and vector V.
570+
The solution of the linear sytem of equations delivers the coefficients of
571+
the field polynomial of `q(a)` when p is the minimal polynomial of `a`.
572+
573+
Is an alternative to `det(x - companion(p, q))` which is faster sometimes.
574+
"""
575+
function com2(p::UnivariatePolynomial{T}, q) where T
576+
n = deg(p)
577+
S = typeof(value(p[0]))
578+
M = zeros(S, n, n)
579+
s = q^0
580+
for i = 0:n-1
581+
k = deg(s)
582+
for k = 0:k
583+
M[k+1, i+1] = s[k]
584+
end
585+
s = mod(s * q, p)
586+
end
587+
V = zeros(S, n)
588+
k = deg(s)
589+
for k = 0:k
590+
V[k+1] = s[k]
591+
end
592+
M, V
593+
end
594+
595+
"""
596+
evaluate2(q::UnivariatePolynomial, a::AlgebraicNumber)
597+
598+
Evaluates polynomial `q` for an algebraic number `a` using algorithm `com2`.
599+
"""
600+
function evaluate2(q::P, a::AlgebraicNumber) where P<:UnivariatePolynomial
601+
p = minimal_polynomial(a)
602+
M, V = com2(p, q)
603+
m = -P(M \ V) + monom(P, deg(a))
604+
AlgebraicNumber(m, q(approx(a)))
605+
end
606+
607+
field_matrix(a::AlgebraicNumber) = companion(minimal_polynomial(a))
608+
norm(a::AlgebraicNumber) = minimal_polynomial(a)[0] * (-1)^deg(a)
609+
tr(a::AlgebraicNumber) = -minimal_polynomial(a)[deg(a)-1]
610+
discriminant(a::AlgebraicNumber) = discriminant(minimal_polynomial(a))

src/numberfield.jl

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,33 @@ generator(::Type{N}) where N<:NumberField = monom(N)
9191

9292
minimal_polynomial(b::NumberField) = minimal_polynomial(AlgebraicNumber(b))
9393

94-
function field_polynomial(b::N) where N<:NumberField
95-
r = b.repr
96-
q = value(r)
97-
p = modulus(r) # == minimal_polynomial(base(N))
98-
x = monom(typeof(p))
99-
m = det(x * I - companion(p, q))
100-
m
94+
minimal_polynomial(::Type{N}) where N<:NumberField = minimal_polynomial(base(N))
95+
96+
"""
97+
field_matrix(b::NumberField)
98+
99+
Return the field matrix for number field element `b` in the standard basis.
100+
"""
101+
function field_matrix(b::N) where N <:NumberField
102+
p = minimal_polynomial(N)
103+
q = value(b.repr)
104+
companion(p, q)
101105
end
102106

107+
"""
108+
field polynomial(b::NumberField)
109+
110+
The characteristic polynomial of any field matrix of an element `b` of a number field
111+
over `a`.
112+
113+
The field matrix of `b` with the standard basis `a^0, a, ..., a^(n-1)` is the
114+
`companion(p, q)`, where `p` is the minimal polynomial of `a` and `q` is the
115+
polynomial representing `b (b = q(a))`.
116+
"""
117+
field_polynomial(b::NumberField) = characteristic_polynomial(field_matrix(b))
118+
tr(b::NumberField) = tr(field_matrix(b))
119+
norm(b::NumberField) = det(field_matrix(b))
120+
103121
function field_polynomial(a::A, b::B) where {A<:AlgebraicNumber,B<:UnivariatePolynomial}
104122
field_polynomial(b(monom(NF(a))))
105123
end

src/univarpolynom.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ end
563563
function content_primpart(p::P) where {T,X,P<:UnivariatePolynomial{QQ{T},X}}
564564
c = content(p)
565565
Z = ZZ{T}
566-
pp = Z[X](Z.(numerator.((p / c).coeff)), ord(p))
566+
pp = Z[X]([Z(numerator(x/c)) for x in p.coeff], ord(p))
567567
c, pp
568568
end
569569

@@ -777,6 +777,8 @@ If `q` is given, return the companion matrix with respect to `q`. For `q == x` t
777777
identical to the original definition. If `p` is the minimal polynomial of an algebraic
778778
number `a`, then `det(x*I - companion(p, q))` is a multiple of the minimal polynomial
779779
of `q(a)`.
780+
781+
Note, that `companion(p, q) == q(companion(p))`.
780782
"""
781783
function companion(p::UnivariatePolynomial{T}) where T
782784
companion(T, p)

test/algebraic.jl

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,15 @@ end
9797
@test approx(c) op(approx(a), approx(b))
9898
end
9999

100-
@testset "Algebraic $op($a,A) arithmetic" for op in (+, -, *, /), a in (11, ZZ(11))
100+
@testset "Algebraic $op($a,A) arithmetic" for op in (+, -, *, /), a in (0, 11, ZZ(11))
101101
a = big(a)
102102
b = AlgebraicNumber(x^3 + x + 1)
103103
c = op(a, b)
104-
@test deg(c) == deg(b)
104+
if a != 0 || op in (+, -)
105+
@test deg(c) == deg(b)
106+
else
107+
@test c == 0
108+
end
105109
@test minimal_polynomial(c)(c) == 0
106110
@test approx(c) op(approx(a), approx(b))
107111
end
@@ -134,15 +138,34 @@ end
134138
end
135139

136140
@testset "Algebraic - expressions" begin
141+
expr2 = :(sqrt(-2))
142+
@test AlgebraicNumber(expr2) == AlgebraicNumber(x^2 + 2, im)
143+
144+
exprq = :(sqrt((11 / 12)^2))
145+
@test AlgebraicNumber(exprq) == AlgebraicNumber(x - 11//12)
146+
137147
expr5 = :(sqrt(5) + 1)
138148
@test AlgebraicNumber(expr5) / 4 == cospi(QQ(1 // 5))
139149

150+
expr217 = :(
151+
(
152+
2sqrt(17 + 3sqrt(17) - sqrt(34 - 2sqrt(17)) - 2sqrt(34 + 2sqrt(17))) +
153+
sqrt(34 - 2sqrt(17)) +
154+
sqrt(17) - 1
155+
) / 8
156+
)
157+
@test AlgebraicNumber(expr217) == cospi(QQ(2 // 17)) * 2
158+
140159
expr17 = :(
141160
1 - sqrt(17) +
142161
sqrt(34 - sqrt(68)) +
143162
sqrt(68 + sqrt(2448) + sqrt(2720 + sqrt(6284288)))
144163
)
145164
@test AlgebraicNumber(expr17) / 16 == cospi(QQ(1 // 17))
165+
166+
a = AlgebraicNumber(:((3^(1 // 3) - 6^(1 // 3) + 12^(1 // 3)) / 3))
167+
b = AlgebraicNumber(:((2^(1 // 3) - 1)^(1 // 3)))
168+
@test a == b
146169
end
147170

148171
@testset "exactness of pure imaginary roots" begin
@@ -158,16 +181,19 @@ end
158181
@test string(a) == "AlgebraicNumber(x^2 + 4, 2, 0.0 + 2.0im)"
159182
end
160183

161-
@testset "cardano's formula $(x^3+a*x^2+b*x+c)" for (a, b, c) in
162-
((1, -8, -6), (6, 9, 8), rand(-10:10, 3))
184+
@testset "cardano's formula $(x^3+a*x^2+b*x+c)" for (a, b, c) in (
185+
(1, -8, -6),
186+
(6, 9, 8),
187+
rand(-10:10, 3),
188+
)
163189
a, b, c = QQ.((a, b, c))
164190
pc = x^3 + a * x^2 + b * x + c
165191
p = b - a^2 / 3
166192
q = 2 * a^3 / 27 - a * b / 3 + c
167193

168194
da = sqrt(AlgebraicNumber((q / 2)^2 + (p / 3)^3))
169195
ua = (da - q / 2)^(1 // 3)
170-
va = (-p/3) / ua # (-da - q / 2)^(1 // 3)
196+
va = (-p / 3) / ua # (-da - q / 2)^(1 // 3)
171197
e3 = cispi(QQ(2 // 3))
172198
@test ua * va == -p / 3
173199

0 commit comments

Comments
 (0)