Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid some conversions to short_digraph when the input is an instance of StaticSparseBackend #39216

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions src/sage/graphs/asteroidal_triples.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ from cysignals.signals cimport sig_on, sig_off
from memory_allocator cimport MemoryAllocator

from sage.data_structures.bitset_base cimport *
from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph


Expand Down Expand Up @@ -124,6 +126,17 @@ def is_asteroidal_triple_free(G, certificate=False):
Traceback (most recent call last):
...
ValueError: The first parameter must be a Graph.

The method is valid for immutable graphs::

sage: G = graphs.RandomGNP(10, .7)
sage: G._backend
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
sage: H = Graph(G, immutable=True)
sage: H._backend
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
sage: G.is_asteroidal_triple_free() == H.is_asteroidal_triple_free()
True
"""
from sage.graphs.graph import Graph
if not isinstance(G, Graph):
Expand All @@ -145,9 +158,16 @@ def is_asteroidal_triple_free(G, certificate=False):
# Copying the whole graph to obtain the list of neighbors quicker than by
# calling out_neighbors. This data structure is well documented in the
# module sage.graphs.base.static_sparse_graph
cdef list int_to_vertex = list(G)
cdef list int_to_vertex
cdef StaticSparseCGraph cg
cdef short_digraph sd
init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
if isinstance(G, StaticSparseBackend):
cg = <StaticSparseCGraph> G._cg
sd = <short_digraph> cg.g
int_to_vertex = cg._vertex_to_labels
else:
int_to_vertex = list(G)
init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)

cdef bitset_t seen
bitset_init(seen, n)
Expand All @@ -168,7 +188,8 @@ def is_asteroidal_triple_free(G, certificate=False):
finally:
# Release memory
bitset_free(seen)
free_short_digraph(sd)
if not isinstance(G, StaticSparseBackend):
free_short_digraph(sd)

# ==> We return the result

Expand Down
2 changes: 2 additions & 0 deletions src/sage/graphs/base/static_sparse_backend.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ cdef class StaticSparseCGraph(CGraph):
self.number_of_loops = <int *>check_calloc(self.g.n, sizeof(int))
except MemoryError:
free_short_digraph(self.g)
if self._directed:
free_short_digraph(self.g_rev)
raise
for i in range(self.g.n):
for tmp in range(out_degree(self.g, i)):
Expand Down
98 changes: 85 additions & 13 deletions src/sage/graphs/base/static_sparse_graph.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -802,21 +802,43 @@
....: s2 = Set(map(Set,scc2))
....: if s1 != s2:
....: print("Ooch !")

Immutable digraphs::

sage: from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
sage: G = digraphs.RandomDirectedGNP(10, .4)
sage: G._backend
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
sage: H = DiGraph(G, immutable=True)
sage: H._backend
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
sage: tarjan_strongly_connected_components(G) == tarjan_strongly_connected_components(H)
True
"""
from sage.graphs.digraph import DiGraph

if not isinstance(G, DiGraph):
raise ValueError("G must be a DiGraph.")

cdef MemoryAllocator mem = MemoryAllocator()
cdef list int_to_vertex = list(G)
cdef list int_to_vertex
cdef StaticSparseCGraph cg
cdef short_digraph g
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
if isinstance(G, StaticSparseBackend):
cg = <StaticSparseCGraph> G._cg
g = <short_digraph> cg.g
int_to_vertex = cg._vertex_to_labels
else:
int_to_vertex = list(G)
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)

cdef MemoryAllocator mem = MemoryAllocator()
cdef int * scc = <int*> mem.malloc(g.n * sizeof(int))
sig_on()
cdef int nscc = tarjan_strongly_connected_components_C(g, scc)
sig_off()
free_short_digraph(g)

if not isinstance(G, StaticSparseBackend):
free_short_digraph(g)

cdef int i
cdef list output = [[] for i in range(nscc)]
Expand Down Expand Up @@ -872,6 +894,7 @@

output.n = nscc
output.m = m
output.edge_labels = NULL

output.neighbors = <uint32_t **> check_allocarray((1+<int>output.n), sizeof(uint32_t *))

Expand Down Expand Up @@ -920,15 +943,37 @@
....: for e in g.edges(sort=False):
....: assert(sccs[e[0]]==sccs[e[1]] or scc_digraph.has_edge(sccs[e[0]],sccs[e[1]]))
....: assert(sccs[e[0]] >= sccs[e[1]])

Immutable digraphs::

sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components_digraph
sage: G = digraphs.RandomDirectedGNP(10, .4)
sage: G._backend
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
sage: H = DiGraph(G, immutable=True)
sage: H._backend
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
sage: A = strongly_connected_components_digraph(G)[0]
sage: B = strongly_connected_components_digraph(H)[0]
sage: A.is_isomorphic(B)
True
"""
from sage.graphs.digraph import DiGraph
if not isinstance(G, DiGraph):
raise ValueError("G must be a DiGraph.")

cdef MemoryAllocator mem = MemoryAllocator()
cdef list int_to_vertex = list(G)
cdef list int_to_vertex
cdef StaticSparseCGraph cg
cdef short_digraph g, scc_g
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
if isinstance(G, StaticSparseBackend):
cg = <StaticSparseCGraph> G._cg
g = <short_digraph> cg.g
int_to_vertex = cg._vertex_to_labels
else:
int_to_vertex = list(G)
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)

cdef MemoryAllocator mem = MemoryAllocator()
cdef int * scc = <int*> mem.malloc(g.n * sizeof(int))
cdef int i, j, nscc
cdef list edges = []
Expand All @@ -944,7 +989,11 @@
edges.append((i, scc_g.neighbors[i][j]))
output.add_edges(edges)
sig_off()
free_short_digraph(g)

if not isinstance(G, StaticSparseBackend):
free_short_digraph(g)
free_short_digraph(scc_g)

return output, {v: scc[i] for i, v in enumerate(int_to_vertex)}


Expand All @@ -968,7 +1017,8 @@
"""
sig_free(g.edges)
sig_free(g.neighbors)
cpython.Py_XDECREF(g.edge_labels)
if g.edge_labels != NULL:
cpython.Py_XDECREF(g.edge_labels)


def triangles_count(G):
Expand All @@ -986,14 +1036,35 @@
{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}
sage: sum(triangles_count(graphs.CompleteGraph(15)).values()) == 3*binomial(15,3) # needs sage.symbolic
True

TESTS:

Immutable graphs::

sage: from sage.graphs.base.static_sparse_graph import triangles_count
sage: G = graphs.RandomGNP(10, .7)
sage: G._backend
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
sage: H = Graph(G, immutable=True)
sage: H._backend
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
sage: triangles_count(G) == triangles_count(H)
True
"""
from sage.rings.integer import Integer
G._scream_if_not_simple()

# 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 list int_to_vertex
cdef StaticSparseCGraph cg
cdef short_digraph g
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
if isinstance(G, StaticSparseBackend):
cg = <StaticSparseCGraph> G._cg
g = <short_digraph> cg.g
int_to_vertex = cg._vertex_to_labels
else:
int_to_vertex = list(G)
init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)

cdef uint64_t * count = <uint64_t *> check_calloc(G.order(), sizeof(uint64_t))

Expand Down Expand Up @@ -1027,7 +1098,8 @@

ans = {w: Integer(count[i] // 2) for i, w in enumerate(int_to_vertex)}

free_short_digraph(g)
if not isinstance(G, StaticSparseBackend):
free_short_digraph(g)
sig_free(count)
return ans

Expand Down Expand Up @@ -1111,7 +1183,7 @@
sage: while not G.is_strongly_connected():
....: shuffle(r)
....: G.add_edges(enumerate(r), loops=False)
sage: spectral_radius(G, 1e-10) # random
sage: spectral_radius(G, 1e-10) # random # long time

Check warning on line 1186 in src/sage/graphs/base/static_sparse_graph.pyx

View workflow job for this annotation

GitHub Actions / test-long (src/sage/[g-o]*)

Warning: slow doctest:

slow doctest:
(1.9997956006500042, 1.9998043797692782)

The algorithm takes care of multiple edges::
Expand Down
Loading
Loading