diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index a512a7735c1..fe3d21efb12 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -3,12 +3,13 @@ AUTHORS: - * Mike Hanson (2007) - original module - * Nathann Cohen, David Joyner (2009-2010) - Gale-Ryser stuff - * Nathann Cohen, David Joyner (2011) - Gale-Ryser bugfix - * Travis Scrimshaw (2012-05-12) - Updated doc-strings to tell the user of - that the class's name is a misnomer (that they only contains non-negative - entries). +* Mike Hanson (2007) - original module +* Nathann Cohen, David Joyner (2009-2010) - Gale-Ryser stuff +* Nathann Cohen, David Joyner (2011) - Gale-Ryser bugfix +* Travis Scrimshaw (2012-05-12) - Updated doc-strings to tell the user of + that the class's name is a misnomer (that they only contains non-negative + entries). +* Travis Scrimshaw (2013-02-04) - Refactored to use ``ClonableIntArray`` """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -26,16 +27,23 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from combinat import CombinatorialClass -from __builtin__ import list as builtinlist -from sage.rings.integer import Integer -from sage.rings.arith import binomial import misc -from sage.rings.infinity import PlusInfinity import integer_list import cartesian_product import functools +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import ClonableIntArray +from sage.misc.classcall_metaclass import ClasscallMetaclass + +from sage.categories.enumerated_sets import EnumeratedSets +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.rings.infinity import PlusInfinity, MinusInfinity +from sage.rings.arith import binomial +from sage.rings.all import NN, ZZ +from sage.rings.integer import Integer def is_gale_ryser(r,s): r""" @@ -71,11 +79,11 @@ def is_gale_ryser(r,s): which does not alter the columns (resp. rows) sums. We can then assume that `r` and `s` are partitions - (see the corresponding class ``Partition``) + (see the corresponding class :class:`Partition`) If `r^*` denote the conjugate of `r`, the Gale-Ryser theorem asserts that a binary Matrix satisfying the constraints exists - if and only if `s\preceq r^*`, where `\preceq` denotes + if and only if `s \preceq r^*`, where `\preceq` denotes the domination order on partitions. EXAMPLES:: @@ -110,7 +118,7 @@ def is_gale_ryser(r,s): rstar = Partition(r2).conjugate() # same number of 1s domination - return len(rstar) <= len(s2) and sum(r2) == sum(s2) and rstar.dominates(s) + return len(rstar) <= len(s2) and sum(r2) == sum(s2) and rstar.dominates(s) def _slider01(A, t, k, p1, p2, fixedcols=[]): r""" @@ -119,21 +127,23 @@ def _slider01(A, t, k, p1, p2, fixedcols=[]): returns a matrix `B` which is the same as `A` except that it has slid `t` of the `1` in each of these rows of `A` over towards the `k`-th column. Care must be taken when the - last leading 1 is in column >=k. It avoids those in columns + last leading 1 is in column `\geq k`. It avoids those in columns listed in fixedcols. - This is a 'private' function for use in gale_ryser_theorem. + This is a 'private' function for use in :func:`gale_ryser_theorem`. INPUT: - - ``A`` -- an `m\times n` (0,1) matrix + - ``A`` -- an `m \times n` `(0,1)`-matrix + - ``t``, ``k`` -- integers satisfying `0 < t < m`, `0 < k < n` + - ``fixedcols`` -- those columns (if any) whose entries - aren't permitted to slide + aren't permitted to slide OUTPUT: - An `m\times n` (0,1) matrix, which is the same as `A` except + An `m \times n` `(0,1)`-matrix, which is the same as `A` except that it has exactly one `1` in `A` slid over to the `k`-th column. @@ -193,241 +203,240 @@ def _slider01(A, t, k, p1, p2, fixedcols=[]): return matrix(B) def gale_ryser_theorem(p1, p2, algorithm="gale"): - r""" - Returns the binary matrix given by the Gale-Ryser theorem. - - The Gale Ryser theorem asserts that if `p_1,p_2` are two - partitions of `n` of respective lengths `k_1,k_2`, then there is - a binary `k_1\times k_2` matrix `M` such that `p_1` is the vector - of row sums and `p_2` is the vector of column sums of `M`, if - and only if the conjugate of `p_2` dominates `p_1`. - - INPUT: - - - ``p1, p2``-- list of integers representing the vectors - of row/column sums + r""" + Returns the binary matrix given by the Gale-Ryser theorem. - - ``algorithm`` -- two possible string values : + The Gale Ryser theorem asserts that if `p_1,p_2` are two + partitions of `n` of respective lengths `k_1,k_2`, then there is + a binary `k_1\times k_2` matrix `M` such that `p_1` is the vector + of row sums and `p_2` is the vector of column sums of `M`, if + and only if the conjugate of `p_2` dominates `p_1`. - - ``"ryser"`` implements the construction due - to Ryser [Ryser63]_. + INPUT: - - ``"gale"`` (default) implements the construction due to Gale [Gale57]_. + - ``p1, p2``-- list of integers representing the vectors + of row/column sums - OUTPUT: + - ``algorithm`` -- two possible string values: - - A binary matrix if it exists, ``None`` otherwise. + - ``'ryser'`` implements the construction due + to Ryser [Ryser63]_. + - ``'gale'`` (default) implements the construction due to Gale [Gale57]_. - Gale's Algorithm: + OUTPUT: - (Gale [Gale57]_): A matrix satisfying the constraints of its - sums can be defined as the solution of the following - Linear Program, which Sage knows how to solve. + A binary matrix if it exists, ``None`` otherwise. - .. MATH:: + Gale's Algorithm: - \forall i&\sum_{j=1}^{k_2} b_{i,j}=p_{1,j}\\ - \forall i&\sum_{j=1}^{k_1} b_{j,i}=p_{2,j}\\ - &b_{i,j}\mbox{ is a binary variable} + (Gale [Gale57]_): A matrix satisfying the constraints of its + sums can be defined as the solution of the following + Linear Program, which Sage knows how to solve. - Ryser's Algorithm: + .. MATH:: - (Ryser [Ryser63]_): The construction of an `m\times n` matrix `A=A_{r,s}`, - due to Ryser, is described as follows. The - construction works if and only if have `s\preceq r^*`. + \forall i&\sum_{j=1}^{k_2} b_{i,j}=p_{1,j}\\ + \forall i&\sum_{j=1}^{k_1} b_{j,i}=p_{2,j}\\ + &b_{i,j}\mbox{ is a binary variable} - * Construct the `m\times n` matrix `B` from `r` by defining - the `i`-th row of `B` to be the vector whose first `r_i` - entries are `1`, and the remainder are 0's, `1\leq i\leq - m`. This maximal matrix `B` with row sum `r` and ones left - justified has column sum `r^{*}`. + Ryser's Algorithm: - * Shift the last `1` in certain rows of `B` to column `n` in - order to achieve the sum `s_n`. Call this `B` again. + (Ryser [Ryser63]_): The construction of an `m\times n` matrix `A=A_{r,s}`, + due to Ryser, is described as follows. The + construction works if and only if have `s\preceq r^*`. - * The `1`'s in column n are to appear in those - rows in which `A` has the largest row sums, giving - preference to the bottom-most positions in case of ties. - * Note: When this step automatically "fixes" other columns, - one must skip ahead to the first column index - with a wrong sum in the step below. + * Construct the `m\times n` matrix `B` from `r` by defining + the `i`-th row of `B` to be the vector whose first `r_i` + entries are `1`, and the remainder are 0's, `1\leq i\leq + m`. This maximal matrix `B` with row sum `r` and ones left + justified has column sum `r^{*}`. - * Proceed inductively to construct columns `n-1`, ..., `2`, `1`. + * Shift the last `1` in certain rows of `B` to column `n` in + order to achieve the sum `s_n`. Call this `B` again. - * Set `A = B`. Return `A`. + * The `1`'s in column n are to appear in those + rows in which `A` has the largest row sums, giving + preference to the bottom-most positions in case of ties. + * Note: When this step automatically "fixes" other columns, + one must skip ahead to the first column index + with a wrong sum in the step below. - EXAMPLES: + * Proceed inductively to construct columns `n-1`, ..., `2`, `1`. - Computing the matrix for `p_1=p_2=2+2+1` :: + * Set `A = B`. Return `A`. - sage: from sage.combinat.integer_vector import gale_ryser_theorem - sage: p1 = [2,2,1] - sage: p2 = [2,2,1] - sage: print gale_ryser_theorem(p1, p2) # not tested - [1 1 0] - [1 0 1] - [0 1 0] - sage: A = gale_ryser_theorem(p1, p2) - sage: rs = [sum(x) for x in A.rows()] - sage: cs = [sum(x) for x in A.columns()] - sage: p1 == rs; p2 == cs - True - True + EXAMPLES: - Or for a non-square matrix with `p_1=3+3+2+1` and `p_2=3+2+2+1+1`, using Ryser's algorithm :: - - sage: from sage.combinat.integer_vector import gale_ryser_theorem - sage: p1 = [3,3,1,1] - sage: p2 = [3,3,1,1] - sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") - [1 1 0 1] - [1 1 1 0] - [0 1 0 0] - [1 0 0 0] - sage: p1 = [4,2,2] - sage: p2 = [3,3,1,1] - sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") - [1 1 1 1] - [1 1 0 0] - [1 1 0 0] - sage: p1 = [4,2,2,0] - sage: p2 = [3,3,1,1,0,0] - sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") - [1 1 1 1 0 0] - [1 1 0 0 0 0] - [1 1 0 0 0 0] - [0 0 0 0 0 0] - sage: p1 = [3,3,2,1] - sage: p2 = [3,2,2,1,1] - sage: print gale_ryser_theorem(p1, p2, algorithm="gale") # not tested - [1 1 1 0 0] - [1 1 0 0 1] - [1 0 1 0 0] - [0 0 0 1 0] - - With `0` in the sequences, and with unordered inputs :: - - sage: from sage.combinat.integer_vector import gale_ryser_theorem - sage: gale_ryser_theorem([3,3,0,1,1,0], [3,1,3,1,0], algorithm = "ryser") - [1 0 1 1 0] - [1 1 1 0 0] - [0 0 0 0 0] - [0 0 1 0 0] - [1 0 0 0 0] - [0 0 0 0 0] - sage: p1 = [3,1,1,1,1]; p2 = [3,2,2,0] - sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") - [1 1 1 0] - [0 0 1 0] - [0 1 0 0] - [1 0 0 0] - [1 0 0 0] - - TESTS: - - This test created a random bipartite graph on `n+m` vertices. Its - adjacency matrix is binary, and it is used to create some - "random-looking" sequences which correspond to an existing matrix. The - ``gale_ryser_theorem`` is then called on these sequences, and the output - checked for correction.:: - - sage: def test_algorithm(algorithm, low = 10, high = 50): - ... n,m = randint(low,high), randint(low,high) - ... g = graphs.RandomBipartite(n, m, .3) - ... s1 = sorted(g.degree([(0,i) for i in range(n)]), reverse = True) - ... s2 = sorted(g.degree([(1,i) for i in range(m)]), reverse = True) - ... m = gale_ryser_theorem(s1, s2, algorithm = algorithm) - ... ss1 = sorted(map(lambda x : sum(x) , m.rows()), reverse = True) - ... ss2 = sorted(map(lambda x : sum(x) , m.columns()), reverse = True) - ... if ((ss1 == s1) and (ss2 == s2)): - ... return True - ... return False - - sage: for algorithm in ["gale", "ryser"]: # long time - ... for i in range(50): # long time - ... if not test_algorithm(algorithm, 3, 10): # long time - ... print "Something wrong with algorithm ", algorithm # long time - ... break # long time - - Null matrix:: - - sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="gale") - [0 0 0 0] - [0 0 0 0] - [0 0 0 0] - sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="ryser") - [0 0 0 0] - [0 0 0 0] - [0 0 0 0] - - REFERENCES: - - .. [Ryser63] H. J. Ryser, Combinatorial Mathematics, - Carus Monographs, MAA, 1963. - .. [Gale57] D. Gale, A theorem on flows in networks, Pacific J. Math. - 7(1957)1073-1082. - """ - from sage.combinat.partition import Partition - from sage.matrix.constructor import matrix - - if not(is_gale_ryser(p1,p2)): - return False + Computing the matrix for `p_1=p_2=2+2+1`:: - if algorithm=="ryser": # ryser's algorithm - from sage.combinat.permutation import Permutation + sage: from sage.combinat.integer_vector import gale_ryser_theorem + sage: p1 = [2,2,1] + sage: p2 = [2,2,1] + sage: print gale_ryser_theorem(p1, p2) # not tested + [1 1 0] + [1 0 1] + [0 1 0] + sage: A = gale_ryser_theorem(p1, p2) + sage: rs = [sum(x) for x in A.rows()] + sage: cs = [sum(x) for x in A.columns()] + sage: p1 == rs; p2 == cs + True + True - # Sorts the sequences if they are not, and remembers the permutation - # applied - tmp = sorted(enumerate(p1), reverse=True, key=lambda x:x[1]) - r = [x[1] for x in tmp if x[1]>0] - r_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] - m = len(r) + Or for a non-square matrix with `p_1=3+3+2+1` and `p_2=3+2+2+1+1`, + using Ryser's algorithm:: - tmp = sorted(enumerate(p2), reverse=True, key=lambda x:x[1]) - s = [x[1] for x in tmp if x[1]>0] - s_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] - n = len(s) + sage: from sage.combinat.integer_vector import gale_ryser_theorem + sage: p1 = [3,3,1,1] + sage: p2 = [3,3,1,1] + sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") + [1 1 0 1] + [1 1 1 0] + [0 1 0 0] + [1 0 0 0] + sage: p1 = [4,2,2] + sage: p2 = [3,3,1,1] + sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") + [1 1 1 1] + [1 1 0 0] + [1 1 0 0] + sage: p1 = [4,2,2,0] + sage: p2 = [3,3,1,1,0,0] + sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") + [1 1 1 1 0 0] + [1 1 0 0 0 0] + [1 1 0 0 0 0] + [0 0 0 0 0 0] + sage: p1 = [3,3,2,1] + sage: p2 = [3,2,2,1,1] + sage: print gale_ryser_theorem(p1, p2, algorithm="gale") # not tested + [1 1 1 0 0] + [1 1 0 0 1] + [1 0 1 0 0] + [0 0 0 1 0] + + With `0` in the sequences, and with unordered inputs:: + + sage: from sage.combinat.integer_vector import gale_ryser_theorem + sage: gale_ryser_theorem([3,3,0,1,1,0], [3,1,3,1,0], algorithm = "ryser") + [1 0 1 1 0] + [1 1 1 0 0] + [0 0 0 0 0] + [0 0 1 0 0] + [1 0 0 0 0] + [0 0 0 0 0] + sage: p1 = [3,1,1,1,1]; p2 = [3,2,2,0] + sage: gale_ryser_theorem(p1, p2, algorithm = "ryser") + [1 1 1 0] + [0 0 1 0] + [0 1 0 0] + [1 0 0 0] + [1 0 0 0] - A0 = matrix([[1]*r[j]+[0]*(n-r[j]) for j in range(m)]) + TESTS: + + This test created a random bipartite graph on `n+m` vertices. Its + adjacency matrix is binary, and it is used to create some + "random-looking" sequences which correspond to an existing matrix. The + ``gale_ryser_theorem`` is then called on these sequences, and the output + checked for correction.:: + + sage: def test_algorithm(algorithm, low = 10, high = 50): + ....: n,m = randint(low,high), randint(low,high) + ....: g = graphs.RandomBipartite(n, m, .3) + ....: s1 = sorted(g.degree([(0,i) for i in range(n)]), reverse = True) + ....: s2 = sorted(g.degree([(1,i) for i in range(m)]), reverse = True) + ....: m = gale_ryser_theorem(s1, s2, algorithm = algorithm) + ....: ss1 = sorted(map(lambda x : sum(x) , m.rows()), reverse = True) + ....: ss2 = sorted(map(lambda x : sum(x) , m.columns()), reverse = True) + ....: if ((ss1 == s1) and (ss2 == s2)): + ....: return True + ....: return False + + sage: for algorithm in ["gale", "ryser"]: # long time + ....: for i in range(50): # long time + ....: if not test_algorithm(algorithm, 3, 10): # long time + ....: print "Something wrong with algorithm ", algorithm # long time + ....: break # long time + + Null matrix:: + + sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="gale") + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="ryser") + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + + REFERENCES: + + .. [Ryser63] H. J. Ryser, Combinatorial Mathematics, + Carus Monographs, MAA, 1963. + .. [Gale57] D. Gale, A theorem on flows in networks, Pacific J. Math. + 7(1957)1073-1082. + """ + from sage.combinat.partition import Partition + from sage.matrix.constructor import matrix - for k in range(1,n+1): - goodcols = [i for i in range(n) if s[i]==sum(A0.column(i))] - if sum(A0.column(n-k))<>s[n-k]: - A0 = _slider01(A0,s[n-k],n-k, p1, p2, goodcols) - - # If we need to add empty rows/columns - if len(p1)!=m: - A0 = A0.stack(matrix([[0]*n]*(len(p1)-m))) - - if len(p2)!=n: - A0 = A0.transpose().stack(matrix([[0]*len(p1)]*(len(p2)-n))).transpose() - - # Applying the permutations to get a matrix satisfying the - # order given by the input - A0 = A0.matrix_from_rows_and_columns(r_permutation, s_permutation) - return A0 - - elif algorithm == "gale": - from sage.numerical.mip import MixedIntegerLinearProgram - k1, k2=len(p1), len(p2) - p = MixedIntegerLinearProgram() - b = p.new_variable(dim=2) - for (i,c) in enumerate(p1): - p.add_constraint(p.sum([b[i][j] for j in xrange(k2)]),min=c,max=c) - for (i,c) in enumerate(p2): - p.add_constraint(p.sum([b[j][i] for j in xrange(k1)]),min=c,max=c) - p.set_objective(None) - p.set_binary(b) - p.solve() - b = p.get_values(b) - M = [[0]*k2 for i in xrange(k1)] - for i in xrange(k1): - for j in xrange(k2): - M[i][j] = int(b[i][j]) - return matrix(M) + if not(is_gale_ryser(p1,p2)): + return False - else: - raise ValueError("The only two algorithms available are \"gale\" and \"ryser\"") + if algorithm=="ryser": # ryser's algorithm + from sage.combinat.permutation import Permutation + + # Sorts the sequences if they are not, and remembers the permutation + # applied + tmp = sorted(enumerate(p1), reverse=True, key=lambda x:x[1]) + r = [x[1] for x in tmp if x[1]>0] + r_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] + m = len(r) + + tmp = sorted(enumerate(p2), reverse=True, key=lambda x:x[1]) + s = [x[1] for x in tmp if x[1]>0] + s_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] + n = len(s) + + A0 = matrix([[1]*r[j]+[0]*(n-r[j]) for j in range(m)]) + + for k in range(1,n+1): + goodcols = [i for i in range(n) if s[i]==sum(A0.column(i))] + if sum(A0.column(n-k))<>s[n-k]: + A0 = _slider01(A0,s[n-k],n-k, p1, p2, goodcols) + + # If we need to add empty rows/columns + if len(p1) != m: + A0 = A0.stack(matrix([[0]*n]*(len(p1)-m))) + + if len(p2) != n: + A0 = A0.transpose().stack(matrix([[0]*len(p1)]*(len(p2)-n))).transpose() + + # Applying the permutations to get a matrix satisfying the + # order given by the input + A0 = A0.matrix_from_rows_and_columns(r_permutation, s_permutation) + return A0 + + if algorithm == "gale": + from sage.numerical.mip import MixedIntegerLinearProgram + k1, k2=len(p1), len(p2) + p = MixedIntegerLinearProgram() + b = p.new_variable(dim=2) + for (i,c) in enumerate(p1): + p.add_constraint(p.sum([b[i][j] for j in xrange(k2)]),min=c,max=c) + for (i,c) in enumerate(p2): + p.add_constraint(p.sum([b[j][i] for j in xrange(k1)]),min=c,max=c) + p.set_objective(None) + p.set_binary(b) + p.solve() + b = p.get_values(b) + M = [[0]*k2 for i in xrange(k1)] + for i in xrange(k1): + for j in xrange(k2): + M[i][j] = int(b[i][j]) + return matrix(M) + + raise ValueError("The only two algorithms available are \"gale\" and \"ryser\"") def _default_function(l, default, i): """ @@ -454,12 +463,11 @@ def _default_function(l, default, i): except IndexError: return default -infinity = PlusInfinity() def list2func(l, default=None): """ - Given a list l, return a function that takes in a value i and - return l[i-1]. If default is not None, then the function will - return the default value for out of range i's. + Given a list ``l``, return a function that takes in a value ``i`` and + return ``l[i-1]``. If default is not ``None``, then the function will + return the default value for out of range ``i``'s. EXAMPLES:: @@ -488,31 +496,53 @@ def list2func(l, default=None): else: return functools.partial(_default_function, l, default) - def constant_func(i): """ - Returns the constant function i. + Return the constant function ``i``. EXAMPLES:: sage: f = sage.combinat.integer_vector.constant_func(3) + doctest:...: DeprecationWarning: constant_func is deprecated. Use lambda x: i instead + See http://trac.sagemath.org/12453 for details. sage: f(-1) 3 sage: f('asf') 3 """ + from sage.misc.superseded import deprecation + deprecation(12453, 'constant_func is deprecated. Use lambda x: i instead') return lambda x: i -def IntegerVectors(n=None, k=None, **kwargs): +class IntegerVector(ClonableIntArray): + """ + An integer vector. """ - Returns the combinatorial class of (non-negative) integer vectors. + def check(self): + """ + Check to make sure this is a valid integer vector by making sure + all entries are non-negative. - NOTE - These integer vectors are non-negative. + EXAMPLES:: - EXAMPLES: If n is not specified, it returns the class of all - integer vectors. + sage: IV = IntegerVectors() + sage: elt = IV([1,2,1]) + sage: elt.check() + """ + if any(x < 0 for x in self): + raise ValueError("All entries must be non-negative") - :: +class IntegerVectors(Parent): + """ + Returns the class of (non-negative) integer vectors. + + .. NOTE:: + + The entries are non-negative integers. + + EXAMPLES: + + If `n` is not specified, it returns the class of all integer vectors:: sage: IntegerVectors() Integer vectors @@ -523,33 +553,25 @@ def IntegerVectors(n=None, k=None, **kwargs): sage: [1, 0, 0] in IntegerVectors() True - Entries are non-negative. - - :: + Entries are non-negative:: sage: [-1, 2] in IntegerVectors() False - If n is specified, then it returns the class of all integer vectors - which sum to n. - - :: + If `n` is specified, then it returns the class of all integer vectors + which sum to `n`:: sage: IV3 = IntegerVectors(3); IV3 Integer vectors that sum to 3 - Note that trailing zeros are ignored so that [3, 0] does not show - up in the following list (since [3] does) - - :: + Note that trailing zeros are ignored so that ``[3, 0]`` does not show + up in the following list (since ``[3]`` does):: sage: IntegerVectors(3, max_length=2).list() [[3], [2, 1], [1, 2], [0, 3]] - If n and k are both specified, then it returns the class of integer - vectors that sum to n and are of length k. - - :: + If `n` and `k` are both specified, then it returns the class of integer + vectors that sum to `n` and are of length `k`:: sage: IV53 = IntegerVectors(5,3); IV53 Integer vectors of length 3 that sum to 5 @@ -562,29 +584,69 @@ def IntegerVectors(n=None, k=None, **kwargs): sage: IV53.random_element() [4, 0, 1] """ - if n is None: - return IntegerVectors_all() - elif k is None: - return IntegerVectors_nconstraints(n,kwargs) - else: - if isinstance(k, builtinlist): - return IntegerVectors_nnondescents(n,k) - else: - if len(kwargs) == 0: - return IntegerVectors_nk(n,k) - else: - return IntegerVectors_nkconstraints(n,k,kwargs) + __metaclass__ = ClasscallMetaclass + + @staticmethod + def __classcall_private__(cls, n=None, k=None, **kwargs): + """ + Choose the correct parent based upon input. + + EXAMPLES:: + + sage: IV1 = IntegerVectors(3, 2) + sage: IV2 = IntegerVectors(3, 2) + sage: IV1 is IV2 + True + """ + if len(kwargs) != 0: + return IntegerVectorsConstraints(n, k, **kwargs) + + if k is None: + if n is None: + return IntegerVectors_all() + return IntegerVectors_n(n) + if n is None: + return IntegerVectors_k(k) + + try: + return IntegerVectors_nnondescents(n, tuple(k)) + except TypeError: + pass + return IntegerVectors_nk(n, k) -class IntegerVectors_all(CombinatorialClass): - def __repr__(self): + def __init__(self, category=None): """ + Initialize ``self``. + EXAMPLES:: - sage: IntegerVectors() + sage: IV = IntegerVectors() + sage: TestSuite(IV).run() + """ + if category is None: + category = EnumeratedSets() + Parent.__init__(self, category=category) + + def _element_constructor_(self, lst): + """ + Construct an element of ``self`` from ``lst``. + + EXAMPLES:: + + sage: IV = IntegerVectors() + sage: elt = IV([3, 1, 0, 3, 2]); elt + [3, 1, 0, 3, 2] + sage: elt.parent() Integer vectors """ - return "Integer vectors" + if isinstance(lst, IntegerVector): + if lst.parent() is self: + return lst + lst = list(lst) + return self.element_class(self, lst) + + Element = IntegerVector def __contains__(self, x): """ @@ -595,68 +657,228 @@ def __contains__(self, x): sage: [3,2,2,1] in IntegerVectors() True """ - if not isinstance(x, builtinlist): + if isinstance(x, IntegerVector): + return True + + if not isinstance(x, (list, tuple)): return False + for i in x: - if not isinstance(i, (int, Integer)): + if i not in ZZ: return False if i < 0: return False - return True - def list(self): +class IntegerVectors_all(IntegerVectors, UniqueRepresentation): + """ + Class of all integer vectors. + """ + def __init__(self): """ + Initialize ``self``. + EXAMPLES:: - sage: IntegerVectors().list() - Traceback (most recent call last): - ... - NotImplementedError: infinite list + sage: IV = IntegerVectors() + sage: TestSuite(IV).run() """ - raise NotImplementedError, "infinite list" # can't use InfiniteAbstractCombinatorialClass + IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) - def cardinality(self): + def _repr_(self): """ EXAMPLES:: - sage: IntegerVectors().cardinality() - +Infinity + sage: IntegerVectors() + Integer vectors """ - return infinity + return "Integer vectors" + def __iter__(self): + """ + Iterate over ``self``. -class IntegerVectors_nk(CombinatorialClass): - def __init__(self, n, k): + EXAMPLES:: + + sage: IV = IntegerVectors() + sage: it = IV.__iter__() + sage: [it.next() for x in range(10)] + [[], [1], [2], [2, 0], [1, 1], [0, 2], [3], [3, 0], [2, 1], [1, 2]] + """ + yield self.element_class(self, []) + n = 1 + while True: + for k in range(1,n+1): + for x in IntegerVectors(n, k): + yield self.element_class(self, list(x)) + n += 1 + +class IntegerVectors_n(IntegerVectors, UniqueRepresentation): + """ + Integer vectors that sum to `n`. + """ + def __init__(self, n): """ TESTS:: - sage: IV = IntegerVectors(2,3) - sage: IV == loads(dumps(IV)) + sage: IV = IntegerVectors(3) + sage: TestSuite(IV).run() + """ + self.n = n + IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) + + def _repr_(self): + """ + TESTS:: + + sage: IV = IntegerVectors(3) + sage: IV + Integer vectors that sum to 3 + """ + return "Integer vectors that sum to %s"%self.n + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: it = IntegerVectors(3).__iter__() + sage: [it.next() for x in range(10)] + [[3], + [3, 0], + [2, 1], + [1, 2], + [0, 3], + [3, 0, 0], + [2, 1, 0], + [2, 0, 1], + [1, 2, 0], + [1, 1, 1]] + """ + k = 1 + while True: + for iv in IntegerVectors_nk(self.n, k): + yield self.element_class(self, list(iv)) + k += 1 + + def __contains__(self, x): + """ + EXAMPLES:: + + sage: [0] in IntegerVectors(0) True + sage: [3] in IntegerVectors(3) + True + sage: [3] in IntegerVectors(2) + False + sage: [3,2,2,1] in IntegerVectors(9) + False + sage: [3,2,2,1] in IntegerVectors(8) + True + """ + if not IntegerVectors.__contains__(self, x): + return False + return sum(x) == self.n + +class IntegerVectors_k(IntegerVectors, UniqueRepresentation): + """ + Integer vectors of length `k`. + """ + def __init__(self, k): + """ + TESTS:: - AUTHORS: + sage: IV = IntegerVectors(k=2) + sage: TestSuite(IV).run() + """ + self.k = k + IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) - - Martin Albrecht + def _repr_(self): + """ + TESTS:: - - Mike Hansen + sage: IV = IntegerVectors(k=2) + sage: IV + Integer vectors of length 2 + """ + return "Integer vectors of length %s"%self.k + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: it = IntegerVectors(k=2).__iter__() + sage: [it.next() for x in range(10)] + [[0, 0], + [1, 0], + [0, 1], + [2, 0], + [1, 1], + [0, 2], + [3, 0], + [2, 1], + [1, 2], + [0, 3]] + """ + n = 0 + while True: + for iv in IntegerVectors_nk(n, self.k): + yield self.element_class(self, list(iv)) + n += 1 + + def __contains__(self, x): + """ + EXAMPLES:: + + sage: [] in IntegerVectors(k=0) + True + sage: [3] in IntegerVectors(k=1) + True + sage: [3] in IntegerVectors(k=2) + False + sage: [3,2,2,1] in IntegerVectors(k=3) + False + sage: [3,2,2,1] in IntegerVectors(k=4) + True + """ + if not IntegerVectors.__contains__(self, x): + return False + return len(x) == self.k + +class IntegerVectors_nk(IntegerVectors, UniqueRepresentation): + """ + Integer vectors of length `k` that sum to `n`. + + AUTHORS: + + - Martin Albrecht + - Mike Hansen + """ + def __init__(self, n, k): + """ + TESTS:: + + sage: IV = IntegerVectors(2, 3) + sage: TestSuite(IV).run() """ self.n = n self.k = k - + IntegerVectors.__init__(self, category=FiniteEnumeratedSets()) def _list_rec(self, n, k): """ - Return a list of a exponent tuples of length `size` such + Return a list of a exponent tuples of length ``size`` such that the degree of the associated monomial is `D`. INPUT: + - ``n`` -- degree (must be 0) - - ``n`` - degree (must be 0) - - - ``k`` - length of exponent tuples (must be 0) - + - ``k`` -- length of exponent tuples (must be 0) EXAMPLES:: @@ -675,44 +897,13 @@ def _list_rec(self, n, k): res.append((n_diff,)+rest) return res - def list(self): - """ - EXAMPLE:: - - sage: IV = IntegerVectors(2,3) - sage: IV.list() - [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] - sage: IntegerVectors(3, 0).list() - [] - sage: IntegerVectors(3, 1).list() - [[3]] - sage: IntegerVectors(0, 1).list() - [[0]] - sage: IntegerVectors(0, 2).list() - [[0, 0]] - sage: IntegerVectors(2, 2).list() - [[2, 0], [1, 1], [0, 2]] - """ - if self.n < 0: - return [] - - if self.k == 0: - if self.n == 0: - return [[]] - else: - return [] - elif self.k == 1: - return [[self.n]] - - res = self._list_rec(self.n, self.k) - return map(list, res) - - def __iter__(self): """ - EXAMPLE:: + Iterate over ``self``. - sage: IV = IntegerVectors(2,3) + EXAMPLES:: + + sage: IV = IntegerVectors(2, 3) sage: list(IV) [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] sage: list(IntegerVectors(3, 0)) @@ -725,13 +916,13 @@ def __iter__(self): [[0, 0]] sage: list(IntegerVectors(2, 2)) [[2, 0], [1, 1], [0, 2]] - sage: IntegerVectors(0,0).list() + sage: IntegerVectors(0, 0).list() [[]] - sage: IntegerVectors(1,0).list() + sage: IntegerVectors(1, 0).list() [] - sage: IntegerVectors(0,1).list() + sage: IntegerVectors(0, 1).list() [[0]] - sage: IntegerVectors(2,2).list() + sage: IntegerVectors(2, 2).list() [[2, 0], [1, 1], [0, 2]] sage: IntegerVectors(-1,0).list() [] @@ -743,24 +934,24 @@ def __iter__(self): if self.k == 0: if self.n == 0: - yield [] + yield self.element_class(self, []) return elif self.k == 1: - yield [self.n] + yield self.element_class(self, [self.n]) return for nbar in range(self.n+1): - n = self.n-nbar - for rest in IntegerVectors_nk(nbar , self.k-1): - yield [n] + rest + n = self.n - nbar + for rest in IntegerVectors_nk(nbar, self.k-1): + yield self.element_class(self, [n] + list(rest)) - def __repr__(self): + def _repr_(self): """ TESTS:: sage: IV = IntegerVectors(2,3) - sage: repr(IV) - 'Integer vectors of length 3 that sum to 2' + sage: IV + Integer vectors of length 3 that sum to 2 """ return "Integer vectors of length %s that sum to %s"%(self.k, self.n) @@ -768,141 +959,318 @@ def __contains__(self, x): """ TESTS:: - sage: IV = IntegerVectors(2,3) + sage: IV = IntegerVectors(2, 3) sage: all([i in IV for i in IV]) True sage: [0,1,2] in IV False sage: [2.0, 0, 0] in IV - False + True sage: [0,1,0,1] in IV False sage: [0,1,1] in IV True sage: [-1,2,1] in IV False + + sage: [0] in IntegerVectors(0, 1) + True + sage: [] in IntegerVectors(0, 0) + True + sage: [] in IntegerVectors(0, 1) + False + sage: [] in IntegerVectors(1, 0) + False + sage: [3] in IntegerVectors(2, 1) + False + sage: [3] in IntegerVectors(3, 1) + True + sage: [3,2,2,1] in IntegerVectors(9, 5) + False + sage: [3,2,2,1] in IntegerVectors(8, 5) + False + sage: [3,2,2,1] in IntegerVectors(8, 4) + True """ - if x not in IntegerVectors(): - return False + if isinstance(x, IntegerVector) and x.parent() is self: + return True - if sum(x) != self.n: + if not IntegerVectors.__contains__(self, x): return False if len(x) != self.k: return False + if sum(x) != self.n: + return False + if len(x) > 0 and min(x) < 0: return False return True -class IntegerVectors_nkconstraints(CombinatorialClass): - def __init__(self, n, k, constraints): +class IntegerVectors_nnondescents(IntegerVectors, UniqueRepresentation): + r""" + Integer vectors `v` graded by two parameters: + + - ``n`` -- the sum of the parts of `v` + + - ``comp`` -- the non descents composition of `v` + + In other words: the length of `v` equals ``c[1]+...+c[k]``, and `v` is + decreasing in the consecutive blocs of length ``c[1], ..., c[k]`` + + Those are the integer vectors of sum ``n`` which are lexicographically + maximal (for the natural left-to-right reading) in their orbit by the + young subgroup `S_{c_1} \times \cdots \times S_{c_k}`. In particular, they + form a set of orbit representative of integer vectors with respect to this + Young subgroup. + """ + @staticmethod + def __classcall_private__(cls, n, comp): """ + Normalize input to ensure a unique representation. + EXAMPLES:: - sage: IV = IntegerVectors(2,3,min_slope=0) - sage: IV == loads(dumps(IV)) + sage: IntegerVectors(4, [2,1]) is IntegerVectors(int(4), (2,1)) True """ + return super(IntegerVectors_nnondescents, cls).__classcall__(cls, n, tuple(comp)) + + def __init__(self, n, comp): + """ + EXAMPLES:: + + sage: IV = IntegerVectors(4, [2]) + sage: TestSuite(IV).run() + """ + self.n = n + self.comp = comp + IntegerVectors.__init__(self, category=FiniteEnumeratedSets()) + + def _repr_(self): + """ + EXAMPLES:: + + sage: IntegerVectors(4, [2]) + Integer vectors of 4 with non-descents composition [2] + """ + return "Integer vectors of %s with non-descents composition %s"%(self.n, list(self.comp)) + + def __iter__(self): + """ + TESTS:: + + sage: IntegerVectors(0, []).list() + [[]] + sage: IntegerVectors(5, []).list() + [] + sage: IntegerVectors(0, [1]).list() + [[0]] + sage: IntegerVectors(4, [1]).list() + [[4]] + sage: IntegerVectors(4, [2]).list() + [[4, 0], [3, 1], [2, 2]] + sage: IntegerVectors(4, [2,2]).list() + [[4, 0, 0, 0], + [3, 1, 0, 0], + [2, 2, 0, 0], + [3, 0, 1, 0], + [2, 1, 1, 0], + [2, 0, 2, 0], + [2, 0, 1, 1], + [1, 1, 2, 0], + [1, 1, 1, 1], + [1, 0, 3, 0], + [1, 0, 2, 1], + [0, 0, 4, 0], + [0, 0, 3, 1], + [0, 0, 2, 2]] + sage: IntegerVectors(5, [1,1,1]).list() + [[5, 0, 0], + [4, 1, 0], + [4, 0, 1], + [3, 2, 0], + [3, 1, 1], + [3, 0, 2], + [2, 3, 0], + [2, 2, 1], + [2, 1, 2], + [2, 0, 3], + [1, 4, 0], + [1, 3, 1], + [1, 2, 2], + [1, 1, 3], + [1, 0, 4], + [0, 5, 0], + [0, 4, 1], + [0, 3, 2], + [0, 2, 3], + [0, 1, 4], + [0, 0, 5]] + sage: IntegerVectors(0, [2,3]).list() + [[0, 0, 0, 0, 0]] + """ + for iv in IntegerVectors(self.n, len(self.comp)): + blocks = [ IntegerVectors(iv[i], self.comp[i], max_slope=0).list() for i in range(len(self.comp))] + for parts in cartesian_product.CartesianProduct(*blocks): + res = [] + for part in parts: + res += part + yield self.element_class(self, res) + +class IntegerVectorsConstraints(IntegerVectors): + """ + Class of integer vectors subject to various constraints. + """ + def __init__(self, n=None, k=None, **constraints): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: TestSuite(IntegerVectors(min_slope=0)).run() + sage: TestSuite(IntegerVectors(3, max_slope=0)).run() + sage: TestSuite(IntegerVectors(3, max_length=4)).run() + sage: TestSuite(IntegerVectors(k=2, max_part=4)).run() + sage: TestSuite(IntegerVectors(k=2, min_part=2, max_part=4)).run() + sage: TestSuite(IntegerVectors(3, 2, max_slope=0)).run() + """ self.n = n self.k = k self.constraints = constraints - def __repr__(self): + if n is not None: + if k is not None or 'max_length' in constraints: + category = FiniteEnumeratedSets() + else: + category = EnumeratedSets() + elif k is not None and 'max_part' in constraints: # n is None + category = FiniteEnumeratedSets() + else: + category = EnumeratedSets() + IntegerVectors.__init__(self, category=category) # placeholder category + + def _repr_(self): """ + Return a string representation of ``self``. + EXAMPLES:: + + sage: IntegerVectors(min_slope=0) + Integer vectors with constraints: min_slope=0 - sage: IntegerVectors(2,3,min_slope=0).__repr__() - 'Integer vectors of length 3 that sum to 2 with constraints: min_slope=0' - """ - return "Integer vectors of length %s that sum to %s with constraints: %s"%(self.k, self.n, ", ".join( ["%s=%s"%(key, self.constraints[key]) for key in sorted(self.constraints.keys())] )) + sage: IntegerVectors(3, max_length=2) + Integer vectors that sum to 3 with constraints: max_length=2 + sage: IntegerVectors(2, 3, min_slope=0) + Integer vectors of length 3 that sum to 2 with constraints: min_slope=0 + """ + if self.k is not None: + if self.n is not None: + base = "Integer vectors of length %s that sum to %s with constraints: "%(self.k, self.n) + else: + base = "Integer vectors of length %s with constraints: "%(self.k) + elif self.n is not None: + base ="Integer vectors that sum to %s with constraints: "%(self.n) + else: + base = "Integer vectors with constraints: " + return base + ", ".join( "%s=%s"%(key, self.constraints[key]) \ + for key in sorted(self.constraints.keys()) ) - def __contains__(self, x): + def __eq__(self, rhs): """ - TESTS:: + EXAMPLES:: - sage: [0] in IntegerVectors(0) - True - sage: [0] in IntegerVectors(0, 1) + sage: IntegerVectors(min_slope=0) == IntegerVectors(min_slope=0) True - sage: [] in IntegerVectors(0, 0) + sage: IntegerVectors(2, min_slope=0) == IntegerVectors(2, min_slope=0) True - sage: [] in IntegerVectors(0, 1) - False - sage: [] in IntegerVectors(1, 0) - False - sage: [3] in IntegerVectors(3) + sage: IntegerVectors(2, 3, min_slope=0) == IntegerVectors(2, 3, min_slope=0) True - sage: [3] in IntegerVectors(2,1) - False - sage: [3] in IntegerVectors(2) - False - sage: [3] in IntegerVectors(3,1) - True - sage: [3,2,2,1] in IntegerVectors(9) - False - sage: [3,2,2,1] in IntegerVectors(9,5) - False - sage: [3,2,2,1] in IntegerVectors(8) - True - sage: [3,2,2,1] in IntegerVectors(8,5) - False - sage: [3,2,2,1] in IntegerVectors(8,4) + """ + if isinstance(rhs, IntegerVectorsConstraints): + return self.n == rhs.n and self.k == rhs.k and self.constraints == rhs.constraints + return False + + def __ne__(self, rhs): + """ + EXAMPLES:: + + sage: IntegerVectors(min_slope=0) != IntegerVectors(min_slope=3) True + """ + return not self.__eq__(rhs) + + def __contains__(self, x): + """ + TESTS:: + sage: [3,2,2,1] in IntegerVectors(8,4, min_part = 1) True sage: [3,2,2,1] in IntegerVectors(8,4, min_part = 2) False + + sage: [0,3,0,1,2] in IntegerVectors(6, max_length=3) + False """ - if x not in IntegerVectors(): - return False + if isinstance(x, IntegerVector) and x.parent() is self: + return True - if sum(x) != self.n: + if not IntegerVectors.__contains__(self, x): return False - if len(x) != self.k: + if self.k is not None and len(x) != self.k: return False - if self.constraints: - if not misc.check_integer_list_constraints(x, singleton=True, **self.constraints): - return False + if self.n is not None and sum(x) != self.n: + return False - return True + return misc.check_integer_list_constraints(x, singleton=True, **self.constraints) def cardinality(self): """ + Return the cardinality of ``self``. + EXAMPLES:: - sage: IntegerVectors(3,3, min_part=1).cardinality() + sage: IntegerVectors(3, 3, min_part=1).cardinality() 1 - sage: IntegerVectors(5,3, min_part=1).cardinality() + sage: IntegerVectors(5, 3, min_part=1).cardinality() 6 + sage: IntegerVectors(13, 4, max_part=4).cardinality() + 20 + sage: IntegerVectors(k=4, max_part=3).cardinality() + 256 + sage: IntegerVectors(k=3, min_part=2, max_part=4).cardinality() + 27 sage: IntegerVectors(13, 4, min_part=2, max_part=4).cardinality() 16 """ - if not self.constraints: - if self.n >= 0: - return binomial(self.n+self.k-1,self.n) - else: - return 0 - else: - if len(self.constraints) == 1 and 'max_part' in self.constraints and self.constraints['max_part'] != infinity: + if self.k is not None and 'max_part' in self.constraints \ + and self.constraints['max_part'] != PlusInfinity(): + if self.n is None and len(self.constraints) == 2 \ + and 'min_part' in self.constraints \ + and self.constraints['min_part'] != MinusInfinity(): + num = self.constraints['max_part'] - self.constraints['min_part'] + 1 + return Integer(num ** self.k) + if len(self.constraints) == 1: m = self.constraints['max_part'] + if self.n is None: + return Integer((m+1) ** self.k) if m >= self.n: - return binomial(self.n+self.k-1,self.n) - else: #do by inclusion / exclusion on the number - #i of parts greater than m - return sum( [(-1)**i * binomial(self.n+self.k-1-i*(m+1), self.k-1)*binomial(self.k,i) for i in range(0, self.n/(m+1)+1)]) - else: - return len(self.list()) - + return Integer(binomial(self.n+self.k-1, self.n)) + # do by inclusion / exclusion on the number + # i of parts greater than m + return Integer(sum( (-1)**i * binomial(self.n+self.k-1-i*(m+1), self.k-1) \ + * binomial(self.k,i) for i in range(0, self.n/(m+1)+1) )) + return Integer(sum(1 for x in self)) def _parameters(self): """ - Returns a tuple (min_length, max_length, floor, ceiling, - min_slope, max_slope) for the parameters of self. + Return a tuple ``(min_length, max_length, floor, ceiling, + min_slope, max_slope)`` for the parameters of ``self``. EXAMPLES:: @@ -915,11 +1283,11 @@ def _parameters(self): sage: [floor(i) for i in range(1,10)] [0, 0, 0, 0, 0, 0, 0, 0, 0] sage: [ceiling(i) for i in range(1,5)] - [+Infinity, +Infinity, +Infinity, +Infinity] + [inf, inf, inf, inf] sage: min_slope 0 sage: max_slope - +Infinity + inf sage: IV = IntegerVectors(3,10,inner=[4,1,3], min_part = 2) sage: min_length, max_length, floor, ceiling, min_slope, max_slope = IV._parameters() @@ -933,30 +1301,29 @@ def _parameters(self): """ constraints = self.constraints #n, min_length, max_length, floor, ceiling, min_slope, max_slope - if self.k == -1: + if self.k is None: min_length = constraints.get('min_length', 0) - max_length = constraints.get('max_length', infinity) + max_length = constraints.get('max_length', float('+inf')) else: min_length = self.k max_length = self.k min_part = constraints.get('min_part', 0) - max_part = constraints.get('max_part', infinity) - min_slope = constraints.get('min_slope', -infinity) - max_slope = constraints.get('max_slope', infinity) + max_part = constraints.get('max_part', float('+inf')) + min_slope = constraints.get('min_slope', float('-inf')) + max_slope = constraints.get('max_slope', float('+inf')) if 'outer' in self.constraints: ceiling = list2func( map(lambda i: min(max_part, i), self.constraints['outer']), default=max_part ) else: - ceiling = constant_func(max_part) + ceiling = lambda x: max_part if 'inner' in self.constraints: floor = list2func( map(lambda i: max(min_part, i), self.constraints['inner']), default=min_part ) else: - floor = constant_func(min_part) + floor = lambda x: min_part return (min_length, max_length, floor, ceiling, min_slope, max_slope) - def first(self): """ EXAMPLES:: @@ -964,16 +1331,23 @@ def first(self): sage: IntegerVectors(2,3,min_slope=0).first() [0, 1, 1] """ - return integer_list.first(self.n, *self._parameters()) + return self.__iter__().next() + + _an_element_ = first def next(self, x): """ EXAMPLES:: - sage: IntegerVectors(2,3,min_slope=0).last() + sage: IV = IntegerVectors(2, 3, min_slope=0) + sage: x = IV.first() + sage: IV.next(x) [0, 0, 2] """ - return integer_list.next(x, *self._parameters()) + ret = self.element_class(self, integer_list.next(x, *self._parameters())) + if ret is None and self.n is None: + return self.element_class(self, integer_list.first(sum(x)+1, *self._parameters())) + return ret def __iter__(self): """ @@ -1022,7 +1396,7 @@ def __iter__(self): :: - sage: iv = [ IntegerVectors(n,k) for n in range(-2, 7) for k in range(7) ] + sage: iv = [ IntegerVectors(n, k) for n in range(-2, 7) for k in range(7) ] sage: all(map(lambda x: x.cardinality() == len(x.list()), iv)) True sage: essai = [[1,1,1], [2,5,6], [6,5,2]] @@ -1030,176 +1404,62 @@ def __iter__(self): sage: all(map(lambda x: x.cardinality() == len(x.list()), iv)) True """ - return integer_list.iterator(self.n, *self._parameters()) - -class IntegerVectors_nconstraints(IntegerVectors_nkconstraints): - def __init__(self, n, constraints): - """ - TESTS:: - - sage: IV = IntegerVectors(3, max_length=2) - sage: IV == loads(dumps(IV)) - True - """ - IntegerVectors_nkconstraints.__init__(self, n, -1, constraints) - - def __repr__(self): - """ - EXAMPLES:: - - sage: repr(IntegerVectors(3)) - 'Integer vectors that sum to 3' - sage: repr(IntegerVectors(3, max_length=2)) - 'Integer vectors that sum to 3 with constraints: max_length=2' - """ - if self.constraints: - return "Integer vectors that sum to %s with constraints: %s"%(self.n,", ".join( ["%s=%s"%(key, self.constraints[key]) for key in sorted(self.constraints.keys())] )) - else: - return "Integer vectors that sum to %s"%(self.n,) - - def __contains__(self, x): - """ - EXAMPLES:: - - sage: [0,3,0,1,2] in IntegerVectors(6) - True - sage: [0,3,0,1,2] in IntegerVectors(6, max_length=3) - False - """ - if self.constraints: - return x in IntegerVectors_all() and misc.check_integer_list_constraints(x, singleton=True, **self.constraints) - else: - return x in IntegerVectors_all() and sum(x) == self.n - - def cardinality(self): - """ - EXAMPLES:: - - sage: IntegerVectors(3, max_length=2).cardinality() - 4 - sage: IntegerVectors(3).cardinality() - +Infinity - """ - if 'max_length' not in self.constraints: - return infinity - else: - return self._CombinatorialClass__cardinality_from_iterator() - - def list(self): - """ - EXAMPLES:: - - sage: IntegerVectors(3, max_length=2).list() - [[3], [2, 1], [1, 2], [0, 3]] - sage: IntegerVectors(3).list() - Traceback (most recent call last): - ... - NotImplementedError: infinite list - """ - if 'max_length' not in self.constraints: - raise NotImplementedError, "infinite list" # no list from infinite iter + if self.n is None: + if self.k is not None and 'max_part' in self.constraints: + n_list = range((self.constraints['max_part'] + 1) * self.k) + else: + n_list = NN else: - return list(self) - - -class IntegerVectors_nnondescents(CombinatorialClass): - r""" - The combinatorial class of integer vectors v graded by two - parameters: - - - n: the sum of the parts of v - - - comp: the non descents composition of v + n_list = [self.n] + for n in n_list: + for x in integer_list.iterator(n, *self._parameters()): + yield self.element_class(self, x) - In other words: the length of v equals c[1]+...+c[k], and v is - decreasing in the consecutive blocs of length c[1], ..., c[k] - - Those are the integer vectors of sum n which are lexicographically - maximal (for the natural left->right reading) in their orbit by the - young subgroup S_c_1 x x S_c_k. In particular, they form a set - of orbit representative of integer vectors w.r.t. this young - subgroup. +def IntegerVectors_nconstraints(n, **constraints): """ - def __init__(self, n, comp): - """ - EXAMPLES:: - - sage: IV = IntegerVectors(4, [2]) - sage: IV == loads(dumps(IV)) - True - """ - self.n = n - self.comp = comp - - def __repr__(self): - """ - EXAMPLES:: - - sage: IntegerVectors(4, [2]).__repr__() - 'Integer vectors of 4 with non-descents composition [2]' - """ - return "Integer vectors of %s with non-descents composition %s"%(self.n, self.comp) + EXAMPLES:: - def __iter__(self): - """ - TESTS:: - - sage: IntegerVectors(0, []).list() - [[]] - sage: IntegerVectors(5, []).list() - [] - sage: IntegerVectors(0, [1]).list() - [[0]] - sage: IntegerVectors(4, [1]).list() - [[4]] - sage: IntegerVectors(4, [2]).list() - [[4, 0], [3, 1], [2, 2]] - sage: IntegerVectors(4, [2,2]).list() - [[4, 0, 0, 0], - [3, 1, 0, 0], - [2, 2, 0, 0], - [3, 0, 1, 0], - [2, 1, 1, 0], - [2, 0, 2, 0], - [2, 0, 1, 1], - [1, 1, 2, 0], - [1, 1, 1, 1], - [1, 0, 3, 0], - [1, 0, 2, 1], - [0, 0, 4, 0], - [0, 0, 3, 1], - [0, 0, 2, 2]] - sage: IntegerVectors(5, [1,1,1]).list() - [[5, 0, 0], - [4, 1, 0], - [4, 0, 1], - [3, 2, 0], - [3, 1, 1], - [3, 0, 2], - [2, 3, 0], - [2, 2, 1], - [2, 1, 2], - [2, 0, 3], - [1, 4, 0], - [1, 3, 1], - [1, 2, 2], - [1, 1, 3], - [1, 0, 4], - [0, 5, 0], - [0, 4, 1], - [0, 3, 2], - [0, 2, 3], - [0, 1, 4], - [0, 0, 5]] - sage: IntegerVectors(0, [2,3]).list() - [[0, 0, 0, 0, 0]] - """ - for iv in IntegerVectors(self.n, len(self.comp)): - blocks = [ IntegerVectors(iv[i], self.comp[i], max_slope=0).list() for i in range(len(self.comp))] - for parts in cartesian_product.CartesianProduct(*blocks): - res = [] - for part in parts: - res += part - yield res + sage: sage.combinat.integer_vector.IntegerVectors_nconstraints(2) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.integer_vector.IntegerVectors_n instead + See http://trac.sagemath.org/12453 for details. + Integer vectors that sum to 2 + sage: sage.combinat.integer_vector.IntegerVectors_nconstraints(2, min_slope=0) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.integer_vector.IntegerVectorsConstraints instead + See http://trac.sagemath.org/12453 for details. + Integer vectors that sum to 2 with constraints: min_slope=0 + """ + from sage.misc.superseded import deprecation + if len(constraints) == 0: + deprecation(12453, 'this class is deprecated. Use sage.combinat.integer_vector.IntegerVectors_n instead') + return IntegerVectors_n(n) + deprecation(12453, 'this class is deprecated. Use sage.combinat.integer_vector.IntegerVectorsConstraints instead') + return IntegerVectorsConstraints(n, **constraints) + +def IntegerVectors_nkconstraints(n=None, k=None, **constraints): + """ + EXAMPLES:: + sage: sage.combinat.integer_vector.IntegerVectors_nkconstraints(3, 2) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.integer_vector.IntegerVectors_nk instead + See http://trac.sagemath.org/12453 for details. + Integer vectors of length 2 that sum to 3 + sage: sage.combinat.integer_vector.IntegerVectors_nkconstraints(3, 2, min_slope=0) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.integer_vector.IntegerVectorsConstraints instead + See http://trac.sagemath.org/12453 for details. + Integer vectors of length 2 that sum to 3 with constraints: min_slope=0 + """ + from sage.misc.superseded import deprecation + if len(constraints) == 0: + if n is None: + deprecation(12453, 'this class is deprecated. Use sage.combinat.integer_vector.IntegerVectors_k instead') + return IntegerVectors_k(k) + deprecation(12453, 'this class is deprecated. Use sage.combinat.integer_vector.IntegerVectors_nk instead') + return IntegerVectors_nk(n, k) + deprecation(12453, 'this class is deprecated. Use sage.combinat.integer_vector.IntegerVectorsConstraints instead') + return IntegerVectorsConstraints(n, k, **constraints) + +# October 2012: fixing outdated pickles which use classes being deprecated +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.combinat.integer_vector', 'IntegerVectors_nconstraints', IntegerVectorsConstraints) +register_unpickle_override('sage.combinat.integer_vector', 'IntegerVectors_nkconstraints', IntegerVectorsConstraints) diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index ae9a54e3776..e02f285d5ce 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -3,13 +3,8 @@ AUTHORS: - - Mike Hansen (2007): initial version, ported from MuPAD-Combinat - - Nicolas M. Thiery (2010-10-30): WeightedIntegerVectors(weights) + cleanup - -.. WARNING:: - - The list(self) function in this file used the :class:`Permutation` class improperly, returning - a list of, generally speaking, invalid permutations (repeated entries, including 0). +- Mike Hansen (2007): initial version, ported from MuPAD-Combinat +- Nicolas M. Thiery (2010-10-30): WeightedIntegerVectors(weights) + cleanup """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen @@ -19,23 +14,25 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading -from __builtin__ import list as builtinlist -from sage.rings.integer import Integer -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.rings.integer import Integer +from sage.rings.all import ZZ +from sage.combinat.integer_vector import IntegerVector from sage.combinat.words.word import Word -from permutation import Permutation +from sage.combinat.permutation import Permutation -def WeightedIntegerVectors(n = None, weight = None): - """ - Returns the combinatorial class of integer vectors of ``n`` - weighted by ``weight``, that is, the nonnegative integer vectors - `(v_1,\\dots,v_{length(weight)})` satisfying `\\sum_i v_i - weight[i]==n`. +class WeightedIntegerVectors(Parent, UniqueRepresentation): + r""" + The class of integer vectors of `n` weighted by ``weight``, that is, the + nonnegative integer vectors `(v_1, \ldots, v_{\ell})` + satisfying `\sum_{i=1}^{\ell} v_i w_i = n` where `\ell` is + ``length(weight)`` and `w_i` is ``weight[i]``. INPUT: @@ -68,139 +65,77 @@ def WeightedIntegerVectors(n = None, weight = None): sage: WeightedIntegerVectors(None,None) Traceback (most recent call last): ... - ValueError: weights should be specified + ValueError: the weights must be specified .. TODO:: - should the order of the arguments ``n`` and ``weight`` be - exchanged to simplify the logic ? + Should the order of the arguments ``n`` and ``weight`` be + exchanged to simplify the logic? """ - if weight is None and n is not None: - weight = n - n = None - if weight is None: - raise ValueError("weights should be specified") - weight = tuple(weight) - if n is None: - return WeightedIntegerVectors_all(weight) - else: - return WeightedIntegerVectors_nweight(n, weight) - -class WeightedIntegerVectors_all(DisjointUnionEnumeratedSets): - r""" - Set of weighted integer vectors. - - EXAMPLES:: - - sage: W = WeightedIntegerVectors([3,1,1,2,1]); W - Integer vectors weighted by [3, 1, 1, 2, 1] - sage: W.cardinality() - +Infinity - - sage: W12 = W.graded_component(12) - sage: W12.an_element() - [4, 0, 0, 0, 0] - sage: W12.last() - [0, 12, 0, 0, 0] - sage: W12.cardinality() - 441 - sage: for w in W12: print w - [4, 0, 0, 0, 0] - [3, 0, 0, 1, 1] - [3, 0, 1, 1, 0] - ... - [0, 11, 1, 0, 0] - [0, 12, 0, 0, 0] - """ - def __init__(self, weights): + @staticmethod + def __classcall_private__(cls, n=None, weight=None): """ + Normalize inputs to ensure a unique representation. + TESTS:: - sage: C = WeightedIntegerVectors([2,1,3]) - sage: C.__class__ - - sage: C.category() - Join of Category of infinite enumerated sets and Category of sets with grading - sage: TestSuite(C).run() + sage: W = WeightedIntegerVectors(8, [1,1,2]) + sage: W2 = WeightedIntegerVectors(int(8), (1,1,2)) + sage: W is W2 + True """ - self._weights = weights - from sage.sets.all import Family, NonNegativeIntegers - # Use "partial" to make the basis function (with the weights - # argument specified) pickleable. Otherwise, it seems to - # cause problems... - from functools import partial - F = Family(NonNegativeIntegers(), partial(WeightedIntegerVectors, weight = weights)) - DisjointUnionEnumeratedSets.__init__(self, F, facade=True, keepkey=False, - category = (SetsWithGrading(), InfiniteEnumeratedSets())) + if weight is None: + if n is None: + raise ValueError("the weights must be specified") + if n in ZZ: + weight = (n,) + else: + weight = tuple(n) + n = None - def _repr_(self): - """ - EXAMPLES:: + weight = tuple(weight) + if n is None: + return WeightedIntegerVectors_all(weight) - sage: WeightedIntegerVectors([2,1,3]) - Integer vectors weighted by [2, 1, 3] - """ - return "Integer vectors weighted by %s"%list(self._weights) + return super(WeightedIntegerVectors, cls).__classcall__(cls, n, weight) - def __contains__(self, x): + def __init__(self, n, weight): """ - EXAMPLES:: + TESTS:: - sage: [] in WeightedIntegerVectors([]) - True - sage: [3,0,0] in WeightedIntegerVectors([2,1,1]) - True - sage: [3,0] in WeightedIntegerVectors([2,1,1]) - False - sage: [3,-1,0] in WeightedIntegerVectors([2,1,1]) - False + sage: WIV = WeightedIntegerVectors(8, [1,1,2]) + sage: TestSuite(WIV).run() """ - return isinstance(x, (builtinlist, Permutation)) and \ - len(x) == len(self._weights) and \ - all(isinstance(i, (int, Integer)) and i>=0 for i in x) + self._n = n + self._weights = weight + Parent.__init__(self, category=FiniteEnumeratedSets()) - def subset(self, size = None): - """ - EXAMPLES:: + Element = IntegerVector - sage: C = WeightedIntegerVectors([2,1,3]) - sage: C.subset(4) - Integer vectors of 4 weighted by [2, 1, 3] + def _element_constructor_(self, lst): """ - if size is None: - return self - return self._family[size] + Construct an element of ``self`` from ``lst``. - def grading(self, x): # or degree / grading - """ EXAMPLES:: - sage: C = WeightedIntegerVectors([2,1,3]) - sage: C.grading((2,1,1)) - 8 - """ - return sum([exp*deg for exp,deg in zip(x, self._weights)]) - -class WeightedIntegerVectors_nweight(UniqueRepresentation, Parent): - def __init__(self, n, weight): - """ - TESTS:: - - sage: C = WeightedIntegerVectors(8, [1,1,2]) - sage: C.__class__ - - sage: TestSuite(C).run() + sage: WIV = WeightedIntegerVectors(3, [2,1,1]) + sage: elt = WIV([1, 2, 0]); elt + [1, 2, 0] + sage: elt.parent() is WIV + True """ - Parent.__init__(self, category = FiniteEnumeratedSets()) - self._n = n - self._weights = weight + if isinstance(lst, IntegerVector): + if lst.parent() is self: + return lst + raise ValueError("Cannot convert %s into %s"(lst, self)) + return self.element_class(self, lst) def _repr_(self): """ TESTS:: - sage: repr(WeightedIntegerVectors(8, [1,1,2])) - 'Integer vectors of 8 weighted by [1, 1, 2]' + sage: WeightedIntegerVectors(8, [1,1,2]) + Integer vectors of 8 weighted by [1, 1, 2] """ return "Integer vectors of %s weighted by %s"%(self._n, list(self._weights)) @@ -231,19 +166,16 @@ def __contains__(self, x): sage: [0] in WeightedIntegerVectors(0, []) False """ - if not isinstance(x, (builtinlist, Permutation)): + if not isinstance(x, (list, IntegerVector, Permutation)): return False if len(self._weights) != len(x): return False s = 0 for i in range(len(x)): - if not isinstance(x[i], (int, Integer)): + if (not isinstance(x[i], (int, Integer))) and (x[i] not in ZZ): return False s += x[i]*self._weights[i] - if s != self._n: - return False - - return True + return s == self._n def _recfun(self, n, l): """ @@ -279,7 +211,7 @@ def __iter__(self): sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ] sage: iv = [ IntegerVectors(k, 3) for k in range(11) ] - sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] ) + sage: all( [ sorted(map(list, iv[k].list())) == sorted(map(list, ivw[k].list())) for k in range(11) ] ) True :: @@ -290,11 +222,121 @@ def __iter__(self): """ if len(self._weights) == 0: if self._n == 0: - yield [] + yield self.element_class(self, []) return perm = Word(self._weights).standard_permutation() - l = [x for x in sorted(self._weights)] - for x in self._recfun(self._n, l): - yield perm.action(x) - #_left_to_right_multiply_on_right(Permutation(x)) + for a in self._recfun(self._n, [x for x in sorted(self._weights)]): + yield self.element_class(self, list(perm.action(a))) + +class WeightedIntegerVectors_all(DisjointUnionEnumeratedSets): + r""" + Set of weighted integer vectors. + + EXAMPLES:: + + sage: W = WeightedIntegerVectors([3,1,1,2,1]); W + Integer vectors weighted by [3, 1, 1, 2, 1] + sage: W.cardinality() + +Infinity + + sage: W12 = W.graded_component(12) + sage: W12.an_element() + [4, 0, 0, 0, 0] + sage: W12.last() + [0, 12, 0, 0, 0] + sage: W12.cardinality() + 441 + sage: for w in W12: print w + [4, 0, 0, 0, 0] + [3, 0, 0, 1, 1] + [3, 0, 1, 1, 0] + ... + [0, 11, 1, 0, 0] + [0, 12, 0, 0, 0] + """ + def __init__(self, weight): + """ + TESTS:: + + sage: C = WeightedIntegerVectors([2,1,3]) + sage: C.category() + Join of Category of sets with grading and Category of infinite enumerated sets + sage: TestSuite(C).run() + """ + self._weights = weight + from sage.sets.all import Family, NonNegativeIntegers + # Use "partial" to make the basis function (with the weights + # argument specified) pickleable. Otherwise, it seems to + # cause problems... + from functools import partial + F = Family(NonNegativeIntegers(), partial(WeightedIntegerVectors, weight=weight)) + DisjointUnionEnumeratedSets.__init__(self, F, facade=True, keepkey=False, + category = (SetsWithGrading(), InfiniteEnumeratedSets())) + + def _repr_(self): + """ + EXAMPLES:: + + sage: WeightedIntegerVectors([2,1,3]) + Integer vectors weighted by [2, 1, 3] + """ + return "Integer vectors weighted by %s"%list(self._weights) + + def __contains__(self, x): + """ + EXAMPLES:: + + sage: [] in WeightedIntegerVectors([]) + True + sage: [3,0,0] in WeightedIntegerVectors([2,1,1]) + True + sage: [3,0] in WeightedIntegerVectors([2,1,1]) + False + sage: [3,-1,0] in WeightedIntegerVectors([2,1,1]) + False + """ + return isinstance(x, (list, IntegerVector, Permutation)) and \ + len(x) == len(self._weights) and \ + all(i in ZZ and i >= 0 for i in x) + + def subset(self, size = None): + """ + EXAMPLES:: + + sage: C = WeightedIntegerVectors([2,1,3]) + sage: C.subset(4) + Integer vectors of 4 weighted by [2, 1, 3] + """ + if size is None: + return self + return self._family[size] + + def grading(self, x): # or degree / grading + """ + EXAMPLES:: + + sage: C = WeightedIntegerVectors([2,1,3]) + sage: C.grading((2,1,1)) + 8 + """ + return sum([exp*deg for exp,deg in zip(x, self._weights)]) + +def WeightedIntegerVectors_nweight(n, weight): + """ + Deprecated in :trac:`12453`. Use :class:`WeightedIntegerVectors` instead. + + EXAMPLES:: + + sage: sage.combinat.integer_vector_weighted.WeightedIntegerVectors_nweight(7, [2,2]) + doctest:...: DeprecationWarning: this class is deprecated. Use WeightedIntegerVectors instead + See http://trac.sagemath.org/12453 for details. + Integer vectors of 7 weighted by [2, 2] + """ + from sage.misc.superseded import deprecation + deprecation(12453, 'this class is deprecated. Use WeightedIntegerVectors instead') + return WeightedIntegerVectors(n, weight) + +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.combinat.integer_vector_weighted', 'WeightedIntegerVectors_nweight', WeightedIntegerVectors) + diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py index 81261258128..5553f4e28a4 100644 --- a/src/sage/combinat/integer_vectors_mod_permgroup.py +++ b/src/sage/combinat/integer_vectors_mod_permgroup.py @@ -304,7 +304,7 @@ def ambient(self): sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]])) sage: S.ambient() - Integer vectors + Integer vectors with constraints: length=4 """ # TODO: Fix me once 'IntegerVectors(length=bla)' will return # the integer vectors of length bla @@ -801,14 +801,8 @@ def ambient(self): Integer vectors that sum to 6 sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=12); S.ambient() Integer vectors that sum to 6 with constraints: max_part=12 - - .. todo:: - - Integer vectors should accept ``max_part`` as a single argument, and the following should change:: - sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=12); S.ambient() - Integer vectors - + Integer vectors with constraints: max_part=12 """ if self._sum is not None: if self._max_part <= -1: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index b7ad2469f08..2f9b010f40b 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -3712,8 +3712,8 @@ def add_horizontal_border_strip(self, k): #list all of the positions for cells #filling each self from the left to the right - l = IntegerVectors(k, len(shelf), outer=shelf).list() - for iv in l: + for iv in IntegerVectors(k, len(shelf), outer=shelf): + iv = list(iv) # Make a mutable list tmp = conj + [0]*k j = 0 for t in range(len(iv)): diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index f3a81976130..6ce8edcc7e3 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -4540,6 +4540,7 @@ def __iter__(self): if i != 1: for k in range(1, self.size+1): for c in IntegerVectors(self.size - k, i-1): + c = list(c) c.append(k) for sst in SemistandardTableaux_shape_weight(part, Composition(c)): yield self.element_class(self, sst) @@ -4642,6 +4643,7 @@ def __iter__(self): if i != 1: for k in range(1, n+1): for c in IntegerVectors(n - k, i-1): + c = list(c) c.append(k) for sst in SemistandardTableaux_shape_weight(self.shape, Composition(c)): yield self.element_class(self, sst)