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,870 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
# cython: binding=True
|
3
|
+
r"""
|
4
|
+
Convexity properties of graphs
|
5
|
+
|
6
|
+
This class gathers the algorithms related to convexity in a graph. It implements
|
7
|
+
the following methods:
|
8
|
+
|
9
|
+
.. csv-table::
|
10
|
+
:class: contentstable
|
11
|
+
:widths: 30, 70
|
12
|
+
:delim: |
|
13
|
+
|
14
|
+
:meth:`ConvexityProperties.hull` | Return the convex hull of a set of vertices
|
15
|
+
:meth:`ConvexityProperties.hull_number` | Compute the hull number of a graph and a corresponding generating set
|
16
|
+
:meth:`geodetic_closure`| Return the geodetic closure of a set of vertices
|
17
|
+
:meth:`is_geodetic` | Check whether the input (di)graph is geodetic
|
18
|
+
|
19
|
+
Some of these methods can be used through the :class:`ConvexityProperties`
|
20
|
+
object returned by :meth:`Graph.convexity_properties`.
|
21
|
+
|
22
|
+
Methods
|
23
|
+
-------
|
24
|
+
"""
|
25
|
+
|
26
|
+
# ****************************************************************************
|
27
|
+
# Copyright (C) 2011 Nathann Cohen <nathann.cohen@gmail.com>
|
28
|
+
# 2021 David Coudert <david.coudert@inria.fr>
|
29
|
+
#
|
30
|
+
# This program is free software: you can redistribute it and/or modify
|
31
|
+
# it under the terms of the GNU General Public License as published by
|
32
|
+
# the Free Software Foundation, either version 2 of the License, or
|
33
|
+
# (at your option) any later version.
|
34
|
+
# https://www.gnu.org/licenses/
|
35
|
+
# ****************************************************************************
|
36
|
+
|
37
|
+
from sage.data_structures.binary_matrix cimport *
|
38
|
+
from sage.numerical.backends.generic_backend cimport GenericBackend
|
39
|
+
from sage.numerical.backends.generic_backend import get_solver
|
40
|
+
from sage.graphs.distances_all_pairs cimport c_distances_all_pairs
|
41
|
+
from cysignals.memory cimport sig_free
|
42
|
+
from memory_allocator cimport MemoryAllocator
|
43
|
+
from libc.stdint cimport uint32_t
|
44
|
+
from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
|
45
|
+
from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
|
46
|
+
from sage.graphs.base.static_sparse_graph cimport (short_digraph,
|
47
|
+
init_short_digraph,
|
48
|
+
free_short_digraph,
|
49
|
+
out_degree,
|
50
|
+
simple_BFS)
|
51
|
+
|
52
|
+
|
53
|
+
cdef class ConvexityProperties:
|
54
|
+
r"""
|
55
|
+
This class gathers the algorithms related to convexity in a graph.
|
56
|
+
|
57
|
+
**Definitions**
|
58
|
+
|
59
|
+
A set `S \subseteq V(G)` of vertices is said to be convex if for all `u,v\in
|
60
|
+
S` the set `S` contains all the vertices located on a shortest path between
|
61
|
+
`u` and `v`. Alternatively, a set `S` is said to be convex if the distances
|
62
|
+
satisfy `\forall u,v\in S, \forall w\in V\backslash S : d_{G}(u,w) +
|
63
|
+
d_{G}(w,v) > d_{G}(u,v)`.
|
64
|
+
|
65
|
+
The convex hull `h(S)` of a set `S` of vertices is defined as the smallest
|
66
|
+
convex set containing `S`.
|
67
|
+
|
68
|
+
It is a closure operator, as trivially `S\subseteq h(S)` and `h(h(S)) =
|
69
|
+
h(S)`.
|
70
|
+
|
71
|
+
**What this class contains**
|
72
|
+
|
73
|
+
As operations on convex sets generally involve the computation of distances
|
74
|
+
between vertices, this class' purpose is to cache that information so that
|
75
|
+
computing the convex hulls of several different sets of vertices does not
|
76
|
+
imply recomputing several times the distances between the vertices.
|
77
|
+
|
78
|
+
In order to compute the convex hull of a set `S` it is possible to write the
|
79
|
+
following algorithm:
|
80
|
+
|
81
|
+
For any pair `u,v` of elements in the set `S`, and for any vertex `w`
|
82
|
+
outside of it, add `w` to `S` if `d_{G}(u,w) + d_{G}(w,v) =
|
83
|
+
d_{G}(u,v)`. When no vertex can be added anymore, the set `S` is convex
|
84
|
+
|
85
|
+
The distances are not actually that relevant. The same algorithm can be
|
86
|
+
implemented by remembering for each pair `u, v` of vertices the list of
|
87
|
+
elements `w` satisfying the condition, and this is precisely what this class
|
88
|
+
remembers, encoded as bitsets to make storage and union operations more
|
89
|
+
efficient.
|
90
|
+
|
91
|
+
.. NOTE::
|
92
|
+
|
93
|
+
* This class is useful if you compute the convex hulls of many sets in
|
94
|
+
the same graph, or if you want to compute the hull number itself as it
|
95
|
+
involves many calls to :meth:`hull`
|
96
|
+
|
97
|
+
* Using this class on non-connected graphs is a waste of space and
|
98
|
+
efficiency ! If your graph is disconnected, the best for you is to
|
99
|
+
deal independently with each connected component, whatever you are
|
100
|
+
doing.
|
101
|
+
|
102
|
+
**Possible improvements**
|
103
|
+
|
104
|
+
When computing a convex set, all the pairs of elements belonging to the set
|
105
|
+
`S` are enumerated several times.
|
106
|
+
|
107
|
+
* There should be a smart way to avoid enumerating pairs of vertices which
|
108
|
+
have already been tested. The cost of each of them is not very high, so
|
109
|
+
keeping track of those which have been tested already may be too expensive
|
110
|
+
to gain any efficiency.
|
111
|
+
|
112
|
+
* The ordering in which they are visited is currently purely lexicographic,
|
113
|
+
while there is a Poset structure to exploit. In particular, when two
|
114
|
+
vertices `u, v` are far apart and generate a set `h(\{u,v\})` of vertices,
|
115
|
+
all the pairs of vertices `u', v'\in h(\{u,v\})` satisfy `h(\{u',v'\})
|
116
|
+
\subseteq h(\{u,v\})`, and so it is useless to test the pair `u', v'` when
|
117
|
+
both `u` and `v` where present.
|
118
|
+
|
119
|
+
* The information cached is for any pair `u,v` of vertices the list of
|
120
|
+
elements `z` with `d_{G}(u,w) + d_{G}(w,v) = d_{G}(u,v)`. This is not in
|
121
|
+
general equal to `h(\{u,v\})` !
|
122
|
+
|
123
|
+
Nothing says these recommendations will actually lead to any actual
|
124
|
+
improvements. There are just some ideas remembered while writing this
|
125
|
+
code. Trying to optimize may well lead to lost in efficiency on many
|
126
|
+
instances.
|
127
|
+
|
128
|
+
EXAMPLES::
|
129
|
+
|
130
|
+
sage: from sage.graphs.convexity_properties import ConvexityProperties
|
131
|
+
sage: g = graphs.PetersenGraph()
|
132
|
+
sage: CP = ConvexityProperties(g)
|
133
|
+
sage: CP.hull([1, 3])
|
134
|
+
[1, 2, 3]
|
135
|
+
sage: CP.hull_number() # needs sage.numerical.mip
|
136
|
+
3
|
137
|
+
|
138
|
+
TESTS::
|
139
|
+
|
140
|
+
sage: ConvexityProperties(digraphs.Circuit(5))
|
141
|
+
Traceback (most recent call last):
|
142
|
+
...
|
143
|
+
NotImplementedError: this is currently implemented for Graphs only, but only minor updates are needed if you want to make it support DiGraphs too
|
144
|
+
"""
|
145
|
+
|
146
|
+
def __init__(self, G):
|
147
|
+
r"""
|
148
|
+
Constructor.
|
149
|
+
|
150
|
+
EXAMPLES::
|
151
|
+
|
152
|
+
sage: from sage.graphs.convexity_properties import ConvexityProperties
|
153
|
+
sage: g = graphs.PetersenGraph()
|
154
|
+
sage: ConvexityProperties(g)
|
155
|
+
<sage.graphs.convexity_properties.ConvexityProperties object at ...>
|
156
|
+
"""
|
157
|
+
from sage.graphs.digraph import DiGraph
|
158
|
+
if isinstance(G, DiGraph):
|
159
|
+
raise NotImplementedError("this is currently implemented for Graphs only, "
|
160
|
+
"but only minor updates are needed if you want "
|
161
|
+
"to make it support DiGraphs too")
|
162
|
+
|
163
|
+
# Cached number of vertices
|
164
|
+
cdef int n = G.order()
|
165
|
+
self._n = n
|
166
|
+
|
167
|
+
cdef int i = 0
|
168
|
+
cdef int j, k
|
169
|
+
|
170
|
+
# Build mappings integer <-> vertices.
|
171
|
+
# Must be consistent with the mappings used in c_distances_all_pairs
|
172
|
+
self._list_integers_to_vertices = list(G)
|
173
|
+
self._dict_vertices_to_integers = {v: i for i, v in enumerate(self._list_integers_to_vertices)}
|
174
|
+
|
175
|
+
# Computation of distances between all pairs. Costly.
|
176
|
+
cdef unsigned short* c_distances = c_distances_all_pairs(G, vertex_list=self._list_integers_to_vertices)
|
177
|
+
# Temporary variables
|
178
|
+
cdef unsigned short* d_i
|
179
|
+
cdef unsigned short* d_j
|
180
|
+
cdef int d_ij
|
181
|
+
|
182
|
+
# We use a binary matrix with one row per pair of vertices,
|
183
|
+
# so n * (n - 1) / 2 rows. Row u * n + v is a bitset whose 1 bits are
|
184
|
+
# the vertices located on a shortest path from vertex u to v
|
185
|
+
#
|
186
|
+
# Note that u < v
|
187
|
+
binary_matrix_init(self._cache_hull_pairs, n * (n - 1) / 2, n)
|
188
|
+
binary_matrix_fill(self._cache_hull_pairs, 0)
|
189
|
+
cdef bitset_t * p_bitset = self._cache_hull_pairs.rows
|
190
|
+
|
191
|
+
# Filling the cache
|
192
|
+
#
|
193
|
+
# The p_bitset variable iterates over the successive elements of the cache
|
194
|
+
#
|
195
|
+
# For any pair i, j of vertices (i < j), we built the bitset of all the
|
196
|
+
# elements k which are on a shortest path from i to j
|
197
|
+
|
198
|
+
for i in range(n):
|
199
|
+
# Caching the distances from i to the other vertices
|
200
|
+
d_i = c_distances + n * i
|
201
|
+
|
202
|
+
for j in range(i + 1, n):
|
203
|
+
# Caching the distances from j to the other vertices
|
204
|
+
d_j = c_distances + n * j
|
205
|
+
|
206
|
+
# Caching the distance between i and j
|
207
|
+
d_ij = d_i[j]
|
208
|
+
|
209
|
+
# Filling it
|
210
|
+
for k in range(n):
|
211
|
+
if d_i[k] + d_j[k] == d_ij:
|
212
|
+
bitset_add(p_bitset[0], k)
|
213
|
+
|
214
|
+
# Next bitset !
|
215
|
+
p_bitset = p_bitset + 1
|
216
|
+
|
217
|
+
sig_free(c_distances)
|
218
|
+
|
219
|
+
def __dealloc__(self):
|
220
|
+
r"""
|
221
|
+
Destructor.
|
222
|
+
|
223
|
+
EXAMPLES::
|
224
|
+
|
225
|
+
sage: from sage.graphs.convexity_properties import ConvexityProperties
|
226
|
+
sage: g = graphs.PetersenGraph()
|
227
|
+
sage: ConvexityProperties(g)
|
228
|
+
<sage.graphs.convexity_properties.ConvexityProperties object at ...>
|
229
|
+
"""
|
230
|
+
binary_matrix_free(self._cache_hull_pairs)
|
231
|
+
|
232
|
+
cdef list _vertices_to_integers(self, vertices):
|
233
|
+
r"""
|
234
|
+
Convert a list of vertices to a list of integers with the cached data.
|
235
|
+
"""
|
236
|
+
return [self._dict_vertices_to_integers[v] for v in vertices]
|
237
|
+
|
238
|
+
cdef list _integers_to_vertices(self, list integers):
|
239
|
+
r"""
|
240
|
+
Convert a list of integers to a list of vertices with the cached data.
|
241
|
+
"""
|
242
|
+
cdef int i
|
243
|
+
return [self._list_integers_to_vertices[i] for i in integers]
|
244
|
+
|
245
|
+
cdef _bitset_convex_hull(self, bitset_t hull):
|
246
|
+
r"""
|
247
|
+
Compute the convex hull of a list of vertices given as a bitset.
|
248
|
+
|
249
|
+
(this method returns nothing and modifies the input)
|
250
|
+
"""
|
251
|
+
cdef int count
|
252
|
+
cdef int tmp_count
|
253
|
+
cdef int i, j
|
254
|
+
|
255
|
+
cdef bitset_t * p_bitset
|
256
|
+
|
257
|
+
# Current size of the set
|
258
|
+
count = bitset_len(hull)
|
259
|
+
|
260
|
+
while True:
|
261
|
+
|
262
|
+
# Iterating over all the elements in the cache
|
263
|
+
p_bitset = self._cache_hull_pairs.rows
|
264
|
+
|
265
|
+
# For any vertex i
|
266
|
+
for i in range(self._n - 1):
|
267
|
+
|
268
|
+
# If i is not in the current set, we skip it !
|
269
|
+
if not bitset_in(hull, i):
|
270
|
+
p_bitset = p_bitset + (self._n - 1 - i)
|
271
|
+
continue
|
272
|
+
|
273
|
+
# If it is, we iterate over all the elements j
|
274
|
+
for j in range(i + 1, self._n):
|
275
|
+
|
276
|
+
# If both i and j are inside, we add all the (cached)
|
277
|
+
# vertices on a shortest ij-path
|
278
|
+
|
279
|
+
if bitset_in(hull, j):
|
280
|
+
bitset_union(hull, hull, p_bitset[0])
|
281
|
+
|
282
|
+
# Next bitset !
|
283
|
+
p_bitset = p_bitset + 1
|
284
|
+
|
285
|
+
tmp_count = bitset_len(hull)
|
286
|
+
|
287
|
+
# If we added nothing new during the previous loop, our set is
|
288
|
+
# convex !
|
289
|
+
if tmp_count == count:
|
290
|
+
return
|
291
|
+
|
292
|
+
# Otherwise, update and back to the loop
|
293
|
+
count = tmp_count
|
294
|
+
|
295
|
+
cpdef hull(self, list vertices):
|
296
|
+
r"""
|
297
|
+
Return the convex hull of a set of vertices.
|
298
|
+
|
299
|
+
INPUT:
|
300
|
+
|
301
|
+
- ``vertices`` -- list of vertices
|
302
|
+
|
303
|
+
EXAMPLES::
|
304
|
+
|
305
|
+
sage: from sage.graphs.convexity_properties import ConvexityProperties
|
306
|
+
sage: g = graphs.PetersenGraph()
|
307
|
+
sage: CP = ConvexityProperties(g)
|
308
|
+
sage: CP.hull([1, 3])
|
309
|
+
[1, 2, 3]
|
310
|
+
"""
|
311
|
+
cdef bitset_t bs
|
312
|
+
bitset_init(bs, self._n)
|
313
|
+
bitset_set_first_n(bs, 0)
|
314
|
+
|
315
|
+
for v in vertices:
|
316
|
+
bitset_add(bs, <mp_bitcnt_t> self._dict_vertices_to_integers[v])
|
317
|
+
|
318
|
+
self._bitset_convex_hull(bs)
|
319
|
+
|
320
|
+
cdef list answer = self._integers_to_vertices(bitset_list(bs))
|
321
|
+
|
322
|
+
bitset_free(bs)
|
323
|
+
|
324
|
+
return answer
|
325
|
+
|
326
|
+
cdef _greedy_increase(self, bitset_t bs):
|
327
|
+
r"""
|
328
|
+
Given a bitset whose hull is not the whole set, greedily add vertices
|
329
|
+
and stop before its hull is the whole set.
|
330
|
+
|
331
|
+
.. NOTE::
|
332
|
+
|
333
|
+
* Counting the bits at each turn is not the best way...
|
334
|
+
"""
|
335
|
+
cdef bitset_t tmp
|
336
|
+
bitset_init(tmp, self._n)
|
337
|
+
|
338
|
+
for i in range(self._n):
|
339
|
+
if not bitset_in(bs, i):
|
340
|
+
bitset_copy(tmp, bs)
|
341
|
+
bitset_add(tmp, i)
|
342
|
+
self._bitset_convex_hull(tmp)
|
343
|
+
if bitset_len(tmp) < self._n:
|
344
|
+
bitset_add(bs, i)
|
345
|
+
|
346
|
+
bitset_free(tmp)
|
347
|
+
|
348
|
+
cpdef hull_number(self, value_only=True, verbose=False):
|
349
|
+
r"""
|
350
|
+
Compute the hull number and a corresponding generating set.
|
351
|
+
|
352
|
+
The hull number `hn(G)` of a graph `G` is the cardinality of a smallest
|
353
|
+
set of vertices `S` such that `h(S)=V(G)`.
|
354
|
+
|
355
|
+
INPUT:
|
356
|
+
|
357
|
+
- ``value_only`` -- boolean (default: ``True``); whether to return only
|
358
|
+
the hull number (default) or a minimum set whose convex hull is the
|
359
|
+
whole graph
|
360
|
+
|
361
|
+
- ``verbose`` -- boolean (default: ``False``); whether to display
|
362
|
+
information on the LP
|
363
|
+
|
364
|
+
**COMPLEXITY:**
|
365
|
+
|
366
|
+
This problem is NP-Hard [HLT1993]_, but seems to be of the "nice" kind.
|
367
|
+
Update this comment if you fall on hard instances `:-)`
|
368
|
+
|
369
|
+
**ALGORITHM:**
|
370
|
+
|
371
|
+
This is solved by linear programming.
|
372
|
+
|
373
|
+
As the function `h(S)` associating to each set `S` its convex hull is a
|
374
|
+
closure operator, it is clear that any set `S_G` of vertices such that
|
375
|
+
`h(S_G)=V(G)` must satisfy `S_G \not \subseteq C` for any *proper*
|
376
|
+
convex set `C \subsetneq V(G)`. The following formulation is hence
|
377
|
+
correct
|
378
|
+
|
379
|
+
.. MATH::
|
380
|
+
|
381
|
+
\text{Minimize :}& \sum_{v\in G}b_v\\
|
382
|
+
\text{Such that :}&\\
|
383
|
+
&\forall C\subsetneq V(G)\text{ a proper convex set }\\
|
384
|
+
&\sum_{v\in V(G)\backslash C} b_v \geq 1
|
385
|
+
|
386
|
+
Of course, the number of convex sets -- and so the number of constraints
|
387
|
+
-- can be huge, and hard to enumerate, so at first an incomplete
|
388
|
+
formulation is solved (it is missing some constraints). If the answer
|
389
|
+
returned by the LP solver is a set `S` generating the whole graph, then
|
390
|
+
it is optimal and so is returned. Otherwise, the constraint
|
391
|
+
corresponding to the set `h(S)` can be added to the LP, which makes the
|
392
|
+
answer `S` infeasible, and another solution computed.
|
393
|
+
|
394
|
+
This being said, simply adding the constraint corresponding to `h(S)` is
|
395
|
+
a bit slow, as these sets can be large (and the corresponding constraint
|
396
|
+
a bit weak). To improve it a bit, before being added, the set `h(S)` is
|
397
|
+
"greedily enriched" to a set `S'` with vertices for as long as
|
398
|
+
`h(S')\neq V(G)`. This way, we obtain a set `S'` with `h(S)\subseteq
|
399
|
+
h(S')\subsetneq V(G)`, and the constraint corresponding to `h(S')` --
|
400
|
+
which is stronger than the one corresponding to `h(S)` -- is added.
|
401
|
+
|
402
|
+
This can actually be seen as a hitting set problem on the complement of
|
403
|
+
convex sets.
|
404
|
+
|
405
|
+
EXAMPLES:
|
406
|
+
|
407
|
+
The Hull number of Petersen's graph::
|
408
|
+
|
409
|
+
sage: from sage.graphs.convexity_properties import ConvexityProperties
|
410
|
+
sage: g = graphs.PetersenGraph()
|
411
|
+
sage: CP = ConvexityProperties(g)
|
412
|
+
sage: CP.hull_number() # needs sage.numerical.mip
|
413
|
+
3
|
414
|
+
sage: generating_set = CP.hull_number(value_only=False) # needs sage.numerical.mip
|
415
|
+
sage: CP.hull(generating_set) # needs sage.numerical.mip
|
416
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
417
|
+
"""
|
418
|
+
cdef int i
|
419
|
+
cdef list constraint # temporary variable to add constraints to the LP
|
420
|
+
|
421
|
+
if self._n <= 2:
|
422
|
+
if value_only:
|
423
|
+
return self._n
|
424
|
+
else:
|
425
|
+
return self._list_integers_to_vertices
|
426
|
+
|
427
|
+
cdef GenericBackend p = <GenericBackend> get_solver(constraint_generation=True)
|
428
|
+
|
429
|
+
# Minimization
|
430
|
+
p.set_sense(False)
|
431
|
+
|
432
|
+
# We have exactly n binary variables, all of them with a coefficient of
|
433
|
+
# 1 in the objective function
|
434
|
+
p.add_variables(self._n, 0, None, True, False, False, 1, None)
|
435
|
+
|
436
|
+
# We know that at least 2 vertices are required to cover the whole graph
|
437
|
+
p.add_linear_constraint([(i, 1) for i in range(self._n)], 2, None)
|
438
|
+
|
439
|
+
# The set of vertices generated by the current LP solution
|
440
|
+
cdef bitset_t current_hull
|
441
|
+
bitset_init(current_hull, self._n)
|
442
|
+
|
443
|
+
# Which is at first empty
|
444
|
+
bitset_set_first_n(current_hull, 1)
|
445
|
+
|
446
|
+
while True:
|
447
|
+
|
448
|
+
# Greedily increase it to obtain a better constraint
|
449
|
+
self._greedy_increase(current_hull)
|
450
|
+
|
451
|
+
if verbose:
|
452
|
+
print("Adding a constraint corresponding to convex set ",
|
453
|
+
end="")
|
454
|
+
print(bitset_list(current_hull))
|
455
|
+
|
456
|
+
# Building the corresponding constraint
|
457
|
+
constraint = []
|
458
|
+
for i in range(self._n):
|
459
|
+
if not bitset_in(current_hull, i):
|
460
|
+
constraint.append((i, 1))
|
461
|
+
|
462
|
+
p.add_linear_constraint(constraint, 1, None)
|
463
|
+
|
464
|
+
p.solve()
|
465
|
+
|
466
|
+
# Computing the current solution's convex hull
|
467
|
+
bitset_set_first_n(current_hull, 0)
|
468
|
+
|
469
|
+
for i in range(self._n):
|
470
|
+
if p.get_variable_value(i) > .5:
|
471
|
+
bitset_add(current_hull, i)
|
472
|
+
|
473
|
+
self._bitset_convex_hull(current_hull)
|
474
|
+
|
475
|
+
# Are we done ?
|
476
|
+
if bitset_len(current_hull) == self._n:
|
477
|
+
break
|
478
|
+
|
479
|
+
bitset_free(current_hull)
|
480
|
+
|
481
|
+
if value_only:
|
482
|
+
return <int> p.get_objective_value()
|
483
|
+
|
484
|
+
constraint = []
|
485
|
+
for i in range(self._n):
|
486
|
+
if p.get_variable_value(i) > .5:
|
487
|
+
constraint.append(i)
|
488
|
+
|
489
|
+
return self._integers_to_vertices(constraint)
|
490
|
+
|
491
|
+
|
492
|
+
def geodetic_closure(G, S):
|
493
|
+
r"""
|
494
|
+
Return the geodetic closure of the set of vertices `S` in `G`.
|
495
|
+
|
496
|
+
The geodetic closure `g(S)` of a subset of vertices `S` of a graph `G` is in
|
497
|
+
[HLT1993]_ as the set of all vertices that lie on a shortest `u-v` path for
|
498
|
+
any pair of vertices `u,v \in S`. We assume that `g(\emptyset) = \emptyset`
|
499
|
+
and that `g(\{u\}) = \{u\}` for any `u` in `G`.
|
500
|
+
|
501
|
+
.. WARNING::
|
502
|
+
|
503
|
+
This operation is **not** a closure function. Indeed, a closure function
|
504
|
+
must satisfy the property that `f(f(X))` should be equal to `f(X)`,
|
505
|
+
which is not always the case here. The term ``closure`` is used here to
|
506
|
+
follow the terminology of the domain. See for instance [HLT1993]_.
|
507
|
+
|
508
|
+
Here, we implement a simple algorithm to determine this set. Roughly, for
|
509
|
+
each vertex `u \in S`, the algorithm first performs a breadth first search
|
510
|
+
from `u` to get distances, and then identifies the vertices of `G` lying on
|
511
|
+
a shortest path from `u` to any `v\in S` using a reversal traversal from
|
512
|
+
vertices in `S`. This algorithm has time complexity in `O(|S|(n + m))` for
|
513
|
+
``SparseGraph``, `O(|S|(n + m) + n^2)` for ``DenseGraph`` and
|
514
|
+
space complexity in `O(n + m)`.
|
515
|
+
|
516
|
+
INPUT:
|
517
|
+
|
518
|
+
- ``G`` -- a Sage graph
|
519
|
+
|
520
|
+
- ``S`` -- a subset of vertices of `G`
|
521
|
+
|
522
|
+
EXAMPLES:
|
523
|
+
|
524
|
+
The vertices of the Petersen graph can be obtained by a geodetic closure of
|
525
|
+
four of its vertices::
|
526
|
+
|
527
|
+
sage: from sage.graphs.convexity_properties import geodetic_closure
|
528
|
+
sage: G = graphs.PetersenGraph()
|
529
|
+
sage: geodetic_closure(G, [0, 2, 8, 9])
|
530
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
531
|
+
|
532
|
+
The vertices of a 2D grid can be obtained by a geodetic closure of
|
533
|
+
two vertices::
|
534
|
+
|
535
|
+
sage: G = graphs.Grid2dGraph(4, 4)
|
536
|
+
sage: c = G.geodetic_closure([(0, 0), (3, 3)])
|
537
|
+
sage: len(c) == G.order()
|
538
|
+
True
|
539
|
+
|
540
|
+
If two vertices belong to different connected components of a graph, their
|
541
|
+
geodetic closure is trivial::
|
542
|
+
|
543
|
+
sage: G = Graph([(0, 1), (2, 3)])
|
544
|
+
sage: geodetic_closure(G, [0, 2])
|
545
|
+
[0, 2]
|
546
|
+
|
547
|
+
The geodetic closure does not satisfy the closure function property that
|
548
|
+
`f(f(X))` should be equal to `f(X)`::
|
549
|
+
|
550
|
+
sage: G = graphs.DiamondGraph()
|
551
|
+
sage: G.subdivide_edge((1, 2), 1)
|
552
|
+
sage: geodetic_closure(G, [0, 3])
|
553
|
+
[0, 1, 2, 3]
|
554
|
+
sage: geodetic_closure(G, geodetic_closure(G, [0, 3]))
|
555
|
+
[0, 1, 2, 3, 4]
|
556
|
+
|
557
|
+
TESTS::
|
558
|
+
|
559
|
+
sage: G = graphs.DiamondGraph()
|
560
|
+
sage: geodetic_closure(G, [])
|
561
|
+
[]
|
562
|
+
sage: geodetic_closure(G, [1])
|
563
|
+
[1]
|
564
|
+
sage: S = geodetic_closure(G, list(G))
|
565
|
+
sage: all(u in G for u in S) and len(S) == G.order()
|
566
|
+
True
|
567
|
+
sage: S = geodetic_closure(G, G)
|
568
|
+
sage: all(u in G for u in S) and len(S) == G.order()
|
569
|
+
True
|
570
|
+
sage: geodetic_closure(G, [1, 'foo'])
|
571
|
+
Traceback (most recent call last):
|
572
|
+
...
|
573
|
+
ValueError: S is not a subset of vertices of the graph
|
574
|
+
sage: geodetic_closure(digraphs.Path(3), [0, 1])
|
575
|
+
Traceback (most recent call last):
|
576
|
+
...
|
577
|
+
NotImplementedError: the geodetic closure of digraphs has not been implemented yet
|
578
|
+
|
579
|
+
The method is valid for immutable graphs::
|
580
|
+
|
581
|
+
sage: G = graphs.RandomGNP(10, .7)
|
582
|
+
sage: G._backend
|
583
|
+
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
|
584
|
+
sage: H = Graph(G, immutable=True)
|
585
|
+
sage: H._backend
|
586
|
+
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
|
587
|
+
sage: geodetic_closure(G, [0, 3]) == geodetic_closure(H, [0, 3])
|
588
|
+
True
|
589
|
+
"""
|
590
|
+
if G.is_directed():
|
591
|
+
raise NotImplementedError("the geodetic closure of digraphs has not been implemented yet")
|
592
|
+
S = list(S)
|
593
|
+
if not S:
|
594
|
+
return []
|
595
|
+
elif any(v not in G for v in S):
|
596
|
+
raise ValueError("S is not a subset of vertices of the graph")
|
597
|
+
elif len(S) == 1 or len(S) == G.order():
|
598
|
+
return S
|
599
|
+
elif not G.is_connected():
|
600
|
+
L = []
|
601
|
+
for g in G.connected_components_subgraphs():
|
602
|
+
Sg = [u for u in S if u in g]
|
603
|
+
L.extend(geodetic_closure(g, Sg))
|
604
|
+
return L
|
605
|
+
|
606
|
+
cdef int n = G.order()
|
607
|
+
cdef int nS = len(S)
|
608
|
+
cdef list int_to_vertex
|
609
|
+
cdef dict vertex_to_int
|
610
|
+
|
611
|
+
# Copy the graph as a short digraph
|
612
|
+
cdef StaticSparseCGraph cg
|
613
|
+
cdef short_digraph sd
|
614
|
+
if isinstance(G, StaticSparseBackend):
|
615
|
+
cg = <StaticSparseCGraph> G._cg
|
616
|
+
sd = <short_digraph> cg.g
|
617
|
+
int_to_vertex = cg._vertex_to_labels
|
618
|
+
vertex_to_int = cg._vertex_to_int
|
619
|
+
else:
|
620
|
+
int_to_vertex = list(G)
|
621
|
+
vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)}
|
622
|
+
init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
|
623
|
+
|
624
|
+
cdef list S_int = [vertex_to_int[u] for u in S]
|
625
|
+
|
626
|
+
# Allocate some data structures
|
627
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
628
|
+
cdef uint32_t * distances = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
|
629
|
+
cdef uint32_t * waiting_list = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
|
630
|
+
if not distances or not waiting_list:
|
631
|
+
free_short_digraph(sd)
|
632
|
+
raise MemoryError()
|
633
|
+
cdef bitset_t seen
|
634
|
+
cdef bitset_t visited
|
635
|
+
cdef bitset_t closure
|
636
|
+
bitset_init(seen, n)
|
637
|
+
bitset_init(visited, n)
|
638
|
+
bitset_init(closure, n)
|
639
|
+
|
640
|
+
cdef int ui, vi, xi, yi, i_begin, i_end, i, j, k
|
641
|
+
|
642
|
+
# Vertices in S are in the closure
|
643
|
+
bitset_clear(closure)
|
644
|
+
for ui in S_int:
|
645
|
+
bitset_add(closure, ui)
|
646
|
+
|
647
|
+
# We now explore geodesics between vertices in S, and we avoid visiting
|
648
|
+
# twice the geodesics between u and v
|
649
|
+
for i in range(nS - 1):
|
650
|
+
ui = S_int[i]
|
651
|
+
|
652
|
+
# Compute distances from ui using BFS
|
653
|
+
_ = simple_BFS(sd, ui, distances, NULL, waiting_list, seen)
|
654
|
+
|
655
|
+
# Perform a reverse BFS from vertices in S, following only vertices
|
656
|
+
# along a shortest path from ui to identify vertices of the geodetic
|
657
|
+
# closure.
|
658
|
+
bitset_clear(visited)
|
659
|
+
i_begin = 0
|
660
|
+
i_end = 0
|
661
|
+
for j in range(i + 1, nS):
|
662
|
+
vi = S_int[j]
|
663
|
+
if not bitset_in(seen, vi) or bitset_in(visited, vi):
|
664
|
+
# vi is not reachable from ui using BFS or has already been
|
665
|
+
# considered for the geodetic closure from ui
|
666
|
+
continue
|
667
|
+
|
668
|
+
# We explore all vertices on a shortest path from ui to vi
|
669
|
+
waiting_list[i_end] = vi
|
670
|
+
i_end += 1
|
671
|
+
bitset_add(visited, vi)
|
672
|
+
|
673
|
+
while i_begin < i_end:
|
674
|
+
xi = waiting_list[i_begin]
|
675
|
+
i_begin += 1
|
676
|
+
|
677
|
+
for k in range(out_degree(sd, xi)):
|
678
|
+
yi = sd.neighbors[xi][k]
|
679
|
+
if distances[xi] == distances[yi] + 1:
|
680
|
+
# yi in on a shortest path to ui
|
681
|
+
if not bitset_in(visited, yi):
|
682
|
+
waiting_list[i_end] = yi
|
683
|
+
i_end += 1
|
684
|
+
bitset_add(visited, yi)
|
685
|
+
|
686
|
+
# Now, visited contains all vertices on geodesics from ui to any other
|
687
|
+
# vertex in S
|
688
|
+
bitset_union(closure, closure, visited)
|
689
|
+
|
690
|
+
cdef list ret = [int_to_vertex[ui] for ui in range(n) if bitset_in(closure, ui)]
|
691
|
+
|
692
|
+
bitset_free(seen)
|
693
|
+
bitset_free(visited)
|
694
|
+
bitset_free(closure)
|
695
|
+
if not isinstance(G, StaticSparseBackend):
|
696
|
+
free_short_digraph(sd)
|
697
|
+
|
698
|
+
return ret
|
699
|
+
|
700
|
+
|
701
|
+
def is_geodetic(G):
|
702
|
+
r"""
|
703
|
+
Check whether the input (di)graph is geodetic.
|
704
|
+
|
705
|
+
A graph `G` is *geodetic* if there exists only one shortest path between
|
706
|
+
every pair of its vertices. This can be checked in time `O(nm)` for
|
707
|
+
``SparseGraph`` and `O(nm+n^2)` for ``DenseGraph`` in unweighted (di)graphs
|
708
|
+
with `n` nodes and `m` edges. Examples of geodetic graphs are trees, cliques
|
709
|
+
and odd cycles. See the :wikipedia:`Geodetic_graph` for more details.
|
710
|
+
|
711
|
+
(Di)graphs with multiple edges are not considered geodetic.
|
712
|
+
|
713
|
+
INPUT:
|
714
|
+
|
715
|
+
- ``G`` -- a graph or a digraph
|
716
|
+
|
717
|
+
EXAMPLES:
|
718
|
+
|
719
|
+
Trees, cliques and odd cycles are geodetic::
|
720
|
+
|
721
|
+
sage: T = graphs.RandomTree(20)
|
722
|
+
sage: T.is_geodetic()
|
723
|
+
True
|
724
|
+
sage: all(graphs.CompleteGraph(n).is_geodetic() for n in range(8))
|
725
|
+
True
|
726
|
+
sage: all(graphs.CycleGraph(n).is_geodetic() for n in range(3, 16, 2))
|
727
|
+
True
|
728
|
+
|
729
|
+
Even cycles of order at least 4 are not geodetic::
|
730
|
+
|
731
|
+
sage: all(graphs.CycleGraph(n).is_geodetic() for n in range(4, 17, 2))
|
732
|
+
False
|
733
|
+
|
734
|
+
The Petersen graph is geodetic::
|
735
|
+
|
736
|
+
sage: P = graphs.PetersenGraph()
|
737
|
+
sage: P.is_geodetic()
|
738
|
+
True
|
739
|
+
|
740
|
+
Grid graphs are not geodetic::
|
741
|
+
|
742
|
+
sage: G = graphs.Grid2dGraph(2, 3)
|
743
|
+
sage: G.is_geodetic()
|
744
|
+
False
|
745
|
+
|
746
|
+
This method is also valid for digraphs::
|
747
|
+
|
748
|
+
sage: G = DiGraph(graphs.PetersenGraph())
|
749
|
+
sage: G.is_geodetic()
|
750
|
+
True
|
751
|
+
sage: G = digraphs.Path(5)
|
752
|
+
sage: G.add_path([0, 'a', 'b', 'c', 4])
|
753
|
+
sage: G.is_geodetic()
|
754
|
+
False
|
755
|
+
|
756
|
+
TESTS::
|
757
|
+
|
758
|
+
sage: all(g.is_geodetic() for g in graphs(3)) # needs nauty
|
759
|
+
True
|
760
|
+
sage: all((2*g).is_geodetic() for g in graphs(3)) # needs nauty
|
761
|
+
True
|
762
|
+
sage: G = graphs.CycleGraph(5)
|
763
|
+
sage: G.allow_loops(True)
|
764
|
+
sage: G.add_edges([(u, u) for u in G])
|
765
|
+
sage: G.is_geodetic()
|
766
|
+
True
|
767
|
+
sage: G.allow_multiple_edges(True)
|
768
|
+
sage: G.is_geodetic()
|
769
|
+
True
|
770
|
+
sage: G.add_edge(G.random_edge())
|
771
|
+
sage: G.is_geodetic()
|
772
|
+
False
|
773
|
+
|
774
|
+
The method is valid for immutable graphs::
|
775
|
+
|
776
|
+
sage: G = graphs.RandomGNP(10, .7)
|
777
|
+
sage: G._backend
|
778
|
+
<sage.graphs.base.sparse_graph.SparseGraphBackend ...>
|
779
|
+
sage: H = Graph(G, immutable=True)
|
780
|
+
sage: H._backend
|
781
|
+
<sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
|
782
|
+
sage: G.is_geodetic() == H.is_geodetic()
|
783
|
+
True
|
784
|
+
"""
|
785
|
+
if G.has_multiple_edges():
|
786
|
+
return False
|
787
|
+
|
788
|
+
if G.order() < 4:
|
789
|
+
return True
|
790
|
+
|
791
|
+
# Copy the graph as a short digraph
|
792
|
+
cdef int n = G.order()
|
793
|
+
cdef StaticSparseCGraph cg
|
794
|
+
cdef short_digraph sd
|
795
|
+
if isinstance(G, StaticSparseBackend):
|
796
|
+
cg = <StaticSparseCGraph> G._cg
|
797
|
+
sd = <short_digraph> cg.g
|
798
|
+
else:
|
799
|
+
init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G))
|
800
|
+
|
801
|
+
# Allocate some data structures
|
802
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
803
|
+
cdef uint32_t * distances = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
|
804
|
+
cdef uint32_t * waiting_list = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
|
805
|
+
if not distances or not waiting_list:
|
806
|
+
if not isinstance(G, StaticSparseBackend):
|
807
|
+
free_short_digraph(sd)
|
808
|
+
raise MemoryError()
|
809
|
+
cdef bitset_t seen
|
810
|
+
bitset_init(seen, n)
|
811
|
+
|
812
|
+
# We now explore geodesics between vertices in S, and we avoid visiting
|
813
|
+
# twice the geodesics between u and v
|
814
|
+
|
815
|
+
cdef uint32_t source, u, v
|
816
|
+
cdef uint32_t waiting_beginning
|
817
|
+
cdef uint32_t waiting_end
|
818
|
+
cdef uint32_t * p_tmp
|
819
|
+
cdef uint32_t * end
|
820
|
+
cdef uint32_t ** p_vertices = sd.neighbors
|
821
|
+
|
822
|
+
for source in range(n):
|
823
|
+
|
824
|
+
# Compute distances from source using BFS
|
825
|
+
bitset_clear(seen)
|
826
|
+
bitset_add(seen, source)
|
827
|
+
distances[source] = 0
|
828
|
+
waiting_beginning = 0
|
829
|
+
waiting_end = 0
|
830
|
+
waiting_list[waiting_beginning] = source
|
831
|
+
|
832
|
+
# For as long as there are vertices left to explore
|
833
|
+
while waiting_beginning <= waiting_end:
|
834
|
+
|
835
|
+
# We pick the first one
|
836
|
+
v = waiting_list[waiting_beginning]
|
837
|
+
p_tmp = p_vertices[v]
|
838
|
+
end = p_vertices[v + 1]
|
839
|
+
|
840
|
+
# and we iterate over all the outneighbors u of v
|
841
|
+
while p_tmp < end:
|
842
|
+
u = p_tmp[0]
|
843
|
+
|
844
|
+
# If we notice one of these neighbors is not seen yet, we set
|
845
|
+
# its parameters and add it to the queue to be explored later.
|
846
|
+
# Otherwise, we check whether we have detected a second shortest
|
847
|
+
# path between source and v.
|
848
|
+
if not bitset_in(seen, u):
|
849
|
+
distances[u] = distances[v] + 1
|
850
|
+
bitset_add(seen, u)
|
851
|
+
waiting_end += 1
|
852
|
+
waiting_list[waiting_end] = u
|
853
|
+
elif distances[u] == distances[v] + 1:
|
854
|
+
# G is not geodetic
|
855
|
+
bitset_free(seen)
|
856
|
+
if not isinstance(G, StaticSparseBackend):
|
857
|
+
free_short_digraph(sd)
|
858
|
+
return False
|
859
|
+
|
860
|
+
p_tmp += 1
|
861
|
+
|
862
|
+
# We go to the next vertex in the queue
|
863
|
+
waiting_beginning += 1
|
864
|
+
|
865
|
+
bitset_free(seen)
|
866
|
+
if not isinstance(G, StaticSparseBackend):
|
867
|
+
free_short_digraph(sd)
|
868
|
+
|
869
|
+
# The graph is geodetic
|
870
|
+
return True
|