-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
some methods for computing orth. discriminants #2748
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
"Orthogonal discriminants" => [ | ||
"introduction.md", | ||
"access.md", | ||
"compute.md", | ||
"misc.md", | ||
], | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
```@meta | ||
CurrentModule = Oscar.OrthogonalDiscriminants | ||
DocTestSetup = quote | ||
using Oscar | ||
end | ||
``` | ||
|
||
# Criteria for computing orthogonal discriminants | ||
|
||
## Character-theoretical criteria | ||
|
||
```@docs | ||
od_from_order | ||
od_from_eigenvalues | ||
od_for_specht_module | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
|
||
# character-theoretical methods | ||
|
||
|
||
@doc raw""" | ||
od_from_order(chi::GAPGroupClassFunction) | ||
Return `(flag, val)` where `flag` is `true` if the order of the group | ||
of `chi` divides only one of the orders of the two orthogonal groups | ||
[`omega_group`](@ref)`(+/-1, d, q)`, where `d` is the degree of `chi` | ||
and `q` is the order of the field of definition of `chi`. | ||
In this case, `val` is `"O+"` or `"O-"`. | ||
```jldoctest | ||
julia> t = character_table("L3(2)"); | ||
julia> Oscar.OrthogonalDiscriminants.od_from_order(mod(t, 3)[4]) | ||
(true, "O-") | ||
julia> Oscar.OrthogonalDiscriminants.od_from_order(mod(t, 2)[4]) | ||
(false, "") | ||
``` | ||
""" | ||
function od_from_order(chi::GAPGroupClassFunction) | ||
characteristic(chi) == 0 && return (false, "") | ||
d = numerator(degree(chi)) | ||
q = order_field_of_definition(chi) | ||
tbl = ordinary_table(chi.table) | ||
ord = order(ZZRingElem, tbl) | ||
|
||
# Compute the order of the subgroup that shall embed into | ||
# the perfect group `omega_group(epsilon, d, q)`. | ||
n = sum(class_lengths(tbl)[class_positions_of_solvable_residuum(tbl)]) | ||
flag1, flag2 = order_omega_mod_N(d, q, n) | ||
if flag1 | ||
return flag2 ? (false, "") : (true, "O+") | ||
else | ||
return flag2 ? (true, "O-") : (false, "") | ||
end | ||
end | ||
|
||
|
||
@doc raw""" | ||
od_from_eigenvalues(chi::GAPGroupClassFunction) | ||
Return `(flag, val)` where `flag` is `true` if there is a conjugacy class | ||
on which representing matrices for `chi` have no eigenvalue $\pm 1$. | ||
In this case, if `chi` is orthogonally stable (this is not checked here) | ||
then `val` is a string that describes the orthogonal discriminant of `chi`. | ||
If `flag` is `false` then `val` is equal to `""`. | ||
This criterion works only if the characteristic of `chi` is not $2$, | ||
`(false, "")` is returned if the characteristic is $2$. | ||
# Examples | ||
```jldoctest | ||
julia> t = character_table("A5"); | ||
julia> Oscar.OrthogonalDiscriminants.od_from_eigenvalues(t[4]) | ||
(true, "5") | ||
julia> Oscar.OrthogonalDiscriminants.od_from_eigenvalues(mod(t, 3)[4]) | ||
(true, "O-") | ||
julia> Oscar.OrthogonalDiscriminants.od_from_eigenvalues(mod(t, 2)[4]) | ||
(false, "") | ||
``` | ||
""" | ||
function od_from_eigenvalues(chi::GAPGroupClassFunction) | ||
p = characteristic(chi) | ||
p == 2 && return (false, "") | ||
|
||
tbl = chi.table | ||
ord = orders_class_representatives(tbl) | ||
for i in 2:length(chi) | ||
n = ord[i] | ||
ev = multiplicities_eigenvalues(chi, i) | ||
if ev[end] != 0 || (iseven(n) && ev[divexact(n, 2)] != 0) | ||
continue | ||
end | ||
|
||
F, z = cyclotomic_field(n) | ||
od = prod(x -> x[1]^x[2], [(z^i-z^-i, ev[i]) for i in 1:n]) | ||
if mod(degree(chi), 4) == 2 | ||
od = -od | ||
end | ||
|
||
K, _ = abelian_closure(QQ) | ||
if p == 0 | ||
# Coerce `od` into the character field of `chi`. | ||
F, emb = character_field(chi) | ||
od = preimage(emb, K(od)) | ||
|
||
# Reduce the representative `od` mod obvious squares | ||
# in the character field. | ||
od = reduce_mod_squares(od) | ||
|
||
# Embed this value into the alg. closure. | ||
od = emb(od) | ||
|
||
# Turn the value into a string (using Atlas notation). | ||
str = atlas_description(od) | ||
else | ||
# Decide if the reduction mod `p` is a square in the char. field. | ||
str = is_square(reduce(K(od), character_field(chi)[1])) ? "O+" : "O-" | ||
end | ||
|
||
return true, str | ||
end | ||
|
||
return false, "" | ||
end | ||
|
||
@doc raw""" | ||
od_for_specht_module(chi::GAPGroupClassFunction) | ||
Return `(flag, val)` where `flag` is `true` if `chi` is an ordinary | ||
irreducible character of a symmetric group or of an alternating group | ||
such that `chi` extends to the corresponding symmetric group. | ||
In this case, if `chi` is orthogonally stable (this is not checked here) | ||
then `val` is a string that describes the orthogonal discriminant of `chi`; | ||
the discriminant is computed using the Jantzen-Schaper formula, | ||
via [`gram_determinant_specht_module`](@ref). | ||
`(false, "")` is returned in all cases where this criterion is not applicable. | ||
# Examples | ||
```jldoctest | ||
julia> t = character_table("A5"); | ||
julia> Oscar.OrthogonalDiscriminants.od_for_specht_module(t[4]) | ||
(true, "5") | ||
julia> Oscar.OrthogonalDiscriminants.od_for_specht_module(mod(t, 3)[4]) | ||
(false, "") | ||
``` | ||
""" | ||
function od_for_specht_module(chi::GAPGroupClassFunction) | ||
characteristic(chi) == 0 || return (false, "") | ||
|
||
# Find out to which alternating or symmetric group `chi` belongs. | ||
tbl = chi.table | ||
name = identifier(tbl) | ||
startswith(name, "A") || return (false, "") | ||
pos = findfirst('.', name) | ||
if pos == nothing | ||
n = parse(Int, name[2:end]) | ||
else | ||
n = parse(Int, name[2:(pos-1)]) | ||
end | ||
n == nothing && return (false, "") | ||
name == "A$n" || name == "A$n.2" || name == "A6.2_1" || return (false, "") | ||
|
||
chipos = findfirst(isequal(chi), tbl) | ||
chipos == nothing && return (false, "") | ||
para = character_parameters(tbl)[chipos] | ||
isa(para, Vector{Int}) || return (false, "") | ||
|
||
# Now we know that `chi` belongs to Sym(n) or extends to Sym(n) | ||
gramdet = gram_determinant_specht_module(partition(para)) | ||
res = ZZRingElem(1) | ||
for pair in gramdet | ||
if is_odd(pair[2]) | ||
res = res * pair[1] | ||
end | ||
end | ||
if mod(degree(ZZRingElem, chi), 4) == 2 | ||
res = - res | ||
end | ||
|
||
return true, string(res) | ||
end |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,98 @@ | ||||||
@doc raw""" | ||||||
order_omega_mod_N(d::IntegerUnion, q::IntegerUnion, N::IntegerUnion) -> Pair{Bool, Bool} | ||||||
Return `(flag_plus, flag_minus)` where `flag_plus` and `flag_minus` | ||||||
are `true` or `false`, depending on whether `N` divides the order | ||||||
of the orthogonal groups $\Omega^+(d, q)$ and $\Omega^-(d, q)$. | ||||||
# Examples | ||||||
```jldoctest | ||||||
julia> Oscar.OrthogonalDiscriminants.order_omega_mod_N(4, 2, 60) | ||||||
(false, true) | ||||||
julia> Oscar.OrthogonalDiscriminants.order_omega_mod_N(4, 5, 60) | ||||||
(true, true) | ||||||
``` | ||||||
""" | ||||||
function order_omega_mod_N(d::IntegerUnion, q::IntegerUnion, N::IntegerUnion) | ||||||
@req is_even(d) "d must be even" | ||||||
m = div(d, 2) | ||||||
exp, N = remove(N, q) | ||||||
facts = collect(factor(q)) | ||||||
p = facts[1][1] | ||||||
if mod(N, p) == 0 | ||||||
exp = exp + 1 | ||||||
fingolfin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
_, N = remove(N, p) | ||||||
end | ||||||
if m*(m-1) < exp | ||||||
# A group of order `N` does not embed in any candidate. | ||||||
return (false, false) | ||||||
end | ||||||
|
||||||
q2 = ZZ(q)^2 | ||||||
q2i = ZZ(1) | ||||||
for i in 1:(m-1) | ||||||
q2i = q2 * q2i | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if i == 1 && is_odd(q) | ||||||
g = gcd(N, div(q2i-1, 2)) | ||||||
else | ||||||
g = gcd(N, q2i-1) | ||||||
end | ||||||
N = div(N, g) | ||||||
if N == 1 | ||||||
# A group of order N may embed in both candidates. | ||||||
return (true, true) | ||||||
end | ||||||
end | ||||||
|
||||||
# embeds in + type?, embeds in - type? | ||||||
return (mod(q^m-1, N) == 0, mod(q^m+1, N) == 0) | ||||||
end | ||||||
|
||||||
|
||||||
@doc raw""" | ||||||
reduce_mod_squares(val::nf_elem) | ||||||
Return an element of `F = parent(val)` that is equal to `val` | ||||||
modulo squares in `F`. | ||||||
If `val` describes an integer then the result corresponds to the | ||||||
squarefree part of this integer. | ||||||
Otherwise the coefficients of the result have a squarefree g.c.d. | ||||||
# Examples | ||||||
```jldoctest | ||||||
julia> F, z = cyclotomic_field(4); | ||||||
julia> Oscar.OrthogonalDiscriminants.reduce_mod_squares(4*z^0) | ||||||
1 | ||||||
julia> Oscar.OrthogonalDiscriminants.reduce_mod_squares(-8*z^0) | ||||||
-2 | ||||||
``` | ||||||
""" | ||||||
function reduce_mod_squares(val::nf_elem) | ||||||
is_zero(val) && return val | ||||||
d = denominator(val) | ||||||
if ! isone(d) | ||||||
val = val * d^2 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to say that this syntax is preferable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my POV either is fine, this is a matter of taste. Pick whichever you prefer :-) |
||||||
end | ||||||
if is_integer(val) | ||||||
intval = ZZ(val) | ||||||
sgn = sign(intval) | ||||||
good = [x[1] for x in collect(factor(intval)) if is_odd(x[2])] | ||||||
F = parent(val) | ||||||
return F(prod(good, init = sgn)) | ||||||
end | ||||||
# Just get rid of the square part of the gcd of the coefficients. | ||||||
c = map(numerator, coefficients(val)) | ||||||
s = 1 | ||||||
for (p, e) in collect(factor(gcd(c))) | ||||||
if iseven(e) | ||||||
s = s * p^e | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
elseif e > 1 | ||||||
s = s * p^(e-1) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
end | ||||||
end | ||||||
return val//s | ||||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ using Oscar | |
using Test | ||
|
||
include("gram_det.jl") | ||
include("utils.jl") | ||
include("theoretical.jl") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
@testset "group order" begin | ||
d = 8 | ||
q = 3 | ||
order_oplus = order(omega_group(1, d, q)) | ||
order_ominus = order(omega_group(-1, d, q)) | ||
f = Oscar.OrthogonalDiscriminants.order_omega_mod_N | ||
@test f(d, q, order_oplus) == (true, false) | ||
@test f(d, q, order_ominus) == (false, true) | ||
@test f(d, q, order(omega_group(0, d-1, q))) == (true, true) | ||
@test f(d, q, q*order_oplus) == (false, false) | ||
@test f(d, q, (q-1)*order_oplus) == (false, false) | ||
|
||
@test_throws ArgumentError f(5, 2, 1) | ||
|
||
for entry in all_od_infos(comment_matches => "order") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_from_order(chi) == (true, entry[:valuestring]) | ||
end | ||
for entry in all_od_infos(identifier => "A8") | ||
if ! comment_matches(entry, "order") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_from_order(chi) == (false, "") | ||
end | ||
end | ||
end | ||
|
||
@testset "eigenvalues" begin | ||
for entry in all_od_infos(comment_matches => "ev") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_from_eigenvalues(chi) == (true, entry[:valuestring]) | ||
end | ||
for entry in all_od_infos(identifier => "A8") | ||
if ! comment_matches(entry, "ev") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_from_eigenvalues(chi) == (false, "") | ||
end | ||
end | ||
end | ||
|
||
@testset "Specht modules" begin | ||
for entry in all_od_infos(comment_matches => "specht") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_for_specht_module(chi) == (true, entry[:valuestring]) | ||
end | ||
for entry in all_od_infos(identifier => "A8") | ||
if ! comment_matches(entry, "specht") | ||
chi = Oscar.OrthogonalDiscriminants.character_of_entry(entry) | ||
@test Oscar.OrthogonalDiscriminants.od_for_specht_module(chi) == (false, "") | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.