From a18eaff3dc03d019b66a90b37dc1a04f9cd33284 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Fri, 26 Jul 2024 16:35:28 +0200 Subject: [PATCH] graphs: init_short_digraph always sort neighbors without extra log complexity --- src/sage/graphs/asteroidal_triples.pyx | 3 +- .../graphs/base/static_sparse_backend.pyx | 11 +- src/sage/graphs/base/static_sparse_graph.pxd | 10 +- src/sage/graphs/base/static_sparse_graph.pyx | 236 ++++++++++-------- src/sage/graphs/centrality.pyx | 6 +- src/sage/graphs/convexity_properties.pyx | 10 +- src/sage/graphs/distances_all_pairs.pyx | 26 +- src/sage/graphs/generic_graph_pyx.pyx | 3 +- .../clique_separators.pyx | 9 +- src/sage/graphs/hyperbolicity.pyx | 3 +- .../graphs/isoperimetric_inequalities.pyx | 6 +- src/sage/graphs/traversals.pyx | 9 +- src/sage/graphs/weakly_chordal.pyx | 9 +- 13 files changed, 158 insertions(+), 183 deletions(-) diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 2056aa3d32c..7c904f777f3 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -147,8 +147,7 @@ def is_asteroidal_triple_free(G, certificate=False): # module sage.graphs.base.static_sparse_graph cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef bitset_t seen bitset_init(seen, n) diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 9e4983ab718..e51fc238ac5 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -425,7 +425,7 @@ cdef class StaticSparseCGraph(CGraph): cdef class StaticSparseBackend(CGraphBackend): - def __init__(self, G, loops=False, multiedges=False): + def __init__(self, G, loops=False, multiedges=False, sort=True): """ A graph :mod:`backend ` for static sparse graphs. @@ -511,10 +511,11 @@ cdef class StaticSparseBackend(CGraphBackend): True """ vertices = list(G) - try: - vertices.sort() - except TypeError: - pass + if sort: + try: + vertices.sort() + except TypeError: + pass cdef StaticSparseCGraph cg = StaticSparseCGraph(G, vertices) self._cg = cg diff --git a/src/sage/graphs/base/static_sparse_graph.pxd b/src/sage/graphs/base/static_sparse_graph.pxd index 03adfe11322..6cb9e53f17d 100644 --- a/src/sage/graphs/base/static_sparse_graph.pxd +++ b/src/sage/graphs/base/static_sparse_graph.pxd @@ -7,27 +7,19 @@ ctypedef unsigned int uint cdef extern from "stdlib.h": ctypedef void const_void "const void" - void qsort(void *base, int nmemb, int size, - int(*compar)(const_void *, const_void *)) nogil - void *bsearch(const_void *key, const_void *base, size_t nmemb, size_t size, int(*compar)(const_void *, const_void *)) nogil -cdef extern from "search.h": - void *lfind(const_void *key, const_void *base, size_t *nmemb, - size_t size, int(*compar)(const_void *, const_void *)) nogil - ctypedef struct short_digraph_s: uint32_t * edges uint32_t ** neighbors PyObject * edge_labels int m int n - bint sorted_neighbors ctypedef short_digraph_s short_digraph[1] -cdef int init_short_digraph(short_digraph g, G, edge_labelled=?, vertex_list=?, sort_neighbors=?) except -1 +cdef int init_short_digraph(short_digraph g, G, edge_labelled=?, vertex_list=?) except -1 cdef void free_short_digraph(short_digraph g) noexcept cdef int init_reverse(short_digraph dst, short_digraph src) except -1 cdef int out_degree(short_digraph g, int u) noexcept diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 3bd7c9c9285..c27f0d9a8cc 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -47,6 +47,9 @@ five fields from `0` to `n-1`) is present so that it remains easy to enumerate the neighbors of vertex `n-1` : the last of them is the element addressed by ``neighbors[n]-1``. + The arrays ``neighbors[i]`` are guaranteed to be sorted so the time + complexity for deciding if ``g`` has edge `(u, v)` is `O(\log{m})` using + binary search. - ``edge_labels`` -- list; this cython list associates a label to each edge of the graph. If a given edge is represented by ``edges[i]``, this its @@ -116,7 +119,7 @@ Cython functions :widths: 30, 70 :delim: | - ``init_short_digraph(short_digraph g, G, edge_labelled, vertex_list, sort_neighbors)`` | Initialize ``short_digraph g`` from a Sage (Di)Graph. + ``init_short_digraph(short_digraph g, G, edge_labelled, vertex_list)`` | Initialize ``short_digraph g`` from a Sage (Di)Graph. ``int n_edges(short_digraph g)`` | Return the number of edges in ``g`` ``int out_degree(short_digraph g, int i)`` | Return the out-degree of vertex `i` in ``g`` ``has_edge(short_digraph g, int u, int v)`` | Test the existence of an edge. @@ -187,10 +190,10 @@ from libc.math cimport sqrt from libcpp.vector cimport vector from cysignals.memory cimport check_allocarray, check_calloc, sig_free from cysignals.signals cimport sig_on, sig_off +from cython.operator cimport postincrement from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * -from sage.graphs.base.c_graph cimport CGraph from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend @@ -205,7 +208,7 @@ cdef extern from "fenv.h": cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, - vertex_list=None, sort_neighbors=True) except -1: + vertex_list=None) except -1: r""" Initialize ``short_digraph g`` from a Sage (Di)Graph. @@ -223,63 +226,78 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, - ``vertex_list`` -- list (default: ``None``); list of all vertices of ``G`` in some order. When given, it is used to map the vertices of the graph to consecutive integers. Otherwise, the result of ``list(G)`` is used - instead. - - - ``sort_neighbors`` -- boolean (default: ``True``); whether to ensure that - the vertices in the list of neighbors of a vertex are sorted by increasing - vertex labels. This choice may have a non-negligeable impact on the time - complexity of some methods. More precisely: - - - When set to ``True``, the time complexity for initializing ``g`` is in - `O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})` for - ``DenseGraph``, and deciding if ``g`` has edge `(u, v)` can be done in - time `O(\log{m})` using binary search. - - - When set to ``False``, the time complexity for initializing ``g`` is - reduced to `O(n + m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph``, but the time complexity for deciding if ``g`` has - edge `(u, v)` increases to `O(m)`. - """ - g.edge_labels = NULL + instead. Beware that if ``vertex_list`` is not ``None``, it is not checked + and this function assumes that it contains a permutation of the vertices + of the graph ``G``. - if G.order() >= INT_MAX: - raise ValueError("this structure can handle at most " + str(INT_MAX) + " vertices") - else: - g.n = G.order() + COMPLEXITY: - cdef int isdigraph + The time complexity for initializing ``g`` is `O(n + m)` for ``SparseGraph`` + and `O(n^2)` for ``DenseGraph``. + TESTS: + + Indirect doctests for sorted output:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: G = graphs.CompleteGraph(5).relabel(list('abcde'), inplace=False) + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_nbrs('a')) + ['b', 'c', 'd', 'e'] + sage: G = graphs.CompleteGraph(5).relabel(list('badec'), inplace=False) + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_nbrs('a')) + ['b', 'd', 'e', 'c'] + + Same with labels:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: G = graphs.CompleteGraph(5).relabel(list('abcde'), inplace=False) + sage: for i, (u, v, _) in enumerate(G.edges()): + ....: G.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_edges('a', True)) + [('a', 'b', 'ab'), ('a', 'c', 'ac'), ('a', 'd', 'ad'), ('a', 'e', 'ae')] + sage: G = graphs.CompleteGraph(5).relabel(list('badec'), inplace=False) + sage: for i, (u, v, _) in enumerate(G.edges()): + ....: G.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_edges('a', True)) + [('a', 'b', 'ab'), ('a', 'd', 'ad'), ('a', 'e', 'ae'), ('a', 'c', 'ac')] + """ from sage.graphs.graph import Graph from sage.graphs.digraph import DiGraph - if isinstance(G, DiGraph): - isdigraph = 1 - elif isinstance(G, Graph): - isdigraph = 0 - else: - raise ValueError("The source graph must be either a DiGraph or a Graph object !") + if not isinstance(G, (Graph, DiGraph)): + raise ValueError("The source graph must be either a DiGraph or a Graph" + "object !") - cdef int i, j, v_id + if G.order() >= INT_MAX: + raise ValueError(f"short_digraph can handle at most {INT_MAX} vertices") + + g.edge_labels = NULL + g.n = G.order() + g.m = G.size() + + cdef int isdigraph = G.is_directed() + cdef uint32_t i, v_id, j cdef list vertices = vertex_list if vertex_list is not None else list(G) cdef dict v_to_id = {v: i for i, v in enumerate(vertices)} cdef list neighbor_label cdef list edge_labels - - g.m = G.size() - cdef int n_edges = g.m if isdigraph else 2*g.m + # Loops are not stored twice for undirected graphs + cdef int n_edges = g.m if isdigraph else 2*g.m - G.number_of_loops() g.edges = check_allocarray(n_edges, sizeof(uint32_t)) g.neighbors = check_allocarray(1 + g.n, sizeof(uint32_t *)) # Initializing the value of neighbors g.neighbors[0] = g.edges - cdef CGraph cg = G._backend - g.sorted_neighbors = sort_neighbors if not G.has_loops(): # Normal case - for i in range(1, (g.n) + 1): - g.neighbors[i] = g.neighbors[i - 1] + (cg.out_degree(vertices[i - 1]) if isdigraph else G.degree(vertices[i - 1])) + for i, v in enumerate(vertices): + g.neighbors[i+1] = g.neighbors[i] + (G.out_degree(v) if isdigraph else G.degree(v)) else: # In the presence of loops. For a funny reason, if a vertex v has a loop # attached to it and no other incident edge, Sage declares that it has @@ -287,55 +305,40 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, # the number of edges, but then the degree of a vertex is not the number # of its neighbors anymore. One should never try to think. It never ends # well. - for i in range(1, (g.n) + 1): - g.neighbors[i] = g.neighbors[i - 1] + len(G.edges_incident(vertices[i - 1])) - - if not edge_labelled: - for u, v in G.edge_iterator(labels=False, sort_vertices=False): - i = v_to_id[u] - j = v_to_id[v] - - g.neighbors[i][0] = j - g.neighbors[i] += 1 + for i, v in enumerate(vertices): + g.neighbors[i+1] = g.neighbors[i] + len(G.edges_incident(v)) - if not isdigraph and i != j: - g.neighbors[j][0] = i - g.neighbors[j] += 1 - - # Reinitializing the value of neighbors - for i in range(g.n - 1, 0, -1): - g.neighbors[i] = g.neighbors[i - 1] - - g.neighbors[0] = g.edges - - if sort_neighbors: - # Sorting the neighbors - for i in range(g.n): - qsort(g.neighbors[i], g.neighbors[i + 1] - g.neighbors[i], sizeof(int), compare_uint32_p) - - else: - from operator import itemgetter + if edge_labelled: edge_labels = [None] * n_edges - if sort_neighbors: - for v in G: - neighbor_label = [(v_to_id[uu], l) if uu != v else (v_to_id[u], l) - for u, uu, l in G.edges_incident(v)] - neighbor_label.sort(key=itemgetter(0)) - v_id = v_to_id[v] - - for i, (j, label) in enumerate(neighbor_label): - g.neighbors[v_id][i] = j - edge_labels[(g.neighbors[v_id] + i) - g.edges] = label + + # Note that neighbors[i] will be naturally sorted by increasing id, + # because the arrays will be built by appending vertices in the same + # order as they appear in ``vertices`` + for i, v in enumerate(vertices): + if isdigraph: + edge_iterator = G.incoming_edge_iterator(v, + labels=edge_labelled) else: - for v in G: - v_id = v_to_id[v] - for i, (u, uu, label) in enumerate(G.edges_incident(v)): - if v == uu: - g.neighbors[v_id][i] = v_to_id[u] - else: - g.neighbors[v_id][i] = v_to_id[uu] - edge_labels[(g.neighbors[v_id] + i) - g.edges] = label + edge_iterator = G.edge_iterator(v, labels=edge_labelled, + sort_vertices=False) + for e in edge_iterator: + u = e[0] if v == e[1] else e[1] + j = v_to_id[u] + # Handle the edge u -> v of G (= the edge j -> i of g) + g.neighbors[j][0] = i + # Note: cannot use the dereference Cython operator here, do not + # known why but the following line does not compile + #dereference(g.neighbors[j]) = i + if edge_labelled: + edge_labels[g.neighbors[j] - g.edges] = e[2] + postincrement(g.neighbors[j]) # increment pointer to next item + + # Reinitializing the value of neighbors + for i in range(g.n-1, 0, -1): + g.neighbors[i] = g.neighbors[i-1] + g.neighbors[0] = g.edges + if edge_labelled: g.edge_labels = edge_labels cpython.Py_XINCREF(g.edge_labels) @@ -365,7 +368,6 @@ cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1: """ dst.n = src.n dst.m = src.m - dst.sorted_neighbors = src.sorted_neighbors dst.edge_labels = NULL cdef list edge_labels @@ -382,6 +384,24 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: """ Initialize ``dst`` to a copy of ``src`` with all edges in the opposite direction. + + TESTS: + + Indirect doctests for sorted output:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: D = digraphs.Complete(5).relabel(list('abcde'), inplace=False) + sage: for i, (u, v, _) in enumerate(D.edges()): + ....: D.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(D, sort=False) + sage: list(B.iterator_in_edges('a', True)) + [('b', 'a', 'ba'), ('c', 'a', 'ca'), ('d', 'a', 'da'), ('e', 'a', 'ea')] + sage: D = digraphs.Complete(5).relabel(list('badec'), inplace=False) + sage: for i, (u, v, _) in enumerate(D.edges()): + ....: D.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(D, sort=False) + sage: list(B.iterator_in_edges('a', True)) + [('b', 'a', 'ba'), ('d', 'a', 'da'), ('e', 'a', 'ea'), ('c', 'a', 'ca')] """ cdef int i, j, v # Allocates memory for dst @@ -391,7 +411,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: if not dst.n: return 0 - # 1/4 + # 1/3 # # In a first pass, we count the in-degrees of each vertex and store it in a # vector. With this information, we can initialize dst.neighbors to its @@ -407,11 +427,14 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[i] = dst.neighbors[i - 1] + in_degree[i - 1] sig_free(in_degree) - # 2/4 + # 2/3 # # Second pass : we list the edges again, and add them in dst.edges. Doing # so, we will change the value of dst.neighbors, but that is not so bad as # we can fix it afterwards. + # Note that neighbors[i] will be naturally sorted by increasing id, + # because the arrays will be built by appending vertices in the same + # order as they appear in ``vertices`` for i in range(0, src.n): for j in range(out_degree(src, i)): v = src.neighbors[i][j] @@ -422,7 +445,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[v] += 1 - # 3/4 + # 3/3 # # Third step : set the correct values of dst.neighbors again. It is easy, as # the correct value of dst.neighbors[i] is actually dst.neighbors[i-1] @@ -430,20 +453,12 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[i] = dst.neighbors[i - 1] dst.neighbors[0] = dst.edges - # 4/4 - # - # Final step : if the neighbors of src are assumed to be sorted by - # increasing labels, we do the same for dst. - if src.sorted_neighbors: - for i in range(dst.n): - qsort(dst.neighbors[i], dst.neighbors[i + 1] - dst.neighbors[i], sizeof(int), compare_uint32_p) - return 0 cdef int compare_uint32_p(const_void *a, const_void *b) noexcept: """ - Comparison function needed for ``bsearch`` and ``lfind``. + Comparison function needed for ``bsearch``. """ return ( a)[0] - ( b)[0] @@ -454,17 +469,16 @@ cdef inline uint32_t * has_edge(short_digraph g, int u, int v) noexcept: Return a pointer to ``v`` in the list of neighbors of ``u`` if found and ``NULL`` otherwise. - """ - if g.sorted_neighbors: - # The neighbors of u are sorted by increasing label. We can use binary - # search to decide if g has edge (u, v) - return bsearch(&v, g.neighbors[u], g.neighbors[u + 1] - g.neighbors[u], - sizeof(uint32_t), compare_uint32_p) - # Otherwise, we use the linear time lfind method - cdef size_t nelem = g.neighbors[u + 1] - g.neighbors[u] - return lfind(&v, g.neighbors[u], &nelem, - sizeof(uint32_t), compare_uint32_p) + .. NOTE:: + + Use the fact that the array ``g.neighbors[u]`` is guaranteed to be sorted. + """ + # The neighbors of u are sorted by increasing label. We can use binary + # search to decide if g has edge (u, v) + return bsearch(&v, g.neighbors[u], + g.neighbors[u+1] - g.neighbors[u], + sizeof(uint32_t), compare_uint32_p) cdef inline object edge_label(short_digraph g, uint32_t * edge): @@ -797,7 +811,7 @@ def tarjan_strongly_connected_components(G): cdef MemoryAllocator mem = MemoryAllocator() cdef list int_to_vertex = list(G) cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef int * scc = mem.malloc(g.n * sizeof(int)) sig_on() cdef int nscc = tarjan_strongly_connected_components_C(g, scc) @@ -914,7 +928,7 @@ def strongly_connected_components_digraph(G): cdef MemoryAllocator mem = MemoryAllocator() cdef list int_to_vertex = list(G) cdef short_digraph g, scc_g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef int * scc = mem.malloc(g.n * sizeof(int)) cdef int i, j, nscc cdef list edges = [] @@ -979,7 +993,7 @@ def triangles_count(G): # g is a copy of G. If G is internally a static sparse graph, we use it. cdef list int_to_vertex = list(G) cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=True) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint64_t * count = check_calloc(G.order(), sizeof(uint64_t)) diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index b3165b69232..e249046ae70 100755 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -178,7 +178,7 @@ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True): mpq_init(mpq_tmp) try: - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) init_reverse(bfs_dag, g) queue = check_allocarray(n, sizeof(uint32_t)) @@ -689,7 +689,7 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef list V = list(G) - init_short_digraph(sd, G, edge_labelled=False, vertex_list=V, sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=V) cdef int n = sd.n cdef int* reachL = mem.malloc(n * sizeof(int)) cdef int* reachU @@ -939,7 +939,7 @@ def centrality_closeness_random_k(G, int k=1): # Copying the whole graph as a static_sparse_graph for fast shortest # paths computation in unweighted graph. This data structure is well # documented in module sage.graphs.base.static_sparse_graph - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) distance = mem.malloc(n * sizeof(uint32_t)) waiting_list = mem.malloc(n * sizeof(uint32_t)) bitset_init(seen, n) diff --git a/src/sage/graphs/convexity_properties.pyx b/src/sage/graphs/convexity_properties.pyx index a51c3db0102..f292cae3eed 100644 --- a/src/sage/graphs/convexity_properties.pyx +++ b/src/sage/graphs/convexity_properties.pyx @@ -506,11 +506,9 @@ def geodetic_closure(G, S): each vertex `u \in S`, the algorithm first performs a breadth first search from `u` to get distances, and then identifies the vertices of `G` lying on a shortest path from `u` to any `v\in S` using a reversal traversal from - vertices in `S`. This algorithm has time complexity in - `O(|S|(n + m) + (n + m\log{m}))` for ``SparseGraph``, - `O(|S|(n + m) + n^2\log{m})` for ``DenseGraph`` and space complexity in - `O(n + m)` (the extra `\log` factor is due to ``init_short_digraph`` being - called with ``sort_neighbors=True``). + vertices in `S`. This algorithm has time complexity in `O(|S|(n + m))` for + ``SparseGraph``, `O(|S|(n + m) + n^2)` for ``DenseGraph`` and + space complexity in `O(n + m)`. INPUT: @@ -757,7 +755,7 @@ def is_geodetic(G): # Copy the graph as a short digraph cdef int n = G.order() cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # Allocate some data structures cdef MemoryAllocator mem = MemoryAllocator() diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 63ad747ebd9..23d2e1c79d7 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -304,8 +304,7 @@ cdef inline all_pairs_shortest_path_BFS(gg, # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors cdef short_digraph sd - init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex) c_all_pairs_shortest_path_BFS(sd, predecessors, distances, eccentricity) @@ -1057,8 +1056,7 @@ def eccentricity(G, algorithm='standard', vertex_list=None): ecc = c_eccentricity(G, vertex_list=int_to_vertex) else: - init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) if algorithm == "DHV": ecc = c_eccentricity_DHV(sd) @@ -1836,8 +1834,7 @@ def diameter(G, algorithm=None, source=None): # module sage.graphs.base.static_sparse_graph cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef short_digraph rev_sd # to store copy of sd with edges reversed # and we map the source to an int in [0,n-1] @@ -1939,8 +1936,7 @@ def radius_DHV(G): cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t source, ecc_source cdef uint32_t antipode, ecc_antipode @@ -2057,8 +2053,7 @@ def wiener_index(G): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # allocated some data structures cdef bitset_t seen @@ -2280,14 +2275,6 @@ def szeged_index(G, algorithm=None): By default (``None``), the ``'low'`` algorithm is used for graphs and the ``'high'`` algorithm for digraphs. - .. NOTE:: - As the graph is converted to a short_digraph, the complexity for the - case ``algorithm == "high"`` has an extra `O(m+n)` for ``SparseGraph`` - and `O(n^2)` for ``DenseGraph``. If ``algorithm == "low"``, the extra - complexity is `O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})` - for ``DenseGraph`` (because ``init_short_digraph`` is called with - ``sort_neighbors=True``). - EXAMPLES: True for any connected graph [KRG1996]_:: @@ -2378,13 +2365,12 @@ def szeged_index(G, algorithm=None): return 0 cdef short_digraph sd + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) cdef uint64_t s if algorithm == "low": - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=True) s = c_szeged_index_low_memory(sd) else: - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=False) s = c_szeged_index_high_memory(sd) free_short_digraph(sd) diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index c9969845710..a350ceeba86 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -1374,8 +1374,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, # static copy of the graph for more efficient operations cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=True) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef short_digraph rev_sd cdef bint reverse = False if directed: diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 0bd02e3b5db..3cb10805b8a 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -172,12 +172,6 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal :meth:`~sage.graphs.traversals.maximum_cardinality_search_M` graph traversal and has time complexity in `O(|V|\cdot|E|)`. - .. NOTE:: - As the graph is converted to a short_digraph (with - ``sort_neighbors=True``), the complexity has an extra - `O(|V|+|E|\log{|E|})` for ``SparseGraph`` and `O(|V|^2\log{|E|})` for - ``DenseGraph``. - If the graph is not connected, we insert empty separators between the lists of separators of each connected components. See the examples below for more details. @@ -464,8 +458,7 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=True) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) # variables for the manipulation of the short digraph cdef uint32_t** p_vertices = sd.neighbors diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index c24c8d3334f..bf24e7d3d97 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -418,8 +418,7 @@ cdef inline distances_and_far_apart_pairs(gg, # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t** p_vertices = sd.neighbors cdef uint32_t* p_tmp cdef uint32_t* end diff --git a/src/sage/graphs/isoperimetric_inequalities.pyx b/src/sage/graphs/isoperimetric_inequalities.pyx index 5fd13cade15..731cfc00040 100644 --- a/src/sage/graphs/isoperimetric_inequalities.pyx +++ b/src/sage/graphs/isoperimetric_inequalities.pyx @@ -110,8 +110,7 @@ def cheeger_constant(g): cdef unsigned long vmin = 1 # value of the volume for the min cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g), - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) subgraph = check_malloc(sd.n * sizeof(int)) bitsubgraph = check_malloc(sd.n * sizeof(int)) @@ -245,8 +244,7 @@ def edge_isoperimetric_number(g): cdef int u = 0 # current vertex cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g), - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) cdef unsigned long bmin = sd.neighbors[1] - sd.neighbors[0] # value of boundary for the min cdef unsigned long vmin = 1 # value of the volume for the min diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index c1b0f883578..0ce87c3ad11 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -1145,8 +1145,7 @@ def lex_M_fast(G, triangulation=False, initial_vertex=None): int_to_v[0], int_to_v[i] = int_to_v[i], int_to_v[0] cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v) cdef uint32_t* p_tmp cdef uint32_t* p_end @@ -1409,8 +1408,7 @@ def maximum_cardinality_search(G, reverse=False, tree=False, initial_vertex=None raise ValueError("vertex ({0}) is not a vertex of the graph".format(initial_vertex)) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t** p_vertices = sd.neighbors cdef uint32_t* p_tmp cdef uint32_t* p_end @@ -1784,8 +1782,7 @@ def maximum_cardinality_search_M(G, initial_vertex=None): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef MemoryAllocator mem = MemoryAllocator() cdef int* alpha = mem.calloc(N, sizeof(int)) diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index c60fae5d6a6..37961e273e5 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -213,8 +213,7 @@ def is_long_hole_free(g, certificate=False): cdef int n = g.order() cdef list id_label = list(g) cdef short_digraph sd - init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label, - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) # Make a dense copy of the graph for quick adjacency tests cdef bitset_t dense_graph @@ -528,9 +527,9 @@ def is_weakly_chordal(g, certificate=False): contain an induced cycle of length at least 5. Using is_long_hole_free() and is_long_antihole_free() yields a run time - of `O(n+m^2)` for ``SparseGraph`` and `O(n^2\log{m} + m^2)` for - ``DenseGraph`` (where `n` is the number of vertices and `m` is the number of - edges of the graph). + of `O(n+m^2)` for ``SparseGraph`` and `O(n^2 + m^2)` for ``DenseGraph`` + (where `n` is the number of vertices and `m` is the number of edges of the + graph). EXAMPLES: