From 45c2036d4c256a54be5740302fca5e46e967ccbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 18 Dec 2023 15:55:56 +0100 Subject: [PATCH 01/17] Make `exterior_power(::ModuleFP)` slightly more type stable --- src/Modules/ExteriorPowers/FreeModules.jl | 18 +++++++++--------- src/Modules/ExteriorPowers/Generic.jl | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Modules/ExteriorPowers/FreeModules.jl b/src/Modules/ExteriorPowers/FreeModules.jl index a40481dc0f8..add7ca694a5 100644 --- a/src/Modules/ExteriorPowers/FreeModules.jl +++ b/src/Modules/ExteriorPowers/FreeModules.jl @@ -9,7 +9,7 @@ # User facing constructor for ⋀ ᵖ F. function exterior_power(F::FreeMod, p::Int; cached::Bool=true) - (p < 0 || p > rank(F)) && error("index out of bounds") + @req 0 <= p <= rank(F) "Exponent out of bounds" if cached powers = _exterior_powers(F) @@ -18,7 +18,7 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) R = base_ring(F) n = rank(F) - result = FreeMod(R, binomial(n, p)) + result = free_module(R, binomial(n, p)) # In case F was graded, we have to take an extra detour. if is_graded(F) @@ -33,18 +33,18 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) # Create the multiplication map function my_mult(u::FreeModElem...) isempty(u) && return result[1] # only the case p=0 - @assert all(x->parent(x)===F, u) "elements must live in the same module" - @assert length(u) == p "need a $p-tuple of elements" - return wedge(collect(u), parent=result) + @req all(x -> parent(x) === F, u) "elements must live in the same module" + @req length(u) == p "need a $p-tuple of elements" + return wedge(collect(u); parent=result) end function my_mult(u::Tuple) return my_mult(u...) end function my_decomp(u::FreeModElem) - parent(u) === result || error("element does not belong to the correct module") - k = findfirst(x->x==u, gens(result)) - k === nothing && error("element must be a generator of the module") + @req parent(u) === result "element does not belong to the correct module" + k = findfirst(x -> x == u, gens(result)) + @req !isnothing(k) "element must be a generator of the module" ind = ordered_multi_index(k, p, n) e = gens(F) return Tuple(e[i] for i in indices(ind)) @@ -77,7 +77,7 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) end end result.S = new_symb - + set_attribute!(result, :show => show_exterior_product) return result, mult_map diff --git a/src/Modules/ExteriorPowers/Generic.jl b/src/Modules/ExteriorPowers/Generic.jl index 40f43cec914..3a5dca9a635 100644 --- a/src/Modules/ExteriorPowers/Generic.jl +++ b/src/Modules/ExteriorPowers/Generic.jl @@ -1,6 +1,6 @@ # We need to cache eventually created exterior powers. -@attr Dict{Int, Tuple{T, <:Map}} function _exterior_powers(F::T) where {T<:ModuleFP} - return Dict{Int, Tuple{typeof(F), Map}}() +@attr Dict{Int, Tuple{typeof(F), MapFromFunc}} function _exterior_powers(F::ModuleFP) + return Dict{Int, Tuple{typeof(F), MapFromFunc}}() end # User facing method to ask whether F = ⋀ ᵖ M for some M. @@ -8,7 +8,7 @@ end # and `(false, F, 0)` otherwise. function is_exterior_power(M::ModuleFP) if has_attribute(M, :is_exterior_power) - MM, p = get_attribute(M, :is_exterior_power) + MM, p = get_attribute(M, :is_exterior_power)::Tuple{typeof(M), Int} return (true, MM, p) end return (false, M, 0) From e683bb6bfed2e377f15f711093d088648c9efb8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 11:49:00 +0100 Subject: [PATCH 02/17] Make exterior powers of Lie modules compatible with ModuleeFP d --- .../LieAlgebras/src/LieAlgebraModule.jl | 110 ++++++++++++------ .../LieAlgebras/src/LieAlgebraModuleHom.jl | 17 ++- .../LieAlgebras/test/LieAlgebraModule-test.jl | 41 +++---- .../test/LieAlgebraModuleHom-test.jl | 29 ++++- 4 files changed, 128 insertions(+), 69 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 75b6fca075b..402f1ff4e9d 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -153,12 +153,12 @@ end function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) io = pretty(io) - println(io, _module_type_to_string(get_attribute(V, :type, :unknown))) + println(io, _module_type_to_string(get_attribute(V, :type, :unknown), V)) println(io, Indent(), "of dimension $(dim(V))") if is_dual(V) || is_direct_sum(V) || is_tensor_product(V) || - is_exterior_power(V) || + is_exterior_power(V)[1] || is_symmetric_power(V) || is_tensor_power(V) _show_inner(io, V) @@ -191,10 +191,10 @@ function _show_inner(io::IO, V::LieAlgebraModule) _show_inner(io, W) end print(io, Dedent()) - elseif type == :exterior_power - println(io, "$(ordinal_number_string(get_attribute(V, :power))) exterior power of") + elseif ((fl, W, k) = is_exterior_power(V); fl) + println(io, "$(ordinal_number_string(k)) exterior power of") print(io, Indent()) - _show_inner(io, base_module(V)) + _show_inner(io, W) print(io, Dedent()) elseif type == :symmetric_power println(io, "$(ordinal_number_string(get_attribute(V, :power))) symmetric power of") @@ -213,12 +213,12 @@ end function Base.show(io::IO, V::LieAlgebraModule) if get(io, :supercompact, false) - print(io, _module_type_to_string(get_attribute(V, :type, :unknown))) + print(io, _module_type_to_string(get_attribute(V, :type, :unknown), V)) else io = pretty(io) print( io, - _module_type_to_string(get_attribute(V, :type, :unknown)), + _module_type_to_string(get_attribute(V, :type, :unknown), V), " of dimension $(dim(V)) over ", Lowercase(), ) @@ -226,7 +226,7 @@ function Base.show(io::IO, V::LieAlgebraModule) end end -function _module_type_to_string(type::Symbol) +function _module_type_to_string(type::Symbol, V::LieAlgebraModule) # TODO: remove first argument if type == :standard_module return "Standard module" elseif type == :dual @@ -235,7 +235,7 @@ function _module_type_to_string(type::Symbol) return "Direct sum module" elseif type == :tensor_product return "Tensor product module" - elseif type == :exterior_power + elseif is_exterior_power(V)[1] return "Exterior power module" elseif type == :symmetric_power return "Symmetric power module" @@ -359,9 +359,9 @@ function (V::LieAlgebraModule{C})( elseif is_tensor_product(V) pure = get_attribute(V, :tensor_pure_function) return pure(a)::LieAlgebraModuleElem{C} - elseif is_exterior_power(V) - pure = get_attribute(V, :exterior_pure_function) - return pure(a)::LieAlgebraModuleElem{C} + elseif is_exterior_power(V)[1] + pure = get_attribute(V, :wedge_pure_function) + return pure(Tuple(a))::LieAlgebraModuleElem{C} elseif is_symmetric_power(V) pure = get_attribute(V, :symmetric_pure_function) return pure(a)::LieAlgebraModuleElem{C} @@ -376,7 +376,16 @@ end function (V::LieAlgebraModule{C})( a::Tuple{T,Vararg{T}} ) where {T<:LieAlgebraModuleElem{C}} where {C<:FieldElem} - return V(collect(a)) + if is_exterior_power(V)[1] + pure = get_attribute(V, :wedge_pure_function) + return pure(a)::LieAlgebraModuleElem{C} + else + throw(ArgumentError("Invalid input.")) + end +end + +function (V::LieAlgebraModule{C})(a::LieAlgebraModuleElem{C}...) where {C<:FieldElem} + return V(a) end function (V::LieAlgebraModule{C})(_::Tuple{}) where {C<:FieldElem} @@ -538,12 +547,18 @@ function is_tensor_product(V::LieAlgebraModule) end @doc raw""" - is_exterior_power(V::LieAlgebraModule{C}) -> Bool + is_exterior_power(V::LieAlgebraModule{C}) -> Bool, LieAlgebraModule{C}, Int Check whether `V` has been constructed as an exterior power of a module. +If it has, return `true`, the base module, and the power. +If not, return `false` as the first return value, and arbitrary values for the other two. """ function is_exterior_power(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :exterior_power + if has_attribute(V, :is_exterior_power) + W, k = get_attribute(V, :is_exterior_power)::Tuple{typeof(V),Int} + return (true, W, k) + end + return (false, V, 0) end @doc raw""" @@ -569,8 +584,8 @@ end Returns the base module of `V`, if `V` has been constructed as a power module. """ -function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} - @req is_dual(V) || is_exterior_power(V) || is_symmetric_power(V) || is_tensor_power(V) "Not a power module." +function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: deprecate + @req is_dual(V) || is_symmetric_power(V) || is_tensor_power(V) "Not a power module." return get_attribute(V, :base_module)::LieAlgebraModule{C} end @@ -729,7 +744,7 @@ Construct the dual module of `V`. ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V = exterior_power(standard_module(L), 2); # some module +julia> V = exterior_power(standard_module(L), 2)[1]; # some module julia> dual(V) Dual module @@ -771,7 +786,7 @@ Construct the direct sum of the modules `V...`. ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V1 = exterior_power(standard_module(L), 2); # some module +julia> V1 = exterior_power(standard_module(L), 2)[1]; # some module julia> V2 = symmetric_power(standard_module(L), 3); # some module @@ -827,7 +842,7 @@ construct their tensor product $V_1 \otimes \cdots \otimes \V_n$. ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V1 = exterior_power(standard_module(L), 2); # some module +julia> V1 = exterior_power(standard_module(L), 2)[1]; # some module julia> V2 = symmetric_power(standard_module(L), 3); # some module @@ -926,7 +941,8 @@ end @doc raw""" exterior_power(V::LieAlgebraModule{C}, k::Int) -> LieAlgebraModule{C} -Construct the `k`-th exterior power $\bigwedge^k (V)$ of the module `V`. +Construct the `k`-th exterior power $\bigwedge^k (V)$ of the module `V`, +together with a map that computes the exterior product of `k` elements of `V`. # Examples ```jldoctest @@ -934,7 +950,10 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V = symmetric_power(standard_module(L), 3); # some module -julia> exterior_power(V, 2) +julia> E, map = exterior_power(V, 2) +(Exterior power module of dimension 45 over sl_3, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> exterior power module) + +julia> E Exterior power module of dimension 45 2nd exterior power of @@ -943,8 +962,16 @@ Exterior power module over special linear Lie algebra of degree 3 over QQ ``` """ -function exterior_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} +function exterior_power( + V::LieAlgebraModule{C}, k::Int; cached::Bool=true +) where {C<:FieldElem} @req k >= 0 "Non-negative exponent needed" + + if cached + powers = _exterior_powers(V) + haskey(powers, k) && return powers[k] + end + L = base_lie_algebra(V) R = coefficient_ring(V) dim_E = binomial(dim(V), k) @@ -977,7 +1004,7 @@ function exterior_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} E_to_T = hom(E, T, E_to_T_mat; check=false) T_to_E = hom(T, E, T_to_E_mat; check=false) - function pure(as::LieAlgebraModuleElem{C}...) + function my_mult(as::LieAlgebraModuleElem{C}...) @req length(as) == k "Length of vector does not match." @req all(a -> parent(a) === V, as) "Incompatible modules." mat = zero_matrix(R, 1, dim_E) @@ -987,14 +1014,11 @@ function exterior_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} end return E(mat) end - function pure(as::Tuple) - return pure(as...)::LieAlgebraModuleElem{C} - end - function pure(as::Vector{LieAlgebraModuleElem{C}}) - return pure(as...)::LieAlgebraModuleElem{C} + function my_mult(as::Tuple) + return my_mult(as...)::LieAlgebraModuleElem{C} end - function inv_pure(a::LieAlgebraModuleElem{C}) + function my_decomp(a::LieAlgebraModuleElem{C}) @req parent(a) === E "Incompatible modules." if iszero(a) return Tuple(zero(V) for _ in 1:k) @@ -1006,18 +1030,28 @@ function exterior_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} return Tuple(basis(V, i) for i in inds) end + mult_map = MapFromFunc( + Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), E, my_mult, my_decomp + ) + inv_mult_map = MapFromFunc(E, domain(mult_map), my_decomp, my_mult) + set_attribute!( E, - :type => :exterior_power, - :power => k, - :base_module => V, - :exterior_pure_function => pure, - :exterior_pure_preimage_function => inv_pure, + :is_exterior_power => (V, k), + :wedge_pure_function => mult_map, + :wedge_generator_decompose_function => inv_mult_map, :embedding_tensor_power => T, :embedding_tensor_power_embedding => E_to_T, :embedding_tensor_power_projection => T_to_E, ) - return E + + cached && (_exterior_powers(V)[k] = (E, mult_map)) + + return E, mult_map +end + +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _exterior_powers(V::LieAlgebraModule) + return Dict{Int,Tuple{typeof(V),MapFromFunc}}() end @doc raw""" @@ -1029,7 +1063,7 @@ Construct the `k`-th symmetric power $S^k (V)$ of the module `V`. ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V = exterior_power(standard_module(L), 2); # some module +julia> V = exterior_power(standard_module(L), 2)[1]; # some module julia> symmetric_power(V, 3) Symmetric power module @@ -1147,7 +1181,7 @@ Construct the `k`-th tensor power $T^k (V)$ of the module `V`. ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V = exterior_power(standard_module(L), 2); # some module +julia> V = exterior_power(standard_module(L), 2)[1]; # some module julia> tensor_power(V, 3) Tensor power module diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index 67fcc7f19fe..a904fde4676 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -502,9 +502,13 @@ $S^k h: V \to W$ (analogous for other types of powers). function hom_power( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom ) where {C<:FieldElem} - if is_exterior_power(V) - @req is_exterior_power(W) "First module is an exterior power, but second module is not" + if ((fl, Vb, Vk) = is_exterior_power(V); fl) + (fl, Wb, Wk) = is_exterior_power(W) + @req fl "First module is an exterior power, but second module is not" + @req Vk == Wk "Exponent mismatch" + @req domain(h) === Vb && codomain(h) === Wb "Domain/codomain mismatch" type = :ext + power = Vk elseif is_symmetric_power(V) @req is_symmetric_power(W) "First module is a symmetric power, but second module is not" type = :sym @@ -514,15 +518,18 @@ function hom_power( else throw(ArgumentError("First module must be a power module")) end - @req get_attribute(V, :power) == get_attribute(W, :power) "Exponent mismatch" - @req domain(h) === base_module(V) && codomain(h) === base_module(W) "Domain/codomain mismatch" + if !is_exterior_power(V)[1] + @req get_attribute(V, :power) == get_attribute(W, :power) "Exponent mismatch" + @req domain(h) === base_module(V) && codomain(h) === base_module(W) "Domain/codomain mismatch" + power = get_attribute(V, :power) + end TV = type == :tensor ? V : get_attribute(V, :embedding_tensor_power) TW = type == :tensor ? W : get_attribute(W, :embedding_tensor_power) mat = reduce( kronecker_product, - [matrix(h) for _ in 1:get_attribute(V, :power)]; + [matrix(h) for _ in 1:power]; init=identity_matrix(coefficient_ring(W), 1), ) TV_to_TW = hom(TV, TW, mat; check=false) diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 19079f1cbd0..df3cf111b1b 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -65,25 +65,25 @@ @testset "⋀^2 S^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = exterior_power(symmetric_power(standard_module(L), 2), 2) + V = exterior_power(symmetric_power(standard_module(L), 2), 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "⋀^2 T^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = exterior_power(tensor_power(standard_module(L), 2), 2) + V = exterior_power(tensor_power(standard_module(L), 2), 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "⋀^2 V of so_4(CF(4))" begin L = special_orthogonal_lie_algebra(cyclotomic_field(4)[1], 4) - V = exterior_power(standard_module(L), 3) + V = exterior_power(standard_module(L), 3)[1] lie_algebra_module_conformance_test(L, V) end @testset "S^2 ⋀^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = symmetric_power(exterior_power(standard_module(L), 2), 2) + V = symmetric_power(exterior_power(standard_module(L), 2)[1], 2) lie_algebra_module_conformance_test(L, V) end @@ -101,7 +101,7 @@ @testset "T^2 ⋀^2 V of sl_4(QQ)" begin L = special_linear_lie_algebra(QQ, 4) - V = tensor_power(exterior_power(standard_module(L), 2), 2) + V = tensor_power(exterior_power(standard_module(L), 2)[1], 2) lie_algebra_module_conformance_test(L, V) end @@ -123,7 +123,7 @@ is_dual(V), is_direct_sum(V), is_tensor_product(V), - is_exterior_power(V), + is_exterior_power(V)[1], is_symmetric_power(V), is_tensor_power(V), ) @@ -187,7 +187,7 @@ end V1 = symmetric_power(standard_module(L), 2) - V2 = exterior_power(standard_module(L), 2) + V2 = exterior_power(standard_module(L), 2)[1] type_V1 = module_type_bools(V1) type_V2 = module_type_bools(V2) @@ -228,7 +228,7 @@ end V1 = symmetric_power(standard_module(L), 2) - V2 = exterior_power(standard_module(L), 2) + V2 = exterior_power(standard_module(L), 2)[1] type_V1 = module_type_bools(V1) type_V2 = module_type_bools(V2) @@ -253,9 +253,9 @@ type_V = module_type_bools(V) for k in 1:3 - E = exterior_power(V, k) + E = exterior_power(V, k)[1] @test type_V == module_type_bools(V) # construction of pow_V should not change type of V - @test base_module(E) === V + @test is_exterior_power(E) === (true, V, k) @test dim(E) == binomial(dim(V), k) @test length(repr(E)) < 10^4 # outputs tend to be excessively long due to recursion @@ -264,14 +264,15 @@ if k == 1 x = L(rand(-10:10, dim(L))) a = V(rand(-10:10, dim(V))) + # @test E(x * a) == x * E(a) # TODO: fix this @test E([x * a]) == x * E([a]) elseif k == 2 a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) - @test !iszero(E([a, b])) - @test iszero(E([a, b]) + E([b, a])) - @test !iszero(E([a, b]) - E([b, a])) - @test iszero(E([a, a])) + @test !iszero(E(a, b)) + @test iszero(E(a, b) + E(b, a)) + @test !iszero(E(a, b) - E(b, a)) + @test iszero(E(a, a)) end T = get_attribute(E, :embedding_tensor_power) @@ -289,7 +290,7 @@ @testset "symmetric_power" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = exterior_power(standard_module(L), 2) + V = exterior_power(standard_module(L), 2)[1] type_V = module_type_bools(V) for k in 1:3 @@ -460,14 +461,14 @@ struct_const_V[1, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(-1), 2)], [(QQ(1), 1)], []] struct_const_V[2, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(-1), 3)], [], [(QQ(1), 1)]] struct_const_V[3, :] = Vector{Tuple{QQFieldElem,Int64}}[[], [(QQ(-1), 3)], [(QQ(1), 2)]] - @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 2)) == + @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 2)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 3, 3) struct_const_V[1, :] = Vector{Tuple{QQFieldElem,Int64}}[[], [(QQ(-1), 3)], [(QQ(1), 2)]] struct_const_V[2, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(1), 3)], [], [(QQ(-1), 1)]] struct_const_V[3, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(-1), 2)], [(QQ(1), 1)], []] - @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 3)) == + @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 3)[1]) == struct_const_V L = special_orthogonal_lie_algebra(QQ, 4) @@ -534,7 +535,7 @@ struct_const_V[6, :] = Vector{Tuple{QQFieldElem,Int64}}[ [], [], [(QQ(-1), 4)], [(QQ(1), 3)] ] - @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 1)) == + @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 1)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 6, 6) @@ -556,7 +557,7 @@ struct_const_V[6, :] = Vector{Tuple{QQFieldElem,Int64}}[ [], [(QQ(-1), 3)], [(QQ(1), 2)], [(QQ(-1), 5)], [(QQ(1), 4)], [] ] - @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 2)) == + @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 2)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 6, 4) @@ -578,7 +579,7 @@ struct_const_V[6, :] = Vector{Tuple{QQFieldElem,Int64}}[ [(QQ(-1), 2)], [(QQ(1), 1)], [], [] ] - @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 3)) == + @test lie_algebra_module_struct_const(L, exterior_power(standard_module(L), 3)[1]) == struct_const_V end diff --git a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl index 78e374680f4..36fc6738bdf 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl @@ -37,7 +37,7 @@ L = special_linear_lie_algebra(QQ, 3) stdV = standard_module(L) V1 = symmetric_power(stdV, 3) - V2 = exterior_power(stdV, 2) + V2 = exterior_power(stdV, 2)[1] h = identity_map(V1) @test domain(h) == V1 @@ -104,7 +104,7 @@ @testset "Direct sum constructions" begin L = special_orthogonal_lie_algebra(QQ, 4) Vs = [ - standard_module(L), dual(standard_module(L)), exterior_power(standard_module(L), 2) + standard_module(L), dual(standard_module(L)), exterior_power(standard_module(L), 2)[1] ] V = direct_sum(Vs...) @@ -127,7 +127,7 @@ L = special_orthogonal_lie_algebra(QQ, 4) V11 = standard_module(L) V12 = dual(standard_module(L)) - V21 = exterior_power(standard_module(L), 2) + V21 = exterior_power(standard_module(L), 2)[1] V22 = dual(dual(standard_module(L))) V1 = direct_sum(V11, V12) V2 = direct_sum(V21, V22) @@ -149,7 +149,7 @@ V11 = standard_module(L) V12 = dual(standard_module(L)) V21 = dual(dual(standard_module(L))) - V22 = exterior_power(standard_module(L), 2) + V22 = exterior_power(standard_module(L), 2)[1] V1 = tensor_product(V11, V12) V2 = tensor_product(V21, V22) h1 = hom(V11, V21, basis(V21)) @@ -174,8 +174,7 @@ @test is_welldefined(h) end - @testset "hom_power ($f_power)" for f_power in - [exterior_power, symmetric_power, tensor_power] + @testset "hom_power ($f_power)" for f_power in [symmetric_power, tensor_power] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) @@ -192,4 +191,22 @@ @test h(V(vs)) == W(hb.(vs)) end end + + @testset "hom_power ($f_power)" for f_power in [exterior_power] + for k in 0:3 + L = special_orthogonal_lie_algebra(QQ, 4) + Vb = standard_module(L) + Wb = dual(dual(standard_module(L))) + V = f_power(Vb, k)[1] + W = f_power(Wb, k)[1] + hb = hom(Vb, Wb, [2 * b for b in basis(Wb)]) + + h = hom_power(V, W, hb) + @test domain(h) == V + @test codomain(h) == W + @test is_welldefined(h) + vs = elem_type(Vb)[Vb(rand(-10:10, dim(Vb))) for _ in 1:k] + @test h(V(vs)) == W(hb.(vs)) + end + end end From 3d01875893bcb0dbcf9948b4c97d2cc3361f3e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 18 Dec 2023 15:26:02 +0100 Subject: [PATCH 03/17] Remove splatting in `my_mult` --- experimental/LieAlgebras/src/LieAlgebraModule.jl | 6 +++--- src/Modules/ExteriorPowers/FreeModules.jl | 6 +++--- src/Modules/ExteriorPowers/SubQuo.jl | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 402f1ff4e9d..0c8aa0433f1 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -1004,7 +1004,7 @@ function exterior_power( E_to_T = hom(E, T, E_to_T_mat; check=false) T_to_E = hom(T, E, T_to_E_mat; check=false) - function my_mult(as::LieAlgebraModuleElem{C}...) + function my_mult(as::Tuple{Vararg{LieAlgebraModuleElem{C}}}) @req length(as) == k "Length of vector does not match." @req all(a -> parent(a) === V, as) "Incompatible modules." mat = zero_matrix(R, 1, dim_E) @@ -1014,8 +1014,8 @@ function exterior_power( end return E(mat) end - function my_mult(as::Tuple) - return my_mult(as...)::LieAlgebraModuleElem{C} + function my_mult(as::LieAlgebraModuleElem{C}...) + return my_mult(as)::LieAlgebraModuleElem{C} end function my_decomp(a::LieAlgebraModuleElem{C}) diff --git a/src/Modules/ExteriorPowers/FreeModules.jl b/src/Modules/ExteriorPowers/FreeModules.jl index add7ca694a5..a95a40b0fd4 100644 --- a/src/Modules/ExteriorPowers/FreeModules.jl +++ b/src/Modules/ExteriorPowers/FreeModules.jl @@ -31,14 +31,14 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) end # Create the multiplication map - function my_mult(u::FreeModElem...) + function my_mult(u::Tuple{Vararg{FreeModElem}}) isempty(u) && return result[1] # only the case p=0 @req all(x -> parent(x) === F, u) "elements must live in the same module" @req length(u) == p "need a $p-tuple of elements" return wedge(collect(u); parent=result) end - function my_mult(u::Tuple) - return my_mult(u...) + function my_mult(u::FreeModElem...) + return my_mult(u) end function my_decomp(u::FreeModElem) diff --git a/src/Modules/ExteriorPowers/SubQuo.jl b/src/Modules/ExteriorPowers/SubQuo.jl index 9c13b8e3855..576bd74e5a4 100644 --- a/src/Modules/ExteriorPowers/SubQuo.jl +++ b/src/Modules/ExteriorPowers/SubQuo.jl @@ -19,14 +19,14 @@ function exterior_power(M::SubquoModule, p::Int; cached::Bool=true) result, mm = _exterior_power(phi, p) end - function my_mult(u::SubquoModuleElem...) + function my_mult(u::Tuple{Vararg{SubquoModuleElem}}) isempty(u) && return result[1] # only the case p=0 @assert all(x->parent(x)===M, u) "elements must live in the same module" @assert length(u) == p "need a $p-tuple of elements" return wedge(collect(u), parent=result) end - function my_mult(u::Tuple) - return my_mult(u...) + function my_mult(u::SubquoModuleElem...) + return my_mult(u) end function my_decomp(u::SubquoModuleElem) From f721e54b94c0d57a6c30a1df83ff9909a8104a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 11:24:00 +0100 Subject: [PATCH 04/17] More type stability for exterior powers `exterior_power(::FreeMod)` now completely stable, for `SubquoModule` only partially --- src/Modules/ExteriorPowers/FreeModules.jl | 12 +++++++----- src/Modules/ExteriorPowers/SubQuo.jl | 17 ++++++++--------- src/Modules/ModuleTypes.jl | 18 +++++++++--------- src/Modules/UngradedModules/FreeModElem.jl | 2 +- src/Modules/UngradedModules/SubquoModule.jl | 4 ++-- src/Modules/flattenings.jl | 2 +- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/Modules/ExteriorPowers/FreeModules.jl b/src/Modules/ExteriorPowers/FreeModules.jl index a95a40b0fd4..160d78acbe0 100644 --- a/src/Modules/ExteriorPowers/FreeModules.jl +++ b/src/Modules/ExteriorPowers/FreeModules.jl @@ -13,21 +13,23 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) if cached powers = _exterior_powers(F) - haskey(powers, p) && return powers[p]::Tuple{typeof(F), <:Map} + haskey(powers, p) && return powers[p] end - R = base_ring(F) + R = base_ring(F)::base_ring_type(F) n = rank(F) - result = free_module(R, binomial(n, p)) + result_ = free_module(R, binomial(n, p)) # In case F was graded, we have to take an extra detour. - if is_graded(F) + result = if is_graded(F) G = grading_group(F) weights = elem_type(G)[] for ind in OrderedMultiIndexSet(p, n) push!(weights, sum(degree(F[i]) for i in indices(ind); init=zero(G))) end - result = grade(result, weights) + grade(result_, weights) + else + result_ end # Create the multiplication map diff --git a/src/Modules/ExteriorPowers/SubQuo.jl b/src/Modules/ExteriorPowers/SubQuo.jl index 576bd74e5a4..76201904082 100644 --- a/src/Modules/ExteriorPowers/SubQuo.jl +++ b/src/Modules/ExteriorPowers/SubQuo.jl @@ -1,16 +1,15 @@ function exterior_power(M::SubquoModule, p::Int; cached::Bool=true) n = rank(ambient_free_module(M)) - R = base_ring(M) - ((p >= 0) && (p <= n)) || error("exponent out of range") + R = base_ring(M)::base_ring_type(M) + @req 0 <= p <= n "Exponent out of bounds" if cached powers = _exterior_powers(M) haskey(powers, p) && return powers[p] end - result = M # Initialize variable if iszero(p) - F = FreeMod(R, 1) + F = free_module(R, 1) result, _ = sub(F, [F[1]]) else C = presentation(M) @@ -21,8 +20,8 @@ function exterior_power(M::SubquoModule, p::Int; cached::Bool=true) function my_mult(u::Tuple{Vararg{SubquoModuleElem}}) isempty(u) && return result[1] # only the case p=0 - @assert all(x->parent(x)===M, u) "elements must live in the same module" - @assert length(u) == p "need a $p-tuple of elements" + @req all(x -> parent(x) === M, u) "elements must live in the same module" + @req length(u) == p "need a $p-tuple of elements" return wedge(collect(u), parent=result) end function my_mult(u::SubquoModuleElem...) @@ -30,9 +29,9 @@ function exterior_power(M::SubquoModule, p::Int; cached::Bool=true) end function my_decomp(u::SubquoModuleElem) - parent(u) === result || error("element does not belong to the correct module") - k = findfirst(x->x==u, gens(result)) - k === nothing && error("element must be a generator of the module") + @req parent(u) === result "element does not belong to the correct module" + k = findfirst(x -> x == u, gens(result)) + @req !isnothing(k) "element must be a generator of the module" ind = ordered_multi_index(k, p, n) e = gens(M) return Tuple(e[i] for i in indices(ind)) diff --git a/src/Modules/ModuleTypes.jl b/src/Modules/ModuleTypes.jl index f0b123bcc69..742301e0828 100644 --- a/src/Modules/ModuleTypes.jl +++ b/src/Modules/ModuleTypes.jl @@ -328,7 +328,7 @@ mutable struct SubQuoHom{ RingMapType<:Any } <: ModuleFPHom{T1, T2, RingMapType} matrix::MatElem - header::Hecke.MapHeader + header::MapHeader{T1,T2} im::Vector # The images of the generators; use `images_of_generators` as a getter. inverse_isomorphism::ModuleFPHom ring_map::RingMapType @@ -345,7 +345,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, Nothing}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -361,7 +361,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, Nothing}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -377,7 +377,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, Nothing}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -398,7 +398,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, RingMapType}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -419,7 +419,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, RingMapType}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -440,7 +440,7 @@ mutable struct SubQuoHom{ @assert all(x-> parent(x) === C, im) r = new{T1, T2, RingMapType}() - r.header = Hecke.MapHeader(D, C) + r.header = MapHeader(D, C) r.header.image = x->image(r, x) r.header.preimage = x->preimage(r, x) r.im = Vector{elem_type(C)}(im) @@ -525,7 +525,7 @@ When computed, the corresponding matrix (via `matrix()`) and inverse isomorphism T1 <: AbstractFreeMod, T2 <: ModuleFP, RingMapType <: Any} <: ModuleFPHom{T1, T2, RingMapType} - header::MapHeader + header::MapHeader{T1, T2} ring_map::RingMapType d::GrpAbFinGenElem imgs_of_gens::Vector # stored here for easy evaluation; use `images_of_generators` as getter @@ -652,7 +652,7 @@ struct FreeModuleHom_dec{ T2 <: ModuleFP, RingMapType <: Any} <: ModuleFPHom{T1, T2, RingMapType} f::FreeModuleHom{T1,T2, RingMapType} - header::MapHeader + header::MapHeader{T1,T2} # TODO degree and homogeneity function FreeModuleHom_dec(F::FreeMod_dec{T}, G::ModuleFP_dec{T}, a::Vector) where {T} diff --git a/src/Modules/UngradedModules/FreeModElem.jl b/src/Modules/UngradedModules/FreeModElem.jl index 65a3797a81b..6c86428ef13 100644 --- a/src/Modules/UngradedModules/FreeModElem.jl +++ b/src/Modules/UngradedModules/FreeModElem.jl @@ -187,7 +187,7 @@ end Return the underlying ring of `F`. """ -base_ring(F::FreeMod) = F.R +base_ring(F::FreeMod) = (F.R)::base_ring_type(F) #TODO: Parent - checks everywhere!!! diff --git a/src/Modules/UngradedModules/SubquoModule.jl b/src/Modules/UngradedModules/SubquoModule.jl index da06dfd3779..7d676d83faa 100644 --- a/src/Modules/UngradedModules/SubquoModule.jl +++ b/src/Modules/UngradedModules/SubquoModule.jl @@ -557,8 +557,8 @@ by submodule of G generated by ``` """ -function cokernel(f::ModuleFPHom) - return quo(codomain(f), image(f)[1], :module) +function cokernel(f::ModuleFPHom{T1, T2}) where {T1, T2} + return quo(codomain(f), image(f)[1], :module)::SubquoModule{elem_type(base_ring_type(T2))} end @doc raw""" diff --git a/src/Modules/flattenings.jl b/src/Modules/flattenings.jl index 6dc4516bc2e..64e062fefa4 100644 --- a/src/Modules/flattenings.jl +++ b/src/Modules/flattenings.jl @@ -93,7 +93,7 @@ end function _change_base_ring_and_preserve_gradings(phi::Map{<:Ring, <:Ring}, F::FreeMod) R = domain(phi) S = codomain(phi) - FS = (is_graded(F) ? graded_free_module(S, degree.(gens(F))) : FreeMod(S, ngens(F))) + FS = (is_graded(F) ? graded_free_module(S, Vector{elem_type(grading_group(F))}(degree.(gens(F)))) : FreeMod(S, ngens(F))) FS.S = F.S return FS, hom(F, FS, gens(FS), phi) end From a044259a11e818d29ab74bca410ebe333858fd7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 18 Dec 2023 17:58:24 +0100 Subject: [PATCH 05/17] Rework `induced_map_on_exterior_power` --- .../LieAlgebras/src/LieAlgebraModuleHom.jl | 65 ++++++++++++------- experimental/LieAlgebras/src/LieAlgebras.jl | 1 + src/Modules/ExteriorPowers/Generic.jl | 12 ++-- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index a904fde4676..07369e77bc9 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -492,6 +492,44 @@ function hom_tensor( return hom(V, W, mat; check=false) end +function _induced_map_on_power( + D::LieAlgebraModule, C::LieAlgebraModule, h::LieAlgebraModuleHom, power::Int, type::Symbol +) + TD = type == :tensor ? D : get_attribute(D, :embedding_tensor_power) + TC = type == :tensor ? C : get_attribute(C, :embedding_tensor_power) + + mat = reduce( + kronecker_product, + [matrix(h) for _ in 1:power]; + init=identity_matrix(coefficient_ring(C), 1), + ) + TD_to_TC = hom(TD, TC, mat; check=false) + + if type == :tensor + return TD_to_TC + else + D_to_TD = get_attribute(D, :embedding_tensor_power_embedding) + TC_to_C = get_attribute(C, :embedding_tensor_power_projection) + return D_to_TD * TD_to_TC * TC_to_C + end +end + +function induced_map_on_exterior_power( + h::LieAlgebraModuleHom; + domain::LieAlgebraModule{C}=exterior_power(Oscar.domain(phi), p)[1], + codomain::LieAlgebraModule{C}=exterior_power(Oscar.codomain(phi), p)[1], +) where {C<:FieldElem} + (domain_fl, domain_base, domain_k) = is_exterior_power(domain) + (codomain_fl, codomain_base, codomain_k) = is_exterior_power(codomain) + @req domain_fl "Domain must be an exterior power" + @req codomain_fl "Codomain must be an exterior power" + @req domain_k == codomain_k "Exponent mismatch" + @req Oscar.domain(h) === domain_base && Oscar.codomain(h) === codomain_base "Domain/codomain mismatch" + + k = domain_k + return _induced_map_on_power(domain, codomain, h, k, :ext) +end + @doc raw""" hom_power(V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom) -> LieAlgebraModuleHom @@ -502,13 +540,8 @@ $S^k h: V \to W$ (analogous for other types of powers). function hom_power( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom ) where {C<:FieldElem} - if ((fl, Vb, Vk) = is_exterior_power(V); fl) - (fl, Wb, Wk) = is_exterior_power(W) - @req fl "First module is an exterior power, but second module is not" - @req Vk == Wk "Exponent mismatch" - @req domain(h) === Vb && codomain(h) === Wb "Domain/codomain mismatch" - type = :ext - power = Vk + if is_exterior_power(V)[1] + return induced_map_on_exterior_power(h; domain=V, codomain=W) elseif is_symmetric_power(V) @req is_symmetric_power(W) "First module is a symmetric power, but second module is not" type = :sym @@ -524,21 +557,5 @@ function hom_power( power = get_attribute(V, :power) end - TV = type == :tensor ? V : get_attribute(V, :embedding_tensor_power) - TW = type == :tensor ? W : get_attribute(W, :embedding_tensor_power) - - mat = reduce( - kronecker_product, - [matrix(h) for _ in 1:power]; - init=identity_matrix(coefficient_ring(W), 1), - ) - TV_to_TW = hom(TV, TW, mat; check=false) - - if type == :tensor - return TV_to_TW - else - V_to_TV = get_attribute(V, :embedding_tensor_power_embedding) - TW_to_W = get_attribute(W, :embedding_tensor_power_projection) - return V_to_TV * TV_to_TW * TW_to_W - end + return _induced_map_on_power(V, W, h, power, type) end diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index 3f11e30ab0c..78fe6c67d7e 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -44,6 +44,7 @@ import ..Oscar: ideal, identity_map, image, + induced_map_on_exterior_power, inv, is_abelian, is_exterior_power, diff --git a/src/Modules/ExteriorPowers/Generic.jl b/src/Modules/ExteriorPowers/Generic.jl index 3a5dca9a635..ad2744995b0 100644 --- a/src/Modules/ExteriorPowers/Generic.jl +++ b/src/Modules/ExteriorPowers/Generic.jl @@ -172,13 +172,13 @@ end # The induced map on exterior powers function hom(M::FreeMod, N::FreeMod, phi::FreeModuleHom) success, F, p = is_exterior_power(M) - success || error("module is not an exterior power") + @req success "module is not an exterior power" success, FF, q = is_exterior_power(N) - success || error("module is not an exterior power") - F === domain(phi) || error("map not compatible") - FF === codomain(phi) || error("map not compatible") - p == q || error("exponents must agree") - return induced_map_on_exterior_power(phi, p, domain=M, codomain=N) + @req success "module is not an exterior power" + @req F === domain(phi) "map not compatible" + @req FF === codomain(phi) "map not compatible" + @req p == q "exponents must agree" + return induced_map_on_exterior_power(phi, p; domain=M, codomain=N) end From ef46cc19d9c066e04fdb1acb12b63a58c1d0f291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 16:58:55 +0100 Subject: [PATCH 06/17] Change `symmetric_power` analogous to the `exterior_power` change --- .../LieAlgebras/src/LieAlgebraModule.jl | 107 +++++++++++------- .../LieAlgebras/src/LieAlgebraModuleHom.jl | 21 +++- experimental/LieAlgebras/src/LieAlgebras.jl | 2 + .../LieAlgebras/test/LieAlgebraModule-test.jl | 53 +++++---- .../test/LieAlgebraModuleHom-test.jl | 10 +- 5 files changed, 119 insertions(+), 74 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 0c8aa0433f1..9a934e4c7c2 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -159,7 +159,7 @@ function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) is_direct_sum(V) || is_tensor_product(V) || is_exterior_power(V)[1] || - is_symmetric_power(V) || + is_symmetric_power(V)[1] || is_tensor_power(V) _show_inner(io, V) end @@ -196,10 +196,10 @@ function _show_inner(io::IO, V::LieAlgebraModule) print(io, Indent()) _show_inner(io, W) print(io, Dedent()) - elseif type == :symmetric_power - println(io, "$(ordinal_number_string(get_attribute(V, :power))) symmetric power of") + elseif ((fl, W, k) = is_symmetric_power(V); fl) + println(io, "$(ordinal_number_string(k)) symmetric power of") print(io, Indent()) - _show_inner(io, base_module(V)) + _show_inner(io, W) print(io, Dedent()) elseif type == :tensor_power println(io, "$(ordinal_number_string(get_attribute(V, :power))) tensor power of") @@ -237,7 +237,7 @@ function _module_type_to_string(type::Symbol, V::LieAlgebraModule) # TODO: remov return "Tensor product module" elseif is_exterior_power(V)[1] return "Exterior power module" - elseif type == :symmetric_power + elseif is_symmetric_power(V)[1] return "Symmetric power module" elseif type == :tensor_power return "Tensor power module" @@ -359,12 +359,8 @@ function (V::LieAlgebraModule{C})( elseif is_tensor_product(V) pure = get_attribute(V, :tensor_pure_function) return pure(a)::LieAlgebraModuleElem{C} - elseif is_exterior_power(V)[1] - pure = get_attribute(V, :wedge_pure_function) - return pure(Tuple(a))::LieAlgebraModuleElem{C} - elseif is_symmetric_power(V) - pure = get_attribute(V, :symmetric_pure_function) - return pure(a)::LieAlgebraModuleElem{C} + elseif is_exterior_power(V)[1] || is_symmetric_power(V)[1] + return V(Tuple(a))::LieAlgebraModuleElem{C} elseif is_tensor_power(V) pure = get_attribute(V, :tensor_pure_function) return pure(a)::LieAlgebraModuleElem{C} @@ -374,11 +370,16 @@ function (V::LieAlgebraModule{C})( end function (V::LieAlgebraModule{C})( - a::Tuple{T,Vararg{T}} + a::Tuple{Vararg{T}} ) where {T<:LieAlgebraModuleElem{C}} where {C<:FieldElem} if is_exterior_power(V)[1] pure = get_attribute(V, :wedge_pure_function) return pure(a)::LieAlgebraModuleElem{C} + elseif is_symmetric_power(V)[1] + pure = get_attribute(V, :mult_pure_function) + return pure(a)::LieAlgebraModuleElem{C} + elseif isempty(a) # TODO: Remove case + return V(LieAlgebraModuleElem{C}[]) else throw(ArgumentError("Invalid input.")) end @@ -388,10 +389,6 @@ function (V::LieAlgebraModule{C})(a::LieAlgebraModuleElem{C}...) where {C<:Field return V(a) end -function (V::LieAlgebraModule{C})(_::Tuple{}) where {C<:FieldElem} - return V(LieAlgebraModuleElem{C}[]) -end - ############################################################################### # # Arithmetic operations @@ -562,12 +559,17 @@ function is_exterior_power(V::LieAlgebraModule) end @doc raw""" - is_symmetric_power(V::LieAlgebraModule{C}) -> Bool + is_symmetric_power(V::LieAlgebraModule{C}) -> Bool, LieAlgebraModule{C}, Int -Check whether `V` has been constructed as a symmetric power of a module. -""" +Check whether `V` has been constructed as an symmetric power of a module. +If it has, return `true`, the base module, and the power. +If not, return `false` as the first return value, and arbitrary values for the other two.""" function is_symmetric_power(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :symmetric_power + if has_attribute(V, :is_symmetric_power) + W, k = get_attribute(V, :is_symmetric_power)::Tuple{typeof(V),Int} + return (true, W, k) + end + return (false, V, 0) end @doc raw""" @@ -585,7 +587,7 @@ end Returns the base module of `V`, if `V` has been constructed as a power module. """ function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: deprecate - @req is_dual(V) || is_symmetric_power(V) || is_tensor_power(V) "Not a power module." + @req is_dual(V) || is_tensor_power(V) "Not a power module." return get_attribute(V, :base_module)::LieAlgebraModule{C} end @@ -788,7 +790,7 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V1 = exterior_power(standard_module(L), 2)[1]; # some module -julia> V2 = symmetric_power(standard_module(L), 3); # some module +julia> V2 = symmetric_power(standard_module(L), 3)[1]; # some module julia> direct_sum(V1, V2) Direct sum module @@ -844,7 +846,7 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V1 = exterior_power(standard_module(L), 2)[1]; # some module -julia> V2 = symmetric_power(standard_module(L), 3); # some module +julia> V2 = symmetric_power(standard_module(L), 3)[1]; # some module julia> tensor_product(V1, V2) Tensor product module @@ -939,16 +941,16 @@ end tensor_product(V, Vs...) @doc raw""" - exterior_power(V::LieAlgebraModule{C}, k::Int) -> LieAlgebraModule{C} + exterior_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C} Construct the `k`-th exterior power $\bigwedge^k (V)$ of the module `V`, -together with a map that computes the exterior product of `k` elements of `V`. +together with a map that computes the wedge product of `k` elements of `V`. # Examples ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); -julia> V = symmetric_power(standard_module(L), 3); # some module +julia> V = symmetric_power(standard_module(L), 3)[1]; # some module julia> E, map = exterior_power(V, 2) (Exterior power module of dimension 45 over sl_3, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> exterior power module) @@ -1055,9 +1057,10 @@ end end @doc raw""" - symmetric_power(V::LieAlgebraModule{C}, k::Int) -> LieAlgebraModule{C} + symmetric_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C} -Construct the `k`-th symmetric power $S^k (V)$ of the module `V`. +Construct the `k`-th symmetric power $S^k (V)$ of the module `V`, +together with a map that computes the product of `k` elements of `V`. # Examples ```jldoctest @@ -1065,7 +1068,10 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V = exterior_power(standard_module(L), 2)[1]; # some module -julia> symmetric_power(V, 3) +julia> S, map = symmetric_power(V, 3) +(Symmetric power module of dimension 10 over sl_3, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> symmetric power module) + +julia> S Symmetric power module of dimension 10 3rd symmetric power of @@ -1074,8 +1080,16 @@ Symmetric power module over special linear Lie algebra of degree 3 over QQ ``` """ -function symmetric_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} +function symmetric_power( + V::LieAlgebraModule{C}, k::Int; cached::Bool=true +) where {C<:FieldElem} @req k >= 0 "Non-negative exponent needed" + + if cached + powers = _symmetric_powers(V) + haskey(powers, k) && return powers[k] + end + L = base_lie_algebra(V) R = coefficient_ring(V) dim_S = binomial(dim(V) + k - 1, k) @@ -1128,7 +1142,7 @@ function symmetric_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} S_to_T = hom(S, T, S_to_T_mat; check=false) T_to_S = hom(T, S, T_to_S_mat; check=false) - function pure(as::LieAlgebraModuleElem{C}...) + function my_mult(as::Tuple{Vararg{LieAlgebraModuleElem{C}}}) @req length(as) == k "Length of vector does not match." @req all(a -> parent(a) === V, as) "Incompatible modules." mat = zero_matrix(R, 1, dim_S) @@ -1139,14 +1153,11 @@ function symmetric_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} end return S(mat) end - function pure(as::Tuple) - return pure(as...)::LieAlgebraModuleElem{C} - end - function pure(as::Vector{LieAlgebraModuleElem{C}}) - return pure(as...)::LieAlgebraModuleElem{C} + function my_mult(as::LieAlgebraModuleElem{C}...) + return my_mult(as)::LieAlgebraModuleElem{C} end - function inv_pure(a::LieAlgebraModuleElem{C}) + function my_decomp(a::LieAlgebraModuleElem{C}) @req parent(a) === S "Incompatible modules." if iszero(a) return Tuple(zero(V) for _ in 1:k) @@ -1158,18 +1169,28 @@ function symmetric_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} return Tuple(basis(V, i) for i in inds) end + mult_map = MapFromFunc( + Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), S, my_mult, my_decomp + ) + inv_mult_map = MapFromFunc(S, domain(mult_map), my_decomp, my_mult) + set_attribute!( S, - :type => :symmetric_power, - :power => k, - :base_module => V, - :symmetric_pure_function => pure, - :symmetric_pure_preimage_function => inv_pure, + :is_symmetric_power => (V, k), + :mult_pure_function => mult_map, + :mult_generator_decompose_function => inv_mult_map, :embedding_tensor_power => T, :embedding_tensor_power_embedding => S_to_T, :embedding_tensor_power_projection => T_to_S, ) - return S + + cached && (_symmetric_powers(V)[k] = (S, mult_map)) + + return S, mult_map +end + +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _symmetric_powers(V::LieAlgebraModule) + return Dict{Int,Tuple{typeof(V),MapFromFunc}}() end @doc raw""" diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index 07369e77bc9..8fe326e2ffd 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -530,6 +530,22 @@ function induced_map_on_exterior_power( return _induced_map_on_power(domain, codomain, h, k, :ext) end +function induced_map_on_symmetric_power( + h::LieAlgebraModuleHom; + domain::LieAlgebraModule{C}=symmetric_power(Oscar.domain(phi), p)[1], + codomain::LieAlgebraModule{C}=symmetric_power(Oscar.codomain(phi), p)[1], +) where {C<:FieldElem} + (domain_fl, domain_base, domain_k) = is_symmetric_power(domain) + (codomain_fl, codomain_base, codomain_k) = is_symmetric_power(codomain) + @req domain_fl "Domain must be an symmetric power" + @req codomain_fl "Codomain must be an symmetric power" + @req domain_k == codomain_k "Exponent mismatch" + @req Oscar.domain(h) === domain_base && Oscar.codomain(h) === codomain_base "Domain/codomain mismatch" + + k = domain_k + return _induced_map_on_power(domain, codomain, h, k, :sym) +end + @doc raw""" hom_power(V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom) -> LieAlgebraModuleHom @@ -542,9 +558,8 @@ function hom_power( ) where {C<:FieldElem} if is_exterior_power(V)[1] return induced_map_on_exterior_power(h; domain=V, codomain=W) - elseif is_symmetric_power(V) - @req is_symmetric_power(W) "First module is a symmetric power, but second module is not" - type = :sym + elseif is_symmetric_power(V)[1] + return induced_map_on_symmetric_power(h; domain=V, codomain=W) elseif is_tensor_power(V) @req is_tensor_power(W) "First module is a tensor power, but second module is not" type = :tensor diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index 78fe6c67d7e..d4728207620 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -115,6 +115,7 @@ export fundamental_weights export general_linear_lie_algebra export hom_direct_sum export hom_power +export induced_map_on_symmetric_power export is_cartan_matrix export is_cartan_type export is_direct_sum @@ -222,6 +223,7 @@ export fundamental_weights export general_linear_lie_algebra export hom_direct_sum export hom_power +export induced_map_on_symmetric_power export is_cartan_matrix export is_cartan_type export is_direct_sum diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index df3cf111b1b..1d81145e494 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -65,7 +65,7 @@ @testset "⋀^2 S^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = exterior_power(symmetric_power(standard_module(L), 2), 2)[1] + V = exterior_power(symmetric_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @@ -83,19 +83,19 @@ @testset "S^2 ⋀^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = symmetric_power(exterior_power(standard_module(L), 2)[1], 2) + V = symmetric_power(exterior_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "S^2 T^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = symmetric_power(tensor_power(standard_module(L), 2), 2) + V = symmetric_power(tensor_power(standard_module(L), 2), 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "S^2 V of so_4(CF(4))" begin L = special_orthogonal_lie_algebra(cyclotomic_field(4)[1], 4) - V = symmetric_power(standard_module(L), 2) + V = symmetric_power(standard_module(L), 2)[1] lie_algebra_module_conformance_test(L, V) end @@ -107,7 +107,7 @@ @testset "T^2 S^2 V of sl_4(QQ)" begin L = special_linear_lie_algebra(QQ, 4) - V = tensor_power(symmetric_power(standard_module(L), 2), 2) + V = tensor_power(symmetric_power(standard_module(L), 2)[1], 2) lie_algebra_module_conformance_test(L, V) end @@ -124,7 +124,7 @@ is_direct_sum(V), is_tensor_product(V), is_exterior_power(V)[1], - is_symmetric_power(V), + is_symmetric_power(V)[1], is_tensor_power(V), ) @@ -145,7 +145,7 @@ @testset "dual" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2) + V = symmetric_power(standard_module(L), 2)[1] type_V = module_type_bools(V) dual_V = dual(V) @@ -169,7 +169,7 @@ @testset "direct_sum" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2) + V = symmetric_power(standard_module(L), 2)[1] type_V = module_type_bools(V) for k in 1:3 @@ -186,7 +186,7 @@ @test ds_V([x * v for v in a]) == x * ds_V(a) end - V1 = symmetric_power(standard_module(L), 2) + V1 = symmetric_power(standard_module(L), 2)[1] V2 = exterior_power(standard_module(L), 2)[1] type_V1 = module_type_bools(V1) type_V2 = module_type_bools(V2) @@ -208,7 +208,7 @@ @testset "tensor_product" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2) + V = symmetric_power(standard_module(L), 2)[1] type_V = module_type_bools(V) for k in 1:3 @@ -227,7 +227,7 @@ @test tp_V == tensor_power(V, k) end - V1 = symmetric_power(standard_module(L), 2) + V1 = symmetric_power(standard_module(L), 2)[1] V2 = exterior_power(standard_module(L), 2)[1] type_V1 = module_type_bools(V1) type_V2 = module_type_bools(V2) @@ -249,11 +249,11 @@ @testset "exterior_power" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2) + V = symmetric_power(standard_module(L), 2)[1] type_V = module_type_bools(V) for k in 1:3 - E = exterior_power(V, k)[1] + E, map = exterior_power(V, k) @test type_V == module_type_bools(V) # construction of pow_V should not change type of V @test is_exterior_power(E) === (true, V, k) @test dim(E) == binomial(dim(V), k) @@ -275,6 +275,9 @@ @test iszero(E(a, a)) end + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test E(as) == map(as) + T = get_attribute(E, :embedding_tensor_power) E_to_T = get_attribute(E, :embedding_tensor_power_embedding) T_to_E = get_attribute(E, :embedding_tensor_power_projection) @@ -294,9 +297,9 @@ type_V = module_type_bools(V) for k in 1:3 - S = symmetric_power(V, k) + S, map = symmetric_power(V, k) @test type_V == module_type_bools(V) # construction of pow_V should not change type of V - @test base_module(S) === V + @test is_symmetric_power(S) === (true, V, k) @test dim(S) == binomial(dim(V) + k - 1, k) @test length(repr(S)) < 10^4 # outputs tend to be excessively long due to recursion @@ -305,16 +308,20 @@ if k == 1 x = L(rand(-10:10, dim(L))) a = V(rand(-10:10, dim(V))) + # @test S(x * a) == x * S(a) # TODO: fix this @test S([x * a]) == x * S([a]) elseif k == 2 a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) - @test !iszero(S([a, b])) - @test !iszero(S([a, b]) + S([b, a])) - @test iszero(S([a, b]) - S([b, a])) - @test !iszero(S([a, a])) + @test !iszero(S(a, b)) + @test !iszero(S(a, b) + S(b, a)) + @test iszero(S(a, b) - S(b, a)) + @test !iszero(S(a, a)) end + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test S(as) == map(as) + T = get_attribute(S, :embedding_tensor_power) S_to_T = get_attribute(S, :embedding_tensor_power_embedding) T_to_S = get_attribute(S, :embedding_tensor_power_projection) @@ -386,7 +393,7 @@ struct_const_V[1, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(-1), 2)], [(QQ(1), 1)], []] struct_const_V[2, :] = Vector{Tuple{QQFieldElem,Int64}}[[(QQ(-1), 3)], [], [(QQ(1), 1)]] struct_const_V[3, :] = Vector{Tuple{QQFieldElem,Int64}}[[], [(QQ(-1), 3)], [(QQ(1), 2)]] - @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 1)) == + @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 1)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 3, 6) @@ -414,7 +421,7 @@ [(QQ(1), 4), (QQ(-1), 6)], [(QQ(2), 5)], ] - @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 2)) == + @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 2)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 3, 10) @@ -454,7 +461,7 @@ [(QQ(2), 8), (QQ(-1), 10)], [(QQ(3), 9)], ] - @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 4)) == + @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 4)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 3, 3) @@ -513,7 +520,7 @@ struct_const_V[6, :] = Vector{Tuple{QQFieldElem,Int64}}[ [], [], [(QQ(-1), 4)], [(QQ(1), 3)] ] - @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 1)) == + @test lie_algebra_module_struct_const(L, symmetric_power(standard_module(L), 1)[1]) == struct_const_V struct_const_V = Matrix{Vector{Tuple{QQFieldElem,Int64}}}(undef, 6, 4) diff --git a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl index 36fc6738bdf..8b43842ce80 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl @@ -36,7 +36,7 @@ @testset "Identity and zero map" begin L = special_linear_lie_algebra(QQ, 3) stdV = standard_module(L) - V1 = symmetric_power(stdV, 3) + V1 = symmetric_power(stdV, 3)[1] V2 = exterior_power(stdV, 2)[1] h = identity_map(V1) @@ -174,7 +174,7 @@ @test is_welldefined(h) end - @testset "hom_power ($f_power)" for f_power in [symmetric_power, tensor_power] + @testset "hom_power ($f_power)" for f_power in [tensor_power] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) @@ -192,13 +192,13 @@ end end - @testset "hom_power ($f_power)" for f_power in [exterior_power] + @testset "hom_power ($f_power)" for f_power in [exterior_power, symmetric_power] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) Wb = dual(dual(standard_module(L))) - V = f_power(Vb, k)[1] - W = f_power(Wb, k)[1] + V, _ = f_power(Vb, k) + W, _ = f_power(Wb, k) hb = hom(Vb, Wb, [2 * b for b in basis(Wb)]) h = hom_power(V, W, hb) From ddf0eb579435169565a69d57ea23c690e3a9b871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 17:35:10 +0100 Subject: [PATCH 07/17] Change `tensor_power` analogous to the `exterior_power` change --- .../LieAlgebras/src/LieAlgebraModule.jl | 100 +++++++++++------- .../LieAlgebras/src/LieAlgebraModuleHom.jl | 44 ++++++-- experimental/LieAlgebras/src/LieAlgebras.jl | 2 + .../LieAlgebras/test/LieAlgebraModule-test.jl | 62 +++++++---- .../test/LieAlgebraModuleHom-test.jl | 7 +- 5 files changed, 142 insertions(+), 73 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 9a934e4c7c2..ec20dd92d43 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -160,7 +160,7 @@ function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) is_tensor_product(V) || is_exterior_power(V)[1] || is_symmetric_power(V)[1] || - is_tensor_power(V) + is_tensor_power(V)[1] _show_inner(io, V) end print(io, Dedent()) @@ -201,10 +201,10 @@ function _show_inner(io::IO, V::LieAlgebraModule) print(io, Indent()) _show_inner(io, W) print(io, Dedent()) - elseif type == :tensor_power - println(io, "$(ordinal_number_string(get_attribute(V, :power))) tensor power of") + elseif ((fl, W, k) = is_tensor_power(V); fl) + println(io, "$(ordinal_number_string(k)) tensor power of") print(io, Indent()) - _show_inner(io, base_module(V)) + _show_inner(io, W) print(io, Dedent()) else println(io, "abstract module") @@ -239,7 +239,7 @@ function _module_type_to_string(type::Symbol, V::LieAlgebraModule) # TODO: remov return "Exterior power module" elseif is_symmetric_power(V)[1] return "Symmetric power module" - elseif type == :tensor_power + elseif is_tensor_power(V)[1] return "Tensor power module" else return "Abstract Lie algebra module" @@ -359,11 +359,8 @@ function (V::LieAlgebraModule{C})( elseif is_tensor_product(V) pure = get_attribute(V, :tensor_pure_function) return pure(a)::LieAlgebraModuleElem{C} - elseif is_exterior_power(V)[1] || is_symmetric_power(V)[1] + elseif is_exterior_power(V)[1] || is_symmetric_power(V)[1] || is_tensor_power(V)[1] return V(Tuple(a))::LieAlgebraModuleElem{C} - elseif is_tensor_power(V) - pure = get_attribute(V, :tensor_pure_function) - return pure(a)::LieAlgebraModuleElem{C} else throw(ArgumentError("Invalid input.")) end @@ -378,6 +375,9 @@ function (V::LieAlgebraModule{C})( elseif is_symmetric_power(V)[1] pure = get_attribute(V, :mult_pure_function) return pure(a)::LieAlgebraModuleElem{C} + elseif is_tensor_power(V)[1] + pure = get_attribute(V, :tensor_pure_function) + return pure(a)::LieAlgebraModuleElem{C} elseif isempty(a) # TODO: Remove case return V(LieAlgebraModuleElem{C}[]) else @@ -563,7 +563,8 @@ end Check whether `V` has been constructed as an symmetric power of a module. If it has, return `true`, the base module, and the power. -If not, return `false` as the first return value, and arbitrary values for the other two.""" +If not, return `false` as the first return value, and arbitrary values for the other two. +""" function is_symmetric_power(V::LieAlgebraModule) if has_attribute(V, :is_symmetric_power) W, k = get_attribute(V, :is_symmetric_power)::Tuple{typeof(V),Int} @@ -573,21 +574,27 @@ function is_symmetric_power(V::LieAlgebraModule) end @doc raw""" - is_tensor_power(V::LieAlgebraModule{C}) -> Bool + is_tensor_power(V::LieAlgebraModule{C}) -> Bool, LieAlgebraModule{C}, Int Check whether `V` has been constructed as a tensor power of a module. +If it has, return `true`, the base module, and the power. +If not, return `false` as the first return value, and arbitrary values for the other two. """ function is_tensor_power(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :tensor_power + if has_attribute(V, :is_tensor_power) + W, k = get_attribute(V, :is_tensor_power)::Tuple{typeof(V),Int} + return (true, W, k) + end + return (false, V, 0) end @doc raw""" base_module(V::LieAlgebraModule{C}) -> LieAlgebraModule{C} -Returns the base module of `V`, if `V` has been constructed as a power module. +Returns the base module of `V`, if `V` has been constructed as a dual module. """ -function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: deprecate - @req is_dual(V) || is_tensor_power(V) "Not a power module." +function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: remove + @req is_dual(V) "Not a dual module." return get_attribute(V, :base_module)::LieAlgebraModule{C} end @@ -602,8 +609,6 @@ function base_modules(V::LieAlgebraModule{C}) where {C<:FieldElem} return get_attribute(V, :tensor_product)::Vector{LieAlgebraModule{C}} elseif is_direct_sum(V) return get_attribute(V, :direct_sum)::Vector{LieAlgebraModule{C}} - elseif is_tensor_power(V) - return [base_module(V) for _ in 1:get_attribute(V, :power)] else error("Not a direct sum or tensor product module.") end @@ -941,7 +946,7 @@ end tensor_product(V, Vs...) @doc raw""" - exterior_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C} + exterior_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C}, Map Construct the `k`-th exterior power $\bigwedge^k (V)$ of the module `V`, together with a map that computes the wedge product of `k` elements of `V`. @@ -979,7 +984,7 @@ function exterior_power( dim_E = binomial(dim(V), k) ind_map = collect(combinations(1:dim(V), k)) - T = tensor_power(V, k) + T, _ = tensor_power(V, k) E_to_T_mat = zero_matrix(coefficient_ring(V), dim_E, dim(T)) T_to_E_mat = zero_matrix(coefficient_ring(V), dim(T), dim_E) for (i, _inds) in enumerate(ind_map), (inds, sgn) in permutations_with_sign(_inds) @@ -1057,7 +1062,7 @@ end end @doc raw""" - symmetric_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C} + symmetric_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C}, Map Construct the `k`-th symmetric power $S^k (V)$ of the module `V`, together with a map that computes the product of `k` elements of `V`. @@ -1095,7 +1100,7 @@ function symmetric_power( dim_S = binomial(dim(V) + k - 1, k) ind_map = collect(multicombinations(1:dim(V), k)) - T = tensor_power(V, k) + T, _ = tensor_power(V, k) S_to_T_mat = zero_matrix(coefficient_ring(V), dim_S, dim(T)) T_to_S_mat = zero_matrix(coefficient_ring(V), dim(T), dim_S) for (i, _inds) in enumerate(ind_map), inds in permutations(_inds) @@ -1194,9 +1199,10 @@ end end @doc raw""" - tensor_power(V::LieAlgebraModule{C}, k::Int) -> LieAlgebraModule{C} + tensor_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C}, Map -Construct the `k`-th tensor power $T^k (V)$ of the module `V`. +Construct the `k`-th tensor power $T^k (V)$ of the module `V`, +together with a map that computes the tensor product of `k` elements of `V`. # Examples ```jldoctest @@ -1204,7 +1210,10 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V = exterior_power(standard_module(L), 2)[1]; # some module -julia> tensor_power(V, 3) +julia> T, map = tensor_power(V, 3) +(Tensor power module of dimension 27 over sl_3, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> tensor power module) + +julia> T Tensor power module of dimension 27 3rd tensor power of @@ -1213,8 +1222,16 @@ Tensor power module over special linear Lie algebra of degree 3 over QQ ``` """ -function tensor_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} +function tensor_power( + V::LieAlgebraModule{C}, k::Int; cached::Bool=true +) where {C<:FieldElem} @req k >= 0 "Non-negative exponent needed" + + if cached + powers = _tensor_powers(V) + haskey(powers, k) && return powers[k] + end + L = base_lie_algebra(V) R = coefficient_ring(V) dim_T = dim(V)^k @@ -1243,7 +1260,7 @@ function tensor_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} T = LieAlgebraModule{C}(L, dim_T, transformation_matrices, s; check=false) - function pure(as::LieAlgebraModuleElem{C}...) + function my_mult(as::Tuple{Vararg{LieAlgebraModuleElem{C}}}) @req length(as) == k "Length of vector does not match." @req all(a -> parent(a) === V, as) "Incompatible modules." mat = zero_matrix(R, 1, dim_T) @@ -1254,14 +1271,11 @@ function tensor_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} end return T(mat) end - function pure(as::Tuple) - return pure(as...)::LieAlgebraModuleElem{C} - end - function pure(as::Vector{LieAlgebraModuleElem{C}}) - return pure(as...)::LieAlgebraModuleElem{C} + function my_mult(as::LieAlgebraModuleElem{C}...) + return my_mult(as)::LieAlgebraModuleElem{C} end - function inv_pure(a::LieAlgebraModuleElem{C}) + function my_decomp(a::LieAlgebraModuleElem{C}) @req parent(a) === T "Incompatible modules." if iszero(a) return Tuple(zero(V) for _ in 1:k) @@ -1273,15 +1287,25 @@ function tensor_power(V::LieAlgebraModule{C}, k::Int) where {C<:FieldElem} return Tuple(basis(V, i) for i in inds) end + mult_map = MapFromFunc( + Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), T, my_mult, my_decomp + ) + inv_mult_map = MapFromFunc(T, domain(mult_map), my_decomp, my_mult) + set_attribute!( T, - :type => :tensor_power, - :power => k, - :base_module => V, - :tensor_pure_function => pure, - :tensor_pure_preimage_function => inv_pure, + :is_tensor_power => (V, k), + :tensor_pure_function => mult_map, + :tensor_generator_decompose_function => inv_mult_map, ) - return T + + cached && (_tensor_powers(V)[k] = (T, mult_map)) + + return T, mult_map +end + +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _tensor_powers(V::LieAlgebraModule) + return Dict{Int,Tuple{typeof(V),MapFromFunc}}() end ############################################################################### diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index 8fe326e2ffd..f6e30842b79 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -476,11 +476,22 @@ This works for $r$th tensor powers as well. """ function hom_tensor( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, hs::Vector{<:LieAlgebraModuleHom} -) where {C<:FieldElem} - @req is_tensor_product(V) || is_tensor_power(V) "First module must be a tensor product or power" - @req is_tensor_product(W) || is_tensor_power(W) "Second module must be a tensor product or power" - Vs = base_modules(V) - Ws = base_modules(W) +) where {C<:FieldElem} # TODO: cleanup after refactoring tensor_product + if is_tensor_product(V) + Vs = base_modules(V) + elseif ((fl, Vb, k) = is_tensor_power(V); fl) + Vs = [Vb for _ in 1:k] + else + throw(ArgumentError("First module must be a tensor product or power")) + end + if is_tensor_product(W) + Ws = base_modules(W) + elseif ((fl, Wb, k) = is_tensor_power(W); fl) + Ws = [Wb for _ in 1:k] + else + throw(ArgumentError("Second module must be a tensor product or power")) + end + @req length(Vs) == length(Ws) == length(hs) "Length mismatch" @req all(i -> domain(hs[i]) === Vs[i] && codomain(hs[i]) === Ws[i], 1:length(hs)) "Domain/codomain mismatch" @@ -546,6 +557,22 @@ function induced_map_on_symmetric_power( return _induced_map_on_power(domain, codomain, h, k, :sym) end +function induced_map_on_tensor_power( + h::LieAlgebraModuleHom; + domain::LieAlgebraModule{C}=tensor_power(Oscar.domain(phi), p)[1], + codomain::LieAlgebraModule{C}=tensor_power(Oscar.codomain(phi), p)[1], +) where {C<:FieldElem} + (domain_fl, domain_base, domain_k) = is_tensor_power(domain) + (codomain_fl, codomain_base, codomain_k) = is_tensor_power(codomain) + @req domain_fl "Domain must be an tensor power" + @req codomain_fl "Codomain must be an tensor power" + @req domain_k == codomain_k "Exponent mismatch" + @req Oscar.domain(h) === domain_base && Oscar.codomain(h) === codomain_base "Domain/codomain mismatch" + + k = domain_k + return _induced_map_on_power(domain, codomain, h, k, :tensor) +end + @doc raw""" hom_power(V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom) -> LieAlgebraModuleHom @@ -555,14 +582,13 @@ $S^k h: V \to W$ (analogous for other types of powers). """ function hom_power( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom -) where {C<:FieldElem} +) where {C<:FieldElem} # TODO: cleanup if is_exterior_power(V)[1] return induced_map_on_exterior_power(h; domain=V, codomain=W) elseif is_symmetric_power(V)[1] return induced_map_on_symmetric_power(h; domain=V, codomain=W) - elseif is_tensor_power(V) - @req is_tensor_power(W) "First module is a tensor power, but second module is not" - type = :tensor + elseif is_tensor_power(V)[1] + return induced_map_on_tensor_power(h; domain=V, codomain=W) else throw(ArgumentError("First module must be a power module")) end diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index d4728207620..4b55748cb54 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -116,6 +116,7 @@ export general_linear_lie_algebra export hom_direct_sum export hom_power export induced_map_on_symmetric_power +export induced_map_on_tensor_power export is_cartan_matrix export is_cartan_type export is_direct_sum @@ -224,6 +225,7 @@ export general_linear_lie_algebra export hom_direct_sum export hom_power export induced_map_on_symmetric_power +export induced_map_on_tensor_power export is_cartan_matrix export is_cartan_type export is_direct_sum diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 1d81145e494..aa935daaa4e 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -57,6 +57,18 @@ lie_algebra_module_conformance_test(L, V) end + @testset "V^* of sl_4(QQ)" begin + L = special_linear_lie_algebra(QQ, 4) + V = dual(standard_module(L)) + lie_algebra_module_conformance_test(L, V) + end + + @testset "(T^2 S^2 V)^* of so_4(QQ)" begin + L = special_orthogonal_lie_algebra(QQ, 4) + V = dual(tensor_power(symmetric_power(standard_module(L), 2)[1], 2)[1]) + lie_algebra_module_conformance_test(L, V) + end + @testset "V of so_4(CF(4))" begin L = special_orthogonal_lie_algebra(cyclotomic_field(4)[1], 4) V = standard_module(L) @@ -71,7 +83,7 @@ @testset "⋀^2 T^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = exterior_power(tensor_power(standard_module(L), 2), 2)[1] + V = exterior_power(tensor_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @@ -89,7 +101,7 @@ @testset "S^2 T^2 V of so_4(QQ)" begin L = special_orthogonal_lie_algebra(QQ, 4) - V = symmetric_power(tensor_power(standard_module(L), 2), 2)[1] + V = symmetric_power(tensor_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @@ -101,19 +113,19 @@ @testset "T^2 ⋀^2 V of sl_4(QQ)" begin L = special_linear_lie_algebra(QQ, 4) - V = tensor_power(exterior_power(standard_module(L), 2)[1], 2) + V = tensor_power(exterior_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "T^2 S^2 V of sl_4(QQ)" begin L = special_linear_lie_algebra(QQ, 4) - V = tensor_power(symmetric_power(standard_module(L), 2)[1], 2) + V = tensor_power(symmetric_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end @testset "T^2 V of sl_4(CF(4))" begin L = special_linear_lie_algebra(cyclotomic_field(4)[1], 4) - V = tensor_power(standard_module(L), 2) + V = tensor_power(standard_module(L), 2)[1] lie_algebra_module_conformance_test(L, V) end end @@ -125,7 +137,7 @@ is_tensor_product(V), is_exterior_power(V)[1], is_symmetric_power(V)[1], - is_tensor_power(V), + is_tensor_power(V)[1], ) @testset "standard_module" begin @@ -224,7 +236,7 @@ a = [V(rand(-10:10, dim(V))) for _ in 1:k] @test sum(tp_V([i == j ? x * v : v for (j, v) in enumerate(a)]) for i in 1:k) == x * tp_V(a) - @test tp_V == tensor_power(V, k) + @test tp_V == tensor_power(V, k)[1] end V1 = symmetric_power(standard_module(L), 2)[1] @@ -254,7 +266,7 @@ for k in 1:3 E, map = exterior_power(V, k) - @test type_V == module_type_bools(V) # construction of pow_V should not change type of V + @test type_V == module_type_bools(V) # construction of E should not change type of V @test is_exterior_power(E) === (true, V, k) @test dim(E) == binomial(dim(V), k) @test length(repr(E)) < 10^4 # outputs tend to be excessively long due to recursion @@ -281,7 +293,7 @@ T = get_attribute(E, :embedding_tensor_power) E_to_T = get_attribute(E, :embedding_tensor_power_embedding) T_to_E = get_attribute(E, :embedding_tensor_power_projection) - @test T == tensor_power(V, k) + @test T == tensor_power(V, k)[1] @test domain(E_to_T) === E @test codomain(E_to_T) === T @test domain(T_to_E) === T @@ -298,7 +310,7 @@ for k in 1:3 S, map = symmetric_power(V, k) - @test type_V == module_type_bools(V) # construction of pow_V should not change type of V + @test type_V == module_type_bools(V) # construction of S should not change type of V @test is_symmetric_power(S) === (true, V, k) @test dim(S) == binomial(dim(V) + k - 1, k) @test length(repr(S)) < 10^4 # outputs tend to be excessively long due to recursion @@ -325,7 +337,7 @@ T = get_attribute(S, :embedding_tensor_power) S_to_T = get_attribute(S, :embedding_tensor_power_embedding) T_to_S = get_attribute(S, :embedding_tensor_power_projection) - @test T == tensor_power(V, k) + @test T == tensor_power(V, k)[1] @test domain(S_to_T) === S @test codomain(S_to_T) === T @test domain(T_to_S) === T @@ -338,29 +350,33 @@ @testset "tensor_power" begin for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] V = standard_module(L) - T = module_type_bools(V) + type_V = module_type_bools(V) for k in 1:3 - pow_V = tensor_power(V, k) - @test T == module_type_bools(V) # construction of pow_V should not change type of V - @test base_module(pow_V) === V - @test dim(pow_V) == dim(V)^k - @test length(repr(pow_V)) < 10^4 # outputs tend to be excessively long due to recursion + T, map = tensor_power(V, k) + @test type_V == module_type_bools(V) # construction of T should not change type of V + @test is_tensor_power(T) === (true, V, k) + @test dim(T) == dim(V)^k + @test length(repr(T)) < 10^4 # outputs tend to be excessively long due to recursion - @test module_type_bools(pow_V) == (false, false, false, false, false, false, true) # tensor_power + @test module_type_bools(T) == (false, false, false, false, false, false, true) # tensor_power if k == 1 x = L(rand(-10:10, dim(L))) a = V(rand(-10:10, dim(V))) - @test pow_V([x * a]) == x * pow_V([a]) + # @test T(x * a) == x * T(a) # TODO: fix this + @test T([x * a]) == x * T([a]) elseif k == 2 a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) - @test !iszero(pow_V([a, b])) - @test !iszero(pow_V([a, b]) + pow_V([b, a])) - @test !iszero(pow_V([a, b]) - pow_V([b, a])) - @test !iszero(pow_V([a, a])) + @test !iszero(T(a, b)) + @test !iszero(T(a, b) + T(b, a)) + @test !iszero(T(a, b) - T(b, a)) + @test !iszero(T(a, a)) end + + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test T(as) == map(as) end end end diff --git a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl index 8b43842ce80..223853b2196 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl @@ -164,7 +164,7 @@ V12 = dual(standard_module(L)) V2i = dual(dual(standard_module(L))) V1 = tensor_product(V11, V12) - V2 = tensor_power(V2i, 2) + V2 = tensor_power(V2i, 2)[1] h1 = hom(V11, V2i, basis(V2i)) h2 = hom(V12, V2i, [zero(V2i) for _ in basis(V12)]) @@ -174,7 +174,7 @@ @test is_welldefined(h) end - @testset "hom_power ($f_power)" for f_power in [tensor_power] + @testset "hom_power ($f_power)" for f_power in [] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) @@ -192,7 +192,8 @@ end end - @testset "hom_power ($f_power)" for f_power in [exterior_power, symmetric_power] + @testset "hom_power ($f_power)" for f_power in + [exterior_power, symmetric_power, tensor_power] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) From 59ded76e0253089b2f1b7cf733f729fcb69c5b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 17:39:56 +0100 Subject: [PATCH 08/17] Cleanup `hom_power` --- .../LieAlgebras/docs/src/module_homs.md | 2 +- .../LieAlgebras/src/LieAlgebraModuleHom.jl | 49 ++++++++----------- experimental/LieAlgebras/src/LieAlgebras.jl | 2 - .../test/LieAlgebraModuleHom-test.jl | 24 ++------- 4 files changed, 25 insertions(+), 52 deletions(-) diff --git a/experimental/LieAlgebras/docs/src/module_homs.md b/experimental/LieAlgebras/docs/src/module_homs.md index 932a2b70e83..a0d891dbed9 100644 --- a/experimental/LieAlgebras/docs/src/module_homs.md +++ b/experimental/LieAlgebras/docs/src/module_homs.md @@ -60,5 +60,5 @@ canonical_projections(::LieAlgebraModule) canonical_projection(::LieAlgebraModule, ::Int) hom_direct_sum(::LieAlgebraModule{C}, ::LieAlgebraModule{C}, ::Matrix{<:LieAlgebraModuleHom}) where {C<:FieldElem} hom_tensor(::LieAlgebraModule{C}, ::LieAlgebraModule{C}, ::Vector{<:LieAlgebraModuleHom}) where {C<:FieldElem} -hom_power(::LieAlgebraModule{C}, ::LieAlgebraModule{C}, ::LieAlgebraModuleHom) where {C<:FieldElem} +hom(::LieAlgebraModule{C}, ::LieAlgebraModule{C}, ::LieAlgebraModuleHom) where {C<:FieldElem} ``` diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index f6e30842b79..f83759eb347 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -503,6 +503,27 @@ function hom_tensor( return hom(V, W, mat; check=false) end +@doc raw""" + hom(V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom) -> LieAlgebraModuleHom + +Given modules `V` and `W` which are exterior/symmetric/tensor powers of the same kind with the same exponent, +say, e.g., $V = S^k V'$, $W = S^k W'$, and given a homomorphism $h : V' \to W'$, return +$S^k h: V \to W$ (analogous for other types of powers). +""" +function hom( + V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom +) where {C<:FieldElem} + if is_exterior_power(V)[1] + return induced_map_on_exterior_power(h; domain=V, codomain=W) + elseif is_symmetric_power(V)[1] + return induced_map_on_symmetric_power(h; domain=V, codomain=W) + elseif is_tensor_power(V)[1] + return induced_map_on_tensor_power(h; domain=V, codomain=W) + else + throw(ArgumentError("First module must be a power module")) + end +end + function _induced_map_on_power( D::LieAlgebraModule, C::LieAlgebraModule, h::LieAlgebraModuleHom, power::Int, type::Symbol ) @@ -572,31 +593,3 @@ function induced_map_on_tensor_power( k = domain_k return _induced_map_on_power(domain, codomain, h, k, :tensor) end - -@doc raw""" - hom_power(V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom) -> LieAlgebraModuleHom - -Given modules `V` and `W` which are exterior/symmetric/tensor powers of the same kind with the same exponent, -say, e.g., $V = S^k V'$, $W = S^k W'$, and given a homomorphism $h : V' \to W'$, return -$S^k h: V \to W$ (analogous for other types of powers). -""" -function hom_power( - V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, h::LieAlgebraModuleHom -) where {C<:FieldElem} # TODO: cleanup - if is_exterior_power(V)[1] - return induced_map_on_exterior_power(h; domain=V, codomain=W) - elseif is_symmetric_power(V)[1] - return induced_map_on_symmetric_power(h; domain=V, codomain=W) - elseif is_tensor_power(V)[1] - return induced_map_on_tensor_power(h; domain=V, codomain=W) - else - throw(ArgumentError("First module must be a power module")) - end - if !is_exterior_power(V)[1] - @req get_attribute(V, :power) == get_attribute(W, :power) "Exponent mismatch" - @req domain(h) === base_module(V) && codomain(h) === base_module(W) "Domain/codomain mismatch" - power = get_attribute(V, :power) - end - - return _induced_map_on_power(V, W, h, power, type) -end diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index 4b55748cb54..cd2212951a9 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -114,7 +114,6 @@ export fundamental_weight export fundamental_weights export general_linear_lie_algebra export hom_direct_sum -export hom_power export induced_map_on_symmetric_power export induced_map_on_tensor_power export is_cartan_matrix @@ -223,7 +222,6 @@ export fundamental_weight export fundamental_weights export general_linear_lie_algebra export hom_direct_sum -export hom_power export induced_map_on_symmetric_power export induced_map_on_tensor_power export is_cartan_matrix diff --git a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl index 223853b2196..2baaa842540 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModuleHom-test.jl @@ -174,26 +174,8 @@ @test is_welldefined(h) end - @testset "hom_power ($f_power)" for f_power in [] - for k in 0:3 - L = special_orthogonal_lie_algebra(QQ, 4) - Vb = standard_module(L) - Wb = dual(dual(standard_module(L))) - V = f_power(Vb, k) - W = f_power(Wb, k) - hb = hom(Vb, Wb, [2 * b for b in basis(Wb)]) - - h = hom_power(V, W, hb) - @test domain(h) == V - @test codomain(h) == W - @test is_welldefined(h) - vs = elem_type(Vb)[Vb(rand(-10:10, dim(Vb))) for _ in 1:k] - @test h(V(vs)) == W(hb.(vs)) - end - end - - @testset "hom_power ($f_power)" for f_power in - [exterior_power, symmetric_power, tensor_power] + @testset "hom (lift $f_power)" for f_power in + [exterior_power, symmetric_power, tensor_power] for k in 0:3 L = special_orthogonal_lie_algebra(QQ, 4) Vb = standard_module(L) @@ -202,7 +184,7 @@ W, _ = f_power(Wb, k) hb = hom(Vb, Wb, [2 * b for b in basis(Wb)]) - h = hom_power(V, W, hb) + h = hom(V, W, hb) @test domain(h) == V @test codomain(h) == W @test is_welldefined(h) From 5384a67cbe279b29b834f6610e7ac080195182cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 19 Dec 2023 17:59:51 +0100 Subject: [PATCH 09/17] Minor changes to `standard_module` and `dual` --- experimental/LieAlgebras/docs/src/modules.md | 1 - .../LieAlgebras/src/LieAlgebraModule.jl | 70 ++++++++++++------- experimental/LieAlgebras/src/LieAlgebras.jl | 2 - .../LieAlgebras/test/LieAlgebraModule-test.jl | 18 ++++- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/experimental/LieAlgebras/docs/src/modules.md b/experimental/LieAlgebras/docs/src/modules.md index 5778e85c939..907a60abb6d 100644 --- a/experimental/LieAlgebras/docs/src/modules.md +++ b/experimental/LieAlgebras/docs/src/modules.md @@ -74,6 +74,5 @@ is_tensor_product(::LieAlgebraModule) is_exterior_power(::LieAlgebraModule) is_symmetric_power(::LieAlgebraModule) is_tensor_power(::LieAlgebraModule) -base_module(::LieAlgebraModule{C}) where {C<:FieldElem} base_modules(::LieAlgebraModule{C}) where {C<:FieldElem} ``` diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index ec20dd92d43..9a628781c6d 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -155,7 +155,7 @@ function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) io = pretty(io) println(io, _module_type_to_string(get_attribute(V, :type, :unknown), V)) println(io, Indent(), "of dimension $(dim(V))") - if is_dual(V) || + if is_dual(V)[1] || is_direct_sum(V) || is_tensor_product(V) || is_exterior_power(V)[1] || @@ -170,12 +170,12 @@ end function _show_inner(io::IO, V::LieAlgebraModule) type = get_attribute(V, :type, :unknown) - if type == :standard_module + if is_standard_module(V) println(io, "standard module") - elseif type == :dual + elseif ((fl, W) = is_dual(V); fl) println(io, "dual of ", Lowercase()) print(io, Indent()) - _show_inner(io, base_module(V)) + _show_inner(io, W) print(io, Dedent()) elseif type == :direct_sum println(io, "direct sum with direct summands") @@ -227,9 +227,9 @@ function Base.show(io::IO, V::LieAlgebraModule) end function _module_type_to_string(type::Symbol, V::LieAlgebraModule) # TODO: remove first argument - if type == :standard_module + if is_standard_module(V) return "Standard module" - elseif type == :dual + elseif is_dual(V)[1] return "Dual module" elseif type == :direct_sum return "Direct sum module" @@ -331,7 +331,7 @@ Return `v`. Fails, in general, if `v` is not an element of `V`. If `V` is the dual module of the parent of `v`, return the dual of `v`. """ function (V::LieAlgebraModule{C})(v::LieAlgebraModuleElem{C}) where {C<:FieldElem} - if is_dual(V) && base_module(V) === parent(v) + if ((fl, W) = is_dual(V); fl) && W === parent(v) return V(coefficients(v)) end @req V === parent(v) "Incompatible modules." @@ -513,16 +513,26 @@ end Check whether `V` has been constructed as a standard module. """ function is_standard_module(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :standard_module + if has_attribute(V, :is_standard_module) + @assert get_attribute(V, :is_standard_module)::Bool === true + return true + end + return false end @doc raw""" - is_dual(V::LieAlgebraModule{C}) -> Bool + is_dual(V::LieAlgebraModule{C}) -> Bool, LieAlgebraModule{C} Check whether `V` has been constructed as a dual module. +If it has, return `true` and the base module. +If not, return `false` as the first return value, and an arbitrary value for the second. """ function is_dual(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :dual + if has_attribute(V, :is_dual) + W = get_attribute(V, :is_dual)::typeof(V) + return (true, W) + end + return (false, V) end @doc raw""" @@ -588,23 +598,13 @@ function is_tensor_power(V::LieAlgebraModule) return (false, V, 0) end -@doc raw""" - base_module(V::LieAlgebraModule{C}) -> LieAlgebraModule{C} - -Returns the base module of `V`, if `V` has been constructed as a dual module. -""" -function base_module(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: remove - @req is_dual(V) "Not a dual module." - return get_attribute(V, :base_module)::LieAlgebraModule{C} -end - @doc raw""" base_modules(V::LieAlgebraModule{C}) -> Vector{LieAlgebraModule{C}} Returns the summands or tensor factors of `V`, if `V` has been constructed as a direct sum or tensor product of modules. """ -function base_modules(V::LieAlgebraModule{C}) where {C<:FieldElem} +function base_modules(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: remove if is_tensor_product(V) return get_attribute(V, :tensor_product)::Vector{LieAlgebraModule{C}} elseif is_direct_sum(V) @@ -710,7 +710,7 @@ function trivial_module(L::LieAlgebra, d::IntegerUnion=1) end @doc raw""" - standard_module(L::LinearLieAlgebra{C}) -> LieAlgebraModule{C} + standard_module(L::LinearLieAlgebra{C}; cached::Bool=true) -> LieAlgebraModule{C} Construct the standard module of the linear Lie algebra `L`. If `L` is a Lie subalgebra of $\mathfrak{gl}_n(R)$, then the standard module @@ -731,19 +731,28 @@ Standard module over special linear Lie algebra of degree 3 over QQ ``` """ -function standard_module(L::LinearLieAlgebra) +function standard_module(L::LinearLieAlgebra; cached::Bool=true) + if cached && has_attribute(L, :standard_module) + return get_attribute( + L, :standard_module + )::LieAlgebraModule{elem_type(coefficient_ring(L))} + end + dim_std_V = L.n transformation_matrices = transpose.(matrix_repr_basis(L)) s = [Symbol("v_$(i)") for i in 1:dim_std_V] std_V = LieAlgebraModule{elem_type(coefficient_ring(L))}( L, dim_std_V, transformation_matrices, s; check=false ) - set_attribute!(std_V, :type => :standard_module) + set_attribute!(std_V, :is_standard_module => true) + + cached && (set_attribute!(L, :standard_module => std_V)) + return std_V end @doc raw""" - dual(V::LieAlgebraModule{C}) -> LieAlgebraModule{C} + dual(V::LieAlgebraModule{C}; cached::Bool=true) -> LieAlgebraModule{C} Construct the dual module of `V`. @@ -762,7 +771,11 @@ Dual module over special linear Lie algebra of degree 3 over QQ ``` """ -function dual(V::LieAlgebraModule{C}) where {C<:FieldElem} +function dual(V::LieAlgebraModule{C}; cached::Bool=true) where {C<:FieldElem} + if cached && has_attribute(V, :dual) + return get_attribute(V, :dual)::typeof(V) + end + L = base_lie_algebra(V) dim_dual_V = dim(V) @@ -777,7 +790,10 @@ function dual(V::LieAlgebraModule{C}) where {C<:FieldElem} end pow_V = LieAlgebraModule{C}(L, dim_dual_V, transformation_matrices, s; check=false) - set_attribute!(pow_V, :type => :dual, :base_module => V) + set_attribute!(pow_V, :is_dual => V) + + cached && (set_attribute!(V, :dual => pow_V)) + return pow_V end diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index cd2212951a9..316ad8363fa 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -92,7 +92,6 @@ export WeylGroup, WeylGroupElem export abelian_lie_algebra export abstract_module export base_lie_algebra -export base_module export base_modules export bracket export cartan_bilinear_form @@ -202,7 +201,6 @@ export WeylGroup, WeylGroupElem export abelian_lie_algebra export abstract_module export base_lie_algebra -export base_module export base_modules export bracket export cartan_bilinear_form diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index aa935daaa4e..3a7d08af2a6 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -132,7 +132,7 @@ module_type_bools(V) = ( is_standard_module(V), - is_dual(V), + is_dual(V)[1], is_direct_sum(V), is_tensor_product(V), is_exterior_power(V)[1], @@ -144,6 +144,8 @@ for (L, dimV) in [(general_linear_lie_algebra(QQ, 3), 3), (special_orthogonal_lie_algebra(QQ, 4), 4)] V = standard_module(L) + @test V === standard_module(L) + @test V !== standard_module(L; cached=false) @test dim(V) == dimV @test length(repr(V)) < 10^4 # outputs tend to be excessively long due to recursion @@ -161,8 +163,10 @@ type_V = module_type_bools(V) dual_V = dual(V) + @test dual_V === dual(V) + @test dual_V !== dual(V; cached=false) @test type_V == module_type_bools(V) # construction of dual_V should not change type of V - @test base_module(dual_V) === V + @test is_dual(dual_V) === (true, V) @test dim(dual_V) == dim(V) @test length(repr(dual_V)) < 10^4 # outputs tend to be excessively long due to recursion @@ -186,6 +190,8 @@ for k in 1:3 ds_V = direct_sum([V for _ in 1:k]...) + @test_broken ds_V === direct_sum([V for _ in 1:k]...) + @test_broken ds_V !== direct_sum([V for _ in 1:k]...; cached=false) @test type_V == module_type_bools(V) # construction of ds_V should not change type of V @test base_modules(ds_V) == [V for _ in 1:k] @test dim(ds_V) == k * dim(V) @@ -225,6 +231,8 @@ for k in 1:3 tp_V = tensor_product([V for _ in 1:k]...) + @test_broken tp_V === tensor_product([V for _ in 1:k]...) + @test_broken tp_V !== tensor_product([V for _ in 1:k]...; cached=false) @test type_V == module_type_bools(V) # construction of tp_V should not change type of V @test base_modules(tp_V) == [V for _ in 1:k] @test dim(tp_V) == dim(V)^k @@ -266,6 +274,8 @@ for k in 1:3 E, map = exterior_power(V, k) + @test E === exterior_power(V, k)[1] + @test E !== exterior_power(V, k; cached=false)[1] @test type_V == module_type_bools(V) # construction of E should not change type of V @test is_exterior_power(E) === (true, V, k) @test dim(E) == binomial(dim(V), k) @@ -310,6 +320,8 @@ for k in 1:3 S, map = symmetric_power(V, k) + @test S === symmetric_power(V, k)[1] + @test S !== symmetric_power(V, k; cached=false)[1] @test type_V == module_type_bools(V) # construction of S should not change type of V @test is_symmetric_power(S) === (true, V, k) @test dim(S) == binomial(dim(V) + k - 1, k) @@ -354,6 +366,8 @@ for k in 1:3 T, map = tensor_power(V, k) + @test T === tensor_power(V, k)[1] + @test T !== tensor_power(V, k; cached=false)[1] @test type_V == module_type_bools(V) # construction of T should not change type of V @test is_tensor_power(T) === (true, V, k) @test dim(T) == dim(V)^k From 60e40694303c50dfb175613b6a65738cfcab18fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 20 Dec 2023 15:08:52 +0100 Subject: [PATCH 10/17] Make `is_direct_sum` and `is_tensor_product` consistent and some cleanup --- experimental/LieAlgebras/docs/src/modules.md | 1 - .../LieAlgebras/src/LieAlgebraModule.jl | 79 ++++++++----------- .../LieAlgebras/src/LieAlgebraModuleHom.jl | 46 +++++------ experimental/LieAlgebras/src/LieAlgebras.jl | 2 - .../LieAlgebras/test/LieAlgebraModule-test.jl | 14 ++-- 5 files changed, 66 insertions(+), 76 deletions(-) diff --git a/experimental/LieAlgebras/docs/src/modules.md b/experimental/LieAlgebras/docs/src/modules.md index 907a60abb6d..a646d517f5d 100644 --- a/experimental/LieAlgebras/docs/src/modules.md +++ b/experimental/LieAlgebras/docs/src/modules.md @@ -74,5 +74,4 @@ is_tensor_product(::LieAlgebraModule) is_exterior_power(::LieAlgebraModule) is_symmetric_power(::LieAlgebraModule) is_tensor_power(::LieAlgebraModule) -base_modules(::LieAlgebraModule{C}) where {C<:FieldElem} ``` diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 9a628781c6d..d7f7c41f597 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -153,11 +153,11 @@ end function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) io = pretty(io) - println(io, _module_type_to_string(get_attribute(V, :type, :unknown), V)) + println(io, _module_type_to_string(V)) println(io, Indent(), "of dimension $(dim(V))") if is_dual(V)[1] || - is_direct_sum(V) || - is_tensor_product(V) || + is_direct_sum(V)[1] || + is_tensor_product(V)[1] || is_exterior_power(V)[1] || is_symmetric_power(V)[1] || is_tensor_power(V)[1] @@ -169,7 +169,6 @@ function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) end function _show_inner(io::IO, V::LieAlgebraModule) - type = get_attribute(V, :type, :unknown) if is_standard_module(V) println(io, "standard module") elseif ((fl, W) = is_dual(V); fl) @@ -177,17 +176,17 @@ function _show_inner(io::IO, V::LieAlgebraModule) print(io, Indent()) _show_inner(io, W) print(io, Dedent()) - elseif type == :direct_sum + elseif ((fl, Ws) = is_direct_sum(V); fl) println(io, "direct sum with direct summands") print(io, Indent()) - for W in base_modules(V) + for W in Ws _show_inner(io, W) end print(io, Dedent()) - elseif type == :tensor_product + elseif ((fl, Ws) = is_tensor_product(V); fl) println(io, "tensor product with tensor factors") print(io, Indent()) - for W in base_modules(V) + for W in Ws _show_inner(io, W) end print(io, Dedent()) @@ -213,27 +212,22 @@ end function Base.show(io::IO, V::LieAlgebraModule) if get(io, :supercompact, false) - print(io, _module_type_to_string(get_attribute(V, :type, :unknown), V)) + print(io, _module_type_to_string(V)) else io = pretty(io) - print( - io, - _module_type_to_string(get_attribute(V, :type, :unknown), V), - " of dimension $(dim(V)) over ", - Lowercase(), - ) + print(io, _module_type_to_string(V), " of dimension $(dim(V)) over ", Lowercase()) print(IOContext(io, :supercompact => true), base_lie_algebra(V)) end end -function _module_type_to_string(type::Symbol, V::LieAlgebraModule) # TODO: remove first argument +function _module_type_to_string(V::LieAlgebraModule) if is_standard_module(V) return "Standard module" elseif is_dual(V)[1] return "Dual module" - elseif type == :direct_sum + elseif is_direct_sum(V)[1] return "Direct sum module" - elseif type == :tensor_product + elseif is_tensor_product(V)[1] return "Tensor product module" elseif is_exterior_power(V)[1] return "Exterior power module" @@ -352,11 +346,11 @@ where _correct_ depends on the case above. function (V::LieAlgebraModule{C})( a::Vector{T} ) where {T<:LieAlgebraModuleElem{C}} where {C<:FieldElem} - if is_direct_sum(V) - @req length(a) == length(base_modules(V)) "Length of vector does not match." - @req all(i -> parent(a[i]) === base_modules(V)[i], 1:length(a)) "Incompatible modules." + if ((fl, Vs) = is_direct_sum(V); fl) + @req length(a) == length(Vs) "Length of vector does not match." + @req all(i -> parent(a[i]) === Vs[i], 1:length(a)) "Incompatible modules." return sum(inji(ai) for (ai, inji) in zip(a, canonical_injections(V)); init=zero(V)) - elseif is_tensor_product(V) + elseif is_tensor_product(V)[1] pure = get_attribute(V, :tensor_pure_function) return pure(a)::LieAlgebraModuleElem{C} elseif is_exterior_power(V)[1] || is_symmetric_power(V)[1] || is_tensor_power(V)[1] @@ -536,21 +530,33 @@ function is_dual(V::LieAlgebraModule) end @doc raw""" - is_direct_sum(V::LieAlgebraModule{C}) -> Bool + is_direct_sum(V::LieAlgebraModule{C}) -> Bool, Vector{LieAlgebraModule{C}} Check whether `V` has been constructed as a direct sum of modules. +If it has, return `true` and the summands. +If not, return `false` as the first return value, and an empty vector for the second. """ function is_direct_sum(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :direct_sum + if has_attribute(V, :is_direct_sum) + summands = get_attribute(V, :is_direct_sum)::Vector{typeof(V)} + return (true, summands) + end + return (false, typeof(V)[]) end @doc raw""" - is_tensor_product(V::LieAlgebraModule{C}) -> Bool + is_tensor_product(V::LieAlgebraModule{C}) -> Bool, Vector{LieAlgebraModule{C}} Check whether `V` has been constructed as a tensor product of modules. +If it has, return `true` and the tensor factors. +If not, return `false` as the first return value, and an empty vector for the second. """ function is_tensor_product(V::LieAlgebraModule) - return get_attribute(V, :type, :fallback)::Symbol == :tensor_product + if has_attribute(V, :is_tensor_product) + factors = get_attribute(V, :is_tensor_product)::Vector{typeof(V)} + return (true, factors) + end + return (false, typeof(V)[]) end @doc raw""" @@ -598,22 +604,6 @@ function is_tensor_power(V::LieAlgebraModule) return (false, V, 0) end -@doc raw""" - base_modules(V::LieAlgebraModule{C}) -> Vector{LieAlgebraModule{C}} - -Returns the summands or tensor factors of `V`, -if `V` has been constructed as a direct sum or tensor product of modules. -""" -function base_modules(V::LieAlgebraModule{C}) where {C<:FieldElem} # TODO: remove - if is_tensor_product(V) - return get_attribute(V, :tensor_product)::Vector{LieAlgebraModule{C}} - elseif is_direct_sum(V) - return get_attribute(V, :direct_sum)::Vector{LieAlgebraModule{C}} - else - error("Not a direct sum or tensor product module.") - end -end - ############################################################################### # # Constructor @@ -847,7 +837,7 @@ function direct_sum(V::LieAlgebraModule{C}, Vs::LieAlgebraModule{C}...) where {C direct_sum_V = LieAlgebraModule{C}( L, dim_direct_sum_V, transformation_matrices, s; check=false ) - set_attribute!(direct_sum_V, :type => :direct_sum, :direct_sum => Vs) + set_attribute!(direct_sum_V, :is_direct_sum => Vs) return direct_sum_V end @@ -949,8 +939,7 @@ function tensor_product( set_attribute!( tensor_product_V, - :type => :tensor_product, - :tensor_product => Vs, + :is_tensor_product => Vs, :tensor_pure_function => pure, :tensor_pure_preimage_function => inv_pure, ) diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index f83759eb347..4854e418e60 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -360,8 +360,9 @@ Return the canonical injections from all components into $V$ where $V$ has been constructed as $V_1 \oplus \cdot \oplus V_n$. """ function canonical_injections(V::LieAlgebraModule) - @req is_direct_sum(V) "Module must be a direct sum" - return [canonical_injection(V, i) for i in 1:length(base_modules(V))] + fl, Vs = is_direct_sum(V) + @req fl "Module must be a direct sum" + return [canonical_injection(V, i) for i in 1:length(Vs)] end @doc raw""" @@ -371,9 +372,9 @@ Return the canonical injection $V_i \to V$ where $V$ has been constructed as $V_1 \oplus \cdot \oplus V_n$. """ function canonical_injection(V::LieAlgebraModule, i::Int) - @req is_direct_sum(V) "Module must be a direct sum" - Vs = base_modules(V) - @req 0 < i <= length(Vs) "Index out of bound" + fl, Vs = is_direct_sum(V) + @req fl "Module must be a direct sum" + @req 1 <= i <= length(Vs) "Index out of bound" j = sum(dim(Vs[l]) for l in 1:(i - 1); init=0) emb = hom(Vs[i], V, [basis(V, l + j) for l in 1:dim(Vs[i])]; check=false) return emb @@ -386,8 +387,9 @@ Return the canonical projections from $V$ to all components where $V$ has been constructed as $V_1 \oplus \cdot \oplus V_n$. """ function canonical_projections(V::LieAlgebraModule) - @req is_direct_sum(V) "Module must be a direct sum" - return [canonical_projection(V, i) for i in 1:length(base_modules(V))] + fl, Vs = is_direct_sum(V) + @req fl "Module must be a direct sum" + return [canonical_projection(V, i) for i in 1:length(Vs)] end @doc raw""" @@ -397,9 +399,9 @@ Return the canonical projection $V \to V_i$ where $V$ has been constructed as $V_1 \oplus \cdot \oplus V_n$. """ function canonical_projection(V::LieAlgebraModule, i::Int) - @req is_direct_sum(V) "Module must be a direct sum" - Vs = base_modules(V) - @req 0 < i <= length(Vs) "Index out of bound" + fl, Vs = is_direct_sum(V) + @req fl "Module must be a direct sum" + @req 1 <= i <= length(Vs) "Index out of bound" j = sum(dim(Vs[l]) for l in 1:(i - 1); init=0) proj = hom( V, @@ -428,10 +430,10 @@ If `hs` is a vector, then it is interpreted as a diagonal matrix. function hom_direct_sum( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, hs::Matrix{<:LieAlgebraModuleHom} ) where {C<:FieldElem} - @req is_direct_sum(V) "First module must be a direct sum" - @req is_direct_sum(W) "Second module must be a direct sum" - Vs = base_modules(V) - Ws = base_modules(W) + fl, Vs = is_direct_sum(V) + @req fl "First module must be a direct sum" + fl, Ws = is_direct_sum(W) + @req fl "Second module must be a direct sum" @req length(Vs) == size(hs, 1) "Length mismatch" @req length(Ws) == size(hs, 2) "Length mismatch" @req all( @@ -454,10 +456,10 @@ end function hom_direct_sum( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, hs::Vector{<:LieAlgebraModuleHom} ) where {C<:FieldElem} - @req is_direct_sum(V) "First module must be a direct sum" - @req is_direct_sum(W) "Second module must be a direct sum" - Vs = base_modules(V) - Ws = base_modules(W) + fl, Vs = is_direct_sum(V) + @req fl "First module must be a direct sum" + fl, Ws = is_direct_sum(W) + @req fl "Second module must be a direct sum" @req length(Vs) == length(Ws) == length(hs) "Length mismatch" @req all(i -> domain(hs[i]) === Vs[i] && codomain(hs[i]) === Ws[i], 1:length(hs)) "Domain/codomain mismatch" @@ -477,15 +479,15 @@ This works for $r$th tensor powers as well. function hom_tensor( V::LieAlgebraModule{C}, W::LieAlgebraModule{C}, hs::Vector{<:LieAlgebraModuleHom} ) where {C<:FieldElem} # TODO: cleanup after refactoring tensor_product - if is_tensor_product(V) - Vs = base_modules(V) + if ((fl, Vs) = is_tensor_product(V); fl) + # nothing to do elseif ((fl, Vb, k) = is_tensor_power(V); fl) Vs = [Vb for _ in 1:k] else throw(ArgumentError("First module must be a tensor product or power")) end - if is_tensor_product(W) - Ws = base_modules(W) + if ((fl, Ws) = is_tensor_product(W); fl) + # nothing to do elseif ((fl, Wb, k) = is_tensor_power(W); fl) Ws = [Wb for _ in 1:k] else diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index 316ad8363fa..cf410b508de 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -92,7 +92,6 @@ export WeylGroup, WeylGroupElem export abelian_lie_algebra export abstract_module export base_lie_algebra -export base_modules export bracket export cartan_bilinear_form export cartan_matrix @@ -201,7 +200,6 @@ export WeylGroup, WeylGroupElem export abelian_lie_algebra export abstract_module export base_lie_algebra -export base_modules export bracket export cartan_bilinear_form export cartan_matrix diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 3a7d08af2a6..0f4f00b9df9 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -133,8 +133,8 @@ module_type_bools(V) = ( is_standard_module(V), is_dual(V)[1], - is_direct_sum(V), - is_tensor_product(V), + is_direct_sum(V)[1], + is_tensor_product(V)[1], is_exterior_power(V)[1], is_symmetric_power(V)[1], is_tensor_power(V)[1], @@ -193,7 +193,8 @@ @test_broken ds_V === direct_sum([V for _ in 1:k]...) @test_broken ds_V !== direct_sum([V for _ in 1:k]...; cached=false) @test type_V == module_type_bools(V) # construction of ds_V should not change type of V - @test base_modules(ds_V) == [V for _ in 1:k] + @test is_direct_sum(ds_V) == (true, [V for _ in 1:k]) + @test all(x -> x[1] === x[2], zip(is_direct_sum(ds_V)[2], [V for _ in 1:k])) @test dim(ds_V) == k * dim(V) @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion @@ -212,7 +213,7 @@ ds_V = direct_sum(V1, V2) @test type_V1 == module_type_bools(V1) # construction of ds_V should not change type of V1 @test type_V2 == module_type_bools(V2) # construction of ds_V should not change type of V2 - @test base_modules(ds_V) == [V1, V2] + @test is_direct_sum(ds_V) == (true, [V1, V2]) @test dim(ds_V) == dim(V1) + dim(V2) @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion @@ -234,7 +235,8 @@ @test_broken tp_V === tensor_product([V for _ in 1:k]...) @test_broken tp_V !== tensor_product([V for _ in 1:k]...; cached=false) @test type_V == module_type_bools(V) # construction of tp_V should not change type of V - @test base_modules(tp_V) == [V for _ in 1:k] + @test is_tensor_product(tp_V) == (true, [V for _ in 1:k]) + @test all(x -> x[1] === x[2], zip(is_tensor_product(tp_V)[2], [V for _ in 1:k])) @test dim(tp_V) == dim(V)^k @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion @@ -255,7 +257,7 @@ tp_V = tensor_product(V1, V2) @test type_V1 == module_type_bools(V1) # construction of tp_V should not change type of V1 @test type_V2 == module_type_bools(V2) # construction of tp_V should not change type of V2 - @test base_modules(tp_V) == [V1, V2] + @test is_tensor_product(tp_V) == (true, [V1, V2]) @test dim(tp_V) == dim(V1) * dim(V2) @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion From f8c92ad58b07ef96f66f599dc97bb563d7441d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 20 Dec 2023 15:34:18 +0100 Subject: [PATCH 11/17] Streamline some caching direct sum and tensor product still missing --- .../LieAlgebras/src/LieAlgebraModule.jl | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index d7f7c41f597..84abf7b20cc 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -670,6 +670,8 @@ function abstract_module( return LieAlgebraModule{C}(L, dimV, transformation_matrices, Symbol.(s); check) end +######################## Trivial module ######################## + @doc raw""" trivial_module(L::LieAlgebra{C}, d=1) -> LieAlgebraModule{C} @@ -695,10 +697,18 @@ function trivial_module(L::LieAlgebra, d::IntegerUnion=1) triv_V = LieAlgebraModule{elem_type(coefficient_ring(L))}( L, dim_triv_V, transformation_matrices, s; check=false ) - # set_attribute!(triv_V, :type => :trivial) return triv_V end +######################## Standard module ######################## + +# cache storage +@attr LieAlgebraModule{elem_type(coefficient_ring(L))} function _standard_module( + L::LinearLieAlgebra +) + return standard_module(L; cached=false) +end + @doc raw""" standard_module(L::LinearLieAlgebra{C}; cached::Bool=true) -> LieAlgebraModule{C} @@ -710,7 +720,6 @@ is $R^n$ with the action of $L$ given by left multiplication. This uses the left action of $L$, and converts that to internally use the equivalent right action. - # Examples ```jldoctest julia> L = special_linear_lie_algebra(QQ, 3); @@ -722,11 +731,7 @@ over special linear Lie algebra of degree 3 over QQ ``` """ function standard_module(L::LinearLieAlgebra; cached::Bool=true) - if cached && has_attribute(L, :standard_module) - return get_attribute( - L, :standard_module - )::LieAlgebraModule{elem_type(coefficient_ring(L))} - end + cached && return _standard_module(L) dim_std_V = L.n transformation_matrices = transpose.(matrix_repr_basis(L)) @@ -736,11 +741,16 @@ function standard_module(L::LinearLieAlgebra; cached::Bool=true) ) set_attribute!(std_V, :is_standard_module => true) - cached && (set_attribute!(L, :standard_module => std_V)) - return std_V end +######################## Duals ######################## + +# cache storage +@attr typeof(V) function _dual(V::LieAlgebraModule) + return dual(V; cached=false) +end + @doc raw""" dual(V::LieAlgebraModule{C}; cached::Bool=true) -> LieAlgebraModule{C} @@ -762,9 +772,7 @@ over special linear Lie algebra of degree 3 over QQ ``` """ function dual(V::LieAlgebraModule{C}; cached::Bool=true) where {C<:FieldElem} - if cached && has_attribute(V, :dual) - return get_attribute(V, :dual)::typeof(V) - end + cached && return _dual(V) L = base_lie_algebra(V) dim_dual_V = dim(V) @@ -782,13 +790,14 @@ function dual(V::LieAlgebraModule{C}; cached::Bool=true) where {C<:FieldElem} pow_V = LieAlgebraModule{C}(L, dim_dual_V, transformation_matrices, s; check=false) set_attribute!(pow_V, :is_dual => V) - cached && (set_attribute!(V, :dual => pow_V)) - return pow_V end Base.:^(V::LieAlgebraModule, ::typeof(Base.:*)) = dual(V) +######################## Direct sums ######################## + +# TODO: add caching @doc raw""" direct_sum(V::LieAlgebraModule{C}...) -> LieAlgebraModule{C} ⊕(V::LieAlgebraModule{C}...) -> LieAlgebraModule{C} @@ -844,6 +853,9 @@ end ⊕(V::LieAlgebraModule{C}, Vs::LieAlgebraModule{C}...) where {C<:FieldElem} = direct_sum(V, Vs...) +######################## Tensor products ######################## + +# TODO: add caching @doc raw""" tensor_product(Vs::LieAlgebraModule{C}...) -> LieAlgebraModule{C} ⊗(Vs::LieAlgebraModule{C}...) -> LieAlgebraModule{C} @@ -950,6 +962,13 @@ end ⊗(V::LieAlgebraModule{C}, Vs::LieAlgebraModule{C}...) where {C<:FieldElem} = tensor_product(V, Vs...) +######################## Exterior powers ######################## + +# cache storage +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _exterior_powers(V::LieAlgebraModule) + return Dict{Int,Tuple{typeof(V),MapFromFunc}}() +end + @doc raw""" exterior_power(V::LieAlgebraModule{C}, k::Int; cached::Bool=true) -> LieAlgebraModule{C}, Map @@ -1062,7 +1081,10 @@ function exterior_power( return E, mult_map end -@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _exterior_powers(V::LieAlgebraModule) +######################## Symmetric powers ######################## + +# cache storage +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _symmetric_powers(V::LieAlgebraModule) return Dict{Int,Tuple{typeof(V),MapFromFunc}}() end @@ -1199,7 +1221,10 @@ function symmetric_power( return S, mult_map end -@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _symmetric_powers(V::LieAlgebraModule) +######################## Tensor powers ######################## + +# cache storage +@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _tensor_powers(V::LieAlgebraModule) return Dict{Int,Tuple{typeof(V),MapFromFunc}}() end @@ -1309,10 +1334,6 @@ function tensor_power( return T, mult_map end -@attr Dict{Int,Tuple{typeof(V),MapFromFunc}} function _tensor_powers(V::LieAlgebraModule) - return Dict{Int,Tuple{typeof(V),MapFromFunc}}() -end - ############################################################################### # # Simple modules (via highest weight) of semisimple Lie algebras From 4375462502689cf82dc0e8cb62d061386a24f1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 21 Dec 2023 12:24:49 +0100 Subject: [PATCH 12/17] Diversify module conformance tests --- .../LieAlgebras/src/LieAlgebraModule.jl | 22 +- .../LieAlgebras/test/LieAlgebraModule-test.jl | 543 ++++++++++-------- 2 files changed, 315 insertions(+), 250 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 84abf7b20cc..157fb68b9cc 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -997,6 +997,7 @@ function exterior_power( V::LieAlgebraModule{C}, k::Int; cached::Bool=true ) where {C<:FieldElem} @req k >= 0 "Non-negative exponent needed" + @req characteristic(coefficient_ring(V)) == 0 "Characteristic must be zero." if cached powers = _exterior_powers(V) @@ -1008,13 +1009,13 @@ function exterior_power( dim_E = binomial(dim(V), k) ind_map = collect(combinations(1:dim(V), k)) - T, _ = tensor_power(V, k) - E_to_T_mat = zero_matrix(coefficient_ring(V), dim_E, dim(T)) - T_to_E_mat = zero_matrix(coefficient_ring(V), dim(T), dim_E) + T, _ = tensor_power(V, k; cached) + E_to_T_mat = zero_matrix(R, dim_E, dim(T)) + T_to_E_mat = zero_matrix(R, dim(T), dim_E) for (i, _inds) in enumerate(ind_map), (inds, sgn) in permutations_with_sign(_inds) j = 1 + sum((ind - 1) * dim(V)^(k - 1) for (k, ind) in enumerate(reverse(inds)); init=0) - E_to_T_mat[i, j] = sgn//factorial(k) - T_to_E_mat[j, i] = sgn + E_to_T_mat[i, j] = R(sgn)//factorial(k) + T_to_E_mat[j, i] = R(sgn) end transformation_matrices = map(1:dim(L)) do i E_to_T_mat * transformation_matrix(T, i) * T_to_E_mat @@ -1116,6 +1117,7 @@ function symmetric_power( V::LieAlgebraModule{C}, k::Int; cached::Bool=true ) where {C<:FieldElem} @req k >= 0 "Non-negative exponent needed" + @req characteristic(coefficient_ring(V)) == 0 "Characteristic must be zero." if cached powers = _symmetric_powers(V) @@ -1127,13 +1129,13 @@ function symmetric_power( dim_S = binomial(dim(V) + k - 1, k) ind_map = collect(multicombinations(1:dim(V), k)) - T, _ = tensor_power(V, k) - S_to_T_mat = zero_matrix(coefficient_ring(V), dim_S, dim(T)) - T_to_S_mat = zero_matrix(coefficient_ring(V), dim(T), dim_S) + T, _ = tensor_power(V, k; cached) + S_to_T_mat = zero_matrix(R, dim_S, dim(T)) + T_to_S_mat = zero_matrix(R, dim(T), dim_S) for (i, _inds) in enumerate(ind_map), inds in permutations(_inds) j = 1 + sum((ind - 1) * dim(V)^(k - 1) for (k, ind) in enumerate(reverse(inds)); init=0) - S_to_T_mat[i, j] += 1//factorial(k) - T_to_S_mat[j, i] = 1 + S_to_T_mat[i, j] += R(1)//factorial(k) + T_to_S_mat[j, i] = R(1) end transformation_matrices = map(1:dim(L)) do i S_to_T_mat * transformation_matrix(T, i) * T_to_S_mat diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 0f4f00b9df9..fc50ffdcc3c 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -57,6 +57,24 @@ lie_algebra_module_conformance_test(L, V) end + @testset "V of so_4(CF(4))" begin + L = special_orthogonal_lie_algebra(cyclotomic_field(4)[1], 4) + V = standard_module(L) + lie_algebra_module_conformance_test(L, V) + end + + @testset "V of sl_4(GF(3))" begin + L = special_linear_lie_algebra(GF(3), 4) + V = standard_module(L) + lie_algebra_module_conformance_test(L, V) + end + + @testset "V of so_4(GF(2^3))" begin + L = special_orthogonal_lie_algebra(GF(2, 3), 4) + V = standard_module(L) + lie_algebra_module_conformance_test(L, V) + end + @testset "V^* of sl_4(QQ)" begin L = special_linear_lie_algebra(QQ, 4) V = dual(standard_module(L)) @@ -69,9 +87,41 @@ lie_algebra_module_conformance_test(L, V) end - @testset "V of so_4(CF(4))" begin - L = special_orthogonal_lie_algebra(cyclotomic_field(4)[1], 4) - V = standard_module(L) + @testset "⊕(T^2 V) of sl_4(GF(5))" begin + L = special_linear_lie_algebra(GF(5), 4) + V = direct_sum(tensor_power(standard_module(L), 2)[1]) + lie_algebra_module_conformance_test(L, V) + end + + @testset "S^2 V ⊕ V^* of sl_4(QQ)" begin + L = special_linear_lie_algebra(QQ, 4) + V = direct_sum(symmetric_power(standard_module(L), 2)[1], dual(standard_module(L))) + lie_algebra_module_conformance_test(L, V) + end + + @testset "V ⊕ V ⊕ V of sl_4(CF(4))" begin + L = special_linear_lie_algebra(cyclotomic_field(4)[1], 4) + V = direct_sum(standard_module(L), standard_module(L), standard_module(L)) + lie_algebra_module_conformance_test(L, V) + end + + @testset "⊗(T^2 V) of sl_4(QQ)" begin + L = special_linear_lie_algebra(QQ, 4) + V = tensor_product(tensor_power(standard_module(L), 2)[1]) + lie_algebra_module_conformance_test(L, V) + end + + @testset "S^2 V ⊗ V^* of sl_4(QQ)" begin + L = special_linear_lie_algebra(QQ, 4) + V = tensor_product( + symmetric_power(standard_module(L), 2)[1], dual(standard_module(L)) + ) + lie_algebra_module_conformance_test(L, V) + end + + @testset "V ⊗ V ⊗ V of sl_4(GF(3^2))" begin + L = special_linear_lie_algebra(GF(3, 2), 4) + V = tensor_product(standard_module(L), standard_module(L), standard_module(L)) lie_algebra_module_conformance_test(L, V) end @@ -117,14 +167,14 @@ lie_algebra_module_conformance_test(L, V) end - @testset "T^2 S^2 V of sl_4(QQ)" begin - L = special_linear_lie_algebra(QQ, 4) + @testset "T^2 S^2 V of sl_4(CF(4))" begin + L = special_linear_lie_algebra(cyclotomic_field(4)[1], 4) V = tensor_power(symmetric_power(standard_module(L), 2)[1], 2)[1] lie_algebra_module_conformance_test(L, V) end - @testset "T^2 V of sl_4(CF(4))" begin - L = special_linear_lie_algebra(cyclotomic_field(4)[1], 4) + @testset "T^2 V of sl_4(GF(2^3))" begin + L = special_linear_lie_algebra(GF(2, 3), 4) V = tensor_power(standard_module(L), 2)[1] lie_algebra_module_conformance_test(L, V) end @@ -140,263 +190,276 @@ is_tensor_power(V)[1], ) - @testset "standard_module" begin - for (L, dimV) in - [(general_linear_lie_algebra(QQ, 3), 3), (special_orthogonal_lie_algebra(QQ, 4), 4)] - V = standard_module(L) - @test V === standard_module(L) - @test V !== standard_module(L; cached=false) - @test dim(V) == dimV - @test length(repr(V)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(V) == (true, false, false, false, false, false, false) # standard_module + @testset "module constructions" begin + @testset for R in [QQ, cyclotomic_field(4)[1], GF(3), GF(2, 3)] + @testset for L in [special_linear_lie_algebra(R, 3)] + @testset "standard_module" begin + V = standard_module(L) + @test V === standard_module(L) + @test V !== standard_module(L; cached=false) + @test dim(V) == L.n + @test length(repr(V)) < 10^4 # outputs tend to be excessively long due to recursion - x = L(rand(-10:10, dim(L))) - v = V(rand(-10:10, dim(V))) - @test x * v == V(matrix_repr(x) * coefficients(v)) - end - end + @test module_type_bools(V) == (true, false, false, false, false, false, false) # standard_module - @testset "dual" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2)[1] - type_V = module_type_bools(V) - - dual_V = dual(V) - @test dual_V === dual(V) - @test dual_V !== dual(V; cached=false) - @test type_V == module_type_bools(V) # construction of dual_V should not change type of V - @test is_dual(dual_V) === (true, V) - @test dim(dual_V) == dim(V) - @test length(repr(dual_V)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(dual_V) == (false, true, false, false, false, false, false) # dual - - dual_dual_V = dual(dual_V) - @test dim(dual_dual_V) == dim(V) - @test all( - i -> - Oscar.LieAlgebras.transformation_matrix(dual_dual_V, i) == - Oscar.LieAlgebras.transformation_matrix(V, i), - 1:dim(L), - ) - end - end - - @testset "direct_sum" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2)[1] - type_V = module_type_bools(V) - - for k in 1:3 - ds_V = direct_sum([V for _ in 1:k]...) - @test_broken ds_V === direct_sum([V for _ in 1:k]...) - @test_broken ds_V !== direct_sum([V for _ in 1:k]...; cached=false) - @test type_V == module_type_bools(V) # construction of ds_V should not change type of V - @test is_direct_sum(ds_V) == (true, [V for _ in 1:k]) - @test all(x -> x[1] === x[2], zip(is_direct_sum(ds_V)[2], [V for _ in 1:k])) - @test dim(ds_V) == k * dim(V) - @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(ds_V) == (false, false, true, false, false, false, false) # direct_sum - - x = L(rand(-10:10, dim(L))) - a = [V(rand(-10:10, dim(V))) for _ in 1:k] - @test ds_V([x * v for v in a]) == x * ds_V(a) - end - - V1 = symmetric_power(standard_module(L), 2)[1] - V2 = exterior_power(standard_module(L), 2)[1] - type_V1 = module_type_bools(V1) - type_V2 = module_type_bools(V2) - - ds_V = direct_sum(V1, V2) - @test type_V1 == module_type_bools(V1) # construction of ds_V should not change type of V1 - @test type_V2 == module_type_bools(V2) # construction of ds_V should not change type of V2 - @test is_direct_sum(ds_V) == (true, [V1, V2]) - @test dim(ds_V) == dim(V1) + dim(V2) - @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(ds_V) == (false, false, true, false, false, false, false) # direct_sum + x = L(rand(-10:10, dim(L))) + v = V(rand(-10:10, dim(V))) + @test x * v == V(matrix_repr(x) * coefficients(v)) + end - x = L(rand(-10:10, dim(L))) - a = [Vi(rand(-10:10, dim(Vi))) for Vi in [V1, V2]] - @test ds_V([x * v for v in a]) == x * ds_V(a) - end - end + @testset "dual" begin + V = direct_sum(standard_module(L), tensor_power(standard_module(L), 2)[1]) + type_V = module_type_bools(V) + + dual_V = dual(V) + @test dual_V === dual(V) + @test dual_V !== dual(V; cached=false) + @test type_V == module_type_bools(V) # construction of dual_V should not change type of V + @test is_dual(dual_V) === (true, V) + @test dim(dual_V) == dim(V) + @test length(repr(dual_V)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(dual_V) == + (false, true, false, false, false, false, false) # dual + + dual_dual_V = dual(dual_V) + @test dim(dual_dual_V) == dim(V) + @test all( + i -> + Oscar.LieAlgebras.transformation_matrix(dual_dual_V, i) == + Oscar.LieAlgebras.transformation_matrix(V, i), + 1:dim(L), + ) + end - @testset "tensor_product" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2)[1] - type_V = module_type_bools(V) + @testset "direct_sum" begin + V = tensor_power(standard_module(L), 2)[1] + type_V = module_type_bools(V) + + for k in 1:3 + ds_V = direct_sum([V for _ in 1:k]...) + @test_broken ds_V === direct_sum([V for _ in 1:k]...) + @test_broken ds_V !== direct_sum([V for _ in 1:k]...; cached=false) + @test type_V == module_type_bools(V) # construction of ds_V should not change type of V + @test is_direct_sum(ds_V) == (true, [V for _ in 1:k]) + @test all(x -> x[1] === x[2], zip(is_direct_sum(ds_V)[2], [V for _ in 1:k])) + @test dim(ds_V) == k * dim(V) + @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(ds_V) == + (false, false, true, false, false, false, false) # direct_sum + + x = L(rand(-10:10, dim(L))) + a = [V(rand(-10:10, dim(V))) for _ in 1:k] + @test ds_V([x * v for v in a]) == x * ds_V(a) + end + + V1 = dual(standard_module(L)) + V2 = tensor_power(standard_module(L), 2)[1] + type_V1 = module_type_bools(V1) + type_V2 = module_type_bools(V2) + + ds_V = direct_sum(V1, V2) + @test type_V1 == module_type_bools(V1) # construction of ds_V should not change type of V1 + @test type_V2 == module_type_bools(V2) # construction of ds_V should not change type of V2 + @test is_direct_sum(ds_V) == (true, [V1, V2]) + @test dim(ds_V) == dim(V1) + dim(V2) + @test length(repr(ds_V)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(ds_V) == (false, false, true, false, false, false, false) # direct_sum - for k in 1:3 - tp_V = tensor_product([V for _ in 1:k]...) - @test_broken tp_V === tensor_product([V for _ in 1:k]...) - @test_broken tp_V !== tensor_product([V for _ in 1:k]...; cached=false) - @test type_V == module_type_bools(V) # construction of tp_V should not change type of V - @test is_tensor_product(tp_V) == (true, [V for _ in 1:k]) - @test all(x -> x[1] === x[2], zip(is_tensor_product(tp_V)[2], [V for _ in 1:k])) - @test dim(tp_V) == dim(V)^k - @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion + x = L(rand(-10:10, dim(L))) + a = [Vi(rand(-10:10, dim(Vi))) for Vi in [V1, V2]] + @test ds_V([x * v for v in a]) == x * ds_V(a) + end - @test module_type_bools(tp_V) == (false, false, false, true, false, false, false) # tensor_product + @testset "tensor_product" begin + V = direct_sum(standard_module(L), dual(standard_module(L))) + type_V = module_type_bools(V) - x = L(rand(-10:10, dim(L))) - a = [V(rand(-10:10, dim(V))) for _ in 1:k] - @test sum(tp_V([i == j ? x * v : v for (j, v) in enumerate(a)]) for i in 1:k) == x * tp_V(a) + for k in 1:3 + tp_V = tensor_product([V for _ in 1:k]...) + @test_broken tp_V === tensor_product([V for _ in 1:k]...) + @test_broken tp_V !== tensor_product([V for _ in 1:k]...; cached=false) + @test type_V == module_type_bools(V) # construction of tp_V should not change type of V + @test is_tensor_product(tp_V) == (true, [V for _ in 1:k]) + @test all(x -> x[1] === x[2], zip(is_tensor_product(tp_V)[2], [V for _ in 1:k])) + @test dim(tp_V) == dim(V)^k + @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion - @test tp_V == tensor_power(V, k)[1] - end + @test module_type_bools(tp_V) == + (false, false, false, true, false, false, false) # tensor_product - V1 = symmetric_power(standard_module(L), 2)[1] - V2 = exterior_power(standard_module(L), 2)[1] - type_V1 = module_type_bools(V1) - type_V2 = module_type_bools(V2) + x = L(rand(-10:10, dim(L))) + a = [V(rand(-10:10, dim(V))) for _ in 1:k] + @test sum(tp_V([i == j ? x * v : v for (j, v) in enumerate(a)]) for i in 1:k) == x * tp_V(a) - tp_V = tensor_product(V1, V2) - @test type_V1 == module_type_bools(V1) # construction of tp_V should not change type of V1 - @test type_V2 == module_type_bools(V2) # construction of tp_V should not change type of V2 - @test is_tensor_product(tp_V) == (true, [V1, V2]) - @test dim(tp_V) == dim(V1) * dim(V2) - @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion + @test tp_V == tensor_power(V, k)[1] + end - @test module_type_bools(tp_V) == (false, false, false, true, false, false, false) # tensor_product + V1 = dual(standard_module(L)) + V2 = tensor_power(standard_module(L), 2)[1] + type_V1 = module_type_bools(V1) + type_V2 = module_type_bools(V2) - x = L(rand(-10:10, dim(L))) - a = [Vi(rand(-10:10, dim(Vi))) for Vi in [V1, V2]] - @test sum(tp_V([i == j ? x * v : v for (j, v) in enumerate(a)]) for i in 1:2) == x * tp_V(a) - end - end + tp_V = tensor_product(V1, V2) + @test type_V1 == module_type_bools(V1) # construction of tp_V should not change type of V1 + @test type_V2 == module_type_bools(V2) # construction of tp_V should not change type of V2 + @test is_tensor_product(tp_V) == (true, [V1, V2]) + @test dim(tp_V) == dim(V1) * dim(V2) + @test length(repr(tp_V)) < 10^4 # outputs tend to be excessively long due to recursion - @testset "exterior_power" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = symmetric_power(standard_module(L), 2)[1] - type_V = module_type_bools(V) + @test module_type_bools(tp_V) == (false, false, false, true, false, false, false) # tensor_product - for k in 1:3 - E, map = exterior_power(V, k) - @test E === exterior_power(V, k)[1] - @test E !== exterior_power(V, k; cached=false)[1] - @test type_V == module_type_bools(V) # construction of E should not change type of V - @test is_exterior_power(E) === (true, V, k) - @test dim(E) == binomial(dim(V), k) - @test length(repr(E)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(E) == (false, false, false, false, true, false, false) # exterior_power - - if k == 1 x = L(rand(-10:10, dim(L))) - a = V(rand(-10:10, dim(V))) - # @test E(x * a) == x * E(a) # TODO: fix this - @test E([x * a]) == x * E([a]) - elseif k == 2 - a = V(rand(-10:10, dim(V))) - b = V(rand(-10:10, dim(V))) - @test !iszero(E(a, b)) - @test iszero(E(a, b) + E(b, a)) - @test !iszero(E(a, b) - E(b, a)) - @test iszero(E(a, a)) + a = [Vi(rand(-10:10, dim(Vi))) for Vi in [V1, V2]] + @test sum(tp_V([i == j ? x * v : v for (j, v) in enumerate(a)]) for i in 1:2) == x * tp_V(a) end - as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) - @test E(as) == map(as) - - T = get_attribute(E, :embedding_tensor_power) - E_to_T = get_attribute(E, :embedding_tensor_power_embedding) - T_to_E = get_attribute(E, :embedding_tensor_power_projection) - @test T == tensor_power(V, k)[1] - @test domain(E_to_T) === E - @test codomain(E_to_T) === T - @test domain(T_to_E) === T - @test codomain(T_to_E) === E - @test compose(E_to_T, T_to_E) == identity_map(E) - end - end - end - - @testset "symmetric_power" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = exterior_power(standard_module(L), 2)[1] - type_V = module_type_bools(V) - - for k in 1:3 - S, map = symmetric_power(V, k) - @test S === symmetric_power(V, k)[1] - @test S !== symmetric_power(V, k; cached=false)[1] - @test type_V == module_type_bools(V) # construction of S should not change type of V - @test is_symmetric_power(S) === (true, V, k) - @test dim(S) == binomial(dim(V) + k - 1, k) - @test length(repr(S)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(S) == (false, false, false, false, false, true, false) # symmetric_power - - if k == 1 - x = L(rand(-10:10, dim(L))) - a = V(rand(-10:10, dim(V))) - # @test S(x * a) == x * S(a) # TODO: fix this - @test S([x * a]) == x * S([a]) - elseif k == 2 - a = V(rand(-10:10, dim(V))) - b = V(rand(-10:10, dim(V))) - @test !iszero(S(a, b)) - @test !iszero(S(a, b) + S(b, a)) - @test iszero(S(a, b) - S(b, a)) - @test !iszero(S(a, a)) + @testset "exterior_power" begin + if characteristic(R) != 0 + @test_throws ArgumentError symmetric_power(standard_module(L), 2)[1] + else + V = symmetric_power(standard_module(L), 2)[1] + type_V = module_type_bools(V) + + for k in 1:3 + E, map = exterior_power(V, k) + @test E === exterior_power(V, k)[1] + @test E !== exterior_power(V, k; cached=false)[1] + @test type_V == module_type_bools(V) # construction of E should not change type of V + @test is_exterior_power(E) === (true, V, k) + @test dim(E) == binomial(dim(V), k) + @test length(repr(E)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(E) == (false, false, false, false, true, false, false) # exterior_power + + if k == 1 + x = L(rand(-10:10, dim(L))) + a = V(rand(-10:10, dim(V))) + # @test E(x * a) == x * E(a) # TODO: fix this + @test E([x * a]) == x * E([a]) + elseif k == 2 + a = V() + b = V() + while iszero(a) || iszero(b) || a == b + a = V(rand(-10:10, dim(V))) + b = V(rand(-10:10, dim(V))) + end + @test !iszero(E(a, b)) + @test iszero(E(a, b) + E(b, a)) + @test !iszero(E(a, b) - E(b, a)) + @test iszero(E(a, a)) + end + + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test E(as) == map(as) + + T = get_attribute(E, :embedding_tensor_power) + E_to_T = get_attribute(E, :embedding_tensor_power_embedding) + T_to_E = get_attribute(E, :embedding_tensor_power_projection) + @test T == tensor_power(V, k)[1] + @test domain(E_to_T) === E + @test codomain(E_to_T) === T + @test domain(T_to_E) === T + @test codomain(T_to_E) === E + @test compose(E_to_T, T_to_E) == identity_map(E) + end + end end - as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) - @test S(as) == map(as) - - T = get_attribute(S, :embedding_tensor_power) - S_to_T = get_attribute(S, :embedding_tensor_power_embedding) - T_to_S = get_attribute(S, :embedding_tensor_power_projection) - @test T == tensor_power(V, k)[1] - @test domain(S_to_T) === S - @test codomain(S_to_T) === T - @test domain(T_to_S) === T - @test codomain(T_to_S) === S - @test compose(S_to_T, T_to_S) == identity_map(S) - end - end - end - - @testset "tensor_power" begin - for L in [general_linear_lie_algebra(QQ, 3), special_orthogonal_lie_algebra(QQ, 4)] - V = standard_module(L) - type_V = module_type_bools(V) - - for k in 1:3 - T, map = tensor_power(V, k) - @test T === tensor_power(V, k)[1] - @test T !== tensor_power(V, k; cached=false)[1] - @test type_V == module_type_bools(V) # construction of T should not change type of V - @test is_tensor_power(T) === (true, V, k) - @test dim(T) == dim(V)^k - @test length(repr(T)) < 10^4 # outputs tend to be excessively long due to recursion - - @test module_type_bools(T) == (false, false, false, false, false, false, true) # tensor_power - - if k == 1 - x = L(rand(-10:10, dim(L))) - a = V(rand(-10:10, dim(V))) - # @test T(x * a) == x * T(a) # TODO: fix this - @test T([x * a]) == x * T([a]) - elseif k == 2 - a = V(rand(-10:10, dim(V))) - b = V(rand(-10:10, dim(V))) - @test !iszero(T(a, b)) - @test !iszero(T(a, b) + T(b, a)) - @test !iszero(T(a, b) - T(b, a)) - @test !iszero(T(a, a)) + @testset "symmetric_power" begin + if characteristic(R) != 0 + @test_throws ArgumentError exterior_power(standard_module(L), 2)[1] + else + V = exterior_power(standard_module(L), 2)[1] + type_V = module_type_bools(V) + + for k in 1:3 + S, map = symmetric_power(V, k) + @test S === symmetric_power(V, k)[1] + @test S !== symmetric_power(V, k; cached=false)[1] + @test type_V == module_type_bools(V) # construction of S should not change type of V + @test is_symmetric_power(S) === (true, V, k) + @test dim(S) == binomial(dim(V) + k - 1, k) + @test length(repr(S)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(S) == (false, false, false, false, false, true, false) # symmetric_power + + if k == 1 + x = L(rand(-10:10, dim(L))) + a = V(rand(-10:10, dim(V))) + # @test S(x * a) == x * S(a) # TODO: fix this + @test S([x * a]) == x * S([a]) + elseif k == 2 + a = V() + b = V() + while iszero(a) || iszero(b) || a == b + a = V(rand(-10:10, dim(V))) + b = V(rand(-10:10, dim(V))) + end + @test !iszero(S(a, b)) + @test !iszero(S(a, b) + S(b, a)) + @test iszero(S(a, b) - S(b, a)) + @test !iszero(S(a, a)) + end + + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test S(as) == map(as) + + T = get_attribute(S, :embedding_tensor_power) + S_to_T = get_attribute(S, :embedding_tensor_power_embedding) + T_to_S = get_attribute(S, :embedding_tensor_power_projection) + @test T == tensor_power(V, k)[1] + @test domain(S_to_T) === S + @test codomain(S_to_T) === T + @test domain(T_to_S) === T + @test codomain(T_to_S) === S + @test compose(S_to_T, T_to_S) == identity_map(S) + end + end end - as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) - @test T(as) == map(as) + @testset "tensor_power" begin + V = standard_module(L) + type_V = module_type_bools(V) + + for k in 1:3 + T, map = tensor_power(V, k) + @test T === tensor_power(V, k)[1] + @test T !== tensor_power(V, k; cached=false)[1] + @test type_V == module_type_bools(V) # construction of T should not change type of V + @test is_tensor_power(T) === (true, V, k) + @test dim(T) == dim(V)^k + @test length(repr(T)) < 10^4 # outputs tend to be excessively long due to recursion + + @test module_type_bools(T) == (false, false, false, false, false, false, true) # tensor_power + + if k == 1 + x = L(rand(-10:10, dim(L))) + a = V(rand(-10:10, dim(V))) + # @test T(x * a) == x * T(a) # TODO: fix this + @test T([x * a]) == x * T([a]) + elseif k == 2 + a = V() + b = V() + while iszero(a) || iszero(b) || a == b + a = V(rand(-10:10, dim(V))) + b = V(rand(-10:10, dim(V))) + end + @test !iszero(T(a, b)) + @test !iszero(T(a, b) + T(b, a)) + @test !iszero(T(a, b) - T(b, a)) + @test !iszero(T(a, a)) + end + + as = NTuple{k,elem_type(V)}(V(rand(-10:10, dim(V))) for _ in 1:k) + @test T(as) == map(as) + end + end end end end - @testset "so_n correctness regression" begin function lie_algebra_module_struct_const( L::LieAlgebra{C}, V::LieAlgebraModule{C} From 5bd052fa7fa7585bc5febbe00ac593e03a8e1ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 21 Dec 2023 17:38:19 +0100 Subject: [PATCH 13/17] Streamline parent object calling --- .../LieAlgebras/src/LieAlgebraModule.jl | 92 +++++++++++-------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 157fb68b9cc..9434d390a4e 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -325,15 +325,14 @@ Return `v`. Fails, in general, if `v` is not an element of `V`. If `V` is the dual module of the parent of `v`, return the dual of `v`. """ function (V::LieAlgebraModule{C})(v::LieAlgebraModuleElem{C}) where {C<:FieldElem} - if ((fl, W) = is_dual(V); fl) && W === parent(v) - return V(coefficients(v)) - end - @req V === parent(v) "Incompatible modules." - return v + # handled by more general function below + return V((v,))::elem_type(V) end @doc raw""" (V::LieAlgebraModule{C})(a::Vector{T}) where {T<:LieAlgebraModuleElem{C}}) -> LieAlgebraModuleElem{C} + (V::LieAlgebraModule{C})(a::NTuple{T}) where {T<:LieAlgebraModuleElem{C}}) -> LieAlgebraModuleElem{C} + (V::LieAlgebraModule{C})(a::T...) where {T<:LieAlgebraModuleElem{C}}) -> LieAlgebraModuleElem{C} If `V` is a direct sum, return its element, where the $i$-th component is equal to `a[i]`. If `V` is a tensor product, return the tensor product of the `a[i]`. @@ -345,35 +344,36 @@ where _correct_ depends on the case above. """ function (V::LieAlgebraModule{C})( a::Vector{T} -) where {T<:LieAlgebraModuleElem{C}} where {C<:FieldElem} - if ((fl, Vs) = is_direct_sum(V); fl) - @req length(a) == length(Vs) "Length of vector does not match." - @req all(i -> parent(a[i]) === Vs[i], 1:length(a)) "Incompatible modules." - return sum(inji(ai) for (ai, inji) in zip(a, canonical_injections(V)); init=zero(V)) - elseif is_tensor_product(V)[1] - pure = get_attribute(V, :tensor_pure_function) - return pure(a)::LieAlgebraModuleElem{C} - elseif is_exterior_power(V)[1] || is_symmetric_power(V)[1] || is_tensor_power(V)[1] - return V(Tuple(a))::LieAlgebraModuleElem{C} - else - throw(ArgumentError("Invalid input.")) - end +) where {C<:FieldElem,T<:LieAlgebraModuleElem{C}} + @req _is_allowed_input_length(V, length(a)) "Invalid input length." # Check here to not compile unnecessary dispatches on tuples + return V(Tuple(a))::elem_type(V) end function (V::LieAlgebraModule{C})( a::Tuple{Vararg{T}} -) where {T<:LieAlgebraModuleElem{C}} where {C<:FieldElem} - if is_exterior_power(V)[1] +) where {C<:FieldElem,T<:LieAlgebraModuleElem{C}} + if length(a) == 1 && V === parent(a[1]) + return a[1] + elseif ((fl, W) = is_dual(V); fl) + @req length(a) == 1 "Invalid input length." + @req W === parent(v) "Incompatible modules." + return V(coefficients(v)) + elseif ((fl, Vs) = is_direct_sum(V); fl) + @req length(a) == length(Vs) "Invalid input length." + @req all(i -> parent(a[i]) === Vs[i], 1:length(a)) "Incompatible modules." + return sum(inji(ai) for (ai, inji) in zip(a, canonical_injections(V)); init=zero(V)) + elseif is_tensor_product(V)[1] + pure = get_attribute(V, :tensor_pure_function) + return pure(a)::elem_type(V) + elseif is_exterior_power(V)[1] pure = get_attribute(V, :wedge_pure_function) - return pure(a)::LieAlgebraModuleElem{C} + return pure(a)::elem_type(V) elseif is_symmetric_power(V)[1] pure = get_attribute(V, :mult_pure_function) - return pure(a)::LieAlgebraModuleElem{C} + return pure(a)::elem_type(V) elseif is_tensor_power(V)[1] pure = get_attribute(V, :tensor_pure_function) - return pure(a)::LieAlgebraModuleElem{C} - elseif isempty(a) # TODO: Remove case - return V(LieAlgebraModuleElem{C}[]) + return pure(a)::elem_type(V) else throw(ArgumentError("Invalid input.")) end @@ -383,6 +383,22 @@ function (V::LieAlgebraModule{C})(a::LieAlgebraModuleElem{C}...) where {C<:Field return V(a) end +function _is_allowed_input_length(V::LieAlgebraModule, a::Int) + if ((fl, Vs) = is_direct_sum(V); fl) + return a == length(Vs) + elseif ((fl, Vs) = is_tensor_product(V); fl) + return a == length(Vs) + elseif ((fl, W, k) = is_exterior_power(V); fl) + return a == k + elseif ((fl, W, k) = is_symmetric_power(V); fl) + return a == k + elseif ((fl, W, k) = is_tensor_power(V); fl) + return a == k + else + throw(ArgumentError("Invalid input.")) + end +end + ############################################################################### # # Arithmetic operations @@ -921,7 +937,7 @@ function tensor_product( L, dim_tensor_product_V, transformation_matrices, s; check=false ) - function pure(as::LieAlgebraModuleElem{C}...) + function my_mult(as::Tuple{Vararg{LieAlgebraModuleElem{C}}}) @req length(as) == length(Vs) "Length of vector does not match." @req all(i -> parent(as[i]) === Vs[i], 1:length(as)) "Incompatible modules." mat = zero_matrix(R, 1, dim_tensor_product_V) @@ -930,14 +946,11 @@ function tensor_product( end return tensor_product_V(mat) end - function pure(as::Tuple) - return pure(as...)::LieAlgebraModuleElem{C} - end - function pure(as::Vector{LieAlgebraModuleElem{C}}) - return pure(as...)::LieAlgebraModuleElem{C} + function my_mult(as::LieAlgebraModuleElem{C}...) + return my_mult(as)::LieAlgebraModuleElem{C} end - function inv_pure(a::LieAlgebraModuleElem{C}) + function my_decomp(a::LieAlgebraModuleElem{C}) @req parent(a) === tensor_product_V "Incompatible modules." if iszero(a) return Tuple(zero(Vi) for Vi in Vs) @@ -949,11 +962,16 @@ function tensor_product( return Tuple(basis(Vi, indi) for (Vi, indi) in zip(Vs, inds)) end + mult_map = MapFromFunc( + Hecke.TupleParent(Tuple([zero(Vi) for Vi in Vs])), tensor_product_V, my_mult, my_decomp + ) + inv_mult_map = MapFromFunc(tensor_product_V, domain(mult_map), my_decomp, my_mult) + set_attribute!( tensor_product_V, :is_tensor_product => Vs, - :tensor_pure_function => pure, - :tensor_pure_preimage_function => inv_pure, + :tensor_pure_function => mult_map, + :tensor_generator_decompose_function => inv_mult_map, ) return tensor_product_V @@ -1063,7 +1081,7 @@ function exterior_power( end mult_map = MapFromFunc( - Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), E, my_mult, my_decomp + Hecke.TupleParent(Tuple([zero(V) for _ in 1:k])), E, my_mult, my_decomp ) inv_mult_map = MapFromFunc(E, domain(mult_map), my_decomp, my_mult) @@ -1204,7 +1222,7 @@ function symmetric_power( end mult_map = MapFromFunc( - Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), S, my_mult, my_decomp + Hecke.TupleParent(Tuple([zero(V) for _ in 1:k])), S, my_mult, my_decomp ) inv_mult_map = MapFromFunc(S, domain(mult_map), my_decomp, my_mult) @@ -1320,7 +1338,7 @@ function tensor_power( end mult_map = MapFromFunc( - Hecke.TupleParent(Tuple([zero(V) for f in 1:k])), T, my_mult, my_decomp + Hecke.TupleParent(Tuple([zero(V) for _ in 1:k])), T, my_mult, my_decomp ) inv_mult_map = MapFromFunc(T, domain(mult_map), my_decomp, my_mult) From a47ce6b0f56447320fb151cb6226d92a7e609f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 4 Jan 2024 13:19:22 +0100 Subject: [PATCH 14/17] Fix tests --- .../src/Morphisms/linear_strands.jl | 2 +- .../src/Morphisms/simplified_complexes.jl | 2 +- .../LieAlgebras/test/LieAlgebraModule-test.jl | 26 ++++++++++++++++--- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/linear_strands.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/linear_strands.jl index 71381e522b7..a676ddce075 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/linear_strands.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/linear_strands.jl @@ -123,7 +123,7 @@ function (fac::LinearStrandComplementChainFactory)(self::AbsHyperComplex, i::Tup offset = zero(G) + sum(i)*G[1] + degree(str)*G[1] min_ind = [k for k in 1:rank(F_full) if degree(F_full[k]) == offset] comp = [k for k in 1:rank(F_full) if !(k in min_ind)] - F = graded_free_module(S, [degree(F_full[k]) for k in comp]) + F = graded_free_module(S, elem_type(G)[degree(F_full[k]) for k in comp]) map = hom(F_full, F, elem_type(F)[(k in comp ? F[findfirst(i->i==k, comp)] : zero(F)) for k in 1:rank(F_full)]) fac.maps_from_original[i] = map return F diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl index 4be43f49a08..684a85feaf9 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl @@ -345,7 +345,7 @@ end ### Helper functions function _make_free_module(M::ModuleFP, g::Vector{T}) where {T<:ModuleFPElem} - if isgraded(M) + if is_graded(M) w = degree.(g) return graded_free_module(base_ring(M), w) else diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index fc50ffdcc3c..b95df820def 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -342,7 +342,13 @@ elseif k == 2 a = V() b = V() - while iszero(a) || iszero(b) || a == b + while iszero(a) || + iszero(b) || + can_solve( + Oscar.LieAlgebras._matrix(a), + Oscar.LieAlgebras._matrix(b); + side=:left, + ) a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) end @@ -394,7 +400,13 @@ elseif k == 2 a = V() b = V() - while iszero(a) || iszero(b) || a == b + while iszero(a) || + iszero(b) || + can_solve( + Oscar.LieAlgebras._matrix(a), + Oscar.LieAlgebras._matrix(b); + side=:left, + ) a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) end @@ -443,10 +455,18 @@ elseif k == 2 a = V() b = V() - while iszero(a) || iszero(b) || a == b + while iszero(a) || + iszero(b) || + can_solve( + Oscar.LieAlgebras._matrix(a), + Oscar.LieAlgebras._matrix(b); + side=:left, + ) a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) end + println(a) + println(b) @test !iszero(T(a, b)) @test !iszero(T(a, b) + T(b, a)) @test !iszero(T(a, b) - T(b, a)) From 68545ce9c0598c81d04a9609b74a24dbe8c998c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 12 Jan 2024 16:16:09 +0100 Subject: [PATCH 15/17] Fix Aqua test --- experimental/LieAlgebras/src/LieAlgebraModule.jl | 4 ++-- experimental/LieAlgebras/test/LieAlgebraModule-test.jl | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 9434d390a4e..e0f79b0864a 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -350,8 +350,8 @@ function (V::LieAlgebraModule{C})( end function (V::LieAlgebraModule{C})( - a::Tuple{Vararg{T}} -) where {C<:FieldElem,T<:LieAlgebraModuleElem{C}} + a::Tuple{Vararg{LieAlgebraModuleElem{C}}} +) where {C<:FieldElem} if length(a) == 1 && V === parent(a[1]) return a[1] elseif ((fl, W) = is_dual(V); fl) diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index b95df820def..e581c196846 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -465,8 +465,6 @@ a = V(rand(-10:10, dim(V))) b = V(rand(-10:10, dim(V))) end - println(a) - println(b) @test !iszero(T(a, b)) @test !iszero(T(a, b) + T(b, a)) @test !iszero(T(a, b) - T(b, a)) From eb66cc82fd9c05390de12cde4466ee465d879d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 12 Jan 2024 16:36:15 +0100 Subject: [PATCH 16/17] Apply suggestions from code review Co-authored-by: Matthias Zach <85350711+HechtiDerLachs@users.noreply.github.com> --- src/Modules/ExteriorPowers/FreeModules.jl | 4 ++-- src/Modules/ExteriorPowers/SubQuo.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Modules/ExteriorPowers/FreeModules.jl b/src/Modules/ExteriorPowers/FreeModules.jl index 160d78acbe0..91e6d32b8d7 100644 --- a/src/Modules/ExteriorPowers/FreeModules.jl +++ b/src/Modules/ExteriorPowers/FreeModules.jl @@ -9,7 +9,7 @@ # User facing constructor for ⋀ ᵖ F. function exterior_power(F::FreeMod, p::Int; cached::Bool=true) - @req 0 <= p <= rank(F) "Exponent out of bounds" + @req 0 <= p <= rank(F) "exponent out of bounds" if cached powers = _exterior_powers(F) @@ -18,7 +18,7 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) R = base_ring(F)::base_ring_type(F) n = rank(F) - result_ = free_module(R, binomial(n, p)) + result_ = FreeMod(R, binomial(n, p)) # In case F was graded, we have to take an extra detour. result = if is_graded(F) diff --git a/src/Modules/ExteriorPowers/SubQuo.jl b/src/Modules/ExteriorPowers/SubQuo.jl index 76201904082..a7f5e962810 100644 --- a/src/Modules/ExteriorPowers/SubQuo.jl +++ b/src/Modules/ExteriorPowers/SubQuo.jl @@ -9,7 +9,7 @@ function exterior_power(M::SubquoModule, p::Int; cached::Bool=true) end if iszero(p) - F = free_module(R, 1) + F = FreeMod(R, 1) result, _ = sub(F, [F[1]]) else C = presentation(M) From 8afcbeb1e8c3fdf6f766b41e47b9b0c9022031a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 16 Jan 2024 18:32:36 +0100 Subject: [PATCH 17/17] Fix tests --- src/Modules/ExteriorPowers/FreeModules.jl | 2 +- src/Modules/ModulesGraded.jl | 2 +- src/Modules/UngradedModules/SubquoModuleElem.jl | 2 +- src/Modules/flattenings.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Modules/ExteriorPowers/FreeModules.jl b/src/Modules/ExteriorPowers/FreeModules.jl index 91e6d32b8d7..08d95363c1f 100644 --- a/src/Modules/ExteriorPowers/FreeModules.jl +++ b/src/Modules/ExteriorPowers/FreeModules.jl @@ -16,7 +16,7 @@ function exterior_power(F::FreeMod, p::Int; cached::Bool=true) haskey(powers, p) && return powers[p] end - R = base_ring(F)::base_ring_type(F) + R = base_ring(F) n = rank(F) result_ = FreeMod(R, binomial(n, p)) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 6e1c20eea5f..259be305f15 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -513,7 +513,7 @@ function degree(f::FreeModElem) iszero(f) && return A[0] f.d = isa(f.d, GrpAbFinGenElem) ? f.d : determine_degree_from_SR(coordinates(f), degrees(parent(f))) isa(f.d, GrpAbFinGenElem) || error("The specified element is not homogeneous.") - return f.d + return f.d::GrpAbFinGenElem end function degree(::Type{Vector{Int}}, f::FreeModElem) diff --git a/src/Modules/UngradedModules/SubquoModuleElem.jl b/src/Modules/UngradedModules/SubquoModuleElem.jl index 533b2f407f7..1e8a9b6e3ba 100644 --- a/src/Modules/UngradedModules/SubquoModuleElem.jl +++ b/src/Modules/UngradedModules/SubquoModuleElem.jl @@ -848,7 +848,7 @@ ngens(M::SubquoModule) = ngens(M.sub) Given an `R`-module `M`, return `R`. """ -base_ring(M::SubquoModule) = base_ring(M.F) +base_ring(M::SubquoModule) = base_ring(M.F)::base_ring_type(M.F) @doc raw""" zero(M::SubquoModule) diff --git a/src/Modules/flattenings.jl b/src/Modules/flattenings.jl index 9ff7ff8838e..5a3fa8bfc96 100644 --- a/src/Modules/flattenings.jl +++ b/src/Modules/flattenings.jl @@ -93,7 +93,7 @@ end function _change_base_ring_and_preserve_gradings(phi::Any, F::FreeMod) R = base_ring(F) S = parent(phi(zero(R))) - FS = (is_graded(F) ? graded_free_module(S, Vector{elem_type(grading_group(F))}(degree.(gens(F)))) : FreeMod(S, ngens(F))) + FS = (is_graded(F) ? graded_free_module(S, degree.(gens(F))) : FreeMod(S, ngens(F))) FS.S = F.S return FS, hom(F, FS, gens(FS), phi) end