Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exterior powers of finitely presented modules #2879

Merged
merged 36 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c5f674f
Implement a prototype for exterior powers of free modules.
HechtiDerLachs Oct 2, 2023
f2ca620
Implement Koszul complexes associated to elements of free modules.
HechtiDerLachs Oct 3, 2023
ed0e6f9
Preserve gradings and implement koszul dual bases.
HechtiDerLachs Oct 3, 2023
abfc597
Implement induced maps on exterior powers.
HechtiDerLachs Oct 3, 2023
7947688
Some tweaks.
HechtiDerLachs Oct 3, 2023
4062c3b
Move everything to appropriate places.
HechtiDerLachs Oct 3, 2023
48b9752
Implement iteration over module monomials of a specific degree.
HechtiDerLachs Oct 4, 2023
4c34531
Add trailing zeroes to the Koszul complex.
HechtiDerLachs Oct 4, 2023
af69344
Allow custom parents and no caching.
HechtiDerLachs Oct 5, 2023
37de4fe
Revert "Implement iteration over module monomials of a specific degree."
HechtiDerLachs Oct 5, 2023
1e0b127
Extend optional caching to Koszul complexes.
HechtiDerLachs Oct 5, 2023
6ace053
Add extra multiplication function.
HechtiDerLachs Oct 5, 2023
9a4b50e
Some tweaks.
HechtiDerLachs Oct 5, 2023
36f6396
Customize printing.
HechtiDerLachs Oct 6, 2023
769478e
Add pure and inv_pure.
HechtiDerLachs Oct 6, 2023
fd61072
Put the check back in.
HechtiDerLachs Oct 6, 2023
aa9828b
Migrate the OrderedMultiIndex.
HechtiDerLachs Oct 6, 2023
5fce897
Export the new functionality.
HechtiDerLachs Oct 6, 2023
c25a897
Clean up the include statements.
HechtiDerLachs Oct 6, 2023
2de9aad
Implement hom method.
HechtiDerLachs Oct 6, 2023
cb807f0
Rename pure and decompose functions.
HechtiDerLachs Oct 6, 2023
df75af0
Migrate tests.
HechtiDerLachs Oct 6, 2023
ddf43ce
Clean up exports from LieAlgebra.jl.
HechtiDerLachs Oct 6, 2023
452ae32
Fix tests.
HechtiDerLachs Oct 6, 2023
bdb32a2
Some improvements in localizations.
HechtiDerLachs Oct 7, 2023
362d188
Implement exterior powers for subquos.
HechtiDerLachs Oct 8, 2023
7c3fb55
Some fixes.
HechtiDerLachs Oct 8, 2023
dc39ef1
Reroute koszul complexes to use the non-Singular methods.
HechtiDerLachs Oct 12, 2023
d395414
Deprecate old singular methods.
HechtiDerLachs Oct 12, 2023
40d381e
Repair Koszul homology.
HechtiDerLachs Oct 12, 2023
e40d9e4
Remove duplicate methods and use singular's depth.
HechtiDerLachs Oct 12, 2023
746a00e
Add tests.
HechtiDerLachs Oct 12, 2023
a364de6
Add some assertions.
HechtiDerLachs Oct 12, 2023
b5134b9
Some tweaks for the ordered multiindices.
HechtiDerLachs Oct 12, 2023
9be4f7e
Pass on the check flag for construction of complexes.
HechtiDerLachs Oct 12, 2023
c2d2e67
Fix doctests and add a truely generic method for the depth computation.
HechtiDerLachs Oct 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions experimental/LieAlgebras/src/LieAlgebras.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import ..Oscar:
image,
inv,
is_abelian,
is_exterior_power,
is_isomorphism,
is_nilpotent,
is_perfect,
Expand Down Expand Up @@ -81,14 +82,12 @@ export coefficient_vector
export coerce_to_lie_algebra_elem
export combinations
export derived_algebra
export exterior_power
export general_linear_lie_algebra
export highest_weight_module
export hom_direct_sum
export hom_power
export is_direct_sum
export is_dual
export is_exterior_power
export is_self_normalizing
export is_standard_module
export is_symmetric_power
Expand Down Expand Up @@ -143,14 +142,12 @@ export base_modules
export bracket
export coerce_to_lie_algebra_elem
export derived_algebra
export exterior_power
export general_linear_lie_algebra
export highest_weight_module
export hom_direct_sum
export hom_power
export is_direct_sum
export is_dual
export is_exterior_power
export is_self_normalizing
export is_standard_module
export is_symmetric_power
Expand Down
182 changes: 182 additions & 0 deletions src/Combinatorics/OrderedMultiIndex.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
########################################################################
# Ordered multiindices of the form 0 < i₁ < i₂ < … < iₚ ≤ n.
#
# For instance, these provide a coherent way to enumerate the generators
# of an exterior power ⋀ ᵖ M of a module M given a chosen set of
# generators of M. But they can also be used for other purposes
# like enumerating subsets of p elements out of a set with n elements.
########################################################################
mutable struct OrderedMultiIndex{IntType<:IntegerUnion}
Copy link
Member

Choose a reason for hiding this comment

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

Why is this struct mutable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question. It probably shouldn't be, unless we want to allow in-place iteration. Do we? Is there a potential speedup from making it immutable?

i::Vector{IntType}
n::IntType

function OrderedMultiIndex(i::Vector{T}, n::T) where {T<:IntegerUnion}
@assert all(k->i[k]<i[k+1], 1:length(i)-1) "indices must be strictly ordered"
return new{T}(i, n)
end
end

function ordered_multi_index(i::Vector{T}, n::T) where {T<:IntegerUnion}
return OrderedMultiIndex(i, n)
end

# For an ordered multiindex i = (0 < i₁ < i₂ < … < iₚ ≤ n) this returns the vector (i₁,…,iₚ).
indices(a::OrderedMultiIndex) = a.i

# For an ordered multiindex i = (0 < i₁ < i₂ < … < iₚ ≤ n) this returns n.
bound(a::OrderedMultiIndex) = a.n

# For an ordered multiindex i = (0 < i₁ < i₂ < … < iₚ ≤ n) this returns p.
length(a::OrderedMultiIndex) = length(a.i)

# For an ordered multiindex i = (0 < i₁ < i₂ < … < iₚ ≤ n) this returns iₖ.
getindex(a::OrderedMultiIndex, k::Int) = a.i[k]

index_type(a::OrderedMultiIndex) = index_type(typeof(a))
index_type(::Type{OrderedMultiIndex{T}}) where {T} = T

# Internal function for "multiplication" of ordered multiindices.
#
# The for i = (0 < i₁ < i₂ < … < iₚ ≤ n) and j = (0 < j₁ < j₂ < … < jᵣ ≤ n)
HechtiDerLachs marked this conversation as resolved.
Show resolved Hide resolved
# the result is a pair `(sign, a)` with `sign` either 0 in case that
# iₖ = jₗ for some k and l, or ±1 depending on the number of transpositions
# needed to put (i₁, …, iₚ, j₁, …, jᵣ) into a strictly increasing order
# to produce `a`.
function _mult(a::OrderedMultiIndex{T}, b::OrderedMultiIndex{T}) where {T}
@assert bound(a) == bound(b) "multiindices must have the same bounds"

# in case of a double index return zero
any(x->(x in indices(b)), indices(a)) && return 0, a
Copy link
Member

Choose a reason for hiding this comment

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

This makes this function _mult not type stable. Maybe

Suggested change
any(x->(x in indices(b)), indices(a)) && return 0, a
any(x->(x in indices(b)), indices(a)) && return 0, indices(a)

or (IMHO nicer)

Suggested change
any(x->(x in indices(b)), indices(a)) && return 0, a
any(x->(x in indices(b)), indices(a)) && return 0, T[]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the catch. But the second needs to reallocate memory, doesn't it?


p = length(a)
q = length(b)
result_indices = vcat(indices(a), indices(b))
sign = 1

# bubble sort result_indices and keep track of the sign
for k in p:-1:1
l = k
c = result_indices[l]
while l < p + q && c > result_indices[l+1]
result_indices[l] = result_indices[l+1]
sign = -sign
l = l+1
end
result_indices[l] = c
end
return sign, result_indices
end

# For two ordered multiindices i = (0 < i₁ < i₂ < … < iₚ ≤ n)
# and j = (0 < j₁ < j₂ < … < jᵣ ≤ n) this returns a pair `(sign, a)`
# with `sign` either 0 in case that iₖ = jₗ for some k and l,
# or ±1 depending on the number of transpositions needed to put
# (i₁, …, iₚ, j₁, …, jᵣ) into a strictly increasing order to produce `a`.
function _wedge(a::OrderedMultiIndex{T}, b::OrderedMultiIndex{T}) where {T}
sign, ind = _mult(a, b)
iszero(sign) && return sign, a
return sign, OrderedMultiIndex(ind, bound(a))
end

function _wedge(a::Vector{T}) where {T <: OrderedMultiIndex}
isempty(a) && error("list must not be empty")
isone(length(a)) && return 1, first(a)
k = div(length(a), 2)
b = a[1:k]
c = a[k+1:end]
sign_b, ind_b = _wedge(b)
sign_c, ind_c = _wedge(c)
sign, ind = _wedge(ind_b, ind_c)
return sign * sign_b * sign_c, ind
end

function ==(a::OrderedMultiIndex{T}, b::OrderedMultiIndex{T}) where {T}
bound(a) == bound(b) || return false
return indices(a) == indices(b)
HechtiDerLachs marked this conversation as resolved.
Show resolved Hide resolved
end

########################################################################
# A data type to facilitate iteration over all ordered multiindices
# of the form 0 < i₁ < i₂ < … < iₚ ≤ n for fixed 0 ≤ p ≤ n.
Copy link
Member

Choose a reason for hiding this comment

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

So... this is essentially the parent of OrderedMultiIndex{T}, except that it does not specify T...? Or "hardcodes" T to be Int (?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was not so sure about this, to be honest. It's not actually a parent in the Oscar sense, but in spirit: yes. And it's true: It doesn't know about the T. But it's hardcoded only in the sense that iterating over this will always produce an OrderedMultiIndex with T = Int64.

At some point I thought of removing the T altogether, but then I didn't see any real problem in keeping it either. It's just a bit weird that OrderdMultiIndexSet doesn't know about it. But since it's not an actual parent: Do you see a real problem here?

#
# Example:
#
# for i in OrderedMultiIndexSet(3, 5)
# # do something with i = (0 < i₁ < i₂ < i₃ ≤ 5).
# end
########################################################################
mutable struct OrderedMultiIndexSet
n::Int
p::Int

function OrderedMultiIndexSet(p::Int, n::Int)
@assert 0 <= p <= n "invalid bounds"
return new(n, p)
end
end

bound(I::OrderedMultiIndexSet) = I.n
index_length(I::OrderedMultiIndexSet) = I.p

Base.eltype(I::OrderedMultiIndexSet) = OrderedMultiIndex
Base.length(I::OrderedMultiIndexSet) = binomial(bound(I), index_length(I))

function Base.iterate(I::OrderedMultiIndexSet)
ind = OrderedMultiIndex([i for i in 1:index_length(I)], bound(I))
return ind, ind
end

function Base.iterate(I::OrderedMultiIndexSet, state::OrderedMultiIndex)
bound(I) == bound(state) || error("index not compatible with set")
ind = copy(indices(state))
l = length(state)
while l > 0 && ind[l] == bound(I) - length(state) + l
l = l - 1
end
iszero(l) && return nothing
ind[l] = ind[l] + 1
l = l + 1
while l <= length(state)
ind[l] = ind[l-1] + 1
l = l + 1
end
result = OrderedMultiIndex(ind, bound(I))
return result, result
end

function Base.show(io::IO, ind::OrderedMultiIndex)
i = indices(ind)
print(io, "0 ")
for i in indices(ind)
print(io, "< $i ")
end
print(io, "<= $(bound(ind))")
end

# For an ordered multiindex i = (0 < i₁ < i₂ < … < iₚ ≤ n) this
# returns the number k so that i appears at the k-th spot in the
# enumeration of all ordered multiindices for this pair 0 ≤ p ≤ n.
function linear_index(ind::OrderedMultiIndex)
n = bound(ind)
p = length(ind)
iszero(p) && return 1
isone(p) && return ind[1]
i = indices(ind)
return binomial(n, p) - binomial(n - first(i) + 1, p) + linear_index(OrderedMultiIndex(i[2:end].-first(i), n-first(i)))
end

# For a pair 0 ≤ p ≤ n return the k-th ordered multiindex in the
# enumeration of all ordered multiindices (0 < i₁ < i₂ < … < iₚ ≤ n).
function ordered_multi_index(k::Int, p::Int, n::Int)
(k < 1 || k > binomial(n, p)) && error("index out of range")
iszero(p) && return OrderedMultiIndex(Int[], n)
isone(p) && return OrderedMultiIndex([k], n)
i1 = 1
bin = binomial(n, p)
while i1 <= n - p && !(bin - binomial(n - i1, p) > k - 1)
i1 = i1 + 1
end
prev_res = ordered_multi_index(k - bin + binomial(n - i1 + 1, p), p-1, n - i1)
return OrderedMultiIndex(pushfirst!(indices(prev_res).+i1, i1), n)
end

4 changes: 4 additions & 0 deletions src/Modules/ExteriorPowers/ExteriorPowers.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include("Helpers.jl") Moved to src/Combinatorics/OrderedMultiIndex.jl
include("FreeModules.jl")
include("SubQuo.jl")
include("Generic.jl")
Loading