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,459 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Partial cubes
|
4
|
+
|
5
|
+
The code in this module that recognizes partial cubes is originally
|
6
|
+
from the PADS library by David Eppstein, which is available at
|
7
|
+
http://www.ics.uci.edu/~eppstein/PADS/ under the MIT license. It has a
|
8
|
+
quadratic runtime and has been described in [Epp2008]_.
|
9
|
+
|
10
|
+
For more information on partial cubes, see the :wikipedia:`Partial cube`.
|
11
|
+
|
12
|
+
Recognition algorithm
|
13
|
+
---------------------
|
14
|
+
|
15
|
+
Definitions
|
16
|
+
^^^^^^^^^^^
|
17
|
+
|
18
|
+
A **partial cube** is an isometric subgraph `G` of a
|
19
|
+
:meth:`~sage.graphs.graph_generators.GraphGenerators.CubeGraph` (of
|
20
|
+
possibly high dimension). Consequently, the vertices of `G` can be
|
21
|
+
labelled with binary sequences in such a way that the distance between
|
22
|
+
two vertices `u,v\in G` is the Hamming distance between their labels.
|
23
|
+
|
24
|
+
**Tokens** and their **action**: in the terminology of
|
25
|
+
[Epp2008]_, a token represents a transition of the form:
|
26
|
+
|
27
|
+
*switch the `k`-th bit of the binary string from 0 to 1*
|
28
|
+
|
29
|
+
Each token can be matched with a 'reversed' token that performs the
|
30
|
+
same switch in the opposite direction. Alternatively, a token can be
|
31
|
+
seen as a set of disjoint (directed) edges of `G`, corresponding to
|
32
|
+
the transitions. When a vertex `v\in G` is the source of such an edge,
|
33
|
+
it is said that the token *acts* on `v`.
|
34
|
+
|
35
|
+
Observations
|
36
|
+
^^^^^^^^^^^^
|
37
|
+
|
38
|
+
**Shortest paths**: in a hypercube, a shortest path between two
|
39
|
+
vertices uses each token at most once. Furthermore, it cannot use both
|
40
|
+
a token and it reverse.
|
41
|
+
|
42
|
+
**Cycles**: a cycle in a partial cube is necessarily even, as
|
43
|
+
hypercubes are bipartite. If an edge `e` of a cycle `C` belongs to a
|
44
|
+
token `T`, then the edge opposite to `e` in `C` belongs to the reverse
|
45
|
+
of `T`.
|
46
|
+
|
47
|
+
**Incident edges**: all `2d_G(v)` arcs incident to a given vertex
|
48
|
+
belong to as many different tokens.
|
49
|
+
|
50
|
+
Algorithm
|
51
|
+
^^^^^^^^^
|
52
|
+
|
53
|
+
**Labeling**: Iteratively, the algorithm selects a vertex `v\in G`,
|
54
|
+
which is naturally associated to `2d(v)` tokens. It then performs a
|
55
|
+
breadth-first search from `v`, applying the previous observation on
|
56
|
+
cycles to attribute a token to some of the edges it meets. None of the
|
57
|
+
edges whose token remains undecided after this step can belong to one
|
58
|
+
of those `2d(v)` tokens, by virtue of the observation on shortest
|
59
|
+
paths.
|
60
|
+
|
61
|
+
The labeled edges can then be simplified (contracted) if the previous
|
62
|
+
step did not lead to a contradiction, and the procedure is applied
|
63
|
+
again until the graph is contracted to a single vertex and all edges
|
64
|
+
are labeled.
|
65
|
+
|
66
|
+
A partial cube is correctly labeled at this step, but some other
|
67
|
+
graphs can also satisfy the procedure.
|
68
|
+
|
69
|
+
**Checking the labeling**: once all tokens are defined and the
|
70
|
+
vertices are labeled with a binary string, we check that they define
|
71
|
+
an isometric subgraph of the hypercube. To ensure that the distance
|
72
|
+
`d(v_0,u)` is what we expect for any vertex `u`, it is sufficient to
|
73
|
+
find, for any vertex `u`, a neighbor `n_u` of `u` whose Hamming
|
74
|
+
distance with `v_0` is strictly less than the Hamming distance between
|
75
|
+
`u` and `v_0`. Here is the algorithm used to check the labeling:
|
76
|
+
|
77
|
+
* For an initial vertex `v`, run a BFS starting from `v`, and
|
78
|
+
associate to every other vertex `u` a token that brings `u` closer
|
79
|
+
to `v`. This yields shortest paths from every vertex to `v`.
|
80
|
+
|
81
|
+
* Assuming that the information is computed (and correct) for `v`, it
|
82
|
+
is easy to update it for a neighbor `v'` of `v`. Indeed, if we write
|
83
|
+
`T` the token that turns `v` into `v'`, only the vertices which were
|
84
|
+
associated with the reverse of `T` need to select a new neighbour. All
|
85
|
+
others can remain as they were previously.
|
86
|
+
|
87
|
+
With this second observation, one can efficiently check that the
|
88
|
+
distance between all pairs of vertices are what they should be. In
|
89
|
+
the implementation, the sequence of the sources `(v, v', ...)` is
|
90
|
+
given by a depth-first search.
|
91
|
+
|
92
|
+
Functions
|
93
|
+
---------
|
94
|
+
"""
|
95
|
+
|
96
|
+
|
97
|
+
def breadth_first_level_search(G, start):
|
98
|
+
r"""
|
99
|
+
Generate a sequence of dictionaries, each mapping the vertices at
|
100
|
+
distance ``i`` from ``start`` to the set of their neighbours at
|
101
|
+
distance ``i+1``.
|
102
|
+
|
103
|
+
Originally written by D. Eppstein for the PADS library
|
104
|
+
(http://www.ics.uci.edu/~eppstein/PADS/).
|
105
|
+
|
106
|
+
INPUT:
|
107
|
+
|
108
|
+
- ``G`` -- a graph to perform the search on
|
109
|
+
|
110
|
+
- ``start`` -- vertex or list of vertices from which to start the traversal
|
111
|
+
|
112
|
+
EXAMPLES::
|
113
|
+
|
114
|
+
sage: H = digraphs.DeBruijn(3,2) # needs sage.combinat
|
115
|
+
sage: list(sage.graphs.partial_cube.breadth_first_level_search(H, '00')) # needs sage.combinat
|
116
|
+
[{'00': {'01', '02'}},
|
117
|
+
{'01': {'10', '11', '12'}, '02': {'20', '21', '22'}},
|
118
|
+
{'10': set(),
|
119
|
+
'11': set(),
|
120
|
+
'12': set(),
|
121
|
+
'20': set(),
|
122
|
+
'21': set(),
|
123
|
+
'22': set()}]
|
124
|
+
"""
|
125
|
+
neighbors = G.neighbor_out_iterator
|
126
|
+
visited = set()
|
127
|
+
if isinstance(start, list):
|
128
|
+
currentLevel = start
|
129
|
+
else:
|
130
|
+
currentLevel = [start]
|
131
|
+
while currentLevel:
|
132
|
+
visited.update(currentLevel)
|
133
|
+
nextLevel = set()
|
134
|
+
levelGraph = {v: set() for v in currentLevel}
|
135
|
+
for v in currentLevel:
|
136
|
+
for w in neighbors(v):
|
137
|
+
if w not in visited:
|
138
|
+
levelGraph[v].add(w)
|
139
|
+
nextLevel.add(w)
|
140
|
+
yield levelGraph
|
141
|
+
currentLevel = nextLevel
|
142
|
+
|
143
|
+
|
144
|
+
def depth_first_traversal(G, start):
|
145
|
+
r"""
|
146
|
+
Generate a sequence of triples (v,w,edgetype) for DFS of graph G.
|
147
|
+
|
148
|
+
Originally written by D. Eppstein for the PADS library
|
149
|
+
(http://www.ics.uci.edu/~eppstein/PADS/).
|
150
|
+
|
151
|
+
INPUT:
|
152
|
+
|
153
|
+
- ``G`` -- a graph to perform the search on
|
154
|
+
|
155
|
+
- ``start`` -- vertex or list of vertices from which to start the traversal
|
156
|
+
|
157
|
+
OUTPUT:
|
158
|
+
|
159
|
+
- a generator of triples ``(v,w,edgetype)``, where ``edgetype`` is ``True``
|
160
|
+
if the algorithm is progressing via the edge ``vw``, or ``False`` if the
|
161
|
+
algorithm is backtracking via the edge ``wv``.
|
162
|
+
|
163
|
+
EXAMPLES::
|
164
|
+
|
165
|
+
sage: H = digraphs.DeBruijn(3,2) # needs sage.combinat
|
166
|
+
sage: t = list(sage.graphs.partial_cube.depth_first_traversal(H, '00')) # needs sage.combinat
|
167
|
+
sage: len(t) # needs sage.combinat
|
168
|
+
16
|
169
|
+
"""
|
170
|
+
neighbors = G.neighbor_out_iterator
|
171
|
+
seen = set()
|
172
|
+
if not isinstance(start, list):
|
173
|
+
start = [start]
|
174
|
+
|
175
|
+
for v in start:
|
176
|
+
if v in seen:
|
177
|
+
continue
|
178
|
+
seen.add(v)
|
179
|
+
stack = [(v, neighbors(v))]
|
180
|
+
while stack:
|
181
|
+
parent, children = stack[-1]
|
182
|
+
try:
|
183
|
+
child = next(children)
|
184
|
+
if child not in seen:
|
185
|
+
yield (parent, child, True)
|
186
|
+
seen.add(child)
|
187
|
+
stack.append((child, neighbors(child)))
|
188
|
+
except StopIteration:
|
189
|
+
stack.pop()
|
190
|
+
if stack:
|
191
|
+
yield (stack[-1][0], parent, False)
|
192
|
+
|
193
|
+
|
194
|
+
def is_partial_cube(G, certificate=False):
|
195
|
+
r"""
|
196
|
+
Test whether the given graph is a partial cube.
|
197
|
+
|
198
|
+
A partial cube is a graph that can be isometrically embedded into a
|
199
|
+
hypercube, i.e., its vertices can be labelled with (0,1)-vectors of some
|
200
|
+
fixed length such that the distance between any two vertices in the graph
|
201
|
+
equals the Hamming distance of their labels.
|
202
|
+
|
203
|
+
Originally written by D. Eppstein for the PADS library
|
204
|
+
(http://www.ics.uci.edu/~eppstein/PADS/), see also
|
205
|
+
[Epp2008]_. The algorithm runs in `O(n^2)` time, where `n`
|
206
|
+
is the number of vertices. See the documentation of
|
207
|
+
:mod:`~sage.graphs.partial_cube` for an overview of the algorithm.
|
208
|
+
|
209
|
+
INPUT:
|
210
|
+
|
211
|
+
- ``certificate`` -- boolean (default: ``False``); this function returns
|
212
|
+
``True`` or ``False`` according to the graph, when ``certificate =
|
213
|
+
False``. When ``certificate = True`` and the graph is a partial cube, the
|
214
|
+
function returns ``(True, mapping)``, where ``mapping`` is an isometric
|
215
|
+
mapping of the vertices of the graph to the vertices of a hypercube
|
216
|
+
((0, 1)-strings of a fixed length). When ``certificate = True`` and the
|
217
|
+
graph is not a partial cube, ``(False, None)`` is returned.
|
218
|
+
|
219
|
+
EXAMPLES:
|
220
|
+
|
221
|
+
The Petersen graph is not a partial cube::
|
222
|
+
|
223
|
+
sage: g = graphs.PetersenGraph()
|
224
|
+
sage: g.is_partial_cube()
|
225
|
+
False
|
226
|
+
|
227
|
+
All prisms are partial cubes::
|
228
|
+
|
229
|
+
sage: g = graphs.CycleGraph(10).cartesian_product(graphs.CompleteGraph(2))
|
230
|
+
sage: g.is_partial_cube()
|
231
|
+
True
|
232
|
+
|
233
|
+
TESTS:
|
234
|
+
|
235
|
+
The returned mapping is an isometric embedding into a hypercube::
|
236
|
+
|
237
|
+
sage: g = graphs.DesarguesGraph()
|
238
|
+
sage: _, m = g.is_partial_cube(certificate=True)
|
239
|
+
sage: m # random
|
240
|
+
{0: '00000',
|
241
|
+
1: '00001',
|
242
|
+
2: '00011',
|
243
|
+
3: '01011',
|
244
|
+
4: '11011',
|
245
|
+
5: '11111',
|
246
|
+
6: '11110',
|
247
|
+
7: '11100',
|
248
|
+
8: '10100',
|
249
|
+
9: '00100',
|
250
|
+
10: '01000',
|
251
|
+
11: '10001',
|
252
|
+
12: '00111',
|
253
|
+
13: '01010',
|
254
|
+
14: '11001',
|
255
|
+
15: '10111',
|
256
|
+
16: '01110',
|
257
|
+
17: '11000',
|
258
|
+
18: '10101',
|
259
|
+
19: '00110'}
|
260
|
+
sage: all(all(g.distance(u, v) == len([i for i in range(len(m[u])) if m[u][i] != m[v][i]]) for v in m) for u in m)
|
261
|
+
True
|
262
|
+
|
263
|
+
A graph without vertices is trivially a partial cube::
|
264
|
+
|
265
|
+
sage: Graph().is_partial_cube(certificate=True)
|
266
|
+
(True, {})
|
267
|
+
"""
|
268
|
+
G._scream_if_not_simple()
|
269
|
+
|
270
|
+
if not G.order():
|
271
|
+
if certificate:
|
272
|
+
return (True, {})
|
273
|
+
else:
|
274
|
+
return True
|
275
|
+
|
276
|
+
if certificate:
|
277
|
+
fail = (False, None)
|
278
|
+
else:
|
279
|
+
fail = False
|
280
|
+
|
281
|
+
if not G.is_connected():
|
282
|
+
return fail
|
283
|
+
n = G.order()
|
284
|
+
|
285
|
+
# Initial sanity check: are there few enough edges?
|
286
|
+
# Needed so that we don't try to use union-find on a dense
|
287
|
+
# graph and incur superquadratic runtimes.
|
288
|
+
if 1 << (2 * G.size() // n) > n:
|
289
|
+
return fail
|
290
|
+
|
291
|
+
# Check for bipartiteness.
|
292
|
+
# This ensures also that each contraction will be bipartite.
|
293
|
+
if not G.is_bipartite():
|
294
|
+
return fail
|
295
|
+
|
296
|
+
# Set up data structures for algorithm:
|
297
|
+
# - contracted: contracted graph at current stage of algorithm
|
298
|
+
# - unionfind: union find data structure representing known edge equivalences
|
299
|
+
# - available: limit on number of remaining available labels
|
300
|
+
from sage.graphs.digraph import DiGraph
|
301
|
+
from sage.graphs.graph import Graph
|
302
|
+
from sage.sets.disjoint_set import DisjointSet
|
303
|
+
contracted = DiGraph({v: {w: (v, w) for w in G[v]} for v in G})
|
304
|
+
unionfind = DisjointSet(contracted.edges(sort=True, labels=False))
|
305
|
+
available = n - 1
|
306
|
+
|
307
|
+
# Main contraction loop in place of the original algorithm's recursion
|
308
|
+
while contracted.order() > 1:
|
309
|
+
# Find max degree vertex in contracted, and update label limit
|
310
|
+
deg, root = max([(contracted.out_degree(v), v) for v in contracted], key=lambda x: x[0])
|
311
|
+
if deg > available:
|
312
|
+
return fail
|
313
|
+
available -= deg
|
314
|
+
|
315
|
+
# Set up bitvectors on vertices
|
316
|
+
bitvec = {v: 0 for v in contracted}
|
317
|
+
neighbors = {}
|
318
|
+
for i, neighbor in enumerate(contracted[root]):
|
319
|
+
bitvec[neighbor] = 1 << i
|
320
|
+
neighbors[1 << i] = neighbor
|
321
|
+
|
322
|
+
# Breadth first search to propagate bitvectors to the rest of the graph
|
323
|
+
for level in breadth_first_level_search(contracted, root):
|
324
|
+
for v in level:
|
325
|
+
for w in level[v]:
|
326
|
+
bitvec[w] |= bitvec[v]
|
327
|
+
|
328
|
+
# Make graph of labeled edges and union them together
|
329
|
+
labeled = Graph([contracted.vertices(sort=False), []])
|
330
|
+
for v, w in contracted.edge_iterator(labels=False):
|
331
|
+
diff = bitvec[v] ^ bitvec[w]
|
332
|
+
if not diff or not bitvec[w] & ~bitvec[v]:
|
333
|
+
continue # zero edge or wrong direction
|
334
|
+
if diff not in neighbors:
|
335
|
+
return fail
|
336
|
+
neighbor = neighbors[diff]
|
337
|
+
unionfind.union(contracted.edge_label(v, w),
|
338
|
+
contracted.edge_label(root, neighbor))
|
339
|
+
unionfind.union(contracted.edge_label(w, v),
|
340
|
+
contracted.edge_label(neighbor, root))
|
341
|
+
labeled.add_edge(v, w)
|
342
|
+
|
343
|
+
# Map vertices to components of labeled-edge graph
|
344
|
+
component = {}
|
345
|
+
for i, SCC in enumerate(labeled.connected_components(sort=False)):
|
346
|
+
for v in SCC:
|
347
|
+
component[v] = i
|
348
|
+
|
349
|
+
# generate new compressed subgraph
|
350
|
+
newgraph = DiGraph()
|
351
|
+
for v, w, t in contracted.edge_iterator():
|
352
|
+
if bitvec[v] == bitvec[w]:
|
353
|
+
vi = component[v]
|
354
|
+
wi = component[w]
|
355
|
+
if vi == wi:
|
356
|
+
return fail
|
357
|
+
if newgraph.has_edge(vi, wi):
|
358
|
+
unionfind.union(newgraph.edge_label(vi, wi), t)
|
359
|
+
else:
|
360
|
+
newgraph.add_edge(vi, wi, t)
|
361
|
+
contracted = newgraph
|
362
|
+
|
363
|
+
# Make a digraph with edges labeled by the equivalence classes in unionfind
|
364
|
+
g = DiGraph({v: {w: unionfind.find((v, w)) for w in G[v]} for v in G})
|
365
|
+
|
366
|
+
# Associates to a vertex the token that acts on it, and check that
|
367
|
+
# no two edges on a single vertex have the same label
|
368
|
+
action = {}
|
369
|
+
for v in g:
|
370
|
+
action[v] = set(t for _, _, t in g.edge_iterator(v))
|
371
|
+
if len(action[v]) != g.out_degree(v):
|
372
|
+
return fail
|
373
|
+
|
374
|
+
# Associate every token to its reverse
|
375
|
+
reverse = {}
|
376
|
+
for v, w, t in g.edge_iterator():
|
377
|
+
rt = g.edge_label(w, v)
|
378
|
+
reverse[t] = rt
|
379
|
+
reverse[rt] = t
|
380
|
+
|
381
|
+
current = initialState = next(g.vertex_iterator())
|
382
|
+
|
383
|
+
# A token T is said to be 'active' for a vertex u if it takes u
|
384
|
+
# one step closer to the source in terms of distance. The 'source'
|
385
|
+
# is initially 'initialState'. See the module's documentation for
|
386
|
+
# more explanations.
|
387
|
+
|
388
|
+
# Find list of tokens that lead to the initial state
|
389
|
+
activeTokens = set()
|
390
|
+
for level in breadth_first_level_search(g, initialState):
|
391
|
+
for v in level:
|
392
|
+
for w in level[v]:
|
393
|
+
activeTokens.add(g.edge_label(w, v))
|
394
|
+
for t in activeTokens:
|
395
|
+
if reverse[t] in activeTokens:
|
396
|
+
return fail
|
397
|
+
activeTokens = list(activeTokens)
|
398
|
+
|
399
|
+
# Rest of data structure: point from states to list and list to states
|
400
|
+
state_to_active_token = {v: -1 for v in g}
|
401
|
+
token_to_states = [[] for _ in activeTokens] # (i.e. vertices on which each token acts)
|
402
|
+
|
403
|
+
def scan(v):
|
404
|
+
"""
|
405
|
+
Find the next token that is effective for v.
|
406
|
+
"""
|
407
|
+
a = next(i for i in range(state_to_active_token[v] + 1, len(activeTokens))
|
408
|
+
if activeTokens[i] is not None and activeTokens[i] in action[v])
|
409
|
+
state_to_active_token[v] = a
|
410
|
+
token_to_states[a].append(v)
|
411
|
+
|
412
|
+
# Initialize isometric embedding into a hypercube
|
413
|
+
if certificate:
|
414
|
+
dim = 0
|
415
|
+
tokmap = {}
|
416
|
+
for t in reverse:
|
417
|
+
if t not in tokmap:
|
418
|
+
tokmap[t] = tokmap[reverse[t]] = 1 << dim
|
419
|
+
dim += 1
|
420
|
+
embed = {initialState: 0}
|
421
|
+
|
422
|
+
# Set initial active states
|
423
|
+
for v in g:
|
424
|
+
if v != current:
|
425
|
+
try:
|
426
|
+
scan(v)
|
427
|
+
except StopIteration:
|
428
|
+
return fail
|
429
|
+
|
430
|
+
# Traverse the graph, maintaining active tokens
|
431
|
+
for prev, current, fwd in depth_first_traversal(g, initialState):
|
432
|
+
if not fwd:
|
433
|
+
prev, current = current, prev
|
434
|
+
elif certificate:
|
435
|
+
embed[current] = embed[prev] ^ tokmap[g.edge_label(prev, current)]
|
436
|
+
|
437
|
+
# Add token to end of list, point to it from old state
|
438
|
+
activeTokens.append(g.edge_label(prev, current))
|
439
|
+
state_to_active_token[prev] = len(activeTokens) - 1
|
440
|
+
token_to_states.append([prev])
|
441
|
+
|
442
|
+
# Deactivate reverse token, find new token for its states
|
443
|
+
#
|
444
|
+
# (the 'active' token of 'current' is necessarily the label of
|
445
|
+
# (current, previous))
|
446
|
+
activeTokens[state_to_active_token[current]] = None
|
447
|
+
for v in token_to_states[state_to_active_token[current]]:
|
448
|
+
if v != current:
|
449
|
+
try:
|
450
|
+
scan(v)
|
451
|
+
except StopIteration:
|
452
|
+
return fail
|
453
|
+
|
454
|
+
# All checks passed, return the result
|
455
|
+
if certificate:
|
456
|
+
format = "{0:0%db}" % dim
|
457
|
+
return (True, {v: format.format(l) for v, l in embed.items()})
|
458
|
+
else:
|
459
|
+
return True
|
Binary file
|