Ticket #10193: trac_10193-graded_sets.patch

File trac_10193-graded_sets.patch, 22.2 KB (added by vdelecroix, 7 years ago)
  • doc/en/reference/categories.rst

    # HG changeset patch
    # User Vincent Delecroix <20100.delecroix at gmail.com>
    # Date 1360881387 18000
    # Node ID 8951649b52fc446c699598381532215bf54cf83a
    # Parent  92ff2a879da9da1dbfadd3598eca7d67eaa146da
    #10193: Create the category of GradedEnumeratedSets
    
    TODO: putting Partitions() in GradedEnumeratedSets implements
    Partitions().an_element(). This changes the value of an_element for
    all symmetric functions, which breaks a lot of tests ...  One
    workaround would be to redefine an_element in symmetric functions to
    be ModulesWithBasis.parent_class.an_element(self) - 1.
    
    diff --git a/doc/en/reference/categories.rst b/doc/en/reference/categories.rst
    a b  
    128128   sage/categories/semigroups
    129129   sage/categories/semirings
    130130   sage/categories/sets_cat
     131   sage/categories/sets_with_grading
    131132   sage/categories/unique_factorization_domains
    132133   sage/categories/vector_spaces
    133134   sage/categories/weyl_groups
  • sage/categories/all.py

    diff --git a/sage/categories/all.py b/sage/categories/all.py
    a b  
    2828from pointed_sets import PointedSets
    2929
    3030from sets_with_partial_maps import SetsWithPartialMaps
     31from sets_with_grading import SetsWithGrading
     32
    3133from groupoid import Groupoid
    3234
    3335# enumerated sets
  • new file sage/categories/examples/sets_with_grading.py

    diff --git a/sage/categories/examples/sets_with_grading.py b/sage/categories/examples/sets_with_grading.py
    new file mode 100644
    - +  
     1r"""
     2An example of graded set: non-negative integers graded by themselves.
     3"""
     4
     5from sage.structure.parent import Parent
     6from sage.structure.unique_representation import UniqueRepresentation
     7from sage.categories.sets_with_grading import SetsWithGrading
     8
     9from sage.rings.integer_ring import IntegerRing
     10from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
     11
     12class NonNegativeIntegers(UniqueRepresentation, Parent):
     13    r"""
     14    Non negative integers graded by themselves.
     15
     16    EXAMPLES::
     17
     18        sage: E = SetsWithGrading().example()
     19        sage: E
     20        Non negative integers
     21        sage: E.graded_component(0)
     22        {0}
     23        sage: E.graded_component(100)
     24        {100}
     25    """
     26    def __init__(self):
     27        r"""
     28        TESTS::
     29
     30            sage: TestSuite(SetsWithGrading().example()).run()
     31        """
     32        Parent.__init__(self, category=SetsWithGrading(), facade=IntegerRing())
     33
     34    def an_element(self):
     35        r"""
     36        Returns 0.
     37
     38        EXAMPLES::
     39
     40            sage: SetsWithGrading().example().an_element()
     41            0
     42        """
     43        return 0
     44
     45    def _repr_(self):
     46        r"""
     47        TESTS::
     48
     49            sage: SetsWithGrading().example() # indirect example
     50            Non negative integers
     51        """
     52        return "Non negative integers"
     53
     54    def graded_component(self, grade):
     55        r"""
     56        Returns the component with grade ``grade``.
     57
     58        EXAMPLES::
     59
     60            sage: N = SetsWithGrading().example()
     61            sage: N.graded_component(65)
     62            {65}
     63        """
     64        return FiniteEnumeratedSet([grade])
     65
     66    def grading(self, elt):
     67        r"""
     68        Returns the grade of ``elt``.
     69
     70        EXAMPLES::
     71
     72            sage: N = SetsWithGrading().example()
     73            sage: N.grading(10)
     74            10
     75        """
     76        return elt
     77
     78    def generating_series(self, var='z'):
     79        r"""
     80        Returns `1 / (1-z)`.
     81
     82
     83        EXAMPLES::
     84
     85            sage: N = SetsWithGrading().example(); N
     86            Non negative integers
     87            sage: f = N.generating_series(); f
     88            1/(-z + 1)
     89            sage: LaurentSeriesRing(ZZ,'z')(f)
     90            1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + z^8 + z^9 + z^10 + z^11 + z^12 + z^13 + z^14 + z^15 + z^16 + z^17 + z^18 + z^19 + O(z^20)
     91        """
     92        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
     93        from sage.rings.integer import Integer
     94        R = PolynomialRing(IntegerRing(), var)
     95        z = R.gen()
     96        return Integer(1) / (Integer(1)-z)
     97
     98Example = NonNegativeIntegers
  • new file sage/categories/sets_with_grading.py

    diff --git a/sage/categories/sets_with_grading.py b/sage/categories/sets_with_grading.py
    new file mode 100644
    - +  
     1r"""
     2Sets With a Grading
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010-2012 Nicolas M. Thiery <nthiery at users.sf.net>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.misc.cachefunc import cached_method
     12from sage.misc.abstract_method import abstract_method
     13from category_types import Category
     14from sage.categories.sets_cat import Sets
     15from sage.categories.enumerated_sets import EnumeratedSets
     16from sage.sets.non_negative_integers import NonNegativeIntegers
     17
     18class SetsWithGrading(Category):
     19    r"""
     20    The category of sets with a grading.
     21
     22    An *set with a grading* is a set `S` equipped with a
     23    grading by some other set `I` (by default the set `\NN` of the non
     24    negative integers):
     25
     26    .. math::
     27
     28         S = \biguplus_{i\in I} S_i
     29
     30    where the *graded components* `S_i` are (usually finite)
     31    sets. The *grading* function maps each element `s` of
     32    `S` to its *grade* `i`, so that `s\in S_i`.
     33
     34    From implementation point of vue, if the graded set is enumerated then each
     35    graded component should be enumerated (there is a check in the method
     36    :meth:`~SetsWithGrading.ParentMethods._test_graded_components`). The
     37    contrary needs not be true.
     38
     39    EXAMPLES:
     40
     41    A typical example of set with grading is the set of non-negative integers
     42    graded by themselves::
     43
     44        sage: N = SetsWithGrading().example(); N
     45        Non negative integers
     46        sage: N.category()
     47        Category of facade sets with grading
     48
     49    It is graded by `\NN`::
     50
     51        sage: N.grading_set()
     52        Non negative integers
     53
     54    The *grading function* is given by ``N.grading``::
     55
     56        sage: N.grading(4)
     57        4
     58
     59    The graded component `S_i` is the set of all integer partitions of
     60    `i`::
     61
     62        sage: N.graded_component(grade = 5)
     63        {5}
     64        sage: N.graded_component(grade = 42)
     65        {42}
     66
     67    Here are some information about this category::
     68
     69        sage: SetsWithGrading()
     70        Category of sets with grading
     71        sage: SetsWithGrading().super_categories()
     72        [Category of sets]
     73        sage: SetsWithGrading().all_super_categories()
     74        [Category of sets with grading,
     75         Category of sets,
     76         Category of sets with partial maps,
     77         Category of objects]
     78
     79    .. TODO::
     80
     81        - This should be moved to Sets().WithGrading()
     82        - Should the grading set be a parameter for this category?
     83        - Does the enumeration need to be compatible with the grading? Be
     84          careful that the fact that graded components are allowed to be finite
     85          or infinite make the answer complicated.
     86
     87    TESTS::
     88
     89        sage: C = SetsWithGrading()
     90        sage: TestSuite(C).run()
     91    """
     92
     93    @cached_method
     94    def super_categories(self):
     95        """
     96        EXAMPLES::
     97
     98            sage: SetsWithGrading().super_categories()
     99            [Category of sets]
     100        """
     101        return [Sets()]
     102
     103    class ParentMethods:
     104
     105        def _test_graded_components(self, **options):
     106            r"""
     107            Test that some graded components of ``self`` are parent with
     108            initialized category and that the parent has a properly implemented
     109            ``grading`` method.
     110
     111            EXAMPLES::
     112
     113                sage: SetsWithGrading().example()._test_graded_components()
     114            """
     115            tester = self._tester(**options)
     116            for grade in self.grading_set().some_elements():
     117                G = self.graded_component(grade)
     118                if self in EnumeratedSets():
     119                    tester.assertTrue(G in EnumeratedSets())
     120                else:
     121                    tester.assertTrue(G in Sets())
     122                for elt in G.some_elements():
     123                    tester.assertEqual(self.grading(elt), grade)
     124
     125        def grading_set(self):
     126            """
     127            Returns the set ``self`` is graded by. By default, this is
     128            the set of non negative integers.
     129
     130            EXAMPLES::
     131
     132                sage: SetsWithGrading().example().grading_set()
     133                Non negative integers
     134            """
     135            return NonNegativeIntegers()
     136
     137        # TODO:
     138        #  - Should this method be in EnumeratedSets? With a default implementation
     139        #    a la ``filter``?
     140        #  - Do we want to enforce implementing subset rather than graded_component?
     141        @abstract_method(optional=True)
     142        def subset(self, *args, **options):
     143            """
     144            Returns the subset of ``self`` described by the given parameters
     145
     146            See also: :meth:`graded_component`
     147
     148            EXAMPLES::
     149
     150                sage: W = WeightedIntegerVectors([3,2,1]); W
     151                Integer vectors weighted by [3, 2, 1]
     152                sage: W.subset(4)
     153                Integer vectors of 4 weighted by [3, 2, 1]
     154            """
     155
     156        def graded_component(self, grade):
     157            """
     158            Return the graded component of ``self`` with grade ``grade``.
     159
     160            The default implementation just calls the method :meth:`subset` with
     161            the argument ``grade``.
     162
     163            EXAMPLES::
     164
     165                sage: N = SetsWithGrading().example(); N
     166                Non negative integers
     167                sage: N.graded_component(3)
     168                {3}
     169            """
     170            return self.subset(grade)
     171
     172        def grading(self, elt):
     173            """
     174            Return the grading of the element ``elt`` of self.`
     175
     176            This default implementation calls ``elt.grade()``.
     177
     178            EXAMPLES::
     179
     180                sage: N = SetsWithGrading().example(); N
     181                Non negative integers
     182                sage: N.grading(4)
     183                4
     184            """
     185            return elt.grade()
     186
     187        def generating_series(self):
     188            """
     189            Default implementation for generating series.
     190
     191            OUTPUT: a series, indexed by the grading set
     192
     193            EXAMPLES::
     194
     195                sage: N = SetsWithGrading().example(); N
     196                Non negative integers
     197                sage: N.generating_series()
     198                1/(-z + 1)
     199            """
     200            from sage.combinat.species.series import LazyPowerSeriesRing
     201            from sage.rings.integer_ring import ZZ
     202            R = LazyPowerSeriesRing(ZZ)
     203            R(self.graded_component(grade).cardinality() for grade in self.grading_set())
     204
     205        # TODO:
     206        #   * asymptotic behavior: we need an object for asymptotic behavior and
     207        #   a default name for the method that should be here. Such method will
     208        #   have two goals (and perhaps need two implementations): give a
     209        #   theorem on asymptotic and be a tool to determine a strategy for
     210        #   algorithms.
     211
  • sage/combinat/integer_vector_weighted.py

    diff --git a/sage/combinat/integer_vector_weighted.py b/sage/combinat/integer_vector_weighted.py
    a b  
    11"""
    22Weighted Integer Vectors
    33
     4AUTHORS:
     5
     6 - Mike Hansen (2007): initial version, ported from MuPAD-Combinat
     7 - Nicolas M. Thiery (2010-10-30): WeightedIntegerVectors(weights) + cleanup
     8
    49.. WARNING::
    510
    611   The list(self) function in this file used the :class:`Permutation_class` class improperly, returning
    712   the list of, generally speaking, invalid permutations (repeated entries, including 0).
    813"""
    914#*****************************************************************************
    10 #       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     15#  Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>
     16#                2010 Nicolas M. Thiery <nthiery at users.sf.net>
    1117#
    1218#  Distributed under the terms of the GNU General Public License (GPL)
    1319#
    14 #    This code is distributed in the hope that it will be useful,
    15 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    16 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    17 #    General Public License for more details.
    18 #
    19 #  The full text of the GPL is available at:
    20 #
    2120#                  http://www.gnu.org/licenses/
    2221#*****************************************************************************
    23 
    24 from combinat import CombinatorialClass
     22from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     23from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
     24from sage.categories.sets_with_grading import SetsWithGrading
    2525from __builtin__ import list as builtinlist
    2626from sage.rings.integer import Integer
     27from sage.structure.unique_representation import UniqueRepresentation
     28from sage.structure.parent import Parent
     29from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
    2730from sage.combinat.words.word import Word
    2831from permutation import Permutation_class
    2932
    30 def WeightedIntegerVectors(n, weight):
     33def WeightedIntegerVectors(n = None, weight = None):
    3134    """
    32     Returns the combinatorial class of integer vectors of n weighted by
    33     weight, that is, the nonnegative integer vectors `(v_1,\\dots,v_{length(weight)})`
    34     satisfying `\\sum_i v_i weight[i]==n`.
    35    
     35    Returns the combinatorial class of integer vectors of ``n``
     36    weighted by ``weight``, that is, the nonnegative integer vectors
     37    `(v_1,\\dots,v_{length(weight)})` satisfying `\\sum_i v_i
     38    weight[i]==n`.
     39
     40    INPUT:
     41
     42     - ``n`` -- a non negative integer (optional)
     43
     44     - ``weight`` -- a tuple (or list or iterable) of positive integers
     45
    3646    EXAMPLES::
    37    
     47
    3848        sage: WeightedIntegerVectors(8, [1,1,2])
    3949        Integer vectors of 8 weighted by [1, 1, 2]
    4050        sage: WeightedIntegerVectors(8, [1,1,2]).first()
     
    4555        25
    4656        sage: WeightedIntegerVectors(8, [1,1,2]).random_element()
    4757        [1, 1, 3]
     58
     59        sage: WeightedIntegerVectors([1,1,2])
     60        Integer vectors weighted by [1, 1, 2]
     61        sage: WeightedIntegerVectors([1,1,2]).cardinality()
     62        +Infinity
     63        sage: WeightedIntegerVectors([1,1,2]).first()
     64        [0, 0, 0]
     65
     66    TODO: should the order of the arguments ``n`` and ``weight`` be
     67    exchanged to simplify the logic?
    4868    """
    49     return WeightedIntegerVectors_nweight(n, weight)
     69    if weight is None and n is not None:
     70        weight = n
     71        n = None
     72    assert weight is not None, "weights should be specified"
     73    weight = tuple(weight)
     74    if n is None:
     75        return WeightedIntegerVectors_all(weight)
     76    else:
     77        return WeightedIntegerVectors_nweight(n, weight)
    5078
    51 class WeightedIntegerVectors_nweight(CombinatorialClass):
     79class WeightedIntegerVectors_all(DisjointUnionEnumeratedSets):
     80    r"""
     81    Set of weighted integer vectors.
     82
     83    EXAMPLES::
     84
     85        sage: W = WeightedIntegerVectors([3,1,1,2,1]); W
     86        Integer vectors weighted by [3, 1, 1, 2, 1]
     87        sage: W.cardinality()
     88        +Infinity
     89
     90        sage: W12 = W.graded_component(12)
     91        sage: W12.an_element()
     92        [4, 0, 0, 0, 0]
     93        sage: W12.last()
     94        [0, 12, 0, 0, 0]
     95        sage: W12.cardinality()
     96        441
     97        sage: for w in W12: print w
     98        [4, 0, 0, 0, 0]
     99        [3, 0, 0, 1, 1]
     100        [3, 0, 1, 1, 0]
     101        ...
     102        [0, 11, 1, 0, 0]
     103        [0, 12, 0, 0, 0]
     104    """
     105    def __init__(self, weights):
     106        """
     107        TESTS::
     108
     109            sage: C = WeightedIntegerVectors([2,1,3])
     110            sage: C.__class__
     111            <class 'sage.combinat.integer_vector_weighted.WeightedIntegerVectors_all_with_category'>
     112            sage: C.category()
     113            Join of Category of sets with grading and Category of infinite enumerated sets
     114            sage: TestSuite(C).run()
     115        """
     116        self._weights = weights
     117        from sage.sets.all import Family, NonNegativeIntegers
     118        # Use "partial" to make the basis function (with the weights
     119        # argument specified) pickleable.  Otherwise, it seems to
     120        # cause problems...
     121        from functools import partial
     122        F = Family(NonNegativeIntegers(), partial(WeightedIntegerVectors, weight = weights))
     123        DisjointUnionEnumeratedSets.__init__(self, F, facade=True, keepkey=False,
     124                                             category = (SetsWithGrading(), InfiniteEnumeratedSets()))
     125
     126    def _repr_(self):
     127        """
     128        EXAMPLES::
     129
     130            sage: WeightedIntegerVectors([2,1,3])
     131            Integer vectors weighted by [2, 1, 3]
     132        """
     133        return "Integer vectors weighted by %s"%list(self._weights)
     134
     135    def __contains__(self, x):
     136        """
     137        EXAMPLES::
     138
     139            sage: [] in WeightedIntegerVectors([])
     140            True
     141            sage: [3,0,0] in WeightedIntegerVectors([2,1,1])
     142            True
     143            sage: [3,0] in WeightedIntegerVectors([2,1,1])
     144            False
     145            sage: [3,-1,0] in WeightedIntegerVectors([2,1,1])
     146            False
     147        """
     148        return isinstance(x, (builtinlist, Permutation_class)) and \
     149            len(x) == len(self._weights)   and \
     150            all(isinstance(i, (int, Integer)) and i>=0 for i in x)
     151
     152    def subset(self, size = None):
     153        """
     154
     155        EXAMPLES::
     156
     157            sage: C = WeightedIntegerVectors([2,1,3])
     158            sage: C.subset(4)
     159            Integer vectors of 4 weighted by [2, 1, 3]
     160        """
     161        if size is None:
     162            return self
     163        return self._family[size]
     164
     165    def grading(self, x): # or degree / grading
     166        """
     167        EXAMPLES::
     168
     169            sage: C = WeightedIntegerVectors([2,1,3])
     170            sage: C.grading((2,1,1))
     171            8
     172        """
     173        return sum([exp*deg for exp,deg in zip(x, self._weights)])
     174
     175class WeightedIntegerVectors_nweight(UniqueRepresentation, Parent):
    52176    def __init__(self, n, weight):
    53177        """
    54178        TESTS::
    55        
    56             sage: WIV = WeightedIntegerVectors(8, [1,1,2])
    57             sage: WIV == loads(dumps(WIV))
    58             True
     179
     180            sage: C = WeightedIntegerVectors(8, [1,1,2])
     181            sage: C.__class__
     182            <class 'sage.combinat.integer_vector_weighted.WeightedIntegerVectors_nweight_with_category'>
     183            sage: TestSuite(C).run()
    59184        """
    60         self.n = n
    61         self.weight = weight
    62        
    63     def __repr__(self):
     185        Parent.__init__(self, category = FiniteEnumeratedSets())
     186        self._n = n
     187        self._weights = weight
     188
     189    def _repr_(self):
    64190        """
    65191        TESTS::
    66        
     192
    67193            sage: repr(WeightedIntegerVectors(8, [1,1,2]))
    68194            'Integer vectors of 8 weighted by [1, 1, 2]'
    69195        """
    70         return "Integer vectors of %s weighted by %s"%(self.n, self.weight)
     196        return "Integer vectors of %s weighted by %s"%(self._n, list(self._weights))
    71197
    72198    def __contains__(self, x):
    73199        """
    74         TESTS::
    75        
     200        EXAMPLES::
     201
    76202            sage: [] in WeightedIntegerVectors(0, [])
    77203            True
    78204            sage: [] in WeightedIntegerVectors(1, [])
     
    96222            sage: [0] in WeightedIntegerVectors(0, [])
    97223            False
    98224        """
    99         if not isinstance(x, builtinlist):
     225        if not isinstance(x, (builtinlist, Permutation_class)):
    100226            return False
    101         if len(self.weight) != len(x):
     227        if len(self._weights) != len(x):
    102228            return False
    103229        s = 0
    104230        for i in range(len(x)):
    105231            if not isinstance(x[i], (int, Integer)):
    106232                return False
    107             s += x[i]*self.weight[i]
    108         if s != self.n:
     233            s += x[i]*self._weights[i]
     234        if s != self._n:
    109235            return False
    110236
    111237        return True
     
    113239    def _recfun(self, n, l):
    114240        """
    115241        EXAMPLES::
    116        
     242
    117243            sage: w = WeightedIntegerVectors(3, [2,1,1])
    118             sage: w._recfun(3, [1,1,2])
     244            sage: list(w._recfun(3, [1,1,2]))
    119245            [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]]
    120246        """
    121         result = []
    122247        w = l[-1]
    123248        l = l[:-1]
    124249        if l == []:
    125250            d = int(n) / int(w)
    126             if n%w == 0:
    127                 return [[d]]
    128             else:
    129                 return [] #bad branch...
    130            
     251            if n % w == 0:
     252                yield [d]
     253                # Otherwise: bad branch
     254            return
     255
    131256        for d in range(int(n)/int(w), -1, -1):
    132             result += [ x + [d] for x in self._recfun(n-d*w, l) ]
     257            for x in self._recfun(n-d*w, l):
     258                yield x + [d]
    133259
    134         return result
    135    
    136     def list(self):
     260    def __iter__(self):
    137261        """
    138262        TESTS::
    139        
     263
    140264            sage: WeightedIntegerVectors(7, [2,2]).list()
    141265            []
    142266            sage: WeightedIntegerVectors(3, [2,1,1]).list()
    143267            [[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]]
    144        
     268
    145269        ::
    146        
     270
    147271            sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ]
    148272            sage: iv  = [ IntegerVectors(k, 3) for k in range(11) ]
    149273            sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] )
    150274            True
    151        
     275
    152276        ::
    153        
     277
    154278            sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ]
    155279            sage: all( [ i.cardinality() == len(i.list()) for i in ivw] )
    156280            True
    157281        """
    158         if len(self.weight) == 0:
     282        if len(self._weights) == 0:
    159283            if self.n == 0:
    160                 return [[]]
    161             else:
    162                 return []
     284                yield []
     285            return
    163286
    164         perm = Word(self.weight).standard_permutation()
    165         l = [x for x in sorted(self.weight)]
    166         return [perm.action(_) for _ in self._recfun(self.n,l)]
     287        perm = Word(self._weights).standard_permutation()
     288        l = [x for x in sorted(self._weights)]
     289        for x in self._recfun(self._n, l):
     290            yield perm.action(x)
     291            #_left_to_right_multiply_on_right(Permutation_class(x))
     292