# -*- coding: utf-8 -*-
r"""
Sage does *not* have a builtin 'kShape' object. *This* module contains useful functions pertaining to `k`-shapes:
AUTHORS:
- Matthew Lancellotti (2018): Initial version
REFERENCES:
.. [genocchi] `Combinatorics of k-shapes and Genocchi numbers <https://www.lri.fr/~hivert/PAPER/kshapes.pdf>`_, in FPSAC 2011, Reykjav´k, Iceland DMTCS proc. AO, 2011, 493-504.
"""
#*****************************************************************************
# Copyright (C) 2018 Matthew Lancellotti <mvlancellotti@gmail.com>
#
# Distributed under the terms of the GNU General Public License (GPL)
# http://www.gnu.org/licenses/
#*****************************************************************************
from sage.all import *
from partition import *
import skew_partition
SetPartitionsAk = None
SetPartitionsBk = None
SetPartitionsIk = None
SetPartitionsPRk = None
SetPartitionsPk = None
SetPartitionsRk = None
SetPartitionsSk = None
SetPartitionsTk = None
# kShape verifier
[docs]def is_k_shape(ptn, k):
r""" A partition is a `k`-*shape* if its `k`-boundary has row-shape and col-shape that are partitions themselves. (Definition 2.1 of [genocchi]_)
Given a :class:`Partition` ``ptn`` and a natural number ``k``, returns ``True`` if and only if ``ptn`` is a `k`-shape.
Given a :class:`Partition` ``ptn`` *only*, returns ``True`` if and only if there exists some `k \in [1, n-1]` such that ``ptn`` is a `k`-shape.
EXAMPLES::
sage: is_k_shape(Partition([3, 1]), 1)
False
sage: is_k_shape(Partition([3, 1]), 2)
True
"""
ptn = Partition(ptn)
if k is None:
# see if it's a k-shape for any k in [1, n-1].
# (note that every partition is a 0-shape and an n-shape)
n = ptn.size()
lis = [is_k_shape(ptn, kk) for kk in range(1, n)]
return any(lis)
else:
k_bdy = ptn.k_boundary(k)
return skew_partition.is_linked(k_bdy)
# kShape stuff
[docs]def h_bounds(p, k, width):
r""" Recall the `H_i` as defined in Definition 3.3 of [genocchi]_.
Given a natural number ``k`` (used for the `k`-shape or `k`-boundary) and a width ``width``, returns `(y_\text{min}, y_\text{max})`, the two vertical coordinates which define the horizontal strip.
EXAMPLES:
The 4-boundary of partition (10, 7, 4, 2, 2, 2, 1, 1, 1, 1) is shown below on a cartesian plane with the vertical lines corresponding to the vertical bounds shown in blue.
.. image:: _static/h-v-bounds.png
:height: 240px
:align: center
:alt: The skew-shape (10, 7, 4, 2, 2, 2, 1, 1, 1, 1) / (7, 4, 2, 1, 1, 1) is shown on a cartesian plane with the vertical lines y = 0, y = 1, y = 2, and y = 10 labelled.
::
sage: h_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=3)
(0, 2)
sage: h_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=2)
(2, 3)
sage: h_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=1)
(3, 10)
::
sage: h_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=99)
(0, 0)
sage: h_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=0)
Traceback (most recent call last):
...
ValueError: min() arg is an empty sequence
.. SEEALSO::
:meth:`v_bounds`
"""
assert is_k_shape(p, k)
r = k_row_lengths(p, k)
# pad with a row of infinite length and a row of length 0
r = [float('inf')] + r + [0]
y_min = max([j for j in range(0, len(r)) if r[j] > width])
y_max = min([j for j in range(0, len(r)) if r[j] < width]) - 1
return (y_min, y_max)
[docs]def v_bounds(p, k, height):
r""" This is `V_i`, the vertical analog of :meth:`h_bounds`.
EXAMPLES:
The 4-boundary of partition (10, 7, 4, 2, 2, 2, 1, 1, 1, 1) is shown below on a cartesian plane with the vertical lines corresponding to the vertical bounds shown in blue.
.. image:: _static/h-v-bounds.png
:height: 240px
:align: center
:alt: The skew-shape (10, 7, 4, 2, 2, 2, 1, 1, 1, 1) / (7, 4, 2, 1, 1, 1) is shown on a cartesian plane with the vertical lines y = 0, y = 1, y = 2, and y = 10 labelled.
::
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=4)
(0, 1)
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=3)
(1, 2)
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=2)
(2, 2)
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=1)
(2, 10)
::
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=99)
(0, 0)
sage: v_bounds(Partition([10, 7, 4, 2, 2, 2, 1, 1, 1, 1]), k=4, width=0)
Traceback (most recent call last):
...
ValueError: min() arg is an empty sequence
.. SEEALSO::
:meth:`h_bounds`
"""
return h_bounds(p.conjugate(), k, height)
[docs]def is_k_reducible_by_rectangle(p, k, hw):
r""" Checks if the ``k``-shape is `k`-reducible for a `k`-rectangle of specific dimensions `h` x `w`.
See Proposition 3.8 in Combinatorics of k-shapes and Genocchi numbers.
INPUTS:
- ``p`` -- a Partition (and a `k`-shape)
- ``k`` -- the `k` of the `k`-shape
- ``hw`` -- an ordered pair ``hw`` = `(h, w)`, where `h` is the height of the rectangle and `w` is the width.
EXAMPLES:
sage: Partition([1]).is_k_reducible_by_rectangle(1, (1,1))
sage: True
sage: Partition([2, 1]).is_k_reducible_by_rectangle(1, (1,1))
sage: True
sage: Partition([1, 1]).is_k_reducible_by_rectangle(2, (1,1))
sage: False
sage: Partition([1, 1]).is_k_reducible_by_rectangle(2, (1,2))
sage: True
sage: Partition([1, 1]).is_k_reducible_by_rectangle(2, (2,1))
sage: False
.. SEEALSO::
:meth:`is_reducible`
"""
assert is_k_shape(p, k)
(h, w) = hw
assert h + w - 1 == k or h + w - 1 == k - 1
# get intersection H_a \cap V_b \cap k_rim
rim = k_rim(p, k)
(y_min, y_max) = h_bounds(p, k, h)
(x_min, x_max) = v_bounds(p, k, w)
intersection_rim = [(x, y) for (x, y) in rim
if x_min <= x <= x_max and y_min <= y <= y_max]
# check condition (iii) of Proposition 3.8
if not intersection_rim:
return False
else:
# min_y is DIFFERENT than y_min
min_y = intersection_rim[0][1]
max_y = intersection_rim[-1][1]
return max_y - min_y >= w
[docs]def is_reducible(ptn, k):
r""" A ``k``-shape ``ptn`` is called *reducible* if there exists a `k`- or `k-1`-rectangle corresponding to both the `k`-row-shape and `k`-column-shape of `ptn`.
For a more rigorous definition, see Definition 3.7 of [genocchi]_.
Note that this is different than the definition of a reducible partition!
Given a `k`-shape ``ptn`` and a natural number ``k``, returns ``True`` if and only if ``ptn`` is reducible.
(Also, a `k`-shape is reducible if and only if it is not irreducible.)
EXAMPLES:
The partition [3, 2, 1] has 3-row-shape [2, 2, 1] and 3-column-shape [2, 2, 1]. It is 3-reducible because there exists a 2x2-rectangle R in the 3-row-shape and the cells that make up R when viewed in the 3-column-shape form a 2x2-rectangle (you can't see it, but the 2's are switched here)::
sage: is_reducible(Partition([3, 2, 1]), k=3)
True
In this example, no good rectangle can be found::
sage: is_reducible(Partition([5, 3, 2, 1, 1]), k=4)
False
.. SEEALSO::
:meth:`is_irreducible`, :meth:`k_to_irreducible_k_shapes`
"""
rect_dim_list = k_rectangle_dimension_list(
k) + k_rectangle_dimension_list(k-1)
for (a, b) in rect_dim_list:
if is_k_reducible_by_rectangle(ptn, k, (a, b)):
return True
return False
[docs]def is_irreducible(s, k):
r""" A ``k``-shape ``ptn`` is called *irreducible* if there does *not* exist a `k`- or `k-1`-rectangle corresponding to both the `k`-row-shape and `k`-column-shape of `ptn`.
For a more rigorous definition, see Definition 3.7 of [genocchi]_.
Given a `k`-shape ``ptn`` and a natural number ``k``, returns ``True`` if and only if ``ptn`` is irreducible.
(Also, a `k`-shape is irreducible if and only if it is not reducible.)
EXAMPLES:
The partition [3, 2, 1] has 3-row-shape [2, 2, 1] and 3-column-shape [2, 2, 1]. It is not 3-irreducible because there exists a 2x2-rectangle R in the 3-row-shape and the cells that make up R when viewed in the 3-column-shape form a 2x2-rectangle (you can't see it, but the 2's are switched here)::
sage: is_irreducible(Partition([3, 2, 1]), k=3)
False
In this example, no good rectangle can be found, making it irreducible::
sage: is_irreducible(Partition([5, 3, 2, 1, 1]), k=4)
True
.. SEEALSO::
:meth:`is_reducible`, :meth:`k_to_irreducible_k_shapes`
"""
return not is_reducible(s, k)
############# GETTER FUNCS ############
[docs]def k_to_irreducible_k_shapes(k):
r""" Given a natural number ``k``, return a list of all irreducible `k`-shapes.
Note that the algorithm runs very slowly after `k=4` :(.
EXAMPLES::
sage: k_to_irreducible_k_shapes(3)
[[], [1], [2, 1]]
.. SEEALSO::
:meth:`is_reducible`, :meth:`is_irreducible`
"""
bound = (k-1)*k/2
n_bound = bound**2
ptns = []
for n in range(0, n_bound+1):
ptns += Partitions(n, max_length=bound, max_part=bound)
k_irr_k_shapes = [p for p in ptns
if is_k_shape(p, k) and is_irreducible(p, k)]
return k_irr_k_shapes