diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd index 408fe26a15a..a71fa3cadd8 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd @@ -114,7 +114,7 @@ cdef inline int OP_find(OrbitPartition *OP, int n) noexcept: OP.parent[n] = OP_find(OP, OP.parent[n]) return OP.parent[n] -cdef inline int OP_join(OrbitPartition *OP, int m, int n) noexcept: +cdef inline void OP_join(OrbitPartition *OP, int m, int n) noexcept: """ Join the cells containing m and n, if they are different. """ diff --git a/src/sage/sets/disjoint_set.pxd b/src/sage/sets/disjoint_set.pxd index 1ec38f5966b..3c8351983e7 100644 --- a/src/sage/sets/disjoint_set.pxd +++ b/src/sage/sets/disjoint_set.pxd @@ -11,13 +11,25 @@ from sage.groups.perm_gps.partn_ref.data_structures cimport OrbitPartition from sage.structure.sage_object cimport SageObject +cpdef DisjointSet(arg) + cdef class DisjointSet_class(SageObject): cdef OrbitPartition *_nodes + cpdef cardinality(self) + cpdef number_of_subsets(self) cdef class DisjointSet_of_integers(DisjointSet_class): - pass + cpdef int find(self, int i) + cpdef void union(self, int i, int j) + cpdef root_to_elements_dict(self) + cpdef element_to_root_dict(self) + cpdef to_digraph(self) cdef class DisjointSet_of_hashables(DisjointSet_class): cdef list _int_to_el cdef dict _el_to_int - cdef DisjointSet_of_integers _d + cpdef find(self, e) + cpdef void union(self, e, f) + cpdef root_to_elements_dict(self) + cpdef element_to_root_dict(self) + cpdef to_digraph(self) diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index e8ae11fc3a1..ce44a37710a 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -6,7 +6,7 @@ The main entry point is :func:`DisjointSet` which chooses the appropriate type to return. For more on the data structure, see :func:`DisjointSet`. This module defines a class for mutable partitioning of a set, which -cannot be used as a key of a dictionary, vertex of a graph etc. For +cannot be used as a key of a dictionary, a vertex of a graph, etc. For immutable partitioning see :class:`SetPartition`. AUTHORS: @@ -14,6 +14,7 @@ AUTHORS: - Sébastien Labbé (2008) - Initial version. - Sébastien Labbé (2009-11-24) - Pickling support - Sébastien Labbé (2010-01) - Inclusion into sage (:issue:`6775`). +- Giorgos Mousa (2024-04-22): Optimize EXAMPLES: @@ -39,9 +40,9 @@ Disjoint set of hashables objects:: sage: d = DisjointSet('abcde') sage: d {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}} - sage: d.union('a','b') - sage: d.union('b','c') - sage: d.union('c','d') + sage: d.union('a', 'b') + sage: d.union('b', 'c') + sage: d.union('c', 'd') sage: d {{'a', 'b', 'c', 'd'}, {'e'}} sage: d.find('c') @@ -58,15 +59,14 @@ Disjoint set of hashables objects:: # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.rings.integer import Integer +from sage.rings.integer cimport Integer from sage.structure.sage_object cimport SageObject from cpython.object cimport PyObject_RichCompare from sage.groups.perm_gps.partn_ref.data_structures cimport * - -def DisjointSet(arg): +cpdef DisjointSet(arg): r""" - Constructs a disjoint set where each element of ``arg`` is in its + Construct a disjoint set where each element of ``arg`` is in its own set. If ``arg`` is an integer, then the disjoint set returned is made of the integers from ``0`` to ``arg - 1``. @@ -86,11 +86,11 @@ def DisjointSet(arg): INPUT: - - ``arg`` -- non negative integer or an iterable of hashable objects. + - ``arg`` -- nonnegative integer or an iterable of hashable objects EXAMPLES: - From a non-negative integer:: + From a nonnegative integer:: sage: DisjointSet(5) {{0}, {1}, {2}, {3}, {4}} @@ -101,7 +101,7 @@ def DisjointSet(arg): {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}} sage: DisjointSet(range(6)) {{0}, {1}, {2}, {3}, {4}, {5}} - sage: DisjointSet(['yi',45,'cheval']) + sage: DisjointSet(['yi', 45, 'cheval']) {{'cheval'}, {'yi'}, {45}} TESTS:: @@ -113,12 +113,12 @@ def DisjointSet(arg): sage: DisjointSet([]) {} - The argument must be a non negative integer:: + The argument must be a nonnegative integer:: sage: DisjointSet(-1) Traceback (most recent call last): ... - ValueError: arg (=-1) must be a non negative integer + ValueError: arg must be a nonnegative integer (-1 given) or an iterable:: @@ -136,12 +136,11 @@ def DisjointSet(arg): """ if isinstance(arg, (Integer, int)): if arg < 0: - raise ValueError('arg (=%s) must be a non negative integer' % arg) + raise ValueError('arg must be a nonnegative integer (%s given)' % arg) return DisjointSet_of_integers(arg) else: return DisjointSet_of_hashables(arg) - cdef class DisjointSet_class(SageObject): r""" Common class and methods for :class:`DisjointSet_of_integers` and @@ -149,24 +148,28 @@ cdef class DisjointSet_class(SageObject): """ def _repr_(self): r""" - Return ``self`` as a unique str. + Return ``self`` as a unique ``str``. EXAMPLES:: sage: e = DisjointSet(5) - sage: e.union(2,4); e._repr_() + sage: e.union(2, 4) + sage: e._repr_() '{{0}, {1}, {2, 4}, {3}}' sage: e = DisjointSet(5) - sage: e.union(4,2); e._repr_() + sage: e.union(4, 2) + sage: e._repr_() '{{0}, {1}, {2, 4}, {3}}' :: sage: e = DisjointSet(range(5)) - sage: e.union(2,4); e._repr_() + sage: e.union(2, 4) + sage: e._repr_() '{{0}, {1}, {2, 4}, {3}}' sage: e = DisjointSet(range(5)) - sage: e.union(4,2); e._repr_() + sage: e.union(4, 2) + sage: e._repr_() '{{0}, {1}, {2, 4}, {3}}' """ res = [] @@ -183,10 +186,9 @@ cdef class DisjointSet_class(SageObject): EXAMPLES:: sage: d = DisjointSet(4) - sage: d.union(2,0) + sage: d.union(2, 0) sage: sorted(d) [[0, 2], [1], [3]] - sage: d = DisjointSet('abc') sage: sorted(d) [['a'], ['b'], ['c']] @@ -211,10 +213,10 @@ cdef class DisjointSet_class(SageObject): :: - sage: d.union(0,3) - sage: d.union(3,4) - sage: e.union(4,0) - sage: e.union(3,0) + sage: d.union(0, 3) + sage: d.union(3, 4) + sage: e.union(4, 0) + sage: e.union(3, 0) sage: e == d True @@ -229,12 +231,12 @@ cdef class DisjointSet_class(SageObject): sage: d = DisjointSet('abcde') sage: e = DisjointSet('abcde') - sage: d.union('a','b') - sage: d.union('b','c') - sage: e.union('c','a') + sage: d.union('a', 'b') + sage: d.union('b', 'c') + sage: e.union('c', 'a') sage: e == d False - sage: e.union('a','b') + sage: e.union('a', 'b') sage: e == d True """ @@ -246,7 +248,43 @@ cdef class DisjointSet_class(SageObject): return NotImplemented return PyObject_RichCompare(s, t, op) - def cardinality(self): + def __dealloc__(self): + r""" + Deallocate ``self`` (i.e. the ``self._nodes``). + + EXAMPLES:: + + sage: d = DisjointSet(5) + sage: del d + sage: d = DisjointSet('abc') + sage: del d + """ + OP_dealloc(self._nodes) + + def __reduce__(self): + r""" + Return a tuple of three elements: + + - The function :func:`DisjointSet` + - Arguments for the function :func:`DisjointSet` + - The actual state of ``self``. + + EXAMPLES:: + + sage: d = DisjointSet(5) + sage: d.__reduce__() + (, (5,), [0, 1, 2, 3, 4]) + + :: + + sage: d.union(2, 4) + sage: d.union(1, 3) + sage: d.__reduce__() + (, (5,), [0, 1, 2, 1, 2]) + """ + return DisjointSet, (self._nodes.degree,), self.__getstate__() + + cpdef cardinality(self): r""" Return the number of elements in ``self``, *not* the number of subsets. @@ -267,7 +305,7 @@ cdef class DisjointSet_class(SageObject): """ return self._nodes.degree - def number_of_subsets(self): + cpdef number_of_subsets(self): r""" Return the number of subsets in ``self``. @@ -297,8 +335,8 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: d {{0}, {1}, {2}, {3}, {4}} - sage: d.union(2,4) - sage: d.union(0,2) + sage: d.union(2, 4) + sage: d.union(0, 2) sage: d {{0, 2, 4}, {1}, {3}} sage: d.find(2) @@ -312,18 +350,18 @@ cdef class DisjointSet_of_integers(DisjointSet_class): :: - sage: a.union(3,4) + sage: a.union(3, 4) sage: a == loads(dumps(a)) True """ def __init__(self, n): r""" - Construction of the DisjointSet where each element (integers from ``0`` + Construct the ``DisjointSet`` where each element (integers from ``0`` to ``n-1``) is in its own set. INPUT: - - ``n`` -- Non negative integer + - ``n`` -- nonnegative integer EXAMPLES:: @@ -336,40 +374,6 @@ cdef class DisjointSet_of_integers(DisjointSet_class): """ self._nodes = OP_new(n) - def __dealloc__(self): - r""" - Deallocates self, i.e. the self._nodes - - EXAMPLES:: - - sage: d = DisjointSet(5) - sage: del d - """ - OP_dealloc(self._nodes) - - def __reduce__(self): - r""" - Return a tuple of three elements: - - - The function :func:`DisjointSet` - - Arguments for the function :func:`DisjointSet` - - The actual state of ``self``. - - EXAMPLES:: - - sage: d = DisjointSet(5) - sage: d.__reduce__() - (, (5,), [0, 1, 2, 3, 4]) - - :: - - sage: d.union(2,4) - sage: d.union(1,3) - sage: d.__reduce__() - (, (5,), [0, 1, 2, 1, 2]) - """ - return DisjointSet, (self._nodes.degree,), self.__getstate__() - def __getstate__(self): r""" Return a list of the parent of each node from ``0`` to ``n-1``. @@ -379,28 +383,29 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: d.__getstate__() [0, 1, 2, 3, 4] - sage: d.union(2,3) + sage: d.union(2, 3) sage: d.__getstate__() [0, 1, 2, 2, 4] - sage: d.union(3,0) + sage: d.union(3, 0) sage: d.__getstate__() [2, 1, 2, 2, 4] - Other parents are obtained when the operations are done is a - distinct order:: + Other parents are obtained when the operations are done in a + different order:: sage: d = DisjointSet(5) - sage: d.union(0,3) + sage: d.union(0, 3) sage: d.__getstate__() [0, 1, 2, 0, 4] - sage: d.union(2,0) + sage: d.union(2, 0) sage: d.__getstate__() [0, 1, 0, 0, 4] """ - l = [] + cdef Py_ssize_t card = self._nodes.degree + cdef list l = [None] * card cdef int i - for i in range(self.cardinality()): - l.append(self._nodes.parent[i]) + for i in range(card): + l[i] = self._nodes.parent[i] return l def __setstate__(self, l): @@ -415,35 +420,36 @@ cdef class DisjointSet_of_integers(DisjointSet_class): EXAMPLES:: sage: d = DisjointSet(5) - sage: d.__setstate__([0,1,2,3,4]) + sage: d.__setstate__([0, 1, 2, 3, 4]) sage: d {{0}, {1}, {2}, {3}, {4}} :: sage: d = DisjointSet(5) - sage: d.__setstate__([1,2,3,4,0]) + sage: d.__setstate__([1, 2, 3, 4, 0]) sage: d {{0, 1, 2, 3, 4}} :: sage: d = DisjointSet(5) - sage: d.__setstate__([1,1,1]) + sage: d.__setstate__([1, 1, 1]) sage: d {{0, 1, 2}, {3}, {4}} :: sage: d = DisjointSet(5) - sage: d.__setstate__([3,3,3]) + sage: d.__setstate__([3, 3, 3]) sage: d {{0, 1, 2, 3}, {4}} """ + cdef int i, parent for i, parent in enumerate(l): self.union(parent, i) - def find(self, int i): + cpdef int find(self, int i): r""" Return the representative of the set that ``i`` currently belongs to. @@ -454,41 +460,46 @@ cdef class DisjointSet_of_integers(DisjointSet_class): EXAMPLES:: sage: e = DisjointSet(5) - sage: e.union(4,2) + sage: e.union(4, 2) sage: e {{0}, {1}, {2, 4}, {3}} sage: e.find(2) 4 sage: e.find(4) 4 - sage: e.union(1,3) + sage: e.union(1, 3) sage: e {{0}, {1, 3}, {2, 4}} sage: e.find(1) 1 sage: e.find(3) 1 - sage: e.union(3,2) + sage: e.union(3, 2) sage: e {{0}, {1, 2, 3, 4}} sage: [e.find(i) for i in range(5)] [0, 1, 1, 1, 1] - sage: e.find(5) - Traceback (most recent call last): + sage: e.find(2**10) + ValueError: i must be between 0 and 4 (1024 given) ... - ValueError: i(=5) must be between 0 and 4 + + .. NOTE:: + + This method performs input checks. To avoid them you may directly + use :meth:`~sage.groups.perm_gps.partn_ref.data_structures.OP_find`. """ - card = self.cardinality() - if i < 0 or i>= card: - raise ValueError('i(=%s) must be between 0 and %s' % (i, card - 1)) + card = self._nodes.degree + if i < 0 or i >= card: + raise ValueError('i must be between 0 and %s (%s given)' % (card - 1, i)) return OP_find(self._nodes, i) - def union(self, int i, int j): + cpdef void union(self, int i, int j): r""" Combine the set of ``i`` and the set of ``j`` into one. All elements in those two sets will share the same representative - that can be gotten using find. + that can be retrieved using + :meth:`~sage.sets.disjoint_set.DisjointSet_of_integers.find`. INPUT: @@ -500,28 +511,32 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: d {{0}, {1}, {2}, {3}, {4}} - sage: d.union(0,1) + sage: d.union(0, 1) sage: d {{0, 1}, {2}, {3}, {4}} - sage: d.union(2,4) + sage: d.union(2, 4) sage: d {{0, 1}, {2, 4}, {3}} - sage: d.union(1,4) + sage: d.union(1, 4) sage: d {{0, 1, 2, 4}, {3}} - sage: d.union(1,5) - Traceback (most recent call last): + sage: d.union(1, 5) + ValueError: j must be between 0 and 4 (5 given) ... - ValueError: j(=5) must be between 0 and 4 + + .. NOTE:: + + This method performs input checks. To avoid them you may directly + use :meth:`~sage.groups.perm_gps.partn_ref.data_structures.OP_join`. """ cdef int card = self._nodes.degree if i < 0 or i >= card: - raise ValueError('i(=%s) must be between 0 and %s' % (i, card - 1)) + raise ValueError('i must be between 0 and %s (%s given)' % (card - 1, i)) if j < 0 or j >= card: - raise ValueError('j(=%s) must be between 0 and %s' % (j, card - 1)) + raise ValueError('j must be between 0 and %s (%s given)' % (card - 1, j)) OP_join(self._nodes, i, j) - def root_to_elements_dict(self): + cpdef root_to_elements_dict(self): r""" Return the dictionary where the keys are the roots of ``self`` and the values are the elements in the same set as the root. @@ -531,25 +546,25 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: sorted(d.root_to_elements_dict().items()) [(0, [0]), (1, [1]), (2, [2]), (3, [3]), (4, [4])] - sage: d.union(2,3) + sage: d.union(2, 3) sage: sorted(d.root_to_elements_dict().items()) [(0, [0]), (1, [1]), (2, [2, 3]), (4, [4])] - sage: d.union(3,0) + sage: d.union(3, 0) sage: sorted(d.root_to_elements_dict().items()) [(1, [1]), (2, [0, 2, 3]), (4, [4])] sage: d {{0, 2, 3}, {1}, {4}} """ - s = {} - cdef int i - for i in range(self.cardinality()): - o = self.find(i) + cdef dict s = {} + cdef int i, o + for i in range(self._nodes.degree): + o = OP_find(self._nodes, i) if o not in s: s[o] = [] s[o].append(i) return s - def element_to_root_dict(self): + cpdef element_to_root_dict(self): r""" Return the dictionary where the keys are the elements of ``self`` and the values are their representative inside a list. @@ -557,30 +572,31 @@ cdef class DisjointSet_of_integers(DisjointSet_class): EXAMPLES:: sage: d = DisjointSet(5) - sage: d.union(2,3) - sage: d.union(4,1) - sage: e = d.element_to_root_dict(); e + sage: d.union(2, 3) + sage: d.union(4, 1) + sage: e = d.element_to_root_dict() + sage: e {0: 0, 1: 4, 2: 2, 3: 2, 4: 4} sage: WordMorphism(e) # needs sage.combinat WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4 """ - d = {} + cdef dict d = {} cdef int i - for i in range(self.cardinality()): - d[i] = self.find(i) + for i in range(self._nodes.degree): + d[i] = OP_find(self._nodes, i) return d - def to_digraph(self): + cpdef to_digraph(self): r""" - Return the current digraph of ``self`` where `(a,b)` is an oriented + Return the current digraph of ``self`` where `(a, b)` is an oriented edge if `b` is the parent of `a`. EXAMPLES:: sage: d = DisjointSet(5) - sage: d.union(2,3) - sage: d.union(4,1) - sage: d.union(3,4) + sage: d.union(2, 3) + sage: d.union(4, 1) + sage: d.union(3, 4) sage: d {{0}, {1, 2, 3, 4}} sage: g = d.to_digraph(); g # needs sage.graphs @@ -591,15 +607,15 @@ cdef class DisjointSet_of_integers(DisjointSet_class): The result depends on the ordering of the union:: sage: d = DisjointSet(5) - sage: d.union(1,2) - sage: d.union(1,3) - sage: d.union(1,4) + sage: d.union(1, 2) + sage: d.union(1, 3) + sage: d.union(1, 4) sage: d {{0}, {1, 2, 3, 4}} sage: d.to_digraph().edges(sort=True) # needs sage.graphs [(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)] """ - d = {i: [self._nodes.parent[i]] for i in range(self.cardinality())} + cdef dict d = {i: [self._nodes.parent[i]] for i in range(self._nodes.degree)} from sage.graphs.digraph import DiGraph return DiGraph(d) @@ -626,18 +642,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): :: - sage: a.union('a','c') + sage: a.union('a', 'c') sage: a == loads(dumps(a)) True """ def __init__(self, iterable): r""" - Construction of the trivial disjoint set where each element is in its - own set. + Construct the trivial disjoint set where each element is in its own set. INPUT: - - ``iterable`` -- An iterable of hashable objects. + - ``iterable`` -- iterable of hashable objects EXAMPLES:: @@ -645,18 +660,18 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}} sage: DisjointSet(range(6)) {{0}, {1}, {2}, {3}, {4}, {5}} - sage: DisjointSet(['yi',45,'cheval']) + sage: DisjointSet(['yi', 45, 'cheval']) {{'cheval'}, {'yi'}, {45}} sage: DisjointSet(set([0, 1, 2, 3, 4])) {{0}, {1}, {2}, {3}, {4}} """ + cdef int i self._int_to_el = [] self._el_to_int = {} for i, e in enumerate(iterable): self._int_to_el.append(e) self._el_to_int[e] = i - self._d = DisjointSet_of_integers(len(self._int_to_el)) - self._nodes = self._d._nodes + self._nodes = OP_new(len(self._int_to_el)) def __reduce__(self): r""" @@ -678,8 +693,8 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): :: - sage: d.union(2,4) - sage: d.union(1,3) + sage: d.union(2, 4) + sage: d.union(1, 3) sage: d.__reduce__() (, ([0, 1, 2, 3, 4],), @@ -696,32 +711,32 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): sage: d = DisjointSet('abcde') sage: d.__getstate__() [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e')] - sage: d.union('c','d') + sage: d.union('c', 'd') sage: d.__getstate__() [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'c'), ('e', 'e')] - sage: d.union('d','a') + sage: d.union('d', 'a') sage: d.__getstate__() [('a', 'c'), ('b', 'b'), ('c', 'c'), ('d', 'c'), ('e', 'e')] - Other parents are obtained when the operations are done is a + Other parents are obtained when the operations are done in a different order:: sage: d = DisjointSet('abcde') - sage: d.union('d','c') + sage: d.union('d', 'c') sage: d.__getstate__() [('a', 'a'), ('b', 'b'), ('c', 'd'), ('d', 'd'), ('e', 'e')] """ - gs = self._d.__getstate__() - l = [] + cdef int card = self._nodes.degree + cdef list l = [None] * card cdef int i - for i in range(self.cardinality()): - l.append(self._int_to_el[gs[i]]) + for i in range(card): + l[i] = self._int_to_el[self._nodes.parent[i]] return list(zip(self._int_to_el, l)) def __setstate__(self, l): r""" Merge the nodes ``a`` and ``b`` for each pair of nodes - ``(a,b)`` in ``l``. + ``(a, b)`` in ``l``. INPUT: @@ -730,21 +745,21 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): EXAMPLES:: sage: d = DisjointSet('abcde') - sage: d.__setstate__([('a','a'),('b','b'),('c','c')]) + sage: d.__setstate__([('a', 'a'), ('b', 'b'), ('c', 'c')]) sage: d {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}} :: sage: d = DisjointSet('abcde') - sage: d.__setstate__([('a','b'),('b','c'),('c','d'),('d','e')]) + sage: d.__setstate__([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')]) sage: d {{'a', 'b', 'c', 'd', 'e'}} """ for a, b in l: self.union(a, b) - def find(self, e): + cpdef find(self, e): r""" Return the representative of the set that ``e`` currently belongs to. @@ -755,7 +770,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): EXAMPLES:: sage: e = DisjointSet(range(5)) - sage: e.union(4,2) + sage: e.union(4, 2) sage: e {{0}, {1}, {2, 4}, {3}} sage: e.find(2) @@ -769,7 +784,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): 1 sage: e.find(3) 1 - sage: e.union(3,2) + sage: e.union(3, 2) sage: e {{0}, {1, 2, 3, 4}} sage: [e.find(i) for i in range(5)] @@ -779,16 +794,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): ... KeyError: 5 """ - i = self._el_to_int[e] - r = self._d.find(i) + cdef int i = self._el_to_int[e] + cdef int r = OP_find(self._nodes, i) return self._int_to_el[r] - def union(self, e, f): + cpdef void union(self, e, f): r""" Combine the set of ``e`` and the set of ``f`` into one. All elements in those two sets will share the same representative - that can be gotten using find. + that can be retrieved using + :meth:`~sage.sets.disjoint_set.DisjointSet_of_hashables.find`. INPUT: @@ -800,21 +816,24 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): sage: e = DisjointSet('abcde') sage: e {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}} - sage: e.union('a','b') + sage: e.union('a', 'b') sage: e {{'a', 'b'}, {'c'}, {'d'}, {'e'}} - sage: e.union('c','e') + sage: e.union('c', 'e') sage: e {{'a', 'b'}, {'c', 'e'}, {'d'}} - sage: e.union('b','e') + sage: e.union('b', 'e') sage: e {{'a', 'b', 'c', 'e'}, {'d'}} + sage: e.union('a', 2**10) + KeyError: 1024 + ... """ - i = self._el_to_int[e] - j = self._el_to_int[f] - self._d.union(i, j) + cdef int i = self._el_to_int[e] + cdef int j = self._el_to_int[f] + OP_join(self._nodes, i, j) - def root_to_elements_dict(self): + cpdef root_to_elements_dict(self): r""" Return the dictionary where the keys are the roots of ``self`` and the values are the elements in the same set. @@ -822,13 +841,13 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): EXAMPLES:: sage: d = DisjointSet(range(5)) - sage: d.union(2,3) - sage: d.union(4,1) + sage: d.union(2, 3) + sage: d.union(4, 1) sage: e = d.root_to_elements_dict() sage: sorted(e.items()) [(0, [0]), (2, [2, 3]), (4, [1, 4])] """ - s = {} + cdef dict s = {} for e in self._int_to_el: r = self.find(e) if r not in s: @@ -836,7 +855,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): s[r].append(e) return s - def element_to_root_dict(self): + cpdef element_to_root_dict(self): r""" Return the dictionary where the keys are the elements of ``self`` and the values are their representative inside a list. @@ -844,33 +863,34 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): EXAMPLES:: sage: d = DisjointSet(range(5)) - sage: d.union(2,3) - sage: d.union(4,1) + sage: d.union(2, 3) + sage: d.union(4, 1) sage: e = d.element_to_root_dict() sage: sorted(e.items()) [(0, 0), (1, 4), (2, 2), (3, 2), (4, 4)] sage: WordMorphism(e) # needs sage.combinat WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4 """ - d = {} + cdef dict d = {} for a in self._int_to_el: d[a] = self.find(a) return d - def to_digraph(self): + cpdef to_digraph(self): r""" - Return the current digraph of ``self`` where `(a,b)` is an oriented + Return the current digraph of ``self`` where `(a, b)` is an oriented edge if `b` is the parent of `a`. EXAMPLES:: sage: d = DisjointSet(range(5)) - sage: d.union(2,3) - sage: d.union(4,1) - sage: d.union(3,4) + sage: d.union(2, 3) + sage: d.union(4, 1) + sage: d.union(3, 4) sage: d {{0}, {1, 2, 3, 4}} - sage: g = d.to_digraph(); g # needs sage.graphs + sage: g = d.to_digraph() + sage: g # needs sage.graphs Looped digraph on 5 vertices sage: g.edges(sort=True) # needs sage.graphs [(0, 0, None), (1, 2, None), (2, 2, None), (3, 2, None), (4, 2, None)] @@ -878,16 +898,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): The result depends on the ordering of the union:: sage: d = DisjointSet(range(5)) - sage: d.union(1,2) - sage: d.union(1,3) - sage: d.union(1,4) + sage: d.union(1, 2) + sage: d.union(1, 3) + sage: d.union(1, 4) sage: d {{0}, {1, 2, 3, 4}} sage: d.to_digraph().edges(sort=True) # needs sage.graphs [(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)] """ - d = {} - for i in range(self.cardinality()): + cdef dict d = {} + cdef int i + for i in range(self._nodes.degree): e = self._int_to_el[i] p = self._int_to_el[self._nodes.parent[i]] d[e] = [p]