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

Linear combinations of set partitions #3944

Merged
merged 31 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d487544
Linear Combinations for Partitions (from december 23)
sebvz777 Jun 13, 2024
ebe1b41
Doctest partially fixed, tests added, renaming
sebvz777 Jun 14, 2024
a307173
deepcopy, fixed test, hash
sebvz777 Jun 18, 2024
e6d82e9
minor comments
sebvz777 Jul 2, 2024
ac62a5a
comments, renaming, test, scale fix, using linear_partition ...
sebvz777 Jul 8, 2024
4127075
return stmt
sebvz777 Jul 8, 2024
b21bb58
cite and get_coefficients
sebvz777 Jul 16, 2024
ac1682f
Merge branch 'oscar-system:master' into sv/LinearComb
sebvz777 Jul 16, 2024
ba5188a
comments
sebvz777 Jul 16, 2024
0636ae8
readme
sebvz777 Jul 16, 2024
3571eaa
import coefficients
sebvz777 Jul 16, 2024
9842ff4
test fix
sebvz777 Jul 16, 2024
a6d8169
iszerro
sebvz777 Jul 16, 2024
d963179
doctest fixes
sebvz777 Jul 16, 2024
e4fe5a5
trying to fix doctest
sebvz777 Jul 17, 2024
b08bef8
doctest fix 2.0
sebvz777 Jul 17, 2024
de2835c
S(X) -> X for doctest
sebvz777 Jul 23, 2024
9fb5313
removed add and substract
sebvz777 Jul 23, 2024
3028f5a
doctest without line breaks
sebvz777 Jul 24, 2024
97a9b63
feedback implementation part 1
sebvz777 Jul 25, 2024
9e08dcd
Update experimental/SetPartitions/src/LinearSetPartition.jl
pinguly Jul 25, 2024
80ad141
rename to LinearPartition, implement base_ring, fix some docstrings
pinguly Jul 26, 2024
91fb265
minor doctest fix for simplify operation and linear_partition
sebvz777 Jul 26, 2024
78e8574
Update experimental/SetPartitions/src/LinearPartition.jl
sebvz777 Jul 30, 2024
25a827f
Update experimental/SetPartitions/src/LinearPartition.jl
sebvz777 Jul 30, 2024
b72f932
Update experimental/SetPartitions/src/LinearPartition.jl
sebvz777 Jul 30, 2024
4069c34
Update experimental/SetPartitions/src/Util.jl
sebvz777 Jul 30, 2024
1721c84
fixing SetPartition reference in doctest and RingElem
sebvz777 Jul 31, 2024
6e8db15
fix base_ring_type import
pinguly Jul 31, 2024
d6de93e
docstring improvements, renaming LinearComb-test to LinearPartition-t…
sebvz777 Aug 26, 2024
5c3e20a
Update experimental/SetPartitions/src/Util.jl
lgoettgens Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions experimental/SetPartitions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ This includes:
* basic set-partitions and operations on them (e.g. composition, tensor product, involution)
* variations like colored partitions [TW18](@cite) and spatial partitions [CW16](@cite)
* enumeration of partitions which can be constructed from a set of generators

In the future, we plan to implement:
* linear combinations of partitions
* examples of tensor categories in the framework of [TensorCategories.jl](https://github.com/FabianMaeurer/TensorCategories.jl)

## Contact

Expand Down
210 changes: 210 additions & 0 deletions experimental/SetPartitions/src/LinearPartition.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
"""
LinearPartition{S <: AbstractPartition, T <: RingElem}

`LinearPartition` represents a linear combination of partitions of type `S`
with coefficients of type `T`.

See for example Chapter 5 in [Gro20](@cite) for further information on linear
combinations of partitions.
"""
struct LinearPartition{S <: AbstractPartition, T <: RingElem}
base_ring :: Ring # parent_type(T)
coefficients :: Dict{S, T}

function LinearPartition{S, T}(ring::Ring, coeffs::Dict{S, T}) where {S <: AbstractPartition, T <: RingElem}
@req isconcretetype(S) "Linear combinations are only defined for concrete subtypes of AbstractPartition"
@req ring isa parent_type(T) "Ring type is not the parent of the coefficient type"
for c in values(coeffs)
@req (parent(c) == ring) "Coefficient does not belong to the base ring"
end
return new(ring, simplify_operation_zero(coeffs))
end
end

"""
linear_partition(ring::Ring, coeffs::Dict{S, <: Any}) where {S <: AbstractPartition}

Construct a linear partition over `ring` with coefficients `coeffs`.

# Examples
```jldoctest
julia> S, d = polynomial_ring(QQ, "d")
(Univariate polynomial ring in d over QQ, d)

julia> linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 4, set_partition([1, 1], [1, 1]) => 4*d))
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 2], [1, 1]) => 4, SetPartition([1, 1], [1, 1]) => 4*d))
```
"""
function linear_partition(ring::Ring, coeffs::Dict{S, <: Any}) where {S <: AbstractPartition}
ring_coeffs = Dict(p => ring(c) for (p, c) in coeffs)
return LinearPartition{S, elem_type(ring)}(ring, ring_coeffs)
end

"""
linear_partition(ring::Ring, terms::Vector{Tuple{S, <: Any}}) where {S <: AbstractPartition}

Return a `LinearPartition` generated from the vector `term` of 2-tuples,
where the first element in the tuple is an `AbstractPartition` and the second
a `RingElem`. The `ring` argument defines the base ring into which the second
elements of the tuples are converted. Furthermore simplify the term before initializing
the `LinearPartition` object with the corresponding dict.

# Examples
```jldoctest
julia> S, d = polynomial_ring(QQ, "d")
(Univariate polynomial ring in d over QQ, d)

julia> linear_partition(S, [(set_partition([1, 1], [1, 1]), 4), (set_partition([1, 1], [1, 1]), 4*d)])
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 1], [1, 1]) => 4*d + 4))
```
"""
function linear_partition(ring::Ring, terms::Vector{Tuple{S, <: Any}}) where {S <: AbstractPartition}
simpl_terms = simplify_operation([(p, ring(c)) for (p, c) in terms])
return linear_partition(ring, Dict{S, elem_type(ring)}(simpl_terms))
end

"""
base_ring(p::LinearPartition{S, T}) where {S <: AbstractPartition, T <: RingElem}

Return the underlying coefficient ring of `p`.
"""
function base_ring(p::LinearPartition{S, T}) where {S <: AbstractPartition, T <: RingElem}
return p.base_ring::parent_type(T)
end

lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
function base_ring_type(::Type{LinearPartition{S, T}}) where {S <: AbstractPartition, T <: RingElem}
return parent_type(T)

Check warning on line 76 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L75-L76

Added lines #L75 - L76 were not covered by tests
end

"""
coefficients(p::LinearPartition)

Return the coefficients of `p` as dictionary from partitions to elements
of the underlying ring.
"""
function coefficients(p::LinearPartition)
return p.coefficients
end

function hash(p::LinearPartition, h::UInt)
return hash(base_ring(p), hash(coefficients(p), h))

Check warning on line 90 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L89-L90

Added lines #L89 - L90 were not covered by tests
end

function ==(p::LinearPartition, q::LinearPartition)
return base_ring(p) == base_ring(q) && coefficients(p) == coefficients(q)
end

function deepcopy_internal(p::LinearPartition, stackdict::IdDict)
if haskey(stackdict, p)
return stackdict[p]

Check warning on line 99 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L97-L99

Added lines #L97 - L99 were not covered by tests
end
q = linear_partition(

Check warning on line 101 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L101

Added line #L101 was not covered by tests
base_ring(p),
deepcopy_internal(coefficients(p), stackdict))
stackdict[p] = q
return q

Check warning on line 105 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L104-L105

Added lines #L104 - L105 were not covered by tests
end

function +(p::LinearPartition{S, T}, q::LinearPartition{S, T}) where
{ S <: AbstractPartition, T <: RingElem }
@req base_ring(p) == base_ring(q) "Linear partitions are defined over different base rings"
result = deepcopy(coefficients(p))
for i in pairs(coefficients(q))
result[i[1]] = get(result, i[1], 0) + i[2]
end
return linear_partition(base_ring(p), result)
end

function *(a::RingElement, p::LinearPartition{S, T}) where
{ S <: AbstractPartition, T <: RingElem }
a = base_ring(p)(a)
result = Dict{S, T}()
for (i, n) in pairs(coefficients(p))
result[i] = a * n
end
return linear_partition(base_ring(p), result)
end

"""
compose(p::LinearPartition{S, T}, q::LinearPartition{S, T}, d::T) where
{ S <: AbstractPartition, T <: RingElem }

Return the composition between `p` and `q`.

The composition is obtained by multiplying each coefficient
of `p` with each coefficient of `q` and composing the corresponding
partitions. The `RingElem` parameter `d` multiplies each
coefficient based on the number of loops identified during the
composition.

# Examples
```jldoctest
julia> S, d = polynomial_ring(QQ, "d")
(Univariate polynomial ring in d over QQ, d)

julia> a = linear_partition(S, [(set_partition([1, 2], [1, 1]), 4), (set_partition([1, 1], [1, 1]), 4*d)])
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 2], [1, 1]) => 4, SetPartition([1, 1], [1, 1]) => 4*d))

julia> compose(a, a, d)
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 2], [1, 1]) => 16*d + 16, SetPartition([1, 1], [1, 1]) => 16*d^2 + 16*d))
```
"""
function compose(p::LinearPartition{S, T}, q::LinearPartition{S, T}, d::T) where
{ S <: AbstractPartition, T <: RingElem }
@req base_ring(p) == base_ring(q) "Linear partitions are defined over different base rings"
result = Dict{S, T}()
for i in pairs(coefficients(p))
for ii in pairs(coefficients(q))
(composition, loop) = compose_count_loops(i[1], ii[1])
new_coefficient = i[2] * ii[2] * (d^loop)
result[composition] = get(result, composition, 0) + new_coefficient
end
end
return linear_partition(base_ring(p), result)
end

"""
tensor_product(p::LinearPartition{S, T}, q::LinearPartition{S, T}) where
{ S <: AbstractPartition, T <: RingElem }

Return the tensor product of `p` and `q`.

# Examples
```jldoctest
julia> S, d = polynomial_ring(QQ, "d")
(Univariate polynomial ring in d over QQ, d)

julia> a = linear_partition(S, [(set_partition([1, 2], [1, 1]), 4), (set_partition([1, 1], [1, 1]), 4*d)])
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 2], [1, 1]) => 4, SetPartition([1, 1], [1, 1]) => 4*d))

julia> tensor_product(a, a)
LinearPartition{SetPartition, QQPolyRingElem}(Univariate polynomial ring in d over QQ, Dict{SetPartition, QQPolyRingElem}(SetPartition([1, 1, 2, 2], [1, 1, 2, 2]) => 16*d^2, SetPartition([1, 2, 3, 3], [1, 1, 3, 3]) => 16*d, SetPartition([1, 2, 3, 4], [1, 1, 3, 3]) => 16, SetPartition([1, 1, 2, 3], [1, 1, 2, 2]) => 16*d))
```
"""
function tensor_product(p::LinearPartition{S, T}, q::LinearPartition{S, T}) where
{ S <: AbstractPartition, T <: RingElem }
@req base_ring(p) == base_ring(q) "Linear partitions are defined over different base rings"
result = Dict{S, T}()
for i in pairs(coefficients(p))
for ii in pairs(coefficients(q))
composition = tensor_product(i[1], ii[1])
new_coefficient = i[2] * ii[2]
result[composition] = get(result, composition, 0) + new_coefficient
end
end
return linear_partition(base_ring(p), result)
end

function ⊗(p::LinearPartition{S, T}, q::LinearPartition{S, T}) where

Check warning on line 198 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L198

Added line #L198 was not covered by tests
{ S <: AbstractPartition, T <: RingElem }
return tensor_product(p, q)

Check warning on line 200 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L200

Added line #L200 was not covered by tests
end

function -(p::LinearPartition)
return (-1 * p)
end

function -(p::LinearPartition{S, T}, q::LinearPartition{S, T}) where
{ S <: AbstractPartition, T <: RingElem }
return p + (-q)
end
27 changes: 22 additions & 5 deletions experimental/SetPartitions/src/SetPartitions.jl
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
module SetPartitions

import Base:
+,
-,
*,
==,
*,
adjoint,
deepcopy,
deepcopy_internal,
hash,
size

import Oscar:
PermGroupElem,
Ring,
RingElem,
RingElement,
⊗,
@req,
base_ring,
base_ring_type,
coefficients,
compose,
cycles,
degree,
elem_type,
involution,
iszero,
join,
PermGroupElem,
parent,
degree,
tensor_product,
@req
parent_type,
tensor_product

export ColoredPartition
export SetPartition
export SpatialPartition
export LinearPartition

export colored_partition
export compose_count_loops
Expand All @@ -36,6 +48,7 @@ export is_non_crossing
export is_pair
export join
export levels
export linear_partition
export lower_colors
export lower_points
export number_of_blocks
Expand All @@ -53,20 +66,23 @@ export upper_colors
export upper_points



include("AbstractPartition.jl")
include("Util.jl")
include("SetPartition.jl")
include("ColoredPartition.jl")
include("SpatialPartition.jl")
include("PartitionProperties.jl")
include("GenerateCategory.jl")
include("LinearPartition.jl")
end

using .SetPartitions

export ColoredPartition
export SetPartition
export SpatialPartition
export LinearPartition

export colored_partition
export compose_count_loops
Expand All @@ -79,6 +95,7 @@ export is_non_crossing
export is_pair
export join
export levels
export linear_partition
export lower_colors
export lower_points
export number_of_blocks
Expand Down
44 changes: 44 additions & 0 deletions experimental/SetPartitions/src/Util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,47 @@ function _add_partition_top_bottom(vector::Vector{Dict{Int, Set{T}}}, p::T) wher

return vector
end

"""
simplify_operation(partition_sum::Vector{Tuple{S, T}}) where { S <: AbstractPartition, T <: RingElement }

Simplify the vector representation of a `LinearPartition` in terms of distributivity.

# Examples
```jldoctest
julia> S, d = polynomial_ring(QQ, "d")
(Univariate polynomial ring in d over QQ, d)

julia> Oscar.SetPartitions.simplify_operation([(set_partition([1, 1], [1, 1]), S(10)), (set_partition([1, 1], [1, 1]), 4*d)])
1-element Vector{Tuple{SetPartition, QQPolyRingElem}}:
(SetPartition([1, 1], [1, 1]), 4*d + 10)
```
"""
function simplify_operation(partition_sum::Vector{Tuple{S, T}}) where { S <: AbstractPartition, T <: RingElement }

partitions = Dict{S, T}()

for (i1, i2) in partition_sum
if iszero(i2)
continue
end
partitions[i1] = get(partitions, i1, 0) + i2
end

return [(s, t) for (s, t) in partitions if !iszero(t)]
end

"""
simplify_operation_zero(p::Dict{S, T}) where { S <: AbstractPartition, T <: RingElement }

Simplify the dict representation of a `LinearPartition` in terms of zero coefficients.
"""
function simplify_operation_zero(p::Dict{S, T}) where { S <: AbstractPartition, T <: RingElement }
result = Dict{S, T}()
for (i1, i2) in pairs(p)
if !iszero(i2)
result[i1] = i2
end
end
return result
end
27 changes: 27 additions & 0 deletions experimental/SetPartitions/test/LinearPartition-test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@testset "LinearCombinations of SetPartitions" begin
@testset "LinearPartition Constructor" begin
S, d = polynomial_ring(QQ, "d")
@test coefficients(linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 4, set_partition([1, 1], [1, 1]) => 8*d))) == Dict(set_partition([1, 2], [1, 1]) => 4, set_partition([1, 1], [1, 1]) => 8*d)
@test coefficients(linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 0, set_partition([1, 1], [1, 1]) => 8*d))) == Dict(set_partition([1, 1], [1, 1]) => 8*d)
@test coefficients(linear_partition(S, [(set_partition([1, 1], [1, 1]), 5), (set_partition([1, 1], [1, 1]), 4*d)])) == Dict(set_partition([1, 1], [1, 1]) => 5 + 4*d)
@test coefficients(linear_partition(S, [(set_partition([1, 1], [1, 1]), 10), (set_partition([1, 1], [1, 1]), 4*d), (set_partition([1, 1], [1, 2]), 4*d), (set_partition([1, 1], [1, 1]), S(0))])) == Dict(set_partition([1, 1], [1, 2]) => 4*d, set_partition([1, 1], [1, 1]) => 4*d + 10)
@test_throws ArgumentError linear_partition(S, [(spatial_partition([2, 4], [4, 99], 2), 4), (set_partition([1, 1], [1, 1]), 4*d)])
end

@testset "LinearPartition Operations" begin
S, d = polynomial_ring(QQ, "d")
a = linear_partition(S, [(set_partition([1, 2], [1, 1]), 5), (set_partition([1, 1], [1, 1]), 5*d)])
@test a + a == linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 10, set_partition([1, 1], [1, 1]) => 10*d))
@test a + linear_partition(S, [(set_partition([1, 1], [1, 1]), 1), (set_partition([1, 1], [1, 1]), 2*d)]) == linear_partition(S, [(set_partition([1, 2], [1, 1]), 5), (set_partition([1, 1], [1, 1]), 7*d + 1)])

@test 2 * linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 10, set_partition([1, 1], [1, 1]) => 8*d)) == linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 20, set_partition([1, 1], [1, 1]) => 16*d))
@test (1 // 2) * linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 8, set_partition([1, 1], [1, 1]) => 8*d)) == linear_partition(S, Dict(SetPartition([1, 2], [1, 1]) => 4, SetPartition([1, 1], [1, 1]) => 4*d))

a = linear_partition(S, [(set_partition([1, 2], [1, 1]), 4), (set_partition([1, 1], [1, 1]), 4*d)])
@test compose(a, a, d) == linear_partition(S, Dict(set_partition([1, 2], [1, 1]) => 16*d + 16, set_partition([1, 1], [1, 1]) => 16*d^2 + 16*d))

@test tensor_product(a, a) == linear_partition(S, Dict(set_partition([1, 1, 2, 2], [1, 1, 2, 2]) => 16*d^2, set_partition([1, 2, 3, 3], [1, 1, 3, 3]) => 16*d, set_partition([1, 2, 3, 4], [1, 1, 3, 3]) => 16, set_partition([1, 1, 2, 3], [1, 1, 2, 2]) => 16*d))

@test a - a == linear_partition(S, Dict(set_partition([1, 1], [1, 1]) => 0))
end
end
Loading