Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- r""" Integer partitions
A partition `p` of a nonnegative integer `n` is a non-increasing list of positive integers (the *parts* of the partition) with total sum `n`.
A partition can be depicted by a diagram made of rows of cells, where the number of cells in the `i^{th}` row starting from the top is the `i^{th}` part of the partition.
The coordinate system related to a partition applies from the top to the bottom and from left to right. So, the corners of the partition `[5, 3, 1]` are `[[0,4], [1,2], [2,0]]`.
For display options, see :obj:`Partitions.options`.
.. NOTE::
- Boxes is a synonym for cells. All methods will use 'cell' and 'cells' instead of 'box' and 'boxes'.
- Partitions are 0 based with coordinates in the form of (row-index, column-index).
- If given coordinates of the form ``(r, c)``, then use Python's \*-operator.
- Throughout this documentation, for a partition `\lambda` we will denote its conjugate partition by `\lambda^{\prime}`. For more on conjugate partitions, see :meth:`Partition.conjugate()`.
- The comparisons on partitions use lexicographic order.
.. NOTE::
We use the convention that lexicographic ordering is read from left-to-right. That is to say `[1, 3, 7]` is smaller than `[2, 3, 4]`.
AUTHORS:
- Mike Hansen (2007): initial version
- Dan Drake (2009-03-28): deprecate RestrictedPartitions and implement Partitions_parts_in
- Travis Scrimshaw (2012-01-12): Implemented latex function to Partition_class
- Travis Scrimshaw (2012-05-09): Fixed Partitions(-1).list() infinite recursion loop by saying Partitions_n is the empty set.
- Travis Scrimshaw (2012-05-11): Fixed bug in inner where if the length was longer than the length of the inner partition, it would include 0's.
- Andrew Mathas (2012-06-01): Removed depreciated functions and added compatibility with the PartitionTuple classes. See :trac:`13072`
- Travis Scrimshaw (2012-10-12): Added options. Made ``Partition_class`` to the element ``Partition``. ``Partitions*`` are now all in the category framework except ``PartitionsRestricted`` (which will eventually be removed). Cleaned up documentation.
EXAMPLES:
There are `5` partitions of the integer `4`::
sage: Partitions(4).cardinality() 5 sage: Partitions(4).list() [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
We can use the method ``.first()`` to get the 'first' partition of a number::
sage: Partitions(4).first() [4]
Using the method ``.next(p)``, we can calculate the 'next' partition after `p`. When we are at the last partition, ``None`` will be returned::
sage: Partitions(4).next([4]) [3, 1] sage: Partitions(4).next([1,1,1,1]) is None True
We can use ``iter`` to get an object which iterates over the partitions one by one to save memory. Note that when we do something like ``for part in Partitions(4)`` this iterator is used in the background::
sage: g = iter(Partitions(4)) sage: next(g) [4] sage: next(g) [3, 1] sage: next(g) [2, 2] sage: for p in Partitions(4): print(p) [4] [3, 1] [2, 2] [2, 1, 1] [1, 1, 1, 1]
We can add constraints to the type of partitions we want. For example, to get all of the partitions of `4` of length `2`, we'd do the following::
sage: Partitions(4, length=2).list() [[3, 1], [2, 2]]
Here is the list of partitions of length at least `2` and the list of ones with length at most `2`::
sage: Partitions(4, min_length=2).list() [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: Partitions(4, max_length=2).list() [[4], [3, 1], [2, 2]]
The options ``min_part`` and ``max_part`` can be used to set constraints on the sizes of all parts. Using ``max_part``, we can select partitions having only 'small' entries. The following is the list of the partitions of `4` with parts at most `2`::
sage: Partitions(4, max_part=2).list() [[2, 2], [2, 1, 1], [1, 1, 1, 1]]
The ``min_part`` options is complementary to ``max_part`` and selects partitions having only 'large' parts. Here is the list of all partitions of `4` with each part at least `2`::
sage: Partitions(4, min_part=2).list() [[4], [2, 2]]
The options ``inner`` and ``outer`` can be used to set part-by-part constraints. This is the list of partitions of `4` with ``[3, 1, 1]`` as an outer bound (that is, partitions of `4` contained in the partition ``[3, 1, 1]``)::
sage: Partitions(4, outer=[3,1,1]).list() [[3, 1], [2, 1, 1]]
``outer`` sets ``max_length`` to the length of its argument. Moreover, the parts of ``outer`` may be infinite to clear constraints on specific parts. Here is the list of the partitions of `4` of length at most `3` such that the second and third part are `1` when they exist::
sage: Partitions(4, outer=[oo,1,1]).list() [[4], [3, 1], [2, 1, 1]]
Finally, here are the partitions of `4` with ``[1,1,1]`` as an inner bound (i. e., the partitions of `4` containing the partition ``[1,1,1]``). Note that ``inner`` sets ``min_length`` to the length of its argument::
sage: Partitions(4, inner=[1,1,1]).list() [[2, 1, 1], [1, 1, 1, 1]]
The options ``min_slope`` and ``max_slope`` can be used to set constraints on the slope, that is on the difference ``p[i+1]-p[i]`` of two consecutive parts. Here is the list of the strictly decreasing partitions of `4`::
sage: Partitions(4, max_slope=-1).list() [[4], [3, 1]]
The constraints can be combined together in all reasonable ways. Here are all the partitions of `11` of length between `2` and `4` such that the difference between two consecutive parts is between `-3` and `-1`::
sage: Partitions(11,min_slope=-3,max_slope=-1,min_length=2,max_length=4).list() [[7, 4], [6, 5], [6, 4, 1], [6, 3, 2], [5, 4, 2], [5, 3, 2, 1]]
Partition objects can also be created individually with :class:`Partition`::
sage: Partition([2,1]) [2, 1]
Once we have a partition object, then there are a variety of methods that we can use. For example, we can get the conjugate of a partition. Geometrically, the conjugate of a partition is the reflection of that partition through its main diagonal. Of course, this operation is an involution::
sage: Partition([4,1]).conjugate() [2, 1, 1, 1] sage: Partition([4,1]).conjugate().conjugate() [4, 1]
If we create a partition with extra zeros at the end, they will be dropped::
sage: Partition([4,1,0,0]) [4, 1] sage: Partition([0]) [] sage: Partition([0,0]) []
The idea of a partition being followed by infinitely many parts of size `0` is consistent with the ``get_part`` method::
sage: p = Partition([5, 2]) sage: p.get_part(0) 5 sage: p.get_part(10) 0
We can go back and forth between the standard and the exponential notations of a partition. The exponential notation can be padded with extra zeros::
sage: Partition([6,4,4,2,1]).to_exp() [1, 1, 0, 2, 0, 1] sage: Partition(exp=[1,1,0,2,0,1]) [6, 4, 4, 2, 1] sage: Partition([6,4,4,2,1]).to_exp(5) [1, 1, 0, 2, 0, 1] sage: Partition([6,4,4,2,1]).to_exp(7) [1, 1, 0, 2, 0, 1, 0] sage: Partition([6,4,4,2,1]).to_exp(10) [1, 1, 0, 2, 0, 1, 0, 0, 0, 0]
We can get the (zero-based!) coordinates of the corners of a partition::
sage: Partition([4,3,1]).corners() [(0, 3), (1, 2), (2, 0)]
We can compute the core and quotient of a partition and build the partition back up from them::
sage: Partition([6,3,2,2]).core(3) [2, 1, 1] sage: Partition([7,7,5,3,3,3,1]).quotient(3) ([2], [1], [2, 2, 2]) sage: p = Partition([11,5,5,3,2,2,2]) sage: p.core(3) [] sage: p.quotient(3) ([2, 1], [4], [1, 1, 1]) sage: Partition(core=[],quotient=([2, 1], [4], [1, 1, 1])) [11, 5, 5, 3, 2, 2, 2]
We can compute the `0-1` sequence and go back and forth::
sage: Partitions().from_zero_one([1, 1, 1, 1, 0, 1, 0]) [5, 4] sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) ....: == mu for n in range(5) for mu in Partitions(n)) True
We can compute the Frobenius coordinates and go back and forth::
sage: Partition([7,3,1]).frobenius_coordinates() ([6, 1], [2, 0]) sage: Partition(frobenius_coordinates=([6,1],[2,0])) [7, 3, 1] sage: all(mu == Partition(frobenius_coordinates=mu.frobenius_coordinates()) ....: for n in range(30) for mu in Partitions(n)) True
We use the lexicographic ordering::
sage: pl = Partition([4,1,1]) sage: ql = Partitions()([3,3]) sage: pl > ql True sage: PL = Partitions() sage: pl = PL([4,1,1]) sage: ql = PL([3,3]) sage: pl > ql True """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import
import six from six.moves import range
from sage.interfaces.all import gap from sage.libs.all import pari from sage.libs.flint.arith import number_of_partitions as flint_number_of_partitions
from sage.structure.global_options import GlobalOptions from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.symbolic.ring import var
from sage.misc.lazy_import import lazy_import lazy_import('sage.combinat.skew_partition', 'SkewPartition') lazy_import('sage.combinat.partition_tuple', 'PartitionTuple')
from sage.misc.all import prod from sage.misc.prandom import randrange from sage.misc.cachefunc import cached_method, cached_function
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
from sage.sets.non_negative_integers import NonNegativeIntegers from sage.rings.all import QQ, ZZ, NN, IntegerModRing from sage.arith.all import factorial, gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer from sage.rings.infinity import infinity
from .combinat import CombinatorialClass, CombinatorialElement from . import tableau from . import permutation from . import composition from sage.combinat.partitions import number_of_partitions as bober_number_of_partitions from sage.combinat.partitions import ZS1_iterator, ZS1_iterator_nk from sage.combinat.integer_vector import IntegerVectors from sage.combinat.integer_lists import IntegerListsLex from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.combinatorial_map import combinatorial_map from sage.groups.perm_gps.permgroup import PermutationGroup from sage.graphs.dot2tex_utils import have_dot2tex
class Partition(CombinatorialElement): r""" A partition `p` of a nonnegative integer `n` is a non-increasing list of positive integers (the *parts* of the partition) with total sum `n`.
A partition is often represented as a diagram consisting of **cells**, or **boxes**, placed in rows on top of each other such that the number of cells in the `i^{th}` row, reading from top to bottom, is the `i^{th}` part of the partition. The rows are left-justified (and become shorter and shorter the farther down one goes). This diagram is called the **Young diagram** of the partition, or more precisely its Young diagram in English notation. (French and Russian notations are variations on this representation.)
The coordinate system related to a partition applies from the top to the bottom and from left to right. So, the corners of the partition ``[5, 3, 1]`` are ``[[0,4], [1,2], [2,0]]``.
For display options, see :meth:`Partitions.options`.
.. NOTE::
Partitions are 0 based with coordinates in the form of (row-index, column-index). For example consider the partition ``mu=Partition([4,3,2,2])``, the first part is ``mu[0]`` (which is 4), the second is ``mu[1]``, and so on, and the upper-left cell in English convention is ``(0, 0)``.
A partition can be specified in one of the following ways:
- a list (the default) - using exponential notation - by Frobenius coordinates - specifying its `0-1` sequence - specifying the core and the quotient
See the examples below.
EXAMPLES:
Creating partitions though parents::
sage: mu = Partitions(8)([3,2,1,1,1]); mu [3, 2, 1, 1, 1] sage: nu = Partition([3,2,1,1,1]); nu [3, 2, 1, 1, 1] sage: mu == nu True sage: mu is nu False sage: mu in Partitions() True sage: mu.parent() Partitions of the integer 8 sage: mu.size() 8 sage: mu.category() Category of elements of Partitions of the integer 8 sage: nu.parent() Partitions sage: nu.category() Category of elements of Partitions sage: mu[0] 3 sage: mu[1] 2 sage: mu[2] 1 sage: mu.pp() *** ** * * * sage: mu.removable_cells() [(0, 2), (1, 1), (4, 0)] sage: mu.down_list() [[2, 2, 1, 1, 1], [3, 1, 1, 1, 1], [3, 2, 1, 1]] sage: mu.addable_cells() [(0, 3), (1, 2), (2, 1), (5, 0)] sage: mu.up_list() [[4, 2, 1, 1, 1], [3, 3, 1, 1, 1], [3, 2, 2, 1, 1], [3, 2, 1, 1, 1, 1]] sage: mu.conjugate() [5, 2, 1] sage: mu.dominates(nu) True sage: nu.dominates(mu) True
Creating partitions using ``Partition``::
sage: Partition([3,2,1]) [3, 2, 1] sage: Partition(exp=[2,1,1]) [3, 2, 1, 1] sage: Partition(core=[2,1], quotient=[[2,1],[3],[1,1,1]]) [11, 5, 5, 3, 2, 2, 2] sage: Partition(frobenius_coordinates=([3,2],[4,0])) [4, 4, 1, 1, 1] sage: Partitions().from_zero_one([1, 1, 1, 1, 0, 1, 0]) [5, 4] sage: [2,1] in Partitions() True sage: [2,1,0] in Partitions() True sage: Partition([1,2,3]) Traceback (most recent call last): ... ValueError: [1, 2, 3] is not an element of Partitions
Sage ignores trailing zeros at the end of partitions::
sage: Partition([3,2,1,0]) [3, 2, 1] sage: Partitions()([3,2,1,0]) [3, 2, 1] sage: Partitions(6)([3,2,1,0]) [3, 2, 1]
TESTS:
Check that only trailing zeros are stripped::
sage: TestSuite( Partition([]) ).run() sage: TestSuite( Partition([4,3,2,2,2,1]) ).run() sage: Partition([3,2,2,2,1,0,0,0]) [3, 2, 2, 2, 1] sage: Partition([3,0,2,2,2,1,0]) Traceback (most recent call last): ... ValueError: [3, 0, 2, 2, 2, 1, 0] is not an element of Partitions sage: Partition([0,7,3]) Traceback (most recent call last): ... ValueError: [0, 7, 3] is not an element of Partitions """ @staticmethod def __classcall_private__(cls, mu=None, **keyword): """ This constructs a list from optional arguments and delegates the construction of a :class:`Partition` to the ``element_class()`` call of the appropriate parent.
EXAMPLES::
sage: Partition([3,2,1]) [3, 2, 1] sage: Partition(exp=[2,1,1]) [3, 2, 1, 1] sage: Partition(core=[2,1], quotient=[[2,1],[3],[1,1,1]]) [11, 5, 5, 3, 2, 2, 2] """ return _Partitions.from_beta_numbers(keyword['beta_numbers']) elif 'zero_one' in keyword: return _Partitions.from_zero_one(keyword['zero_one'])
raise ValueError('incorrect syntax for Partition()')
def __setstate__(self, state): r""" In order to maintain backwards compatibility and be able to unpickle a old pickle from ``Partition_class`` we have to override the default ``__setstate__``.
EXAMPLES::
sage: loads(b'x\x9ck`J.NLO\xd5K\xce\xcfM\xca\xccK,\xd1+H,*\xc9,\xc9\xcc\xcf\xe3\n\x80\xb1\xe2\x93s\x12\x8b\x8b\xb9\n\x195\x1b\x0b\x99j\x0b\x995BY\xe33\x12\x8b3\nY\xfc\x80\xac\x9c\xcc\xe2\x92B\xd6\xd8B6\r\x88IE\x99y\xe9\xc5z\x99y%\xa9\xe9\xa9E\\\xb9\x89\xd9\xa9\xf10N!{(\xa3qkP!G\x06\x90a\x04dp\x82\x18\x86@\x06Wji\x92\x1e\x00x0.\xb5') [3, 2, 1] sage: loads(dumps( Partition([3,2,1]) )) # indirect doctest [3, 2, 1] """ else:
def __init__(self, parent, mu): """ Initialize ``self``.
EXAMPLES::
sage: p = Partition([3,1]) sage: TestSuite(p).run()
TESTS:
Fix that tuples raise the correct error::
sage: Partition((3,1,7)) Traceback (most recent call last): ... ValueError: [3, 1, 7] is not an element of Partitions """ # Since we are (suppose to be) immutable, we can share the underlying data
and mu[-1] in NN): # strip all trailing zeros else:
else:
@cached_method def __hash__(self): r""" Return the hash of ``self``.
TESTS::
sage: P = Partition([4,2,2,1]) sage: hash(P) == hash(P) True """
def _repr_(self): r""" Return a string representation of ``self`` depending on :meth:`Partitions.options`.
EXAMPLES::
sage: mu=Partition([7,7,7,3,3,2,1,1,1,1,1,1,1]); mu # indirect doctest [7, 7, 7, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1] sage: Partitions.options.display="diagram"; mu ******* ******* ******* *** *** ** * * * * * * * sage: Partitions.options.display="list"; mu [7, 7, 7, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1] sage: Partitions.options.display="compact_low"; mu 1^7,2,3^2,7^3 sage: Partitions.options.display="compact_high"; mu 7^3,3^2,2,1^7 sage: Partitions.options.display="exp_low"; mu 1^7, 2, 3^2, 7^3 sage: Partitions.options.display="exp_high"; mu 7^3, 3^2, 2, 1^7
sage: Partitions.options.convention="French"; sage: mu=Partition([7,7,7,3,3,2,1,1,1,1,1,1,1]); mu # indirect doctest 7^3, 3^2, 2, 1^7 sage: Partitions.options.display="diagram"; mu * * * * * * * ** *** *** ******* ******* ******* sage: Partitions.options.display="list"; mu [7, 7, 7, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1] sage: Partitions.options.display="compact_low"; mu 1^7,2,3^2,7^3 sage: Partitions.options.display="compact_high"; mu 7^3,3^2,2,1^7 sage: Partitions.options.display="exp_low"; mu 1^7, 2, 3^2, 7^3 sage: Partitions.options.display="exp_high"; mu 7^3, 3^2, 2, 1^7
sage: Partitions.options._reset() """
def _ascii_art_(self): """ TESTS::
sage: ascii_art(Partitions(5).list()) [ * ] [ ** * ] [ *** ** * * ] [ **** *** * ** * * ] [ *****, * , ** , * , * , * , * ] """
def _unicode_art_(self): """ TESTS::
sage: unicode_art(Partitions(5).list()) ⎡ ┌┐ ⎤ ⎢ ┌┬┐ ├┤ ⎥ ⎢ ┌┬┬┐ ┌┬┐ ├┼┘ ├┤ ⎥ ⎢ ┌┬┬┬┐ ┌┬┬┐ ├┼┴┘ ├┼┤ ├┤ ├┤ ⎥ ⎢ ┌┬┬┬┬┐ ├┼┴┴┘ ├┼┼┘ ├┤ ├┼┘ ├┤ ├┤ ⎥ ⎣ └┴┴┴┴┘, └┘ , └┴┘ , └┘ , └┘ , └┘ , └┘ ⎦ sage: Partitions.options.convention = "French" sage: unicode_art(Partitions(5).list()) ⎡ ┌┐ ⎤ ⎢ ┌┐ ├┤ ⎥ ⎢ ┌┐ ┌┐ ├┤ ├┤ ⎥ ⎢ ┌┐ ┌┬┐ ├┤ ├┼┐ ├┤ ├┤ ⎥ ⎢ ┌┬┬┬┬┐ ├┼┬┬┐ ├┼┼┐ ├┼┬┐ ├┼┤ ├┼┐ ├┤ ⎥ ⎣ └┴┴┴┴┘, └┴┴┴┘, └┴┴┘, └┴┴┘, └┴┘, └┴┘, └┘ ⎦ sage: Partitions.options._reset() """ return u'∅' else:
else:
def _repr_list(self): """ Return a string representation of ``self`` as a list.
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_list()) [7, 7, 7, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1] """
def _repr_exp_low(self): """ Return a string representation of ``self`` in exponential form (lowest first).
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_exp_low()) 1^7, 2, 3^2, 7^3 sage: print(Partition([])._repr_exp_low()) - """ for (m,e) in enumerate(exp) if e > 0)
def _repr_exp_high(self): """ Return a string representation of ``self`` in exponential form (highest first).
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_exp_high()) 7^3, 3^2, 2, 1^7
sage: print(Partition([])._repr_exp_high()) - """ for (m,e) in enumerate(exp) if e>0)
def _repr_compact_low(self): """ Return a string representation of ``self`` in compact form (exponential form with lowest first).
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_compact_low()) 1^7,2,3^2,7^3 sage: print(Partition([])._repr_compact_low()) - """ for (m,e) in enumerate(exp) if e > 0)
def _repr_compact_high(self): """ Return a string representation of ``self`` in compact form (exponential form with highest first).
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_compact_high()) 7^3,3^2,2,1^7 sage: print(Partition([])._repr_compact_low()) - """ for (m,e) in enumerate(exp) if e>0)
def _repr_diagram(self): r""" Return a representation of ``self`` as a Ferrers diagram.
EXAMPLES::
sage: print(Partition([7,7,7,3,3,2,1,1,1,1,1,1,1])._repr_diagram()) ******* ******* ******* *** *** ** * * * * * * * """
def level(self): """ Return the level of ``self``, which is always 1.
This method exists only for compatibility with :class:`PartitionTuples`.
EXAMPLES::
sage: Partition([4,3,2]).level() 1 """
def components(self): """ Return a list containing the shape of ``self``.
This method exists only for compatibility with :class:`PartitionTuples`.
EXAMPLES::
sage: Partition([3,2]).components() [[3, 2]] """
def _latex_(self): r""" Return a LaTeX version of ``self``.
For more on the latex options, see :meth:`Partitions.options`.
EXAMPLES::
sage: mu = Partition([2, 1]) sage: Partitions.options.latex='diagram'; latex(mu) # indirect doctest {\def\lr#1{\multicolumn{1}{@{\hspace{.6ex}}c@{\hspace{.6ex}}}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\\ \lr{\ast}&\lr{\ast}\\ \lr{\ast}\\ \end{array}$} } sage: Partitions.options.latex='exp_high'; latex(mu) # indirect doctest 2,1 sage: Partitions.options.latex='exp_low'; latex(mu) # indirect doctest 1,2 sage: Partitions.options.latex='list'; latex(mu) # indirect doctest [2, 1] sage: Partitions.options.latex='young_diagram'; latex(mu) # indirect doctest {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} \lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-2} \lr{\phantom{x}}\\\cline{1-1} \end{array}$} }
sage: Partitions.options(latex="young_diagram", convention="french") sage: Partitions.options.latex='exp_high'; latex(mu) # indirect doctest 2,1 sage: Partitions.options.latex='exp_low'; latex(mu) # indirect doctest 1,2 sage: Partitions.options.latex='list'; latex(mu) # indirect doctest [2, 1] sage: Partitions.options.latex='young_diagram'; latex(mu) # indirect doctest {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[t]{*{2}c}\cline{1-1} \lr{\phantom{x}}\\\cline{1-2} \lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-2} \end{array}$} }
sage: Partitions.options._reset() """
def _latex_young_diagram(self): r""" LaTeX output as a Young diagram.
EXAMPLES::
sage: print(Partition([2, 1])._latex_young_diagram()) {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} \lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-2} \lr{\phantom{x}}\\\cline{1-1} \end{array}$} } sage: print(Partition([])._latex_young_diagram()) {\emptyset} """
def _latex_diagram(self): r""" LaTeX output as a Ferrers' diagram.
EXAMPLES::
sage: print(Partition([2, 1])._latex_diagram()) {\def\lr#1{\multicolumn{1}{@{\hspace{.6ex}}c@{\hspace{.6ex}}}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\\ \lr{\ast}&\lr{\ast}\\ \lr{\ast}\\ \end{array}$} } sage: print(Partition([])._latex_diagram()) {\emptyset} """
def _latex_list(self): r""" LaTeX output as a list.
EXAMPLES::
sage: print(Partition([2, 1])._latex_list()) [2, 1] sage: print(Partition([])._latex_list()) [] """
def _latex_exp_low(self): r""" LaTeX output in exponential notation (lowest first).
EXAMPLES::
sage: print(Partition([2,2,1])._latex_exp_low()) 1,2^{2} sage: print(Partition([])._latex_exp_low()) {\emptyset} """ for (m,e) in enumerate(exp) if e > 0)
def _latex_exp_high(self): r""" LaTeX output in exponential notation (highest first).
EXAMPLES::
sage: print(Partition([2,2,1])._latex_exp_high()) 2^{2},1 sage: print(Partition([])._latex_exp_high()) {\emptyset} """ for (m,e) in enumerate(exp) if e>0)
def ferrers_diagram(self): r""" Return the Ferrers diagram of ``self``.
EXAMPLES::
sage: mu=Partition([5,5,2,1]) sage: Partitions.options(diagram_str='*', convention="english") sage: print(mu.ferrers_diagram()) ***** ***** ** * sage: Partitions.options(diagram_str='#') sage: print(mu.ferrers_diagram()) ##### ##### ## # sage: Partitions.options.convention="french" sage: print(mu.ferrers_diagram()) # ## ##### ##### sage: print(Partition([]).ferrers_diagram()) - sage: Partitions.options(diagram_str='-') sage: print(Partition([]).ferrers_diagram()) (/) sage: Partitions.options._reset() """ else:
def pp(self): r""" Prints the Ferrers diagram.
See :meth:`ferrers_diagram` for more on the Ferrers diagram.
EXAMPLES::
sage: Partition([5,5,2,1]).pp() ***** ***** ** * sage: Partitions.options.convention='French' sage: Partition([5,5,2,1]).pp() * ** ***** ***** sage: Partitions.options._reset() """
def __truediv__(self, p): """ Returns the skew partition ``self / p``.
EXAMPLES::
sage: p = Partition([3,2,1]) sage: p/[1,1] [3, 2, 1] / [1, 1] sage: p/[3,2,1] [3, 2, 1] / [3, 2, 1] sage: p/Partition([1,1]) [3, 2, 1] / [1, 1] sage: p/[2,2,2] Traceback (most recent call last): ... ValueError: To form a skew partition p/q, q must be contained in p. """
__div__ = __truediv__
def power(self, k): r""" Return the cycle type of the `k`-th power of any permutation with cycle type ``self`` (thus describes the powermap of symmetric groups).
Equivalent to GAP's ``PowerPartition``.
EXAMPLES::
sage: p = Partition([5,3]) sage: p.power(1) [5, 3] sage: p.power(2) [5, 3] sage: p.power(3) [5, 1, 1, 1] sage: p.power(4) [5, 3]
Now let us compare this to the power map on `S_8`::
sage: G = SymmetricGroup(8) sage: g = G([(1,2,3,4,5),(6,7,8)]) sage: g (1,2,3,4,5)(6,7,8) sage: g^2 (1,3,5,2,4)(6,8,7) sage: g^3 (1,4,2,5,3) sage: g^4 (1,5,4,3,2)(6,7,8)
::
sage: Partition([3,2,1]).power(3) [2, 1, 1, 1, 1] """
def __next__(self): """ Return the partition that lexicographically follows ``self``. If ``self`` is the last partition, then return ``False``.
EXAMPLES::
sage: next(Partition([4])) [3, 1] sage: next(Partition([1,1,1,1])) False """
#Check to see if we are at the last (all ones) partition
# #If we are not, then run the ZS1 algorithm. #
#Let h be the number of non-one entries in the #partition
else:
else: h += 1 next_p[h-1] = t
next = __next__
def size(self): """ Return the size of ``self``.
EXAMPLES::
sage: Partition([2,2]).size() 4 sage: Partition([3,2,1]).size() 6 """
def sign(self): r""" Return the sign of any permutation with cycle type ``self``.
This function corresponds to a homomorphism from the symmetric group `S_n` into the cyclic group of order 2, whose kernel is exactly the alternating group `A_n`. Partitions of sign `1` are called even partitions while partitions of sign `-1` are called odd.
EXAMPLES::
sage: Partition([5,3]).sign() 1 sage: Partition([5,2]).sign() -1
Zolotarev's lemma states that the Legendre symbol `\left(\frac{a}{p}\right)` for an integer `a \pmod p` (`p` a prime number), can be computed as sign(p_a), where sign denotes the sign of a permutation and p_a the permutation of the residue classes `\pmod p` induced by modular multiplication by `a`, provided `p` does not divide `a`.
We verify this in some examples.
::
sage: F = GF(11) sage: a = F.multiplicative_generator();a 2 sage: plist = [int(a*F(x)) for x in range(1,11)]; plist [2, 4, 6, 8, 10, 1, 3, 5, 7, 9]
This corresponds to the permutation (1, 2, 4, 8, 5, 10, 9, 7, 3, 6) (acting the set `\{1,2,...,10\}`) and to the partition [10].
::
sage: p = PermutationGroupElement('(1, 2, 4, 8, 5, 10, 9, 7, 3, 6)') sage: p.sign() -1 sage: Partition([10]).sign() -1 sage: kronecker_symbol(11,2) -1
Now replace `2` by `3`::
sage: plist = [int(F(3*x)) for x in range(1,11)]; plist [3, 6, 9, 1, 4, 7, 10, 2, 5, 8] sage: list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sage: p = PermutationGroupElement('(3,4,8,7,9)') sage: p.sign() 1 sage: kronecker_symbol(3,11) 1 sage: Partition([5,1,1,1,1,1]).sign() 1
In both cases, Zolotarev holds.
REFERENCES:
:wikipedia:`Zolotarev's_lemma` """
def standard_tableaux(self): """ Return the :class:`standard tableaux<StandardTableaux>` of this shape.
EXAMPLES::
sage: Partition([3,2,2,1]).standard_tableaux() Standard tableaux of shape [3, 2, 2, 1] """
def up(self): r""" Returns a generator for partitions that can be obtained from ``self`` by adding a cell.
EXAMPLES::
sage: [p for p in Partition([2,1,1]).up()] [[3, 1, 1], [2, 2, 1], [2, 1, 1, 1]] sage: [p for p in Partition([3,2]).up()] [[4, 2], [3, 3], [3, 2, 1]] sage: [p for p in Partition([]).up()] [[1]] """ else:
def up_list(self): """ Return a list of the partitions that can be formed from ``self`` by adding a cell.
EXAMPLES::
sage: Partition([2,1,1]).up_list() [[3, 1, 1], [2, 2, 1], [2, 1, 1, 1]] sage: Partition([3,2]).up_list() [[4, 2], [3, 3], [3, 2, 1]] sage: Partition([]).up_list() [[1]] """
def down(self): r""" Return a generator for partitions that can be obtained from ``self`` by removing a cell.
EXAMPLES::
sage: [p for p in Partition([2,1,1]).down()] [[1, 1, 1], [2, 1]] sage: [p for p in Partition([3,2]).down()] [[2, 2], [3, 1]] sage: [p for p in Partition([3,2,1]).down()] [[2, 2, 1], [3, 1, 1], [3, 2]]
TESTS:
We check that :trac:`11435` is fixed::
sage: Partition([]).down_list() #indirect doctest [] """ else:
def down_list(self): """ Return a list of the partitions that can be obtained from ``self`` by removing a cell.
EXAMPLES::
sage: Partition([2,1,1]).down_list() [[1, 1, 1], [2, 1]] sage: Partition([3,2]).down_list() [[2, 2], [3, 1]] sage: Partition([3,2,1]).down_list() [[2, 2, 1], [3, 1, 1], [3, 2]] sage: Partition([]).down_list() #checks :trac:`11435` [] """
@combinatorial_map(name="cell poset") def cell_poset(self, orientation="SE"): """ Return the Young diagram of ``self`` as a poset. The optional keyword variable ``orientation`` determines the order relation of the poset.
The poset always uses the set of cells of the Young diagram of ``self`` as its ground set. The order relation of the poset depends on the ``orientation`` variable (which defaults to ``"SE"``). Concretely, ``orientation`` has to be specified to one of the strings ``"NW"``, ``"NE"``, ``"SW"``, and ``"SE"``, standing for "northwest", "northeast", "southwest" and "southeast", respectively. If ``orientation`` is ``"SE"``, then the order relation of the poset is such that a cell `u` is greater or equal to a cell `v` in the poset if and only if `u` lies weakly southeast of `v` (this means that `u` can be reached from `v` by a sequence of south and east steps; the sequence is allowed to consist of south steps only, or of east steps only, or even be empty). Similarly the order relation is defined for the other three orientations. The Young diagram is supposed to be drawn in English notation.
The elements of the poset are the cells of the Young diagram of ``self``, written as tuples of zero-based coordinates (so that `(3, 7)` stands for the `8`-th cell of the `4`-th row, etc.).
EXAMPLES::
sage: p = Partition([3,3,1]) sage: Q = p.cell_poset(); Q Finite poset containing 7 elements sage: sorted(Q) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0)] sage: sorted(Q.maximal_elements()) [(1, 2), (2, 0)] sage: Q.minimal_elements() [(0, 0)] sage: sorted(Q.upper_covers((1, 0))) [(1, 1), (2, 0)] sage: Q.upper_covers((1, 1)) [(1, 2)]
sage: P = p.cell_poset(orientation="NW"); P Finite poset containing 7 elements sage: sorted(P) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0)] sage: sorted(P.minimal_elements()) [(1, 2), (2, 0)] sage: P.maximal_elements() [(0, 0)] sage: P.upper_covers((2, 0)) [(1, 0)] sage: sorted(P.upper_covers((1, 2))) [(0, 2), (1, 1)] sage: sorted(P.upper_covers((1, 1))) [(0, 1), (1, 0)] sage: sorted([len(P.upper_covers(v)) for v in P]) [0, 1, 1, 1, 1, 2, 2]
sage: R = p.cell_poset(orientation="NE"); R Finite poset containing 7 elements sage: sorted(R) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0)] sage: R.maximal_elements() [(0, 2)] sage: R.minimal_elements() [(2, 0)] sage: sorted([len(R.upper_covers(v)) for v in R]) [0, 1, 1, 1, 1, 2, 2] sage: R.is_isomorphic(P) False sage: R.is_isomorphic(P.dual()) False
Linear extensions of ``p.cell_poset()`` are in 1-to-1 correspondence with standard Young tableaux of shape `p`::
sage: all( len(p.cell_poset().linear_extensions()) ....: == len(p.standard_tableaux()) ....: for n in range(8) for p in Partitions(n) ) True
This is not the case for northeast orientation::
sage: q = Partition([3, 1]) sage: q.cell_poset(orientation="NE").is_chain() True
TESTS:
We check that the posets are really what they should be for size up to `7`::
sage: def check_NW(n): ....: for p in Partitions(n): ....: P = p.cell_poset(orientation="NW") ....: for c in p.cells(): ....: for d in p.cells(): ....: if P.le(c, d) != (c[0] >= d[0] ....: and c[1] >= d[1]): ....: return False ....: return True sage: all( check_NW(n) for n in range(8) ) True
sage: def check_NE(n): ....: for p in Partitions(n): ....: P = p.cell_poset(orientation="NE") ....: for c in p.cells(): ....: for d in p.cells(): ....: if P.le(c, d) != (c[0] >= d[0] ....: and c[1] <= d[1]): ....: return False ....: return True sage: all( check_NE(n) for n in range(8) ) True
sage: def test_duality(n, ori1, ori2): ....: for p in Partitions(n): ....: P = p.cell_poset(orientation=ori1) ....: Q = p.cell_poset(orientation=ori2) ....: for c in p.cells(): ....: for d in p.cells(): ....: if P.lt(c, d) != Q.lt(d, c): ....: return False ....: return True sage: all( test_duality(n, "NW", "SE") for n in range(8) ) True sage: all( test_duality(n, "NE", "SW") for n in range(8) ) True sage: all( test_duality(n, "NE", "SE") for n in range(4) ) False """ else: else: else: else: else:
def frobenius_coordinates(self): """ Return a pair of sequences of Frobenius coordinates aka beta numbers of the partition.
These are two strictly decreasing sequences of nonnegative integers of the same length.
EXAMPLES::
sage: Partition([]).frobenius_coordinates() ([], []) sage: Partition([1]).frobenius_coordinates() ([0], [0]) sage: Partition([3,3,3]).frobenius_coordinates() ([2, 1, 0], [2, 1, 0]) sage: Partition([9,1,1,1,1,1,1]).frobenius_coordinates() ([8], [6])
""" else:
def frobenius_rank(self): r""" Return the Frobenius rank of the partition ``self``.
The Frobenius rank of a partition `\lambda = (\lambda_1, \lambda_2, \lambda_3, \cdots)` is defined to be the largest `i` such that `\lambda_i \geq i`. In other words, it is the number of cells on the main diagonal of `\lambda`. In yet other words, it is the size of the largest square fitting into the Young diagram of `\lambda`.
EXAMPLES::
sage: Partition([]).frobenius_rank() 0 sage: Partition([1]).frobenius_rank() 1 sage: Partition([3,3,3]).frobenius_rank() 3 sage: Partition([9,1,1,1,1,1]).frobenius_rank() 1 sage: Partition([2,1,1,1,1,1]).frobenius_rank() 1 sage: Partition([2,2,1,1,1,1]).frobenius_rank() 2 sage: Partition([3,2]).frobenius_rank() 2 sage: Partition([3,2,2]).frobenius_rank() 2 sage: Partition([8,4,4,4,4]).frobenius_rank() 4 sage: Partition([8,4,1]).frobenius_rank() 2 sage: Partition([3,3,1]).frobenius_rank() 2 """
def beta_numbers(self, length=None): """ Return the set of beta numbers corresponding to ``self``.
The optional argument ``length`` specifies the length of the beta set (which must be at least the length of ``self``).
For more on beta numbers, see :meth:`frobenius_coordinates`.
EXAMPLES::
sage: Partition([4,3,2]).beta_numbers() [6, 4, 2] sage: Partition([4,3,2]).beta_numbers(5) [8, 6, 4, 1, 0] sage: Partition([]).beta_numbers() [] sage: Partition([]).beta_numbers(3) [2, 1, 0] sage: Partition([6,4,1,1]).beta_numbers() [9, 6, 2, 1] sage: Partition([6,4,1,1]).beta_numbers(6) [11, 8, 4, 3, 1, 0] sage: Partition([1,1,1]).beta_numbers() [3, 2, 1] sage: Partition([1,1,1]).beta_numbers(4) [4, 3, 2, 0] """ raise ValueError("length must be at least the length of the partition")
def crank(self): r""" Return the Dyson crank of ``self``.
The Dyson crank of a partition `\lambda` is defined as follows: If `\lambda` contains at least one `1`, then the crank is `\mu(\lambda) - \omega(\lambda)`, where `\omega(\lambda)` is the number of `1`s in `\lambda`, and `\mu(\lambda)` is the number of parts of `\lambda` larger than `\omega(\lambda)`. If `\lambda` contains no `1`, then the crank is simply the largest part of `\lambda`.
REFERENCES:
.. [AG1988] George E. Andrews, F. G. Garvan, *Dyson's crank of a partition*. Bull. Amer. Math. Soc. (N.S.) Volume 18, Number 2 (1988), 167-171. http://projecteuclid.org/euclid.bams/1183554533
EXAMPLES::
sage: Partition([]).crank() 0 sage: Partition([3,2,2]).crank() 3 sage: Partition([5,4,2,1,1]).crank() 0 sage: Partition([1,1,1]).crank() -3 sage: Partition([6,4,4,3]).crank() 6 sage: Partition([6,3,3,1,1]).crank() 1 sage: Partition([6]).crank() 6 sage: Partition([5,1]).crank() 0 sage: Partition([4,2]).crank() 4 sage: Partition([4,1,1]).crank() -1 sage: Partition([3,3]).crank() 3 sage: Partition([3,2,1]).crank() 1 sage: Partition([3,1,1,1]).crank() -3 sage: Partition([2,2,2]).crank() 2 sage: Partition([2,2,1,1]).crank() -2 sage: Partition([2,1,1,1,1]).crank() -4 sage: Partition([1,1,1,1,1,1]).crank() -6 """
def t_completion(self, t): r""" Return the ``t``-completion of the partition ``self``.
If `\lambda = (\lambda_1, \lambda_2, \lambda_3, \ldots)` is a partition and `t` is an integer greater or equal to `\left\lvert \lambda \right\rvert + \lambda_1`, then the `t`-*completion of* `\lambda` is defined as the partition `(t - \left\lvert \lambda \right\rvert, \lambda_1, \lambda_2, \lambda_3, \ldots)` of `t`. This partition is denoted by `\lambda[t]` in [BOR09]_, by `\lambda_{[t]}` in [BdVO12]_, and by `\lambda(t)` in [CO10]_.
REFERENCES:
.. [BOR09] Emmanuel Briand, Rosa Orellana, Mercedes Rosas. *The stability of the Kronecker products of Schur functions*. :arxiv:`0907.4652v2`.
.. [CO10] Jonathan Comes, Viktor Ostrik. *On blocks of Deligne's category* `\underline{\mathrm{Rep}}(S_t)`. :arxiv:`0910.5695v2`, http://pages.uoregon.edu/jcomes/blocks.pdf
.. [BdVO12] Christopher Bowman, Maud De Visscher, Rosa Orellana. *The partition algebra and the Kronecker coefficients*. :arXiv:`1210.5579v6`.
EXAMPLES::
sage: Partition([]).t_completion(0) [] sage: Partition([]).t_completion(1) [1] sage: Partition([]).t_completion(2) [2] sage: Partition([]).t_completion(3) [3] sage: Partition([2, 1]).t_completion(5) [2, 2, 1] sage: Partition([2, 1]).t_completion(6) [3, 2, 1] sage: Partition([4, 2, 2, 1]).t_completion(13) [4, 4, 2, 2, 1] sage: Partition([4, 2, 2, 1]).t_completion(19) [10, 4, 2, 2, 1] sage: Partition([4, 2, 2, 1]).t_completion(10) Traceback (most recent call last): ... ValueError: 10-completion is not defined sage: Partition([4, 2, 2, 1]).t_completion(5) Traceback (most recent call last): ... ValueError: 5-completion is not defined """
def larger_lex(self, rhs): """ Return ``True`` if ``self`` is larger than ``rhs`` in lexicographic order. Otherwise return ``False``.
EXAMPLES::
sage: p = Partition([3,2]) sage: p.larger_lex([3,1]) True sage: p.larger_lex([1,4]) True sage: p.larger_lex([3,2,1]) False sage: p.larger_lex([3]) True sage: p.larger_lex([5]) False sage: p.larger_lex([3,1,1,1,1,1,1,1]) True """
def dominates(self, p2): r""" Return ``True`` if ``self`` dominates the partition ``p2``. Otherwise it returns ``False``.
EXAMPLES::
sage: p = Partition([3,2]) sage: p.dominates([3,1]) True sage: p.dominates([2,2]) True sage: p.dominates([2,1,1]) True sage: p.dominates([3,3]) False sage: p.dominates([4]) False sage: Partition([4]).dominates(p) False sage: Partition([]).dominates([1]) False sage: Partition([]).dominates([]) True sage: Partition([1]).dominates([]) True """
def cells(self): """ Return the coordinates of the cells of ``self``.
EXAMPLES::
sage: Partition([2,2]).cells() [(0, 0), (0, 1), (1, 0), (1, 1)] sage: Partition([3,2]).cells() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1)] """
def generalized_pochhammer_symbol(self, a, alpha): r""" Return the generalized Pochhammer symbol `(a)_{self}^{(\alpha)}`. This is the product over all cells `(i,j)` in ``self`` of `a - (i-1) / \alpha + j - 1`.
EXAMPLES::
sage: Partition([2,2]).generalized_pochhammer_symbol(2,1) 12 """
def get_part(self, i, default=Integer(0)): r""" Return the `i^{th}` part of ``self``, or ``default`` if it does not exist.
EXAMPLES::
sage: p = Partition([2,1]) sage: p.get_part(0), p.get_part(1), p.get_part(2) (2, 1, 0) sage: p.get_part(10,-1) -1 sage: Partition([]).get_part(0) 0 """ else:
@combinatorial_map(name="partition to minimal Dyck word") def to_dyck_word(self, n=None): r""" Return the ``n``-Dyck word whose corresponding partition is ``self`` (or, if ``n`` is not specified, the `n`-Dyck word with smallest `n` to satisfy this property).
If `w` is an `n`-Dyck word (that is, a Dyck word with `n` open symbols and `n` close symbols), then the Dyck path corresponding to `w` can be regarded as a lattice path in the northeastern half of an `n \times n`-square. The region to the northeast of this Dyck path can be regarded as a partition. It is called the partition corresponding to the Dyck word `w`. (See :meth:`~sage.combinat.dyck_word.DyckWord.to_partition`.)
For every partition `\lambda` and every nonnegative integer `n`, there exists at most one `n`-Dyck word `w` such that the partition corresponding to `w` is `\lambda` (in fact, such `w` exists if and only if `\lambda_i + i \leq n` for every `i`, where `\lambda` is written in the form `(\lambda_1, \lambda_2, \ldots, \lambda_k)` with `\lambda_k > 0`). This method computes this `w` for a given `\lambda` and `n`. If `n` is not specified, this method computes the `w` for the smallest possible `n` for which such an `w` exists. (The minimality of `n` means that the partition demarcated by the Dyck path touches the diagonal.)
EXAMPLES::
sage: Partition([2,2]).to_dyck_word() [1, 1, 0, 0, 1, 1, 0, 0] sage: Partition([2,2]).to_dyck_word(4) [1, 1, 0, 0, 1, 1, 0, 0] sage: Partition([2,2]).to_dyck_word(5) [1, 1, 1, 0, 0, 1, 1, 0, 0, 0] sage: Partition([6,3,1]).to_dyck_word() [1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0] sage: Partition([]).to_dyck_word() [] sage: Partition([]).to_dyck_word(3) [1, 1, 1, 0, 0, 0]
The partition corresponding to ``self.dyck_word()`` is ``self`` indeed::
sage: all( p.to_dyck_word().to_partition() == p ....: for p in Partitions(5) ) True """ # This n is also max(i+j for (i,j) in self.cells()) + 2.
@combinatorial_map(order=2, name="conjugate partition") def conjugate(self): """ Return the conjugate partition of the partition ``self``. This is also called the associated partition or the transpose in the literature.
EXAMPLES::
sage: Partition([2,2]).conjugate() [2, 2] sage: Partition([6,3,1]).conjugate() [3, 2, 2, 1, 1, 1]
The conjugate partition is obtained by transposing the Ferrers diagram of the partition (see :meth:`.ferrers_diagram`)::
sage: print(Partition([6,3,1]).ferrers_diagram()) ****** *** * sage: print(Partition([6,3,1]).conjugate().ferrers_diagram()) *** ** ** * * * """
def suter_diagonal_slide(self, n, exp=1): r""" Return the image of ``self`` in `Y_n` under Suter's diagonal slide `\sigma_n`, where the notations used are those defined in [Sut2002]_.
The set `Y_n` is defined as the set of all partitions `\lambda` such that the hook length of the `(0, 0)`-cell (i.e. the northwestern most cell in English notation) of `\lambda` is less than `n`, including the empty partition.
The map `\sigma_n` sends a partition (with non-zero entries) `(\lambda_1, \lambda_2, \ldots, \lambda_m) \in Y_n` to the partition `(\lambda_2 + 1, \lambda_3 + 1, \ldots, \lambda_m + 1, \underbrace{1, 1, \ldots, 1}_{n - m - \lambda_1\text{ ones}})`. In other words, it pads the partition with trailing zeroes until it has length `n - \lambda_1`, then removes its first part, and finally adds `1` to each part.
By Theorem 2.1 of [Sut2002]_, the dihedral group `D_n` with `2n` elements acts on `Y_n` by letting the primitive rotation act as `\sigma_n` and the reflection act as conjugation of partitions (:meth:`conjugate()`). This action is faithful if `n \geq 3`.
INPUT:
- ``n`` -- nonnegative integer
- ``exp`` -- (default: 1) how many times `\sigma_n` should be applied
OUTPUT:
The result of applying Suter's diagonal slide `\sigma_n` to ``self``, assuming that ``self`` lies in `Y_n`. If the optional argument ``exp`` is set, then the slide `\sigma_n` is applied not just once, but ``exp`` times (note that ``exp`` is allowed to be negative, since the slide has finite order).
EXAMPLES::
sage: Partition([5,4,1]).suter_diagonal_slide(8) [5, 2] sage: Partition([5,4,1]).suter_diagonal_slide(9) [5, 2, 1] sage: Partition([]).suter_diagonal_slide(7) [1, 1, 1, 1, 1, 1] sage: Partition([]).suter_diagonal_slide(1) [] sage: Partition([]).suter_diagonal_slide(7, exp=-1) [6] sage: Partition([]).suter_diagonal_slide(1, exp=-1) [] sage: P7 = Partitions(7) sage: all( p == p.suter_diagonal_slide(9, exp=-1).suter_diagonal_slide(9) ....: for p in P7 ) True sage: all( p == p.suter_diagonal_slide(9, exp=3) ....: .suter_diagonal_slide(9, exp=3) ....: .suter_diagonal_slide(9, exp=3) ....: for p in P7 ) True sage: all( p == p.suter_diagonal_slide(9, exp=6) ....: .suter_diagonal_slide(9, exp=6) ....: .suter_diagonal_slide(9, exp=6) ....: for p in P7 ) True sage: all( p == p.suter_diagonal_slide(9, exp=-1) ....: .suter_diagonal_slide(9, exp=1) ....: for p in P7 ) True
Check of the assertion in [Sut2002]_ that `\sigma_n\bigl( \sigma_n( \lambda^{\prime})^{\prime} \bigr) = \lambda`::
sage: all( p.suter_diagonal_slide(8).conjugate() ....: == p.conjugate().suter_diagonal_slide(8, exp=-1) ....: for p in P7 ) True
Check of Claim 1 in [Sut2002]_::
sage: P5 = Partitions(5) sage: all( all( (p.suter_diagonal_slide(6) in q.suter_diagonal_slide(6).down()) ....: or (q.suter_diagonal_slide(6) in p.suter_diagonal_slide(6).down()) ....: for p in q.down() ) ....: for q in P5 ) True
TESTS:
Check for ``exp = 0``::
sage: P = Partitions(4) sage: all(p == p.suter_diagonal_slide(7, 0) for p in P) True
Check for invalid input::
sage: p = Partition([2,1]) sage: p.hook_length(0, 0) 3 sage: p.suter_diagonal_slide(2) Traceback (most recent call last): ... ValueError: the hook length must be less than n
REFERENCES:
.. [Sut2002] Ruedi Suter. *Young's Lattice and Dihedral Symmetries*. Europ. J. Combinatorics (2002) 23, 233--238. http://www.sciencedirect.com/science/article/pii/S0195669801905414 """ # Check for valid input # Arbitrary exp # Suter's map \sigma_n else: # exp < 0 since if exp == 0, we would exit the while loop # inverse map \sigma_n^{-1}
@combinatorial_map(name="reading tableau") def reading_tableau(self): r""" Return the RSK recording tableau of the reading word of the (standard) tableau `T` labeled down (in English convention) each column to the shape of ``self``.
For an example of the tableau `T`, consider the partition `\lambda = (3,2,1)`, then we have::
1 4 6 2 5 3
For more, see :func:`~sage.combinat.rsk.RSK()`.
EXAMPLES::
sage: Partition([3,2,1]).reading_tableau() [[1, 3, 6], [2, 5], [4]] """
@combinatorial_map(name="initial tableau") def initial_tableau(self): r""" Return the :class:`standard tableau<StandardTableau>` which has the numbers `1, 2, \ldots, n` where `n` is the :meth:`size` of ``self`` entered in order from left to right along the rows of each component, where the components are ordered from left to right.
EXAMPLES::
sage: Partition([3,2,2]).initial_tableau() [[1, 2, 3], [4, 5], [6, 7]] """ # In Python 3, improve this using itertools.accumulate for i in range(len(mu))]
def initial_column_tableau(self): r""" Return the initial column tableau of shape ``self``.
The initial column taleau of shape self is the standard tableau that has the numbers `1` to `n`, where `n` is the :meth:`size` of ``self``, entered in order from top to bottom and then left to right down the columns of ``self``.
EXAMPLES::
sage: Partition([3,2]).initial_column_tableau() [[1, 3, 5], [2, 4]] """
def garnir_tableau(self,*cell): r""" Return the Garnir tableau of shape ``self`` corresponding to the cell ``cell``. If ``cell`` `= (a,c)` then `(a+1,c)` must belong to the diagram of ``self``.
The Garnir tableaux play an important role in integral and non-semisimple representation theory because they determine the "straightening" rules for the Specht modules over an arbitrary ring.
The Garnir tableaux are the "first" non-standard tableaux which arise when you act by simple transpositions. If `(a,c)` is a cell in the Young diagram of a partition, which is not at the bottom of its column, then the corresponding Garnir tableau has the integers `1, 2, \ldots, n` entered in order from left to right along the rows of the diagram up to the cell `(a,c-1)`, then along the cells `(a+1,1)` to `(a+1,c)`, then `(a,c)` until the end of row `a` and then continuing from left to right in the remaining positions. The examples below probably make this clearer!
.. NOTE::
The function also sets ``g._garnir_cell``, where ``g`` is the resulting Garnir tableau, equal to ``cell`` which is used by some other functions.
EXAMPLES::
sage: g=Partition([5,3,3,2]).garnir_tableau((0,2)); g.pp() 1 2 6 7 8 3 4 5 9 10 11 12 13 sage: g.is_row_strict(); g.is_column_strict() True False
sage: Partition([5,3,3,2]).garnir_tableau(0,2).pp() 1 2 6 7 8 3 4 5 9 10 11 12 13 sage: Partition([5,3,3,2]).garnir_tableau(2,1).pp() 1 2 3 4 5 6 7 8 9 12 13 10 11 sage: Partition([5,3,3,2]).garnir_tableau(2,2).pp() Traceback (most recent call last): ... ValueError: (row+1, col) must be inside the diagram
.. SEEALSO::
- :meth:`top_garnir_tableau` """
def top_garnir_tableau(self,e,cell): r""" Return the most dominant *standard* tableau which dominates the corresponding Garnir tableau and has the same ``e``-residue.
The Garnir tableau play an important role in integral and non-semisimple representation theory because they determine the "straightening" rules for the Specht modules. The *top Garnir tableaux* arise in the graded representation theory of the symmetric groups and higher level Hecke algebras. They were introduced in [KMR]_.
If the Garnir node is ``cell=(r,c)`` and `m` and `M` are the entries in the cells ``(r,c)`` and ``(r+1,c)``, respectively, in the initial tableau then the top ``e``-Garnir tableau is obtained by inserting the numbers `m, m+1, \ldots, M` in order from left to right first in the cells in row ``r+1`` which are not in the ``e``-Garnir belt, then in the cell in rows ``r`` and ``r+1`` which are in the Garnir belt and then, finally, in the remaining cells in row ``r`` which are not in the Garnir belt. All other entries in the tableau remain unchanged.
If ``e = 0``, or if there are no ``e``-bricks in either row ``r`` or ``r+1``, then the top Garnir tableau is the corresponding Garnir tableau.
EXAMPLES::
sage: Partition([5,4,3,2]).top_garnir_tableau(2,(0,2)).pp() 1 2 4 5 8 3 6 7 9 10 11 12 13 14 sage: Partition([5,4,3,2]).top_garnir_tableau(3,(0,2)).pp() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 sage: Partition([5,4,3,2]).top_garnir_tableau(4,(0,2)).pp() 1 2 6 7 8 3 4 5 9 10 11 12 13 14 sage: Partition([5,4,3,2]).top_garnir_tableau(0,(0,2)).pp() 1 2 6 7 8 3 4 5 9 10 11 12 13 14
TESTS::
sage: Partition([5,4,3,2]).top_garnir_tableau(0,(3,2)).pp() Traceback (most recent call last): ... ValueError: (4,2)=(row+1,col) must be inside the diagram
REFERENCE:
- [KMR]_ """
# now we will put the number m,m+1,...,t[row+1][col] in order into t
@cached_method def young_subgroup(self): r""" Return the corresponding Young, or parabolic, subgroup of the symmetric group.
The Young subgroup of a partition `\lambda = (\lambda_1, \lambda_2, \ldots, \lambda_{\ell})` of `n` is the group:
.. MATH::
S_{\lambda_1} \times S_{\lambda_2} \times \cdots \times S_{\lambda_{\ell}}
embedded into `S_n` in the standard way (i.e., the `S_{\lambda_i}` factor acts on the numbers from `\lambda_1 + \lambda_2 + \cdots + \lambda_{i-1} + 1` to `\lambda_1 + \lambda_2 + \cdots + \lambda_i`).
EXAMPLES::
sage: Partition([4,2]).young_subgroup() Permutation Group with generators [(), (5,6), (3,4), (2,3), (1,2)] """
def young_subgroup_generators(self): """ Return an indexing set for the generators of the corresponding Young subgroup. Here the generators correspond to the simple adjacent transpositions `s_i = (i \; i+1)`.
EXAMPLES::
sage: Partition([4,2]).young_subgroup_generators() [1, 2, 3, 5] sage: Partition([1,1,1]).young_subgroup_generators() [] sage: Partition([2,2]).young_subgroup_generators() [1, 3]
.. SEEALSO::
:meth:`young_subgroup` """
@cached_method def _initial_degree(self, e, multicharge=(0,)): r""" Return the Brundan-Kleshchev-Wang degree of the initial row tableau of shape ``self``.
This degree depends only the shape of the tableau and it is used as the base case for computing the degrees of all tableau of shape ``self``, which is why this method is cached. See :meth:`sage.combinat.tableau.Tableau.degree` for more information.
EXAMPLES::
sage: Partition([5,3,2])._initial_degree(0) 0 sage: Partition([5,3,2])._initial_degree(2) 4 sage: Partition([5,3,2])._initial_degree(3) 2 sage: Partition([5,3,2])._initial_degree(4) 1 """ else:
def degree(self, e): r""" Return the ``e``-th degree of ``self``.
The `e`-th degree of a partition `\lambda` is the sum of the `e`-th degrees of the standard tableaux of shape `\lambda`. The `e`-th degree is the exponent of `\Phi_e(q)` in the Gram determinant of the Specht module for a semisimple Iwahori-Hecke algebra of type `A` with parameter `q`.
INPUT:
- ``e`` -- an integer `e > 1`
OUTPUT:
A non-negative integer.
EXAMPLES::
sage: Partition([4,3]).degree(2) 28 sage: Partition([4,3]).degree(3) 15 sage: Partition([4,3]).degree(4) 8 sage: Partition([4,3]).degree(5) 13 sage: Partition([4,3]).degree(6) 0 sage: Partition([4,3]).degree(7) 0
Therefore, the Gram determinant of `S(5,3)` when the Hecke parameter `q` is "generic" is
.. MATH::
q^N \Phi_2(q)^{28} \Phi_3(q)^{15} \Phi_4(q)^8 \Phi_5(q)^{13}
for some integer `N`. Compare with :meth:`prime_degree`. """
def prime_degree(self, p): r""" Return the prime degree for the prime integer``p`` for ``self``.
INPUT:
- ``p`` -- a prime integer
OUTPUT:
A non-negative integer
The degree of a partition `\lambda` is the sum of the `e`-:meth:`degree` of the standard tableaux of shape `\lambda`, for `e` a poer of the prime `p`. The prime degree gives the exponent of `p` in the Gram determinant of the integral Specht module of the symmetric group.
EXAMPLES::
sage: Partition([4,3]).prime_degree(2) 36 sage: Partition([4,3]).prime_degree(3) 15 sage: Partition([4,3]).prime_degree(5) 13 sage: Partition([4,3]).prime_degree(7) 0
THerefore, the Gram determinant of `S(5,3)` when `q = 1` is `2^{36} 3^{15} 5^{13}`. Compare with :meth:`degree`. """
def arm_length(self, i, j): r""" Return the length of the arm of cell `(i,j)` in ``self``.
The arm of cell `(i,j)` is the cells that appear to the right of cell `(i,j)`.
The cell coordinates are zero-based, i. e., the northwesternmost cell is `(0,0)`.
INPUT:
- ``i, j`` -- two integers
OUTPUT:
An integer or a ``ValueError``
EXAMPLES::
sage: p = Partition([2,2,1]) sage: p.arm_length(0, 0) 1 sage: p.arm_length(0, 1) 0 sage: p.arm_length(2, 0) 0 sage: Partition([3,3]).arm_length(0, 0) 2 sage: Partition([3,3]).arm_length(*[0,0]) 2 """ else: raise ValueError("The cell is not in the diagram")
def arm_lengths(self, flat=False): """ Return a tableau of shape ``self`` where each cell is filled with its arm length. The optional boolean parameter ``flat`` provides the option of returning a flat list.
EXAMPLES::
sage: Partition([2,2,1]).arm_lengths() [[1, 0], [1, 0], [0]] sage: Partition([2,2,1]).arm_lengths(flat=True) [1, 0, 1, 0, 0] sage: Partition([3,3]).arm_lengths() [[2, 1, 0], [2, 1, 0]] sage: Partition([3,3]).arm_lengths(flat=True) [2, 1, 0, 2, 1, 0] """ else:
def arm_cells(self, i, j): r""" Return the list of the cells of the arm of cell `(i,j)` in ``self``.
The arm of cell `c = (i,j)` is the boxes that appear to the right of `c`.
The cell coordinates are zero-based, i. e., the northwesternmost cell is `(0,0)`.
INPUT:
- ``i, j`` -- two integers
OUTPUT:
A list of pairs of integers
EXAMPLES::
sage: Partition([4,4,3,1]).arm_cells(1,1) [(1, 2), (1, 3)]
sage: Partition([]).arm_cells(0,0) Traceback (most recent call last): ... ValueError: The cell is not in the diagram
""" else:
def leg_length(self, i, j): """ Return the length of the leg of cell `(i,j)` in ``self``.
The leg of cell `c = (i,j)` is defined to be the cells below `c` (in English convention).
The cell coordinates are zero-based, i. e., the northwesternmost cell is `(0,0)`.
INPUT:
- ``i, j`` -- two integers
OUTPUT:
An integer or a ``ValueError``
EXAMPLES::
sage: p = Partition([2,2,1]) sage: p.leg_length(0, 0) 2 sage: p.leg_length(0,1) 1 sage: p.leg_length(2,0) 0 sage: Partition([3,3]).leg_length(0, 0) 1 sage: cell = [0,0]; Partition([3,3]).leg_length(*cell) 1 """
else:
def leg_lengths(self, flat=False): """ Return a tableau of shape ``self`` with each cell filled in with its leg length. The optional boolean parameter ``flat`` provides the option of returning a flat list.
EXAMPLES::
sage: Partition([2,2,1]).leg_lengths() [[2, 1], [1, 0], [0]] sage: Partition([2,2,1]).leg_lengths(flat=True) [2, 1, 1, 0, 0] sage: Partition([3,3]).leg_lengths() [[1, 1, 1], [0, 0, 0]] sage: Partition([3,3]).leg_lengths(flat=True) [1, 1, 1, 0, 0, 0] """ else:
def leg_cells(self, i, j): r""" Return the list of the cells of the leg of cell `(i,j)` in ``self``.
The leg of cell `c = (i,j)` is defined to be the cells below `c` (in English convention).
The cell coordinates are zero-based, i. e., the northwesternmost cell is `(0,0)`.
INPUT:
- ``i, j`` -- two integers
OUTPUT:
A list of pairs of integers
EXAMPLES::
sage: Partition([4,4,3,1]).leg_cells(1,1) [(2, 1)] sage: Partition([4,4,3,1]).leg_cells(0,1) [(1, 1), (2, 1)]
sage: Partition([]).leg_cells(0,0) Traceback (most recent call last): ... ValueError: The cell is not in the diagram """
def attacking_pairs(self): """ Return a list of the attacking pairs of the Young diagram of ``self``.
A pair of cells `(c, d)` of a Young diagram (in English notation) is said to be attacking if one of the following conditions holds:
1. `c` and `d` lie in the same row with `c` strictly to the west of `d`.
2. `c` is in the row immediately to the south of `d`, and `c` lies strictly east of `d`.
This particular method returns each pair `(c, d)` as a tuple, where each of `c` and `d` is given as a tuple `(i, j)` with `i` and `j` zero-based (so `i = 0` means that the cell lies in the topmost row).
EXAMPLES::
sage: p = Partition([3, 2]) sage: p.attacking_pairs() [((0, 0), (0, 1)), ((0, 0), (0, 2)), ((0, 1), (0, 2)), ((1, 0), (1, 1)), ((1, 1), (0, 0))] sage: Partition([]).attacking_pairs() [] """ #c is in position (i,j) #Find the d that satisfy condition 1
#Find the d that satisfy condition 2
def dominated_partitions(self, rows=None): """ Return a list of the partitions dominated by `n`. If ``rows`` is specified, then it only returns the ones whose number of rows is at most ``rows``.
EXAMPLES::
sage: Partition([3,2,1]).dominated_partitions() [[3, 2, 1], [3, 1, 1, 1], [2, 2, 2], [2, 2, 1, 1], [2, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]] sage: Partition([3,2,1]).dominated_partitions(rows=3) [[3, 2, 1], [2, 2, 2]] """ #Naive implementation because iteration is so fast else:
def contains(self, x): """ Return ``True`` if ``x`` is a partition whose Ferrers diagram is contained in the Ferrers diagram of ``self``.
EXAMPLES::
sage: p = Partition([3,2,1]) sage: p.contains([2,1]) True sage: all(p.contains(mu) for mu in Partitions(3)) True sage: all(p.contains(mu) for mu in Partitions(4)) False """
def hook_product(self, a): """ Return the Jack hook-product.
EXAMPLES::
sage: Partition([3,2,1]).hook_product(x) (2*x + 3)*(x + 2)^2 sage: Partition([2,2]).hook_product(x) 2*(x + 2)*(x + 1) """
def hook_polynomial(self, q, t): """ Return the two-variable hook polynomial.
EXAMPLES::
sage: R.<q,t> = PolynomialRing(QQ) sage: a = Partition([2,2]).hook_polynomial(q,t) sage: a == (1 - t)*(1 - q*t)*(1 - t^2)*(1 - q*t^2) True sage: a = Partition([3,2,1]).hook_polynomial(q,t) sage: a == (1 - t)^3*(1 - q*t^2)^2*(1 - q^2*t^3) True """
def hook_length(self, i, j): r""" Return the length of the hook of cell `(i,j)` in ``self``.
The (length of the) hook of cell `(i,j)` of a partition `\lambda` is
.. MATH::
\lambda_i + \lambda^{\prime}_j - i - j + 1
where `\lambda^{\prime}` is the conjugate partition. In English convention, the hook length is the number of cells horizontally to the right and vertically below the cell `(i,j)` (including that cell).
EXAMPLES::
sage: p = Partition([2,2,1]) sage: p.hook_length(0, 0) 4 sage: p.hook_length(0, 1) 2 sage: p.hook_length(2, 0) 1 sage: Partition([3,3]).hook_length(0, 0) 4 sage: cell = [0,0]; Partition([3,3]).hook_length(*cell) 4 """
def hooks(self): """ Return a sorted list of the hook lengths in ``self``.
EXAMPLES::
sage: Partition([3,2,1]).hooks() [5, 3, 3, 1, 1, 1] """
def hook_lengths(self): r""" Return a tableau of shape ``self`` with the cells filled in with the hook lengths.
In each cell, put the sum of one plus the number of cells horizontally to the right and vertically below the cell (the hook length).
For example, consider the partition ``[3,2,1]`` of 6 with Ferrers diagram::
# # # # # #
When we fill in the cells with the hook lengths, we obtain::
5 3 1 3 1 1
EXAMPLES::
sage: Partition([2,2,1]).hook_lengths() [[4, 2], [3, 1], [1]] sage: Partition([3,3]).hook_lengths() [[4, 3, 2], [3, 2, 1]] sage: Partition([3,2,1]).hook_lengths() [[5, 3, 1], [3, 1], [1]] sage: Partition([2,2]).hook_lengths() [[3, 2], [2, 1]] sage: Partition([5]).hook_lengths() [[5, 4, 3, 2, 1]]
REFERENCES:
- http://mathworld.wolfram.com/HookLengthFormula.html """
def upper_hook(self, i, j, alpha): r""" Return the upper hook length of the cell `(i,j)` in ``self``. When ``alpha = 1``, this is just the normal hook length.
The upper hook length of a cell `(i,j)` in a partition `\kappa` is defined by
.. MATH::
h^*_\kappa(i,j) = \kappa^\prime_j - i + \alpha(\kappa_i - j + 1).
EXAMPLES::
sage: p = Partition([2,1]) sage: p.upper_hook(0,0,1) 3 sage: p.hook_length(0,0) 3 sage: [ p.upper_hook(i,j,x) for i,j in p.cells() ] [2*x + 1, x, x] """
def upper_hook_lengths(self, alpha): r""" Return a tableau of shape ``self`` with the cells filled in with the upper hook lengths. When ``alpha = 1``, these are just the normal hook lengths.
The upper hook length of a cell `(i,j)` in a partition `\kappa` is defined by
.. MATH::
h^*_\kappa(i,j) = \kappa^\prime_j - i + \alpha(\kappa_i - j + 1).
EXAMPLES::
sage: Partition([3,2,1]).upper_hook_lengths(x) [[3*x + 2, 2*x + 1, x], [2*x + 1, x], [x]] sage: Partition([3,2,1]).upper_hook_lengths(1) [[5, 3, 1], [3, 1], [1]] sage: Partition([3,2,1]).hook_lengths() [[5, 3, 1], [3, 1], [1]] """
def lower_hook(self, i, j, alpha): r""" Return the lower hook length of the cell `(i,j)` in ``self``. When ``alpha = 1``, this is just the normal hook length.
The lower hook length of a cell `(i,j)` in a partition `\kappa` is defined by
.. MATH::
h_*^\kappa(i,j) = \kappa^\prime_j - i + 1 + \alpha(\kappa_i - j).
EXAMPLES::
sage: p = Partition([2,1]) sage: p.lower_hook(0,0,1) 3 sage: p.hook_length(0,0) 3 sage: [ p.lower_hook(i,j,x) for i,j in p.cells() ] [x + 2, 1, 1] """
def lower_hook_lengths(self, alpha): r""" Return a tableau of shape ``self`` with the cells filled in with the lower hook lengths. When ``alpha = 1``, these are just the normal hook lengths.
The lower hook length of a cell `(i,j)` in a partition `\kappa` is defined by
.. MATH::
h_*^\kappa(i,j) = \kappa^\prime_j - i + 1 + \alpha(\kappa_i - j).
EXAMPLES::
sage: Partition([3,2,1]).lower_hook_lengths(x) [[2*x + 3, x + 2, 1], [x + 2, 1], [1]] sage: Partition([3,2,1]).lower_hook_lengths(1) [[5, 3, 1], [3, 1], [1]] sage: Partition([3,2,1]).hook_lengths() [[5, 3, 1], [3, 1], [1]] """
def weighted_size(self): r""" Return the weighted size of ``self``.
The weighted size of a partition `\lambda` is
.. MATH::
\sum_i i \cdot \lambda_i,
where `\lambda = (\lambda_0, \lambda_1, \lambda_2, \cdots )`.
This also the sum of the leg length of every cell in `\lambda`, or
.. MATH::
\sum_i \binom{\lambda^{\prime}_i}{2}
where `\lambda^{\prime}` is the conjugate partition of `\lambda`.
EXAMPLES::
sage: Partition([2,2]).weighted_size() 2 sage: Partition([3,3,3]).weighted_size() 9 sage: Partition([5,2]).weighted_size() 2 sage: Partition([]).weighted_size() 0 """
def is_empty(self): """ Return ``True`` if ``self`` is the empty partition.
EXAMPLES::
sage: Partition([]).is_empty() True sage: Partition([2,1,1]).is_empty() False """
def length(self): """ Return the number of parts in ``self``.
EXAMPLES::
sage: Partition([3,2]).length() 2 sage: Partition([2,2,1]).length() 3 sage: Partition([]).length() 0 """
def to_exp(self, k=0): """ Return a list of the multiplicities of the parts of a partition. Use the optional parameter ``k`` to get a return list of length at least ``k``.
EXAMPLES::
sage: Partition([3,2,2,1]).to_exp() [1, 2, 1] sage: Partition([3,2,2,1]).to_exp(5) [1, 2, 1, 0, 0]
TESTS::
sage: [parent(x) for x in Partition([3,2,2,1]).to_exp(5)] [Integer Ring, Integer Ring, Integer Ring, Integer Ring, Integer Ring] """
def evaluation(self): r""" Return the evaluation of ``self``.
The **commutative evaluation**, often shortened to **evaluation**, of a word (we think of a partition as a word in `\{1, 2, 3, \ldots\}`) is its image in the free commutative monoid. In other words, this counts how many occurrences there are of each letter.
This is also is known as **Parikh vector** and **abelianization** and has the same output as :meth:`to_exp()`.
EXAMPLES::
sage: Partition([4,3,1,1]).evaluation() [2, 0, 1, 1] """
def to_exp_dict(self): """ Return a dictionary containing the multiplicities of the parts of ``self``.
EXAMPLES::
sage: p = Partition([4,2,2,1]) sage: d = p.to_exp_dict() sage: d[4] 1 sage: d[2] 2 sage: d[1] 1 sage: 5 in d False """
def centralizer_size(self, t=0, q=0): r""" Return the size of the centralizer of any permutation of cycle type ``self``.
If `m_i` is the multiplicity of `i` as a part of `p`, this is given by
.. MATH::
\prod_i m_i! i^{m_i}.
Including the optional parameters `t` and `q` gives the `q,t` analog, which is the former product times
.. MATH::
\prod_{i=1}^{\mathrm{length}(p)} \frac{1 - q^{p_i}}{1 - t^{p_i}}.
See [Ker]_.
EXAMPLES::
sage: Partition([2,2,1]).centralizer_size() 8 sage: Partition([2,2,2]).centralizer_size() 48 sage: Partition([2,2,1]).centralizer_size(q=2, t=3) 9/16 sage: Partition([]).centralizer_size() 1 sage: Partition([]).centralizer_size(q=2, t=4) 1 """ for i, mi in six.iteritems(self.to_exp_dict())) for j in self)
def aut(self): r""" Return a factor for the number of permutations with cycle type ``self``.
This method returns `1^{j_1}j_1! \cdots n^{j_n}j_n!` where `j_k` is the number of parts in ``self`` equal to `k`.
The number of permutations having ``self`` as a cycle type is given by
.. MATH::
\frac{n!}{1^{j_1}j_1! \cdots n^{j_n}j_n!}
(where `n` is the size of ``self``).
EXAMPLES::
sage: Partition([2,1]).aut() 2 """
def content(self, r, c, multicharge=(0,)): r""" Return the content of the cell at row `r` and column `c`.
The content of a cell is `c - r`.
For consistency with partition tuples there is also an optional ``multicharge`` argument which is an offset to the usual content. By setting the ``multicharge`` equal to the 0-element of the ring `\ZZ/e\ZZ`, the corresponding `e`-residue will be returned. This is the content modulo `e`.
The content (and residue) do not strictly depend on the partition, however, this method is included because it is often useful in the context of partitions.
EXAMPLES::
sage: Partition([2,1]).content(1,0) -1 sage: p = Partition([3,2]) sage: sum([p.content(*c) for c in p.cells()]) 2
and now we return the 3-residue of a cell::
sage: Partition([2,1]).content(1,0, multicharge=[IntegerModRing(3)(0)]) 2 """
def residue(self, r, c, l): """ Return the ``l``-residue of the cell at row ``r`` and column ``c``.
The `\ell`-residue of a cell is `c - r` modulo `\ell`.
This does not strictly depend upon the partition, however, this method is included because it is often useful in the context of partitions.
EXAMPLES::
sage: Partition([2,1]).residue(1, 0, 3) 2 """
def defect(self, e, multicharge=(0,)): r""" Return the ``e``-defect or the ``e``-weight of ``self``.
The `e`-defect is the number of (connected) `e`-rim hooks that can be removed from the partition.
The defect of a partition is given by
.. MATH::
\text{defect}(\beta) = (\Lambda, \beta) - \tfrac12(\beta, \beta)
where `\Lambda = \sum_r \Lambda_{\kappa_r}` for the multicharge `(\kappa_1, \ldots, \kappa_{\ell})` and `\beta = \sum_{(r,c)} \alpha_{(c-r) \pmod e}`, with the sum being over the cells in the partition.
EXAMPLES::
sage: Partition([4,3,2]).defect(3) 3 sage: Partition([0]).defect(3) 0 sage: Partition([3]).defect(3) 1 sage: Partition([6]).defect(3) 2 sage: Partition([9]).defect(3) 3 sage: Partition([12]).defect(3) 4
TESTS::
sage: all(mu.core(e).size() + e * mu.defect(e) == 9 ....: for mu in Partitions(9) for e in [2,3,4]) True """
for i in range(e))
def conjugacy_class_size(self): """ Return the size of the conjugacy class of the symmetric group indexed by ``self``.
EXAMPLES::
sage: Partition([2,2,2]).conjugacy_class_size() 15 sage: Partition([2,2,1]).conjugacy_class_size() 15 sage: Partition([2,1,1]).conjugacy_class_size() 6
REFERENCES:
.. [Ker] Kerber, A. 'Algebraic Combinatorics via Finite Group Actions' 1.3 p24 """
def corners(self): r""" Return a list of the corners of the partition ``self``.
A corner of a partition `\lambda` is a cell of the Young diagram of `\lambda` which can be removed from the Young diagram while still leaving a straight shape behind.
The entries of the list returned are pairs of the form `(i,j)`, where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`.
EXAMPLES::
sage: Partition([3,2,1]).corners() [(0, 2), (1, 1), (2, 0)] sage: Partition([3,3,1]).corners() [(1, 2), (2, 0)] sage: Partition([]).corners() [] """
else:
inside_corners = corners removable_cells = corners # for compatibility with partition tuples
def corners_residue(self, i, l): r""" Return a list of the corners of the partition ``self`` having ``l``-residue ``i``.
A corner of a partition `\lambda` is a cell of the Young diagram of `\lambda` which can be removed from the Young diagram while still leaving a straight shape behind. See :meth:`residue` for the definition of the ``l``-residue.
The entries of the list returned are pairs of the form `(i,j)`, where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`.
EXAMPLES::
sage: Partition([3,2,1]).corners_residue(0, 3) [(1, 1)] sage: Partition([3,2,1]).corners_residue(1, 3) [(2, 0)] sage: Partition([3,2,1]).corners_residue(2, 3) [(0, 2)] """
inside_corners_residue = corners_residue removable_cells_residue = corners_residue
def outside_corners(self): r""" Return a list of the outside corners of the partition ``self``.
An outside corner (also called a cocorner) of a partition `\lambda` is a cell on `\ZZ^2` which does not belong to the Young diagram of `\lambda` but can be added to this Young diagram to still form a straight-shape Young diagram.
The entries of the list returned are pairs of the form `(i,j)`, where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`.
EXAMPLES::
sage: Partition([2,2,1]).outside_corners() [(0, 2), (2, 1), (3, 0)] sage: Partition([2,2]).outside_corners() [(0, 2), (2, 0)] sage: Partition([6,3,3,1,1,1]).outside_corners() [(0, 6), (1, 3), (3, 1), (6, 0)] sage: Partition([]).outside_corners() [(0, 0)] """
addable_cells = outside_corners # for compatibility with partition tuples
def outside_corners_residue(self, i, l): r""" Return a list of the outside corners of the partition ``self`` having ``l``-residue ``i``.
An outside corner (also called a cocorner) of a partition `\lambda` is a cell on `\ZZ^2` which does not belong to the Young diagram of `\lambda` but can be added to this Young diagram to still form a straight-shape Young diagram. See :meth:`residue` for the definition of the ``l``-residue.
The entries of the list returned are pairs of the form `(i,j)`, where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`.
EXAMPLES::
sage: Partition([3,2,1]).outside_corners_residue(0, 3) [(0, 3), (3, 0)] sage: Partition([3,2,1]).outside_corners_residue(1, 3) [(1, 2)] sage: Partition([3,2,1]).outside_corners_residue(2, 3) [(2, 1)] """
addable_cells_residue = outside_corners_residue
def rim(self): r""" Return the rim of ``self``.
The rim of a partition `\lambda` is defined as the cells which belong to `\lambda` and which are adjacent to cells not in `\lambda`.
EXAMPLES:
The rim of the partition `[5,5,2,1]` consists of the cells marked with ``#`` below::
****# *#### ## #
sage: Partition([5,5,2,1]).rim() [(3, 0), (2, 0), (2, 1), (1, 1), (1, 2), (1, 3), (1, 4), (0, 4)]
sage: Partition([2,2,1]).rim() [(2, 0), (1, 0), (1, 1), (0, 1)] sage: Partition([2,2]).rim() [(1, 0), (1, 1), (0, 1)] sage: Partition([6,3,3,1,1]).rim() [(4, 0), (3, 0), (2, 0), (2, 1), (2, 2), (1, 2), (0, 2), (0, 3), (0, 4), (0, 5)] sage: Partition([]).rim() [] """
def outer_rim(self): r""" Return the outer rim of ``self``.
The outer rim of a partition `\lambda` is defined as the cells which do not belong to `\lambda` and which are adjacent to cells in `\lambda`.
EXAMPLES:
The outer rim of the partition `[4,1]` consists of the cells marked with ``#`` below::
****# *#### ##
::
sage: Partition([4,1]).outer_rim() [(2, 0), (2, 1), (1, 1), (1, 2), (1, 3), (1, 4), (0, 4)]
sage: Partition([2,2,1]).outer_rim() [(3, 0), (3, 1), (2, 1), (2, 2), (1, 2), (0, 2)] sage: Partition([2,2]).outer_rim() [(2, 0), (2, 1), (2, 2), (1, 2), (0, 2)] sage: Partition([6,3,3,1,1]).outer_rim() [(5, 0), (5, 1), (4, 1), (3, 1), (3, 2), (3, 3), (2, 3), (1, 3), (1, 4), (1, 5), (1, 6), (0, 6)] sage: Partition([]).outer_rim() [(0, 0)] """
def zero_one_sequence(self): r""" Compute the finite `0-1` sequence of the partition.
The full `0-1` sequence is the sequence (infinite in both directions) indicating the steps taken when following the outer rim of the diagram of the partition. We use the convention that in English convention, a 1 corresponds to an East step, and a 0 corresponds to a North step.
Note that every full `0-1` sequence starts with infinitely many 0's and ends with infinitely many 1's.
One place where these arise is in the affine symmetric group where one takes an affine permutation `w` and every `i` such that `w(i) \leq 0` corresponds to a 1 and `w(i) > 0` corresponds to a 0. See pages 24-25 of [LLMMSZ13]_ for connections to affine Grassmannian elements (note there they use the French convention for their partitions).
These are also known as **path sequences**, **Maya diagrams**, **plus-minus diagrams**, **Comet code** [Sta-EC2]_, among others.
OUTPUT:
The finite `0-1` sequence is obtained from the full `0-1` sequence by omitting all heading 0's and trailing 1's. The output sequence is finite, starts with a 1 and ends with a 0 (unless it is empty, for the empty partition). Its length is the sum of the first part of the partition with the length of the partition.
REFERENCES:
.. [LLMMSZ13] Thomas Lam, Luc Laponte, Jennifer Morse, Anne Schilling, Mark Shimozono, and Mike Zabrocki. `k`-Schur Functions and Affine Schubert Calculus. 2013. :arxiv:`1301.3569`.
EXAMPLES::
sage: Partition([5,4]).zero_one_sequence() [1, 1, 1, 1, 0, 1, 0] sage: Partition([]).zero_one_sequence() [] sage: Partition([2]).zero_one_sequence() [1, 1, 0]
TESTS::
sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) == mu for n in range(10) for mu in Partitions(n)) True """
def core(self, length): r""" Return the ``length``-core of the partition -- in the literature the core is commonly referred to as the `k`-core, `p`-core, `r`-core, ... .
The `r`-core of a partition `\lambda` can be obtained by repeatedly removing rim hooks of size `r` from (the Young diagram of) `\lambda` until this is no longer possible. The remaining partition is the core.
EXAMPLES::
sage: Partition([6,3,2,2]).core(3) [2, 1, 1] sage: Partition([]).core(3) [] sage: Partition([8,7,7,4,1,1,1,1,1]).core(3) [2, 1, 1]
TESTS::
sage: Partition([3,3,3,2,1]).core(3) [] sage: Partition([10,8,7,7]).core(4) [] sage: Partition([21,15,15,9,6,6,6,3,3]).core(3) [] """ #Normalize the length
#Add the canonical vector to the partition
#Remove the canonical vector #Select the r-core
def quotient(self, length): r""" Return the quotient of the partition -- in the literature the quotient is commonly referred to as the `k`-quotient, `p`-quotient, `r`-quotient, ... .
The `r`-quotient of a partition `\lambda` is a list of `r` partitions (labelled from `0` to `r-1`), constructed in the following way. Label each cell in the Young diagram of `\lambda` with its content modulo `r`. Let `R_i` be the set of rows ending in a cell labelled `i`, and `C_i` be the set of columns ending in a cell labelled `i`. Then the `j`-th component of the quotient of `\lambda` is the partition defined by intersecting `R_j` with `C_{j+1}`. (See Theorem 2.7.37 in [JamesKerber]_.)
REFERENCES:
.. [JamesKerber] Gordon James, Adalbert Kerber, *The Representation Theory of the Symmetric Group*, Encyclopedia of Mathematics and its Applications, vol. 16, Addison-Wesley 1981.
EXAMPLES::
sage: Partition([7,7,5,3,3,3,1]).quotient(3) ([2], [1], [2, 2, 2])
TESTS::
sage: Partition([8,7,7,4,1,1,1,1,1]).quotient(3) ([2, 1], [2, 2], [2]) sage: Partition([10,8,7,7]).quotient(4) ([2], [3], [2], [1]) sage: Partition([6,3,3]).quotient(3) ([1], [1], [2]) sage: Partition([3,3,3,2,1]).quotient(3) ([1], [1, 1], [1]) sage: Partition([6,6,6,3,3,3]).quotient(3) ([2, 1], [2, 1], [2, 1]) sage: Partition([21,15,15,9,6,6,6,3,3]).quotient(3) ([5, 2, 1], [5, 2, 1], [7, 3, 2]) sage: Partition([21,15,15,9,6,6,3,3]).quotient(3) ([5, 2], [5, 2, 1], [7, 3, 1]) sage: Partition([14,12,11,10,10,10,10,9,6,4,3,3,2,1]).quotient(5) ([3, 3], [2, 2, 1], [], [3, 3, 3], [1])
sage: all(p == Partition(core=p.core(k), quotient=p.quotient(k)) ....: for i in range(10) for p in Partitions(i) ....: for k in range(1,6)) True """ #Normalize the length
#Add the canonical vector to the partition
#Reducing vector
def is_core(self, k): r""" Tests whether the partition is a `k`-core or not. Visuallly, this can be checked by trying to remove border strips of size `k` from ``self``. If this is not possible, then ``self`` is a `k`-core.
A partition is said to be a *`k`-core* if it has no hooks of length `k`. Equivalently, a partition is said to be a `k`-core if it is its own `k`-core (where the latter is defined as in :meth:`core`).
EXAMPLES::
sage: p = Partition([12,8,5,5,2,2,1]) sage: p.is_core(4) False sage: p.is_core(5) True sage: p.is_core(0) True """
def k_interior(self, k): r""" Return the partition consisting of the cells of ``self`` whose hook lengths are greater than ``k``.
EXAMPLES::
sage: p = Partition([3,2,1]) sage: p.hook_lengths() [[5, 3, 1], [3, 1], [1]] sage: p.k_interior(2) [2, 1] sage: p.k_interior(3) [1]
sage: p = Partition([]) sage: p.k_interior(3) [] """ for row in self.hook_lengths()])
def k_boundary(self, k): r""" Return the skew partition formed by removing the cells of the ``k``-interior, see :meth:`k_interior`.
EXAMPLES::
sage: p = Partition([3,2,1]) sage: p.k_boundary(2) [3, 2, 1] / [2, 1] sage: p.k_boundary(3) [3, 2, 1] / [1]
sage: p = Partition([12,8,5,5,2,2,1]) sage: p.k_boundary(4) [12, 8, 5, 5, 2, 2, 1] / [8, 5, 2, 2] """
def add_cell(self, i, j = None): r""" Return a partition corresponding to ``self`` with a cell added in row ``i``. (This does not change ``self``.)
EXAMPLES::
sage: Partition([3, 2, 1, 1]).add_cell(0) [4, 2, 1, 1] sage: cell = [4, 0]; Partition([3, 2, 1, 1]).add_cell(*cell) [3, 2, 1, 1, 1] """
else:
else:
raise ValueError("[%s, %s] is not an addable cell"%(i,j))
def remove_cell(self, i, j = None): """ Return the partition obtained by removing a cell at the end of row ``i`` of ``self``.
EXAMPLES::
sage: Partition([2,2]).remove_cell(1) [2, 1] sage: Partition([2,2,1]).remove_cell(2) [2, 2] sage: #Partition([2,2]).remove_cell(0)
::
sage: Partition([2,2]).remove_cell(1,1) [2, 1] sage: #Partition([2,2]).remove_cell(1,0) """
raise ValueError("i must be less than the length of the partition")
raise ValueError("[%d,%d] is not a corner of the partition" % (i,j))
else:
def k_irreducible(self, k): r""" Return the partition with all `r \times (k+1-r)` rectangles removed.
If ``self`` is a `k`-bounded partition, then this method will return the partition where all rectangles of dimension `r \times (k+1-r)` for `1 \leq r \leq k` have been deleted.
If ``self`` is not a `k`-bounded partition then the method will raise an error.
INPUT:
- ``k`` -- a non-negative integer
OUTPUT:
- a partition
EXAMPLES::
sage: Partition([3,2,2,1,1,1]).k_irreducible(4) [3, 2, 2, 1, 1, 1] sage: Partition([3,2,2,1,1,1]).k_irreducible(3) [] sage: Partition([3,3,3,2,2,2,2,2,1,1,1,1]).k_irreducible(3) [2, 1] """
def k_skew(self, k): r""" Return the `k`-skew partition.
The `k`-skew diagram of a `k`-bounded partition is the skew diagram denoted `\lambda/^k` satisfying the conditions:
1. row `i` of `\lambda/^k` has length `\lambda_i`,
2. no cell in `\lambda/^k` has hook-length exceeding `k`,
3. every square above the diagram of `\lambda/^k` has hook length exceeding `k`.
REFERENCES:
.. [LM2004] Lapointe, L. and Morse, J. 'Order Ideals in Weak Subposets of Young's Lattice and Associated Unimodality Conjectures'. Ann. Combin. (2004)
EXAMPLES::
sage: p = Partition([4,3,2,2,1,1]) sage: p.k_skew(4) [9, 5, 3, 2, 1, 1] / [5, 2, 1] """
#Find the k-skew diagram of the partition formed #by removing the first row
#Find the leftmost column with less than # or equal to kdiff cells
else:
else:
def to_core(self, k): r""" Maps the `k`-bounded partition ``self`` to its corresponding `k+1`-core.
See also :meth:`k_skew`.
EXAMPLES::
sage: p = Partition([4,3,2,2,1,1]) sage: c = p.to_core(4); c [9, 5, 3, 2, 1, 1] sage: type(c) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: c.to_bounded_partition() == p True """
def from_kbounded_to_reduced_word(self, k): r""" Maps a `k`-bounded partition to a reduced word for an element in the affine permutation group.
This uses the fact that there is a bijection between `k`-bounded partitions and `(k+1)`-cores and an action of the affine nilCoxeter algebra of type `A_k^{(1)}` on `(k+1)`-cores as described in [LM2006]_.
REFERENCES:
.. [LM2006] MR2167475 (2006j:05214) L. Lapointe, J. Morse. Tableaux on `k+1`-cores, reduced words for affine permutations, and `k`-Schur expansions. J. Combin. Theory Ser. A 112 (2005), no. 1, 44--81.
EXAMPLES::
sage: p=Partition([2,1,1]) sage: p.from_kbounded_to_reduced_word(2) [2, 1, 2, 0] sage: p=Partition([3,1]) sage: p.from_kbounded_to_reduced_word(3) [3, 2, 1, 0] sage: p.from_kbounded_to_reduced_word(2) Traceback (most recent call last): ... ValueError: the partition must be 2-bounded sage: p=Partition([]) sage: p.from_kbounded_to_reduced_word(2) [] """
def from_kbounded_to_grassmannian(self, k): r""" Maps a `k`-bounded partition to a Grassmannian element in the affine Weyl group of type `A_k^{(1)}`.
For details, see the documentation of the method :meth:`from_kbounded_to_reduced_word` .
EXAMPLES::
sage: p=Partition([2,1,1]) sage: p.from_kbounded_to_grassmannian(2) [-1 1 1] [-2 2 1] [-2 1 2] sage: p=Partition([]) sage: p.from_kbounded_to_grassmannian(2) [1 0 0] [0 1 0] [0 0 1] """
def to_list(self): r""" Return ``self`` as a list.
EXAMPLES::
sage: p = Partition([2,1]).to_list(); p [2, 1] sage: type(p) <... 'list'>
TESTS::
sage: p = Partition([2,1]) sage: pl = p.to_list() sage: pl[0] = 0; p [2, 1] """
def add_vertical_border_strip(self, k): """ Return a list of all the partitions that can be obtained by adding a vertical border strip of length ``k`` to ``self``.
EXAMPLES::
sage: Partition([]).add_vertical_border_strip(0) [[]] sage: Partition([]).add_vertical_border_strip(2) [[1, 1]] sage: Partition([2,2]).add_vertical_border_strip(2) [[3, 3], [3, 2, 1], [2, 2, 1, 1]] sage: Partition([3,2,2]).add_vertical_border_strip(2) [[4, 3, 2], [4, 2, 2, 1], [3, 3, 3], [3, 3, 2, 1], [3, 2, 2, 1, 1]] """
def add_horizontal_border_strip(self, k): """ Return a list of all the partitions that can be obtained by adding a horizontal border strip of length ``k`` to ``self``.
EXAMPLES::
sage: Partition([]).add_horizontal_border_strip(0) [[]] sage: Partition([]).add_horizontal_border_strip(2) [[2]] sage: Partition([2,2]).add_horizontal_border_strip(2) [[2, 2, 2], [3, 2, 1], [4, 2]] sage: Partition([3,2,2]).add_horizontal_border_strip(2) [[3, 2, 2, 2], [3, 3, 2, 1], [4, 2, 2, 1], [4, 3, 2], [5, 2, 2]]
.. TODO::
Reimplement like ``remove_horizontal_border_strip`` using :class:`IntegerListsLex` """
#added the last shelf on the right side of #the first line
#list all of the positions for cells #filling each self from the left to the right
def remove_horizontal_border_strip(self, k): """ Return the partitions obtained from ``self`` by removing an horizontal border strip of length ``k``.
EXAMPLES::
sage: Partition([5,3,1]).remove_horizontal_border_strip(0).list() [[5, 3, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(1).list() [[5, 3], [5, 2, 1], [4, 3, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(2).list() [[5, 2], [5, 1, 1], [4, 3], [4, 2, 1], [3, 3, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(3).list() [[5, 1], [4, 2], [4, 1, 1], [3, 3], [3, 2, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(4).list() [[4, 1], [3, 2], [3, 1, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(5).list() [[3, 1]] sage: Partition([5,3,1]).remove_horizontal_border_strip(6).list() []
The result is returned as an instance of :class:`Partitions_with_constraints`::
sage: Partition([5,3,1]).remove_horizontal_border_strip(5) The subpartitions of [5, 3, 1] obtained by removing an horizontal border strip of length 5
TESTS::
sage: Partition([3,2,2]).remove_horizontal_border_strip(2).list() [[3, 2], [2, 2, 1]] sage: Partition([3,2,2]).remove_horizontal_border_strip(2).first().parent() The subpartitions of [3, 2, 2] obtained by removing an horizontal border strip of length 2 sage: Partition([]).remove_horizontal_border_strip(0).list() [[]] sage: Partition([]).remove_horizontal_border_strip(6).list() [] """ min_length = len(self)-1, max_length = len(self), floor = self[1:]+[0], ceiling = self[:], max_slope = 0, name = "The subpartitions of {} obtained by removing an horizontal border strip of length {}".format(self,k))
def k_conjugate(self, k): r""" Return the ``k``-conjugate of ``self``.
The `k`-conjugate is the partition that is given by the columns of the `k`-skew diagram of the partition.
We can also define the `k`-conjugate in the following way. Let `P` denote the bijection from `(k+1)`-cores to `k`-bounded partitions. The `k`-conjugate of a `(k+1)`-core `\lambda` is
.. MATH::
\lambda^{(k)} = P^{-1}\left( (P(\lambda))^{\prime} \right).
EXAMPLES::
sage: p = Partition([4,3,2,2,1,1]) sage: p.k_conjugate(4) [3, 2, 2, 1, 1, 1, 1, 1, 1] """
def arms_legs_coeff(self, i, j): r""" This is a statistic on a cell `c = (i,j)` in the diagram of partition `p` given by
.. MATH::
\frac{ 1 - q^a \cdot t^{\ell + 1} }{ 1 - q^{a + 1} \cdot t^{\ell} }
where `a` is the arm length of `c` and `\ell` is the leg length of `c`.
The coordinates ``i`` and ``j`` of the cell are understood to be `0`-based, so that ``(0, 0)`` is the northwesternmost cell (in English notation).
EXAMPLES::
sage: Partition([3,2,1]).arms_legs_coeff(1,1) (-t + 1)/(-q + 1) sage: Partition([3,2,1]).arms_legs_coeff(0,0) (-q^2*t^3 + 1)/(-q^3*t^2 + 1) sage: Partition([3,2,1]).arms_legs_coeff(*[0,0]) (-q^2*t^3 + 1)/(-q^3*t^2 + 1) """ return ZZ.one()
def atom(self): """ Return a list of the standard tableaux of size ``self.size()`` whose atom is equal to ``self``.
EXAMPLES::
sage: Partition([2,1]).atom() [[[1, 2], [3]]] sage: Partition([3,2,1]).atom() [[[1, 2, 3, 6], [4, 5]], [[1, 2, 3], [4, 5], [6]]] """
def k_atom(self, k): """ Return a list of the standard tableaux of size ``self.size()`` whose ``k``-atom is equal to ``self``.
EXAMPLES::
sage: p = Partition([3,2,1]) sage: p.k_atom(1) [] sage: p.k_atom(3) [[[1, 1, 1], [2, 2], [3]], [[1, 1, 1, 2], [2], [3]], [[1, 1, 1, 3], [2, 2]], [[1, 1, 1, 2, 3], [2]]] sage: Partition([3,2,1]).k_atom(4) [[[1, 1, 1], [2, 2], [3]], [[1, 1, 1, 3], [2, 2]]]
TESTS::
sage: Partition([1]).k_atom(1) [[[1]]] sage: Partition([1]).k_atom(2) [[[1]]] sage: Partition([]).k_atom(1) [[]] """
def k_split(self, k): """ Return the ``k``-split of ``self``.
EXAMPLES::
sage: Partition([4,3,2,1]).k_split(3) [] sage: Partition([4,3,2,1]).k_split(4) [[4], [3, 2], [1]] sage: Partition([4,3,2,1]).k_split(5) [[4, 3], [2, 1]] sage: Partition([4,3,2,1]).k_split(6) [[4, 3, 2], [1]] sage: Partition([4,3,2,1]).k_split(7) [[4, 3, 2, 1]] sage: Partition([4,3,2,1]).k_split(8) [[4, 3, 2, 1]] """ else:
def jacobi_trudi(self): """ Return the Jacobi-Trudi matrix of ``self`` thought of as a skew partition. See :meth:`SkewPartition.jacobi_trudi() <sage.combinat.skew_partition.SkewPartition.jacobi_trudi>`.
EXAMPLES::
sage: part = Partition([3,2,1]) sage: jt = part.jacobi_trudi(); jt [h[3] h[1] 0] [h[4] h[2] h[]] [h[5] h[3] h[1]] sage: s = SymmetricFunctions(QQ).schur() sage: h = SymmetricFunctions(QQ).homogeneous() sage: h( s(part) ) h[3, 2, 1] - h[3, 3] - h[4, 1, 1] + h[5, 1] sage: jt.det() h[3, 2, 1] - h[3, 3] - h[4, 1, 1] + h[5, 1] """
def character_polynomial(self): r""" Return the character polynomial associated to the partition ``self``.
The character polynomial `q_\mu` associated to a partition `\mu` is defined by
.. MATH::
q_\mu(x_1, x_2, \ldots, x_k) = \downarrow \sum_{\alpha \vdash k} \frac{ \chi^\mu_\alpha }{1^{a_1}2^{a_2}\cdots k^{a_k}a_1!a_2!\cdots a_k!} \prod_{i=1}^{k} (ix_i-1)^{a_i}
where `k` is the size of `\mu`, and `a_i` is the multiplicity of `i` in `\alpha`.
It is computed in the following manner:
1. Expand the Schur function `s_\mu` in the power-sum basis,
2. Replace each `p_i` with `ix_i-1`,
3. Apply the umbral operator `\downarrow` to the resulting polynomial.
EXAMPLES::
sage: Partition([1]).character_polynomial() x - 1 sage: Partition([1,1]).character_polynomial() 1/2*x0^2 - 3/2*x0 - x1 + 1 sage: Partition([2,1]).character_polynomial() 1/3*x0^3 - 2*x0^2 + 8/3*x0 - x2 """
#Create the polynomial ring we will use
#Expand s_mu in the power sum basis
#Replace each p_i by i*x_i-1
#Write things in the monomial basis
#Apply the umbral operator and return the result
def dimension(self, smaller = [], k = 1): r""" Return the number of paths from the ``smaller`` partition to the partition ``self``, where each step consists of adding a `k`-ribbon while keeping a partition.
Note that a 1-ribbon is just a single cell, so this counts paths in the Young graph when `k = 1`.
Note also that the default case (`k = 1` and ``smaller = []``) gives the dimension of the irreducible representation of the symmetric group corresponding to ``self``.
INPUT:
- ``smaller`` -- a partition (default: an empty list ``[]``)
- `k` -- a positive integer (default: 1)
OUTPUT:
The number of such paths
EXAMPLES:
Looks at the number of ways of getting from ``[5,4]`` to the empty partition, removing one cell at a time::
sage: mu = Partition([5,4]) sage: mu.dimension() 42
Same, but removing one 3-ribbon at a time. Note that the 3-core of ``mu`` is empty::
sage: mu.dimension(k=3) 3
The 2-core of ``mu`` is not the empty partition::
sage: mu.dimension(k=2) 0
Indeed, the 2-core of ``mu`` is ``[1]``::
sage: mu.dimension(Partition([1]),k=2) 2
TESTS:
Checks that the sum of squares of dimensions of characters of the symmetric group is the order of the group::
sage: all(sum(mu.dimension()^2 for mu in Partitions(i))==factorial(i) for i in range(10)) True
A check coming from the theory of `k`-differentiable posets::
sage: k=2; core = Partition([2,1]) sage: all(sum(mu.dimension(core,k=2)^2 ....: for mu in Partitions(3+i*2) if mu.core(2) == core) ....: == 2^i*factorial(i) for i in range(10)) True
Checks that the dimension satisfies the obvious recursion relation::
sage: test = lambda larger, smaller: larger.dimension(smaller) == sum(mu.dimension(smaller) for mu in larger.down()) sage: all(test(larger,smaller) for l in range(1,10) for s in range(0,10) ....: for larger in Partitions(l) for smaller in Partitions(s) if smaller != larger) True
ALGORITHM:
Depending on the parameters given, different simplifications occur. When `k=1` and ``smaller`` is empty, this function uses the hook formula. When `k=1` and ``smaller`` is not empty, it uses a formula from [ORV]_.
When `k \neq 1`, we first check that both ``self`` and ``smaller`` have the same `k`-core, then use the `k`-quotients and the same algorithm on each of the `k`-quotients.
REFERENCES:
.. [ORV] Grigori Olshanski, Amitai Regev, Anatoly Vershik, *Frobenius-Schur functions*, :arxiv:`math/0110077v1`. Possibly newer version at http://www.wisdom.weizmann.ac.il/~regev/papers/FrobeniusSchurFunctions.ps
AUTHORS:
- Paul-Olivier Dehaye (2011-06-07) """ else: else: # relative dimension # Uses a formula of Olshanski, Regev, Vershik (see reference) else: else:
# count the number of ways of performing the k paths in parallel, # if we know the total length alloted for each of the paths (sizes), and the number # of paths for each component. A multinomial picks the ordering of the components where # each step is taken.
def plancherel_measure(self): r""" Return the probability of ``self`` under the Plancherel probability measure on partitions of the same size.
This probability distribution comes from the uniform distribution on permutations via the Robinson-Schensted correspondence.
See :wikipedia:`Plancherel\_measure` and :meth:`Partitions_n.random_element_plancherel`.
EXAMPLES::
sage: Partition([]).plancherel_measure() 1 sage: Partition([1]).plancherel_measure() 1 sage: Partition([2]).plancherel_measure() 1/2 sage: [mu.plancherel_measure() for mu in Partitions(3)] [1/6, 2/3, 1/6] sage: Partition([5,4]).plancherel_measure() 7/1440
TESTS::
sage: all(sum(mu.plancherel_measure() for mu in Partitions(n))==1 for n in range(10)) True """
def outline(self, variable=None): r""" Return the outline of the partition ``self``.
This is a piecewise linear function, normalized so that the area under the partition ``[1]`` is 2.
INPUT:
- variable -- a variable (default: ``'x'`` in the symbolic ring)
EXAMPLES::
sage: [Partition([5,4]).outline()(x=i) for i in range(-10,11)] [10, 9, 8, 7, 6, 5, 6, 5, 6, 5, 4, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sage: Partition([]).outline() abs(x)
sage: Partition([1]).outline() abs(x + 1) + abs(x - 1) - abs(x)
sage: y=sage.symbolic.ring.var("y") sage: Partition([6,5,1]).outline(variable=y) abs(y + 6) - abs(y + 5) + abs(y + 4) - abs(y + 3) + abs(y - 1) - abs(y - 2) + abs(y - 3)
TESTS::
sage: integrate(Partition([1]).outline()-abs(x),(x,-10,10)) 2 """ -sum(abs(variable+c) for c in inside_contents)
def dual_equivalence_graph(self, directed=False, coloring=None): r""" Return the dual equivalence graph of ``self``.
Two permutations `p` and `q` in the symmetric group `S_n` differ by an `i`-*elementary dual equivalence (or dual Knuth) relation* (where `i` is an integer with `1 < i < n`) when the following two conditions are satisfied:
- In the one-line notation of the permutation `p`, the letter `i` does not appear inbetween `i-1` and `i+1`.
- The permutation `q` is obtained from `p` by switching two of the three letters `i-1, i, i+1` (in its one-line notation) -- namely, the leftmost and the rightmost one in order of their appearance in `p`.
Notice that this is equivalent to the statement that the permutations `p^{-1}` and `q^{-1}` differ by an elementary Knuth equivalence at positions `i-1, i, i+1`.
Two standard Young tableaux of shape `\lambda` differ by an `i`-elementary dual equivalence relation (of color `i`), if their reading words differ by an `i`-elementary dual equivalence relation.
The *dual equivalence graph* of the partition `\lambda` is the edge-colored graph whose vertices are the standard Young tableaux of shape `\lambda`, and whose edges colored by `i` are given by the `i`-elementary dual equivalences.
INPUT:
- ``directed`` -- (default: ``False``) whether to have the dual equivalence graph be directed (where we have a directed edge `S \to T` if `i` appears to the left of `i+1` in the reading word of `T`; otherwise we have the directed edge `T \to S`)
- ``coloring`` -- (optional) a function which sends each integer `i > 1` to a color (as a string, e.g., ``'red'`` or ``'black'``) to be used when visually representing the resulting graph using dot2tex; the default choice is ``2 -> 'red', 3 -> 'blue', 4 -> 'green', 5 -> 'purple', 6 -> 'brown', 7 -> 'orange', 8 -> 'yellow', anything greater than 8 -> 'black'``.
REFERENCES:
.. [AssafDEG] Sami Assaf. *Dual equivalence graphs and a combinatorial proof of LLT and Macdonald positivity*. (2008). :arxiv:`1005.3759v5`.
EXAMPLES::
sage: P = Partition([3,1,1]) sage: G = P.dual_equivalence_graph() sage: sorted(G.edges()) [([[1, 2, 3], [4], [5]], [[1, 2, 4], [3], [5]], 3), ([[1, 2, 4], [3], [5]], [[1, 2, 5], [3], [4]], 4), ([[1, 2, 4], [3], [5]], [[1, 3, 4], [2], [5]], 2), ([[1, 2, 5], [3], [4]], [[1, 3, 5], [2], [4]], 2), ([[1, 3, 4], [2], [5]], [[1, 3, 5], [2], [4]], 4), ([[1, 3, 5], [2], [4]], [[1, 4, 5], [2], [3]], 3)] sage: G = P.dual_equivalence_graph(directed=True) sage: sorted(G.edges()) [([[1, 2, 4], [3], [5]], [[1, 2, 3], [4], [5]], 3), ([[1, 2, 5], [3], [4]], [[1, 2, 4], [3], [5]], 4), ([[1, 3, 4], [2], [5]], [[1, 2, 4], [3], [5]], 2), ([[1, 3, 5], [2], [4]], [[1, 2, 5], [3], [4]], 2), ([[1, 3, 5], [2], [4]], [[1, 3, 4], [2], [5]], 4), ([[1, 4, 5], [2], [3]], [[1, 3, 5], [2], [4]], 3)]
TESTS::
sage: G = Partition([1]).dual_equivalence_graph() sage: G.vertices() [[[1]]] sage: G = Partition([]).dual_equivalence_graph() sage: G.vertices() [[]]
sage: P = Partition([3,1,1]) sage: G = P.dual_equivalence_graph(coloring=lambda x: 'red') sage: G2 = P.dual_equivalence_graph(coloring={2: 'black', 3: 'blue', 4: 'cyan', 5: 'grey'}) sage: G is G2 False sage: G == G2 True """ # We do some custom caching to not recreate the graph, but to make # copies with the desired coloring (i.e., act like a factory). else: if coloring is None: d = {2: 'red', 3: 'blue', 4: 'green', 5: 'purple', 6: 'brown', 7: 'orange', 8: 'yellow'} def coloring(i): if i in d: return d[i] return 'black' elif isinstance(coloring, dict): d = coloring coloring = lambda x: d[x] G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=coloring)
else: e = [to_tab[Perm(x)], t, i] edges.append(e)
immutable=True, multiedges=True) else: immutable=True, multiedges=True)
############## # Partitions # ##############
class Partitions(UniqueRepresentation, Parent): r""" ``Partitions(n, **kwargs)`` returns the combinatorial class of integer partitions of `n` subject to the constraints given by the keywords.
Valid keywords are: ``starting``, ``ending``, ``min_part``, ``max_part``, ``max_length``, ``min_length``, ``length``, ``max_slope``, ``min_slope``, ``inner``, ``outer``, ``parts_in`` and ``regular``. They have the following meanings:
- ``starting=p`` specifies that the partitions should all be less than or equal to `p` in lex order. This argument cannot be combined with any other (see :trac:`15467`).
- ``ending=p`` specifies that the partitions should all be greater than or equal to `p` in lex order. This argument cannot be combined with any other (see :trac:`15467`).
- ``length=k`` specifies that the partitions have exactly `k` parts.
- ``min_length=k`` specifies that the partitions have at least `k` parts.
- ``min_part=k`` specifies that all parts of the partitions are at least `k`.
- ``inner=p`` specifies that the partitions must contain the partition `p`.
- ``outer=p`` specifies that the partitions be contained inside the partition `p`.
- ``min_slope=k`` specifies that the partitions have slope at least `k`; the slope at position `i` is the difference between the `(i+1)`-th part and the `i`-th part.
- ``parts_in=S`` specifies that the partitions have parts in the set `S`, which can be any sequence of pairwise distinct positive integers. This argument cannot be combined with any other (see :trac:`15467`).
- ``regular=ell`` specifies that the partitions are `\ell`-regular, and can only be combined with the ``max_length`` or ``max_part``, but not both, keywords if `n` is not specified
The ``max_*`` versions, along with ``inner`` and ``ending``, work analogously.
Right now, the ``parts_in``, ``starting``, ``ending``, and ``regular`` keyword arguments are mutually exclusive, both of each other and of other keyword arguments. If you specify, say, ``parts_in``, all other keyword arguments will be ignored; ``starting``, ``ending``, and ``regular`` work the same way.
EXAMPLES:
If no arguments are passed, then the combinatorial class of all integer partitions is returned::
sage: Partitions() Partitions sage: [2,1] in Partitions() True
If an integer `n` is passed, then the combinatorial class of integer partitions of `n` is returned::
sage: Partitions(3) Partitions of the integer 3 sage: Partitions(3).list() [[3], [2, 1], [1, 1, 1]]
If ``starting=p`` is passed, then the combinatorial class of partitions greater than or equal to `p` in lexicographic order is returned::
sage: Partitions(3, starting=[2,1]) Partitions of the integer 3 starting with [2, 1] sage: Partitions(3, starting=[2,1]).list() [[2, 1], [1, 1, 1]]
If ``ending=p`` is passed, then the combinatorial class of partitions at most `p` in lexicographic order is returned::
sage: Partitions(3, ending=[2,1]) Partitions of the integer 3 ending with [2, 1] sage: Partitions(3, ending=[2,1]).list() [[3], [2, 1]]
Using ``max_slope=-1`` yields partitions into distinct parts -- each part differs from the next by at least 1. Use a different ``max_slope`` to get parts that differ by, say, 2::
sage: Partitions(7, max_slope=-1).list() [[7], [6, 1], [5, 2], [4, 3], [4, 2, 1]] sage: Partitions(15, max_slope=-1).cardinality() 27
The number of partitions of `n` into odd parts equals the number of partitions into distinct parts. Let's test that for `n` from 10 to 20::
sage: test = lambda n: Partitions(n, max_slope=-1).cardinality() == Partitions(n, parts_in=[1,3..n]).cardinality() sage: all(test(n) for n in [10..20]) True
The number of partitions of `n` into distinct parts that differ by at least 2 equals the number of partitions into parts that equal 1 or 4 modulo 5; this is one of the Rogers-Ramanujan identities::
sage: test = lambda n: Partitions(n, max_slope=-2).cardinality() == Partitions(n, parts_in=([1,6..n] + [4,9..n])).cardinality() sage: all(test(n) for n in [10..20]) True
Here are some more examples illustrating ``min_part``, ``max_part``, and ``length``::
sage: Partitions(5,min_part=2) Partitions of the integer 5 satisfying constraints min_part=2 sage: Partitions(5,min_part=2).list() [[5], [3, 2]]
::
sage: Partitions(3,max_length=2).list() [[3], [2, 1]]
::
sage: Partitions(10, min_part=2, length=3).list() [[6, 2, 2], [5, 3, 2], [4, 4, 2], [4, 3, 3]]
Some examples using the ``regular`` keyword::
sage: Partitions(regular=4) 4-Regular Partitions sage: Partitions(regular=4, max_length=3) 4-Regular Partitions with max length 3 sage: Partitions(regular=4, max_part=3) 4-Regular 3-Bounded Partitions sage: Partitions(3, regular=4) 4-Regular Partitions of the integer 3
Here are some further examples using various constraints::
sage: [x for x in Partitions(4)] [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: [x for x in Partitions(4, length=2)] [[3, 1], [2, 2]] sage: [x for x in Partitions(4, min_length=2)] [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: [x for x in Partitions(4, max_length=2)] [[4], [3, 1], [2, 2]] sage: [x for x in Partitions(4, min_length=2, max_length=2)] [[3, 1], [2, 2]] sage: [x for x in Partitions(4, max_part=2)] [[2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: [x for x in Partitions(4, min_part=2)] [[4], [2, 2]] sage: [x for x in Partitions(4, outer=[3,1,1])] [[3, 1], [2, 1, 1]] sage: [x for x in Partitions(4, outer=[infinity, 1, 1])] [[4], [3, 1], [2, 1, 1]] sage: [x for x in Partitions(4, inner=[1,1,1])] [[2, 1, 1], [1, 1, 1, 1]] sage: [x for x in Partitions(4, max_slope=-1)] [[4], [3, 1]] sage: [x for x in Partitions(4, min_slope=-1)] [[4], [2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: [x for x in Partitions(11, max_slope=-1, min_slope=-3, min_length=2, max_length=4)] [[7, 4], [6, 5], [6, 4, 1], [6, 3, 2], [5, 4, 2], [5, 3, 2, 1]] sage: [x for x in Partitions(11, max_slope=-1, min_slope=-3, min_length=2, max_length=4, outer=[6,5,2])] [[6, 5], [6, 4, 1], [6, 3, 2], [5, 4, 2]]
Note that if you specify ``min_part=0``, then it will treat the minimum part as being 1 (see :trac:`13605`)::
sage: [x for x in Partitions(4, length=3, min_part=0)] [[2, 1, 1]] sage: [x for x in Partitions(4, min_length=3, min_part=0)] [[2, 1, 1], [1, 1, 1, 1]]
Except for very special cases, counting is done by brute force iteration through all the partitions. However the iteration itself has a reasonable complexity (see :class:`IntegerListsLex`), which allows for manipulating large partitions::
sage: Partitions(1000, max_length=1).list() [[1000]]
In particular, getting the first element is also constant time::
sage: Partitions(30, max_part=29).first() [29, 1]
TESTS::
sage: TestSuite(Partitions(0)).run() sage: TestSuite(Partitions(5)).run() sage: TestSuite(Partitions(5, min_part=2)).run()
sage: repr( Partitions(5, min_part=2) ) 'Partitions of the integer 5 satisfying constraints min_part=2'
sage: P = Partitions(5, min_part=2) sage: P.first().parent() Partitions... sage: [2,1] in P False sage: [2,2,1] in P False sage: [3,2] in P True
sage: Partitions(5, inner=[2,1], min_length=3).list() [[3, 1, 1], [2, 2, 1], [2, 1, 1, 1]] sage: Partitions(5, inner=Partition([2,2]), min_length=3).list() [[2, 2, 1]] sage: Partitions(7, inner=(2, 2), min_length=3).list() [[4, 2, 1], [3, 3, 1], [3, 2, 2], [3, 2, 1, 1], [2, 2, 2, 1], [2, 2, 1, 1, 1]] sage: Partitions(5, inner=[2,0,0,0,0,0]).list() [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1]] sage: Partitions(6, length=2, max_slope=-1).list() [[5, 1], [4, 2]]
sage: Partitions(length=2, max_slope=-1).list() Traceback (most recent call last): ... ValueError: the size must be specified with any keyword argument
sage: Partitions(max_part = 3) 3-Bounded Partitions
Check that :trac:`14145` has been fixed::
sage: 1 in Partitions() False
Check :trac:`15467`::
sage: Partitions(5,parts_in=[1,2,3,4], length=4) Traceback (most recent call last): ... ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. sage: Partitions(5,starting=[3,2], length=2) Traceback (most recent call last): ... ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. sage: Partitions(5,ending=[3,2], length=2) Traceback (most recent call last): ... ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. sage: Partitions(NN, length=2) Traceback (most recent call last): ... ValueError: the size must be specified with any keyword argument sage: Partitions(('la','la','laaaa'), max_part=8) Traceback (most recent call last): ... ValueError: n must be an integer or be equal to one of None, NN, NonNegativeIntegers()
Check that calling ``Partitions`` with ``outer=a`` no longer mutates ``a`` (:trac:`16234`)::
sage: a = [4,3,2,1,1,1,1] sage: for p in Partitions(8, outer=a, min_slope=-1): ....: print(p) [3, 3, 2] [3, 2, 2, 1] [3, 2, 1, 1, 1] [2, 2, 2, 1, 1] [2, 2, 1, 1, 1, 1] [2, 1, 1, 1, 1, 1, 1] sage: a [4, 3, 2, 1, 1, 1, 1]
Check that ``inner`` and ``outer`` indeed accept a partition as argument (:trac:`18423`)::
sage: P = Partitions(5, inner=Partition([2,1]), outer=Partition([3,2])); P Partitions of the integer 5 satisfying constraints inner=[2, 1], outer=[3, 2] sage: P.list() [[3, 2]] """ @staticmethod def __classcall_private__(cls, n=None, **kwargs): """ Return the correct parent based upon the input.
TESTS::
sage: P = Partitions() sage: P2 = Partitions(NN) sage: P is P2 True sage: P2 = Partitions(NonNegativeIntegers()) sage: P is P2 True sage: P = Partitions(4) sage: P2 = Partitions(int(4)) sage: P is P2 True
Check that :trac:`17898` is fixed::
sage: P = Partitions(5, min_slope=0) sage: list(P) [[5], [1, 1, 1, 1, 1]] """ raise ValueError("n cannot be infinite") raise ValueError("the regularity must be a positive integer")
('parts_in' in kwargs or 'starting' in kwargs or 'ending' in kwargs)): "'ending' cannot be combined with anything else.")
# FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute
# min_part is at least 1, and it is 1 by default
# max_slope is at most 0, and it is 0 by default
raise ValueError("the minimum slope must be non-negative")
kwargs.get('max_length', infinity))
kwargs.get('min_length',0))
"None, NN, NonNegativeIntegers()")
def __init__(self, is_infinite=False): """ Initialize ``self``.
INPUT:
- ``is_infinite`` -- (Default: ``False``) If ``True``, then the number of partitions in this set is infinite.
EXAMPLES::
sage: Partitions() Partitions sage: Partitions(2) Partitions of the integer 2 """ else:
Element = Partition
# add options to class class options(GlobalOptions): r""" Sets and displays the global options for elements of the partition, skew partition, and partition tuple classes. If no parameters are set, then the function returns a copy of the options dictionary.
The ``options`` to partitions can be accessed as the method :obj:`Partitions.options` of :class:`Partitions` and related parent classes.
@OPTIONS@
EXAMPLES::
sage: P = Partition([4,2,2,1]) sage: P [4, 2, 2, 1] sage: Partitions.options.display="exp" sage: P 1, 2^2, 4 sage: Partitions.options.display="exp_high" sage: P 4, 2^2, 1
It is also possible to use user defined functions for the ``display`` and ``latex`` options::
sage: Partitions.options(display=lambda mu: '<%s>' % ','.join('%s'%m for m in mu._list)); P <4,2,2,1> sage: Partitions.options(latex=lambda mu: '\\Diagram{%s}' % ','.join('%s'%m for m in mu._list)); latex(P) \Diagram{4,2,2,1} sage: Partitions.options(display="diagram", diagram_str="#") sage: P #### ## ## # sage: Partitions.options(diagram_str="*", convention="french") sage: print(P.ferrers_diagram()) * ** ** ****
Changing the ``convention`` for partitions also changes the ``convention`` option for tableaux and vice versa::
sage: T = Tableau([[1,2,3],[4,5]]) sage: T.pp() 4 5 1 2 3 sage: Tableaux.options.convention="english" sage: print(P.ferrers_diagram()) **** ** ** * sage: T.pp() 1 2 3 4 5 sage: Partitions.options._reset() """ NAME = 'Partitions' module = 'sage.combinat.partition' display = dict(default="list", description='Specifies how partitions should be printed', values=dict(list='displayed as a list', exp_low='in exponential form (lowest first)', exp_high='in exponential form (highest first)', diagram='as a Ferrers diagram', compact_low='compact form of ``exp_low``', compact_high='compact form of ``exp_high``'), alias=dict(exp="exp_low", compact="compact_low", array="diagram", ferrers_diagram="diagram", young_diagram="diagram"), case_sensitive=False) latex = dict(default="young_diagram", description='Specifies how partitions should be latexed', values=dict(diagram='latex as a Ferrers diagram', young_diagram='latex as a Young diagram', list='latex as a list', exp_high='latex as a list in exponential notation (highest first)', exp_low='as a list latex in exponential notation (lowest first)'), alias=dict(exp="exp_low", array="diagram", ferrers_diagram="diagram"), case_sensitive=False) description='The character used for the cells when printing Ferrers diagrams', checker=lambda char: isinstance(char,str)) latex_diagram_str = dict(default="\\ast", description='The character used for the cells when latexing Ferrers diagrams', checker=lambda char: isinstance(char,str)) convention = dict(link_to=(tableau.Tableaux.options,'convention')) notation = dict(alt_name='convention')
def __reversed__(self): """ A reversed iterator.
EXAMPLES::
sage: [x for x in reversed(Partitions(4))] [[1, 1, 1, 1], [2, 1, 1], [2, 2], [3, 1], [4]] """ raise NotImplementedError("The set is infinite. This needs a custom reverse iterator")
def _element_constructor_(self, lst): """ Construct an element with ``self`` as parent.
EXAMPLES::
sage: P = Partitions() sage: p = P([3,3,1]); p [3, 3, 1] sage: P(p) is p True sage: P([3, 2, 1, 0]) [3, 2, 1]
sage: PT = PartitionTuples() sage: elt = PT([[4,4,2,2,1]]); elt ([4, 4, 2, 2, 1]) sage: P(elt) [4, 4, 2, 2, 1] """ raise ValueError('%s is not an element of %s'%(lst, self)) # Trailing zeros are removed in the element constructor
def __contains__(self, x): """ Check if ``x`` is contained in ``self``.
TESTS::
sage: P = Partitions() sage: Partition([2,1]) in P True sage: [2,1] in P True sage: [3,2,1] in P True sage: [1,2] in P False sage: [] in P True sage: [0] in P True
Check that types that represent integers are not excluded::
sage: P = Partitions() sage: [3/1, 2/2] in P True sage: Partition([3/1, 2]) in P True """ all(x[i] in NN and x[i] >= x[i+1] for i in range(len(x)-1)))
def subset(self, *args, **kwargs): r""" Return ``self`` if no arguments are given, otherwise raises a ``ValueError``.
EXAMPLES::
sage: P = Partitions(5, starting=[3,1]); P Partitions of the integer 5 starting with [3, 1] sage: P.subset() Partitions of the integer 5 starting with [3, 1] sage: P.subset(ending=[3,1]) Traceback (most recent call last): ... ValueError: Invalid combination of arguments """
class Partitions_all(Partitions): """ Class of all partitions.
TESTS::
sage: TestSuite( sage.combinat.partition.Partitions_all() ).run() """
def __init__(self): """ Initialize ``self``.
TESTS::
sage: P = Partitions() sage: P.category() Category of infinite enumerated sets sage: Partitions().cardinality() +Infinity sage: TestSuite(P).run() """ Partitions.__init__(self, is_infinite=True)
def subset(self, size=None, **kwargs): """ Returns the subset of partitions of a given size and additional keyword arguments.
EXAMPLES::
sage: P = Partitions() sage: P.subset(4) Partitions of the integer 4 """ return self
def _repr_(self): """ Return a string representation of ``self``.
TESTS::
sage: Partitions() # indirect doctest Partitions """
def __iter__(self): """ An iterator for all partitions.
EXAMPLES::
sage: p = Partitions() sage: it = p.__iter__() sage: [next(it) for i in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [1, 1, 1], [4], [3, 1], [2, 2]] """
def __reversed__(self): """ A reversed iterator for all partitions.
This reverse iterates through partitions of fixed `n` and incrementing `n` after reaching the end.
EXAMPLES::
sage: p = Partitions() sage: revit = p.__reversed__() sage: [next(revit) for i in range(10)] [[], [1], [1, 1], [2], [1, 1, 1], [2, 1], [3], [1, 1, 1, 1], [2, 1, 1], [2, 2]] """
def from_frobenius_coordinates(self, frobenius_coordinates): """ Returns a partition from a pair of sequences of Frobenius coordinates.
EXAMPLES::
sage: Partitions().from_frobenius_coordinates(([],[])) [] sage: Partitions().from_frobenius_coordinates(([0],[0])) [1] sage: Partitions().from_frobenius_coordinates(([1],[1])) [2, 1] sage: Partitions().from_frobenius_coordinates(([6,3,2],[4,1,0])) [7, 5, 5, 1, 1] """ raise ValueError('%s is not a valid partition, two sequences of coordinates are needed'%str(frobenius_coordinates)) else: raise ValueError('%s is not a valid partition, the sequences of coordinates need to be the same length'%str(frobenius_coordinates)) # should add tests to see if a and b are sorted down, nonnegative and strictly decreasing # should check that a is strictly decreasing raise ValueError('%s is not a partition, no coordinate can be negative'%str(frobenius_coordinates)) else: raise ValueError('%s is not a partition, no coordinate can be negative'%str(frobenius_coordinates)) else: raise ValueError('%s is not a partition, the coordinates need to be strictly decreasing'%str(frobenius_coordinates))
def from_beta_numbers(self, beta): r""" Return a partition corresponding to a sequence of beta numbers.
A sequence of beta numbers is a strictly increasing sequence `0 \leq b_1 < \cdots < b_k` of non-negative integers. The corresponding partition `\mu = (\mu_k, \ldots, \mu_1)` is given by `\mu_i = [1,i) \setminus \{ b_1, \ldots, b_i \}`. This gives a bijection from the set of partitions with at most `k` non-zero parts to the set of strictly increasing sequences of non-negative integers of length `k`.
EXAMPLES::
sage: Partitions().from_beta_numbers([0,1,2,4,5,8]) [3, 1, 1] sage: Partitions().from_beta_numbers([0,2,3,6]) [3, 1, 1] """
def from_exp(self, exp): """ Returns a partition from its list of multiplicities.
EXAMPLES::
sage: Partitions().from_exp([2,2,1]) [3, 2, 2, 1, 1] """
def from_zero_one(self, seq): r""" Return a partition from its `0-1` sequence.
The full `0-1` sequence is the sequence (infinite in both directions) indicating the steps taken when following the outer rim of the diagram of the partition. We use the convention that in English convention, a 1 corresponds to an East step, and a 0 corresponds to a North step.
Note that every full `0-1` sequence starts with infinitely many 0's and ends with infinitely many 1's.
.. SEEALSO::
:meth:`Partition.zero_one_sequence()`
INPUT:
The input should be a finite sequence of 0's and 1's. The heading 0's and trailing 1's will be discarded.
EXAMPLES::
sage: Partitions().from_zero_one([]) [] sage: Partitions().from_zero_one([1,0]) [1] sage: Partitions().from_zero_one([1, 1, 1, 1, 0, 1, 0]) [5, 4]
Heading 0's and trailing 1's are correctly handled::
sage: Partitions().from_zero_one([0,0,1,1,1,1,0,1,0,1,1,1]) [5, 4]
TESTS::
sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) == mu for n in range(10) for mu in Partitions(n)) True """
def from_core_and_quotient(self, core, quotient): """ Returns a partition from its core and quotient.
Algorithm from mupad-combinat.
EXAMPLES::
sage: Partitions().from_core_and_quotient([2,1], [[2,1],[3],[1,1,1]]) [11, 5, 5, 3, 2, 2, 2]
TESTS::
sage: Partitions().from_core_and_quotient([2,1], [[2,1],[2,3,1],[1,1,1]]) Traceback (most recent call last): ... ValueError: the quotient [[2, 1], [2, 3, 1], [1, 1, 1]] must be a tuple of partitions
We check that :trac:`11412` is actually fixed::
sage: test = lambda x, k: x == Partition(core=x.core(k), ....: quotient=x.quotient(k)) sage: all(test(mu,k) for k in range(1,5) ....: for n in range(10) for mu in Partitions(n)) True sage: test2 = lambda core, mus: ( ....: Partition(core=core, quotient=mus).core(mus.level()) == core ....: and ....: Partition(core=core, quotient=mus).quotient(mus.level()) == mus) sage: all(test2(core,mus) # long time (5s on sage.math, 2011) ....: for k in range(1,10) ....: for n_core in range(10-k) ....: for core in Partitions(n_core) ....: if core.core(k) == core ....: for n_mus in range(10-k) ....: for mus in PartitionTuples(k,n_mus)) True """ # k needs to be large enough. this seems to me like the smallest it can be # k needs to be chosen so lw >= lq
class Partitions_all_bounded(Partitions):
def __init__(self, k): """ TESTS::
sage: TestSuite( sage.combinat.partition.Partitions_all_bounded(3) ).run() # long time """
def __contains__(self, x): """ TESTS::
sage: P = Partitions(max_part=3) sage: Partition([2,1]) in P True sage: [2,1] in P True sage: [3,2,1] in P True sage: [1,2] in P False sage: [5,1] in P False sage: [0] in P True sage: [] in P True """
def _repr_(self): """ TESTS::
sage: from sage.combinat.partition import Partitions_all_bounded sage: Partitions_all_bounded(3) 3-Bounded Partitions """
def __iter__(self): """ An iterator for all `k`-bounded partitions.
EXAMPLES::
sage: p = Partitions(max_part=3) sage: it = p.__iter__() sage: [next(it) for i in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [1, 1, 1], [3, 1], [2, 2], [2, 1, 1]] """
class Partitions_n(Partitions): """ Partitions of the integer `n`.
TESTS::
sage: TestSuite( sage.combinat.partition.Partitions_n(0) ).run() sage: TestSuite( sage.combinat.partition.Partitions_n(0) ).run() """
def __init__(self, n): """ Initialize ``self``.
TESTS::
sage: TestSuite( Partitions(5) ).run() """
def __contains__(self, x): """ Check if ``x`` is contained in ``self``.
TESTS::
sage: p = Partitions(5) sage: [2,1] in p False sage: [2,2,1] in p True sage: [3,2] in p True sage: [2,3] in p False """
def _repr_(self): """ Return a string representation of ``self``.
TESTS::
sage: Partitions(5) # indirect doctest Partitions of the integer 5 """
def _an_element_(self): """ Returns a partition in ``self``.
EXAMPLES::
sage: Partitions(4).an_element() # indirect doctest [3, 1] sage: Partitions(0).an_element() [] sage: Partitions(1).an_element() [1] """ else:
def cardinality(self, algorithm='flint'): r""" Return the number of partitions of the specified size.
INPUT:
- ``algorithm`` - (default: ``'flint'``)
- ``'flint'`` -- use FLINT (currently the fastest) - ``'bober'`` -- Use Jonathan Bober's implementation (*very* fast) - ``'gap'`` -- use GAP (VERY *slow*) - ``'pari'`` -- use PARI. Speed seems the same as GAP until `n` is in the thousands, in which case PARI is faster.
It is possible to associate with every partition of the integer `n` a conjugacy class of permutations in the symmetric group on `n` points and vice versa. Therefore the number of partitions `p_n` is the number of conjugacy classes of the symmetric group on `n` points.
EXAMPLES::
sage: v = Partitions(5).list(); v [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] sage: len(v) 7 sage: Partitions(5).cardinality(algorithm='gap') 7 sage: Partitions(5).cardinality(algorithm='pari') 7 sage: Partitions(5).cardinality(algorithm='bober') 7 sage: number_of_partitions(5, algorithm='flint') 7
The input must be a nonnegative integer or a ``ValueError`` is raised.
::
sage: Partitions(10).cardinality() 42 sage: Partitions(3).cardinality() 3 sage: Partitions(10).cardinality() 42 sage: Partitions(3).cardinality(algorithm='pari') 3 sage: Partitions(10).cardinality(algorithm='pari') 42 sage: Partitions(40).cardinality() 37338 sage: Partitions(100).cardinality() 190569292
A generating function for `p_n` is given by the reciprocal of Euler's function:
.. MATH::
\sum_{n=0}^{\infty} p_n x^n = \prod_{k=1}^{\infty} \frac{1}{1-x^k}.
We use Sage to verify that the first several coefficients do indeed agree::
sage: q = PowerSeriesRing(QQ, 'q', default_prec=9).gen() sage: prod([(1-q^k)^(-1) for k in range(1,9)]) ## partial product of 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + 15*q^7 + 22*q^8 + O(q^9) sage: [Partitions(k).cardinality() for k in range(2,10)] [2, 3, 5, 7, 11, 15, 22, 30]
Another consistency test for ``n`` up to 500::
sage: len([n for n in [1..500] if Partitions(n).cardinality() != Partitions(n).cardinality(algorithm='pari')]) 0
REFERENCES:
- :wikipedia:`Partition\_(number\_theory)` """
return bober_number_of_partitions(self.n)
return ZZ(gap.eval("NrPartitions(%s)" % (ZZ(self.n))))
raise ValueError("unknown algorithm '%s'" % algorithm)
def random_element(self, measure = 'uniform'): """ Return a random partitions of `n` for the specified measure.
INPUT:
- ``measure`` -- ``'uniform'`` or ``'Plancherel'`` (default: ``'uniform'``)
.. SEEALSO::
- :meth:`random_element_uniform` - :meth:`random_element_plancherel`
EXAMPLES::
sage: Partitions(5).random_element() # random [2, 1, 1, 1] sage: Partitions(5).random_element(measure='Plancherel') # random [2, 1, 1, 1] """ else: raise ValueError("Unkown measure: %s" % (measure))
def random_element_uniform(self): """ Return a random partition of `n` with uniform probability.
EXAMPLES::
sage: Partitions(5).random_element_uniform() # random [2, 1, 1, 1] sage: Partitions(20).random_element_uniform() # random [9, 3, 3, 2, 2, 1]
TESTS::
sage: all(Part.random_element_uniform() in Part ....: for Part in map(Partitions, range(10))) True
Check that :trac:`18752` is fixed::
sage: P = Partitions(5) sage: la = P.random_element_uniform() sage: la.parent() is P True
ALGORITHM:
- It is a python Implementation of RANDPAR, see [nw]_. The complexity is unknown, there may be better algorithms.
.. TODO::
Check in Knuth AOCP4.
- There is also certainly a lot of room for optimizations, see comments in the code.
REFERENCES:
.. [nw] Nijenhuis, Wilf, Combinatorial Algorithms, Academic Press (1978).
AUTHOR:
- Florent Hivert (2009-11-23) """ # Choose a pair d,j = 1,2..., with d*j <= n with probability # d*numpart(n-d*j) / n / numpart(n) # and add d^j to the result partition. The resulting partitions is # equiprobable.
# The following could be made faster by a clever use of floats
# It is better to start by the j = 1 pairs because they are the # most probable. Maybe there is an even more clever order. else:
def random_element_plancherel(self): r""" Return a random partition of `n` (for the Plancherel measure).
This probability distribution comes from the uniform distribution on permutations via the Robinson-Schensted correspondence.
See :wikipedia:`Plancherel\_measure` and :meth:`Partition.plancherel_measure`.
EXAMPLES::
sage: Partitions(5).random_element_plancherel() # random [2, 1, 1, 1] sage: Partitions(20).random_element_plancherel() # random [9, 3, 3, 2, 2, 1]
TESTS::
sage: all(Part.random_element_plancherel() in Part ....: for Part in map(Partitions, range(10))) True
Check that :trac:`18752` is fixed::
sage: P = Partitions(5) sage: la = P.random_element_plancherel() sage: la.parent() is P True
ALGORITHM:
- insert by Robinson-Schensted a uniform random permutations of n and returns the shape of the resulting tableau. The complexity is `O(n\ln(n))` which is likely optimal. However, the implementation could be optimized.
AUTHOR:
- Florent Hivert (2009-11-23) """
def first(self): """ Returns the lexicographically first partition of a positive integer `n`. This is the partition ``[n]``.
EXAMPLES::
sage: Partitions(4).first() [4] """
def next(self, p): """ Return the lexicographically next partition after the partition ``p``.
EXAMPLES::
sage: Partitions(4).next([4]) [3, 1] sage: Partitions(4).next([1,1,1,1]) is None True """
def last(self): """ Return the lexicographically last partition of the positive integer `n`. This is the all-ones partition.
EXAMPLES::
sage: Partitions(4).last() [1, 1, 1, 1] """
def __iter__(self): """ An iterator for the partitions of `n`.
EXAMPLES::
sage: [x for x in Partitions(4)] [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] """
def subset(self, **kwargs): r""" Return a subset of ``self`` with the additional optional arguments.
EXAMPLES::
sage: P = Partitions(5); P Partitions of the integer 5 sage: P.subset(starting=[3,1]) Partitions of the integer 5 starting with [3, 1] """
class Partitions_nk(Partitions): """ Partitions of the integer `n` of length equal to `k`.
TESTS::
sage: TestSuite( sage.combinat.partition.Partitions_nk(0,0) ).run() sage: TestSuite( sage.combinat.partition.Partitions_nk(0,0) ).run() """
def __init__(self, n, k): """ Initialize ``self``.
TESTS::
sage: TestSuite( Partitions(5, length=2) ).run() """
def __contains__(self, x): """ Check if ``x`` is contained in ``self``.
TESTS::
sage: p = Partitions(5, length=2) sage: [2,1] in p False sage: [2,2,1] in p False sage: [3,2] in p True sage: [2,3] in p False sage: [4,1] in p True sage: [1,1,1,1,1] in p False sage: [5] in p False """
def _repr_(self): """ Return a string representation of ``self``.
TESTS::
sage: Partitions(5, length=2) # indirect doctest Partitions of the integer 5 of length 2 """
def _an_element_(self): """ Returns a partition in ``self``.
EXAMPLES::
sage: Partitions(4, length=1).an_element() # indirect doctest [4] sage: Partitions(4, length=2).an_element() [3, 1] sage: Partitions(4, length=3).an_element() [2, 1, 1] sage: Partitions(4, length=4).an_element() [1, 1, 1, 1]
sage: Partitions(1, length=1).an_element() [1]
sage: Partitions(0, length=0).an_element() [] """ else: from sage.categories.sets_cat import EmptySetError raise EmptySetError else: from sage.categories.sets_cat import EmptySetError raise EmptySetError
def __iter__(self): """ An iterator for all partitions of `n` of length `k`.
EXAMPLES::
sage: p = Partitions(9, length=3) sage: it = p.__iter__() sage: list(it) [[7, 1, 1], [6, 2, 1], [5, 3, 1], [5, 2, 2], [4, 4, 1], [4, 3, 2], [3, 3, 3]]
sage: p = Partitions(9, length=10) sage: list(p.__iter__()) []
sage: p = Partitions(0, length=0) sage: list(p.__iter__()) [[]]
sage: from sage.combinat.partition import number_of_partitions_length sage: all( len(Partitions(n, length=k).list()) ....: == number_of_partitions_length(n, k) ....: for n in range(9) for k in range(n+2) ) True """
def cardinality(self, algorithm='hybrid'): r""" Return the number of partitions of the specified size with the specified length.
INPUT:
- ``algorithm`` -- (default: ``'hybrid'``) the algorithm to compute the cardinality and can be one of the following:
* ``'hybrid'`` - use a hybrid algorithm which uses heuristics to reduce the complexity * ``'gap'`` - use GAP
EXAMPLES::
sage: v = Partitions(5, length=2).list(); v [[4, 1], [3, 2]] sage: len(v) 2 sage: Partitions(5, length=2).cardinality() 2
More generally, the number of partitions of `n` of length `2` is `\left\lfloor \frac{n}{2} \right\rfloor`::
sage: all( Partitions(n, length=2).cardinality() ....: == n // 2 for n in range(10) ) True
The number of partitions of `n` of length `1` is `1` for `n` positive::
sage: all( Partitions(n, length=1).cardinality() == 1 ....: for n in range(1, 10) ) True
Further examples::
sage: Partitions(5, length=3).cardinality() 2 sage: Partitions(6, length=3).cardinality() 3 sage: Partitions(8, length=4).cardinality() 5 sage: Partitions(8, length=5).cardinality() 3 sage: Partitions(15, length=6).cardinality() 26 sage: Partitions(0, length=0).cardinality() 1 sage: Partitions(0, length=1).cardinality() 0 sage: Partitions(1, length=0).cardinality() 0 sage: Partitions(1, length=4).cardinality() 0
TESTS:
We check the hybrid approach gives the same results as GAP::
sage: N = [0, 1, 2, 3, 5, 10, 20, 500, 850] sage: K = [0, 1, 2, 3, 5, 10, 11, 20, 21, 250, 499, 500] sage: all(Partitions(n,length=k).cardinality() == Partitions(n,length=k).cardinality('gap') ....: for n in N for k in K) True sage: P = Partitions(4562, length=2800) sage: P.cardinality() == P.cardinality('gap') True """
def subset(self, **kwargs): r""" Return a subset of ``self`` with the additional optional arguments.
EXAMPLES::
sage: P = Partitions(5, length=2); P Partitions of the integer 5 of length 2 sage: P.subset(max_part=3) Partitions of the integer 5 satisfying constraints length=2, max_part=3 """
class Partitions_parts_in(Partitions): """ Partitions of `n` with parts in a given set `S`.
This is invoked indirectly when calling ``Partitions(n, parts_in=parts)``, where ``parts`` is a list of pairwise distinct integers.
TESTS::
sage: TestSuite( sage.combinat.partition.Partitions_parts_in(6, parts=[2,1]) ).run() """
@staticmethod def __classcall_private__(cls, n, parts): """ Normalize the input to ensure a unique representation.
TESTS::
sage: P = Partitions(4, parts_in=[2,1]) sage: P2 = Partitions(4, parts_in=(1,2)) sage: P is P2 True """
def __init__(self, n, parts): """ Initialize ``self``.
TESTS::
sage: TestSuite(Partitions(5, parts_in=[1,2,3])).run() """
def __contains__(self, x): """ TESTS::
sage: p = Partitions(5, parts_in=[1,2]) sage: [2,1,1,1] in p True sage: [4,1] in p False """ all(p in self.parts for p in x))
def _repr_(self): """ TESTS::
sage: Partitions(5, parts_in=[1,2,3]) # indirect doctest Partitions of the integer 5 with parts in [1, 2, 3] """
def cardinality(self): r""" Return the number of partitions with parts in ``self``. Wraps GAP's ``NrRestrictedPartitions``.
EXAMPLES::
sage: Partitions(15, parts_in=[2,3,7]).cardinality() 5
If you can use all parts 1 through `n`, we'd better get `p(n)`::
sage: Partitions(20, parts_in=[1..20]).cardinality() == Partitions(20).cardinality() True
TESTS:
Let's check the consistency of GAP's function and our own algorithm that actually generates the partitions::
sage: ps = Partitions(15, parts_in=[1,2,3]) sage: ps.cardinality() == len(ps.list()) True sage: ps = Partitions(15, parts_in=[]) sage: ps.cardinality() == len(ps.list()) True sage: ps = Partitions(3000, parts_in=[50,100,500,1000]) sage: ps.cardinality() == len(ps.list()) True sage: ps = Partitions(10, parts_in=[3,6,9]) sage: ps.cardinality() == len(ps.list()) True sage: ps = Partitions(0, parts_in=[1,2]) sage: ps.cardinality() == len(ps.list()) True """ # GAP complains if you give it an empty list else:
def first(self): """ Return the lexicographically first partition of a positive integer `n` with the specified parts, or ``None`` if no such partition exists.
EXAMPLES::
sage: Partitions(9, parts_in=[3,4]).first() [3, 3, 3] sage: Partitions(6, parts_in=[1..6]).first() [6] sage: Partitions(30, parts_in=[4,7,8,10,11]).first() [11, 11, 8] """ except TypeError: return None
def _findfirst(self, n, parts): """ TESTS::
sage: p = Partitions(9, parts_in=[3,4]) sage: p._findfirst(p.n, p.parts[:]) [3, 3, 3] sage: p._findfirst(0, p.parts[:]) [] sage: p._findfirst(p.n, [10])
""" else:
def last(self): """ Return the lexicographically last partition of the positive integer `n` with the specified parts, or ``None`` if no such partition exists.
EXAMPLES::
sage: Partitions(15, parts_in=[2,3]).last() [3, 2, 2, 2, 2, 2, 2] sage: Partitions(30, parts_in=[4,7,8,10,11]).last() [7, 7, 4, 4, 4, 4] sage: Partitions(10, parts_in=[3,6]).last() is None True sage: Partitions(50, parts_in=[11,12,13]).last() [13, 13, 12, 12] sage: Partitions(30, parts_in=[4,7,8,10,11]).last() [7, 7, 4, 4, 4, 4]
TESTS::
sage: Partitions(6, parts_in=[1..6]).last() [1, 1, 1, 1, 1, 1] sage: Partitions(0, parts_in=[]).last() [] sage: Partitions(50, parts_in=[11,12]).last() is None True """
def _findlast(self, n, parts): """ Return the lexicographically largest partition of `n` using the given parts, or ``None`` if no such partition exists. This function is not intended to be called directly.
INPUT:
- ``n`` -- nonnegative integer
- ``parts`` -- a sorted list of positive integers.
OUTPUT:
A list of integers in weakly decreasing order, or ``None``. The output is just a list, not a partition object.
EXAMPLES::
sage: ps = Partitions(1, parts_in=[1]) sage: ps._findlast(15, [2,3]) [3, 2, 2, 2, 2, 2, 2] sage: ps._findlast(9, [2,4]) is None True sage: ps._findlast(0, []) [] sage: ps._findlast(100, [9,17,31]) [31, 17, 17, 17, 9, 9] """ # If the smallest part doesn't divide n, try using the next # largest part else: # If we get to here, nothing ever worked, so there's no such # partitions, and we return None.
def __iter__(self): """ An iterator through the partitions of `n` with all parts belonging to a particular set.
EXAMPLES::
sage: [x for x in Partitions(4)] [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] """
def _fast_iterator(self, n, parts): """ A fast iterator for the partitions of ``n`` which returns lists and not partition types. This function is not intended to be called directly.
INPUT:
- ``n`` -- nonnegative integer.
- ``parts`` -- a list of parts to use. This list will be destroyed, so pass things here with ``foo[:]`` (or something equivalent) if you want to preserve your list. In particular, the ``__iter__`` method needs to use ``self.parts[:]``, or else we forget which parts we're using!
OUTPUT:
A generator object for partitions of `n` with parts in ``parts``.
If the parts in ``parts`` are sorted in increasing order, this function returns weakly decreasing lists. If ``parts`` is not sorted, your lists won't be, either.
EXAMPLES::
sage: P = Partitions(4, parts_in=[2,4]) sage: it = P._fast_iterator(4, [2,4]) sage: next(it) [4] sage: type(_) <... 'list'> """ else:
class Partitions_starting(Partitions): """ All partitions with a given start. """
@staticmethod def __classcall_private__(cls, n, starting_partition): """ Normalize the input to ensure a unique representation.
TESTS::
sage: P = Partitions(4, starting=[2,1]) sage: P2 = Partitions(4, starting=[2,1]) sage: P is P2 True """ starting_partition)
def __init__(self, n, starting_partition): """ Initilizes ``self``.
EXAMPLES::
sage: Partitions(3, starting=[2,1]) Partitions of the integer 3 starting with [2, 1] sage: Partitions(3, starting=[2,1]).list() [[2, 1], [1, 1, 1]]
TESTS::
sage: p = Partitions(3, starting=[2,1]) sage: TestSuite(p).run() """
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: Partitions(3, starting=[2,1]) # indirect doctest Partitions of the integer 3 starting with [2, 1] """
def __contains__(self, x): """ Checks if ``x`` is contained in ``self``.
EXAMPLES::
sage: p = Partitions(3, starting=[2,1]) sage: [1,1] in p False sage: [2,1] in p True sage: [1,1,1] in p True sage: [3] in p False """
def first(self): """ Return the first partition in ``self``.
EXAMPLES::
sage: Partitions(3, starting=[2,1]).first() [2, 1] """
def next(self, part): """ Return the next partition after ``part`` in ``self``.
EXAMPLES::
sage: Partitions(3, starting=[2,1]).next(Partition([2,1])) [1, 1, 1] """
class Partitions_ending(Partitions): """ All partitions with a given ending. """
@staticmethod def __classcall_private__(cls, n, ending_partition): """ Normalize the input to ensure a unique representation.
TESTS::
sage: P = Partitions(4) sage: P2 = Partitions(4) sage: P is P2 True """ ending_partition)
def __init__(self, n, ending_partition): """ Initializes ``self``.
EXAMPLES::
sage: Partitions(4, ending=[1,1,1,1]).list() [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] sage: Partitions(4, ending=[2,2]).list() [[4], [3, 1], [2, 2]] sage: Partitions(4, ending=[4]).list() [[4]]
TESTS::
sage: p = Partitions(4, ending=[1,1,1,1]) sage: TestSuite(p).run() """
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: Partitions(4, ending=[1,1,1,1]) # indirect doctest Partitions of the integer 4 ending with [1, 1, 1, 1] """
def __contains__(self, x): """ Checks if ``x`` is contained in ``self``.
EXAMPLES::
sage: p = Partitions(4, ending=[2,2]) sage: [4] in p True sage: [2,1,1] in p False sage: [2,1] in p False """
def first(self): """ Return the first partition in ``self``.
EXAMPLES::
sage: Partitions(4, ending=[1,1,1,1]).first() [4] """
def next(self, part): """ Return the next partition after ``part`` in ``self``.
EXAMPLES::
sage: Partitions(4, ending=[1,1,1,1]).next(Partition([4])) [3, 1] sage: Partitions(4, ending=[1,1,1,1]).next(Partition([1,1,1,1])) is None True """ else:
class PartitionsInBox(Partitions): r""" All partitions which fit in an `h \times w` box.
EXAMPLES::
sage: PartitionsInBox(2,2) Integer partitions which fit in a 2 x 2 box sage: PartitionsInBox(2,2).list() [[], [1], [1, 1], [2], [2, 1], [2, 2]] """
def __init__(self, h, w): """ Initialize ``self``.
TESTS::
sage: p = PartitionsInBox(2,2) sage: TestSuite(p).run() """
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: PartitionsInBox(2,2) # indirect doctest Integer partitions which fit in a 2 x 2 box """
def __contains__(self, x): """ Checks if ``x`` is contained in ``self``.
EXAMPLES::
sage: [] in PartitionsInBox(2,2) True sage: [2,1] in PartitionsInBox(2,2) True sage: [3,1] in PartitionsInBox(2,2) False sage: [2,1,1] in PartitionsInBox(2,2) False sage: [3,1] in PartitionsInBox(3, 2) False sage: [3,1] in PartitionsInBox(2, 3) True """ and (len(x) == 0 or x[0] <= self.w)
def list(self): """ Return a list of all the partitions inside a box of height `h` and width `w`.
EXAMPLES::
sage: PartitionsInBox(2,2).list() [[], [1], [1, 1], [2], [2, 1], [2, 2]] sage: PartitionsInBox(2,3).list() [[], [1], [1, 1], [2], [2, 1], [2, 2], [3], [3, 1], [3, 2], [3, 3]]
TESTS:
Check :trac:`10890`::
sage: type(PartitionsInBox(0,0)[0]) <class 'sage.combinat.partition.PartitionsInBox_with_category.element_class'> """ else:
class Partitions_constraints(IntegerListsLex): """ For unpickling old constrained ``Partitions_constraints`` objects created with sage <= 3.4.1. See :class:`Partitions`. """ def __setstate__(self, data): r""" TESTS::
sage: dmp = 'x\x9ck`J.NLO\xd5K\xce\xcfM\xca\xccK,\xd1+H,*\xc9,\xc9\xcc\xcf\xe3\n\x80\xb1\x8a\xe3\x93\x81DIQbf^I1W!\xa3fc!Sm!\xb3F(7\x92x!Km!k(GnbE<\xc8\x88B6\x88\xb9E\x99y\xe9\xc5z@\x05\xa9\xe9\xa9E\\\xb9\x89\xd9\xa9\xf10N!{(\xa3QkP!Gq(c^\x06\x90c\x0c\xe4p\x96&\xe9\x01\x00\xc2\xe53\xfd' sage: sp = loads(dmp); sp Integer lists of sum 3 satisfying certain constraints sage: sp.list() [[2, 1], [1, 1, 1]] """ 'min_part' : 1}
class Partitions_with_constraints(IntegerListsLex): """ Partitions which satisfy a set of constraints.
EXAMPLES::
sage: P = Partitions(6, inner=[1,1], max_slope=-1) sage: list(P) [[5, 1], [4, 2], [3, 2, 1]]
TESTS::
sage: P = Partitions(6, min_part=2, max_slope=-1) sage: TestSuite(P).run()
Test that :trac:`15525` is fixed::
sage: loads(dumps(P)) == P True """ # def __init__(self, n, **kwargs): # """ # Initialize ``self``. # """ # IntegerListsLex.__init__(self, n, **kwargs)
Element = Partition options = Partitions.options
###################### # Regular Partitions # ######################
class RegularPartitions(Partitions): r""" Base class for `\ell`-regular partitions.
Let `\ell` be a positive integer. A partition `\lambda` is `\ell`-*regular* if `m_i < \ell` for all `i`, where `m_i` is the multiplicity of `i` in `\lambda`.
.. NOTE::
This is conjugate to the notion of `\ell`-*restricted* partitions, where the difference between any two consecutive parts is `< \ell`.
INPUT:
- ``ell`` -- the positive integer `\ell` - ``is_infinite`` -- boolean; if the subset of `\ell`-regular partitions is infinite """ def __init__(self, ell, is_infinite=False): """ Initialize ``self``.
EXAMPLES::
sage: P = Partitions(regular=2) sage: TestSuite(P).run() """
def ell(self): r""" Return the value `\ell`.
EXAMPLES::
sage: P = Partitions(regular=2) sage: P.ell() 2 """
def __contains__(self, x): """ TESTS::
sage: P = Partitions(regular=3) sage: [5] in P True sage: [] in P True sage: [3, 3, 2, 2] in P True sage: [3, 3, 3, 1] in P False sage: [4, 0, 0, 0, 0, 0] in P True sage: Partition([4,2,2,1]) in P True sage: Partition([4,2,2,2]) in P False sage: Partition([10,1]) in P True """
def _fast_iterator(self, n, max_part): """ A fast (recursive) iterator which returns a list.
EXAMPLES::
sage: P = Partitions(regular=3) sage: list(P._fast_iterator(5, 5)) [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] sage: list(P._fast_iterator(5, 3)) [[3, 2], [3, 1, 1], [2, 2, 1]] sage: list(P._fast_iterator(5, 6)) [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] """
class RegularPartitions_all(RegularPartitions): r""" The class of all `\ell`-regular partitions.
INPUT:
- ``ell`` -- the positive integer `\ell`
.. SEEALSO::
:class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, ell): """ Initialize ``self``.
EXAMPLES::
sage: P = Partitions(regular=4) sage: TestSuite(P).run()
1-regular partitions::
sage: P = Partitions(regular=1) sage: P in FiniteEnumeratedSets() True sage: TestSuite(P).run() """
def _repr_(self): """ TESTS::
sage: from sage.combinat.partition import RegularPartitions_all sage: RegularPartitions_all(3) 3-Regular Partitions """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: P = Partitions(regular=3) sage: it = P.__iter__() sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [2, 1, 1]]
Check that 1-regular partitions works (:trac:`20584`)::
sage: P = Partitions(regular=1) sage: list(P) [[]] """
class RegularPartitions_truncated(RegularPartitions): r""" The class of `\ell`-regular partitions with max length `k`.
INPUT:
- ``ell`` -- the integer `\ell` - ``max_len`` -- integer; the maximum length
.. SEEALSO::
:class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, ell, max_len): """ Initialize ``self``.
EXAMPLES::
sage: P = Partitions(regular=4, max_length=3) sage: TestSuite(P).run() """
def max_length(self): """ Return the maximum length of the partitions of ``self``.
EXAMPLES::
sage: P = Partitions(regular=4, max_length=3) sage: P.max_length() 3 """
def __contains__(self, x): """ TESTS::
sage: P = Partitions(regular=4, max_length=3) sage: [3, 3, 3] in P True sage: [] in P True sage: [4, 2, 1, 1] in P False """
def _repr_(self): """ TESTS::
sage: from sage.combinat.partition import RegularPartitions_truncated sage: RegularPartitions_truncated(4, 3) 4-Regular Partitions with max length 3 """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: P = Partitions(regular=3, max_length=2) sage: it = P.__iter__() sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]]
Check that 1-regular partitions works (:trac:`20584`)::
sage: P = Partitions(regular=1, max_length=2) sage: list(P) [[]] """
def _fast_iterator(self, n, max_part, depth=0): """ A fast (recursive) iterator which returns a list.
EXAMPLES::
sage: P = Partitions(regular=2, max_length=2) sage: list(P._fast_iterator(5, 5)) [[5], [4, 1], [3, 2]] sage: list(P._fast_iterator(5, 3)) [[3, 2]] sage: list(P._fast_iterator(5, 6)) [[5], [4, 1], [3, 2]] """
# Special case
class RegularPartitions_bounded(RegularPartitions): r""" The class of `\ell`-regular `k`-bounded partitions.
INPUT:
- ``ell`` -- the integer `\ell` - ``k`` -- integer; the value `k`
.. SEEALSO::
:class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, ell, k): """ Initialize ``self``.
EXAMPLES::
sage: P = Partitions(regular=4, max_part=3) sage: TestSuite(P).run()
1-regular partitions::
sage: P = Partitions(regular=1, max_part=3) sage: P in FiniteEnumeratedSets() True sage: TestSuite(P).run() """
def __contains__(self, x): """ TESTS::
sage: P = Partitions(regular=4, max_part=3) sage: [3, 3, 3] in P True sage: [] in P True sage: [4, 2, 1] in P False """
def _repr_(self): """ TESTS::
sage: from sage.combinat.partition import RegularPartitions_bounded sage: RegularPartitions_bounded(4, 3) 4-Regular 3-Bounded Partitions """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: P = Partitions(regular=2, max_part=3) sage: list(P) [[3, 2, 1], [3, 2], [3, 1], [3], [2, 1], [2], [1], []]
Check that 1-regular partitions works (:trac:`20584`)::
sage: P = Partitions(regular=1, max_part=3) sage: list(P) [[]] """
class RegularPartitions_n(RegularPartitions, Partitions_n): r""" The class of `\ell`-regular partitions of `n`.
INPUT:
- ``n`` -- the integer `n` to partition - ``ell`` -- the integer `\ell`
.. SEEALSO::
:class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, n, ell): """ Initialize ``self``.
EXAMPLES::
sage: P = Partitions(5, regular=3) sage: TestSuite(P).run()
1-regular partitions::
sage: P = Partitions(5, regular=1) sage: TestSuite(P).run() """
def _repr_(self): """ TESTS::
sage: from sage.combinat.partition import RegularPartitions_n sage: RegularPartitions_n(3, 5) 5-Regular Partitions of the integer 3 """
def __contains__(self, x): """ TESTS::
sage: P = Partitions(5, regular=3) sage: [3, 1, 1] in P True sage: [3, 2, 1] in P False """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: P = Partitions(5, regular=3) sage: list(P) [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] """
def cardinality(self): """ Return the cardinality of ``self``.
EXAMPLES::
sage: P = Partitions(5, regular=3) sage: P.cardinality() 5 sage: P = Partitions(5, regular=6) sage: P.cardinality() 7 sage: P.cardinality() == Partitions(5).cardinality() True
TESTS:
Check the corner case::
sage: P = Partitions(0, regular=3) sage: P.cardinality() 1
Check for 1-regular partitions::
sage: P = Partitions(0, regular=1) sage: P.cardinality() 1 sage: P = Partitions(5, regular=1) sage: P.cardinality() 0
"""
def _an_element_(self): """ Returns a partition in ``self``.
EXAMPLES::
sage: P = Partitions(5, regular=2) sage: P._an_element_() [4, 1]
sage: P = Partitions(0, regular=1) sage: P._an_element_() []
sage: P = Partitions(5, regular=1) sage: P._an_element_() Traceback (most recent call last): ... EmptySetError """
###################### # Ordered Partitions # ######################
class OrderedPartitions(Partitions): """ The class of ordered partitions of `n`. If `k` is specified, then this contains only the ordered partitions of length `k`.
An *ordered partition* of a nonnegative integer `n` means a list of positive integers whose sum is `n`. This is the same as a composition of `n`.
.. NOTE::
It is recommended that you use :meth:`Compositions` instead as :meth:`OrderedPartitions` wraps GAP.
EXAMPLES::
sage: OrderedPartitions(3) Ordered partitions of 3 sage: OrderedPartitions(3).list() [[3], [2, 1], [1, 2], [1, 1, 1]] sage: OrderedPartitions(3,2) Ordered partitions of 3 of length 2 sage: OrderedPartitions(3,2).list() [[2, 1], [1, 2]]
sage: OrderedPartitions(10,k=2).list() [[9, 1], [8, 2], [7, 3], [6, 4], [5, 5], [4, 6], [3, 7], [2, 8], [1, 9]] sage: OrderedPartitions(4).list() [[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]]
"""
@staticmethod def __classcall_private__(cls, n, k=None): """ Normalize the input to ensure a unique representation.
TESTS::
sage: P = OrderedPartitions(3,2) sage: P2 = OrderedPartitions(3,2) sage: P is P2 True """
def __init__(self, n, k): """ Initialize ``self``.
EXAMPLES::
sage: o = OrderedPartitions(4,2)
TESTS::
sage: TestSuite( OrderedPartitions(5,3) ).run() """
def __contains__(self, x): """ Check to see if ``x`` is an element of ``self``.
EXAMPLES::
sage: o = OrderedPartitions(4,2) sage: [2,1] in o False sage: [2,2] in o True sage: [1,2,1] in o False """
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: OrderedPartitions(3) # indirect doctest Ordered partitions of 3 sage: OrderedPartitions(3,2) # indirect doctest Ordered partitions of 3 of length 2 """
def list(self): """ Return a list of partitions in ``self``.
EXAMPLES::
sage: OrderedPartitions(3).list() [[3], [2, 1], [1, 2], [1, 1, 1]] sage: OrderedPartitions(3,2).list() [[2, 1], [1, 2]] """ else:
def cardinality(self): """ Return the cardinality of ``self``.
EXAMPLES::
sage: OrderedPartitions(3).cardinality() 4 sage: OrderedPartitions(3,2).cardinality() 2 sage: OrderedPartitions(10,2).cardinality() 9 sage: OrderedPartitions(15).cardinality() 16384 """ else:
########################## # Partitions Greatest LE # ##########################
class PartitionsGreatestLE(UniqueRepresentation, IntegerListsLex): """ The class of all (unordered) "restricted" partitions of the integer `n` having parts less than or equal to the integer `k`.
EXAMPLES::
sage: PartitionsGreatestLE(10,2) Partitions of 10 having parts less than or equal to 2 sage: PartitionsGreatestLE(10,2).list() [[2, 2, 2, 2, 2], [2, 2, 2, 2, 1, 1], [2, 2, 2, 1, 1, 1, 1], [2, 2, 1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
sage: [4,3,2,1] in PartitionsGreatestLE(10,2) False sage: [2,2,2,2,2] in PartitionsGreatestLE(10,2) True sage: PartitionsGreatestLE(10,2).first().parent() Partitions... """
def __init__(self, n, k): """ Initialize ``self``.
TESTS::
sage: p = PartitionsGreatestLE(10,2) sage: p.n, p.k (10, 2) sage: TestSuite(p).run() """
def _repr_(self): """ Return a string representation of ``self``.
TESTS::
sage: PartitionsGreatestLE(10, 2) # indirect doctest Partitions of 10 having parts less than or equal to 2 """
Element = Partition options = Partitions.options
########################## # Partitions Greatest EQ # ##########################
class PartitionsGreatestEQ(UniqueRepresentation, IntegerListsLex): """ The class of all (unordered) "restricted" partitions of the integer `n` having its greatest part equal to the integer `k`.
EXAMPLES::
sage: PartitionsGreatestEQ(10,2) Partitions of 10 having greatest part equal to 2 sage: PartitionsGreatestEQ(10,2).list() [[2, 2, 2, 2, 2], [2, 2, 2, 2, 1, 1], [2, 2, 2, 1, 1, 1, 1], [2, 2, 1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1, 1, 1, 1, 1]]
sage: [4,3,2,1] in PartitionsGreatestEQ(10,2) False sage: [2,2,2,2,2] in PartitionsGreatestEQ(10,2) True sage: [1]*10 in PartitionsGreatestEQ(10,2) False
sage: PartitionsGreatestEQ(10,2).first().parent() Partitions... """
def __init__(self, n, k): """ Initialize ``self``.
TESTS::
sage: p = PartitionsGreatestEQ(10,2) sage: p.n, p.k (10, 2) sage: TestSuite(p).run() """
def _repr_(self): """ Return a string representation of ``self``.
TESTS::
sage: PartitionsGreatestEQ(10,2) # indirect doctest Partitions of 10 having greatest part equal to 2 """
Element = Partition options = Partitions.options
######################### # Restricted Partitions # #########################
def RestrictedPartitions(n, S, k=None): r""" This function has been deprecated and will be removed in a future version of Sage; use :class:`Partitions` with the ``parts_in`` keyword. Note, however, that the current implementation of :class:`Partitions` does not allow the ``parts_in`` keyword to be combined with keywords such as ``max_length``; see :trac:`13072` and :trac:`12278` for more details. This class should not be removed until this problem has been fixed.
Original docstring follows.
A restricted partition is, like an ordinary partition, an unordered sum `n = p_1+p_2+\ldots+p_k` of positive integers and is represented by the list `p = [p_1,p_2,\ldots,p_k]`, in nonincreasing order. The difference is that here the `p_i` must be elements from the set `S`, while for ordinary partitions they may be elements from `[1..n]`.
Returns the list of all restricted partitions of the positive integer n into sums with `k` summands with the summands of the partition coming from the set `S`. If `k` is not given all restricted partitions for all `k` are returned.
Wraps GAP's ``RestrictedPartitions``.
EXAMPLES::
sage: from sage.combinat.partition import RestrictedPartitions sage: RestrictedPartitions(5,[3,2,1]) doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. doctest:...: DeprecationWarning: RestrictedPartitions_nsk is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. Partitions of 5 restricted to the values [1, 2, 3] sage: RestrictedPartitions(5,[3,2,1]).list() [[3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] sage: RestrictedPartitions(5,[3,2,1],4) Partitions of 5 restricted to the values [1, 2, 3] of length 4 sage: RestrictedPartitions(5,[3,2,1],4).list() [[2, 1, 1, 1]] """
class RestrictedPartitions_nsk(CombinatorialClass): r""" We are deprecating :meth:`RestrictedPartitions`, so this class should be deprecated too. See :trac:`13072`. """ def __init__(self, n, S, k=None): """ Initialize ``self``.
TESTS::
sage: from sage.combinat.partition import RestrictedPartitions sage: r = RestrictedPartitions(5,[3,2,1]) doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. sage: r == loads(dumps(r)) True """
Element = Partition options = Partitions.options
def __contains__(self, x): """ Check to see if ``x`` is in ``self``.
EXAMPLES::
sage: from sage.combinat.partition import RestrictedPartitions sage: [4,1] in RestrictedPartitions(5,[3,2,1]) doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. False sage: [3,2] in RestrictedPartitions(5,[3,2,1]) True sage: [3,2] in RestrictedPartitions(5,[3,2,1],4) False sage: [2,1,1,1] in RestrictedPartitions(5,[3,2,1],4) True """ and (self.k is None or len(x) == self.k)
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: from sage.combinat.partition import RestrictedPartitions sage: RestrictedPartitions(5,[3,2,1]).__repr__() doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. 'Partitions of 5 restricted to the values [1, 2, 3]' """
def list(self): r""" Returns the list of all restricted partitions of the positive integer `n` into sums with `k` summands with the summands of the partition coming from the set `S`. If `k` is not given all restricted partitions for all `k` are returned.
Wraps GAP's RestrictedPartitions.
EXAMPLES::
sage: from sage.combinat.partition import RestrictedPartitions sage: RestrictedPartitions(8,[1,3,5,7]).list() doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. [[7, 1], [5, 3], [5, 1, 1, 1], [3, 3, 1, 1], [3, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]] sage: RestrictedPartitions(8,[1,3,5,7],2).list() [[7, 1], [5, 3]] """ else:
def cardinality(self): """ Returns the size of ``self``.
Wraps GAP's NrRestrictedPartitions.
EXAMPLES::
sage: from sage.combinat.partition import RestrictedPartitions sage: RestrictedPartitions(8,[1,3,5,7]).cardinality() doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. 6 sage: RestrictedPartitions(8,[1,3,5,7],2).cardinality() 2 """ else:
#########################################################################
#### partitions
def number_of_partitions(n, algorithm='default'): r""" Returns the number of partitions of `n` with, optionally, at most `k` parts.
The options of :meth:`number_of_partitions()` are being deprecated :trac:`13072` in favour of :meth:`Partitions_n.cardinality()` so that :meth:`number_of_partitions()` can become a stripped down version of the fastest algorithm available (currently this is using FLINT).
INPUT:
- ``n`` -- an integer
- ``algorithm`` -- (default: 'default') [Will be deprecated except in Partition().cardinality() ]
- ``'default'`` -- If ``k`` is not ``None``, then use Gap (very slow). If ``k`` is ``None``, use FLINT.
- ``'flint'`` -- use FLINT
- ``'bober'`` -- use Jonathan Bober's implementation
EXAMPLES::
sage: v = Partitions(5).list(); v [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] sage: len(v) 7 sage: number_of_partitions(5, algorithm='bober') 7
The input must be a nonnegative integer or a ``ValueError`` is raised.
::
sage: number_of_partitions(-5) Traceback (most recent call last): ... ValueError: n (=-5) must be a nonnegative integer
::
sage: number_of_partitions(10) 42 sage: number_of_partitions(3) 3 sage: number_of_partitions(10) 42 sage: number_of_partitions(40) 37338 sage: number_of_partitions(100) 190569292 sage: number_of_partitions(100000) 27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
A generating function for the number of partitions `p_n` is given by the reciprocal of Euler's function:
.. MATH::
\sum_{n=0}^{\infty} p_n x^n = \prod_{k=1}^{\infty} \left( \frac{1}{1-x^k} \right).
We use Sage to verify that the first several coefficients do instead agree::
sage: q = PowerSeriesRing(QQ, 'q', default_prec=9).gen() sage: prod([(1-q^k)^(-1) for k in range(1,9)]) ## partial product of 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + 15*q^7 + 22*q^8 + O(q^9) sage: [number_of_partitions(k) for k in range(2,10)] [2, 3, 5, 7, 11, 15, 22, 30]
REFERENCES:
- :wikipedia:`Partition\_(number\_theory)`
TESTS::
sage: n = 500 + randint(0,500) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1500 + randint(0,1500) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 1000000 + randint(0,1000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 True sage: n = 100000000 + randint(0,100000000) sage: number_of_partitions( n - (n % 385) + 369) % 385 == 0 # long time (4s on sage.math, 2011) True
""" return ZZ.one()
raise ValueError("unknown algorithm '%s'"%algorithm)
def number_of_partitions_length(n, k, algorithm='hybrid'): r""" Return the number of partitions of `n` with length `k`.
This is a wrapper for GAP's ``NrPartitions`` function.
EXAMPLES::
sage: from sage.combinat.partition import number_of_partitions_length sage: number_of_partitions_length(5, 2) 2 sage: number_of_partitions_length(10, 2) 5 sage: number_of_partitions_length(10, 4) 9 sage: number_of_partitions_length(10, 0) 0 sage: number_of_partitions_length(10, 1) 1 sage: number_of_partitions_length(0, 0) 1 sage: number_of_partitions_length(0, 1) 0 """ # Do the hybrid algorithm
# Special relations between n and k
# Special case of n # Note: we've already checked the case when n == k == 0 return ZZ.zero()
# Small values of k
# We have one column of length `k` and all (inner) partitions of # size `n-k` can't have length more than `k`
# Fall back to GAP
########## # trac 14225: Partitions() is frequently used, but only weakly cached. Hence, # establish a strong reference to it.
_Partitions = Partitions()
# Rather than caching an under-used function I have cached the default # number_of_partitions functions which is currently using FLINT. # AM trac #13072 cached_number_of_partitions = cached_function( flint_number_of_partitions )
# October 2012: fixing outdated pickles which use classes being deprecated from sage.structure.sage_object import register_unpickle_override from sage.combinat.partition_tuple import PartitionTuples_level_size register_unpickle_override('sage.combinat.partition', 'PartitionTuples_nk', PartitionTuples_level_size) register_unpickle_override('sage.combinat.partition', 'Partition_class', Partition) register_unpickle_override('sage.combinat.partition', 'OrderedPartitions_nk', OrderedPartitions) register_unpickle_override('sage.combinat.partition', 'PartitionsInBox_hw', PartitionsInBox) register_unpickle_override('sage.combinat.partition', 'PartitionsGreatestLE_nk', PartitionsGreatestLE) register_unpickle_override('sage.combinat.partition', 'PartitionsGreatestEQ_nk', PartitionsGreatestEQ)
# Deprecations from trac:18555. July 2016 from sage.misc.superseded import deprecated_function_alias Partitions.global_options=deprecated_function_alias(18555, Partitions.options) PartitionOptions = deprecated_function_alias(18555, Partitions.options) Partitions_with_constraints.global_options = deprecated_function_alias(18555, Partitions_with_constraints.options) PartitionsGreatestLE.global_options = deprecated_function_alias(18555, PartitionsGreatestLE.options) PartitionsGreatestEQ.global_options = deprecated_function_alias(18555, PartitionsGreatestEQ.options) RestrictedPartitions_nsk.global_options = deprecated_function_alias(18555, RestrictedPartitions_nsk.options) |