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 29 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
207 changes: 207 additions & 0 deletions experimental/SetPartitions/src/LinearPartition.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""
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,
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
where the first element in the tuple is a `RingElement` and the second
a `SetPartition`. Furthermore simplify the term before initializing
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
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 75 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L74 - L75 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 89 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L88 - L89 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 98 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L96-L98

Added lines #L96 - L98 were not covered by tests
end
q = linear_partition(

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

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L100

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

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

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L103 - L104 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"
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
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.
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved

# 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"
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
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"
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
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 195 in experimental/SetPartitions/src/LinearPartition.jl

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L195

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

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

View check run for this annotation

Codecov / codecov/patch

experimental/SetPartitions/src/LinearPartition.jl#L197

Added line #L197 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
31 changes: 26 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 Down Expand Up @@ -51,6 +63,10 @@ export set_partition
export spatial_partition
export upper_colors
export upper_points
export simplify_operation_zero
export simplify_operation
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
export linear_partition
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved



include("AbstractPartition.jl")
Expand All @@ -60,13 +76,15 @@ 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 Down Expand Up @@ -94,3 +112,6 @@ export set_partition
export spatial_partition
export upper_colors
export upper_points
export simplify_operation_zero
export simplify_operation
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
export linear_partition
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
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> simplify_operation([(set_partition([1, 1], [1, 1]), S(10)), (set_partition([1, 1], [1, 1]), 4*d)])
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
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/LinearComb-test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@testset "LinearCombinations of SetPartitions" begin
sebvz777 marked this conversation as resolved.
Show resolved Hide resolved
@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