Skip to content

Commit

Permalink
Merge pull request #4 from kalmarek/codecov
Browse files Browse the repository at this point in the history
Increasing code coverage
  • Loading branch information
tweisser authored Jul 29, 2020
2 parents 5306873 + 2a70504 commit 835db32
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 71 deletions.
24 changes: 12 additions & 12 deletions src/eigenspacedecomposition.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function row_echelon_form!(A::Matrix{T}) where T <: PrimeFields.GF
function row_echelon_form!(A::Matrix{T}) where T <: FiniteFields.GF
c, d = size(A)
l = Int[]
pos = 0
Expand All @@ -24,12 +24,12 @@ function row_echelon_form!(A::Matrix{T}) where T <: PrimeFields.GF
return A, l
end

function row_echelon_form(A::Matrix{T}) where T <: PrimeFields.GF
function row_echelon_form(A::Matrix{T}) where T <: FiniteFields.GF
return row_echelon_form!(deepcopy(A))
end

#=
function Base.inv(A::Matrix{T}) where T <: PrimeFields.GF
function Base.inv(A::Matrix{T}) where T <: FiniteFields.GF
n, m = size(A)
@assert n == m
B = hcat(deepcopy(A), Matrix{T}(I, n, n))
Expand All @@ -38,7 +38,7 @@ function Base.inv(A::Matrix{T}) where T <: PrimeFields.GF
end
=#

function right_nullspace(M::Matrix{T}) where T <: PrimeFields.GF
function right_nullspace(M::Matrix{T}) where T <: FiniteFields.GF
A, l = row_echelon_form(M)
c, d = size(A)
(length(l) == d) && return zeros(T, d)
Expand All @@ -56,11 +56,11 @@ function right_nullspace(M::Matrix{T}) where T <: PrimeFields.GF
return W
end

function left_nullspace(M::Matrix{T}) where T <: PrimeFields.GF
function left_nullspace(M::Matrix{T}) where T <: FiniteFields.GF
return Matrix(transpose(right_nullspace(Matrix(transpose(M)))))
end

function left_eigen(M::Matrix{T}) where T <: PrimeFields.GF
function left_eigen(M::Matrix{T}) where T <: FiniteFields.GF
@assert ==(size(M)...)
Id = Matrix{eltype(M)}(I, size(M)...)
eigen = Dict{T, typeof(M)}()
Expand All @@ -81,12 +81,12 @@ function left_eigen(M::Matrix{T}) where T <: PrimeFields.GF
return eigen
end

function normalize(v::Array{T, 2}) where T <: PrimeFields.GF
function normalize(v::Array{T, 2}) where T <: FiniteFields.GF
@assert !iszero(v[1])
return v./v[1]
end

function _find_l(M::Matrix{T}) where T <: PrimeFields.GF
function _find_l(M::Matrix{T}) where T <: FiniteFields.GF
# this function should be redundant when defining a better structure for echelonized subspaces
l = Int[]
for i = 1:size(M, 2)
Expand All @@ -100,7 +100,7 @@ end

# EigenSpaceDecomposition

function eigen_decomposition!(M::Matrix{T}) where T <: PrimeFields.GF
function eigen_decomposition!(M::Matrix{T}) where T <: FiniteFields.GF
eigspace_ptrs = Vector{Int}()
eigen = left_eigen(M)
sizehint!(eigspace_ptrs, length(eigen) + 1)
Expand All @@ -117,22 +117,22 @@ function eigen_decomposition!(M::Matrix{T}) where T <: PrimeFields.GF
return M, eigspace_ptrs
end

mutable struct EigenSpaceDecomposition{T <: PrimeFields.GF}
mutable struct EigenSpaceDecomposition{T <: FiniteFields.GF}
basis::Matrix{T}
eigspace_ptrs::Vector{Int}

function EigenSpaceDecomposition(
basis::Matrix{T},
eigspace_ptrs::AbstractVector{<:Integer}
) where T <: PrimeFields.GF
) where T <: FiniteFields.GF

@assert eigspace_ptrs[1] == 1
@assert eigspace_ptrs[end] == size(basis, 1) + 1
return new{T}(basis, eigspace_ptrs)
end
end

EigenSpaceDecomposition(M::Matrix{T}) where T <: PrimeFields.GF =
EigenSpaceDecomposition(M::Matrix{T}) where T <: FiniteFields.GF =
EigenSpaceDecomposition(eigen_decomposition!(deepcopy(M))...)

function Base.show(io::IO, ::MIME"text/plain", esd::EigenSpaceDecomposition{T}) where T
Expand Down
58 changes: 33 additions & 25 deletions src/gf.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
module PrimeFields
module FiniteFields

# taken from https://github.com/kalmarek/RamanujanGraphs.jl/blob/master/src/gf.jl

export GF, int, characteristic
export generator, issqrt, order
using Primes
export GF, int, characteristic
export generator, issqrt

struct GF{q} <: Number
value::Int16

function GF{q}(n) where {q}
@assert q > 1
return new{q}(mod(n,q))
value::Int

function GF{q}(n, check=true) where {q}
if check
@assert q > 1
@assert isprime(q)
end
return new{q}(mod(n, q))
end
end

Expand All @@ -23,25 +27,29 @@ Base.:(==)(n::GF{q}, m::GF{q}) where {q} = int(n) == int(m)
Base.hash(n::GF{q}, h::UInt) where {q} =
xor(0x04fd9e474909f8bf, hash(q, hash(int(n), h)))

Base.:+(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) + int(m))
Base.:-(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) - int(m))
Base.:*(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) * int(m))
Base.:+(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) + int(m), false)
Base.:-(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) - int(m), false)
Base.:*(n::GF{q}, m::GF{q}) where {q} = GF{q}(int(n) * int(m), false)
Base.:/(n::GF{q}, m::GF{q}) where {q} = n * inv(m)

Base.:-(n::GF{q}) where {q} = GF{q}(q - int(n))
Base.inv(n::GF{q}) where {q} = GF{q}(invmod(int(n), q))
Base.:-(n::GF{q}) where {q} = GF{q}(q - int(n), false)
Base.inv(n::GF{q}) where {q} = GF{q}(invmod(int(n), q), false)

function Base.:^(n::GF{q}, i::Integer) where {q}
i < 0 && return inv(n)^-i
return GF{q}(powermod(int(n), i, q))
return GF{q}(powermod(int(n), i, q), false)
end

Base.zero(::Type{GF{q}}) where {q} = GF{q}(0)
Base.one(::Type{GF{q}}) where {q} = GF{q}(1)
Base.zero(::Type{GF{q}}) where {q} = GF{q}(0, false)
Base.one(::Type{GF{q}}) where {q} = GF{q}(1, false)
Base.iszero(n::GF) = int(n) == 0
Base.isone(n::GF) = int(n) == 1

Base.promote_rule(::Type{GF{q}}, ::Type{I}) where {q,I<:Integer} = GF{q}
Base.promote_rule(::Type{GF{p}}, ::Type{GF{q}}) where {p,q} = throw(DomainError(
(GF{p}, GF{q}),
"Cannot perform arithmetic on elements from different fields",
))

# taken from ValidatedNumerics, under under the MIT "Expat" License:
# https://github.com/JuliaIntervals/ValidatedNumerics.jl/blob/master/LICENSE.md
Expand All @@ -59,17 +67,18 @@ function legendresymbol(n, q)
return -one(n)
end

function generator(n::GF{q}) where {q}
for i = 2:q-1
g = GF{q}(i)
function generator(::Type{GF{q}}) where {q}
q == 2 && return one(GF{2})
for i = 2:q-1 # bruteforce loop
g = GF{q}(i, false)
any(isone, g^k for k = 2:q-2) && continue
return g
end
return zero(n) # never hit, to keep compiler happy
return zero(GF{q}) # never hit, to keep compiler happy
end

Base.sqrt(n::GF{q}) where {q} = GF{q}(sqrtmod(int(n), q))
issqrt(n::GF{q}) where {q} = legendresymbol(int(n), q) >= 0
Base.sqrt(n::GF{q}) where {q} = GF{q}(sqrtmod(int(n), q), false)
issquare(n::GF{q}) where {q} = legendresymbol(int(n), q) >= 0

function sqrtmod(n::Integer, q::Integer)
l = legendresymbol(n, q)
Expand All @@ -82,9 +91,8 @@ function sqrtmod(n::Integer, q::Integer)
return zero(n) # never hit, to keep compiler happy
end

order(::Type{GF{q}}) where {q} = q
Base.iterate(::Type{GF{q}}, s = 0) where {q} =
s >= q ? nothing : (GF{q}(s), s + 1)
s >= q ? nothing : (GF{q}(s, false), s + 1)
Base.eltype(::Type{GF{q}}) where {q} = GF{q}
Base.size(gf::Type{<:GF}) = (order(gf),)
Base.size(gf::Type{<:GF}) = (characteristic(gf),)
end # module
131 changes: 131 additions & 0 deletions test/ccmatrix.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Pkg
Pkg.add(PackageSpec(url="https://github.com/kalmarek/PermutationGroups.jl"))
using PermutationGroups

function generic_tests_ccmatrix(C)
r = length(C)
for i = 1:r
ccm = SymbolicWedderburn.CCMatrix(C, i)
l = length(C[i])
@test all([sum(ccm[:,j]) == l for j = 1:r])
end
ccm1 = SymbolicWedderburn.CCMatrix(C, 1)
@test all([isone(ccm1[i,i]) for i = 1:r])
end

@testset "ConjClassMatrix" begin
@testset "example: Symmetric group (4)" begin
G = SymbolicWedderburn.AbstractAlgebra.SymmetricGroup(4)
C = conjugacy_classes(G)
generic_tests_ccmatrix(C)
@test SymbolicWedderburn.CCMatrix(C, 2) == [0 1 0 0 0;
6 0 3 0 2;
0 4 0 4 0;
0 0 3 0 4;
0 1 0 2 0]
@test SymbolicWedderburn.CCMatrix(C, 3) == [0 0 1 0 0;
0 4 0 4 0;
8 0 4 0 8;
0 4 0 4 0;
0 0 3 0 0]
@test SymbolicWedderburn.CCMatrix(C, 4) == [0 0 0 1 0;
0 0 3 0 4;
0 4 0 4 0;
6 0 3 0 2;
0 2 0 1 0]
@test SymbolicWedderburn.CCMatrix(C, 5) == [0 0 0 0 1;
0 1 0 2 0;
0 0 3 0 0;
0 2 0 1 0;
3 0 0 0 2]
end
@testset "example: Alternating group (4)" begin
a = PermutationGroups.perm"(1,2,3)(4)"
b = PermutationGroups.perm"(1,2,4)"
G = PermutationGroups.PermGroup([a,b])
C = [
Orbit([a,b], one(G)),
Orbit([a,b], PermutationGroups.perm"(1,2)(3,4)"),
Orbit([a,b], PermutationGroups.perm"(1,2,3)(4)"),
Orbit([a,b], PermutationGroups.perm"(1,3,2)(4)"),
]

generic_tests_ccmatrix(C)
@test SymbolicWedderburn.CCMatrix(C, 1) == [1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1]
@test SymbolicWedderburn.CCMatrix(C, 2) == [0 1 0 0
3 2 0 0
0 0 3 0
0 0 0 3]
@test SymbolicWedderburn.CCMatrix(C, 3) == [0 0 1 0
0 0 3 0
0 0 0 4
4 4 0 0]
@test SymbolicWedderburn.CCMatrix(C, 4) == [0 0 0 1
0 0 0 3
4 4 0 0
0 0 4 0]
end

@testset "example: C₅ ⋊ C₄" begin
S = [PermutationGroups.perm"(1,2,4,5,3)", PermutationGroups.perm"(2,5,3,4)"]
G = PermGroup(S);

ccG = [
Orbit(S, one(G)),
Orbit(S, PermutationGroups.perm"(2,3)(4,5)"),
Orbit(S, PermutationGroups.perm"(2,4,3,5)"),
Orbit(S, PermutationGroups.perm"(2,5,3,4)"),
Orbit(S, PermutationGroups.perm"(1,2,4,5,3)"),
]
# the order of cclasses is taken from GAP

@assert sum(length, ccG) == PermutationGroups.order(G)

@test SymbolicWedderburn.CCMatrix(ccG, 1) ==
[ 1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1 ]
@test SymbolicWedderburn.CCMatrix(ccG, 2) ==
[ 0 1 0 0 0
5 0 0 0 5
0 0 0 5 0
0 0 5 0 0
0 4 0 0 0 ]
@test SymbolicWedderburn.CCMatrix(ccG, 3) ==
[ 0 0 1 0 0
0 0 0 5 0
0 5 0 0 0
5 0 0 0 5
0 0 4 0 0 ]
@test SymbolicWedderburn.CCMatrix(ccG, 4) ==
[ 0 0 0 1 0
0 0 5 0 0
5 0 0 0 5
0 5 0 0 0
0 0 0 4 0 ]
@test SymbolicWedderburn.CCMatrix(ccG, 5) ==
[ 0 0 0 0 1
0 4 0 0 0
0 0 4 0 0
0 0 0 4 0
4 0 0 0 3 ]

generic_tests_ccmatrix(ccG)
generic_tests_ccmatrix(conjugacy_classes(G)) # might be in different order
end

@testset "random subgroups of SymetricGroup(N)" begin
for i in 2:6
G = SymbolicWedderburn.AbstractAlgebra.SymmetricGroup(i)
for _ in 1:5
PG = PermutationGroups.PermGroup(rand(G, 2))
generic_tests_ccmatrix(SymbolicWedderburn.conjugacy_classes(PG))
end
end
end
end
27 changes: 21 additions & 6 deletions test/dixon.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
function _dixon_prime(n::Int)
F = SymbolicWedderburn.Primes.factor(n)
e = lcm(collect(keys(F)))
p = SymbolicWedderburn.dixon_prime(n, e)
@test mod(p, e) == 1
@test p > 2*floor(sqrt(n))
end


@testset "DixonPrime" begin
@testset "DixonPrimeNumbers" begin
@testset "examples" begin
@test SymbolicWedderburn.dixon_prime(20, 20) == 41
end

@testset "random" begin
for i in abs.(rand(2:1000, 10))
_dixon_prime(i)
end
end
end

@testset "DixonPrimeGroups" begin
G = SymbolicWedderburn.AbstractAlgebra.SymmetricGroup(4)
ccG = SymbolicWedderburn.PermutationGroups.conjugacy_classes(G)
@test exponent(G) == 12
@test exponent(ccG) == 12

@test SymbolicWedderburn.dixon_prime(G) == SymbolicWedderburn.dixon_prime(ccG)
@test SymbolicWedderburn.dixon_prime(20, 20) == 41
end

@testset "DixonPrimeGroups" begin
end
end
@warn "This section needs more tests!"
Loading

0 comments on commit 835db32

Please sign in to comment.