Path: blob/master/src/sage/combinat/integer_vector_weighted.py
8815 views
"""1Weighted Integer Vectors23AUTHORS:45- Mike Hansen (2007): initial version, ported from MuPAD-Combinat6- Nicolas M. Thiery (2010-10-30): WeightedIntegerVectors(weights) + cleanup78.. WARNING::910The list(self) function in this file used the :class:`Permutation` class improperly, returning11a list of, generally speaking, invalid permutations (repeated entries, including 0).12"""13#*****************************************************************************14# Copyright (C) 2007 Mike Hansen <[email protected]>15# 2010 Nicolas M. Thiery <nthiery at users.sf.net>16#17# Distributed under the terms of the GNU General Public License (GPL)18#19# http://www.gnu.org/licenses/20#*****************************************************************************21from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets22from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets23from sage.categories.sets_with_grading import SetsWithGrading24from __builtin__ import list as builtinlist25from sage.rings.integer import Integer26from sage.structure.unique_representation import UniqueRepresentation27from sage.structure.parent import Parent28from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets29from sage.combinat.words.word import Word30from permutation import Permutation3132def WeightedIntegerVectors(n = None, weight = None):33"""34Returns the combinatorial class of integer vectors of ``n``35weighted by ``weight``, that is, the nonnegative integer vectors36`(v_1,\\dots,v_{length(weight)})` satisfying `\\sum_i v_i37weight[i]==n`.3839INPUT:4041- ``n`` -- a non negative integer (optional)4243- ``weight`` -- a tuple (or list or iterable) of positive integers4445EXAMPLES::4647sage: WeightedIntegerVectors(8, [1,1,2])48Integer vectors of 8 weighted by [1, 1, 2]49sage: WeightedIntegerVectors(8, [1,1,2]).first()50[0, 0, 4]51sage: WeightedIntegerVectors(8, [1,1,2]).last()52[8, 0, 0]53sage: WeightedIntegerVectors(8, [1,1,2]).cardinality()542555sage: WeightedIntegerVectors(8, [1,1,2]).random_element()56[1, 1, 3]5758sage: WeightedIntegerVectors([1,1,2])59Integer vectors weighted by [1, 1, 2]60sage: WeightedIntegerVectors([1,1,2]).cardinality()61+Infinity62sage: WeightedIntegerVectors([1,1,2]).first()63[0, 0, 0]6465TESTS::6667sage: WeightedIntegerVectors(None,None)68Traceback (most recent call last):69...70ValueError: weights should be specified7172.. TODO::7374should the order of the arguments ``n`` and ``weight`` be75exchanged to simplify the logic ?76"""77if weight is None and n is not None:78weight = n79n = None80if weight is None:81raise ValueError("weights should be specified")82weight = tuple(weight)83if n is None:84return WeightedIntegerVectors_all(weight)85else:86return WeightedIntegerVectors_nweight(n, weight)8788class WeightedIntegerVectors_all(DisjointUnionEnumeratedSets):89r"""90Set of weighted integer vectors.9192EXAMPLES::9394sage: W = WeightedIntegerVectors([3,1,1,2,1]); W95Integer vectors weighted by [3, 1, 1, 2, 1]96sage: W.cardinality()97+Infinity9899sage: W12 = W.graded_component(12)100sage: W12.an_element()101[4, 0, 0, 0, 0]102sage: W12.last()103[0, 12, 0, 0, 0]104sage: W12.cardinality()105441106sage: for w in W12: print w107[4, 0, 0, 0, 0]108[3, 0, 0, 1, 1]109[3, 0, 1, 1, 0]110...111[0, 11, 1, 0, 0]112[0, 12, 0, 0, 0]113"""114def __init__(self, weights):115"""116TESTS::117118sage: C = WeightedIntegerVectors([2,1,3])119sage: C.__class__120<class 'sage.combinat.integer_vector_weighted.WeightedIntegerVectors_all_with_category'>121sage: C.category()122Join of Category of infinite enumerated sets and Category of sets with grading123sage: TestSuite(C).run()124"""125self._weights = weights126from sage.sets.all import Family, NonNegativeIntegers127# Use "partial" to make the basis function (with the weights128# argument specified) pickleable. Otherwise, it seems to129# cause problems...130from functools import partial131F = Family(NonNegativeIntegers(), partial(WeightedIntegerVectors, weight = weights))132DisjointUnionEnumeratedSets.__init__(self, F, facade=True, keepkey=False,133category = (SetsWithGrading(), InfiniteEnumeratedSets()))134135def _repr_(self):136"""137EXAMPLES::138139sage: WeightedIntegerVectors([2,1,3])140Integer vectors weighted by [2, 1, 3]141"""142return "Integer vectors weighted by %s"%list(self._weights)143144def __contains__(self, x):145"""146EXAMPLES::147148sage: [] in WeightedIntegerVectors([])149True150sage: [3,0,0] in WeightedIntegerVectors([2,1,1])151True152sage: [3,0] in WeightedIntegerVectors([2,1,1])153False154sage: [3,-1,0] in WeightedIntegerVectors([2,1,1])155False156"""157return isinstance(x, (builtinlist, Permutation)) and \158len(x) == len(self._weights) and \159all(isinstance(i, (int, Integer)) and i>=0 for i in x)160161def subset(self, size = None):162"""163EXAMPLES::164165sage: C = WeightedIntegerVectors([2,1,3])166sage: C.subset(4)167Integer vectors of 4 weighted by [2, 1, 3]168"""169if size is None:170return self171return self._family[size]172173def grading(self, x): # or degree / grading174"""175EXAMPLES::176177sage: C = WeightedIntegerVectors([2,1,3])178sage: C.grading((2,1,1))1798180"""181return sum([exp*deg for exp,deg in zip(x, self._weights)])182183class WeightedIntegerVectors_nweight(UniqueRepresentation, Parent):184def __init__(self, n, weight):185"""186TESTS::187188sage: C = WeightedIntegerVectors(8, [1,1,2])189sage: C.__class__190<class 'sage.combinat.integer_vector_weighted.WeightedIntegerVectors_nweight_with_category'>191sage: TestSuite(C).run()192"""193Parent.__init__(self, category = FiniteEnumeratedSets())194self._n = n195self._weights = weight196197def _repr_(self):198"""199TESTS::200201sage: repr(WeightedIntegerVectors(8, [1,1,2]))202'Integer vectors of 8 weighted by [1, 1, 2]'203"""204return "Integer vectors of %s weighted by %s"%(self._n, list(self._weights))205206def __contains__(self, x):207"""208EXAMPLES::209210sage: [] in WeightedIntegerVectors(0, [])211True212sage: [] in WeightedIntegerVectors(1, [])213False214sage: [3,0,0] in WeightedIntegerVectors(6, [2,1,1])215True216sage: [1] in WeightedIntegerVectors(1, [1])217True218sage: [1] in WeightedIntegerVectors(2, [2])219True220sage: [2] in WeightedIntegerVectors(4, [2])221True222sage: [2, 0] in WeightedIntegerVectors(4, [2, 2])223True224sage: [2, 1] in WeightedIntegerVectors(4, [2, 2])225False226sage: [2, 1] in WeightedIntegerVectors(6, [2, 2])227True228sage: [2, 1, 0] in WeightedIntegerVectors(6, [2, 2])229False230sage: [0] in WeightedIntegerVectors(0, [])231False232"""233if not isinstance(x, (builtinlist, Permutation)):234return False235if len(self._weights) != len(x):236return False237s = 0238for i in range(len(x)):239if not isinstance(x[i], (int, Integer)):240return False241s += x[i]*self._weights[i]242if s != self._n:243return False244245return True246247def _recfun(self, n, l):248"""249EXAMPLES::250251sage: w = WeightedIntegerVectors(3, [2,1,1])252sage: list(w._recfun(3, [1,1,2]))253[[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]]254"""255w = l[-1]256l = l[:-1]257if l == []:258d = int(n) / int(w)259if n % w == 0:260yield [d]261# Otherwise: bad branch262return263264for d in range(int(n)/int(w), -1, -1):265for x in self._recfun(n-d*w, l):266yield x + [d]267268def __iter__(self):269"""270TESTS::271272sage: WeightedIntegerVectors(7, [2,2]).list()273[]274sage: WeightedIntegerVectors(3, [2,1,1]).list()275[[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]]276277::278279sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ]280sage: iv = [ IntegerVectors(k, 3) for k in range(11) ]281sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] )282True283284::285286sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ]287sage: all( [ i.cardinality() == len(i.list()) for i in ivw] )288True289"""290if len(self._weights) == 0:291if self._n == 0:292yield []293return294295perm = Word(self._weights).standard_permutation()296l = [x for x in sorted(self._weights)]297for x in self._recfun(self._n, l):298yield perm.action(x)299#_left_to_right_multiply_on_right(Permutation(x))300301302