Skip to content

Commit

Permalink
Fix some combinatorics (#3860)
Browse files Browse the repository at this point in the history
* Make sure the combinatorics functions work for every integer type

* Fix `is_standard` for negative values

* Fix documentation of Schur polynomials

* Fix `schur_polynomial`

* Change one-line/terse printing of tableaux
  • Loading branch information
joschmitt authored Jun 17, 2024
1 parent 8a1076f commit 265e89c
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 110 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Schur polynomials

Given a partition $\lambda$ of $n$, the **Schur polynomial** is defined to be
Given a partition $\lambda$ with $n$ parts, the **Schur polynomial** is defined to be
the polynomial

$$s_\lambda := \sum x_1^{m_1}\dots x_n^{m_n}$$
Expand Down
25 changes: 11 additions & 14 deletions src/Combinatorics/EnumerativeCombinatorics/partitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,9 @@ function Base.iterate(P::Partitions{T}) where T
return partition(T[1], check=false), (T[1], 1, 0)
end

d = fill( T(1), n )
d = fill(T(1), Int(n))
d[1] = n
return partition(d[1:1], check=false), (d, 1, 1)

end

@inline function Base.iterate(P::Partitions{T}, state::Tuple{Vector{T}, Int, Int}) where T
Expand Down Expand Up @@ -381,7 +380,7 @@ function Base.iterate(P::PartitionsFixedNumParts{T}) where T
only_distinct_parts = P.distinct_parts

if n == 0 && k == 0
return partition(T[], check=false), (T[], T[], 0, 0, 1, false)
return partition(T[], check=false), (T[], T[], T(0), T(0), 1, false)
end

# This iterator should be empty
Expand All @@ -391,17 +390,17 @@ function Base.iterate(P::PartitionsFixedNumParts{T}) where T

if n == k && lb == 1
only_distinct_parts && k > 1 && return nothing
return partition(T[1 for i in 1:n], check=false), (T[], T[], 0, 0, 1, false)
return partition(T[1 for i in 1:n], check=false), (T[], T[], T(0), T(0), 1, false)
end

if k == 1 && lb <= n <= ub
return partition(T[n], check=false), (T[], T[], 0, 0, 1, false)
return partition(T[n], check=false), (T[], T[], T(0), T(0), 1, false)
end

x = zeros(T,k)
y = zeros(T,k)
jj = only_distinct_parts*k*(k-1)
N = n - k*lb - div(jj,2)
N = T(n - k*lb - div(jj,2))
L2 = ub-lb
0 <= N <= k*L2 - jj || return nothing

Expand All @@ -410,7 +409,7 @@ function Base.iterate(P::PartitionsFixedNumParts{T}) where T
end

i = 1
L2 = L2 - only_distinct_parts*(k-1)
L2 = L2 - only_distinct_parts*T(k-1)

while N > L2
N -= L2
Expand All @@ -421,15 +420,15 @@ function Base.iterate(P::PartitionsFixedNumParts{T}) where T
return partition(x[1:k], check = false), (x, y, N, L2, i, true)
end

@inline function Base.iterate(P::PartitionsFixedNumParts{T}, state::Tuple{Vector{T}, Vector{T}, T, IntegerUnion, Int, Bool}) where T
@inline function Base.iterate(P::PartitionsFixedNumParts{T}, state::Tuple{Vector{T}, Vector{T}, T, T, Int, Bool}) where T
k = P.k
x, y, N, L2, i, flag = state

N == 0 && return nothing

if flag
if i < k && N > 1
N = 1
N = T(1)
x[i] = x[i] - 1
i += 1
x[i] = y[i] + 1
Expand All @@ -442,12 +441,11 @@ end
flag = false
end
end

if !flag
lcycle = false
for j in i - 1:-1:1
L2 = x[j] - y[j] - 1
N = N + 1
L2 = x[j] - y[j] - T(1)
N = N + T(1)
if N <= (k-j)*L2
x[j] = y[j] + L2
lcycle = true
Expand All @@ -457,7 +455,6 @@ end
x[i] = y[i]
i = j
end

lcycle || return nothing
while N > L2
N -= L2
Expand Down Expand Up @@ -769,7 +766,7 @@ end
x[i] = m
i -= 1
i == 0 && break # inner while loop
r = ii[i]
r = Int(ii[i])
N = N + x[i] - m
m = y[i]
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function schur_polynomial_cbf(R::ZZMPolyRing, lambda::Partition{T}, n::Int = len
end

#calculate sub_dets[2:n] using Laplace extension
exp = zeros(Int, n)
exp = zeros(Int, ngens(R))
for i = 2:n
for (columnview, ) in sub_dets[i]
d = R() #the alternating sum of minors
Expand All @@ -134,8 +134,11 @@ function schur_polynomial_cbf(R::ZZMPolyRing, lambda::Partition{T}, n::Int = len

#multiply by the factorized term
factor = MPolyBuildCtx(R)
exp = zeros(Int, n)
exp[columnview] .= exp_incr[i]
exp = zeros(Int, ngens(R))
for j in 1:n
columnview[j] || continue
exp[j] = exp_incr[i]
end
push_term!(factor, one(ZZ), exp)
sub_dets[i][columnview] = mul!(d, d, finish(factor))
end
Expand Down
5 changes: 2 additions & 3 deletions src/Combinatorics/EnumerativeCombinatorics/tableaux.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ young_tableau(v::Vector{Vector{T}}; check::Bool = true) where T <: IntegerUnion
data(tab::YoungTableau) = tab.t

function Base.show(io::IO, tab::YoungTableau)
print(io, "Young tableau")
# TODO: is there meaningful information to add in one-line mode?
print(io, data(tab))
end

function Base.show(io::IO, ::MIME"text/plain", tab::YoungTableau)
Expand Down Expand Up @@ -591,7 +590,7 @@ function is_standard(tab::YoungTableau)
numbs = falses(n)
for i = 1:length(s)
for j = 1:s[i]
if tab[i][j] > n
if tab[i][j] < 1 || tab[i][j] > n
return false
end
numbs[tab[i][j]] = true
Expand Down
6 changes: 3 additions & 3 deletions src/Combinatorics/EnumerativeCombinatorics/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ struct CompositionsFixedNumParts{T<:IntegerUnion}
if k > n
# 1 does not have any weak compositions into 0 parts, so this will
# produce an empty iterator
nk = 1
nk = T(1)
kk = 0
else
nk = n - k
nk = n - T(k)
kk = k
end
return new{T}(n, k, weak_compositions(nk, kk))
Expand Down Expand Up @@ -165,7 +165,7 @@ struct PartitionsFixedNumParts{T<:IntegerUnion}
if lb == 0
lb = 1
end
return new{T}(n, convert(T, k), T(lb), T(ub), only_distinct_parts)
return new{T}(n, Int(k), T(lb), T(ub), only_distinct_parts)
end

end
Expand Down
33 changes: 20 additions & 13 deletions test/Combinatorics/EnumerativeCombinatorics/compositions.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
@testset "compositions" begin
@testset "Compositions with integer type $T" for T in [Int, Int8, ZZRingElem]
# Check some stupid cases
@test number_of_compositions(0, 0) == 1
@test number_of_compositions(0, 1) == 0
@test number_of_compositions(1, 0) == 0
@test number_of_compositions(0) == 1
@test number_of_compositions(T(0), T(0)) == 1
@test number_of_compositions(T(0), T(1)) == 0
@test number_of_compositions(T(1), T(0)) == 0
@test number_of_compositions(T(0)) == 1

# First few number of compositions from https://oeis.org/A011782
nums = [1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592]

# Check if number_of_compositions is correct in these examples
@test [number_of_compositions(n) for n in 0:length(nums) - 1] == nums
@test [number_of_compositions(T(n)) for n in 0:length(nums) - 1] == nums

# Complete check of compositions for small cases
for n in 0:5
# We'll piece together all compositions of n by the compositions
# of n into k parts for 0 <= k <= n
allcomps = []
for k in 0:n
C = collect(compositions(n, k))
@test length(C) == number_of_compositions(n, k)
C = @inferred collect(compositions(T(n), T(k)))
@test C isa Vector{Oscar.Composition{T}}
@test length(C) == number_of_compositions(T(n), T(k))

# Check if each composition consists of k parts and sums up to n
for c in C
Expand All @@ -37,19 +38,25 @@
@test allcomps == unique(allcomps)

# Number of compositions needs to be correct
@test length(allcomps) == number_of_compositions(n)
@test length(allcomps) == number_of_compositions(T(n))

# Finally, check compositions(n) function
allcomps2 = collect(compositions(n))
allcomps2 = @inferred collect(compositions(T(n)))
@test allcomps2 isa Vector{Oscar.Composition{T}}
@test allcomps2 == unique(allcomps2)
@test Set(allcomps) == Set(allcomps2)
end

# Test the case k > n
C = @inferred collect(compositions(T(2), T(3)))
@test C isa Vector{Oscar.Composition{T}}
@test isempty(C)
end

@testset "Ascending compositions" begin
@testset "Ascending compositions with integer type $T" for T in [Int, Int8, ZZRingElem]
for n in 0:20
C = collect(ascending_compositions(n))
@test length(C) == number_of_partitions(n)
C = @inferred collect(ascending_compositions(T(n)))
@test length(C) == number_of_partitions(T(n))
@test C == unique(C)
for lambda in C
@test sum(lambda) == n
Expand Down
Loading

0 comments on commit 265e89c

Please sign in to comment.