# HG changeset patch
# User Nathann Cohen
# Date 1291575004 -3600
# Node ID e8203abc9c19aa7850cf3187099ace78303c1d1f
# Parent 063ffd2a1aa7c0aaf2f7548513dc8ee546a0a108
trac 10432 - DiGraph.is_directed_acyclic in Cython (--> without NetworX) and its certificates
diff -r 063ffd2a1aa7 -r e8203abc9c19 sage/combinat/posets/posets.py
--- a/sage/combinat/posets/posets.py Tue Jan 25 12:36:18 2011 +0000
+++ b/sage/combinat/posets/posets.py Sun Dec 05 19:50:04 2010 +0100
@@ -998,10 +998,10 @@
sage: P = Posets.SymmetricGroupBruhatOrderPoset(4)
sage: [(v,P.rank(v)) for v in P]
- [(1234, 0),
- (1243, 1),
+ [(1234, 0),
+ (1324, 1),
...
- (4312, 5),
+ (4231, 5),
(4321, 6)]
"""
if element is None:
@@ -1430,7 +1430,7 @@
sage: B = Posets.BooleanLattice(4)
sage: B.order_filter([3,8])
- [8, 9, 10, 3, 11, 12, 13, 14, 7, 15]
+ [3, 7, 8, 9, 10, 11, 12, 13, 14, 15]
"""
vertices = sorted(map(self._element_to_vertex,elements))
of = self._hasse_diagram.order_filter(vertices)
@@ -1444,7 +1444,7 @@
sage: B = Posets.BooleanLattice(4)
sage: B.principal_order_filter(2)
- [2, 10, 3, 11, 6, 14, 7, 15]
+ [2, 3, 6, 7, 10, 11, 14, 15]
"""
return self.order_filter([x])
@@ -1459,7 +1459,7 @@
sage: B = Posets.BooleanLattice(4)
sage: B.order_ideal([7,10])
- [0, 8, 1, 2, 10, 3, 4, 5, 6, 7]
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 10]
"""
vertices = map(self._element_to_vertex,elements)
oi = self._hasse_diagram.order_ideal(vertices)
diff -r 063ffd2a1aa7 -r e8203abc9c19 sage/geometry/fan.py
--- a/sage/geometry/fan.py Tue Jan 25 12:36:18 2011 +0000
+++ b/sage/geometry/fan.py Sun Dec 05 19:50:04 2010 +0100
@@ -98,14 +98,14 @@
:meth:`~sage.geometry.fan.RationalPolyhedralFan.cones`::
sage: [cone.ambient_ray_indices() for cone in fan1.cones(2)]
- [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
- (2, 4), (3, 4), (3, 5), (4, 5), (0, 5), (1, 5)]
+ [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
+ (2, 4), (3, 4), (1, 5), (3, 5), (4, 5), (0, 5)]
In fact, you don't have to type ``.cones``::
sage: [cone.ambient_ray_indices() for cone in fan1(2)]
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
- (2, 4), (3, 4), (3, 5), (4, 5), (0, 5), (1, 5)]
+ (2, 4), (3, 4), (1, 5), (3, 5), (4, 5), (0, 5)]
You may also need to know the inclusion relations between all of the cones of
the fan. In this case check out
diff -r 063ffd2a1aa7 -r e8203abc9c19 sage/graphs/base/c_graph.pyx
--- a/sage/graphs/base/c_graph.pyx Tue Jan 25 12:36:18 2011 +0000
+++ b/sage/graphs/base/c_graph.pyx Sun Dec 05 19:50:04 2010 +0100
@@ -2663,6 +2663,194 @@
cdef set b = set(self.depth_first_search(v, reverse=True))
return list(a & b)
+ def is_directed_acyclic(self, certificate = False):
+ r"""
+ Returns whether the graph is both directed and acylic (possibly with a
+ certificate)
+
+
+ INPUT:
+
+ - ``certificate`` -- whether to return a certificate (``False`` by
+ default).
+
+ OUTPUT:
+
+ When ``certificate=False``, returns a boolean value. When
+ ``certificate=True`` :
+
+ * If the graph is acyclic, returns a pair ``(True, ordering)`` where
+ ``ordering`` is a list of the vertices such that ``u`` appears
+ before ``v`` in ``ordering`` if ``u, v`` is an edge.
+
+ * Else, returns a pair ``(False, cycle)`` where ``cycle`` is a list
+ of vertices representing a circuit in the graph.
+
+ .. NOTE::
+
+ The graph is assumed to be directed. An exception is raised if it is
+ not.
+
+ EXAMPLES:
+
+ At first, the following graph is acyclic::
+
+ sage: D = DiGraph({ 0:[1,2,3], 4:[2,5], 1:[8], 2:[7], 3:[7], 5:[6,7], 7:[8], 6:[9], 8:[10], 9:[10] })
+ sage: D.plot(layout='circular').show()
+ sage: D.is_directed_acyclic()
+ True
+
+ Adding an edge from `9` to `7` does not change it::
+
+ sage: D.add_edge(9,7)
+ sage: D.is_directed_acyclic()
+ True
+
+ We can obtain as a proof an ordering of the vertices such that `u`
+ appears before `v` if `uv` is an edge of the graph::
+
+ sage: D.is_directed_acyclic(certificate = True)
+ (True, [4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10])
+
+ Adding an edge from 7 to 4, though, makes a difference::
+
+ sage: D.add_edge(7,4)
+ sage: D.is_directed_acyclic()
+ False
+
+ Indeed, it creates a circuit `7, 4, 5`::
+
+ sage: D.is_directed_acyclic(certificate = True)
+ (False, [7, 4, 5])
+
+ Checking acyclic graphs are indeed acyclic ::
+
+ sage: def random_acyclic(n, p):
+ ... g = graphs.RandomGNP(n, p)
+ ... h = DiGraph()
+ ... h.add_edges([ ((u,v) if uself._cg).active_vertices.size)
+ bitset_set_first_n(seen, 0)
+
+ # Activated vertices
+ cdef bitset_t activated
+ bitset_init(activated, (self._cg).active_vertices.size)
+ bitset_set_first_n(activated, (self._cg).active_vertices.size)
+
+ # Vertices whose neighbors have already been added to the stack
+ cdef bitset_t tried
+ bitset_init(tried, (self._cg).active_vertices.size)
+ bitset_set_first_n(tried, 0)
+
+ # Parent of a vertex in the discovery tree
+ cdef dict parent = {}
+
+ # The vertices left to be visited
+ cdef list stack = []
+
+ # Final ordering, if the graph turns out to be acyclic
+ cdef list ordering = []
+
+ # Circuit, if the graph turns out to contain one
+ cdef list cycle
+
+ # We try any vertex as the source of the exploration tree
+ for v in (self._cg).verts():
+
+ # We are not interested in trying de-activated vertices
+ if bitset_not_in(activated, v):
+ continue
+
+ stack = [v]
+ bitset_add(seen, v)
+
+ # For as long as some vertices are to be visited
+ while stack:
+
+ # We take the last one (depth-first search)
+ u = stack.pop(-1)
+
+ # If we never tried it, we put it at the end of the stack again,
+ # but remember we tried it once.
+ if bitset_not_in(tried, u):
+ bitset_add(tried, u)
+ bitset_add(seen, u)
+ stack.append(u)
+
+ # If we tried all the path starting from this vertex already, it
+ # means it leads to no circuit. We can remove it, and go to the
+ # next one
+ else:
+ ordering.insert(0, vertex_label(u, self.vertex_ints, self.vertex_labels, self._cg))
+ bitset_discard(seen, u)
+ bitset_discard(activated, u)
+ continue
+
+ # For each out-neighbor of the current vertex
+ for uu in self._cg.out_neighbors(u):
+
+ # If we have found a new vertex, we put it at the end of the
+ # stack. We ignored de-activated vertices.
+ if bitset_not_in(seen, uu):
+ if bitset_in(activated, uu):
+ parent[uu] = u
+ stack.append(uu)
+
+ # If we have already met this vertex, it means we have
+ # found a circuit !
+ else:
+
+ bitset_free(seen)
+ bitset_free(activated)
+ bitset_free(tried)
+
+ if not certificate:
+ return False
+
+ # We build it, then return it
+ # // answer = [u]
+ cycle = [vertex_label(u, self.vertex_ints, self.vertex_labels, self._cg)]
+
+ tmp = u
+ while u != uu:
+ u = parent.get(u,uu)
+ cycle.append(vertex_label(u, self.vertex_ints, self.vertex_labels, self._cg))
+
+ cycle.reverse()
+ return (False, cycle)
+
+ # Each time we have explored all the descendants of a vertex v and
+ # met no circuit, we de-activate all these vertices, as we know we
+ # do not need them anyway.
+ for v in (self._cg).verts():
+ if bitset_in(seen, v):
+ bitset_discard(seen, v)
+ bitset_discard(activated, v)
+ bitset_add(tried, v)
+
+
+ # No Cycle... Good news ! Let's return it.
+ bitset_free(seen)
+ bitset_free(activated)
+ bitset_free(tried)
+
+ if certificate:
+ return (True, ordering)
+ else:
+ return True
+
cdef class Search_iterator:
r"""
An iterator for traversing a (di)graph.
diff -r 063ffd2a1aa7 -r e8203abc9c19 sage/graphs/digraph.py
--- a/sage/graphs/digraph.py Tue Jan 25 12:36:18 2011 +0000
+++ b/sage/graphs/digraph.py Sun Dec 05 19:50:04 2010 +0100
@@ -758,39 +758,78 @@
### Properties
- def is_directed_acyclic(self):
+ def is_directed_acyclic(self, certificate = False):
"""
Returns whether the digraph is acyclic or not.
- A directed graph is acyclic if for any vertex v, there is no
- directed path that starts and ends at v. Every directed acyclic
- graph (dag) corresponds to a partial ordering of its vertices,
- however multiple dags may lead to the same partial ordering.
+ A directed graph is acyclic if for any vertex `v`, there is no directed
+ path that starts and ends at `v`. Every directed acyclic graph (DAG)
+ corresponds to a partial ordering of its vertices, however multiple dags
+ may lead to the same partial ordering.
+
+ INPUT:
+
+ - ``certificate`` -- whether to return a certificate (``False`` by
+ default).
+
+ OUTPUT:
+
+ * When ``certificate=False``, returns a boolean value.
+
+ * When ``certificate=True`` :
+
+ * If the graph is acyclic, returns a pair ``(True, ordering)``
+ where ``ordering`` is a list of the vertices such that ``u``
+ appears before ``v`` in ``ordering`` if ``u, v`` is an edge.
+
+ * Else, returns a pair ``(False, cycle)`` where ``cycle`` is a
+ list of vertices representing a circuit in the graph.
- EXAMPLES::
+ EXAMPLES:
+
+ At first, the following graph is acyclic::
sage: D = DiGraph({ 0:[1,2,3], 4:[2,5], 1:[8], 2:[7], 3:[7], 5:[6,7], 7:[8], 6:[9], 8:[10], 9:[10] })
sage: D.plot(layout='circular').show()
sage: D.is_directed_acyclic()
True
- ::
+ Adding an edge from `9` to `7` does not change it::
sage: D.add_edge(9,7)
sage: D.is_directed_acyclic()
True
+
+ We can obtain as a proof an ordering of the vertices such that `u`
+ appears before `v` if `uv` is an edge of the graph::
+
+ sage: D.is_directed_acyclic(certificate = True)
+ (True, [4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10])
- ::
+ Adding an edge from 7 to 4, though, makes a difference::
sage: D.add_edge(7,4)
sage: D.is_directed_acyclic()
False
+
+ Indeed, it creates a circuit `7, 4, 5`::
+
+ sage: D.is_directed_acyclic(certificate = True)
+ (False, [7, 4, 5])
+
+ Checking acyclic graphs are indeed acyclic ::
+
+ sage: def random_acyclic(n, p):
+ ... g = graphs.RandomGNP(n, p)
+ ... h = DiGraph()
+ ... h.add_edges([ ((u,v) if u