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.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,530 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Hypergraph isomorphic copy search
4
+
5
+ This module implements a code for the following problem:
6
+
7
+ **INPUT:** two hypergraphs `H_1, H_2`
8
+
9
+ **OUTPUT:** a copy of `H_2` in `H_1`
10
+
11
+ It is also possible to enumerate all such copies, and to require that such
12
+ copies be induced copies. More formally:
13
+
14
+ A copy of `H_2` in `H_1` is an injection `f:V(H_2)\mapsto V(H_1)` such that
15
+ for any set `S_2\in E(H_2)` we have `f(S_2)\in E(H_1)`.
16
+
17
+ It is an *induced* copy if no other set of `E(H_1)` is contained in
18
+ `f(V(H_2))`, i.e. `|E(H_2)|=\{S:S\in E(H_1)\text{ and }S\subseteq
19
+ f(V(H_2))\}`.
20
+
21
+ The functions implemented here lists all such injections. In particular, the
22
+ number of copies of `H` in itself is equal to `|Aut(H)|`.
23
+
24
+ The feature is available through
25
+ :meth:`IncidenceStructure.isomorphic_substructures_iterator`.
26
+
27
+ Implementation
28
+ --------------
29
+
30
+ A hypergraph is stored as a list of edges, each of which is a "dense" bitset
31
+ over `|V(H_1)|` points. In particular, two sets of distinct cardinalities
32
+ require the same memory space. A hypergraph is a C struct with the following
33
+ fields:
34
+
35
+ * ``n``, ``m`` -- (``int``) number of points and edges
36
+
37
+ * ``limbs`` -- (``int``) number of 64-bits blocks per set
38
+
39
+ * ``set_space`` -- (``uint64_t *``) address of the memory used to store the
40
+ sets
41
+
42
+ * ``sets`` -- (``uint64_t **``) ``sets[i]`` points toward the ``limbs``
43
+ blocks encoding set `i`. Note also that ``sets[i][limbs]`` is equal to the
44
+ cardinality of ``set[i]``, so that ``sets`` has length
45
+ ``m*(limbs+1)*sizeof(uint64_t)``.
46
+
47
+ * ``names`` -- (``int *``) associates an integer 'name' to each of the ``n``
48
+ points
49
+
50
+ The operations used on this data structure are:
51
+
52
+ * ``void permute(hypergraph * h, int n1, int n2)`` -- exchanges points `n1`
53
+ and `n2` in the data structure. Note that their names are also exchanged
54
+ so that we still know which is which.
55
+
56
+ * ``int induced_hypergraph(hypergraph * h1, int n, hypergraph * tmp1)`` --
57
+ stores in ``tmp1`` the hypergraph induced by the first `n` points,
58
+ i.e. all sets `S` such that `S\subseteq \{0,...,n-1\}`. The function
59
+ returns the number of such sets.
60
+
61
+ * ``void trace_hypergraph64(hypergraph * h, int n, hypergraph * tmp)`` -- stores
62
+ in ``tmp1`` the trace of `h` on the first `n` points, i.e. all sets of the
63
+ form `S\cap \{0, \ldots, n-1\}`.
64
+
65
+ Algorithm
66
+ ---------
67
+
68
+ We try all possible assignments of a representant `r_i\in H_1` for every `i\in
69
+ H_2`. When we have picked a representant for the first `n<` points
70
+ `\{0, \ldots, n-1\}\subsetneq V(H_2)`, we check that:
71
+
72
+ * The hypergraph induced by the (ordered) list `0, \ldots, n-1` in `H_2` is
73
+ equal to the one induced by `r_0, \ldots, r_{n-1}` in `H_1`.
74
+
75
+ * If `S\subseteq \{0,...,n-1\}` is contained in `c` sets of size `k` in
76
+ `H_2`, then `\{r_i:i\in S\}` is contained in `\geq c` sets of size `k` in
77
+ `H_1`. This is done by comparing the trace of the hypergraphs while
78
+ remembering the original size of each set.
79
+
80
+ As we very often need to build the hypergraph obtained by the trace of the first
81
+ `n` points (for all possible `n`), those hypergraphs are cached. The hypergraphs
82
+ induced by the same points are handled similarly.
83
+
84
+ Limitations
85
+ -----------
86
+
87
+ **Number of points** For efficiency reason the implementation assumes that `H_2`
88
+ has `\leq 64` points. Making this work for larger values means that calls to
89
+ ``qsort`` have to be replaced by calls to ``qsort_r`` (i.e. to sort the edges
90
+ you need to know the number of limbs per edge) and that induces a big slowdown
91
+ for small cases (~50% when this code was implemented). Also, 64 points for `H_2`
92
+ is already very very big considering the problem at hand. Even `|V(H_1)|> 64`
93
+ seems too much.
94
+
95
+ **Vertex ordering** The order of vertices in `H_2` has a huge influence on the
96
+ performance of the algorithm. If no set of `H_2` contains more that one of the
97
+ first `k<n` points, then almost all partial assignments of representants are
98
+ possible for the first `k` points (though the degree of the vertices is taken
99
+ into account). For this reason it is best to pick an ordering such that the
100
+ first vertices are contained in as many sets as possible together. A heuristic
101
+ is implemented at
102
+ :meth:`~sage.combinat.designs.subhypergraph_search.SubHypergraphSearch.relabel_heuristic`.
103
+
104
+ AUTHORS:
105
+
106
+ - Nathann Cohen (November 2014, written in various airports between Nice and
107
+ Chennai).
108
+
109
+ Methods
110
+ -------
111
+ """
112
+ # ****************************************************************************
113
+ # Copyright (C) 2014 Nathann Cohen <nathann.cohen@gmail.com>
114
+ #
115
+ # Distributed under the terms of the GNU General Public License (GPL)
116
+ # as published by the Free Software Foundation; either version 2 of
117
+ # the License, or (at your option) any later version.
118
+ # https://www.gnu.org/licenses/
119
+ # ****************************************************************************
120
+
121
+ from libc.stdlib cimport qsort
122
+ from libc.stdint cimport uint64_t
123
+ from cysignals.memory cimport sig_malloc, sig_calloc, sig_free
124
+
125
+ ctypedef struct hypergraph:
126
+ int n
127
+ int m
128
+ int limbs
129
+ uint64_t ** sets
130
+ uint64_t * set_space
131
+ int * names
132
+
133
+ cdef inline int bs_get(uint64_t * bitset, int index) noexcept:
134
+ r"""
135
+ Return a bit of a bitset
136
+ """
137
+ return (bitset[index // 64] >> (index % 64)) & 1
138
+
139
+ cdef inline void bs_set(uint64_t * bitset, int index, int bit) noexcept:
140
+ r"""
141
+ Set a bit of a bitset.
142
+
143
+ "bit" *MUST* be equal to either 0 or to 1. The code does not involve any
144
+ "if".
145
+ """
146
+ bitset[index // 64] &= ~((<uint64_t> 1) << index % 64)
147
+ bitset[index // 64] |= (<uint64_t> bit) << index % 64
148
+
149
+ cdef inline int bs_issubset64(uint64_t * b1, uint64_t b2, int limbs) noexcept:
150
+ r"""
151
+ Test whether bitset ``b1`` (on ``limbs`` blocks) is a subset of b2 (one block).
152
+
153
+ It implies in particular that all last `limbs-1` blocks of ``b1`` are equal
154
+ to zero.
155
+ """
156
+ # Checking that the whole field is zero can be done by computing b1[0] |
157
+ # b1[1] | b[2] | ..., then calling "if" on that variable. May be faster, but
158
+ # I have no real situation on which to test it.
159
+ cdef int i
160
+ for i in range(1,limbs):
161
+ if b1[i]:
162
+ return 0
163
+ return (b1[0]&(~b2)) == 0
164
+
165
+ cdef void h_free(hypergraph h) noexcept:
166
+ r"""
167
+ Free the hypergraph
168
+ """
169
+ sig_free(h.names)
170
+ sig_free(h.set_space)
171
+ sig_free(h.sets)
172
+ h.names = NULL
173
+ h.set_space = NULL
174
+ h.sets = NULL
175
+
176
+ cdef hypergraph h_init(int n, list H) noexcept:
177
+ r"""
178
+ Build a C hypergraph from a list `H` of sets on `\{0,...,n-1\}`.
179
+ """
180
+ cdef int x,i
181
+ cdef hypergraph h
182
+ h.n = n
183
+ h.m = len(H)
184
+ h.limbs = (n+63) // 64 # =ceil(n/64)
185
+ h.names = <int *> sig_malloc(sizeof(int)*n)
186
+ h.sets = <uint64_t **> sig_malloc(h.m*sizeof(uint64_t *))
187
+ h.set_space = <uint64_t *> sig_calloc(h.m*(h.limbs+1),sizeof(uint64_t))
188
+
189
+ # Consistency check
190
+ for S in H:
191
+ for x in S:
192
+ if x < 0 or x >= n:
193
+ h.n = -1
194
+
195
+ if (h.names == NULL or
196
+ h.sets == NULL or
197
+ h.set_space == NULL or
198
+ h.n == -1):
199
+ h.n = -1
200
+ return h
201
+
202
+ for i in range(n):
203
+ h.names[i] = i
204
+ for i in range(h.m):
205
+ h.sets[i] = h.set_space+i*(h.limbs+1)
206
+
207
+ for i,S in enumerate(H):
208
+ for x in S:
209
+ bs_set(h.sets[i],x,1)
210
+ h.sets[i][h.limbs] = len(S)
211
+
212
+ return h
213
+
214
+ cdef inline void permute(hypergraph * h, int n1, int n2) noexcept:
215
+ r"""
216
+ Permutes two points of h inplace.
217
+
218
+ This is only a data structure change, as h still represents the same
219
+ hypergraph. In particular, the names of `n1` and `n2` are also exchanged.
220
+ """
221
+ if n1==n2:
222
+ return
223
+
224
+ h.names[n1],h.names[n2] = h.names[n2],h.names[n1]
225
+
226
+ cdef int i,b1,b2
227
+ for i in range(h.m):
228
+ b1 = bs_get(h.sets[i],n1)
229
+ b2 = bs_get(h.sets[i],n2)
230
+ bs_set(h.sets[i],n1,b2)
231
+ bs_set(h.sets[i],n2,b1)
232
+
233
+ cdef induced_hypergraph(hypergraph * h, int n, hypergraph * tmp):
234
+ r"""
235
+ Fills tmp with the hypergraph induced by points {0,...,n-1} in h.
236
+
237
+ Assumes `n<=64`.
238
+ """
239
+ cdef int i,num_sets
240
+ num_sets = 0
241
+ cdef uint64_t current_set = ((<uint64_t> 1)<<(n+1))-1
242
+ for i in range(h.m):
243
+ if bs_issubset64(h.sets[i],current_set,h.limbs):
244
+ tmp.sets[num_sets][0] = h.sets[i][0]
245
+ num_sets += 1
246
+ tmp.m = num_sets
247
+ tmp.n = n
248
+ tmp.limbs = 1
249
+
250
+
251
+ cdef void trace_hypergraph64(hypergraph * h, int n, hypergraph * tmp) noexcept:
252
+ r"""
253
+ Store in `tmp` the trace of the sets on `\{0, \ldots, n-1\}` in h1.
254
+
255
+ Note that the size of the sets are kept as they are, i.e. the size of a set
256
+ stored in tmp is what it was in h. This is useful information we use to cut
257
+ the exploration.
258
+
259
+ Assumes `n<=64`.
260
+ """
261
+ cdef int i
262
+ cdef uint64_t current_set = ((<uint64_t> 1)<<(n+1))-1
263
+ for i in range(h.m):
264
+ tmp.sets[i][0] = h.sets[i][0]&current_set
265
+ tmp.sets[i][1] = h.sets[i][h.limbs]
266
+
267
+ tmp.limbs = 1
268
+
269
+ cdef int is_subhypergraph_admissible(hypergraph h1, hypergraph * h2_trace, int n, hypergraph tmp1) noexcept:
270
+ r"""
271
+ If there are `c` sets of size `k` containing `S\subseteq \{0,...,n-1\}` in
272
+ `h2`, then there must be `>=c` sets of size `k` containing `S` in h1. This
273
+ function checks that this property hold.
274
+
275
+ ``h2_trace`` is expected to point toward the trace of ``h2`` on its first
276
+ `n` points. Besides, its sets must be sorted with respect to
277
+ ``cmp_128_bits``.
278
+
279
+ Assumes `n<=64`.
280
+ """
281
+ trace_hypergraph64(&h1,n,&tmp1)
282
+ qsort(tmp1.sets,h1.m,sizeof(uint64_t *),cmp_128_bits)
283
+
284
+ cdef int i1,i2
285
+ i1 = -1
286
+ for i2 in range(h2_trace.m):
287
+ i1 += 1
288
+ while (i1<h1.m and
289
+ (tmp1.sets[i1][0] < h2_trace.sets[i2][0] or
290
+ tmp1.sets[i1][1] < h2_trace.sets[i2][1])):
291
+ i1 += 1
292
+ if (i1>=h1.m or
293
+ (tmp1.sets[i1][0] > h2_trace.sets[i2][0] or
294
+ tmp1.sets[i1][1] > h2_trace.sets[i2][1])):
295
+ return 0
296
+
297
+ return 1
298
+
299
+ cdef int cmp_128_bits(const void * a, const void * b) noexcept nogil:
300
+ r"""
301
+ Lexicographic order on 128-bits words
302
+ """
303
+ cdef uint64_t * p1 = (<uint64_t **> a)[0]
304
+ cdef uint64_t * p2 = (<uint64_t **> b)[0]
305
+ if p1[0] > p2[0]:
306
+ return 1
307
+ elif p1[0] == p2[0]:
308
+ return 1 if p1[1] > p2[1] else -1
309
+ else:
310
+ return -1
311
+
312
+ cdef int is_induced_admissible64(hypergraph h1, hypergraph * h2_induced, int n, hypergraph tmp1) noexcept:
313
+ r"""
314
+ Test if the hypergraph induced in h1 by 0,...,n-1 is equal to the hypergraph
315
+ induced in h2 by 0,...,n-1.
316
+
317
+ ``h2_induced`` is expected to be a pointer toward the hypergraph induced by
318
+ the first n points of ``h2``. Its sets must be sorted according to
319
+ ``cmp_128_bits``.
320
+
321
+ Assumes `n<=64`.
322
+ """
323
+ # *MUST* cache the info of tmp2 which only depends on n !!!!
324
+ # *MUST* cache the sets' size
325
+ induced_hypergraph(&h1,n,&tmp1)
326
+
327
+ if tmp1.m!=h2_induced.m:
328
+ return 0
329
+
330
+ qsort(tmp1.sets,tmp1.m,sizeof(uint64_t *),cmp_128_bits)
331
+
332
+ cdef int i
333
+ for i in range(tmp1.m):
334
+ if tmp1.sets[i][0] != h2_induced.sets[i][0]:
335
+ return 0
336
+
337
+ return 1
338
+
339
+ cdef class SubHypergraphSearch:
340
+
341
+ cdef hypergraph h1,h2,tmp1,tmp2
342
+ cdef list points1,points2
343
+ cdef int induced
344
+ cdef int * step
345
+ cdef hypergraph * h2_traces
346
+ cdef hypergraph * h2_induced
347
+
348
+ def __cinit__(self, H1, H2, induced):
349
+ r"""
350
+ See the documentation's class.
351
+
352
+ EXAMPLES::
353
+
354
+ sage: from sage.combinat.designs.subhypergraph_search import SubHypergraphSearch
355
+ sage: g1 = IncidenceStructure(graphs.PetersenGraph().edges(sort=True, labels=False))
356
+ sage: g2 = IncidenceStructure(graphs.CycleGraph(5).edges(sort=True, labels=False))
357
+ sage: S = SubHypergraphSearch(g1,g2,0)
358
+ sage: sum(1 for _ in S)
359
+ 120
360
+ """
361
+ self.points1 = H1._points
362
+ self.points2 = H2._points
363
+ self.induced = induced
364
+ cdef int n1 = H1.num_points()
365
+ cdef int n2 = H2.num_points()
366
+
367
+ if n2>64:
368
+ raise RuntimeError("H2 has {}>64 points".format(n2))
369
+
370
+ self.h1 = h_init(n1,H1._blocks)
371
+ self.h2 = h_init(n2,H2._blocks)
372
+ self.tmp1 = h_init(n1,H1._blocks) # No actual need to fill them,
373
+ self.tmp2 = h_init(n2,H2._blocks) # only allocate the memory
374
+
375
+ self.step = <int *> sig_malloc((n2+1)*sizeof(int))
376
+
377
+ # all possible traces/induced subgraphs for h2
378
+ #
379
+ # (calloc sets all internal pointers to NULL)
380
+ self.h2_traces = <hypergraph *> sig_calloc(n2+1,sizeof(hypergraph))
381
+ self.h2_induced = <hypergraph *> sig_calloc(n2+1,sizeof(hypergraph))
382
+
383
+ if (self.h1.n == -1 or
384
+ self.h2.n == -1 or
385
+ self.tmp1.n == -1 or
386
+ self.tmp2.n == -1 or
387
+ self.h2_traces == NULL or
388
+ self.h2_induced == NULL or
389
+ self.step == NULL):
390
+ raise MemoryError # also calls __dealloc__
391
+
392
+ self.relabel_heuristic()
393
+
394
+ # cache all n2+1 traces of h2, we will need them often.
395
+ cdef int i
396
+ for i in range(n2+1):
397
+ self.h2_traces[i] = h_init(n2,H2._blocks)
398
+ if self.h2_traces[i].n == -1:
399
+ raise MemoryError
400
+ trace_hypergraph64(&self.h2,i,&self.h2_traces[i])
401
+ qsort(self.h2_traces[i].sets,self.h2.m,sizeof(uint64_t *),cmp_128_bits)
402
+
403
+ # cache all n2+1 induced subhypergraphs of h2, we will need them often.
404
+ for i in range(n2+1):
405
+ self.h2_induced[i] = h_init(n2,H2._blocks)
406
+ if self.h2_induced[i].n == -1:
407
+ raise MemoryError
408
+ induced_hypergraph(&self.h2,i,&self.h2_induced[i])
409
+ qsort(self.h2_induced[i].sets,self.h2_induced[i].m,sizeof(uint64_t *),cmp_128_bits)
410
+
411
+ def __dealloc__(self):
412
+ r"""
413
+ Free the ressources
414
+ """
415
+ # If self.h2 was not allocated, then self.h2.n = -1
416
+ cdef int i
417
+ for i in range(self.h2.n+1):
418
+ h_free(self.h2_traces[i])
419
+ h_free(self.h2_induced[i])
420
+ h_free(self.h1)
421
+ h_free(self.h2)
422
+ h_free(self.tmp1)
423
+ h_free(self.tmp2)
424
+ sig_free(self.step)
425
+ sig_free(self.h2_traces)
426
+ sig_free(self.h2_induced)
427
+
428
+ def relabel_heuristic(self):
429
+ r"""
430
+ Relabel `H_2` in order to make the algorithm faster.
431
+
432
+ Objective: we try to pick an ordering `p_1,...,p_k` of the points of
433
+ `H_2` that maximizes the number of sets involving the first points in
434
+ the ordering. One way to formalize the problems indicates that it may be
435
+ NP-Hard (generalizes the max clique problem for graphs) so we do not try
436
+ to solve it exactly: we just need a sufficiently good heuristic.
437
+
438
+ Assuming that the first points are `p_1,...,p_k`, we determine `p_{k+1}`
439
+ as the point `x` such that the number of sets `S` with `x\in S` and
440
+ `S\cap \{p_1,...,p_k\}\neq \emptyset` is maximal. In case of ties, we
441
+ take a point with maximum degree.
442
+
443
+ This function is called when an instance of :class:`SubHypergraphSearch`
444
+ is created.
445
+
446
+ EXAMPLES::
447
+
448
+ sage: d = designs.projective_plane(3) # needs sage.schemes
449
+ sage: d.isomorphic_substructures_iterator(d).relabel_heuristic() # needs sage.schemes
450
+ """
451
+ cdef hypergraph h2 = self.h2
452
+ cdef int x,y,i
453
+ cdef list degree = [0]*h2.n
454
+ cdef list degree_with_ordered_points = [0]*h2.n
455
+ cdef uint64_t current_set = 0
456
+
457
+ # pre-compute the degree of each point
458
+ for x in range(h2.n):
459
+ y = 0
460
+ for i in range(h2.m):
461
+ y += bs_get(h2.sets[i],x)
462
+ degree[x] = y
463
+
464
+ # Compute the actual ordering
465
+ for x in range(h2.n):
466
+ y = 0
467
+ for i in range(h2.m):
468
+ if h2.sets[i][0] & current_set:
469
+ y += bs_get(h2.sets[i],x)
470
+ degree_with_ordered_points[x] = y
471
+ next_point = max((degree_with_ordered_points[i],degree[i],i)
472
+ for i in range(x,h2.n))[2]
473
+ permute(&h2,x,next_point)
474
+ current_set += 1<<x
475
+
476
+ def __iter__(self):
477
+ r"""
478
+ Iterate over all copies of h2 in h1.
479
+
480
+ EXAMPLES:
481
+
482
+ How many distinct `C_5` in Petersen's graph ? ::
483
+
484
+ sage: P = graphs.PetersenGraph()
485
+ sage: C = graphs.CycleGraph(5)
486
+ sage: IP = IncidenceStructure(P.edges(sort=True, labels=False))
487
+ sage: IC = IncidenceStructure(C.edges(sort=True, labels=False))
488
+ sage: sum(1 for _ in IP.isomorphic_substructures_iterator(IC))
489
+ 120
490
+ """
491
+ cdef hypergraph h1 = self.h1
492
+ cdef hypergraph h2 = self.h2
493
+ cdef hypergraph tmp1 = self.tmp1
494
+ cdef int * step = self.step
495
+
496
+ if h1.n<h2.n or h1.m<h2.m:
497
+ return
498
+
499
+ self.step[0] = -1
500
+
501
+ # n is the current point of H2 whose representant must be decided
502
+ cdef int n = 0
503
+
504
+ while n>=0:
505
+ step[n] += 1
506
+
507
+ # If we went through all permutations beginning with
508
+ # h1.names[0],...,h1.names[n-1].
509
+ if n+step[n]==h1.n:
510
+ step[n] = -1
511
+ n -= 1
512
+ if n>=0:
513
+ permute(&h1,n,n+step[n])
514
+ continue
515
+
516
+ permute(&h1,n,n+step[n])
517
+
518
+ # Filter the assignments. If this 'if' is replaced by 'if True:', the
519
+ # code enumerates all n1!/(n1-n2)! injections from [n2] into [n1].
520
+ if ((not self.induced or is_induced_admissible64(h1,&self.h2_induced[n],n,tmp1)) and
521
+ is_subhypergraph_admissible(h1,&self.h2_traces[n],n,tmp1)):
522
+ n += 1
523
+ step[n] = -1
524
+ if n == h2.n:
525
+ yield {self.points2[h2.names[i]]:self.points1[h1.names[i]] for i in range(h2.n)}
526
+ n -= 1
527
+ else:
528
+ continue
529
+
530
+ permute(&h1,n,n+step[n])