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,812 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Functions for reading/building graphs/digraphs
|
4
|
+
|
5
|
+
This module gathers functions needed to build a graph from any other data.
|
6
|
+
|
7
|
+
.. NOTE::
|
8
|
+
|
9
|
+
This is an **internal** module of Sage. All features implemented here are
|
10
|
+
made available to end-users through the constructors of :class:`Graph` and
|
11
|
+
:class:`DiGraph`.
|
12
|
+
|
13
|
+
Note that because they are called by the constructors of :class:`Graph` and
|
14
|
+
:class:`DiGraph`, most of these functions modify a graph inplace.
|
15
|
+
|
16
|
+
{INDEX_OF_FUNCTIONS}
|
17
|
+
|
18
|
+
Functions
|
19
|
+
---------
|
20
|
+
"""
|
21
|
+
from sage.cpython.string import bytes_to_str
|
22
|
+
from sage.misc.rest_index_of_methods import gen_rest_table_index
|
23
|
+
import sys
|
24
|
+
|
25
|
+
|
26
|
+
def from_graph6(G, g6_string):
|
27
|
+
r"""
|
28
|
+
Fill ``G`` with the data of a graph6 string.
|
29
|
+
|
30
|
+
INPUT:
|
31
|
+
|
32
|
+
- ``G`` -- a graph
|
33
|
+
|
34
|
+
- ``g6_string`` -- a graph6 string
|
35
|
+
|
36
|
+
EXAMPLES::
|
37
|
+
|
38
|
+
sage: from sage.graphs.graph_input import from_graph6
|
39
|
+
sage: g = Graph()
|
40
|
+
sage: from_graph6(g, 'IheA@GUAo')
|
41
|
+
sage: g.is_isomorphic(graphs.PetersenGraph())
|
42
|
+
True
|
43
|
+
"""
|
44
|
+
from .generic_graph_pyx import length_and_string_from_graph6, binary_string_from_graph6
|
45
|
+
|
46
|
+
if isinstance(g6_string, bytes):
|
47
|
+
g6_string = bytes_to_str(g6_string)
|
48
|
+
elif not isinstance(g6_string, str):
|
49
|
+
raise ValueError('if input format is graph6, then g6_string must be a string')
|
50
|
+
n = g6_string.find('\n')
|
51
|
+
if n == -1:
|
52
|
+
n = len(g6_string)
|
53
|
+
ss = g6_string[:n]
|
54
|
+
n, s = length_and_string_from_graph6(ss)
|
55
|
+
m = binary_string_from_graph6(s, n)
|
56
|
+
expected = n * (n - 1) // 2 + (6 - n * (n - 1) // 2) % 6
|
57
|
+
if len(m) > expected:
|
58
|
+
raise RuntimeError("the string (%s) seems corrupt: for n = %d, the string is too long" % (ss, n))
|
59
|
+
elif len(m) < expected:
|
60
|
+
raise RuntimeError("the string (%s) seems corrupt: for n = %d, the string is too short" % (ss, n))
|
61
|
+
G.add_vertices(range(n))
|
62
|
+
k = 0
|
63
|
+
for i in range(n):
|
64
|
+
for j in range(i):
|
65
|
+
if m[k] == '1':
|
66
|
+
G._backend.add_edge(i, j, None, False)
|
67
|
+
k += 1
|
68
|
+
|
69
|
+
|
70
|
+
def from_sparse6(G, g6_string):
|
71
|
+
r"""
|
72
|
+
Fill ``G`` with the data of a sparse6 string.
|
73
|
+
|
74
|
+
INPUT:
|
75
|
+
|
76
|
+
- ``G`` -- a graph
|
77
|
+
|
78
|
+
- ``g6_string`` -- a sparse6 string
|
79
|
+
|
80
|
+
EXAMPLES::
|
81
|
+
|
82
|
+
sage: from sage.graphs.graph_input import from_sparse6
|
83
|
+
sage: g = Graph()
|
84
|
+
sage: from_sparse6(g, ':I`ES@obGkqegW~')
|
85
|
+
sage: g.is_isomorphic(graphs.PetersenGraph())
|
86
|
+
True
|
87
|
+
"""
|
88
|
+
from .generic_graph_pyx import length_and_string_from_graph6, int_to_binary_string
|
89
|
+
|
90
|
+
if isinstance(g6_string, bytes):
|
91
|
+
g6_string = bytes_to_str(g6_string)
|
92
|
+
elif not isinstance(g6_string, str):
|
93
|
+
raise ValueError('if input format is graph6, then g6_string must be a string')
|
94
|
+
|
95
|
+
n = g6_string.find('\n')
|
96
|
+
if n == -1:
|
97
|
+
n = len(g6_string)
|
98
|
+
s = g6_string[:n]
|
99
|
+
n, s = length_and_string_from_graph6(s[1:])
|
100
|
+
if not n:
|
101
|
+
edges = []
|
102
|
+
else:
|
103
|
+
from sage.rings.integer_ring import ZZ
|
104
|
+
k = int((ZZ(n) - 1).nbits())
|
105
|
+
ords = [ord(i) for i in s]
|
106
|
+
if any(o > 126 or o < 63 for o in ords):
|
107
|
+
raise RuntimeError("the string seems corrupt: valid characters are \n" + ''.join(chr(i) for i in range(63, 127)))
|
108
|
+
bits = ''.join(int_to_binary_string(o-63).zfill(6) for o in ords)
|
109
|
+
if not k:
|
110
|
+
b = [int(x) for x in bits]
|
111
|
+
x = [0] * len(b)
|
112
|
+
else:
|
113
|
+
b = []
|
114
|
+
x = []
|
115
|
+
for i in range(0, len(bits)-k, k+1):
|
116
|
+
b.append(int(bits[i:i+1], 2))
|
117
|
+
x.append(int(bits[i+1:i+k+1], 2))
|
118
|
+
v = 0
|
119
|
+
edges = []
|
120
|
+
for i in range(len(b)):
|
121
|
+
v += b[i] # +1 if b[i] == 1 else 0
|
122
|
+
if x[i] > v:
|
123
|
+
v = x[i]
|
124
|
+
else:
|
125
|
+
if v < n:
|
126
|
+
edges.append((x[i], v))
|
127
|
+
G.add_vertices(range(n))
|
128
|
+
G.add_edges(edges)
|
129
|
+
|
130
|
+
|
131
|
+
def from_dig6(G, dig6_string):
|
132
|
+
r"""
|
133
|
+
Fill ``G`` with the data of a dig6 string.
|
134
|
+
|
135
|
+
INPUT:
|
136
|
+
|
137
|
+
- ``G`` -- a graph
|
138
|
+
|
139
|
+
- ``dig6_string`` -- a dig6 string
|
140
|
+
|
141
|
+
EXAMPLES::
|
142
|
+
|
143
|
+
sage: from sage.graphs.graph_input import from_dig6
|
144
|
+
sage: g = DiGraph()
|
145
|
+
sage: from_dig6(g, digraphs.Circuit(10).dig6_string())
|
146
|
+
sage: g.is_isomorphic(digraphs.Circuit(10))
|
147
|
+
True
|
148
|
+
|
149
|
+
The string may represent a directed graph with loops::
|
150
|
+
|
151
|
+
sage: L = DiGraph(loops=True)
|
152
|
+
sage: from_dig6(L, 'CW`C')
|
153
|
+
sage: L.edges(labels=False, sort=True)
|
154
|
+
[(0, 1), (0, 2), (1, 2), (2, 3), (3, 3)]
|
155
|
+
"""
|
156
|
+
from .generic_graph_pyx import length_and_string_from_graph6, binary_string_from_dig6
|
157
|
+
if isinstance(dig6_string, bytes):
|
158
|
+
dig6_string = bytes_to_str(dig6_string)
|
159
|
+
elif not isinstance(dig6_string, str):
|
160
|
+
raise ValueError('if input format is dig6, then dig6_string must be a string')
|
161
|
+
n = dig6_string.find('\n')
|
162
|
+
if n == -1:
|
163
|
+
n = len(dig6_string)
|
164
|
+
ss = dig6_string[:n]
|
165
|
+
n, s = length_and_string_from_graph6(ss)
|
166
|
+
m = binary_string_from_dig6(s, n)
|
167
|
+
expected = n**2
|
168
|
+
if len(m) > expected:
|
169
|
+
raise RuntimeError("the string (%s) seems corrupt: for n = %d, the string is too long" % (ss, n))
|
170
|
+
elif len(m) < expected:
|
171
|
+
raise RuntimeError("the string (%s) seems corrupt: for n = %d, the string is too short" % (ss, n))
|
172
|
+
G.add_vertices(range(n))
|
173
|
+
k = 0
|
174
|
+
for i in range(n):
|
175
|
+
for j in range(n):
|
176
|
+
if m[k] == '1':
|
177
|
+
G._backend.add_edge(i, j, None, True)
|
178
|
+
k += 1
|
179
|
+
|
180
|
+
|
181
|
+
def from_seidel_adjacency_matrix(G, M):
|
182
|
+
r"""
|
183
|
+
Fill ``G`` with the data of a Seidel adjacency matrix.
|
184
|
+
|
185
|
+
INPUT:
|
186
|
+
|
187
|
+
- ``G`` -- a graph
|
188
|
+
|
189
|
+
- ``M`` -- a Seidel adjacency matrix
|
190
|
+
|
191
|
+
EXAMPLES::
|
192
|
+
|
193
|
+
sage: from sage.graphs.graph_input import from_seidel_adjacency_matrix
|
194
|
+
sage: g = Graph()
|
195
|
+
sage: sam = graphs.PetersenGraph().seidel_adjacency_matrix() # needs sage.modules
|
196
|
+
sage: from_seidel_adjacency_matrix(g, sam) # needs sage.modules
|
197
|
+
sage: g.is_isomorphic(graphs.PetersenGraph()) # needs sage.modules
|
198
|
+
True
|
199
|
+
"""
|
200
|
+
from sage.structure.element import Matrix
|
201
|
+
from sage.rings.integer_ring import ZZ
|
202
|
+
assert isinstance(M, Matrix)
|
203
|
+
|
204
|
+
if M.base_ring() != ZZ:
|
205
|
+
try:
|
206
|
+
M = M.change_ring(ZZ)
|
207
|
+
except TypeError:
|
208
|
+
raise ValueError("the adjacency matrix of a Seidel graph must" +
|
209
|
+
" have only 0,1,-1 integer entries")
|
210
|
+
|
211
|
+
if M.is_sparse():
|
212
|
+
entries = set(M[i, j] for i, j in M.nonzero_positions())
|
213
|
+
else:
|
214
|
+
entries = set(M.list())
|
215
|
+
|
216
|
+
if any(e < -1 or e > 1 for e in entries):
|
217
|
+
raise ValueError("the adjacency matrix of a Seidel graph must" +
|
218
|
+
" have only 0,1,-1 integer entries")
|
219
|
+
if any(i == j for i, j in M.nonzero_positions()):
|
220
|
+
raise ValueError("the adjacency matrix of a Seidel graph must" +
|
221
|
+
" have 0s on the main diagonal")
|
222
|
+
if not M.is_symmetric():
|
223
|
+
raise ValueError("the adjacency matrix of a Seidel graph must be symmetric")
|
224
|
+
|
225
|
+
G.add_vertices(range(M.nrows()))
|
226
|
+
G.add_edges((i, j) for i, j in M.nonzero_positions() if i <= j and M[i, j] < 0)
|
227
|
+
|
228
|
+
|
229
|
+
def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False):
|
230
|
+
r"""
|
231
|
+
Fill ``G`` with the data of an adjacency matrix.
|
232
|
+
|
233
|
+
INPUT:
|
234
|
+
|
235
|
+
- ``G`` -- a :class:`Graph` or :class:`DiGraph`
|
236
|
+
|
237
|
+
- ``M`` -- an adjacency matrix
|
238
|
+
|
239
|
+
- ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``);
|
240
|
+
whether to consider the graph as having loops, multiple edges, or weights
|
241
|
+
|
242
|
+
EXAMPLES::
|
243
|
+
|
244
|
+
sage: from sage.graphs.graph_input import from_adjacency_matrix
|
245
|
+
sage: g = Graph()
|
246
|
+
sage: from_adjacency_matrix(g, graphs.PetersenGraph().adjacency_matrix()) # needs sage.modules
|
247
|
+
sage: g.is_isomorphic(graphs.PetersenGraph()) # needs sage.modules
|
248
|
+
True
|
249
|
+
"""
|
250
|
+
from sage.structure.element import Matrix
|
251
|
+
from sage.rings.integer_ring import ZZ
|
252
|
+
assert isinstance(M, Matrix)
|
253
|
+
# note: the adjacency matrix might be weighted and hence not
|
254
|
+
# necessarily consists of integers
|
255
|
+
if not weighted and M.base_ring() != ZZ:
|
256
|
+
try:
|
257
|
+
M = M.change_ring(ZZ)
|
258
|
+
except TypeError:
|
259
|
+
if weighted is False:
|
260
|
+
raise ValueError("the adjacency matrix of a non-weighted graph" +
|
261
|
+
" must have only nonnegative integer entries")
|
262
|
+
weighted = True
|
263
|
+
|
264
|
+
if M.is_sparse():
|
265
|
+
entries = set(M[i, j] for i, j in M.nonzero_positions())
|
266
|
+
else:
|
267
|
+
entries = set(M.list())
|
268
|
+
|
269
|
+
if not weighted and any(e < 0 for e in entries):
|
270
|
+
if weighted is False:
|
271
|
+
raise ValueError("the adjacency matrix of a non-weighted graph" +
|
272
|
+
" must have only nonnegative integer entries")
|
273
|
+
weighted = True
|
274
|
+
if multiedges is None:
|
275
|
+
multiedges = False
|
276
|
+
if weighted is None:
|
277
|
+
weighted = False
|
278
|
+
|
279
|
+
if multiedges is None:
|
280
|
+
multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries))
|
281
|
+
|
282
|
+
if not loops and any(M[i, i] for i in range(M.nrows())):
|
283
|
+
if loops is False:
|
284
|
+
raise ValueError("the adjacency matrix of a non-weighted graph" +
|
285
|
+
" must have zeroes on the diagonal")
|
286
|
+
loops = True
|
287
|
+
if loops is None:
|
288
|
+
loops = False
|
289
|
+
G.allow_loops(loops, check=False)
|
290
|
+
G.allow_multiple_edges(multiedges, check=False)
|
291
|
+
G.add_vertices(range(M.nrows()))
|
292
|
+
if G.is_directed():
|
293
|
+
pairs = M.nonzero_positions()
|
294
|
+
else:
|
295
|
+
pairs = ((i, j) for i, j in M.nonzero_positions() if i <= j)
|
296
|
+
if weighted:
|
297
|
+
G.add_edges((i, j, M[i][j]) for i, j in pairs)
|
298
|
+
elif multiedges:
|
299
|
+
G.add_edges((i, j) for i, j in pairs for _ in range(int(M[i][j])))
|
300
|
+
else:
|
301
|
+
G.add_edges((i, j) for i, j in pairs)
|
302
|
+
G._weighted = weighted
|
303
|
+
|
304
|
+
|
305
|
+
def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False):
|
306
|
+
r"""
|
307
|
+
Fill ``G`` with the data of an incidence matrix.
|
308
|
+
|
309
|
+
INPUT:
|
310
|
+
|
311
|
+
- ``G`` -- a graph
|
312
|
+
|
313
|
+
- ``M`` -- an incidence matrix
|
314
|
+
|
315
|
+
- ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``);
|
316
|
+
whether to consider the graph as having loops, multiple edges, or weights
|
317
|
+
|
318
|
+
EXAMPLES::
|
319
|
+
|
320
|
+
sage: from sage.graphs.graph_input import from_incidence_matrix
|
321
|
+
sage: g = Graph()
|
322
|
+
sage: from_incidence_matrix(g, graphs.PetersenGraph().incidence_matrix()) # needs sage.modules
|
323
|
+
sage: g.is_isomorphic(graphs.PetersenGraph()) # needs sage.modules
|
324
|
+
True
|
325
|
+
"""
|
326
|
+
from sage.structure.element import Matrix
|
327
|
+
assert isinstance(M, Matrix)
|
328
|
+
|
329
|
+
oriented = any(M[pos] < 0 for pos in M.nonzero_positions(copy=False))
|
330
|
+
|
331
|
+
positions = []
|
332
|
+
for i in range(M.ncols()):
|
333
|
+
NZ = M.nonzero_positions_in_column(i)
|
334
|
+
if len(NZ) == 1:
|
335
|
+
if oriented:
|
336
|
+
raise ValueError("column {} of the (oriented) incidence "
|
337
|
+
"matrix contains only one nonzero value".format(i))
|
338
|
+
elif M[NZ[0], i] != 2:
|
339
|
+
raise ValueError("each column of a non-oriented incidence "
|
340
|
+
"matrix must sum to 2, but column {} does not".format(i))
|
341
|
+
if loops is None:
|
342
|
+
loops = True
|
343
|
+
positions.append((NZ[0], NZ[0]))
|
344
|
+
elif (len(NZ) != 2 or
|
345
|
+
(oriented and not ((M[NZ[0], i] == +1 and M[NZ[1], i] == -1) or
|
346
|
+
(M[NZ[0], i] == -1 and M[NZ[1], i] == +1))) or
|
347
|
+
(not oriented and (M[NZ[0], i] != 1 or M[NZ[1], i] != 1))):
|
348
|
+
msg = "there must be one or two nonzero entries per column in an incidence matrix, "
|
349
|
+
msg += "got entries {} in column {}".format([M[j, i] for j in NZ], i)
|
350
|
+
raise ValueError(msg)
|
351
|
+
else:
|
352
|
+
positions.append(tuple(NZ))
|
353
|
+
|
354
|
+
if weighted is None:
|
355
|
+
G._weighted = False
|
356
|
+
if multiedges is None:
|
357
|
+
total = len(positions)
|
358
|
+
multiedges = len(set(positions)) < total
|
359
|
+
G.allow_loops(False if loops is None else loops, check=False)
|
360
|
+
G.allow_multiple_edges(multiedges, check=False)
|
361
|
+
G.add_vertices(range(M.nrows()))
|
362
|
+
G.add_edges(positions)
|
363
|
+
|
364
|
+
|
365
|
+
def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False):
|
366
|
+
r"""
|
367
|
+
Fill ``G`` with the data of an *oriented* incidence matrix.
|
368
|
+
|
369
|
+
An oriented incidence matrix is the incidence matrix of a directed graph, in
|
370
|
+
which each non-loop edge corresponds to a `+1` and a `-1`, indicating its
|
371
|
+
source and destination.
|
372
|
+
|
373
|
+
INPUT:
|
374
|
+
|
375
|
+
- ``G`` -- a :class:`DiGraph`
|
376
|
+
|
377
|
+
- ``M`` -- an incidence matrix
|
378
|
+
|
379
|
+
- ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``);
|
380
|
+
whether to consider the graph as having loops, multiple edges, or weights
|
381
|
+
|
382
|
+
.. NOTE:: ``weighted`` is currently ignored.
|
383
|
+
|
384
|
+
EXAMPLES::
|
385
|
+
|
386
|
+
sage: from sage.graphs.graph_input import from_oriented_incidence_matrix
|
387
|
+
sage: g = DiGraph()
|
388
|
+
sage: im = digraphs.Circuit(10).incidence_matrix() # needs sage.modules
|
389
|
+
sage: from_oriented_incidence_matrix(g, im) # needs sage.modules
|
390
|
+
sage: g.is_isomorphic(digraphs.Circuit(10)) # needs sage.modules
|
391
|
+
True
|
392
|
+
|
393
|
+
TESTS:
|
394
|
+
|
395
|
+
Fix bug reported in :issue:`22985`::
|
396
|
+
|
397
|
+
sage: DiGraph(matrix ([[1,0,0,1],[0,0,1,1],[0,0,1,1]]).transpose()) # needs sage.modules
|
398
|
+
Traceback (most recent call last):
|
399
|
+
...
|
400
|
+
ValueError: each column represents an edge: -1 goes to 1
|
401
|
+
|
402
|
+
Handle incidence matrix containing a column with only zeros (:issue:`29275`)::
|
403
|
+
|
404
|
+
sage: m = Matrix([[0,1],[0,-1],[0,0]]); m # needs sage.modules
|
405
|
+
[ 0 1]
|
406
|
+
[ 0 -1]
|
407
|
+
[ 0 0]
|
408
|
+
sage: G = DiGraph(m, format='incidence_matrix') # needs sage.modules
|
409
|
+
sage: list(G.edges(sort=True, labels=False)) # needs sage.modules
|
410
|
+
[(1, 0)]
|
411
|
+
|
412
|
+
Handle incidence matrix [[1],[-1]] (:issue:`29275`)::
|
413
|
+
|
414
|
+
sage: m = Matrix([[1],[-1]]); m # needs sage.modules
|
415
|
+
[ 1]
|
416
|
+
[-1]
|
417
|
+
sage: G = DiGraph(m, format='incidence_matrix') # needs sage.modules
|
418
|
+
sage: list(G.edges(sort=True, labels=False)) # needs sage.modules
|
419
|
+
[(1, 0)]
|
420
|
+
"""
|
421
|
+
from sage.structure.element import Matrix
|
422
|
+
assert isinstance(M, Matrix)
|
423
|
+
|
424
|
+
positions = []
|
425
|
+
for c in M.columns():
|
426
|
+
NZ = c.nonzero_positions()
|
427
|
+
if not NZ:
|
428
|
+
continue
|
429
|
+
if len(NZ) != 2:
|
430
|
+
raise ValueError("there must be two nonzero entries (-1 & 1) per column")
|
431
|
+
L = sorted([c[i] for i in NZ])
|
432
|
+
if L != [-1, 1]:
|
433
|
+
raise ValueError("each column represents an edge: -1 goes to 1")
|
434
|
+
if c[NZ[0]] == -1:
|
435
|
+
positions.append(tuple(NZ))
|
436
|
+
else:
|
437
|
+
positions.append((NZ[1], NZ[0]))
|
438
|
+
if multiedges is None:
|
439
|
+
total = len(positions)
|
440
|
+
multiedges = len(set(positions)) < total
|
441
|
+
G.allow_loops(bool(loops), check=False)
|
442
|
+
G.allow_multiple_edges(multiedges, check=False)
|
443
|
+
G.add_vertices(range(M.nrows()))
|
444
|
+
G.add_edges(positions)
|
445
|
+
|
446
|
+
|
447
|
+
def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, convert_empty_dict_labels_to_None=False):
|
448
|
+
r"""
|
449
|
+
Fill ``G`` with the data of a dictionary of dictionaries.
|
450
|
+
|
451
|
+
INPUT:
|
452
|
+
|
453
|
+
- ``G`` -- a graph
|
454
|
+
|
455
|
+
- ``M`` -- dictionary of dictionaries
|
456
|
+
|
457
|
+
- ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``);
|
458
|
+
whether to consider the graph as having loops, multiple edges, or weights
|
459
|
+
|
460
|
+
- ``convert_empty_dict_labels_to_None`` -- booleans (default: ``False``);
|
461
|
+
whether to adjust for empty dicts instead of ``None`` in NetworkX default
|
462
|
+
edge labels
|
463
|
+
|
464
|
+
EXAMPLES::
|
465
|
+
|
466
|
+
sage: from sage.graphs.graph_input import from_dict_of_dicts
|
467
|
+
sage: g = Graph()
|
468
|
+
sage: from_dict_of_dicts(g, graphs.PetersenGraph().to_dictionary(edge_labels=True))
|
469
|
+
sage: g.is_isomorphic(graphs.PetersenGraph())
|
470
|
+
True
|
471
|
+
|
472
|
+
The resulting order of vertices is unspecified but deterministic::
|
473
|
+
|
474
|
+
sage: from sage.graphs.graph_input import from_dict_of_dicts
|
475
|
+
sage: g = Graph()
|
476
|
+
sage: from_dict_of_dicts(g, {i: {} for i in range(99, 90, -1)})
|
477
|
+
sage: g.vertices(sort=False)
|
478
|
+
[99, 98, 97, 96, 95, 94, 93, 92, 91]
|
479
|
+
|
480
|
+
TESTS:
|
481
|
+
|
482
|
+
:issue:`32831` is fixed::
|
483
|
+
|
484
|
+
sage: DiGraph({0: {}, 1: {}, 2: {}, 3: {}, 4: {}})
|
485
|
+
Digraph on 5 vertices
|
486
|
+
"""
|
487
|
+
if any(not isinstance(M[u], dict) for u in M):
|
488
|
+
raise ValueError("input dict must be a consistent format")
|
489
|
+
|
490
|
+
if not loops:
|
491
|
+
if any(u in neighb for u, neighb in M.items()):
|
492
|
+
if loops is False:
|
493
|
+
u = next(u for u, neighb in M.items() if u in neighb)
|
494
|
+
raise ValueError("the graph was built with loops=False but input M has a loop at {}".format(u))
|
495
|
+
loops = True
|
496
|
+
if loops is None:
|
497
|
+
loops = False
|
498
|
+
if weighted is None:
|
499
|
+
G._weighted = False
|
500
|
+
input_multiedges = multiedges
|
501
|
+
if multiedges is not False:
|
502
|
+
if not all(isinstance(M[u][v], list) for u in M for v in M[u]):
|
503
|
+
if multiedges:
|
504
|
+
raise ValueError("dict of dicts for multigraph must be in the format {v: {u: list}}")
|
505
|
+
multiedges = False
|
506
|
+
if multiedges is None and M:
|
507
|
+
multiedges = True
|
508
|
+
|
509
|
+
G.allow_loops(loops, check=False)
|
510
|
+
G.allow_multiple_edges(multiedges, check=False)
|
511
|
+
# Use keys of a dictionary instead of a set, to preserve insertion order
|
512
|
+
verts = dict(M)
|
513
|
+
for d in M.values():
|
514
|
+
verts.update(d)
|
515
|
+
G.add_vertices(verts.keys())
|
516
|
+
if convert_empty_dict_labels_to_None:
|
517
|
+
def relabel(x):
|
518
|
+
return x if x != {} else None
|
519
|
+
else:
|
520
|
+
def relabel(x):
|
521
|
+
return x
|
522
|
+
|
523
|
+
is_directed = G.is_directed()
|
524
|
+
if not is_directed and multiedges:
|
525
|
+
v_to_id = {v: i for i, v in enumerate(verts.keys())}
|
526
|
+
for u in M:
|
527
|
+
for v in M[u]:
|
528
|
+
if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v:
|
529
|
+
for label in M[u][v]:
|
530
|
+
G._backend.add_edge(u, v, relabel(label), False)
|
531
|
+
elif multiedges:
|
532
|
+
for u in M:
|
533
|
+
for v in M[u]:
|
534
|
+
for label in M[u][v]:
|
535
|
+
G._backend.add_edge(u, v, relabel(label), is_directed)
|
536
|
+
else:
|
537
|
+
for u in M:
|
538
|
+
for v in M[u]:
|
539
|
+
G._backend.add_edge(u, v, relabel(M[u][v]), is_directed)
|
540
|
+
if not G.size() and input_multiedges is not True:
|
541
|
+
G.allow_multiple_edges(False, check=False)
|
542
|
+
|
543
|
+
|
544
|
+
def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False):
|
545
|
+
r"""
|
546
|
+
Fill ``G`` with the data of a dictionary of lists.
|
547
|
+
|
548
|
+
INPUT:
|
549
|
+
|
550
|
+
- ``G`` -- a :class:`Graph` or :class:`DiGraph`
|
551
|
+
|
552
|
+
- ``D`` -- dictionary of lists
|
553
|
+
|
554
|
+
- ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``);
|
555
|
+
whether to consider the graph as having loops, multiple edges, or weights
|
556
|
+
|
557
|
+
EXAMPLES::
|
558
|
+
|
559
|
+
sage: from sage.graphs.graph_input import from_dict_of_lists
|
560
|
+
sage: g = Graph()
|
561
|
+
sage: from_dict_of_lists(g, graphs.PetersenGraph().to_dictionary())
|
562
|
+
sage: g.is_isomorphic(graphs.PetersenGraph())
|
563
|
+
True
|
564
|
+
|
565
|
+
The resulting order of vertices is unspecified but deterministic::
|
566
|
+
|
567
|
+
sage: from sage.graphs.graph_input import from_dict_of_lists
|
568
|
+
sage: g = Graph()
|
569
|
+
sage: from_dict_of_lists(g, {i: [] for i in range(99, 90, -1)})
|
570
|
+
sage: g.vertices(sort=False)
|
571
|
+
[99, 98, 97, 96, 95, 94, 93, 92, 91]
|
572
|
+
"""
|
573
|
+
# Use keys of a dictionary instead of a set, to preserve insertion order
|
574
|
+
verts = dict(D)
|
575
|
+
verts.update({v: None for l in D.values() for v in l})
|
576
|
+
if not loops:
|
577
|
+
if any(u in neighb for u, neighb in D.items()):
|
578
|
+
if loops is False:
|
579
|
+
u = next(u for u, neighb in D.items() if u in neighb)
|
580
|
+
raise ValueError("the graph was built with loops=False but input D has a loop at {}".format(u))
|
581
|
+
loops = True
|
582
|
+
if loops is None:
|
583
|
+
loops = False
|
584
|
+
if weighted is None:
|
585
|
+
G._weighted = False
|
586
|
+
if not multiedges:
|
587
|
+
for u in D:
|
588
|
+
if len(set(D[u])) != len(D[u]):
|
589
|
+
if multiedges is False:
|
590
|
+
v = next(v for v in D[u] if D[u].count(v) > 1)
|
591
|
+
raise ValueError("non-multigraph got several edges (%s, %s)" % (u, v))
|
592
|
+
multiedges = True
|
593
|
+
break
|
594
|
+
if multiedges is None:
|
595
|
+
multiedges = False
|
596
|
+
G.allow_loops(loops, check=False)
|
597
|
+
G.allow_multiple_edges(multiedges, check=False)
|
598
|
+
G.add_vertices(verts.keys())
|
599
|
+
|
600
|
+
is_directed = G.is_directed()
|
601
|
+
if not is_directed and multiedges:
|
602
|
+
v_to_id = {v: i for i, v in enumerate(verts.keys())}
|
603
|
+
for u in D:
|
604
|
+
for v in D[u]:
|
605
|
+
if (v_to_id[u] <= v_to_id[v] or
|
606
|
+
v not in D or u not in D[v] or u == v):
|
607
|
+
G._backend.add_edge(u, v, None, False)
|
608
|
+
else:
|
609
|
+
for u in D:
|
610
|
+
for v in D[u]:
|
611
|
+
G._backend.add_edge(u, v, None, is_directed)
|
612
|
+
|
613
|
+
|
614
|
+
def from_networkx_graph(G, gnx, weighted=None, loops=None, multiedges=None,
|
615
|
+
convert_empty_dict_labels_to_None=None):
|
616
|
+
r"""
|
617
|
+
Fill `G` with the data of a NetworkX (di)graph.
|
618
|
+
|
619
|
+
INPUT:
|
620
|
+
|
621
|
+
- ``G`` -- a :class:`Graph` or :class:`DiGraph`
|
622
|
+
|
623
|
+
- ``gnx`` -- a NetworkX ``Graph``, ``MultiGraph``, ``DiGraph`` or
|
624
|
+
``MultiDiGraph``
|
625
|
+
|
626
|
+
- ``weighted`` -- boolean (default: ``None``); whether graph thinks of
|
627
|
+
itself as weighted or not. See
|
628
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.weighted`.
|
629
|
+
|
630
|
+
- ``loops`` -- boolean (default: ``None``); whether to allow loops
|
631
|
+
|
632
|
+
- ``multiedges`` -- boolean (default: ``None``); whether to allow multiple
|
633
|
+
edges
|
634
|
+
|
635
|
+
- ``convert_empty_dict_labels_to_None`` -- boolean (default: ``None``);
|
636
|
+
whether to replace the default edge labels used by NetworkX (empty
|
637
|
+
dictionaries) by ``None``, the default Sage edge label. When set to
|
638
|
+
``False``, empty dictionaries are not converted to ``None``.
|
639
|
+
|
640
|
+
EXAMPLES:
|
641
|
+
|
642
|
+
Feeding a :class:`Graph` with a NetworkX ``Graph``::
|
643
|
+
|
644
|
+
sage: # needs networkx
|
645
|
+
sage: from sage.graphs.graph_input import from_networkx_graph
|
646
|
+
sage: import networkx
|
647
|
+
sage: G = Graph()
|
648
|
+
sage: _ = gnx = networkx.Graph()
|
649
|
+
sage: _ = gnx.add_edge(0, 1)
|
650
|
+
sage: _ = gnx.add_edge(1, 2)
|
651
|
+
sage: from_networkx_graph(G, gnx)
|
652
|
+
sage: G.edges(sort=True, labels=False)
|
653
|
+
[(0, 1), (1, 2)]
|
654
|
+
|
655
|
+
Feeding a :class:`Graph` with a NetworkX ``MultiGraph``::
|
656
|
+
|
657
|
+
sage: # needs networkx
|
658
|
+
sage: G = Graph()
|
659
|
+
sage: gnx = networkx.MultiGraph()
|
660
|
+
sage: _ = gnx.add_edge(0, 1)
|
661
|
+
sage: _ = gnx.add_edge(0, 1)
|
662
|
+
sage: from_networkx_graph(G, gnx)
|
663
|
+
sage: G.edges(sort=True, labels=False)
|
664
|
+
[(0, 1), (0, 1)]
|
665
|
+
sage: G = Graph()
|
666
|
+
sage: from_networkx_graph(G, gnx, multiedges=False)
|
667
|
+
sage: G.edges(sort=True, labels=False)
|
668
|
+
[(0, 1)]
|
669
|
+
|
670
|
+
When feeding a :class:`Graph` `G` with a NetworkX ``DiGraph`` `D`, `G` has
|
671
|
+
one edge `(u, v)` whenever `D` has arc `(u, v)` or `(v, u)` or both::
|
672
|
+
|
673
|
+
sage: # needs networkx
|
674
|
+
sage: G = Graph()
|
675
|
+
sage: D = networkx.DiGraph()
|
676
|
+
sage: _ = D.add_edge(0, 1)
|
677
|
+
sage: from_networkx_graph(G, D)
|
678
|
+
sage: G.edges(sort=True, labels=False)
|
679
|
+
[(0, 1)]
|
680
|
+
sage: G = Graph()
|
681
|
+
sage: _ = D.add_edge(1, 0)
|
682
|
+
sage: from_networkx_graph(G, D)
|
683
|
+
sage: G.edges(sort=True, labels=False)
|
684
|
+
[(0, 1)]
|
685
|
+
|
686
|
+
When feeding a :class:`Graph` `G` with a NetworkX ``MultiDiGraph`` `D`, the
|
687
|
+
number of edges between `u` and `v` in `G` is the maximum between the number
|
688
|
+
of arcs `(u, v)` and the number of arcs `(v, u)` in D`::
|
689
|
+
|
690
|
+
sage: # needs networkx
|
691
|
+
sage: G = Graph()
|
692
|
+
sage: D = networkx.MultiDiGraph()
|
693
|
+
sage: _ = D.add_edge(0, 1)
|
694
|
+
sage: _ = D.add_edge(1, 0)
|
695
|
+
sage: _ = D.add_edge(1, 0)
|
696
|
+
sage: D.edges()
|
697
|
+
OutMultiEdgeDataView([(0, 1), (1, 0), (1, 0)])
|
698
|
+
sage: from_networkx_graph(G, D)
|
699
|
+
sage: G.edges(sort=True, labels=False)
|
700
|
+
[(0, 1), (0, 1)]
|
701
|
+
|
702
|
+
Feeding a :class:`DiGraph` with a NetworkX ``DiGraph``::
|
703
|
+
|
704
|
+
sage: # needs networkx
|
705
|
+
sage: from sage.graphs.graph_input import from_networkx_graph
|
706
|
+
sage: import networkx
|
707
|
+
sage: G = DiGraph()
|
708
|
+
sage: _ = gnx = networkx.DiGraph()
|
709
|
+
sage: _ = gnx.add_edge(0, 1)
|
710
|
+
sage: _ = gnx.add_edge(1, 2)
|
711
|
+
sage: from_networkx_graph(G, gnx)
|
712
|
+
sage: G.edges(sort=True, labels=False)
|
713
|
+
[(0, 1), (1, 2)]
|
714
|
+
|
715
|
+
Feeding a :class:`DiGraph` with a NetworkX ``MultiDiGraph``::
|
716
|
+
|
717
|
+
sage: # needs networkx
|
718
|
+
sage: G = DiGraph()
|
719
|
+
sage: gnx = networkx.MultiDiGraph()
|
720
|
+
sage: _ = gnx.add_edge(0, 1)
|
721
|
+
sage: _ = gnx.add_edge(0, 1)
|
722
|
+
sage: from_networkx_graph(G, gnx)
|
723
|
+
sage: G.edges(sort=True, labels=False)
|
724
|
+
[(0, 1), (0, 1)]
|
725
|
+
sage: G = DiGraph()
|
726
|
+
sage: from_networkx_graph(G, gnx, multiedges=False)
|
727
|
+
sage: G.edges(sort=True, labels=False)
|
728
|
+
[(0, 1)]
|
729
|
+
|
730
|
+
When feeding a :class:`DiGraph` `G` with a NetworkX ``Graph`` `H`, `G` has
|
731
|
+
both arcs `(u, v)` and `(v, u)` if `G` has edge `(u, v)`::
|
732
|
+
|
733
|
+
sage: # needs networkx
|
734
|
+
sage: G = DiGraph()
|
735
|
+
sage: H = networkx.Graph()
|
736
|
+
sage: _ = H.add_edge(0, 1)
|
737
|
+
sage: from_networkx_graph(G, H)
|
738
|
+
sage: G.edges(labels=False, sort=True)
|
739
|
+
[(0, 1), (1, 0)]
|
740
|
+
|
741
|
+
When feeding a :class:`DiGraph` `G` with a NetworkX ``MultiGraph`` `H`, `G`
|
742
|
+
has `k` arcs `(u, v)` and `k` arcs `(v, u)` if `H` has `k` edges `(u, v)`,
|
743
|
+
unless parameter ``multiedges`` is set to ``False``::
|
744
|
+
|
745
|
+
sage: # needs networkx
|
746
|
+
sage: G = DiGraph()
|
747
|
+
sage: H = networkx.MultiGraph()
|
748
|
+
sage: _ = H.add_edge(0, 1)
|
749
|
+
sage: _ = H.add_edge(0, 1)
|
750
|
+
sage: _ = H.add_edge(0, 1)
|
751
|
+
sage: H.edges()
|
752
|
+
MultiEdgeDataView([(0, 1), (0, 1), (0, 1)])
|
753
|
+
sage: from_networkx_graph(G, H)
|
754
|
+
sage: G.edges(labels=False, sort=True)
|
755
|
+
[(0, 1), (0, 1), (0, 1), (1, 0), (1, 0), (1, 0)]
|
756
|
+
sage: G = DiGraph()
|
757
|
+
sage: from_networkx_graph(G, H, multiedges=False)
|
758
|
+
sage: G.edges(labels=False, sort=True)
|
759
|
+
[(0, 1), (1, 0)]
|
760
|
+
|
761
|
+
TESTS:
|
762
|
+
|
763
|
+
The first parameter must be a :class:`Graph` or :class:`DiGraph`::
|
764
|
+
|
765
|
+
sage: from sage.graphs.graph_input import from_networkx_graph
|
766
|
+
sage: from_networkx_graph("foo", "bar")
|
767
|
+
Traceback (most recent call last):
|
768
|
+
...
|
769
|
+
ValueError: the first parameter must a Sage Graph or DiGraph
|
770
|
+
|
771
|
+
The second parameter must be a NetworkX ``Graph``, ``MultiGraph``,
|
772
|
+
``DiGraph`` or ``MultiDiGraph``::
|
773
|
+
|
774
|
+
sage: from sage.graphs.graph_input import from_networkx_graph
|
775
|
+
sage: from_networkx_graph(Graph(), "bar") # needs networkx
|
776
|
+
Traceback (most recent call last):
|
777
|
+
...
|
778
|
+
ValueError: the second parameter must be a NetworkX (Multi)(Di)Graph
|
779
|
+
"""
|
780
|
+
from sage.graphs.graph import Graph
|
781
|
+
from sage.graphs.digraph import DiGraph
|
782
|
+
if not isinstance(G, (Graph, DiGraph)):
|
783
|
+
raise ValueError("the first parameter must a Sage Graph or DiGraph")
|
784
|
+
import networkx
|
785
|
+
if not isinstance(gnx, (networkx.Graph, networkx.DiGraph)):
|
786
|
+
raise ValueError("the second parameter must be a NetworkX (Multi)(Di)Graph")
|
787
|
+
|
788
|
+
if G.is_directed() != gnx.is_directed():
|
789
|
+
if gnx.is_directed():
|
790
|
+
gnx = gnx.to_undirected()
|
791
|
+
else:
|
792
|
+
gnx = gnx.to_directed()
|
793
|
+
|
794
|
+
if weighted is None:
|
795
|
+
if multiedges is None:
|
796
|
+
multiedges = gnx.is_multigraph()
|
797
|
+
if loops is None:
|
798
|
+
loops = any(u == v for u, v in gnx.edges())
|
799
|
+
|
800
|
+
G.allow_loops(loops, check=False)
|
801
|
+
G.allow_multiple_edges(multiedges, check=False)
|
802
|
+
G.add_vertices(gnx.nodes())
|
803
|
+
G.set_vertices(gnx.nodes(data=True))
|
804
|
+
if convert_empty_dict_labels_to_None is not False:
|
805
|
+
def r(label):
|
806
|
+
return None if label == {} else label
|
807
|
+
G.add_edges((u, v, r(ll)) for u, v, ll in gnx.edges(data=True))
|
808
|
+
else:
|
809
|
+
G.add_edges(gnx.edges(data=True))
|
810
|
+
|
811
|
+
|
812
|
+
__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__]))
|