passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,1583 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Static sparse graph backend
|
4
|
+
|
5
|
+
This module implement a immutable sparse graph backend using the data structure
|
6
|
+
from :mod:`sage.graphs.base.static_sparse_graph`. It supports both directed and
|
7
|
+
undirected graphs, as well as vertex/edge labels, loops and multiple edges. As
|
8
|
+
it uses a very compact C structure it should be very small in memory.
|
9
|
+
|
10
|
+
As it is a sparse data structure, you can expect it to be very efficient when
|
11
|
+
you need to list the graph's edge, or those incident to a vertex, but an
|
12
|
+
adjacency test can be much longer than in a dense data structure (i.e. like in
|
13
|
+
:mod:`sage.graphs.base.static_dense_graph`)
|
14
|
+
|
15
|
+
For an overview of graph data structures in sage, see
|
16
|
+
:mod:`~sage.graphs.base.overview`.
|
17
|
+
|
18
|
+
Two classes
|
19
|
+
-----------
|
20
|
+
|
21
|
+
This module implements two classes
|
22
|
+
|
23
|
+
* :class:`StaticSparseCGraph` extends :class:`~sage.graphs.base.c_graph.CGraph`
|
24
|
+
and is a Cython class that manages the definition/deallocation of the
|
25
|
+
``short_digraph`` structure. It does not know anything about labels on
|
26
|
+
vertices.
|
27
|
+
|
28
|
+
* :class:`StaticSparseBackend` extends
|
29
|
+
:class:`~sage.graphs.base.c_graph.CGraphBackend` and is a Python class that
|
30
|
+
does know about vertex labels and contains an instance of
|
31
|
+
:class:`StaticSparseCGraph` as an internal variable. The input/output of its
|
32
|
+
methods are labeled vertices, which it translates to integer id before
|
33
|
+
forwarding them to the :class:`StaticSparseCGraph` instance.
|
34
|
+
|
35
|
+
Classes and methods
|
36
|
+
-------------------
|
37
|
+
"""
|
38
|
+
|
39
|
+
cimport cython
|
40
|
+
from cysignals.memory cimport check_calloc, sig_free
|
41
|
+
|
42
|
+
from sage.graphs.base.static_sparse_graph cimport (init_short_digraph,
|
43
|
+
init_reverse,
|
44
|
+
out_degree,
|
45
|
+
has_edge,
|
46
|
+
free_short_digraph,
|
47
|
+
edge_label)
|
48
|
+
from sage.graphs.base.c_graph cimport CGraphBackend
|
49
|
+
from sage.data_structures.bitset cimport FrozenBitset
|
50
|
+
from libc.stdint cimport uint32_t
|
51
|
+
from sage.data_structures.bitset_base cimport *
|
52
|
+
|
53
|
+
cdef extern from "Python.h":
|
54
|
+
int unlikely(int) nogil # Defined by Cython
|
55
|
+
|
56
|
+
cdef class StaticSparseCGraph(CGraph):
|
57
|
+
"""
|
58
|
+
:mod:`CGraph <sage.graphs.base.c_graph>` class based on the sparse graph
|
59
|
+
data structure :mod:`static sparse graphs
|
60
|
+
<sage.graphs.base.static_sparse_graph>`.
|
61
|
+
"""
|
62
|
+
|
63
|
+
def __cinit__(self, G, vertex_list=None):
|
64
|
+
r"""
|
65
|
+
Cython constructor.
|
66
|
+
|
67
|
+
INPUT:
|
68
|
+
|
69
|
+
- ``G`` -- a :class:`Graph` object
|
70
|
+
|
71
|
+
- ``vertex_list`` -- (optional) list of all vertices of ``G``
|
72
|
+
|
73
|
+
The optional argument ``vertex_list`` is assumed to be a list of all
|
74
|
+
vertices of the graph ``G`` in some order.
|
75
|
+
**Beware that no serious checks are made that this input is correct**.
|
76
|
+
|
77
|
+
If ``vertex_list`` is given, it will be used to map vertices
|
78
|
+
of the graph to consecutive integers. Otherwise, the result of
|
79
|
+
``G.vertices(sort=True)`` will be used instead. Because this
|
80
|
+
only works if the vertices can be sorted, using
|
81
|
+
``vertex_list`` is useful when working with possibly
|
82
|
+
non-sortable objects in Python 3.
|
83
|
+
|
84
|
+
TESTS::
|
85
|
+
|
86
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
87
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
88
|
+
|
89
|
+
Check that the digraph methods are working (see :issue:`20253`)::
|
90
|
+
|
91
|
+
sage: G = DiGraph([(0, 1), (1, 0)])
|
92
|
+
sage: G2 = G.copy(immutable=True)
|
93
|
+
sage: G2.is_strongly_connected()
|
94
|
+
True
|
95
|
+
|
96
|
+
Using the ``vertex_list`` optional argument::
|
97
|
+
|
98
|
+
sage: g = StaticSparseCGraph(DiGraph({0: [2]}), vertex_list=[2, 0])
|
99
|
+
sage: g.has_arc(0, 1)
|
100
|
+
False
|
101
|
+
sage: g.has_arc(1, 0)
|
102
|
+
True
|
103
|
+
|
104
|
+
sage: g = StaticSparseCGraph(DiGraph({0: [2]}), vertex_list=[2, 0, 4])
|
105
|
+
Traceback (most recent call last):
|
106
|
+
...
|
107
|
+
ValueError: vertex_list has wrong length
|
108
|
+
"""
|
109
|
+
cdef int i, j, tmp
|
110
|
+
has_labels = any(l is not None for _, _, l in G.edge_iterator())
|
111
|
+
self._directed = G.is_directed()
|
112
|
+
|
113
|
+
if vertex_list is not None and len(vertex_list) != G.order():
|
114
|
+
raise ValueError('vertex_list has wrong length')
|
115
|
+
|
116
|
+
init_short_digraph(self.g, G, edge_labelled=has_labels,
|
117
|
+
vertex_list=vertex_list)
|
118
|
+
if self._directed:
|
119
|
+
init_reverse(self.g_rev, self.g)
|
120
|
+
|
121
|
+
# Store the number of loops for undirected graphs
|
122
|
+
elif not G.has_loops():
|
123
|
+
self.number_of_loops = NULL
|
124
|
+
else:
|
125
|
+
try:
|
126
|
+
self.number_of_loops = <int *>check_calloc(self.g.n, sizeof(int))
|
127
|
+
except MemoryError:
|
128
|
+
free_short_digraph(self.g)
|
129
|
+
if self._directed:
|
130
|
+
free_short_digraph(self.g_rev)
|
131
|
+
raise
|
132
|
+
for i in range(self.g.n):
|
133
|
+
for tmp in range(out_degree(self.g, i)):
|
134
|
+
j = self.g.neighbors[i][tmp]
|
135
|
+
if j == i:
|
136
|
+
self.number_of_loops[i] += 1
|
137
|
+
if j > i:
|
138
|
+
break
|
139
|
+
|
140
|
+
# Defining the meaningless set of 'active' vertices. Because of CGraph.
|
141
|
+
# As well as num_verts and num_edges
|
142
|
+
bitset_init(self.active_vertices, self.g.n + 1)
|
143
|
+
bitset_set_first_n(self.active_vertices, self.g.n)
|
144
|
+
|
145
|
+
self.num_verts = self.g.n
|
146
|
+
self.num_arcs = self.g.m if self._directed else (2 * self.g.m)
|
147
|
+
|
148
|
+
def __dealloc__(self):
|
149
|
+
r"""
|
150
|
+
Free the memory.
|
151
|
+
|
152
|
+
TESTS::
|
153
|
+
|
154
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
155
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
156
|
+
"""
|
157
|
+
bitset_free(self.active_vertices)
|
158
|
+
free_short_digraph(self.g)
|
159
|
+
sig_free(self.number_of_loops)
|
160
|
+
if self._directed:
|
161
|
+
free_short_digraph(self.g_rev)
|
162
|
+
|
163
|
+
cpdef bint has_vertex(self, int v) except -1:
|
164
|
+
r"""
|
165
|
+
Test if a vertex belongs to the graph
|
166
|
+
|
167
|
+
INPUT:
|
168
|
+
|
169
|
+
- ``n`` -- integer
|
170
|
+
|
171
|
+
TESTS::
|
172
|
+
|
173
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
174
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
175
|
+
sage: g.has_vertex(1)
|
176
|
+
True
|
177
|
+
sage: g.has_vertex(10)
|
178
|
+
False
|
179
|
+
"""
|
180
|
+
return 0 <= v and v < self.g.n
|
181
|
+
|
182
|
+
cdef int add_vertex_unsafe(self, int v) except -1:
|
183
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
184
|
+
|
185
|
+
cdef int del_vertex_unsafe(self, int v) except -1:
|
186
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
187
|
+
|
188
|
+
def add_vertex(self, int k):
|
189
|
+
r"""
|
190
|
+
Add a vertex to the graph. No way.
|
191
|
+
|
192
|
+
TESTS::
|
193
|
+
|
194
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
195
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
196
|
+
sage: g.add_vertex(45)
|
197
|
+
Traceback (most recent call last):
|
198
|
+
...
|
199
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
200
|
+
"""
|
201
|
+
self.add_vertex_unsafe(k)
|
202
|
+
|
203
|
+
cpdef del_vertex(self, int k):
|
204
|
+
r"""
|
205
|
+
Remove a vertex from the graph. No way.
|
206
|
+
|
207
|
+
TESTS::
|
208
|
+
|
209
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
210
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
211
|
+
sage: g.del_vertex(45)
|
212
|
+
Traceback (most recent call last):
|
213
|
+
...
|
214
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
215
|
+
"""
|
216
|
+
self.del_vertex_unsafe(k)
|
217
|
+
|
218
|
+
cpdef list verts(self):
|
219
|
+
r"""
|
220
|
+
Return the list of vertices.
|
221
|
+
|
222
|
+
TESTS::
|
223
|
+
|
224
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
225
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
226
|
+
sage: g.verts()
|
227
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
228
|
+
"""
|
229
|
+
return list(range(self.g.n))
|
230
|
+
|
231
|
+
cdef int has_arc_label_unsafe(self, int u, int v, int l) except -1:
|
232
|
+
"""
|
233
|
+
Label is ignored.
|
234
|
+
"""
|
235
|
+
return ((0 <= u) and
|
236
|
+
(0 <= v) and
|
237
|
+
(u < self.g.n) and
|
238
|
+
(v < self.g.n) and
|
239
|
+
has_edge(self.g, u, v) != NULL)
|
240
|
+
|
241
|
+
cpdef bint has_arc(self, int u, int v) except -1:
|
242
|
+
r"""
|
243
|
+
Test if `uv` is an edge of the graph
|
244
|
+
|
245
|
+
INPUT:
|
246
|
+
|
247
|
+
- ``u``, ``v`` -- integers
|
248
|
+
|
249
|
+
TESTS::
|
250
|
+
|
251
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
252
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
253
|
+
sage: g.has_arc(0, 1)
|
254
|
+
True
|
255
|
+
sage: g.has_arc(0, 7)
|
256
|
+
False
|
257
|
+
"""
|
258
|
+
return self.has_arc_unsafe(u, v)
|
259
|
+
|
260
|
+
cdef inline int next_out_neighbor_unsafe(self, int u, int v, int* l) except -2:
|
261
|
+
"""
|
262
|
+
Return the next out-neighbor of ``u`` after ``v``.
|
263
|
+
|
264
|
+
If ``v`` is ``-1`` return the first neighbor of ``u``.
|
265
|
+
|
266
|
+
Return ``-1`` in case there does not exist such an out-neighbor.
|
267
|
+
|
268
|
+
.. NOTE::
|
269
|
+
|
270
|
+
A caller may not alter ``l``.
|
271
|
+
It is used to keep track of the current position.
|
272
|
+
"""
|
273
|
+
cdef int degree = out_degree(self.g, u)
|
274
|
+
if v == -1:
|
275
|
+
l[0] = -1
|
276
|
+
for i in range(l[0] + 1, degree):
|
277
|
+
if self.g.neighbors[u][i] != v:
|
278
|
+
l[0] = i
|
279
|
+
return self.g.neighbors[u][i]
|
280
|
+
else:
|
281
|
+
return -1
|
282
|
+
|
283
|
+
cdef inline int next_in_neighbor_unsafe(self, int u, int v, int* l) except -2:
|
284
|
+
"""
|
285
|
+
Return the next in-neighbor of ``u`` after ``v``.
|
286
|
+
|
287
|
+
If ``v`` is ``-1`` return the first neighbor of ``u``.
|
288
|
+
|
289
|
+
Return ``-1`` in case there does not exist such an in-neighbor.
|
290
|
+
|
291
|
+
.. NOTE::
|
292
|
+
|
293
|
+
A caller may not alter ``l``.
|
294
|
+
It is used to keep track of the current position.
|
295
|
+
"""
|
296
|
+
if not self._directed:
|
297
|
+
return self.next_out_neighbor_unsafe(u, v, l)
|
298
|
+
cdef int degree = out_degree(self.g_rev, u)
|
299
|
+
if v == -1:
|
300
|
+
l[0] = -1
|
301
|
+
for i in range(l[0] + 1, degree):
|
302
|
+
if self.g_rev.neighbors[u][i] != v:
|
303
|
+
l[0] = i
|
304
|
+
return self.g_rev.neighbors[u][i]
|
305
|
+
else:
|
306
|
+
return -1
|
307
|
+
|
308
|
+
cdef int out_neighbors_unsafe(self, int u, int *neighbors, int size) except -2:
|
309
|
+
cdef int degree = self.g.neighbors[u+1] - self.g.neighbors[u]
|
310
|
+
cdef int i
|
311
|
+
for i in range(min(degree, size)):
|
312
|
+
neighbors[i] = self.g.neighbors[u][i]
|
313
|
+
return -1 if size < degree else degree
|
314
|
+
|
315
|
+
cdef int in_neighbors_unsafe(self, int u, int *neighbors, int size) except -2:
|
316
|
+
if not self._directed:
|
317
|
+
return self.out_neighbors_unsafe(u, neighbors, size)
|
318
|
+
|
319
|
+
cdef int degree = self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]
|
320
|
+
cdef int i
|
321
|
+
for i in range(min(degree, size)):
|
322
|
+
neighbors[i] = self.g_rev.neighbors[u][i]
|
323
|
+
return -1 if size < degree else degree
|
324
|
+
|
325
|
+
cpdef list out_neighbors(self, int u):
|
326
|
+
r"""
|
327
|
+
List the out-neighbors of a vertex.
|
328
|
+
|
329
|
+
INPUT:
|
330
|
+
|
331
|
+
- ``u`` -- a vertex
|
332
|
+
|
333
|
+
TESTS::
|
334
|
+
|
335
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
336
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
337
|
+
sage: g.out_neighbors(0)
|
338
|
+
[1, 4, 5]
|
339
|
+
sage: g.out_neighbors(10)
|
340
|
+
Traceback (most recent call last):
|
341
|
+
...
|
342
|
+
LookupError: the vertex does not belong to the graph
|
343
|
+
"""
|
344
|
+
if u < 0 or u >= self.g.n:
|
345
|
+
raise LookupError("the vertex does not belong to the graph")
|
346
|
+
|
347
|
+
cdef int i
|
348
|
+
return [<int> self.g.neighbors[u][i] for i in range(out_degree(self.g, u))]
|
349
|
+
|
350
|
+
cpdef list in_neighbors(self, int u):
|
351
|
+
r"""
|
352
|
+
Return the in-neighbors of a vertex.
|
353
|
+
|
354
|
+
INPUT:
|
355
|
+
|
356
|
+
- ``u`` -- a vertex
|
357
|
+
|
358
|
+
TESTS::
|
359
|
+
|
360
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
361
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
362
|
+
sage: g.in_neighbors(0)
|
363
|
+
[1, 4, 5]
|
364
|
+
sage: g.in_neighbors(10)
|
365
|
+
Traceback (most recent call last):
|
366
|
+
...
|
367
|
+
LookupError: the vertex does not belong to the graph
|
368
|
+
"""
|
369
|
+
if not self._directed:
|
370
|
+
return self.out_neighbors(u)
|
371
|
+
|
372
|
+
if u < 0 or u >= self.g.n:
|
373
|
+
raise LookupError("the vertex does not belong to the graph")
|
374
|
+
|
375
|
+
cdef int i
|
376
|
+
return [<int> self.g_rev.neighbors[u][i] for i in range(out_degree(self.g_rev, u))]
|
377
|
+
|
378
|
+
cpdef int out_degree(self, int u) except -1:
|
379
|
+
r"""
|
380
|
+
Return the out-degree of a vertex
|
381
|
+
|
382
|
+
INPUT:
|
383
|
+
|
384
|
+
- ``u`` -- a vertex
|
385
|
+
|
386
|
+
TESTS::
|
387
|
+
|
388
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
389
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
390
|
+
sage: g.out_degree(0)
|
391
|
+
3
|
392
|
+
sage: g.out_degree(10)
|
393
|
+
Traceback (most recent call last):
|
394
|
+
...
|
395
|
+
LookupError: the vertex does not belong to the graph
|
396
|
+
"""
|
397
|
+
if u < 0 or u >= self.g.n:
|
398
|
+
raise LookupError("the vertex does not belong to the graph")
|
399
|
+
|
400
|
+
return out_degree(self.g, u)
|
401
|
+
|
402
|
+
cpdef int in_degree(self, int u) except -1:
|
403
|
+
r"""
|
404
|
+
Return the in-degree of a vertex
|
405
|
+
|
406
|
+
INPUT:
|
407
|
+
|
408
|
+
- ``u`` -- a vertex
|
409
|
+
|
410
|
+
TESTS::
|
411
|
+
|
412
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
|
413
|
+
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
|
414
|
+
sage: g.in_degree(0)
|
415
|
+
3
|
416
|
+
sage: g.in_degree(10)
|
417
|
+
Traceback (most recent call last):
|
418
|
+
...
|
419
|
+
LookupError: the vertex does not belong to the graph
|
420
|
+
"""
|
421
|
+
if u < 0 or u >= self.g.n:
|
422
|
+
raise LookupError("the vertex does not belong to the graph")
|
423
|
+
|
424
|
+
if not self._directed:
|
425
|
+
return out_degree(self.g, u)
|
426
|
+
return out_degree(self.g_rev, u)
|
427
|
+
|
428
|
+
|
429
|
+
cdef class StaticSparseBackend(CGraphBackend):
|
430
|
+
|
431
|
+
def __init__(self, G, loops=False, multiedges=False, sort=True):
|
432
|
+
"""
|
433
|
+
A graph :mod:`backend <sage.graphs.base.graph_backends>` for static
|
434
|
+
sparse graphs.
|
435
|
+
|
436
|
+
EXAMPLES::
|
437
|
+
|
438
|
+
sage: D = sage.graphs.base.sparse_graph.SparseGraphBackend(9)
|
439
|
+
sage: D.add_edge(0, 1, None, False)
|
440
|
+
sage: list(D.iterator_edges(range(9), True))
|
441
|
+
[(0, 1, None)]
|
442
|
+
|
443
|
+
::
|
444
|
+
|
445
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
446
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
447
|
+
sage: list(g.iterator_edges([0], 1))
|
448
|
+
[(0, 1, None), (0, 4, None), (0, 5, None)]
|
449
|
+
|
450
|
+
::
|
451
|
+
|
452
|
+
sage: # needs sage.combinat
|
453
|
+
sage: g = DiGraph(digraphs.DeBruijn(4, 3), data_structure='static_sparse')
|
454
|
+
sage: gi = DiGraph(g, data_structure='static_sparse')
|
455
|
+
sage: gi.edges(sort=True)[0]
|
456
|
+
('000', '000', '0')
|
457
|
+
sage: sorted(gi.edges_incident('111'))
|
458
|
+
[('111', '110', '0'),
|
459
|
+
('111', '111', '1'),
|
460
|
+
('111', '112', '2'),
|
461
|
+
('111', '113', '3')]
|
462
|
+
|
463
|
+
sage: set(g.edges(sort=False)) == set(gi.edges(sort=False)) # needs sage.combinat
|
464
|
+
True
|
465
|
+
|
466
|
+
::
|
467
|
+
|
468
|
+
sage: g = graphs.PetersenGraph()
|
469
|
+
sage: gi = Graph(g, data_structure='static_sparse')
|
470
|
+
sage: g == gi
|
471
|
+
True
|
472
|
+
sage: set(g.edges(sort=False)) == set(gi.edges(sort=False))
|
473
|
+
True
|
474
|
+
|
475
|
+
::
|
476
|
+
|
477
|
+
sage: gi = Graph({ 0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2}}, data_structure='static_sparse')
|
478
|
+
sage: (0, 4, 2) in gi.edges(sort=False)
|
479
|
+
True
|
480
|
+
sage: gi.has_edge(0, 4)
|
481
|
+
True
|
482
|
+
|
483
|
+
::
|
484
|
+
|
485
|
+
sage: G = Graph({1:{2:28, 6:10}, 2:{3:16, 7:14}, 3:{4:12}, 4:{5:22, 7:18}, 5:{6:25, 7:24}})
|
486
|
+
sage: GI = Graph({1:{2:28, 6:10}, 2:{3:16, 7:14}, 3:{4:12}, 4:{5:22, 7:18}, 5:{6:25, 7:24}}, data_structure='static_sparse')
|
487
|
+
sage: G == GI
|
488
|
+
True
|
489
|
+
|
490
|
+
::
|
491
|
+
|
492
|
+
sage: G = graphs.OddGraph(4)
|
493
|
+
sage: d = G.diameter()
|
494
|
+
sage: H = G.distance_graph(list(range(d + 1)))
|
495
|
+
sage: HI = Graph(H, data_structure='static_sparse')
|
496
|
+
sage: HI.size() == len(HI.edges(sort=False))
|
497
|
+
True
|
498
|
+
|
499
|
+
::
|
500
|
+
|
501
|
+
sage: g = Graph({1: {1: [1, 2, 3]}}, data_structure='static_sparse')
|
502
|
+
sage: g.size()
|
503
|
+
3
|
504
|
+
sage: g.order()
|
505
|
+
1
|
506
|
+
sage: g.vertices(sort=False)
|
507
|
+
[1]
|
508
|
+
sage: g.edges(sort=True)
|
509
|
+
[(1, 1, 1), (1, 1, 2), (1, 1, 3)]
|
510
|
+
|
511
|
+
:issue:`15810` is fixed::
|
512
|
+
|
513
|
+
sage: DiGraph({1: {2: ['a', 'b'], 3: ['c']}, 2: {3: ['d']}}, immutable=True).is_directed_acyclic()
|
514
|
+
True
|
515
|
+
"""
|
516
|
+
vertices = list(G)
|
517
|
+
if sort:
|
518
|
+
try:
|
519
|
+
vertices.sort()
|
520
|
+
except TypeError:
|
521
|
+
pass
|
522
|
+
cdef StaticSparseCGraph cg = <StaticSparseCGraph> StaticSparseCGraph(G, vertices)
|
523
|
+
self._cg = cg
|
524
|
+
|
525
|
+
self._directed = cg._directed
|
526
|
+
|
527
|
+
self._order = G.order()
|
528
|
+
|
529
|
+
# Does it allow loops/multiedges ?
|
530
|
+
self._loops = loops
|
531
|
+
self._multiedges = multiedges
|
532
|
+
|
533
|
+
# Dictionary translating a vertex int to a label, and the other way around.
|
534
|
+
self._vertex_to_labels = vertices
|
535
|
+
self._vertex_to_int = {v: i for i, v in enumerate(vertices)}
|
536
|
+
|
537
|
+
# Needed by CGraph. The first one is just an alias, and the second is
|
538
|
+
# useless : accessing _vertex_to_labels (which is a list) is faster than
|
539
|
+
# vertex_labels (which is a dictionary)
|
540
|
+
self.vertex_ints = self._vertex_to_int
|
541
|
+
self.vertex_labels = {i: v for i, v in enumerate(vertices)}
|
542
|
+
self._multiple_edges = self._multiedges
|
543
|
+
|
544
|
+
def has_vertex(self, v):
|
545
|
+
r"""
|
546
|
+
Test if the vertex belongs to the graph.
|
547
|
+
|
548
|
+
INPUT:
|
549
|
+
|
550
|
+
- ``v`` -- a vertex (or not?)
|
551
|
+
|
552
|
+
TESTS::
|
553
|
+
|
554
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
555
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
556
|
+
sage: g.has_vertex(0)
|
557
|
+
True
|
558
|
+
sage: g.has_vertex("Hey")
|
559
|
+
False
|
560
|
+
"""
|
561
|
+
return v in self._vertex_to_int
|
562
|
+
|
563
|
+
def add_vertex(self, object v):
|
564
|
+
r"""
|
565
|
+
Add a vertex to the graph. No way
|
566
|
+
|
567
|
+
INPUT:
|
568
|
+
|
569
|
+
- ``v`` -- a vertex (or not?)
|
570
|
+
|
571
|
+
TESTS::
|
572
|
+
|
573
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
574
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
575
|
+
sage: g.add_vertex(123)
|
576
|
+
Traceback (most recent call last):
|
577
|
+
...
|
578
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
579
|
+
"""
|
580
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
581
|
+
|
582
|
+
cpdef add_edge(self, object u, object v, object l, bint directed):
|
583
|
+
r"""
|
584
|
+
Add an edge to the graph. No way.
|
585
|
+
|
586
|
+
TESTS::
|
587
|
+
|
588
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
589
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
590
|
+
sage: g.add_edge(1,2,3,True)
|
591
|
+
Traceback (most recent call last):
|
592
|
+
...
|
593
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
594
|
+
"""
|
595
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
596
|
+
|
597
|
+
def add_edges(self, edges, directed, remove_loops=False):
|
598
|
+
r"""
|
599
|
+
Add edges to the graph. No way.
|
600
|
+
|
601
|
+
INPUT:
|
602
|
+
|
603
|
+
- ``edges`` -- a list of edges (or not?)
|
604
|
+
|
605
|
+
TESTS::
|
606
|
+
|
607
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
608
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
609
|
+
sage: g.add_edges([[1, 2]], True)
|
610
|
+
Traceback (most recent call last):
|
611
|
+
...
|
612
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
613
|
+
"""
|
614
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
615
|
+
|
616
|
+
def add_vertices(self, vertices):
|
617
|
+
r"""
|
618
|
+
Add vertices to the graph. No way.
|
619
|
+
|
620
|
+
INPUT:
|
621
|
+
|
622
|
+
- ``vertices`` -- a list of vertices (or not?)
|
623
|
+
|
624
|
+
TESTS::
|
625
|
+
|
626
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
627
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
628
|
+
sage: g.add_vertices([1, 2])
|
629
|
+
Traceback (most recent call last):
|
630
|
+
...
|
631
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
632
|
+
"""
|
633
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
634
|
+
|
635
|
+
def del_vertex(self, object v):
|
636
|
+
r"""
|
637
|
+
Delete a vertex from the graph. No way
|
638
|
+
|
639
|
+
INPUT:
|
640
|
+
|
641
|
+
- ``v`` -- a vertex (or not?)
|
642
|
+
|
643
|
+
TESTS::
|
644
|
+
|
645
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
646
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
647
|
+
sage: g.del_vertex(123)
|
648
|
+
Traceback (most recent call last):
|
649
|
+
...
|
650
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
651
|
+
|
652
|
+
Check that :issue:`39270` is fixed::
|
653
|
+
|
654
|
+
sage: g.del_vertex('a')
|
655
|
+
Traceback (most recent call last):
|
656
|
+
...
|
657
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
658
|
+
"""
|
659
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
660
|
+
|
661
|
+
def del_vertices(self, vertices):
|
662
|
+
r"""
|
663
|
+
Delete vertices from the graph. No way
|
664
|
+
|
665
|
+
INPUT:
|
666
|
+
|
667
|
+
- ``vertices`` -- a list of vertices (or not?)
|
668
|
+
|
669
|
+
TESTS::
|
670
|
+
|
671
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
672
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
673
|
+
sage: g.del_vertices([123, 234])
|
674
|
+
Traceback (most recent call last):
|
675
|
+
...
|
676
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
677
|
+
"""
|
678
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
679
|
+
|
680
|
+
cpdef del_edge(self, object u, object v, object l, bint directed):
|
681
|
+
r"""
|
682
|
+
Delete an edge of the graph. No way.
|
683
|
+
|
684
|
+
TESTS::
|
685
|
+
|
686
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
687
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
688
|
+
sage: g.del_edge(1,2,3,True)
|
689
|
+
Traceback (most recent call last):
|
690
|
+
...
|
691
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
692
|
+
"""
|
693
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
694
|
+
|
695
|
+
def del_edges(self, edges, directed):
|
696
|
+
r"""
|
697
|
+
Delete edges of the graph. No way.
|
698
|
+
|
699
|
+
TESTS::
|
700
|
+
|
701
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
702
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
703
|
+
sage: g.del_edges([[1,2,3]], True)
|
704
|
+
Traceback (most recent call last):
|
705
|
+
...
|
706
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
707
|
+
"""
|
708
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
709
|
+
|
710
|
+
def set_edge_label(self, u, v, l, directed):
|
711
|
+
r"""
|
712
|
+
Set edge label. No way.
|
713
|
+
|
714
|
+
TESTS::
|
715
|
+
|
716
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
717
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
718
|
+
sage: g.set_edge_label(1,2,3,True)
|
719
|
+
Traceback (most recent call last):
|
720
|
+
...
|
721
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
722
|
+
"""
|
723
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
724
|
+
|
725
|
+
def relabel(self, perm, directed):
|
726
|
+
r"""
|
727
|
+
Relabel the graphs' vertices. No way.
|
728
|
+
|
729
|
+
TESTS::
|
730
|
+
|
731
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
732
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
733
|
+
sage: g.relabel([],True)
|
734
|
+
Traceback (most recent call last):
|
735
|
+
...
|
736
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
737
|
+
"""
|
738
|
+
raise ValueError("graph is immutable; please change a copy instead (use function copy())")
|
739
|
+
|
740
|
+
def get_edge_label(self, object u, object v):
|
741
|
+
"""
|
742
|
+
Return the edge label for ``(u, v)``.
|
743
|
+
|
744
|
+
INPUT:
|
745
|
+
|
746
|
+
- ``u``, ``v`` -- two vertices
|
747
|
+
|
748
|
+
TESTS::
|
749
|
+
|
750
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
751
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
752
|
+
sage: print(g.get_edge_label(0, 1))
|
753
|
+
None
|
754
|
+
sage: print(g.get_edge_label(0, "Hey"))
|
755
|
+
Traceback (most recent call last):
|
756
|
+
...
|
757
|
+
LookupError: one of the two vertices does not belong to the graph
|
758
|
+
sage: print(g.get_edge_label(0, 7))
|
759
|
+
Traceback (most recent call last):
|
760
|
+
...
|
761
|
+
LookupError: the edge does not exist
|
762
|
+
|
763
|
+
::
|
764
|
+
|
765
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
766
|
+
sage: g = StaticSparseBackend(digraphs.DeBruijn(3, 2)) # needs sage.combinat
|
767
|
+
sage: g.has_edge('00', '01', '1') # needs sage.combinat
|
768
|
+
True
|
769
|
+
sage: g.has_edge('00', '01', '0') # needs sage.combinat
|
770
|
+
False
|
771
|
+
"""
|
772
|
+
try:
|
773
|
+
u = self._vertex_to_int[u]
|
774
|
+
v = self._vertex_to_int[v]
|
775
|
+
except KeyError:
|
776
|
+
raise LookupError("one of the two vertices does not belong to the graph")
|
777
|
+
|
778
|
+
cdef StaticSparseCGraph cg = self._cg
|
779
|
+
|
780
|
+
cdef uint32_t * edge = has_edge(cg.g, u, v)
|
781
|
+
if not edge:
|
782
|
+
raise LookupError("the edge does not exist")
|
783
|
+
|
784
|
+
# At this level, edge points toward a edge from u to v in the graph, but
|
785
|
+
# not necessarily to the leftmost edge. Hence, we first decrease edge to
|
786
|
+
# make it point toward the leftmost such edge, then build the list of
|
787
|
+
# all labels.
|
788
|
+
if self.multiple_edges(None):
|
789
|
+
return self._all_edge_labels(u, v, edge)
|
790
|
+
return edge_label(cg.g, edge)
|
791
|
+
|
792
|
+
cdef inline list _all_edge_labels(self, int u, int v, uint32_t* edge=NULL):
|
793
|
+
"""
|
794
|
+
Give the labels of all arcs from ``u`` to ``v``.
|
795
|
+
|
796
|
+
``u`` and ``v`` are the integers corresponding to vertices.
|
797
|
+
|
798
|
+
``edge`` may point to an edge from ``u`` to ``v``.
|
799
|
+
"""
|
800
|
+
cdef StaticSparseCGraph cg = self._cg
|
801
|
+
if edge is NULL:
|
802
|
+
edge = has_edge(cg.g, u, v)
|
803
|
+
|
804
|
+
while edge > cg.g.neighbors[u] and (edge - 1)[0] == v:
|
805
|
+
edge -= 1
|
806
|
+
cdef list l = []
|
807
|
+
while edge < cg.g.neighbors[u+1] and edge[0] == v:
|
808
|
+
l.append(edge_label(cg.g, edge))
|
809
|
+
edge += 1
|
810
|
+
return l
|
811
|
+
|
812
|
+
def has_edge(self, object u, object v, object l):
|
813
|
+
"""
|
814
|
+
Return whether this graph has edge ``(u, v)`` with label ``l``.
|
815
|
+
|
816
|
+
If ``l`` is ``None``, return whether this graph has an edge ``(u, v)``
|
817
|
+
with any label.
|
818
|
+
|
819
|
+
INPUT:
|
820
|
+
|
821
|
+
- ``u``, ``v`` -- two vertices
|
822
|
+
|
823
|
+
- ``l`` -- a label
|
824
|
+
|
825
|
+
TESTS::
|
826
|
+
|
827
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
828
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
829
|
+
sage: g.has_edge(0, 1, 'e')
|
830
|
+
False
|
831
|
+
sage: g.has_edge(0, 4, None)
|
832
|
+
True
|
833
|
+
"""
|
834
|
+
try:
|
835
|
+
u = self._vertex_to_int[u]
|
836
|
+
v = self._vertex_to_int[v]
|
837
|
+
except KeyError:
|
838
|
+
raise LookupError("one of the two vertices does not belong to the graph")
|
839
|
+
|
840
|
+
return self._has_labeled_edge_unsafe(u, v, l)
|
841
|
+
|
842
|
+
cdef inline bint _has_labeled_edge_unsafe(self, int u, int v, object l) except -1:
|
843
|
+
"""
|
844
|
+
Return whether ``self`` has an arc specified by indices of the vertices
|
845
|
+
and an arc label.
|
846
|
+
"""
|
847
|
+
cdef uint32_t * edge = NULL
|
848
|
+
cdef StaticSparseCGraph cg = <StaticSparseCGraph> (self._cg)
|
849
|
+
edge = has_edge(cg.g, u, v)
|
850
|
+
if not edge:
|
851
|
+
return False
|
852
|
+
if l is None:
|
853
|
+
return True
|
854
|
+
|
855
|
+
# At this level, edge points toward a edge from u to v in the graph, but
|
856
|
+
# not necessarily toward the right label. As there may be many uv edges
|
857
|
+
# with different labels, we first make edge point toward the leftmost uv
|
858
|
+
# edge, then scan them all to find the right label.
|
859
|
+
while edge > cg.g.neighbors[u] and (edge - 1)[0] == v:
|
860
|
+
edge -= 1
|
861
|
+
|
862
|
+
while edge[0] == v and edge < cg.g.neighbors[u+1]:
|
863
|
+
if edge_label(cg.g, edge) == l:
|
864
|
+
return True
|
865
|
+
edge += 1
|
866
|
+
|
867
|
+
return False
|
868
|
+
|
869
|
+
def iterator_in_edges(self, object vertices, bint labels):
|
870
|
+
"""
|
871
|
+
Iterate over the incoming edges incident to a sequence of vertices.
|
872
|
+
|
873
|
+
INPUT:
|
874
|
+
|
875
|
+
- ``vertices`` -- list of vertices
|
876
|
+
|
877
|
+
- ``labels`` -- whether to return labels too
|
878
|
+
|
879
|
+
TESTS::
|
880
|
+
|
881
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
882
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
883
|
+
sage: list(g.iterator_in_edges([0], False))
|
884
|
+
[(0, 1), (0, 4), (0, 5)]
|
885
|
+
sage: list(g.iterator_in_edges([0], True))
|
886
|
+
[(0, 1, None), (0, 4, None), (0, 5, None)]
|
887
|
+
|
888
|
+
::
|
889
|
+
|
890
|
+
sage: DiGraph(digraphs.Path(5), immutable=False).incoming_edges([2])
|
891
|
+
[(1, 2, None)]
|
892
|
+
sage: DiGraph(digraphs.Path(5), immutable=True).incoming_edges([2])
|
893
|
+
[(1, 2, None)]
|
894
|
+
"""
|
895
|
+
cdef StaticSparseCGraph cg = self._cg
|
896
|
+
if not cg._directed:
|
897
|
+
for x in self.iterator_out_edges(vertices, labels):
|
898
|
+
yield x
|
899
|
+
return
|
900
|
+
|
901
|
+
try:
|
902
|
+
vertices = [self._vertex_to_int[x] for x in vertices]
|
903
|
+
except KeyError:
|
904
|
+
raise LookupError("one of the vertices does not belong to the graph")
|
905
|
+
|
906
|
+
cdef int i, j
|
907
|
+
for i in vertices:
|
908
|
+
vi = self._vertex_to_labels[i]
|
909
|
+
for j in range(out_degree(cg.g_rev, i)):
|
910
|
+
if labels:
|
911
|
+
yield (self._vertex_to_labels[cg.g_rev.neighbors[i][j]],
|
912
|
+
vi,
|
913
|
+
edge_label(cg.g_rev, cg.g_rev.neighbors[i] + j))
|
914
|
+
else:
|
915
|
+
yield self._vertex_to_labels[cg.g_rev.neighbors[i][j]], vi
|
916
|
+
|
917
|
+
def iterator_out_edges(self, object vertices, bint labels):
|
918
|
+
"""
|
919
|
+
Iterate over the outbound edges incident to a sequence of vertices.
|
920
|
+
|
921
|
+
INPUT:
|
922
|
+
|
923
|
+
- ``vertices`` -- list of vertices
|
924
|
+
|
925
|
+
- ``labels`` -- whether to return labels too
|
926
|
+
|
927
|
+
TESTS::
|
928
|
+
|
929
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
930
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
931
|
+
sage: list(g.iterator_out_edges([0], False))
|
932
|
+
[(0, 1), (0, 4), (0, 5)]
|
933
|
+
sage: list(g.iterator_out_edges([0], True))
|
934
|
+
[(0, 1, None), (0, 4, None), (0, 5, None)]
|
935
|
+
"""
|
936
|
+
try:
|
937
|
+
vertices = [self._vertex_to_int[x] for x in vertices]
|
938
|
+
except KeyError:
|
939
|
+
raise LookupError("one of the vertices does not belong to the graph")
|
940
|
+
|
941
|
+
cdef StaticSparseCGraph cg = self._cg
|
942
|
+
cdef int i, j
|
943
|
+
for i in vertices:
|
944
|
+
vi = self._vertex_to_labels[i]
|
945
|
+
for j in range(out_degree(cg.g, i)):
|
946
|
+
if labels:
|
947
|
+
yield (vi,
|
948
|
+
self._vertex_to_labels[cg.g.neighbors[i][j]],
|
949
|
+
edge_label(cg.g, cg.g.neighbors[i] + j))
|
950
|
+
else:
|
951
|
+
yield vi, self._vertex_to_labels[cg.g.neighbors[i][j]]
|
952
|
+
|
953
|
+
def iterator_verts(self, vertices):
|
954
|
+
r"""
|
955
|
+
Iterate over the vertices.
|
956
|
+
|
957
|
+
INPUT:
|
958
|
+
|
959
|
+
- ``vertices`` -- list of objects; the method will only return the
|
960
|
+
elements of the graph which are contained in ``vertices``. It's not
|
961
|
+
very efficient. If ``vertices`` is equal to ``None``, all the vertices
|
962
|
+
are returned.
|
963
|
+
|
964
|
+
TESTS::
|
965
|
+
|
966
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
967
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
968
|
+
sage: list(g.iterator_verts(None))
|
969
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
970
|
+
sage: list(g.iterator_verts([1, "Hey", "I am a french fry"]))
|
971
|
+
[1]
|
972
|
+
"""
|
973
|
+
if vertices is None:
|
974
|
+
for x in self._vertex_to_labels:
|
975
|
+
yield x
|
976
|
+
return
|
977
|
+
|
978
|
+
cdef set V = set(vertices)
|
979
|
+
for x in self._vertex_to_labels:
|
980
|
+
if x in V:
|
981
|
+
yield x
|
982
|
+
return
|
983
|
+
|
984
|
+
def num_verts(self):
|
985
|
+
r"""
|
986
|
+
Return the number of vertices.
|
987
|
+
|
988
|
+
TESTS::
|
989
|
+
|
990
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
991
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
992
|
+
sage: g.num_verts()
|
993
|
+
10
|
994
|
+
"""
|
995
|
+
return self._order
|
996
|
+
|
997
|
+
def allows_loops(self, value=None):
|
998
|
+
r"""
|
999
|
+
Return whether the graph allows loops.
|
1000
|
+
|
1001
|
+
INPUT:
|
1002
|
+
|
1003
|
+
- ``value`` -- only useful for compatibility with other graph backends,
|
1004
|
+
where this method can be used to define this boolean. This method
|
1005
|
+
raises an exception if ``value`` is not equal to ``None``.
|
1006
|
+
|
1007
|
+
TESTS::
|
1008
|
+
|
1009
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
1010
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
1011
|
+
sage: g.allows_loops()
|
1012
|
+
False
|
1013
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph(), loops=True)
|
1014
|
+
sage: g.allows_loops()
|
1015
|
+
True
|
1016
|
+
"""
|
1017
|
+
if value is None:
|
1018
|
+
return self._loops
|
1019
|
+
else:
|
1020
|
+
raise ValueError("the graph is immutable and cannot be changed in any way")
|
1021
|
+
|
1022
|
+
def multiple_edges(self, value=None):
|
1023
|
+
r"""
|
1024
|
+
Return whether the graph allows multiple edges.
|
1025
|
+
|
1026
|
+
INPUT:
|
1027
|
+
|
1028
|
+
- ``value`` -- only useful for compatibility with other graph backends,
|
1029
|
+
where this method can be used to define this boolean. This method
|
1030
|
+
raises an exception if ``value`` is not equal to ``None``.
|
1031
|
+
|
1032
|
+
TESTS::
|
1033
|
+
|
1034
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
1035
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
1036
|
+
sage: g.multiple_edges()
|
1037
|
+
False
|
1038
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph(), multiedges=True)
|
1039
|
+
sage: g.multiple_edges()
|
1040
|
+
True
|
1041
|
+
"""
|
1042
|
+
if value is None:
|
1043
|
+
return self._multiedges
|
1044
|
+
else:
|
1045
|
+
raise ValueError("the graph is immutable and cannot be changed in any way")
|
1046
|
+
|
1047
|
+
def num_edges(self, directed):
|
1048
|
+
r"""
|
1049
|
+
Return the number of edges.
|
1050
|
+
|
1051
|
+
INPUT:
|
1052
|
+
|
1053
|
+
- ``directed`` -- boolean; whether to consider the graph as directed or
|
1054
|
+
not
|
1055
|
+
|
1056
|
+
TESTS::
|
1057
|
+
|
1058
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
1059
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
1060
|
+
sage: g.num_edges(False)
|
1061
|
+
15
|
1062
|
+
|
1063
|
+
Testing the exception::
|
1064
|
+
|
1065
|
+
sage: g = StaticSparseBackend(digraphs.Circuit(4))
|
1066
|
+
sage: g.num_edges(False)
|
1067
|
+
Traceback (most recent call last):
|
1068
|
+
...
|
1069
|
+
NotImplementedError: Sorry, I have no idea what is expected in this situation. I don't think that it is well-defined either, especially for multigraphs.
|
1070
|
+
|
1071
|
+
:issue:`15491`::
|
1072
|
+
|
1073
|
+
sage: g = digraphs.RandomDirectedGNP(10, .3)
|
1074
|
+
sage: gi = DiGraph(g, data_structure='static_sparse')
|
1075
|
+
sage: gi.size() == len(gi.edges(sort=False))
|
1076
|
+
True
|
1077
|
+
"""
|
1078
|
+
cdef StaticSparseCGraph cg = <StaticSparseCGraph> self._cg
|
1079
|
+
|
1080
|
+
if directed:
|
1081
|
+
if cg._directed:
|
1082
|
+
# Returns the real number of directed arcs
|
1083
|
+
return int(cg.g.m)
|
1084
|
+
else:
|
1085
|
+
# Returns twice the number of edges, minus the number of
|
1086
|
+
# loops. This is actually equal to the index of
|
1087
|
+
# cg.g.neighbors[cg.g.n] in the array `cg.g.edges`
|
1088
|
+
return int(cg.g.neighbors[cg.g.n] - cg.g.edges)
|
1089
|
+
else:
|
1090
|
+
if cg._directed:
|
1091
|
+
raise NotImplementedError("Sorry, I have no idea what is expected "
|
1092
|
+
"in this situation. I don't think "
|
1093
|
+
"that it is well-defined either, "
|
1094
|
+
"especially for multigraphs.")
|
1095
|
+
else:
|
1096
|
+
# Returns the number of edges
|
1097
|
+
return int(cg.g.m)
|
1098
|
+
|
1099
|
+
def iterator_edges(self, vertices, bint labels):
|
1100
|
+
r"""
|
1101
|
+
Iterate over the graph's edges.
|
1102
|
+
|
1103
|
+
INPUT:
|
1104
|
+
|
1105
|
+
- ``vertices`` -- list; only returns the edges incident to at least one
|
1106
|
+
vertex of ``vertices``
|
1107
|
+
|
1108
|
+
- ``labels`` -- boolean; whether to return edge labels too
|
1109
|
+
|
1110
|
+
TESTS::
|
1111
|
+
|
1112
|
+
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
|
1113
|
+
sage: g = StaticSparseBackend(graphs.PetersenGraph())
|
1114
|
+
sage: list(g.iterator_edges(g.iterator_verts(None), False))
|
1115
|
+
[(0, 1), (0, 4), (0, 5), (1, 2), (1, 6), (2, 3), (2, 7),
|
1116
|
+
(3, 4), (3, 8), (4, 9), (5, 7), (5, 8), (6, 8), (6, 9), (7, 9)]
|
1117
|
+
|
1118
|
+
:issue:`15665`::
|
1119
|
+
|
1120
|
+
sage: Graph(immutable=True).edges(sort=False)
|
1121
|
+
[]
|
1122
|
+
"""
|
1123
|
+
cdef FrozenBitset b_vertices
|
1124
|
+
|
1125
|
+
if not vertices:
|
1126
|
+
return
|
1127
|
+
|
1128
|
+
if self._directed:
|
1129
|
+
raise RuntimeError("this is not meant for directed graphs")
|
1130
|
+
|
1131
|
+
try:
|
1132
|
+
vertices = [self._vertex_to_int[x] for x in vertices]
|
1133
|
+
b_vertices = FrozenBitset(vertices)
|
1134
|
+
except KeyError:
|
1135
|
+
raise LookupError("one of the vertices does not belong to the graph")
|
1136
|
+
|
1137
|
+
cdef StaticSparseCGraph cg = self._cg
|
1138
|
+
cdef int i, j, tmp
|
1139
|
+
|
1140
|
+
for i in vertices:
|
1141
|
+
vi = self._vertex_to_labels[i]
|
1142
|
+
for tmp in range(out_degree(cg.g, i)):
|
1143
|
+
j = cg.g.neighbors[i][tmp]
|
1144
|
+
if j < i and j in b_vertices:
|
1145
|
+
continue
|
1146
|
+
if labels:
|
1147
|
+
yield (vi,
|
1148
|
+
self._vertex_to_labels[j],
|
1149
|
+
edge_label(cg.g, cg.g.neighbors[i] + tmp))
|
1150
|
+
else:
|
1151
|
+
yield vi, self._vertex_to_labels[j]
|
1152
|
+
|
1153
|
+
iterator_unsorted_edges = iterator_edges
|
1154
|
+
|
1155
|
+
cdef int _use_edge_iterator_on_subgraph(self, CGraphBackend other, object vertices, const int modus) except -1:
|
1156
|
+
"""
|
1157
|
+
Use an edge iterator on the subgraph induced by ``vertices`` and do something according to ``modus``.
|
1158
|
+
|
1159
|
+
INPUT:
|
1160
|
+
|
1161
|
+
- ``other`` -- a (mutable) subclass of :class:`CGraphBackend`
|
1162
|
+
- ``vertices`` -- list of vertex labels
|
1163
|
+
- ``modus`` -- integer representing the modus:
|
1164
|
+
- ``0`` -- initialize ``other`` to be the subgraph induced by the vertices;
|
1165
|
+
see :meth:`subgraph_given_vertices`
|
1166
|
+
- ``1`` -- test whether subgraph of ``self`` induced by the vertices is a subgraph of ``other``
|
1167
|
+
- ``2`` -- as ``1`` but ignore the labels
|
1168
|
+
"""
|
1169
|
+
cdef object v, l
|
1170
|
+
cdef int u_int, prev_u_int, v_int, l_int_other, tmp
|
1171
|
+
cdef StaticSparseCGraph cg = self._cg
|
1172
|
+
cdef CGraph cg_other = other.cg()
|
1173
|
+
cdef list b_vertices_2
|
1174
|
+
cdef FrozenBitset b_vertices
|
1175
|
+
cdef int n_vertices = len(vertices)
|
1176
|
+
cdef bint loops = other.loops()
|
1177
|
+
cdef bint ignore_multiple_edges = modus == 0 and self.multiple_edges(None) and not other.multiple_edges(None)
|
1178
|
+
|
1179
|
+
if self._directed and not other._directed and modus == 0:
|
1180
|
+
raise ValueError("cannot obtain an undirected subgraph of a directed graph")
|
1181
|
+
|
1182
|
+
if self._directed != other._directed and 1 <= modus <= 2:
|
1183
|
+
if self._directed:
|
1184
|
+
raise ValueError("cannot check if directed graph is a subgraph of an undirected")
|
1185
|
+
else:
|
1186
|
+
raise ValueError("cannot check if undirected graph is a subgraph of a directed")
|
1187
|
+
|
1188
|
+
try:
|
1189
|
+
b_vertices_2 = [self._vertex_to_int[x] for x in vertices]
|
1190
|
+
b_vertices = FrozenBitset(b_vertices_2)
|
1191
|
+
except KeyError:
|
1192
|
+
raise LookupError("one of the vertices does not belong to the graph")
|
1193
|
+
except ValueError:
|
1194
|
+
# Avoiding "Bitset must not be empty"
|
1195
|
+
# in this case there is nothing to do
|
1196
|
+
return 1
|
1197
|
+
|
1198
|
+
cdef int length = len(b_vertices)
|
1199
|
+
cdef int i
|
1200
|
+
cdef int* vertices_translation = <int *> sig_malloc(b_vertices.capacity() * sizeof(int))
|
1201
|
+
|
1202
|
+
try:
|
1203
|
+
# Iterate through the vertices.
|
1204
|
+
if cg_other.active_vertices.size < length:
|
1205
|
+
cg_other.realloc(length)
|
1206
|
+
for j in range(n_vertices):
|
1207
|
+
i = b_vertices_2[j]
|
1208
|
+
if i >= 0:
|
1209
|
+
v = self.vertex_label(i)
|
1210
|
+
if modus == 0:
|
1211
|
+
# Add the vertex and obtain the corresponding index.
|
1212
|
+
vertices_translation[i] = other.check_labelled_vertex(v, False)
|
1213
|
+
elif 1 <= modus <= 2:
|
1214
|
+
# Obtain the corresponding index if the vertex is contained in ``other``.
|
1215
|
+
foo = other.get_vertex_checked(v)
|
1216
|
+
if foo >= 0:
|
1217
|
+
vertices_translation[i] = foo
|
1218
|
+
else:
|
1219
|
+
# Not a subgraph.
|
1220
|
+
return 0
|
1221
|
+
|
1222
|
+
# Iterate through the edges.
|
1223
|
+
for v_int in b_vertices:
|
1224
|
+
prev_u_int = -1
|
1225
|
+
for tmp in range(out_degree(cg.g, v_int)):
|
1226
|
+
u_int = cg.g.neighbors[v_int][tmp]
|
1227
|
+
if (u_int < b_vertices.capacity() and bitset_in(b_vertices._bitset, u_int)
|
1228
|
+
and (u_int >= v_int or other._directed)):
|
1229
|
+
|
1230
|
+
if unlikely(ignore_multiple_edges and u_int == prev_u_int):
|
1231
|
+
# Delete multiple edges, if ``other`` does not allow them.
|
1232
|
+
continue
|
1233
|
+
|
1234
|
+
if modus == 0:
|
1235
|
+
prev_u_int = u_int
|
1236
|
+
|
1237
|
+
if unlikely(not loops and u_int == v_int):
|
1238
|
+
# Ignore loops, if ``other`` does not allow them.
|
1239
|
+
continue
|
1240
|
+
|
1241
|
+
l = edge_label(cg.g, cg.g.neighbors[v_int] + tmp)
|
1242
|
+
|
1243
|
+
# Will return ``0``, if ``other`` does not support edge labels.
|
1244
|
+
l_int_other = other.new_edge_label(l)
|
1245
|
+
|
1246
|
+
cg_other.add_arc_label_unsafe(vertices_translation[v_int], vertices_translation[u_int], l_int_other)
|
1247
|
+
|
1248
|
+
else:
|
1249
|
+
# Modus is 1 or 2.
|
1250
|
+
|
1251
|
+
# Check if the arc is contained in ``other``.
|
1252
|
+
|
1253
|
+
if unlikely(u_int == prev_u_int):
|
1254
|
+
# Check if all of the multiple edges are contained.
|
1255
|
+
if not other.multiple_edges(None):
|
1256
|
+
# ``other`` does not allow multiple edges.
|
1257
|
+
# As ``self`` has a multiple edges (not only allows), it cannot be a subgraph.
|
1258
|
+
return 0
|
1259
|
+
|
1260
|
+
all_arc_labels = self._all_edge_labels(v_int, u_int)
|
1261
|
+
all_arc_labels_other = other._all_edge_labels(vertices_translation[v_int], vertices_translation[u_int])
|
1262
|
+
if modus == 2:
|
1263
|
+
# Ignore the labels.
|
1264
|
+
if len(all_arc_labels) > len(all_arc_labels_other):
|
1265
|
+
return 0
|
1266
|
+
else:
|
1267
|
+
for l in all_arc_labels:
|
1268
|
+
try:
|
1269
|
+
all_arc_labels_other.remove(l)
|
1270
|
+
except ValueError:
|
1271
|
+
return 0
|
1272
|
+
|
1273
|
+
continue
|
1274
|
+
prev_u_int = u_int
|
1275
|
+
|
1276
|
+
l = edge_label(cg.g, cg.g.neighbors[v_int] + tmp)
|
1277
|
+
|
1278
|
+
if modus == 1:
|
1279
|
+
if not other._has_labeled_edge_unsafe(vertices_translation[v_int], vertices_translation[u_int], l):
|
1280
|
+
return 0
|
1281
|
+
else:
|
1282
|
+
# Ignore the label.
|
1283
|
+
if not cg_other.has_arc_unsafe(vertices_translation[v_int], vertices_translation[u_int]):
|
1284
|
+
return 0
|
1285
|
+
|
1286
|
+
finally:
|
1287
|
+
sig_free(vertices_translation)
|
1288
|
+
|
1289
|
+
return 1
|
1290
|
+
|
1291
|
+
def degree(self, v, directed):
|
1292
|
+
r"""
|
1293
|
+
Return the degree of a vertex.
|
1294
|
+
|
1295
|
+
INPUT:
|
1296
|
+
|
1297
|
+
- ``v`` -- a vertex
|
1298
|
+
|
1299
|
+
- ``directed`` -- boolean; whether to take into account the orientation
|
1300
|
+
of this graph in counting the degree of ``v``
|
1301
|
+
|
1302
|
+
EXAMPLES::
|
1303
|
+
|
1304
|
+
sage: g = Graph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1305
|
+
sage: g.degree(0)
|
1306
|
+
3
|
1307
|
+
|
1308
|
+
:issue:`17225` about the degree of a vertex with a loop::
|
1309
|
+
|
1310
|
+
sage: Graph({0: [0]}, immutable=True).degree(0)
|
1311
|
+
2
|
1312
|
+
sage: Graph({0: [0], 1: [0, 1, 1, 1]}, immutable=True).degree(1)
|
1313
|
+
7
|
1314
|
+
"""
|
1315
|
+
try:
|
1316
|
+
v = self._vertex_to_int[v]
|
1317
|
+
except KeyError:
|
1318
|
+
raise LookupError("the vertex does not belong to the graph")
|
1319
|
+
|
1320
|
+
cdef StaticSparseCGraph cg = self._cg
|
1321
|
+
|
1322
|
+
if directed:
|
1323
|
+
if cg._directed:
|
1324
|
+
return cg.in_degree(v) + cg.out_degree(v)
|
1325
|
+
else:
|
1326
|
+
return 2 * cg.out_degree(v)
|
1327
|
+
else:
|
1328
|
+
if cg._directed:
|
1329
|
+
raise NotImplementedError("Sorry, I have no idea what is expected "
|
1330
|
+
"in this situation. I don't think "
|
1331
|
+
"that it is well-defined either, "
|
1332
|
+
"especially for multigraphs.")
|
1333
|
+
else:
|
1334
|
+
return cg.out_degree(v) + (0 if not cg.number_of_loops else cg.number_of_loops[v])
|
1335
|
+
|
1336
|
+
def in_degree(self, v):
|
1337
|
+
r"""
|
1338
|
+
Return the in-degree of a vertex.
|
1339
|
+
|
1340
|
+
INPUT:
|
1341
|
+
|
1342
|
+
- ``v`` -- a vertex
|
1343
|
+
|
1344
|
+
EXAMPLES::
|
1345
|
+
|
1346
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1347
|
+
sage: g.in_degree(0)
|
1348
|
+
3
|
1349
|
+
"""
|
1350
|
+
try:
|
1351
|
+
v = self._vertex_to_int[v]
|
1352
|
+
except KeyError:
|
1353
|
+
raise LookupError("the vertex does not belong to the graph")
|
1354
|
+
|
1355
|
+
cdef StaticSparseCGraph cg = self._cg
|
1356
|
+
|
1357
|
+
if cg._directed:
|
1358
|
+
return cg.in_degree(v)
|
1359
|
+
else:
|
1360
|
+
return cg.out_degree(v)
|
1361
|
+
|
1362
|
+
def out_degree(self, v):
|
1363
|
+
r"""
|
1364
|
+
Return the out-degree of a vertex.
|
1365
|
+
|
1366
|
+
INPUT:
|
1367
|
+
|
1368
|
+
- ``v`` -- a vertex
|
1369
|
+
|
1370
|
+
EXAMPLES::
|
1371
|
+
|
1372
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1373
|
+
sage: g.out_degree(0)
|
1374
|
+
3
|
1375
|
+
"""
|
1376
|
+
try:
|
1377
|
+
v = self._vertex_to_int[v]
|
1378
|
+
except KeyError:
|
1379
|
+
raise LookupError("the vertex does not belong to the graph")
|
1380
|
+
|
1381
|
+
cdef StaticSparseCGraph cg = self._cg
|
1382
|
+
|
1383
|
+
return cg.out_degree(v)
|
1384
|
+
|
1385
|
+
def iterator_nbrs(self, v):
|
1386
|
+
r"""
|
1387
|
+
Iterate over the neighbors of a vertex.
|
1388
|
+
|
1389
|
+
INPUT:
|
1390
|
+
|
1391
|
+
- ``v`` -- a vertex
|
1392
|
+
|
1393
|
+
EXAMPLES::
|
1394
|
+
|
1395
|
+
sage: g = Graph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1396
|
+
sage: g.neighbors(0)
|
1397
|
+
[1, 4, 5]
|
1398
|
+
|
1399
|
+
TESTS:
|
1400
|
+
|
1401
|
+
Issue :issue:`25550` is fixed::
|
1402
|
+
|
1403
|
+
sage: g = DiGraph({0: [1]}, immutable=True)
|
1404
|
+
sage: g.neighbors(1)
|
1405
|
+
[0]
|
1406
|
+
sage: g = DiGraph({0: [0, 1, 1]}, loops=True, multiedges=True, immutable=True)
|
1407
|
+
sage: g.neighbors(0)
|
1408
|
+
[0, 1]
|
1409
|
+
sage: g = DiGraph({0: [1, 1], 1:[0, 0]}, multiedges=True, immutable=True)
|
1410
|
+
sage: g.neighbors(0)
|
1411
|
+
[1]
|
1412
|
+
sage: g.neighbors(1)
|
1413
|
+
[0]
|
1414
|
+
"""
|
1415
|
+
try:
|
1416
|
+
v = self._vertex_to_int[v]
|
1417
|
+
except KeyError:
|
1418
|
+
raise LookupError("the vertex does not belong to the graph")
|
1419
|
+
|
1420
|
+
cdef StaticSparseCGraph cg = self._cg
|
1421
|
+
cdef int i, u
|
1422
|
+
cdef set seen = set()
|
1423
|
+
|
1424
|
+
if cg._directed:
|
1425
|
+
for i in range(out_degree(cg.g, v)):
|
1426
|
+
u = cg.g.neighbors[v][i]
|
1427
|
+
if u not in seen:
|
1428
|
+
yield self._vertex_to_labels[u]
|
1429
|
+
seen.add(u)
|
1430
|
+
for i in range(out_degree(cg.g_rev, v)):
|
1431
|
+
u = cg.g_rev.neighbors[v][i]
|
1432
|
+
if u not in seen:
|
1433
|
+
yield self._vertex_to_labels[u]
|
1434
|
+
seen.add(u)
|
1435
|
+
else:
|
1436
|
+
for i in range(out_degree(cg.g, v)):
|
1437
|
+
u = cg.g.neighbors[v][i]
|
1438
|
+
if u not in seen:
|
1439
|
+
yield self._vertex_to_labels[cg.g.neighbors[v][i]]
|
1440
|
+
seen.add(u)
|
1441
|
+
|
1442
|
+
def iterator_out_nbrs(self, v):
|
1443
|
+
r"""
|
1444
|
+
Iterate over the out-neighbors of a vertex.
|
1445
|
+
|
1446
|
+
INPUT:
|
1447
|
+
|
1448
|
+
- ``v`` -- a vertex
|
1449
|
+
|
1450
|
+
EXAMPLES::
|
1451
|
+
|
1452
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1453
|
+
sage: g.neighbors_out(0)
|
1454
|
+
[1, 4, 5]
|
1455
|
+
"""
|
1456
|
+
try:
|
1457
|
+
v = self._vertex_to_int[v]
|
1458
|
+
except KeyError:
|
1459
|
+
raise LookupError("the vertex does not belong to the graph")
|
1460
|
+
|
1461
|
+
cdef StaticSparseCGraph cg = self._cg
|
1462
|
+
cdef int i, u
|
1463
|
+
cdef set seen = set()
|
1464
|
+
|
1465
|
+
for i in range(out_degree(cg.g, v)):
|
1466
|
+
u = cg.g.neighbors[v][i]
|
1467
|
+
if u not in seen:
|
1468
|
+
yield self._vertex_to_labels[u]
|
1469
|
+
seen.add(u)
|
1470
|
+
|
1471
|
+
def iterator_in_nbrs(self, v):
|
1472
|
+
r"""
|
1473
|
+
Iterate over the in-neighbors of a vertex.
|
1474
|
+
|
1475
|
+
INPUT:
|
1476
|
+
|
1477
|
+
- ``v`` -- a vertex
|
1478
|
+
|
1479
|
+
EXAMPLES::
|
1480
|
+
|
1481
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1482
|
+
sage: g.neighbors_in(0)
|
1483
|
+
[1, 4, 5]
|
1484
|
+
|
1485
|
+
TESTS::
|
1486
|
+
|
1487
|
+
sage: g = DiGraph({0: [1]}, immutable=True)
|
1488
|
+
sage: print(g.neighbors_in(1))
|
1489
|
+
[0]
|
1490
|
+
"""
|
1491
|
+
try:
|
1492
|
+
v = self._vertex_to_int[v]
|
1493
|
+
except KeyError:
|
1494
|
+
raise LookupError("the vertex does not belong to the graph")
|
1495
|
+
|
1496
|
+
cdef StaticSparseCGraph cg = self._cg
|
1497
|
+
cdef int i, u
|
1498
|
+
cdef set seen = set()
|
1499
|
+
|
1500
|
+
if cg._directed:
|
1501
|
+
for i in range(out_degree(cg.g_rev, v)):
|
1502
|
+
u = cg.g_rev.neighbors[v][i]
|
1503
|
+
if u not in seen:
|
1504
|
+
yield self._vertex_to_labels[u]
|
1505
|
+
seen.add(u)
|
1506
|
+
else:
|
1507
|
+
for i in range(out_degree(cg.g, v)):
|
1508
|
+
u = cg.g.neighbors[v][i]
|
1509
|
+
if u not in seen:
|
1510
|
+
yield self._vertex_to_labels[u]
|
1511
|
+
seen.add(u)
|
1512
|
+
|
1513
|
+
def add_vertex(self, v):
|
1514
|
+
r"""
|
1515
|
+
Addition of vertices is not available on an immutable graph.
|
1516
|
+
|
1517
|
+
EXAMPLES::
|
1518
|
+
|
1519
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1520
|
+
sage: g.add_vertex(1)
|
1521
|
+
Traceback (most recent call last):
|
1522
|
+
...
|
1523
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
1524
|
+
sage: g.add_vertices([1,2,3])
|
1525
|
+
Traceback (most recent call last):
|
1526
|
+
...
|
1527
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
1528
|
+
"""
|
1529
|
+
(<StaticSparseCGraph> self._cg).add_vertex(v)
|
1530
|
+
|
1531
|
+
def del_vertex(self, v):
|
1532
|
+
r"""
|
1533
|
+
Removal of vertices is not available on an immutable graph.
|
1534
|
+
|
1535
|
+
EXAMPLES::
|
1536
|
+
|
1537
|
+
sage: g = DiGraph(graphs.PetersenGraph(), data_structure='static_sparse')
|
1538
|
+
sage: g.delete_vertex(1)
|
1539
|
+
Traceback (most recent call last):
|
1540
|
+
...
|
1541
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
1542
|
+
sage: g.delete_vertices([1,2,3])
|
1543
|
+
Traceback (most recent call last):
|
1544
|
+
...
|
1545
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
1546
|
+
"""
|
1547
|
+
(<StaticSparseCGraph> self._cg).del_vertex(v)
|
1548
|
+
|
1549
|
+
|
1550
|
+
@cython.binding(True)
|
1551
|
+
def _run_it_on_static_instead(f):
|
1552
|
+
r"""
|
1553
|
+
A decorator function to force the (Di)Graph functions to compute from a
|
1554
|
+
static sparse graph
|
1555
|
+
|
1556
|
+
This decorator can be used on methods from (Di)Graph. When it is applied,
|
1557
|
+
the method that was meant to compute something on a graph first converts
|
1558
|
+
this graph to a static sparse graph, then does what it had to do on this new
|
1559
|
+
graph. Of course, it makes no sense to decorate Graph.add_vertex with it as
|
1560
|
+
such a method will never work on an immutable graph. But it can help find
|
1561
|
+
new bugs, from time to time.
|
1562
|
+
|
1563
|
+
EXAMPLES::
|
1564
|
+
|
1565
|
+
sage: from sage.graphs.base.static_sparse_backend import _run_it_on_static_instead
|
1566
|
+
sage: @_run_it_on_static_instead
|
1567
|
+
....: def new_graph_method(g):
|
1568
|
+
....: print("My backend is of type {}".format(type(g._backend)))
|
1569
|
+
sage: Graph.new_graph_method = new_graph_method
|
1570
|
+
sage: g = Graph(5)
|
1571
|
+
sage: print("My backend is of type {}".format(type(g._backend)))
|
1572
|
+
My backend is of type <class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
|
1573
|
+
sage: g.new_graph_method()
|
1574
|
+
My backend is of type <class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
|
1575
|
+
"""
|
1576
|
+
def same_function_on_static_version(*kwd, **kwds):
|
1577
|
+
if not isinstance(kwd[0]._backend, StaticSparseBackend):
|
1578
|
+
gcopy = kwd[0].copy(data_structure='static_sparse')
|
1579
|
+
return getattr(gcopy, f.__name__)(*kwd[1:], **kwds)
|
1580
|
+
else:
|
1581
|
+
return f(*kwd, **kwds)
|
1582
|
+
|
1583
|
+
return same_function_on_static_version
|