diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index c5747a43c28..ef2233eeb49 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -549,7 +549,8 @@ def Poset(data=None, element_labels=None, cover_relations=False, linear_extensio # Determine cover relations, if necessary. if cover_relations is False: - D = D.transitive_reduction() + from sage.graphs.generic_graph_pyx import transitive_reduction_acyclic + D = transitive_reduction_acyclic(D) # Check that the digraph does not contain loops, multiple edges # and is transitively reduced. diff --git a/src/sage/graphs/base/static_dense_graph.pyx b/src/sage/graphs/base/static_dense_graph.pyx index b872d51117b..11e98fe212d 100644 --- a/src/sage/graphs/base/static_dense_graph.pyx +++ b/src/sage/graphs/base/static_dense_graph.pyx @@ -41,7 +41,7 @@ Functions --------- """ -cdef dict dense_graph_init(binary_matrix_t m, g, translation = False): +cdef dict dense_graph_init(binary_matrix_t m, g, translation=False): r""" Initializes the binary matrix from a Sage (di)graph. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 632464aaeae..b0b43e0de10 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -14575,15 +14575,9 @@ def transitive_closure(self): sage: digraphs.Path(5).copy(immutable=True).transitive_closure() Transitive closure of Path: Digraph on 5 vertices """ - from copy import copy G = self.copy(immutable=False) G.name('Transitive closure of ' + self.name()) - for v in G: - # todo optimization opportunity: we are adding edges that - # are already in the graph and we are adding edges - # one at a time. - for e in G.breadth_first_search(v): - G.add_edge((v,e)) + G.add_edges((v,e) for v in G for e in G.breadth_first_search(v)) return G def transitive_reduction(self): @@ -14612,14 +14606,17 @@ def transitive_reduction(self): sage: g.transitive_reduction().size() 5 """ - from copy import copy - from sage.rings.infinity import Infinity - G = copy(self) + if self.is_directed_acyclic(): + from sage.graphs.generic_graph_pyx import transitive_reduction_acyclic + return transitive_reduction_acyclic(self) + + G = self.copy(immutable=False) + n = G.order() for e in self.edge_iterator(): # Try deleting the edge, see if we still have a path # between the vertices. G.delete_edge(e) - if G.distance(e[0],e[1])==Infinity: + if G.distance(e[0],e[1]) > n: # oops, we shouldn't have deleted it G.add_edge(e) return G diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index c456ffe29cc..983d600ea9c 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -18,6 +18,7 @@ AUTHORS: include "sage/ext/interrupt.pxi" include 'sage/ext/cdefs.pxi' include 'sage/ext/stdsage.pxi' +include "sage/misc/binary_matrix.pxi" # import from Python standard library from sage.misc.prandom import random @@ -1280,3 +1281,66 @@ cpdef tuple find_hamiltonian( G, long max_iter=100000, long reset_bound=30000, l return (True,output) +def transitive_reduction_acyclic(G): + r""" + Returns the transitive reduction of an acyclic digraph + + INPUT: + + - ``G`` -- an acyclic digraph. + + EXAMPLE:: + + sage: from sage.graphs.generic_graph_pyx import transitive_reduction_acyclic + sage: G = posets.BooleanLattice(4).hasse_diagram() + sage: G == transitive_reduction_acyclic(G.transitive_closure()) + True + """ + cdef int n = G.order() + cdef dict v_to_int = {vv:i for i,vv in enumerate(G.vertices())} + cdef int u,v,i + + cdef list linear_extension = G.topological_sort() + linear_extension.reverse() + + cdef binary_matrix_t closure + + # Build the transitive closure of G + # + # A point is reachable from u if it is one of its neighbours, or if it is + # reachable from one of its neighbours. + binary_matrix_init(closure,n,n) + binary_matrix_fill(closure,0) + for uu in linear_extension: + u = v_to_int[uu] + for vv in G.neighbors_out(uu): + v = v_to_int[vv] + binary_matrix_set1(closure,u,v) + for i in range(closure.width): + closure.rows[u][i] |= closure.rows[v][i] + + # Build the transitive reduction of G + # + # An edge uv belongs to the transitive reduction of G if no outneighbor of u + # can reach v (except v itself, of course). + linear_extension.reverse() + cdef list useful_edges = [] + for uu in linear_extension: + u = v_to_int[uu] + for vv in G.neighbors_out(uu): + v = v_to_int[vv] + for i in range(closure.width): + closure.rows[u][i] &= ~closure.rows[v][i] + for vv in G.neighbors_out(uu): + v = v_to_int[vv] + if binary_matrix_get(closure,u,v): + useful_edges.append((uu,vv)) + + from sage.graphs.digraph import DiGraph + reduced = DiGraph() + reduced.add_edges(useful_edges) + reduced.add_vertices(linear_extension) + + binary_matrix_free(closure) + + return reduced