Skip to content

Commit

Permalink
Make Cox rings of linear quotients work in greater generality (oscar-…
Browse files Browse the repository at this point in the history
  • Loading branch information
joschmitt committed Aug 23, 2023
1 parent ae19865 commit 346e640
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 48 deletions.
2 changes: 0 additions & 2 deletions experimental/LinearQuotients/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ with the spectrum of the invariant ring. Following the (more or less implicit)
convention in the invariant theory code, this means that we actually construct the
linear quotient $V^\ast/G$, that is, we work with the dual representation.
We should decide, whether this is as desired.
* The function `cox_ring(::LinearQuotient)` should work for arbitrary groups, not just
for groups not containing reflections
* The function `cox_ring_of_qq_factorial_terminalization(::LinearQuotient)` should work
for arbitrary subgroups of $\operatorname{SL}_n$, not just for groups generated by
junior elements
Expand Down
2 changes: 0 additions & 2 deletions experimental/LinearQuotients/docs/src/cox_rings.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ CurrentModule = Oscar
By a theorem of Arzhantsev and Gaifullin [AG10](@cite), the Cox ring of a linear
quotient $V/G$ is graded isomorphic to the invariant ring $K[V]^{[G,G]}$, where
$[G,G]$ is the derived subgroup of $G$.
This functionality is so far only available if the group does not contain any
reflections.
```@docs
cox_ring(L::LinearQuotient)
```
Expand Down
2 changes: 1 addition & 1 deletion experimental/LinearQuotients/docs/src/linear_quotients.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ linear_quotient(G::MatrixGroup)

## Class group

The divisor class group of a linear quotient $V/G$ is controlled by the reflections
The divisor class group of a linear quotient $V/G$ is controlled by the pseudo-reflections
contained in the group $G$, see [Ben93](@cite).
```@docs
class_group(L::LinearQuotient)
Expand Down
48 changes: 28 additions & 20 deletions experimental/LinearQuotients/src/cox_rings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,27 +195,31 @@ end
#
################################################################################

# Let G < GL(V) be a finite group and GtoAbG the map G -> Ab(G) = G/[G, G].
# The invariant ring K[V]^{[G,G]} has a well-defined action by Ab(G) and is hence
# graded by the characters of Ab(G). The group of irreducible characters of the
# abelian group Ab(G) is isomorphic to Ab(G), so we assume that K[V]^{[G, G]} is
# graded by Ab(G). To choose a canonical isomorphism, we require a root of unity
# of order at least exponent(Ab(G)). This is the input zeta: it is the root of
# unity and its order (this is in the context of fixed_root_of_unity(::LinearQuotient)).
# Return the degree of the polynomial f as an element of codomain(GtoAbG),
# Let G < GL(V) be a finite group and GtoA a surjective map G -> A to an abelian
# group A. Let H be the kernel of GtoA. The invariant ring K[V]^H has a
# well-defined action by A and is hence graded by the characters of A. The group
# of irreducible characters of the abelian group A is isomorphic to A, so we
# assume that K[V]^H is graded by A. To choose a canonical isomorphism, we
# require a root of unity of order at least exponent(A). This is the input zeta:
# it is the root of unity and its order (this is in the context of
# fixed_root_of_unity(::LinearQuotient)).
# This is intended to be used in the situation, where f is an element of the Cox
# ring of the linear quotient V/G and GtoA is the map from G to the class group
# of V/G.
# Return the degree of the polynomial f as an element of codomain(GtoA),
# assuming that f is homogeneous.
function ab_g_degree(GtoAbG::Map, f::MPolyRingElem, zeta::Tuple{<:FieldElem, Int})
AbG = codomain(GtoAbG)
function ab_g_degree(GtoA::Map, f::MPolyRingElem, zeta::Tuple{<:FieldElem, Int})
A = codomain(GtoA)
K = coefficient_ring(parent(f))
@assert K === parent(zeta[1])

powers_of_zeta = _powers_of_root_of_unity(zeta...)
l = zeta[2]

c = zeros(ZZRingElem, ngens(AbG))
eldivs = elementary_divisors(AbG)
for i in 1:ngens(AbG)
fi = right_action(f, GtoAbG\AbG[i])
c = zeros(ZZRingElem, ngens(A))
eldivs = elementary_divisors(A)
for i in 1:ngens(A)
fi = right_action(f, GtoA\A[i])
q, r = divrem(fi, f)
@assert is_zero(r) "Polynomial is not homogeneous"
z = first(AbstractAlgebra.coefficients(q))
Expand All @@ -224,7 +228,7 @@ function ab_g_degree(GtoAbG::Map, f::MPolyRingElem, zeta::Tuple{<:FieldElem, Int
@assert is_integral(k)
c[i] = numerator(k)
end
return AbG(c)
return A(c)
end

@doc raw"""
Expand All @@ -233,17 +237,21 @@ end
Return the Cox ring of the linear quotient `L` in a presentation as a graded affine
algebra (`MPolyQuoRing`) and an injective map from this ring into a polynomial ring.
By a theorem of Arzhantsev--Gaifullin [AG10](@cite) the Cox ring is graded isomorphic
to the invariant ring of the derived subgroup of `group(L)`.
Let `G = group(L)` and let `H` be the subgroup generated by the pseudo-reflections
contained in `G`. By a theorem of Arzhantsev--Gaifullin [AG10](@cite), the Cox
ring is graded isomorphic to the invariant ring of the group `H[G,G]`, where
`[G,G]` is the derived subgroup of `G`.
We use ideas from [DK17](@cite) to find homogeneous generators of the invariant ring.
To get a map from `group(G)` to the grading group of the returned ring, use
[`class_group`](@ref).
"""
function cox_ring(L::LinearQuotient; algo_gens::Symbol = :default, algo_rels::Symbol = :groebner_basis)
G = group(L)
is_small_group(G) || error("Only implemented for groups not containing reflections")

Grefl, GrefltoG = subgroup_of_pseudo_reflections(G)
H, HtoG = derived_subgroup(G)
# Compute H*Grefl
H = matrix_group(base_ring(G), degree(G),
vcat(map(HtoG, gens(H)), map(GrefltoG, gens(Grefl))))
A, GtoA = class_group(L)

RH = invariant_ring(H)
Expand Down Expand Up @@ -300,7 +308,7 @@ function cox_ring(L::LinearQuotient; algo_gens::Symbol = :default, algo_rels::Sy
T = grade(T, degrees)[1]

# The relations are in general not homogeneous
relsT = reduce(vcat, [ collect(values(homogeneous_components(T(f)))) for f in relsT ])
relsT = reduce(vcat, [ collect(values(homogeneous_components(T(f)))) for f in relsT ], init = elem_type(T)[])
Q, TtoQ = quo(T, ideal(T, relsT))
QtoR = hom(Q, Rgraded, [ Rgraded(f) for f in hom_invars ])

Expand Down
18 changes: 14 additions & 4 deletions experimental/LinearQuotients/src/linear_quotients.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,19 @@ function fixed_root_of_unity(L::LinearQuotient)
K isa AnticNumberField || throw(Hecke.NotImplemented())
fl, l = Hecke.is_cyclotomic_type(K)
fl || throw(Hecke.NotImplemented())
fl, q = divides(l, e)
if is_odd(l)
ll = 2l
else
ll = l
end
fl, q = divides(ll, e)
fl || error("$(base_ring(L)) does not contain a $(e)-th root of unity")
L.root_of_unity = (gen(K)^q, e)
if is_odd(l)
zeta = (-gen(K))^q
else
zeta = gen(K)^q
end
L.root_of_unity = (zeta, e)
end
return L.root_of_unity
end
Expand All @@ -57,15 +67,15 @@ Return the class group of the linear quotient `L` and a map from `group(L)` to
this group.
If `G = group(L)`, then the class group is `Ab(G/H)`, where `H` is the subgroup
of `G` generated by the reflections.
of `G` generated by the pseudo-reflections.
"""
function class_group(L::LinearQuotient)
if isdefined(L, :class_group)
return L.class_group
end

G = group(L)
H = subgroup_of_reflections(G)
H, _ = subgroup_of_pseudo_reflections(G)
if !is_trivial(H)
K, GtoK = quo(G, H)
else
Expand Down
44 changes: 25 additions & 19 deletions experimental/LinearQuotients/src/misc.jl
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
# Try to 'update' the base_ring of G
function map_entries(K::Field, G::MatrixGroup)
g = dense_matrix_type(K)[]
for h in gens(G)
push!(g, map_entries(K, h.elm))
end
return matrix_group(g)
end
@doc raw"""
is_pseudo_reflection(g::MatrixGroupElem)
function is_reflection(g::MatrixGroupElem)
Return `true` if `g` is a pseudo-reflection, `false` otherwise. By a
pseudo-reflection, we mean a matrix with fixed space of codimension 1.
"""
function is_pseudo_reflection(g::MatrixGroupElem)
return rank(g.elm - one(parent(g)).elm) == 1
end

function subgroup_of_reflections(G::MatrixGroup)
@doc raw"""
subgroup_of_pseudo_reflections(G::MatrixGroup)
Return the subgroup `H` of `G` generated by the pseudo-reflections in `G` and an
embedding `H \to G`.
See also [`is_pseudo_reflection`](@ref).
"""
function subgroup_of_pseudo_reflections(G::MatrixGroup)
g = elem_type(G)[]
for c in conjugacy_classes(G)
if is_reflection(representative(c))
if is_pseudo_reflection(representative(c))
append!(g, collect(c))
end
end
return matrix_group(base_ring(G), degree(G), g)
return sub(G, g, check = false)
end

# Check if G contains reflections
@doc raw"""
is_small_group(G::MatrixGroup)
Return `true` if `G` does not contain any pseudo-reflections and `false` otherwise.
See also [`is_pseudo_reflection`](@ref).
"""
function is_small_group(G::MatrixGroup)
return !any(c -> is_reflection(representative(c)), conjugacy_classes(G))
return !any(c -> is_pseudo_reflection(representative(c)), conjugacy_classes(G))
end

# Somehow one needs to look up powers of a given root of unity quite often...
Expand Down Expand Up @@ -151,8 +162,3 @@ function homogenize_at_last_variable(I::MPolyIdeal, S::MPolyDecRing)
end
return SptoS(ideal(Sp, res))
end

function ideal(S::MPolyDecRing, I::MPolyIdeal)
@assert base_ring(I) === forget_grading(S)
return ideal(S, [ S(f) for f in gens(I) ])
end
16 changes: 16 additions & 0 deletions experimental/LinearQuotients/test/cox_rings-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@
for i = 1:ngens(R)
@test Oscar.ab_g_degree(GtoA, RtoS(gen(R, i)), Oscar.fixed_root_of_unity(L)) == degree(gen(R, i))
end

# An example containing reflections
K, a = cyclotomic_field(6, "a")
g1 = matrix(K, 3, 3, [ -1 0 0; 0 1 0; 0 0 1 ])
g2 = matrix(K, 3, 3, [ 0 0 1; 1 0 0; 0 1 0 ])
G = matrix_group(g1, g2)
L = linear_quotient(G)
R, RtoS = cox_ring(L)
@test ngens(R) == 3
@test grading_group(R).snf == ZZRingElem[ 3 ]
A, GtoA = class_group(L)
@test A === grading_group(R)
for i = 1:ngens(R)
@test Oscar.ab_g_degree(GtoA, RtoS(gen(R, i)), Oscar.fixed_root_of_unity(L)) == degree(gen(R, i))
end
@test is_zero(modulus(R))
end

@testset "Cox rings of QQ-factorial terminalizations" begin
Expand Down
12 changes: 12 additions & 0 deletions experimental/LinearQuotients/test/linear_quotients-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,16 @@
@test val(x[2]) == 1
@test val(x[1] + x[2]) == 4
@test val(x[1] - x[2]) == 1

# An example containing pseudo-reflections
K, a = cyclotomic_field(6, "a")
g1 = matrix(K, 3, 3, [ -1 0 0; 0 1 0; 0 0 1 ])
g2 = matrix(K, 3, 3, [ 0 0 1; 1 0 0; 0 1 0 ])
G = matrix_group(g1, g2)
L = linear_quotient(G)
A, GtoA = class_group(L)
@test is_snf(A)
@test A.snf == ZZRingElem[ 3 ]
@test is_zero(GtoA(G(g1)))
@test GtoA(G(g2)) == A([ 1 ])
end
7 changes: 7 additions & 0 deletions src/Groups/matrices/MatGrp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ 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

########################################################################
#
Expand Down
6 changes: 6 additions & 0 deletions src/Rings/mpoly-ideals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ function ideal(g::Vector{T}) where {T <: MPolyRingElem}
return ideal(parent(g[1]), g)
end

# Coerce an ungraded ideal in a graded ring
function ideal(S::MPolyDecRing, I::MPolyIdeal)
@req base_ring(I) === forget_grading(S) "Rings do not coincide"
return ideal(S, [ S(f) for f in gens(I) ])
end

function is_graded(I::MPolyIdeal)
return is_graded(Hecke.ring(I))
end
Expand Down
5 changes: 5 additions & 0 deletions test/Groups/matrixgroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ end
@testset for x in gens(G)
@test iseven(rank(matrix(x)-1))
end

G = matrix_group(matrix(QQ, 2, 2, [ -1 0 ; 0 -1 ]))
K, _ = cyclotomic_field(4)
H = change_base_ring(K, G)
@test H == matrix_group(matrix(K, 2, 2, [ -1 0 ; 0 -1 ]))
end


Expand Down
4 changes: 4 additions & 0 deletions test/Rings/mpoly-graded.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ end
I = ideal(S, [ f ])
@test forget_decoration(I) == ideal(R, [ x + y ])
@test forget_grading(I) == ideal(R, [ x + y ])
@test ideal(S, forget_decoration(I)) == I

T, _ = graded_polynomial_ring(QQ, [ "t" ], [ 1 ])
@test_throws ArgumentError ideal(T, forget_decoration(I))
end

@testset "Verify homogenization bugfix" begin
Expand Down

0 comments on commit 346e640

Please sign in to comment.