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

add map_entries for matrix groups #3341

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/src/Groups/matgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ end
matrix_group(R::Ring, m::Int, V::AbstractVector{T}; check::Bool=true) where T<:Union{MatElem,MatrixGroupElem}
MatrixGroup{RE<:RingElem, T<:MatElem{RE}}
MatrixGroupElem{RE<:RingElem, T<:MatElem{RE}}
base_ring(G::MatrixGroup)
base_ring(G::MatrixGroup{RE}) where RE <: RingElem
degree(G::MatrixGroup)
centralizer(G::MatrixGroup{T}, x::MatrixGroupElem{T}) where T <: FinFieldElem
map_entries(f, G::MatrixGroup)
```

## Elements of matrix groups
Expand Down
55 changes: 48 additions & 7 deletions src/Groups/matrices/MatGrp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,7 @@ function Base.deepcopy_internal(x::MatrixGroupElem, dict::IdDict)
error("$x has neither :X nor :elm")
end

function change_base_ring(R::Ring, G::MatrixGroup)
g = dense_matrix_type(R)[]
for h in gens(G)
push!(g, map_entries(R, h.elm))
end
return matrix_group(g)
end
change_base_ring(R::Ring, G::MatrixGroup) = map_entries(R, G)

########################################################################
#
Expand Down Expand Up @@ -547,6 +541,53 @@ function order(::Type{T}, G::MatrixGroup) where T <: IntegerUnion
return T(res)::T
end

"""
map_entries(f, G::MatrixGroup)

Return the matrix group obtained by applying `f` element-wise to
each generator of `G`.

`f` can be a ring or a field, a suitable map, or a Julia function.

# Examples
```jldoctest
julia> mat = matrix(ZZ, 2, 2, [1, 1, 0, 1]);

julia> G = matrix_group(mat);

julia> G2 = map_entries(x -> -x, G)
Matrix group of degree 2
over integer ring

julia> is_finite(G2)
false

julia> order(map_entries(GF(3), G))
3
```
"""
function map_entries(f, G::MatrixGroup)
Ggens = gens(G)
if length(Ggens) == 0
z = f(zero(base_ring(G)))
return matrix_group(parent(z), degree(G), MatrixGroupElem[])
else
imgs = [map_entries(f, matrix(x)) for x in gens(G)]
return matrix_group(imgs)
end
end

function map_entries(R::Ring, G::MatrixGroup)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also change_base_ring for this case, which does the same. Maybe one should call the other?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one could just have one method of the form

map_entries(f, G::MatrixGroup)

without specifying the type of the first argument. This is how all other map_* methods usually are implemented.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joschmitt change_base_ring seems to only cover the case where the first argument is a ring, right? Of course we can make sure one is implemented in terms of the other. The current method is

function change_base_ring(R::Ring, G::MatrixGroup)
  g = dense_matrix_type(R)[]
  for h in gens(G)
    push!(g, map_entries(R, h.elm))
  end
  return matrix_group(g)
end

which does not deal with empty generator lists. We could just change it to

change_base_ring(R::Ring, G::MatrixGroup) = map_entries(R, G)

@thofma those other map_entries methods don't need to deal with an empty list of matrices, though... But yeah one could merge the methods though one could consider the two simple methods as "optimizations" of the generic Function one? Is there any harm in having these additional methods?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you are right. Maybe my suggestion is just to remove the restriction on the first argument and shuffle the docstring, so that it is not at the "wrong" method (at the moment the signature of the docstring and the function are not the same).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just change it to

change_base_ring(R::Ring, G::MatrixGroup) = map_entries(R, G)

Yes, exactly.

imgs = [map_entries(R, matrix(x)) for x in gens(G)]
return matrix_group(R, degree(G), imgs)
end

function map_entries(mp::Map, G::MatrixGroup)
imgs = [map_entries(mp, matrix(x)) for x in gens(G)]
return matrix_group(codomain(mp), degree(G), imgs)
end


########################################################################
#
# Constructors
Expand Down
32 changes: 32 additions & 0 deletions test/Groups/matrixgroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,38 @@ end
@test_throws ArgumentError matrix_group([x1,x3])
end

@testset "map_entries for matrix groups" begin
mat = matrix(ZZ, 2, 2, [1, 1, 0, 1])
G = matrix_group(mat)
T = trivial_subgroup(G)[1]
@test length(gens(T)) == 0
for R in [GF(2), GF(3, 2), residue_ring(ZZ, 6)[1]]
red = map_entries(R, G)
@test matrix(gen(red, 1)) == map_entries(R, mat)
red = map_entries(R, T)
@test matrix(one(red)) == map_entries(R, one(mat))
end

F = GF(2)
mp = MapFromFunc(ZZ, F, x -> F(x))
red = map_entries(mp, G)
@test red == map_entries(F, G)
red = map_entries(mp, T)
@test red == map_entries(F, T)

G1 = special_linear_group(2, 9)
G2 = map_entries(x -> x^3, G1)
@test gens(G1) != gens(G2)
@test G1 == G2
T = trivial_subgroup(G1)[1]
@test length(gens(T)) == 0
@test map_entries(x -> x^3, T) == trivial_subgroup(G2)[1]

mat = matrix(QQ, 2, 2, [2, 1, 0, 1])
G = matrix_group(mat)
@test_throws ArgumentError map_entries(GF(2), G)
end

@testset "Iterator" begin
G = SL(2,3)
N = 0
Expand Down
Loading