Skip to content
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

fixed QabElem, improved printing of char. tables #752

Merged
merged 2 commits into from Oct 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Groups/MatrixDisplay.jl
Expand Up @@ -190,16 +190,19 @@ function labelled_matrix_formatted(io::IO, mat::Matrix{String})
labels_row = get(io, :labels_row, fill("", m2, 0))

if length(size(labels_row)) == 1
# one column of row labels
labels_row = reshape(labels_row, length(labels_row), 1)
end
if length(size(labels_col)) == 1
# one row of column labels
labels_col = reshape(labels_col, 1, length(labels_col))
end

m1 = size(labels_col, 1)
n1 = size(labels_row, 2)
corner = get(io, :corner, fill("", m1, n1))
if length(size(corner)) == 1
# one column of labels above the row labels
corner = reshape(corner, length(corner), 1)
end

Expand Down
106 changes: 79 additions & 27 deletions src/Groups/group_characters.jl
Expand Up @@ -42,7 +42,8 @@ function QabElem(cyc::GapInt)
coeffs = Vector{fmpz}(coeffs)
coeffs = coeffs[1:dim]
denom = fmpz(denom)
F, z = Nemo.CyclotomicField(n)
FF = abelian_closure(QQ)[1]
F, z = Oscar.AbelianClosure.cyclotomic_field(FF, n)
val = Nemo.elem_from_mat_row(F, Nemo.matrix(Nemo.ZZ, 1, dim, coeffs), 1, denom)
return QabElem(val, n)
end
Expand All @@ -62,12 +63,12 @@ complex_conjugate(elm::QabElem) = elm^QabAutomorphism(-1)
abstract type GroupCharacterTable end

mutable struct GAPGroupCharacterTable <: GroupCharacterTable
GAPGroup::Oscar.GAPGroup # the underlying group, if any
GAPGroup::GAPGroup # the underlying group, if any
GAPTable::GAP.GapObj # the character table object
characteristic::Int
AbstractAlgebra.@declare_other

function GAPGroupCharacterTable(G::Oscar.GAPGroup, tab::GAP.GapObj, char::Int)
function GAPGroupCharacterTable(G::GAPGroup, tab::GAP.GapObj, char::Int)
ct = new()
ct.GAPGroup = G
ct.GAPTable = tab
Expand All @@ -85,7 +86,7 @@ mutable struct GAPGroupCharacterTable <: GroupCharacterTable
end

"""
character_table(G::Oscar.GAPGroup, p::Int = 0)
character_table(G::GAPGroup, p::Int = 0)

Return the ordinary (if `p == 0`) or `p`-modular character table of the
finite group `G`.
Expand Down Expand Up @@ -125,7 +126,7 @@ Sym( [ 1 .. 3 ] )

```
"""
function character_table(G::Oscar.GAPGroup, p::Int = 0)
function character_table(G::GAPGroup, p::Int = 0)
tbls = AbstractAlgebra.get_special(G, :character_tables)
if tbls == nothing
tbls = Dict()
Expand Down Expand Up @@ -232,14 +233,54 @@ function Base.iterate(wi::WordsIterator, state::Int)
end


# Utility:
# Turn integer values to strings, but replace `0` by `"."`,
# print irrationalities via the `Hecke.math_html` method for `nf_elem`.
# If `alphabet` is nonempty then represent irrationalities by names
# generated by iterating over `WordsIterator(alphabet)`,
# and store the meanings of these strings in the form of pairs `value => name`
# in the legend array that is returned.
function matrix_of_strings(tbl::GAPGroupCharacterTable; alphabet::String = "")
@doc Markdown.doc"""
as_sum_of_roots(val::nf_elem, root::String)

Return a string representing the element `val` of a cyclotomic field
as a sum of multiples of powers of the primitive root which is printed as
`root`.
"""
function as_sum_of_roots(val::nf_elem, root::String)
F = parent(val)
flag, N = Hecke.iscyclotomic_type(F)
flag || error("$val is not an element of a cyclotomic field")

# `string` yields an expression of the right structure,
# but not in terms of `root`
# and without curly brackets for subscripts and superscripts.
str = string(val)
str = replace(str, "*" => "")
str = replace(str, string(F.S) => root*"_{"*string(N)*"}")
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
str = replace(str, r"\^([0-9]*)" => s"^{\1}")
return str
end


@doc Markdown.doc"""
matrix_of_strings(tbl::GAPGroupCharacterTable; alphabet::String = "", root::String = "\\zeta")

Return `(mat, legend)` where `mat` is a matrix of strings that describe
the values of the irreducible characters of `tbl`,
and `legend` is a list of triples `(val, name, disp)` such that
`name` occurs in `mat`,
the character value `val` is represented by `name` in `mat`,
and `disp` is a string that describes `val`.

Integral character values are just turned into strings, except that `0` gets
replaced by `"."`.

If `alphabet` is empty then irrational values are written as sums of powers
of roots of unity,
where $\exp(2 \pi i/n)$ is shown as `root` followed by the index $n$.
Since all entries of the matrix are self-explanatory, `legend` is empty
in this case.

If `alphabet` is nonempty then irrational values are written as words in terms
of `alphabet`, and these words are the `name` entries in `legend`;
the corresponding `disp` entries are sums of powers of roots of unity
in terms of `root`.
"""
function matrix_of_strings(tbl::GAPGroupCharacterTable; alphabet::String = "", root::String = "\\zeta")
n = nrows(tbl)
m = Array{String}(undef, n, n)
legend = []
Expand All @@ -261,31 +302,44 @@ function matrix_of_strings(tbl::GAPGroupCharacterTable; alphabet::String = "")
elseif val.c == 1
m[i, j] = string(val.data)
elseif alphabet != ""
# write irrationalities using symbols
# write irrationalities using symbolic names
pos = findnext(x -> x[1] == val, legend, 1)
if pos != nothing
m[i,j] = legend[pos][2]
else
pos = findnext(x -> x[1] == -val, legend, 1)
if pos != nothing
# The negative of a known name is shown relative to that name.
m[i,j] = "-" * legend[pos][2]
else
name, state = iterate(iter, state)
push!(legend, val => name)
disp = as_sum_of_roots(val.data, root)
push!(legend, (val, name, disp))
valbar = complex_conjugate(val)
if valbar != val
push!(legend, valbar => "\\overline{"*name*"}")
#TODO: represent the unique Galois conjugate of `A` different from `A` by `A*`,
# and show both `A` and `A*` in the footer;
# for elements in quadratic fields, show also an expression in terms of
# The complex conjugate of a known name is shown relative to
# that name.
disp = as_sum_of_roots(valbar.data, root)
push!(legend, (valbar, "\\overline{"*name*"}", disp))
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
else
info = Oscar.AbelianClosure.quadratic_irrationality_info(val)
if info != nothing
# `val` generates a quadratic field extension.
# Show the unique Galois conjugate of `A` different from
# `A` as `A*`, and show both `A` and `A*` in the footer.
#TODO: For elements in quadratic fields, show also an expression in terms of
# square roots in the footer.
valstar = 2*info[1] - val
disp = as_sum_of_roots(valstar.data, root)
push!(legend, (valstar, name*"*", disp))
end
end
m[i,j] = name
end
end
else
# write irrationalities in terms of `\zeta`
m[i,j] = sprint(Hecke.math_html, val.data)
# write irrationalities in terms of `root`.
m[i,j] = as_sum_of_roots(val.data, root)
end
end
end
Expand All @@ -294,7 +348,7 @@ end

# Produce LaTeX output if `"text/html"` is prescribed,
# via the `:TeX` attribute of the io context.
function Base.show(io::IO, ::MIME"text/html", tbl::GAPGroupCharacterTable)
function Base.show(io::IO, ::MIME"text/latex", tbl::GAPGroupCharacterTable)
print(io, "\$")
show(IOContext(io, :TeX => true), tbl)
print(io, "\$")
Expand Down Expand Up @@ -383,9 +437,7 @@ function Base.show(io::IO, tbl::GAPGroupCharacterTable)

# footer (an array of strings)
:footer => length(legend) == 0 ? [] :
vcat([""],
[pair[2] * " = " * sprint(Hecke.math_html, pair[1].data)
for pair in legend]),
vcat([""], [triple[2]*" = "*triple[3] for triple in legend]),
)

# print the table
Expand Down Expand Up @@ -519,7 +571,7 @@ function group_class_function(tbl::GAPGroupCharacterTable, values::Vector{QabEle
return GAPGroupClassFunction(tbl, GAP.Globals.ClassFunction(tbl.GAPTable, gapvalues))
end

function group_class_function(G::Oscar.GAPGroup, values::Vector{QabElem})
function group_class_function(G::GAPGroup, values::Vector{QabElem})
return group_class_function(character_table(G), values)
end

Expand All @@ -528,7 +580,7 @@ function trivial_character(tbl::GAPGroupCharacterTable)
return group_class_function(tbl, [val for i in 1:ncols(tbl)])
end

function trivial_character(G::Oscar.GAPGroup)
function trivial_character(G::GAPGroup)
val = QabElem(1)
return group_class_function(G, [val for i in 1:GAP.Globals.NrConjugacyClasses(G.X)])
end
Expand Down
12 changes: 6 additions & 6 deletions test/Groups/group_characters.jl
Expand Up @@ -12,7 +12,7 @@
@test String(take!(io)) == "Alt( [ 1 .. 4 ] )\n\n 2 2 2 . .\n 3 1 . 1 1\n \n 1a 2a 3a 3b\n2P 1a 1a 3b 3a\n3P 1a 2a 1a 1a\n \nχ₁ 1 1 1 1\nχ₂ 1 1 -ζ₃ - 1 ζ₃\nχ₃ 1 1 ζ₃ -ζ₃ - 1\nχ₄ 3 -1 . .\n"

# LaTeX format
show(io, MIME("text/html"), t_a4)
show(io, MIME("text/latex"), t_a4)
@test String(take!(io)) == "\$Alt( [ 1 .. 4 ] )\n\n\\begin{array}{rrrrr}\n2 & 2 & 2 & . & . \\\\\n3 & 1 & . & 1 & 1 \\\\\n & & & & \\\\\n & 1a & 2a & 3a & 3b \\\\\n2P & 1a & 1a & 3b & 3a \\\\\n3P & 1a & 2a & 1a & 1a \\\\\n & & & & \\\\\n\\chi_{1} & 1 & 1 & 1 & 1 \\\\\n\\chi_{2} & 1 & 1 & -\\zeta_{3} - 1 & \\zeta_{3} \\\\\n\\chi_{3} & 1 & 1 & \\zeta_{3} & -\\zeta_{3} - 1 \\\\\n\\chi_{4} & 3 & -1 & . & . \\\\\n\\end{array}\n\$"

# show a legend of irrationalities instead of self-explanatory values,
Expand All @@ -21,12 +21,12 @@
@test String(take!(io)) == "Alt( [ 1 .. 4 ] )\n\n 2 2 2 . .\n 3 1 . 1 1\n \n 1a 2a 3a 3b\n2P 1a 1a 3b 3a\n3P 1a 2a 1a 1a\n \nχ₁ 1 1 1 1\nχ₂ 1 1 A A̅\nχ₃ 1 1 A̅ A\nχ₄ 3 -1 . .\n\nA = -ζ₃ - 1\nA̅ = ζ₃\n"

# ... and in LaTeX format
show(IOContext(io, :with_legend => true), MIME("text/html"), t_a4)
show(IOContext(io, :with_legend => true), MIME("text/latex"), t_a4)
@test String(take!(io)) == "\$Alt( [ 1 .. 4 ] )\n\n\\begin{array}{rrrrr}\n2 & 2 & 2 & . & . \\\\\n3 & 1 & . & 1 & 1 \\\\\n & & & & \\\\\n & 1a & 2a & 3a & 3b \\\\\n2P & 1a & 1a & 3b & 3a \\\\\n3P & 1a & 2a & 1a & 1a \\\\\n & & & & \\\\\n\\chi_{1} & 1 & 1 & 1 & 1 \\\\\n\\chi_{2} & 1 & 1 & A & \\overline{A} \\\\\n\\chi_{3} & 1 & 1 & \\overline{A} & A \\\\\n\\chi_{4} & 3 & -1 & . & . \\\\\n\\end{array}\n\nA = -\\zeta_{3} - 1\n\\overline{A} = \\zeta_{3}\n\$"

# show the screen format for a table with real and non-real irrationalities
show(IOContext(io, :with_legend => true), character_table("L2(11)"))
@test String(take!(io)) == "L2(11)\n\n 2 2 2 1 . . 1 . .\n 3 1 1 1 . . 1 . .\n 5 1 . . 1 1 . . .\n 11 1 . . . . . 1 1\n \n 1a 2a 3a 5a 5b 6a 11a 11b\n 2P 1a 1a 3a 5b 5a 3a 11b 11a\n 3P 1a 2a 1a 5b 5a 2a 11a 11b\n 5P 1a 2a 3a 1a 1a 6a 11a 11b\n11P 1a 2a 3a 5a 5b 6a 1a 1a\n \n χ₁ 1 1 1 1 1 1 1 1\n χ₂ 5 1 -1 . . 1 C \n χ₃ 5 1 -1 . . 1 C\n χ₄ 10 -2 1 . . 1 -1 -1\n χ₅ 10 2 1 . . -1 -1 -1\n χ₆ 11 -1 -1 1 1 -1 . .\n χ₇ 12 . . A B . 1 1\n χ₈ 12 . . B A . 1 1\n\nA = -ζ₅³ - ζ₅² - 1\nB = ζ₅³ + ζ₅²\nC = ζ₁₁⁹ + ζ₁₁⁵ + ζ₁₁⁴ + ζ₁₁³ + ζ₁₁\nC̅ = -ζ₁₁⁹ - ζ₁₁⁵ - ζ₁₁⁴ - ζ₁₁³ - ζ₁₁ - 1\n"
@test String(take!(io)) == "L2(11)\n\n 2 2 2 1 . . 1 . .\n 3 1 1 1 . . 1 . .\n 5 1 . . 1 1 . . .\n 11 1 . . . . . 1 1\n \n 1a 2a 3a 5a 5b 6a 11a 11b\n 2P 1a 1a 3a 5b 5a 3a 11b 11a\n 3P 1a 2a 1a 5b 5a 2a 11a 11b\n 5P 1a 2a 3a 1a 1a 6a 11a 11b\n11P 1a 2a 3a 5a 5b 6a 1a 1a\n \n χ₁ 1 1 1 1 1 1 1 1\n χ₂ 5 1 -1 . . 1 B \n χ₃ 5 1 -1 . . 1 B\n χ₄ 10 -2 1 . . 1 -1 -1\n χ₅ 10 2 1 . . -1 -1 -1\n χ₆ 11 -1 -1 1 1 -1 . .\n χ₇ 12 . . A A* . 1 1\n χ₈ 12 . . A* A . 1 1\n\nA = -ζ₅³ - ζ₅² - 1\nA* = ζ₅³ + ζ₅²\nB = ζ₁₁⁹ + ζ₁₁⁵ + ζ₁₁⁴ + ζ₁₁³ + ζ₁₁\nB̅ = -ζ₁₁⁹ - ζ₁₁⁵ - ζ₁₁⁴ - ζ₁₁³ - ζ₁₁ - 1\n"
fingolfin marked this conversation as resolved.
Show resolved Hide resolved

# show some separating lines, in the screen format ...
show(IOContext(io, :separators_col => [0,5],
Expand All @@ -36,7 +36,7 @@
# ... and in LaTeX format
show(IOContext(io, :separators_col => [0,5],
:separators_row => [0,5]),
MIME("text/html"), t_a5)
MIME("text/latex"), t_a5)
@test String(take!(io)) == "\$A5\n\n\\begin{array}{r|rrrrr|}\n2 & 2 & 2 & . & . & . \\\\\n3 & 1 & . & 1 & . & . \\\\\n5 & 1 & . & . & 1 & 1 \\\\\n & & & & & \\\\\n & 1a & 2a & 3a & 5a & 5b \\\\\n2P & 1a & 1a & 3a & 5b & 5a \\\\\n3P & 1a & 2a & 1a & 5b & 5a \\\\\n5P & 1a & 2a & 3a & 1a & 1a \\\\\n & & & & & \\\\\n\\hline\n\\chi_{1} & 1 & 1 & 1 & 1 & 1 \\\\\n\\chi_{2} & 3 & -1 & . & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 & -\\zeta_{5}^{3} - \\zeta_{5}^{2} \\\\\n\\chi_{3} & 3 & -1 & . & -\\zeta_{5}^{3} - \\zeta_{5}^{2} & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 \\\\\n\\chi_{4} & 4 & . & 1 & -1 & -1 \\\\\n\\chi_{5} & 5 & 1 & -1 & . & . \\\\\n\\hline\n\\end{array}\n\$"

# distribute the table into column portions, in the screen format ...
Expand All @@ -48,7 +48,7 @@
# ... and in LaTeX format
show(IOContext(io, :separators_col => [0],
:separators_row => [0],
:portions_col => [2,3]), MIME("text/html"), t_a5)
:portions_col => [2,3]), MIME("text/latex"), t_a5)
@test String(take!(io)) == "\$A5\n\n\\begin{array}{r|rr}\n2 & 2 & 2 \\\\\n3 & 1 & . \\\\\n5 & 1 & . \\\\\n & & \\\\\n & 1a & 2a \\\\\n2P & 1a & 1a \\\\\n3P & 1a & 2a \\\\\n5P & 1a & 2a \\\\\n & & \\\\\n\\hline\n\\chi_{1} & 1 & 1 \\\\\n\\chi_{2} & 3 & -1 \\\\\n\\chi_{3} & 3 & -1 \\\\\n\\chi_{4} & 4 & . \\\\\n\\chi_{5} & 5 & 1 \\\\\n\\end{array}\n\n\\begin{array}{r|rrr}\n2 & . & . & . \\\\\n3 & 1 & . & . \\\\\n5 & . & 1 & 1 \\\\\n & & & \\\\\n & 3a & 5a & 5b \\\\\n2P & 3a & 5b & 5a \\\\\n3P & 1a & 5b & 5a \\\\\n5P & 3a & 1a & 1a \\\\\n & & & \\\\\n\\hline\n\\chi_{1} & 1 & 1 & 1 \\\\\n\\chi_{2} & . & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 & -\\zeta_{5}^{3} - \\zeta_{5}^{2} \\\\\n\\chi_{3} & . & -\\zeta_{5}^{3} - \\zeta_{5}^{2} & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 \\\\\n\\chi_{4} & 1 & -1 & -1 \\\\\n\\chi_{5} & -1 & . & . \\\\\n\\end{array}\n\$"

# distribute the table into row portions,
Expand All @@ -61,7 +61,7 @@
# ... and in LaTeX format (may be interesting)
show(IOContext(io, :separators_col => [0],
:separators_row => [0],
:portions_row => [2,3]), MIME("text/html"), t_a5)
:portions_row => [2,3]), MIME("text/latex"), t_a5)
@test String(take!(io)) == "\$A5\n\n\\begin{array}{r|rrrrr}\n2 & 2 & 2 & . & . & . \\\\\n3 & 1 & . & 1 & . & . \\\\\n5 & 1 & . & . & 1 & 1 \\\\\n & & & & & \\\\\n & 1a & 2a & 3a & 5a & 5b \\\\\n2P & 1a & 1a & 3a & 5b & 5a \\\\\n3P & 1a & 2a & 1a & 5b & 5a \\\\\n5P & 1a & 2a & 3a & 1a & 1a \\\\\n & & & & & \\\\\n\\hline\n\\chi_{1} & 1 & 1 & 1 & 1 & 1 \\\\\n\\chi_{2} & 3 & -1 & . & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 & -\\zeta_{5}^{3} - \\zeta_{5}^{2} \\\\\n\\end{array}\n\n\\begin{array}{r|rrrrr}\n2 & 2 & 2 & . & . & . \\\\\n3 & 1 & . & 1 & . & . \\\\\n5 & 1 & . & . & 1 & 1 \\\\\n & & & & & \\\\\n & 1a & 2a & 3a & 5a & 5b \\\\\n2P & 1a & 1a & 3a & 5b & 5a \\\\\n3P & 1a & 2a & 1a & 5b & 5a \\\\\n5P & 1a & 2a & 3a & 1a & 1a \\\\\n & & & & & \\\\\n\\hline\n\\chi_{3} & 3 & -1 & . & -\\zeta_{5}^{3} - \\zeta_{5}^{2} & \\zeta_{5}^{3} + \\zeta_{5}^{2} + 1 \\\\\n\\chi_{4} & 4 & . & 1 & -1 & -1 \\\\\n\\chi_{5} & 5 & 1 & -1 & . & . \\\\\n\\end{array}\n\$"
end

Expand Down