# -*- coding: utf-8 -*-
r"""
Sage has have a builtin `StrongTableau <https://doc.sagemath.org/html/en/reference/combinat/sage/combinat/k_tableau.html>`_ object. *This* module contains useful functions pertaining to Standard Marked Tableau (SMT), the most standard form of a StrongTableau (a strong tableau with standard weight).
AUTHORS:
- George Seelinger (2018): Initial version
- Matthew Lancellotti (2018): End core to strong marked tableaux
REFERENCES:
"""
#*****************************************************************************
# Copyright (C) 2018 George Seelinger <ghseeli@gmail.com> and
# 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
def shape_cell_indices(shape):
# r"""
# Takes a shape (partition or list of lists) and returns the
# indices of the cells in the shape.
# """
shape = Partition(shape)
return shape.cells()
def tableau_cell_indices(tab):
# r"""
# Takes a tableau (or list of lists) and returns the indices
# of the cells in the tableau.
# """
tab = Tableau(tab)
return shape_cell_indices(tab.shape())
def tableau_contains(outer, inner):
# r"""
# Checks to see if `inner` tableau is contained in the `outer`
# tableau exactly. Somewhat dangerous function.
# """
ol = outer.to_list()
il = inner.to_list()
result = True
indices = tableau_cell_indices(inner)
for (row_index, col_index) in indices:
if il[row_index][col_index] != ol[row_index][col_index] and il[row_index][col_index] is not None:
result = False
break
return result
def strong_tableau_quotient2(outer_tab, inner_shape):
# r"""
# Takes a strong tableau and skews it; that is, it sets all the
# cells in `outer_tab` that are contained in `inner_shape` to `None`
# and returns a `Tableau` object.
# """
ol = outer_tab.to_list()
indices = shape_cell_indices(inner_shape)
for (row_index, col_index) in indices:
ol[row_index][col_index] = None
return Tableau(ol)
def strong_tableau_quotient(outer, inner):
# r"""
# Takes a strong tableau and skews it; that is, it sets all the
# cells in `outer_tab` that are contained in `inner_shape` to `None`
# and returns a `StrongTableau` object.
# """
assert tableau_contains(outer, inner)
ol = outer.to_list()
indices = tableau_cell_indices(inner)
for (row_index, col_index) in indices:
ol[row_index][col_index] = None
return StrongTableau(ol, outer.k)
[docs]def strong_tableau_has_row_marking(tab, row_index):
r""" Returns whether ``tab`` has a row marking in row ``row_index``.
Checks if a strong tableau ``tab`` has a row marking in row
``row_index`` (row indexing starts at 0) and returns ``True``
if row_marking is present; ``False`` otherwise.
.. SEEALSO::
:meth:`is_row_markable`
"""
# WARNING: Indices start at 0
if len(tab) <= row_index:
return False
row = tab[row_index]
result = (True in [i is not None and i < 0 for i in row])
return result
def add_skew_tabs(tab1, tab2):
# Delete this
# Dangerous if overlapping entries / has 0's
m1 = matrix(RationalField(), tab1.to_list())
m2 = matrix(RationalField(), tab2.to_list())
added = m1+m2
tab_list = [map(lambda i: None if i == 0 else i, row) for row in added]
return Tableau(tab_list)
def add_skew_tab_to_tab(tab, skew_tab):
# r"""
# Given a tableau `tab` as a base, this function layers a skew tableau
# `skew_tab` on top of it.
# """
skew_list = skew_tab.to_list()
# Necessary because skew strong tableaux does not get item correctly?
tab_list = tab.to_list()
for (i, j) in tableau_cell_indices(tab):
skew_list[i][j] = tab_list[i][j]
return Tableau(skew_list)
def shape_to_homogeneous_tab(outer_shape, entry, inner_shape=[]):
li = []
for i in range(len(outer_shape)):
row = []
if len(inner_shape) > i:
row = row + [None]*inner_shape[i] + \
[entry]*(outer_shape[i]-inner_shape[i])
else:
row = row + [entry]*outer_shape[i]
li.append(row)
return Tableau(li)
def std_strong_tab_from_core_sequence(core_sequence, k, markings):
# 'markings' are (row,col) coordinates
if len(core_sequence) == 0:
return StrongTableaux(k, [], []).an_element()
wt = [1]*(len(core_sequence)-1)
core_zero = core_sequence[0]
base = Tableau([[None]*i for i in core_zero])
for core_index in range(1, len(core_sequence)):
skew = shape_to_homogeneous_tab(
core_sequence[core_index], core_index, core_sequence[core_index-1])
base = add_skew_tab_to_tab(base, skew)
return StrongTableaux.add_marking(base, markings, k, wt)
def _strong_marked_tableau(lis, k):
# helper to create a strong marked tableau
st = StrongTableau(lis, k)
for w in st.weight():
if w != 1:
raise ValueError('Not a standard weight SMT.')
if st.to_list() != st.to_standard_list():
raise ValueError('Not a standard SMT.')
return st
def k_coverees1(root, k):
# THIS FUNCTIONALITY IS ALREADY BUILTIN. See algorithm 2 of k_coverees.
# one way to get the k coverees
root = Core(root, k+1)
root = root.to_partition()
# set of coveree candidates (a superset of the coverees)
candidates = set()
def add_to_candidates(ptn, is_root=False):
# add to dictionary if not already there
if ptn not in candidates:
candidates.add(ptn)
# if it is not a leaf, recurse
if is_root or not ptn.is_core(k+1):
for (i, j) in ptn.removable_cells():
sub_ptn = ptn.remove_cell(i, j)
add_to_candidates(sub_ptn)
add_to_candidates(root, is_root=True)
# Now that all k+1-cores (and some other things) have been populated in candidates, filter to the ones that really are k+1-cores and have correct k-boundary size.
coverees = set(ptn for ptn in candidates if ptn.is_core(k+1)
and k_size(ptn, k) == k_size(root, k) - 1)
return coverees
[docs]def k_coverees(core, k, algorithm=1):
# THIS FUNCTIONALITY IS ALREADY BUILTIN. See algorithm 2 below.
r""" Given a `k+1`-core, find all sub-`k+1`-cores that have `k`-boundary 1 less than the given. """
if algorithm == 1:
return k_coverees1(core, k)
elif algorithm == 2:
core = Core(core, k+1)
coveree_core_list = core.strong_down_list()
coverees = set(c.to_partition() for c in coveree_core_list)
return coverees
else:
raise ValueError('Unknown algorithm.')
def __go_to_ribbon_head(cells, start_cell):
# Given the cells of a ribbon or multiple disconnected ribbons, and a starting point, find the head of the ribbon
if start_cell not in cells:
raise ValueError('Starting position is not in list of cells.')
cell = start_cell
while True:
right_cell = (cell[0], cell[1] + 1)
if right_cell in cells:
cell = right_cell
continue
down_cell = (cell[0] - 1, cell[1])
if down_cell in cells:
cell = down_cell
continue
break
# nothing to right or down, so we must have reached head of ribbon
head = cell
return head
[docs]def row_marking_to_marking(outer_core, inner_core, row_marking):
r""" Convert a row marking to a marking.
Given the skew shape defined by ``outer_core`` and ``inner_core``, and the row index ``row_marking`` where a marking exists, return the marking `(\text{marking row index}, \text{marking column index})`.
Note that `\text{marking row index}` mentioned above is simply ``row_marking``. Therefore, the real usefulness of this function is that it finds the column index of the marking.
EXAMPLES:
The skew shape [3, 2, 2] / [2, 1] depicted by
.. image:: _static/marking.JPG
:width: 140px
:align: center
:alt: The skew shape [3, 2, 2] / [2, 1]
has the marking `(0, 2)` in row `0`. Hence::
sage: row_marking_to_marking([3, 2, 2], [2, 1], 0)
(0, 2)
It also has the marking `(1, 1)` in row `1`. Therefore::
sage: row_marking_to_marking([3, 2, 2], [2, 1], 1)
(1, 1)
It has no marking in row `2`, so::
sage: row_marking_to_marking([3, 2, 2], [2, 1], 2)
Traceback (most recent call last):
...
ValueError: no such row marking
.. SEEALSO::
:meth:`row_markings_to_markings`
"""
sp = SkewPartition([outer_core, inner_core])
cells = sp.cells()
row_indices = set(cell[0] for cell in cells)
if row_marking not in row_indices:
raise ValueError('no such row marking')
start_cell = (row_marking, skew_partition.right(sp, row_marking))
head = __go_to_ribbon_head(cells, start_cell)
if head[0] == row_marking:
return head
else:
raise ValueError('no such row marking')
[docs]def row_markings_to_markings(core_sequence, row_markings):
r""" Given a ``core_sequence`` and corresponding ``row_markings`` for each cover of the sequence, convert the row markings to markings and return them.
Each row marking in ``row_markings`` is merely the row index of where the marking occurs. The purpose of this function is to convert each row marking to a "marking" which includes the column index. In order to understand this function, it is best to understand :meth:`row_marking_to_marking` first.
EXAMPLES:
Consider the core sequence ([], [1], [1, 1], [2, 1, 1]) which corresponds to the sequence of skew shapes
.. image:: _static/markings.JPG
:height: 140px
:align: center
:alt: The skew shapes [1] / []; [1, 1] / [1]; and [2, 1, 1] / [1, 1].
Now consider the row markings 0, 1, and 2, respectively. Pair each skew shape up with its respective row marking, and then convert the row marking to a marking. Row marking 0 corresponds to marking `(0, 0)`, row marking 1 corresponds to marking `(1, 0)`, and row marking 2 corresponds to marking `(2, 0)`::
sage: row_markings_to_markings(([], [1], [1, 1], [2, 1, 1]), [0, 1, 2])
[(0, 0), (1, 0), (2, 0)]
Similarly::
sage: row_markings_to_markings(([], [1], [1, 1], [2, 1, 1]), [0, 1, 0])
[(0, 0), (1, 0), (0, 1)]
.. SEEALSO::
:meth:`row_marking_to_marking`
"""
assert len(core_sequence) == len(row_markings) + 1
markings = []
for index in range(len(row_markings)):
# get vars
row_marking = row_markings[index]
inner_core = core_sequence[index]
outer_core = core_sequence[index + 1]
# convert to marking
marking = row_marking_to_marking(outer_core, inner_core, row_marking)
markings.append(marking)
return markings
[docs]def is_row_markable(outer_core, inner_core, row_marking):
r""" Given two cores (typically consecutive cores in a core sequence), see if ``row_marking`` is a possible row_marking of ``outer_core`` / ``inner_core``.
EXAMPLES:
Consider the skew shape [3, 2, 2] / [2, 1] depicted
.. image:: _static/marking.JPG
:height: 140px
:align: center
:alt: The skew shape [3, 2, 2] / [2, 1]
It has a marking in row 0 and row 1, but not in row 2::
sage: is_row_markable([3, 2, 2], [2, 1], 0)
True
sage: is_row_markable([3, 2, 2], [2, 1], 1)
True
sage: is_row_markable([3, 2, 2], [2, 1], 2)
False
.. SEEALSO::
:meth:`strong_tableau_has_row_marking`
"""
try:
row_marking_to_marking(outer_core, inner_core, row_marking)
return True
except ValueError:
return False
[docs]def k_marked_coverees(core, k, row_marking):
r""" Given a k+1-core, find all sub-k+1-cores that have k-boundary 1 less than the given with the given row_marking.
INPUTS:
- ``core`` -- a `k+1`-core
- ``k`` -- an integer
- ``row_marking`` -- The desired row marking for the covers
OUTPUTS:
The set of all `k+1`-cores `\lambda` that satify:
* ``core`` is a cover of `\lambda`
* The skew shape ``core`` / `\lambda` has a marking in the row indexed ``row_marking``.
EXAMPLES::
sage: k_marked_coverees([6, 4, 2, 2, 1], 5, 0)
{[5, 4, 2, 2, 1]}
sage: k_marked_coverees([6, 4, 2, 2, 1], 5, 1)
{[6, 2, 2, 2, 1], [6, 3, 2, 2]}
sage: k_marked_coverees([6, 4, 2, 2, 1], 5, 2)
{}
sage: k_marked_coverees([6, 4, 2, 2, 1], 5, 3)
{[6, 4, 2, 1, 1]}
sage: k_marked_coverees([6, 4, 2, 2, 1], 5, 4)
{[6, 3, 2, 2]}
"""
coverees = k_coverees(core, k)
marked_coverees = [c for c in coverees
if is_row_markable(core, c, row_marking)]
return set(marked_coverees)
[docs]def end_core_to_marked_core_sequences(end_core, k, row_markings):
r"""
Return the set of core sequences marked by ``row_markings`` ending in ``end_core``.
INPUTS:
- ``end_core`` -- a `k+1`-core
- ``k`` -- All of the cores in the core sequences are `k+1`-cores.
- ``row_markings`` -- vector of row-indices indicating the rows of the markings for each core sequence. (Note that "markings" are row-col-coordinates, while "row_markings" are merely row-coordinates.)
OUTPUTS:
- A set of all possible core sequences that end in ``end_core`` and can be marked by the row marking vector ``row_markings``.
EXAMPLES::
sage: end_core_to_marked_core_sequences([5, 3, 1], 2, [0, 1, 2, 0, 1])
{([], [1], [1, 1], [2, 1, 1], [3, 1, 1], [5, 3, 1])}
sage: end_core_to_marked_core_sequences([5, 3, 1], 2, [1])
{([3, 1, 1], [5, 3, 1]), ([4, 2], [5, 3, 1])}
.. SEEALSO::
:meth:`end_core_to_strong_marked_tableaux`
"""
# check inputs
k = NonNegativeIntegerSemiring()(k)
end_core = Core(end_core, k+1)
end_core = end_core.to_partition()
for row_marking in row_markings:
NonNegativeIntegerSemiring()(row_marking)
# find marked core sequences
sequences = []
if not row_markings:
# base case
sequences.append(tuple([end_core]))
else:
# inductive step
coverees = k_marked_coverees(end_core, k, row_markings[-1])
for coveree in coverees:
prefix_sequences = end_core_to_marked_core_sequences(
coveree, k, row_markings[:-1])
for prefix_sequence in prefix_sequences:
sequence = prefix_sequence + tuple([end_core])
sequences.append(sequence)
return set(sequences)
[docs]def end_core_to_strong_marked_tableaux(end_core, k, row_markings):
r"""
Return the set of strong marked tableaux marked by ``row_markings`` ending in ``end_core``.
INPUTS:
- ``end_core`` -- a `k+1`-core
- ``k`` -- All of the cores in the core sequences are `k+1`-cores.
- ``row_markings`` -- vector of row-indices indicating the rows of the markings for each core sequence. (Note that "markings" are row-col-coordinates, while "row_markings" are merely row-coordinates.)
OUTPUTS:
- A set of all possible strong marked tableau that end in ``end_core`` and can be marked by the row marking vector ``row_markings``.
EXAMPLES::
sage: end_core_to_strong_marked_tableaux([5, 3, 1], 2, [0, 1, 2, 0, 1])
{[[-1, 3, -4, 5, 5], [-2, 5, -5], [-3]]}
sage: end_core_to_strong_marked_tableaux([5, 3, 1], 2, [1])
{[[None, None, None, 1, 1], [None, 1, -1], [None]],
[[None, None, None, None, 1], [None, None, -1], [1]]}
.. SEEALSO::
:meth:`end_core_to_marked_core_sequences`
"""
core_sequences = end_core_to_marked_core_sequences(
end_core, k, row_markings)
smts = set()
for core_sequence in core_sequences:
markings = row_markings_to_markings(core_sequence, row_markings)
smt = std_strong_tab_from_core_sequence(core_sequence, k, markings)
smts.add(smt)
return smts