Ticket #6637: trac_6637_recursive_set-sl.patch

File trac_6637_recursive_set-sl.patch, 15.8 KB (added by slabbe, 8 years ago)
  • sage/combinat/backtrack.py

    # HG changeset patch
    # User Sebastien Labbe <slabqc at gmail.com>
    # Date 1360530054 -3600
    # Node ID 2b3e05c461cf43c650572ca7b515a38950e4c128
    # Parent  c8fd736f752526aacfe9fdbe71468d457aec54e4
    Some fixes to TransitiveIdeal and TransitiveIdealGraded, added RecursiveSet factory
    
    diff --git a/sage/combinat/backtrack.py b/sage/combinat/backtrack.py
    a b tree or graph structure. 
    99  search through a tree described by a ``children`` function.
    1010- :class:`GenericBacktracker`: Depth first search through a tree
    1111  described by a ``children`` function, with branch pruning, etc.
    12 - :class:`TransitiveIdeal`: Depth first search through a
     12
     13Old classes:
     14
     15- :class:`TransitiveIdeal`: Depth and breadth first search through a
    1316  graph described by a ``neighbours`` relation.
    14 - :class:`TransitiveIdealGraded`: Breath first search
     17- :class:`TransitiveIdealGraded`: Breadth first search
    1518  through a graph described by a ``neighbours`` relation.
    1619
    1720TODO:
    CommutativeAdditiveSemigroups) 
    5255from sage.structure.unique_representation import UniqueRepresentation
    5356from sage.rings.integer_ring import ZZ
    5457from sage.misc.sage_itertools import imap_and_filter_none
     58from sage.structure.sage_object import SageObject
    5559
    5660class GenericBacktracker(object):
    5761    r"""
    class TransitiveIdeal(): 
    809813
    810814    def __iter__(self):
    811815        r"""
    812         Returns an iterator on the elements of self.
     816        Returns an iterator on the elements of self (depth first).
    813817
    814818        TESTS::
    815819
    class TransitiveIdeal(): 
    817821            sage: list(C) # indirect doctest
    818822            []
    819823
     824        """
     825        return self.depth_first_search_iterator()
     826
     827    def breadth_first_search_iterator(self, max_depth=float("inf")):
     828        r"""
     829        Returns an iterator on the elements of self (breadth first).
     830
     831        INPUT:
     832
     833        - ``max_depth`` -- (Default: infinity) Specifies the
     834            maximal depth to which elements are computed
     835
     836        TESTS::
     837
     838            sage: C = TransitiveIdeal(lambda x: [1,2], ())
     839            sage: list(C.breadth_first_search_iterator())
     840            []
     841
     842        ::
     843
    820844            sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
    821             sage: list(C) # indirect doctest
     845            sage: list(C.breadth_first_search_iterator())
     846            [1, 2]
     847
     848        ::
     849
     850            sage: C = TransitiveIdeal(lambda x: [], (1,2))
     851            sage: list(C.breadth_first_search_iterator())
     852            [1, 2]
     853
     854        ::
     855
     856            sage: fn = lambda i: [i+1] if i<10 else []
     857            sage: C = TransitiveIdeal(fn, [0])
     858            sage: list(C.breadth_first_search_iterator(max_depth=1))
     859            [0, 1]
     860        """
     861        current_level = self._generators
     862        known = set(current_level)
     863        depth = 0
     864        while len(current_level) > 0 and depth <= max_depth:
     865            next_level = set()
     866            for x in current_level:
     867                yield x
     868                for y in self._succ(x):
     869                    if y == None or y in known:
     870                        continue
     871                    next_level.add(y)
     872                    known.add(y)
     873            current_level = next_level
     874            depth += 1
     875    def depth_first_search_iterator(self):
     876        r"""
     877        Returns an iterator on the elements of self (depth first).
     878
     879        TESTS::
     880
     881            sage: C = TransitiveIdeal(lambda x: [1,2], ())
     882            sage: list(C.depth_first_search_iterator())
     883            []
     884
     885            sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
     886            sage: list(C.depth_first_search_iterator())
    822887            [1, 2]
    823888
    824889            sage: C = TransitiveIdeal(lambda x: [], (1,2))
    825             sage: list(C) # indirect doctest
     890            sage: list(C.depth_first_search_iterator())
    826891            [1, 2]
    827892
    828893        """
    class TransitiveIdealGraded(TransitiveId 
    854919
    855920    Returns the set `S` of elements that can be obtained by repeated
    856921    application of ``relation`` on the elements of ``generators``.
    857      
     922
    858923    Consider ``relation`` as modeling a directed graph (possibly with
    859924    loops, cycles, or circuits). Then `S` is the ideal generated by
    860925    ``generators`` under this relation.
    class TransitiveIdealGraded(TransitiveId 
    899964    [2,1,3,4] in the permutohedron::
    900965
    901966          sage: [p for p in TransitiveIdealGraded(attrcall("permutohedron_succ"), [Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
    902           [[3, 1, 2, 4], [2, 1, 3, 4], [2, 1, 4, 3], [3, 2, 1, 4], [2, 3, 1, 4], [3, 1, 4, 2], [2, 3, 4, 1], [3, 4, 1, 2], [3, 2, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [4, 3, 1, 2], [4, 2, 1, 3], [3, 4, 2, 1], [4, 2, 3, 1], [4, 3, 2, 1]] 
     967          [[3, 1, 2, 4], [2, 1, 3, 4], [2, 1, 4, 3], [3, 2, 1, 4], [2, 3, 1, 4], [3, 1, 4, 2], [2, 3, 4, 1], [3, 4, 1, 2], [3, 2, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [4, 3, 1, 2], [4, 2, 1, 3], [3, 4, 2, 1], [4, 2, 3, 1], [4, 3, 2, 1]]
    903968
    904969    """
    905970    def __init__(self, succ, generators, max_depth=float("inf")):
    class TransitiveIdealGraded(TransitiveId 
    923988
    924989        TESTS::
    925990
    926             sage: C = TransitiveIdeal(lambda x: [1,2], ())
     991            sage: C = TransitiveIdealGraded(lambda x: [1,2], ())
    927992            sage: list(C) # indirect doctest
    928993            []
    929994
    930             sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
     995            sage: C = TransitiveIdealGraded(lambda x: [1,2], (1,))
    931996            sage: list(C) # indirect doctest
    932997            [1, 2]
    933998
    934             sage: C = TransitiveIdeal(lambda x: [], (1,2))
     999            sage: C = TransitiveIdealGraded(lambda x: [], (1,2))
    9351000            sage: list(C) # indirect doctest
    9361001            [1, 2]
    9371002
    938             sage: [i for i in TransitiveIdealGraded(lambda i: [i+1] if i<10 else [], [0], max_depth=1).__iter__()]
     1003        ::
     1004
     1005            sage: fn = lambda i: [i+1] if i<10 else []
     1006            sage: C = TransitiveIdealGraded(fn, [0], max_depth=1)
     1007            sage: list(C)
    9391008            [0, 1]
    9401009        """
    941         current_level = self._generators
    942         known = set(current_level)
    943         depth = 0
    944         while len(current_level) > 0 and depth <= self._max_depth:
    945             next_level = set()
    946             for x in current_level:
    947                 yield x
    948                 for y in self._succ(x):
    949                     if y == None or y in known:
    950                         continue
    951                     next_level.add(y)
    952                     known.add(y)
    953             current_level = next_level
    954             depth += 1
    955         return
     1010        return self.breadth_first_search_iterator(max_depth=self._max_depth)
    9561011
     1012from sage.misc.classcall_metaclass import ClasscallMetaclass
     1013class RecursiveSet(SageObject):
     1014    r"""
     1015    INPUT:
     1016
     1017    - ``roots``: list (or iterable)
     1018    - ``relation``: function (or callable) returning a list (or iterable)
     1019    - ``structure``: string (default: ``None``), structure of the
     1020      relation, possible values are:
     1021
     1022      - ``"graded"`` - if the relation is graded
     1023      - ``"symmetric"`` - if the relation is symmetric
     1024      - ``"forest"`` - if the relation generates a forest
     1025      - ``None`` - nothing is known about the relation
     1026
     1027    EXAMPLES:
     1028
     1029    A recursive set with no other information::
     1030
     1031        sage: from sage.combinat.backtrack import RecursiveSet
     1032        sage: f = lambda i: [mod(i+2,10)]
     1033        sage: C = RecursiveSet([0], f)
     1034        sage: C
     1035        <sage.combinat.backtrack.TransitiveIdeal instance at ...>
     1036        sage: list(C)
     1037        [0, 2, 4, 6, 8]
     1038
     1039    A recursive set with a forest structure::
     1040
     1041        sage: f = lambda a: [2*a,2*a+1]
     1042        sage: C = RecursiveSet([1], f, structure='forest')
     1043        sage: C
     1044        An enumerated set with a forest structure
     1045        sage: it = C.depth_first_search_iterator()
     1046        sage: [next(it) for _ in range(7)]
     1047        [1, 2, 4, 8, 16, 32, 64]
     1048        sage: it = C.breadth_first_search_iterator()
     1049        sage: [next(it) for _ in range(7)]
     1050        [1, 2, 3, 4, 5, 6, 7]
     1051
     1052    A recursive set given by a symmetric relation::
     1053
     1054        sage: f = lambda a: [a-1,a+1]
     1055        sage: C = RecursiveSet([10, 15], f, structure='symmetric')
     1056        sage: C
     1057        An enumerated set with a symmetric relation
     1058        sage: it = iter(C)
     1059        sage: [next(it) for _ in range(7)]
     1060        [10, 15, 16, 9, 11, 14, 8]
     1061
     1062    A recursive set given by a graded relation::
     1063
     1064        sage: f = lambda a: [a+1, a+I]
     1065        sage: C = RecursiveSet([0], f, structure='graded')
     1066        sage: C
     1067        An enumerated set with a graded relation
     1068        sage: it = iter(C)                  # todo: not implemented
     1069        sage: [next(it) for _ in range(7)]  # todo: not implemented
     1070        [0, 1, I, I + 1, 2, 2*I, I + 2]
     1071
     1072    TESTS::
     1073
     1074        sage: f = lambda a: [a-1,a+1]
     1075        sage: C = RecursiveSet([1], f, structure='symmetric')
     1076        sage: isinstance(C, RecursiveSet)
     1077        True
     1078
     1079    ::
     1080
     1081        sage: C = RecursiveSet((1, 2, 3), factor)
     1082        sage: C._succ
     1083        <function factor at ...>
     1084        sage: C._generators
     1085        (1, 2, 3)
     1086        sage: loads(dumps(C))   # should test for equality with C, but equality is not implemented
     1087    """
     1088    __metaclass__ = ClasscallMetaclass
     1089    @staticmethod
     1090    def __classcall_private__(cls, roots, relation, structure=None,
     1091            algorithm='depth', post_process=None, facade=None, category=None):
     1092        r"""
     1093        EXAMPLES::
     1094        """
     1095        if structure is None:
     1096            return TransitiveIdeal(relation, roots)
     1097        elif structure == 'symmetric':
     1098            return RecursiveSet_symmetric(roots, relation)
     1099        elif structure == 'forest':
     1100            return SearchForest(roots=roots, children=relation,
     1101                 post_process=post_process, algorithm=algorithm, facade=facade,
     1102                 category=category)
     1103        elif structure == 'graded':
     1104            return RecursiveSet_graded(roots, relation)
     1105        else:
     1106            raise ValueError("Unknown value for structure (=%s)" % structure)
     1107
     1108class RecursiveSet_symmetric(RecursiveSet):
     1109    r"""
     1110    Generic tool for constructing ideals of a symmetric relation.
     1111
     1112    INPUT:
     1113
     1114    - ``roots``: a list (or iterable)
     1115    - ``relation``: a function (or callable) returning a list (or iterable)
     1116
     1117    EXAMPLES::
     1118
     1119        sage: from sage.combinat.backtrack import RecursiveSet
     1120        sage: f = lambda a: [a-1,a+1]
     1121        sage: C = RecursiveSet([0], f, structure='symmetric')
     1122        sage: C
     1123        An enumerated set with a symmetric relation
     1124        sage: it = iter(C)
     1125        sage: [next(it) for _ in range(7)]
     1126        [0, 1, -1, 2, -2, 3, -3]
     1127
     1128    """
     1129    def __init__(self, roots, relation):
     1130        r"""
     1131        TESTS::
     1132
     1133            sage: from sage.combinat.backtrack import RecursiveSet
     1134            sage: f = lambda a: [a-1,a+1]
     1135            sage: C = RecursiveSet([0], f, structure='symmetric')
     1136            sage: C
     1137            An enumerated set with a symmetric relation
     1138            sage: C._relation
     1139            <function <lambda> at ...>
     1140            sage: C._roots
     1141            [0]
     1142
     1143        Fix this::
     1144
     1145            sage: loads(dumps(C))
     1146            Traceback (most recent call last):
     1147            ...
     1148            PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
     1149
     1150        """
     1151        self._roots = roots
     1152        self._relation = relation
     1153
     1154    def _repr_(self):
     1155        r"""
     1156        TESTS::
     1157
     1158            sage: from sage.combinat.backtrack import RecursiveSet
     1159            sage: RecursiveSet([1], lambda x: [x+1], structure='symmetric')
     1160            An enumerated set with a symmetric relation
     1161        """
     1162        return "An enumerated set with a symmetric relation"
     1163    def __iter__(self):
     1164        r"""
     1165        Returns an iterator on the elements of self (breadth first).
     1166
     1167        EXAMPLES::
     1168
     1169            sage: from sage.combinat.backtrack import RecursiveSet
     1170            sage: f = lambda a: [a-1,a+1]
     1171            sage: S = RecursiveSet([10], f, structure='symmetric')
     1172            sage: it = iter(S)
     1173            sage: [next(it) for _ in range(7)]
     1174            [10, 9, 11, 8, 12, 13, 7]
     1175        """
     1176        return self.breadth_first_search_iterator()
     1177
     1178    def breadth_first_search_iterator(self):
     1179        r"""
     1180        Returns an iterator on the elements of self (breadth first).
     1181
     1182        EXAMPLES::
     1183
     1184            sage: from sage.combinat.backtrack import RecursiveSet
     1185            sage: f = lambda a: [a+1, a+I]
     1186            sage: S = RecursiveSet([0], f, structure='symmetric')
     1187            sage: it = iter(S)
     1188            sage: [next(it) for _ in range(7)]
     1189            [0, 1, I, I + 1, 2, 2*I, I + 2]
     1190        """
     1191        for level in self.level_iterator():
     1192            for a in level:
     1193                yield a
     1194
     1195    def elements_of_depth_iterator(self, depth=0):
     1196        r"""
     1197        Returns an iterator over the elements of ``self`` of given depth.
     1198        An element of depth `n` can be obtained applying `n` times the
     1199        children function from a root.
     1200
     1201        EXAMPLES::
     1202        """
     1203        pass
     1204
     1205    def level_iterator(self):
     1206        r"""
     1207        Returns an iterator over the levels of self.
     1208
     1209        OUTPUT:
     1210
     1211            an iterator of sets
     1212
     1213        EXAMPLES::
     1214
     1215            sage: from sage.combinat.backtrack import RecursiveSet
     1216            sage: f = lambda a: [a-1, a+1]
     1217            sage: S = RecursiveSet([10], f, structure='symmetric')
     1218            sage: it = S.level_iterator()
     1219            sage: [sorted(next(it)) for _ in range(5)]
     1220            [[10], [9, 11], [8, 12], [7, 13], [6, 14]]
     1221
     1222        Starting with two roots::
     1223
     1224            sage: f = lambda a: [a-1, a+1]
     1225            sage: S = RecursiveSet([5, 10], f, structure='symmetric')
     1226            sage: it = S.level_iterator()
     1227            sage: [sorted(next(it)) for _ in range(5)]
     1228            [[5, 10], [4, 6, 9, 11], [3, 7, 8, 12], [2, 13], [1, 14]]
     1229
     1230        Gaussian integers::
     1231
     1232            sage: f = lambda a: [a+1, a+I]
     1233            sage: S = RecursiveSet([0], f, structure='symmetric')
     1234            sage: it = S.level_iterator()
     1235            sage: [sorted(next(it)) for _ in range(7)]
     1236            [[0],
     1237             [1, I],
     1238             [I + 1, 2, 2*I],
     1239             [I + 2, 3, 2*I + 1, 3*I],
     1240             [I + 3, 4*I, 4, 3*I + 1, 2*I + 2],
     1241             [5*I, 5, I + 4, 3*I + 2, 2*I + 3, 4*I + 1],
     1242             [6*I, 2*I + 4, 4*I + 2, 5*I + 1, 6, I + 5, 3*I + 3]]
     1243        """
     1244        A = set()
     1245        B = self._roots
     1246        while len(B) > 0:
     1247            yield B
     1248            A,B = B, self._get_next_level(A, B)
     1249
     1250    def _get_next_level(self, A, B):
     1251        r"""
     1252        Return the set of elements of depth n+1.
     1253
     1254        INPUT:
     1255
     1256        - ``A`` - set, the set of elements of depth n-1
     1257        - ``B`` - set, the set of elements of depth n
     1258
     1259        OUTPUT:
     1260
     1261        - ``C`` - set, the set of elements of depth n+1
     1262
     1263        EXAMPLES::
     1264
     1265            sage: from sage.combinat.backtrack import RecursiveSet
     1266            sage: f = lambda a: [a-1, a+1]
     1267            sage: S = RecursiveSet([5, 10], f, structure='symmetric')
     1268            sage: sorted(S._get_next_level([2,8], [3,7]))
     1269            [4, 6]
     1270            sage: sorted(S._get_next_level([3,7], [2,8]))
     1271            [1, 9]
     1272        """
     1273        C = set()
     1274        for x in B:
     1275            for y in self._relation(x):
     1276                if (y is None or y in A or y in B):
     1277                    continue
     1278                C.add(y)
     1279        return C
     1280
     1281class RecursiveSet_graded(RecursiveSet):
     1282    def __init__(self, roots, relation):
     1283        r"""
     1284        TESTS::
     1285
     1286        """
     1287        self._roots = roots
     1288        self._relation = relation
     1289
     1290    def _repr_(self):
     1291        r"""
     1292        TESTS::
     1293
     1294            sage: from sage.combinat.backtrack import RecursiveSet
     1295            sage: RecursiveSet([1], lambda x: [x+1, x+I], structure='graded')
     1296            An enumerated set with a graded relation
     1297        """
     1298        return "An enumerated set with a graded relation"