okstra 0.107.2 → 0.109.0

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 (649) hide show
  1. package/package.json +1 -1
  2. package/runtime/BUILD.json +2 -2
  3. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  4. package/runtime/prompts/profiles/_common-contract.md +3 -2
  5. package/runtime/python/okstra_ctl/graphify_cmd.py +225 -0
  6. package/runtime/python/okstra_ctl/resolve_task_key.py +15 -9
  7. package/runtime/python/okstra_ctl/session.py +44 -0
  8. package/runtime/python/okstra_project/__init__.py +2 -0
  9. package/runtime/python/okstra_project/state.py +36 -0
  10. package/runtime/python/okstra_token_usage/claude.py +15 -9
  11. package/runtime/python/okstra_token_usage/cli.py +23 -0
  12. package/runtime/python/okstra_token_usage/collect.py +163 -4
  13. package/runtime/python/okstra_vendor/__init__.py +41 -0
  14. package/runtime/python/okstra_vendor/graphify/.vendored-version +1 -0
  15. package/runtime/python/okstra_vendor/graphify/__init__.py +28 -0
  16. package/runtime/python/okstra_vendor/graphify/__main__.py +1371 -0
  17. package/runtime/python/okstra_vendor/graphify/analyze.py +540 -0
  18. package/runtime/python/okstra_vendor/graphify/benchmark.py +129 -0
  19. package/runtime/python/okstra_vendor/graphify/build.py +107 -0
  20. package/runtime/python/okstra_vendor/graphify/cache.py +169 -0
  21. package/runtime/python/okstra_vendor/graphify/cluster.py +137 -0
  22. package/runtime/python/okstra_vendor/graphify/detect.py +510 -0
  23. package/runtime/python/okstra_vendor/graphify/export.py +1014 -0
  24. package/runtime/python/okstra_vendor/graphify/extract.py +3277 -0
  25. package/runtime/python/okstra_vendor/graphify/hooks.py +220 -0
  26. package/runtime/python/okstra_vendor/graphify/ingest.py +297 -0
  27. package/runtime/python/okstra_vendor/graphify/manifest.py +4 -0
  28. package/runtime/python/okstra_vendor/graphify/report.py +175 -0
  29. package/runtime/python/okstra_vendor/graphify/security.py +203 -0
  30. package/runtime/python/okstra_vendor/graphify/serve.py +373 -0
  31. package/runtime/python/okstra_vendor/graphify/skill-aider.md +1184 -0
  32. package/runtime/python/okstra_vendor/graphify/skill-claw.md +1184 -0
  33. package/runtime/python/okstra_vendor/graphify/skill-codex.md +1242 -0
  34. package/runtime/python/okstra_vendor/graphify/skill-copilot.md +1268 -0
  35. package/runtime/python/okstra_vendor/graphify/skill-droid.md +1239 -0
  36. package/runtime/python/okstra_vendor/graphify/skill-kiro.md +1183 -0
  37. package/runtime/python/okstra_vendor/graphify/skill-opencode.md +1238 -0
  38. package/runtime/python/okstra_vendor/graphify/skill-trae.md +1208 -0
  39. package/runtime/python/okstra_vendor/graphify/skill-vscode.md +253 -0
  40. package/runtime/python/okstra_vendor/graphify/skill-windows.md +1245 -0
  41. package/runtime/python/okstra_vendor/graphify/skill.md +1319 -0
  42. package/runtime/python/okstra_vendor/graphify/transcribe.py +182 -0
  43. package/runtime/python/okstra_vendor/graphify/validate.py +72 -0
  44. package/runtime/python/okstra_vendor/graphify/watch.py +188 -0
  45. package/runtime/python/okstra_vendor/graphify/wiki.py +214 -0
  46. package/runtime/python/okstra_vendor/networkx/__init__.py +62 -0
  47. package/runtime/python/okstra_vendor/networkx/algorithms/__init__.py +134 -0
  48. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/__init__.py +26 -0
  49. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clique.py +259 -0
  50. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clustering_coefficient.py +71 -0
  51. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/connectivity.py +412 -0
  52. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/density.py +396 -0
  53. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/distance_measures.py +150 -0
  54. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/dominating_set.py +149 -0
  55. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/kcomponents.py +369 -0
  56. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/matching.py +44 -0
  57. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/maxcut.py +143 -0
  58. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/ramsey.py +53 -0
  59. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/steinertree.py +265 -0
  60. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
  61. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +41 -0
  62. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +112 -0
  63. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +199 -0
  64. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +146 -0
  65. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +59 -0
  66. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +78 -0
  67. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +303 -0
  68. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +8 -0
  69. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +94 -0
  70. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +31 -0
  71. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +306 -0
  72. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +1014 -0
  73. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +274 -0
  74. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +68 -0
  75. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/traveling_salesman.py +1508 -0
  76. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/treewidth.py +255 -0
  77. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/vertex_cover.py +83 -0
  78. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/__init__.py +5 -0
  79. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/connectivity.py +122 -0
  80. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/correlation.py +302 -0
  81. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/mixing.py +255 -0
  82. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/neighbor_degree.py +160 -0
  83. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/pairs.py +127 -0
  84. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
  85. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +81 -0
  86. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +143 -0
  87. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +122 -0
  88. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +174 -0
  89. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +107 -0
  90. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +87 -0
  91. package/runtime/python/okstra_vendor/networkx/algorithms/asteroidal.py +164 -0
  92. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/__init__.py +88 -0
  93. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/basic.py +322 -0
  94. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/centrality.py +290 -0
  95. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/cluster.py +289 -0
  96. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/covering.py +57 -0
  97. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/edgelist.py +360 -0
  98. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/extendability.py +105 -0
  99. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/generators.py +603 -0
  100. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/link_analysis.py +316 -0
  101. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matching.py +590 -0
  102. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matrix.py +232 -0
  103. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/projection.py +526 -0
  104. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/redundancy.py +112 -0
  105. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/spectral.py +69 -0
  106. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
  107. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +125 -0
  108. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +192 -0
  109. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +84 -0
  110. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +33 -0
  111. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +240 -0
  112. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +334 -0
  113. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +407 -0
  114. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +218 -0
  115. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +327 -0
  116. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +138 -0
  117. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +409 -0
  118. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +35 -0
  119. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +80 -0
  120. package/runtime/python/okstra_vendor/networkx/algorithms/boundary.py +168 -0
  121. package/runtime/python/okstra_vendor/networkx/algorithms/bridges.py +205 -0
  122. package/runtime/python/okstra_vendor/networkx/algorithms/broadcasting.py +164 -0
  123. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/__init__.py +20 -0
  124. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness.py +591 -0
  125. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness_subset.py +236 -0
  126. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/closeness.py +282 -0
  127. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness.py +364 -0
  128. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness_subset.py +227 -0
  129. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_closeness.py +96 -0
  130. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/degree_alg.py +150 -0
  131. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/dispersion.py +107 -0
  132. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/eigenvector.py +357 -0
  133. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/flow_matrix.py +130 -0
  134. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/group.py +787 -0
  135. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/harmonic.py +88 -0
  136. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/katz.py +331 -0
  137. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/laplacian.py +150 -0
  138. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/load.py +200 -0
  139. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/percolation.py +128 -0
  140. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/reaching.py +209 -0
  141. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/second_order.py +141 -0
  142. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/subgraph_alg.py +361 -0
  143. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
  144. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +923 -0
  145. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +354 -0
  146. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +274 -0
  147. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +259 -0
  148. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +147 -0
  149. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +43 -0
  150. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +144 -0
  151. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +73 -0
  152. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +186 -0
  153. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +277 -0
  154. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +122 -0
  155. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +345 -0
  156. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +220 -0
  157. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +344 -0
  158. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +87 -0
  159. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +140 -0
  160. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +82 -0
  161. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +110 -0
  162. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +302 -0
  163. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +64 -0
  164. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/trophic.py +181 -0
  165. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/voterank_alg.py +95 -0
  166. package/runtime/python/okstra_vendor/networkx/algorithms/chains.py +172 -0
  167. package/runtime/python/okstra_vendor/networkx/algorithms/chordal.py +443 -0
  168. package/runtime/python/okstra_vendor/networkx/algorithms/clique.py +818 -0
  169. package/runtime/python/okstra_vendor/networkx/algorithms/cluster.py +732 -0
  170. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/__init__.py +4 -0
  171. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/equitable_coloring.py +505 -0
  172. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/greedy_coloring.py +565 -0
  173. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
  174. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +863 -0
  175. package/runtime/python/okstra_vendor/networkx/algorithms/communicability_alg.py +163 -0
  176. package/runtime/python/okstra_vendor/networkx/algorithms/community/__init__.py +28 -0
  177. package/runtime/python/okstra_vendor/networkx/algorithms/community/asyn_fluid.py +153 -0
  178. package/runtime/python/okstra_vendor/networkx/algorithms/community/bipartitions.py +354 -0
  179. package/runtime/python/okstra_vendor/networkx/algorithms/community/centrality.py +171 -0
  180. package/runtime/python/okstra_vendor/networkx/algorithms/community/community_utils.py +30 -0
  181. package/runtime/python/okstra_vendor/networkx/algorithms/community/divisive.py +216 -0
  182. package/runtime/python/okstra_vendor/networkx/algorithms/community/kclique.py +79 -0
  183. package/runtime/python/okstra_vendor/networkx/algorithms/community/label_propagation.py +338 -0
  184. package/runtime/python/okstra_vendor/networkx/algorithms/community/leiden.py +162 -0
  185. package/runtime/python/okstra_vendor/networkx/algorithms/community/local.py +220 -0
  186. package/runtime/python/okstra_vendor/networkx/algorithms/community/louvain.py +384 -0
  187. package/runtime/python/okstra_vendor/networkx/algorithms/community/lukes.py +227 -0
  188. package/runtime/python/okstra_vendor/networkx/algorithms/community/modularity_max.py +452 -0
  189. package/runtime/python/okstra_vendor/networkx/algorithms/community/quality.py +347 -0
  190. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
  191. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +147 -0
  192. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +157 -0
  193. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +85 -0
  194. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +106 -0
  195. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +91 -0
  196. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +241 -0
  197. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +138 -0
  198. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +76 -0
  199. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +264 -0
  200. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +152 -0
  201. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +340 -0
  202. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +139 -0
  203. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +26 -0
  204. package/runtime/python/okstra_vendor/networkx/algorithms/components/__init__.py +6 -0
  205. package/runtime/python/okstra_vendor/networkx/algorithms/components/attracting.py +115 -0
  206. package/runtime/python/okstra_vendor/networkx/algorithms/components/biconnected.py +394 -0
  207. package/runtime/python/okstra_vendor/networkx/algorithms/components/connected.py +282 -0
  208. package/runtime/python/okstra_vendor/networkx/algorithms/components/semiconnected.py +71 -0
  209. package/runtime/python/okstra_vendor/networkx/algorithms/components/strongly_connected.py +359 -0
  210. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
  211. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +70 -0
  212. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +248 -0
  213. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +138 -0
  214. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +55 -0
  215. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +193 -0
  216. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +96 -0
  217. package/runtime/python/okstra_vendor/networkx/algorithms/components/weakly_connected.py +196 -0
  218. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/__init__.py +11 -0
  219. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/connectivity.py +811 -0
  220. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/cuts.py +616 -0
  221. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/disjoint_paths.py +408 -0
  222. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_augmentation.py +1270 -0
  223. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_kcomponents.py +592 -0
  224. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcomponents.py +220 -0
  225. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcutsets.py +235 -0
  226. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/stoerwagner.py +152 -0
  227. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
  228. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +421 -0
  229. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +309 -0
  230. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +249 -0
  231. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +502 -0
  232. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +488 -0
  233. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +323 -0
  234. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +280 -0
  235. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +102 -0
  236. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/utils.py +88 -0
  237. package/runtime/python/okstra_vendor/networkx/algorithms/core.py +588 -0
  238. package/runtime/python/okstra_vendor/networkx/algorithms/covering.py +142 -0
  239. package/runtime/python/okstra_vendor/networkx/algorithms/cuts.py +416 -0
  240. package/runtime/python/okstra_vendor/networkx/algorithms/cycles.py +1234 -0
  241. package/runtime/python/okstra_vendor/networkx/algorithms/d_separation.py +677 -0
  242. package/runtime/python/okstra_vendor/networkx/algorithms/dag.py +1392 -0
  243. package/runtime/python/okstra_vendor/networkx/algorithms/distance_measures.py +1095 -0
  244. package/runtime/python/okstra_vendor/networkx/algorithms/distance_regular.py +272 -0
  245. package/runtime/python/okstra_vendor/networkx/algorithms/dominance.py +142 -0
  246. package/runtime/python/okstra_vendor/networkx/algorithms/dominating.py +268 -0
  247. package/runtime/python/okstra_vendor/networkx/algorithms/efficiency_measures.py +167 -0
  248. package/runtime/python/okstra_vendor/networkx/algorithms/euler.py +470 -0
  249. package/runtime/python/okstra_vendor/networkx/algorithms/flow/__init__.py +11 -0
  250. package/runtime/python/okstra_vendor/networkx/algorithms/flow/boykovkolmogorov.py +370 -0
  251. package/runtime/python/okstra_vendor/networkx/algorithms/flow/capacityscaling.py +407 -0
  252. package/runtime/python/okstra_vendor/networkx/algorithms/flow/dinitz_alg.py +238 -0
  253. package/runtime/python/okstra_vendor/networkx/algorithms/flow/edmondskarp.py +241 -0
  254. package/runtime/python/okstra_vendor/networkx/algorithms/flow/gomory_hu.py +178 -0
  255. package/runtime/python/okstra_vendor/networkx/algorithms/flow/maxflow.py +611 -0
  256. package/runtime/python/okstra_vendor/networkx/algorithms/flow/mincost.py +356 -0
  257. package/runtime/python/okstra_vendor/networkx/algorithms/flow/networksimplex.py +662 -0
  258. package/runtime/python/okstra_vendor/networkx/algorithms/flow/preflowpush.py +425 -0
  259. package/runtime/python/okstra_vendor/networkx/algorithms/flow/shortestaugmentingpath.py +300 -0
  260. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
  261. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
  262. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
  263. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
  264. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +128 -0
  265. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +573 -0
  266. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +155 -0
  267. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +475 -0
  268. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +481 -0
  269. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
  270. package/runtime/python/okstra_vendor/networkx/algorithms/flow/utils.py +194 -0
  271. package/runtime/python/okstra_vendor/networkx/algorithms/graph_hashing.py +435 -0
  272. package/runtime/python/okstra_vendor/networkx/algorithms/graphical.py +483 -0
  273. package/runtime/python/okstra_vendor/networkx/algorithms/hierarchy.py +57 -0
  274. package/runtime/python/okstra_vendor/networkx/algorithms/hybrid.py +196 -0
  275. package/runtime/python/okstra_vendor/networkx/algorithms/isolate.py +107 -0
  276. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/__init__.py +7 -0
  277. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/ismags.py +1306 -0
  278. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorph.py +336 -0
  279. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorphvf2.py +1262 -0
  280. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/matchhelpers.py +352 -0
  281. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/temporalisomorphvf2.py +308 -0
  282. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
  283. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
  284. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
  285. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
  286. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
  287. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +719 -0
  288. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +103 -0
  289. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +490 -0
  290. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +64 -0
  291. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +212 -0
  292. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +202 -0
  293. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +1655 -0
  294. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +3118 -0
  295. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +196 -0
  296. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tree_isomorphism.py +264 -0
  297. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2pp.py +1102 -0
  298. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2userfunc.py +192 -0
  299. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/__init__.py +2 -0
  300. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/hits_alg.py +337 -0
  301. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/pagerank_alg.py +498 -0
  302. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
  303. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +77 -0
  304. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +213 -0
  305. package/runtime/python/okstra_vendor/networkx/algorithms/link_prediction.py +687 -0
  306. package/runtime/python/okstra_vendor/networkx/algorithms/lowest_common_ancestors.py +280 -0
  307. package/runtime/python/okstra_vendor/networkx/algorithms/matching.py +1148 -0
  308. package/runtime/python/okstra_vendor/networkx/algorithms/minors/__init__.py +27 -0
  309. package/runtime/python/okstra_vendor/networkx/algorithms/minors/contraction.py +738 -0
  310. package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +544 -0
  311. package/runtime/python/okstra_vendor/networkx/algorithms/mis.py +78 -0
  312. package/runtime/python/okstra_vendor/networkx/algorithms/moral.py +59 -0
  313. package/runtime/python/okstra_vendor/networkx/algorithms/node_classification.py +219 -0
  314. package/runtime/python/okstra_vendor/networkx/algorithms/non_randomness.py +155 -0
  315. package/runtime/python/okstra_vendor/networkx/algorithms/operators/__init__.py +4 -0
  316. package/runtime/python/okstra_vendor/networkx/algorithms/operators/all.py +324 -0
  317. package/runtime/python/okstra_vendor/networkx/algorithms/operators/binary.py +468 -0
  318. package/runtime/python/okstra_vendor/networkx/algorithms/operators/product.py +633 -0
  319. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
  320. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +328 -0
  321. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +451 -0
  322. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +491 -0
  323. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +55 -0
  324. package/runtime/python/okstra_vendor/networkx/algorithms/operators/unary.py +77 -0
  325. package/runtime/python/okstra_vendor/networkx/algorithms/perfect_graph.py +73 -0
  326. package/runtime/python/okstra_vendor/networkx/algorithms/planar_drawing.py +464 -0
  327. package/runtime/python/okstra_vendor/networkx/algorithms/planarity.py +1463 -0
  328. package/runtime/python/okstra_vendor/networkx/algorithms/polynomials.py +306 -0
  329. package/runtime/python/okstra_vendor/networkx/algorithms/reciprocity.py +98 -0
  330. package/runtime/python/okstra_vendor/networkx/algorithms/regular.py +167 -0
  331. package/runtime/python/okstra_vendor/networkx/algorithms/richclub.py +138 -0
  332. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/__init__.py +5 -0
  333. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/astar.py +239 -0
  334. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/dense.py +264 -0
  335. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/generic.py +716 -0
  336. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
  337. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +254 -0
  338. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +212 -0
  339. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +88 -0
  340. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +511 -0
  341. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +149 -0
  342. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +983 -0
  343. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/unweighted.py +625 -0
  344. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/weighted.py +2542 -0
  345. package/runtime/python/okstra_vendor/networkx/algorithms/similarity.py +2107 -0
  346. package/runtime/python/okstra_vendor/networkx/algorithms/simple_paths.py +966 -0
  347. package/runtime/python/okstra_vendor/networkx/algorithms/smallworld.py +404 -0
  348. package/runtime/python/okstra_vendor/networkx/algorithms/smetric.py +30 -0
  349. package/runtime/python/okstra_vendor/networkx/algorithms/sparsifiers.py +296 -0
  350. package/runtime/python/okstra_vendor/networkx/algorithms/structuralholes.py +374 -0
  351. package/runtime/python/okstra_vendor/networkx/algorithms/summarization.py +564 -0
  352. package/runtime/python/okstra_vendor/networkx/algorithms/swap.py +406 -0
  353. package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
  354. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +23 -0
  355. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +154 -0
  356. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +144 -0
  357. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +109 -0
  358. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +136 -0
  359. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +129 -0
  360. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +300 -0
  361. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +678 -0
  362. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +80 -0
  363. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +266 -0
  364. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +85 -0
  365. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +171 -0
  366. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +984 -0
  367. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +340 -0
  368. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +835 -0
  369. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +831 -0
  370. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +85 -0
  371. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +299 -0
  372. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +115 -0
  373. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +58 -0
  374. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +314 -0
  375. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +872 -0
  376. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +163 -0
  377. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +46 -0
  378. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +24 -0
  379. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +26 -0
  380. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +615 -0
  381. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +459 -0
  382. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +556 -0
  383. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +179 -0
  384. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +62 -0
  385. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +15 -0
  386. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +140 -0
  387. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +60 -0
  388. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +27 -0
  389. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +274 -0
  390. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +556 -0
  391. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +57 -0
  392. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +37 -0
  393. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +88 -0
  394. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +149 -0
  395. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +1158 -0
  396. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +803 -0
  397. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +76 -0
  398. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +8 -0
  399. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +138 -0
  400. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +191 -0
  401. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +642 -0
  402. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +179 -0
  403. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +270 -0
  404. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +431 -0
  405. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +161 -0
  406. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +248 -0
  407. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +41 -0
  408. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +103 -0
  409. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +54 -0
  410. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +157 -0
  411. package/runtime/python/okstra_vendor/networkx/algorithms/threshold.py +981 -0
  412. package/runtime/python/okstra_vendor/networkx/algorithms/time_dependent.py +142 -0
  413. package/runtime/python/okstra_vendor/networkx/algorithms/tournament.py +406 -0
  414. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/__init__.py +5 -0
  415. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/beamsearch.py +90 -0
  416. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/breadth_first_search.py +576 -0
  417. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/depth_first_search.py +529 -0
  418. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgebfs.py +185 -0
  419. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgedfs.py +182 -0
  420. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
  421. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +25 -0
  422. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +203 -0
  423. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +307 -0
  424. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +147 -0
  425. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +131 -0
  426. package/runtime/python/okstra_vendor/networkx/algorithms/tree/__init__.py +7 -0
  427. package/runtime/python/okstra_vendor/networkx/algorithms/tree/branchings.py +1042 -0
  428. package/runtime/python/okstra_vendor/networkx/algorithms/tree/coding.py +413 -0
  429. package/runtime/python/okstra_vendor/networkx/algorithms/tree/decomposition.py +88 -0
  430. package/runtime/python/okstra_vendor/networkx/algorithms/tree/distance_measures.py +219 -0
  431. package/runtime/python/okstra_vendor/networkx/algorithms/tree/mst.py +1281 -0
  432. package/runtime/python/okstra_vendor/networkx/algorithms/tree/operations.py +106 -0
  433. package/runtime/python/okstra_vendor/networkx/algorithms/tree/recognition.py +273 -0
  434. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
  435. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +624 -0
  436. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +114 -0
  437. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +79 -0
  438. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +99 -0
  439. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +934 -0
  440. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +53 -0
  441. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +174 -0
  442. package/runtime/python/okstra_vendor/networkx/algorithms/triads.py +500 -0
  443. package/runtime/python/okstra_vendor/networkx/algorithms/vitality.py +76 -0
  444. package/runtime/python/okstra_vendor/networkx/algorithms/voronoi.py +86 -0
  445. package/runtime/python/okstra_vendor/networkx/algorithms/walks.py +77 -0
  446. package/runtime/python/okstra_vendor/networkx/algorithms/wiener.py +278 -0
  447. package/runtime/python/okstra_vendor/networkx/classes/__init__.py +13 -0
  448. package/runtime/python/okstra_vendor/networkx/classes/coreviews.py +435 -0
  449. package/runtime/python/okstra_vendor/networkx/classes/digraph.py +1363 -0
  450. package/runtime/python/okstra_vendor/networkx/classes/filters.py +95 -0
  451. package/runtime/python/okstra_vendor/networkx/classes/function.py +1549 -0
  452. package/runtime/python/okstra_vendor/networkx/classes/graph.py +2082 -0
  453. package/runtime/python/okstra_vendor/networkx/classes/graphviews.py +269 -0
  454. package/runtime/python/okstra_vendor/networkx/classes/multidigraph.py +977 -0
  455. package/runtime/python/okstra_vendor/networkx/classes/multigraph.py +1294 -0
  456. package/runtime/python/okstra_vendor/networkx/classes/reportviews.py +1447 -0
  457. package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
  458. package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +192 -0
  459. package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +476 -0
  460. package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +362 -0
  461. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +331 -0
  462. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +110 -0
  463. package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +177 -0
  464. package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +1045 -0
  465. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +950 -0
  466. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +12 -0
  467. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +349 -0
  468. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +459 -0
  469. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +528 -0
  470. package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +1421 -0
  471. package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +131 -0
  472. package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +371 -0
  473. package/runtime/python/okstra_vendor/networkx/conftest.py +261 -0
  474. package/runtime/python/okstra_vendor/networkx/convert.py +502 -0
  475. package/runtime/python/okstra_vendor/networkx/convert_matrix.py +1314 -0
  476. package/runtime/python/okstra_vendor/networkx/drawing/__init__.py +7 -0
  477. package/runtime/python/okstra_vendor/networkx/drawing/layout.py +2036 -0
  478. package/runtime/python/okstra_vendor/networkx/drawing/nx_agraph.py +470 -0
  479. package/runtime/python/okstra_vendor/networkx/drawing/nx_latex.py +570 -0
  480. package/runtime/python/okstra_vendor/networkx/drawing/nx_pydot.py +361 -0
  481. package/runtime/python/okstra_vendor/networkx/drawing/nx_pylab.py +2978 -0
  482. package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
  483. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
  484. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
  485. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
  486. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
  487. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
  488. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
  489. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +237 -0
  490. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +229 -0
  491. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +285 -0
  492. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +631 -0
  493. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +146 -0
  494. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +1582 -0
  495. package/runtime/python/okstra_vendor/networkx/exception.py +131 -0
  496. package/runtime/python/okstra_vendor/networkx/generators/__init__.py +34 -0
  497. package/runtime/python/okstra_vendor/networkx/generators/atlas.dat.gz +0 -0
  498. package/runtime/python/okstra_vendor/networkx/generators/atlas.py +227 -0
  499. package/runtime/python/okstra_vendor/networkx/generators/classic.py +1091 -0
  500. package/runtime/python/okstra_vendor/networkx/generators/cographs.py +68 -0
  501. package/runtime/python/okstra_vendor/networkx/generators/community.py +1070 -0
  502. package/runtime/python/okstra_vendor/networkx/generators/degree_seq.py +886 -0
  503. package/runtime/python/okstra_vendor/networkx/generators/directed.py +572 -0
  504. package/runtime/python/okstra_vendor/networkx/generators/duplication.py +174 -0
  505. package/runtime/python/okstra_vendor/networkx/generators/ego.py +66 -0
  506. package/runtime/python/okstra_vendor/networkx/generators/expanders.py +499 -0
  507. package/runtime/python/okstra_vendor/networkx/generators/geometric.py +1037 -0
  508. package/runtime/python/okstra_vendor/networkx/generators/harary_graph.py +163 -0
  509. package/runtime/python/okstra_vendor/networkx/generators/internet_as_graphs.py +443 -0
  510. package/runtime/python/okstra_vendor/networkx/generators/intersection.py +125 -0
  511. package/runtime/python/okstra_vendor/networkx/generators/interval_graph.py +70 -0
  512. package/runtime/python/okstra_vendor/networkx/generators/joint_degree_seq.py +664 -0
  513. package/runtime/python/okstra_vendor/networkx/generators/lattice.py +405 -0
  514. package/runtime/python/okstra_vendor/networkx/generators/line.py +501 -0
  515. package/runtime/python/okstra_vendor/networkx/generators/mycielski.py +110 -0
  516. package/runtime/python/okstra_vendor/networkx/generators/nonisomorphic_trees.py +259 -0
  517. package/runtime/python/okstra_vendor/networkx/generators/random_clustered.py +117 -0
  518. package/runtime/python/okstra_vendor/networkx/generators/random_graphs.py +1416 -0
  519. package/runtime/python/okstra_vendor/networkx/generators/small.py +1070 -0
  520. package/runtime/python/okstra_vendor/networkx/generators/social.py +554 -0
  521. package/runtime/python/okstra_vendor/networkx/generators/spectral_graph_forge.py +120 -0
  522. package/runtime/python/okstra_vendor/networkx/generators/stochastic.py +54 -0
  523. package/runtime/python/okstra_vendor/networkx/generators/sudoku.py +131 -0
  524. package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
  525. package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +75 -0
  526. package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +642 -0
  527. package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +20 -0
  528. package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +362 -0
  529. package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +224 -0
  530. package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +189 -0
  531. package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +103 -0
  532. package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +39 -0
  533. package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +182 -0
  534. package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +488 -0
  535. package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +133 -0
  536. package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +221 -0
  537. package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +28 -0
  538. package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +144 -0
  539. package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +125 -0
  540. package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +264 -0
  541. package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +316 -0
  542. package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +30 -0
  543. package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +82 -0
  544. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +33 -0
  545. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +495 -0
  546. package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +220 -0
  547. package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +49 -0
  548. package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +72 -0
  549. package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +92 -0
  550. package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +64 -0
  551. package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +195 -0
  552. package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +15 -0
  553. package/runtime/python/okstra_vendor/networkx/generators/time_series.py +74 -0
  554. package/runtime/python/okstra_vendor/networkx/generators/trees.py +1070 -0
  555. package/runtime/python/okstra_vendor/networkx/generators/triads.py +94 -0
  556. package/runtime/python/okstra_vendor/networkx/lazy_imports.py +188 -0
  557. package/runtime/python/okstra_vendor/networkx/linalg/__init__.py +13 -0
  558. package/runtime/python/okstra_vendor/networkx/linalg/algebraicconnectivity.py +650 -0
  559. package/runtime/python/okstra_vendor/networkx/linalg/attrmatrix.py +466 -0
  560. package/runtime/python/okstra_vendor/networkx/linalg/bethehessianmatrix.py +77 -0
  561. package/runtime/python/okstra_vendor/networkx/linalg/graphmatrix.py +168 -0
  562. package/runtime/python/okstra_vendor/networkx/linalg/laplacianmatrix.py +512 -0
  563. package/runtime/python/okstra_vendor/networkx/linalg/modularitymatrix.py +166 -0
  564. package/runtime/python/okstra_vendor/networkx/linalg/spectrum.py +186 -0
  565. package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
  566. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +400 -0
  567. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +108 -0
  568. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +40 -0
  569. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +275 -0
  570. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +334 -0
  571. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +86 -0
  572. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +70 -0
  573. package/runtime/python/okstra_vendor/networkx/readwrite/__init__.py +17 -0
  574. package/runtime/python/okstra_vendor/networkx/readwrite/adjlist.py +330 -0
  575. package/runtime/python/okstra_vendor/networkx/readwrite/edgelist.py +489 -0
  576. package/runtime/python/okstra_vendor/networkx/readwrite/gexf.py +1084 -0
  577. package/runtime/python/okstra_vendor/networkx/readwrite/gml.py +879 -0
  578. package/runtime/python/okstra_vendor/networkx/readwrite/graph6.py +427 -0
  579. package/runtime/python/okstra_vendor/networkx/readwrite/graphml.py +1053 -0
  580. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/__init__.py +19 -0
  581. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/adjacency.py +156 -0
  582. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/cytoscape.py +190 -0
  583. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/node_link.py +261 -0
  584. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
  585. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +78 -0
  586. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +78 -0
  587. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +109 -0
  588. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +48 -0
  589. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tree.py +137 -0
  590. package/runtime/python/okstra_vendor/networkx/readwrite/leda.py +108 -0
  591. package/runtime/python/okstra_vendor/networkx/readwrite/multiline_adjlist.py +393 -0
  592. package/runtime/python/okstra_vendor/networkx/readwrite/p2g.py +113 -0
  593. package/runtime/python/okstra_vendor/networkx/readwrite/pajek.py +286 -0
  594. package/runtime/python/okstra_vendor/networkx/readwrite/sparse6.py +379 -0
  595. package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
  596. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +354 -0
  597. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +318 -0
  598. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +612 -0
  599. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +744 -0
  600. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +181 -0
  601. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +1531 -0
  602. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +30 -0
  603. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +63 -0
  604. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +128 -0
  605. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +166 -0
  606. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +1742 -0
  607. package/runtime/python/okstra_vendor/networkx/readwrite/text.py +851 -0
  608. package/runtime/python/okstra_vendor/networkx/relabel.py +285 -0
  609. package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
  610. package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +248 -0
  611. package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +321 -0
  612. package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +531 -0
  613. package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +349 -0
  614. package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +281 -0
  615. package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +40 -0
  616. package/runtime/python/okstra_vendor/networkx/tests/test_import.py +11 -0
  617. package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +96 -0
  618. package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +349 -0
  619. package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +8 -0
  620. package/runtime/python/okstra_vendor/networkx/utils/__init__.py +8 -0
  621. package/runtime/python/okstra_vendor/networkx/utils/backends.py +2171 -0
  622. package/runtime/python/okstra_vendor/networkx/utils/configs.py +396 -0
  623. package/runtime/python/okstra_vendor/networkx/utils/decorators.py +1233 -0
  624. package/runtime/python/okstra_vendor/networkx/utils/heaps.py +338 -0
  625. package/runtime/python/okstra_vendor/networkx/utils/mapped_queue.py +297 -0
  626. package/runtime/python/okstra_vendor/networkx/utils/misc.py +703 -0
  627. package/runtime/python/okstra_vendor/networkx/utils/random_sequence.py +198 -0
  628. package/runtime/python/okstra_vendor/networkx/utils/rcm.py +159 -0
  629. package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
  630. package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +11 -0
  631. package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +225 -0
  632. package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +263 -0
  633. package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +510 -0
  634. package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +131 -0
  635. package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +268 -0
  636. package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +393 -0
  637. package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +53 -0
  638. package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +63 -0
  639. package/runtime/python/okstra_vendor/networkx/utils/tests/test_unionfind.py +55 -0
  640. package/runtime/python/okstra_vendor/networkx/utils/union_find.py +106 -0
  641. package/runtime/skills/okstra-graphify/SKILL.md +161 -0
  642. package/runtime/skills/okstra-inspect/SKILL.md +17 -9
  643. package/runtime/templates/reports/settings.template.json +4 -0
  644. package/runtime/validators/forbidden_actions.py +8 -2
  645. package/runtime/validators/validate_session_conformance.py +26 -5
  646. package/src/cli-registry.mjs +7 -0
  647. package/src/commands/graphify.mjs +32 -0
  648. package/src/commands/lifecycle/doctor.mjs +9 -0
  649. package/src/lib/skill-catalog.mjs +1 -0
@@ -0,0 +1,1655 @@
1
+ import itertools as it
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+ from networkx import vf2pp_all_isomorphisms, vf2pp_is_isomorphic, vf2pp_isomorphism
7
+
8
+ labels_same = ["blue"]
9
+
10
+ labels_many = [
11
+ "white",
12
+ "red",
13
+ "blue",
14
+ "green",
15
+ "orange",
16
+ "black",
17
+ "purple",
18
+ "yellow",
19
+ "brown",
20
+ "cyan",
21
+ "solarized",
22
+ "pink",
23
+ "none",
24
+ ]
25
+
26
+ graph_classes = [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
27
+
28
+
29
+ class TestPreCheck:
30
+ def test_first_graph_empty(self):
31
+ G1 = nx.Graph()
32
+ G2 = nx.Graph([(0, 1), (1, 2)])
33
+ assert not vf2pp_is_isomorphic(G1, G2)
34
+
35
+ def test_second_graph_empty(self):
36
+ G1 = nx.Graph([(0, 1), (1, 2)])
37
+ G2 = nx.Graph()
38
+ assert not vf2pp_is_isomorphic(G1, G2)
39
+
40
+ def test_different_order1(self):
41
+ G1 = nx.path_graph(5)
42
+ G2 = nx.path_graph(6)
43
+ assert not vf2pp_is_isomorphic(G1, G2)
44
+
45
+ def test_different_order2(self):
46
+ G1 = nx.barbell_graph(100, 20)
47
+ G2 = nx.barbell_graph(101, 20)
48
+ assert not vf2pp_is_isomorphic(G1, G2)
49
+
50
+ def test_different_order3(self):
51
+ G1 = nx.complete_graph(7)
52
+ G2 = nx.complete_graph(8)
53
+ assert not vf2pp_is_isomorphic(G1, G2)
54
+
55
+ def test_different_degree_sequences1(self):
56
+ G1 = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (0, 4)])
57
+ G2 = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (0, 4), (2, 5)])
58
+ assert not vf2pp_is_isomorphic(G1, G2)
59
+
60
+ G2.remove_node(3)
61
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(["a"]))), "label")
62
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle("a"))), "label")
63
+
64
+ assert vf2pp_is_isomorphic(G1, G2)
65
+
66
+ def test_different_degree_sequences2(self):
67
+ G1 = nx.Graph(
68
+ [
69
+ (0, 1),
70
+ (1, 2),
71
+ (0, 2),
72
+ (2, 3),
73
+ (3, 4),
74
+ (4, 5),
75
+ (5, 6),
76
+ (6, 3),
77
+ (4, 7),
78
+ (7, 8),
79
+ (8, 3),
80
+ ]
81
+ )
82
+ G2 = G1.copy()
83
+ G2.add_edge(8, 0)
84
+ assert not vf2pp_is_isomorphic(G1, G2)
85
+
86
+ G1.add_edge(6, 1)
87
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(["a"]))), "label")
88
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle("a"))), "label")
89
+
90
+ assert vf2pp_is_isomorphic(G1, G2)
91
+
92
+ def test_different_degree_sequences3(self):
93
+ G1 = nx.Graph([(0, 1), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4), (2, 5), (2, 6)])
94
+ G2 = nx.Graph(
95
+ [(0, 1), (0, 6), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4), (2, 5), (2, 6)]
96
+ )
97
+ assert not vf2pp_is_isomorphic(G1, G2)
98
+
99
+ G1.add_edge(3, 5)
100
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(["a"]))), "label")
101
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle("a"))), "label")
102
+
103
+ assert vf2pp_is_isomorphic(G1, G2)
104
+
105
+ def test_label_distribution(self):
106
+ G1 = nx.Graph([(0, 1), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4), (2, 5), (2, 6)])
107
+ G2 = nx.Graph([(0, 1), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4), (2, 5), (2, 6)])
108
+
109
+ colors1 = ["blue", "blue", "blue", "yellow", "black", "purple", "purple"]
110
+ colors2 = ["blue", "blue", "yellow", "yellow", "black", "purple", "purple"]
111
+
112
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(colors1[::-1]))), "label")
113
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(colors2[::-1]))), "label")
114
+
115
+ assert not vf2pp_is_isomorphic(G1, G2, node_label="label")
116
+ G2.nodes[3]["label"] = "blue"
117
+ assert vf2pp_is_isomorphic(G1, G2, node_label="label")
118
+
119
+
120
+ class TestAllGraphTypesEdgeCases:
121
+ @pytest.mark.parametrize("graph_type", graph_classes)
122
+ def test_both_graphs_empty(self, graph_type):
123
+ G = graph_type()
124
+ H = graph_type()
125
+ assert vf2pp_isomorphism(G, H) is None
126
+
127
+ G.add_node(0)
128
+
129
+ assert vf2pp_isomorphism(G, H) is None
130
+ assert vf2pp_isomorphism(H, G) is None
131
+
132
+ H.add_node(0)
133
+ assert vf2pp_isomorphism(G, H) == {0: 0}
134
+
135
+ @pytest.mark.parametrize("graph_type", graph_classes)
136
+ def test_first_graph_empty(self, graph_type):
137
+ G = graph_type()
138
+ H = graph_type([(0, 1)])
139
+ assert vf2pp_isomorphism(G, H) is None
140
+
141
+ @pytest.mark.parametrize("graph_type", graph_classes)
142
+ def test_second_graph_empty(self, graph_type):
143
+ G = graph_type([(0, 1)])
144
+ H = graph_type()
145
+ assert vf2pp_isomorphism(G, H) is None
146
+
147
+
148
+ class TestGraphISOVF2pp:
149
+ def test_custom_graph1_same_labels(self):
150
+ G1 = nx.Graph()
151
+
152
+ mapped = {1: "A", 2: "B", 3: "C", 4: "D", 5: "Z", 6: "E"}
153
+ edges1 = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 6), (3, 4), (5, 1), (5, 2)]
154
+ # 5-1 5-1 D-A
155
+ # |/|\ |/|\ |/|\ 6-X; 2-C; 5-D; 1-A; 3-B; 7-E; 4-Z
156
+ # 2-3-4 2-3-4 C-B-Z
157
+ # | | |/ | |/
158
+ # 6 6 7 X E
159
+
160
+ G1.add_edges_from(edges1)
161
+ G2 = nx.relabel_nodes(G1, mapped)
162
+
163
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
164
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_many))), "label")
165
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
166
+
167
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
168
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
169
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
170
+
171
+ # Add edge making G1 symmetrical
172
+ G1.add_edge(3, 7)
173
+ G1.nodes[7]["label"] = "blue"
174
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
175
+
176
+ all_self_mappings = list(vf2pp_all_isomorphisms(G1, G1))
177
+ assert len(all_self_mappings) == 2
178
+ # check that one of the mappings is not the identity for node 2
179
+ assert sum(1 for m in all_self_mappings if m[2] == 2) == 1
180
+
181
+ # Make G2 isomorphic to G1
182
+ G2.add_edges_from([(mapped[3], "X"), (mapped[6], mapped[5])])
183
+ G1.add_edge(4, 7)
184
+ G2.nodes["X"]["label"] = "blue"
185
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
186
+
187
+ # Re-structure maintaining isomorphism
188
+ G1.remove_edges_from([(1, 4), (1, 3)])
189
+ G2.remove_edges_from([(mapped[1], mapped[5]), (mapped[1], mapped[2])])
190
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
191
+
192
+ def test_custom_graph2_same_labels(self):
193
+ G1 = nx.Graph()
194
+
195
+ mapped = {1: "A", 2: "C", 3: "D", 4: "E", 5: "G", 7: "B", 6: "F"}
196
+ edges1 = [(1, 2), (1, 5), (5, 6), (2, 3), (2, 4), (3, 4), (4, 5), (2, 7)]
197
+ # 6-5
198
+ # /|\ 6-F; 2-C; 3-D; 1-A; 7-B; 4-E; 5-G
199
+ # 1-2-4
200
+ # /|/
201
+ # 7 3
202
+
203
+ G1.add_edges_from(edges1)
204
+ G2 = nx.relabel_nodes(G1, mapped)
205
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
206
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
207
+
208
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
209
+
210
+ # Obtain two isomorphic subgraphs from the graph
211
+ G2.remove_edge(mapped[1], mapped[2])
212
+ G2.add_edge(mapped[1], mapped[4])
213
+ H1 = nx.Graph(G1.subgraph([2, 3, 4, 7]))
214
+ H2 = nx.Graph(G2.subgraph([mapped[1], mapped[4], mapped[5], mapped[6]]))
215
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
216
+
217
+ # Add edges maintaining isomorphism
218
+ H1.add_edges_from([(3, 7), (4, 7)])
219
+ H2.add_edges_from([(mapped[1], mapped[6]), (mapped[4], mapped[6])])
220
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
221
+
222
+ def test_custom_graph2_different_labels(self):
223
+ G1 = nx.Graph()
224
+
225
+ mapped = {1: "A", 2: "C", 3: "D", 4: "E", 5: "G", 7: "B", 6: "F"}
226
+ edges1 = [(1, 2), (1, 5), (5, 6), (2, 3), (2, 4), (3, 4), (4, 5), (2, 7)]
227
+
228
+ G1.add_edges_from(edges1)
229
+ G2 = nx.relabel_nodes(G1, mapped)
230
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
231
+ nx.set_node_attributes(
232
+ G2,
233
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
234
+ "label",
235
+ )
236
+
237
+ # Adding new nodes
238
+ G1.add_node(0)
239
+ G2.add_node("Z")
240
+ G1.nodes[0]["label"] = G1.nodes[1]["label"]
241
+ G2.nodes["Z"]["label"] = G1.nodes[1]["label"]
242
+ mapped.update({0: "Z"})
243
+
244
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
245
+
246
+ # Change the color of one of the nodes
247
+ G2.nodes["Z"]["label"] = G1.nodes[2]["label"]
248
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
249
+
250
+ # Add an extra edge
251
+ G1.nodes[0]["label"] = "blue"
252
+ G2.nodes["Z"]["label"] = "blue"
253
+ G1.add_edge(0, 1)
254
+
255
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
256
+
257
+ # Add extra edge to both
258
+ G2.add_edge("Z", "A")
259
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
260
+
261
+ def test_custom_graph3_same_labels(self):
262
+ G1 = nx.Graph()
263
+
264
+ mapped = {1: 9, 2: 8, 3: 7, 4: 6, 5: 3, 8: 5, 9: 4, 7: 1, 6: 2}
265
+ edges1 = [
266
+ (1, 2),
267
+ (1, 3),
268
+ (2, 3),
269
+ (3, 4),
270
+ (4, 5),
271
+ (4, 7),
272
+ (4, 9),
273
+ (5, 8),
274
+ (8, 9),
275
+ (5, 6),
276
+ (6, 7),
277
+ (5, 2),
278
+ ]
279
+ G1.add_edges_from(edges1)
280
+ G2 = nx.relabel_nodes(G1, mapped)
281
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
282
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
283
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
284
+
285
+ # Connect nodes maintaining symmetry
286
+ G1.add_edges_from([(6, 9), (7, 8)])
287
+ G2.add_edges_from([(mapped[6], mapped[8]), (mapped[7], mapped[9])])
288
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
289
+
290
+ # Make isomorphic
291
+ G1.add_edges_from([(6, 8), (7, 9)])
292
+ G2.add_edges_from([(mapped[6], mapped[9]), (mapped[7], mapped[8])])
293
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
294
+
295
+ # Connect more nodes
296
+ G1.add_edges_from([(2, 7), (3, 6)])
297
+ G2.add_edges_from([(mapped[2], mapped[7]), (mapped[3], mapped[6])])
298
+ G1.add_node(10)
299
+ G2.add_node("Z")
300
+ G1.nodes[10]["label"] = "blue"
301
+ G2.nodes["Z"]["label"] = "blue"
302
+
303
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
304
+
305
+ # Connect the newly added node, to opposite sides of the graph
306
+ G1.add_edges_from([(10, 1), (10, 5), (10, 8)])
307
+ G2.add_edges_from([("Z", mapped[1]), ("Z", mapped[4]), ("Z", mapped[9])])
308
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
309
+
310
+ # Get two subgraphs that are not isomorphic but are easy to make
311
+ H1 = nx.Graph(G1.subgraph([2, 3, 4, 5, 6, 7, 10]))
312
+ H2 = nx.Graph(
313
+ G2.subgraph(
314
+ [mapped[4], mapped[5], mapped[6], mapped[7], mapped[8], mapped[9], "Z"]
315
+ )
316
+ )
317
+ assert vf2pp_isomorphism(H1, H2, node_label="label") is None
318
+
319
+ # Restructure both to make them isomorphic
320
+ H1.add_edges_from([(10, 2), (10, 6), (3, 6), (2, 7), (2, 6), (3, 7)])
321
+ H2.add_edges_from(
322
+ [("Z", mapped[7]), (mapped[6], mapped[9]), (mapped[7], mapped[8])]
323
+ )
324
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
325
+
326
+ # Add edges with opposite direction in each Graph
327
+ H1.add_edge(3, 5)
328
+ H2.add_edge(mapped[5], mapped[7])
329
+ assert vf2pp_isomorphism(H1, H2, node_label="label") is None
330
+
331
+ def test_custom_graph3_different_labels(self):
332
+ G1 = nx.Graph()
333
+
334
+ mapped = {1: 9, 2: 8, 3: 7, 4: 6, 5: 3, 8: 5, 9: 4, 7: 1, 6: 2}
335
+ edges1 = [
336
+ (1, 2),
337
+ (1, 3),
338
+ (2, 3),
339
+ (3, 4),
340
+ (4, 5),
341
+ (4, 7),
342
+ (4, 9),
343
+ (5, 8),
344
+ (8, 9),
345
+ (5, 6),
346
+ (6, 7),
347
+ (5, 2),
348
+ ]
349
+ G1.add_edges_from(edges1)
350
+ G2 = nx.relabel_nodes(G1, mapped)
351
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
352
+ nx.set_node_attributes(
353
+ G2,
354
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
355
+ "label",
356
+ )
357
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
358
+
359
+ # Add extra edge to G1
360
+ G1.add_edge(1, 7)
361
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
362
+
363
+ # Compensate in G2
364
+ G2.add_edge(9, 1)
365
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
366
+
367
+ # Add extra node
368
+ G1.add_node("A")
369
+ G2.add_node("K")
370
+ G1.nodes["A"]["label"] = "green"
371
+ G2.nodes["K"]["label"] = "green"
372
+ mapped.update({"A": "K"})
373
+
374
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
375
+
376
+ # Connect A to one side of G1 and K to the opposite
377
+ G1.add_edge("A", 6)
378
+ G2.add_edge("K", 5)
379
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
380
+
381
+ # Make the graphs symmetrical
382
+ G1.add_edge(1, 5)
383
+ G1.add_edge(2, 9)
384
+ G2.add_edge(9, 3)
385
+ G2.add_edge(8, 4)
386
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
387
+
388
+ # Assign same colors so the two opposite sides are identical
389
+ for node in G1.nodes():
390
+ color = "red"
391
+ G1.nodes[node]["label"] = color
392
+ G2.nodes[mapped[node]]["label"] = color
393
+
394
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
395
+
396
+ def test_custom_graph4_different_labels(self):
397
+ G1 = nx.Graph()
398
+ edges1 = [
399
+ (1, 2),
400
+ (2, 3),
401
+ (3, 8),
402
+ (3, 4),
403
+ (4, 5),
404
+ (4, 6),
405
+ (3, 6),
406
+ (8, 7),
407
+ (8, 9),
408
+ (5, 9),
409
+ (10, 11),
410
+ (11, 12),
411
+ (12, 13),
412
+ (11, 13),
413
+ ]
414
+
415
+ mapped = {
416
+ 1: "n",
417
+ 2: "m",
418
+ 3: "l",
419
+ 4: "j",
420
+ 5: "k",
421
+ 6: "i",
422
+ 7: "g",
423
+ 8: "h",
424
+ 9: "f",
425
+ 10: "b",
426
+ 11: "a",
427
+ 12: "d",
428
+ 13: "e",
429
+ }
430
+
431
+ G1.add_edges_from(edges1)
432
+ G2 = nx.relabel_nodes(G1, mapped)
433
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
434
+ nx.set_node_attributes(
435
+ G2,
436
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
437
+ "label",
438
+ )
439
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
440
+
441
+ def test_custom_graph4_same_labels(self):
442
+ G1 = nx.Graph()
443
+ edges1 = [
444
+ (1, 2),
445
+ (2, 3),
446
+ (3, 8),
447
+ (3, 4),
448
+ (4, 5),
449
+ (4, 6),
450
+ (3, 6),
451
+ (8, 7),
452
+ (8, 9),
453
+ (5, 9),
454
+ (10, 11),
455
+ (11, 12),
456
+ (12, 13),
457
+ (11, 13),
458
+ ]
459
+
460
+ mapped = {
461
+ 1: "n",
462
+ 2: "m",
463
+ 3: "l",
464
+ 4: "j",
465
+ 5: "k",
466
+ 6: "i",
467
+ 7: "g",
468
+ 8: "h",
469
+ 9: "f",
470
+ 10: "b",
471
+ 11: "a",
472
+ 12: "d",
473
+ 13: "e",
474
+ }
475
+
476
+ G1.add_edges_from(edges1)
477
+ G2 = nx.relabel_nodes(G1, mapped)
478
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
479
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
480
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
481
+
482
+ all_self_mappings = list(vf2pp_all_isomorphisms(G1, G1))
483
+ assert len(all_self_mappings) == 2
484
+ # check that one of the mappings is not the identity for node 2
485
+ assert sum(1 for m in all_self_mappings if m[12] == 12) == 1
486
+
487
+ # Add nodes of different label
488
+ G1.add_node(0)
489
+ G2.add_node("z")
490
+ G1.nodes[0]["label"] = "green"
491
+ G2.nodes["z"]["label"] = "blue"
492
+
493
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
494
+
495
+ # Make the labels identical
496
+ G2.nodes["z"]["label"] = "green"
497
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
498
+
499
+ # Change the structure of the graphs, keeping them isomorphic
500
+ G1.add_edge(2, 5)
501
+ G2.remove_edge("i", "l")
502
+ G2.add_edge("g", "l")
503
+ G2.add_edge("m", "f")
504
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
505
+
506
+ # Change the structure of the disconnected sub-graph, keeping it isomorphic
507
+ G1.remove_node(13)
508
+ G2.remove_node("d")
509
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
510
+
511
+ # Connect the newly added node to the disconnected graph, which now is just a path of size 3
512
+ G1.add_edge(0, 10)
513
+ G2.add_edge("e", "z")
514
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
515
+
516
+ # Connect the two disconnected sub-graphs, forming a single graph
517
+ G1.add_edge(11, 3)
518
+ G1.add_edge(0, 8)
519
+ G2.add_edge("a", "l")
520
+ G2.add_edge("z", "j")
521
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
522
+
523
+ def test_custom_graph5_same_labels(self):
524
+ # Lots of symmetries
525
+ # 1
526
+ # /|\
527
+ # / 2 \
528
+ # | / \ |
529
+ # 5-6 3-4
530
+ # | \ / |
531
+ # \ 7 /
532
+ # \|/
533
+ # 8
534
+ G1 = nx.Graph()
535
+ edges1 = [
536
+ (1, 5),
537
+ (1, 2),
538
+ (1, 4),
539
+ (2, 3),
540
+ (2, 6),
541
+ (3, 4),
542
+ (3, 7),
543
+ (4, 8),
544
+ (5, 8),
545
+ (5, 6),
546
+ (6, 7),
547
+ (7, 8),
548
+ ]
549
+ mapped = {1: "a", 2: "h", 3: "d", 4: "i", 5: "g", 6: "b", 7: "j", 8: "c"}
550
+
551
+ G1.add_edges_from(edges1)
552
+ G2 = nx.relabel_nodes(G1, mapped)
553
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
554
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
555
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
556
+
557
+ all_self_mappings = list(vf2pp_all_isomorphisms(G1, G1))
558
+ assert len(all_self_mappings) == 48
559
+ # check that one of the mappings is not the identity for node 2
560
+ assert all(sum(1 for m in all_self_mappings if m[u] == u) == 6 for u in G1)
561
+
562
+ # Add different edges in each graph, maintaining symmetry
563
+ G1.add_edges_from([(3, 6), (2, 7), (2, 5), (1, 3), (4, 7), (6, 8)])
564
+ G2.add_edges_from(
565
+ [
566
+ (mapped[6], mapped[3]),
567
+ (mapped[2], mapped[7]),
568
+ (mapped[1], mapped[6]),
569
+ (mapped[5], mapped[7]),
570
+ (mapped[3], mapped[8]),
571
+ (mapped[2], mapped[4]),
572
+ ]
573
+ )
574
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
575
+
576
+ # Obtain two different but isomorphic subgraphs from G1 and G2
577
+ H1 = nx.Graph(G1.subgraph([1, 5, 8, 6, 7, 3]))
578
+ H2 = nx.Graph(
579
+ G2.subgraph(
580
+ [mapped[1], mapped[4], mapped[8], mapped[7], mapped[3], mapped[5]]
581
+ )
582
+ )
583
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
584
+
585
+ # Delete corresponding node from the two graphs
586
+ H1.remove_node(8)
587
+ H2.remove_node(mapped[7])
588
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
589
+
590
+ # Re-orient, maintaining isomorphism
591
+ H1.add_edge(1, 6)
592
+ H1.remove_edge(3, 6)
593
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
594
+
595
+ def test_custom_graph5_different_labels(self):
596
+ G1 = nx.Graph()
597
+ edges1 = [
598
+ (1, 5),
599
+ (1, 2),
600
+ (1, 4),
601
+ (2, 3),
602
+ (2, 6),
603
+ (3, 4),
604
+ (3, 7),
605
+ (4, 8),
606
+ (5, 8),
607
+ (5, 6),
608
+ (6, 7),
609
+ (7, 8),
610
+ ]
611
+ mapped = {1: "a", 2: "h", 3: "d", 4: "i", 5: "g", 6: "b", 7: "j", 8: "c"}
612
+
613
+ G1.add_edges_from(edges1)
614
+ G2 = nx.relabel_nodes(G1, mapped)
615
+
616
+ colors = ["red", "blue", "grey", "none", "brown", "solarized", "yellow", "pink"]
617
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
618
+ nx.set_node_attributes(
619
+ G2,
620
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
621
+ "label",
622
+ )
623
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
624
+
625
+ # Assign different colors to matching nodes
626
+ c = 0
627
+ for node in G1.nodes():
628
+ color1 = colors[c]
629
+ color2 = colors[(c + 3) % len(colors)]
630
+ G1.nodes[node]["label"] = color1
631
+ G2.nodes[mapped[node]]["label"] = color2
632
+ c += 1
633
+
634
+ assert vf2pp_isomorphism(G1, G2, node_label="label") is None
635
+
636
+ # Get symmetrical sub-graphs of G1,G2 and compare them
637
+ H1 = G1.subgraph([1, 5])
638
+ H2 = G2.subgraph(["i", "c"])
639
+ c = 0
640
+ for node1, node2 in zip(H1.nodes(), H2.nodes()):
641
+ H1.nodes[node1]["label"] = "red"
642
+ H2.nodes[node2]["label"] = "red"
643
+ c += 1
644
+
645
+ assert vf2pp_isomorphism(H1, H2, node_label="label")
646
+
647
+ def test_disconnected_graph_all_same_labels(self):
648
+ G1 = nx.Graph()
649
+ G1.add_nodes_from(list(range(10)))
650
+
651
+ mapped = {0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0}
652
+ G2 = nx.relabel_nodes(G1, mapped)
653
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
654
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
655
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
656
+
657
+ def test_disconnected_graph_all_different_labels(self):
658
+ G1 = nx.Graph()
659
+ G1.add_nodes_from(list(range(10)))
660
+
661
+ mapped = {0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0}
662
+ G2 = nx.relabel_nodes(G1, mapped)
663
+
664
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
665
+ nx.set_node_attributes(
666
+ G2,
667
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
668
+ "label",
669
+ )
670
+ assert vf2pp_isomorphism(G1, G2, node_label="label") == mapped
671
+
672
+ def test_disconnected_graph_some_same_labels(self):
673
+ G1 = nx.Graph()
674
+ G1.add_nodes_from(list(range(10)))
675
+
676
+ mapped = {0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0}
677
+ G2 = nx.relabel_nodes(G1, mapped)
678
+
679
+ colors = [
680
+ "white",
681
+ "white",
682
+ "white",
683
+ "purple",
684
+ "purple",
685
+ "red",
686
+ "red",
687
+ "pink",
688
+ "pink",
689
+ "pink",
690
+ ]
691
+
692
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(colors))), "label")
693
+ nx.set_node_attributes(
694
+ G2, dict(zip([mapped[n] for n in G1], it.cycle(colors))), "label"
695
+ )
696
+
697
+ assert vf2pp_isomorphism(G1, G2, node_label="label")
698
+
699
+
700
+ class TestMultiGraphISOVF2pp:
701
+ def test_custom_multigraph1_same_labels(self):
702
+ G1 = nx.MultiGraph()
703
+
704
+ mapped = {1: "A", 2: "B", 3: "C", 4: "D", 5: "Z", 6: "E"}
705
+ edges1 = [
706
+ (1, 2),
707
+ (1, 3),
708
+ (1, 4),
709
+ (1, 4),
710
+ (1, 4),
711
+ (2, 3),
712
+ (2, 6),
713
+ (2, 6),
714
+ (3, 4),
715
+ (3, 4),
716
+ (5, 1),
717
+ (5, 1),
718
+ (5, 2),
719
+ (5, 2),
720
+ ]
721
+
722
+ G1.add_edges_from(edges1)
723
+ G2 = nx.relabel_nodes(G1, mapped)
724
+
725
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
726
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
727
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
728
+ assert m
729
+
730
+ # Transfer the 2-clique to the right side of G1
731
+ G1.remove_edges_from([(2, 6), (2, 6)])
732
+ G1.add_edges_from([(3, 6), (3, 6)])
733
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
734
+ assert not m
735
+
736
+ # Delete an edges, making them symmetrical, so the position of the 2-clique doesn't matter
737
+ G2.remove_edge(mapped[1], mapped[4])
738
+ G1.remove_edge(1, 4)
739
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
740
+ assert m
741
+
742
+ # Add self-loops
743
+ G1.add_edges_from([(5, 5), (5, 5), (1, 1)])
744
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
745
+ assert not m
746
+
747
+ # Compensate in G2
748
+ G2.add_edges_from(
749
+ [(mapped[1], mapped[1]), (mapped[4], mapped[4]), (mapped[4], mapped[4])]
750
+ )
751
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
752
+ assert m
753
+
754
+ def test_custom_multigraph1_different_labels(self):
755
+ G1 = nx.MultiGraph()
756
+
757
+ mapped = {1: "A", 2: "B", 3: "C", 4: "D", 5: "Z", 6: "E"}
758
+ edges1 = [
759
+ (1, 2),
760
+ (1, 3),
761
+ (1, 4),
762
+ (1, 4),
763
+ (1, 4),
764
+ (2, 3),
765
+ (2, 6),
766
+ (2, 6),
767
+ (3, 4),
768
+ (3, 4),
769
+ (5, 1),
770
+ (5, 1),
771
+ (5, 2),
772
+ (5, 2),
773
+ ]
774
+
775
+ G1.add_edges_from(edges1)
776
+ G2 = nx.relabel_nodes(G1, mapped)
777
+
778
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
779
+ nx.set_node_attributes(
780
+ G2,
781
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
782
+ "label",
783
+ )
784
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
785
+ assert m
786
+ assert m == mapped
787
+
788
+ # Re-structure G1, maintaining the degree sequence
789
+ G1.remove_edge(1, 4)
790
+ G1.add_edge(1, 5)
791
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
792
+ assert not m
793
+
794
+ # Restructure G2, making it isomorphic to G1
795
+ G2.remove_edge("A", "D")
796
+ G2.add_edge("A", "Z")
797
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
798
+ assert m
799
+ assert m == mapped
800
+
801
+ # Add edge from node to itself
802
+ G1.add_edges_from([(6, 6), (6, 6), (6, 6)])
803
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
804
+ assert not m
805
+
806
+ # Same for G2
807
+ G2.add_edges_from([("E", "E"), ("E", "E"), ("E", "E")])
808
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
809
+ assert m
810
+ assert m == mapped
811
+
812
+ def test_custom_multigraph2_same_labels(self):
813
+ G1 = nx.MultiGraph()
814
+
815
+ mapped = {1: "A", 2: "C", 3: "D", 4: "E", 5: "G", 7: "B", 6: "F"}
816
+ edges1 = [
817
+ (1, 2),
818
+ (1, 2),
819
+ (1, 5),
820
+ (1, 5),
821
+ (1, 5),
822
+ (5, 6),
823
+ (2, 3),
824
+ (2, 3),
825
+ (2, 4),
826
+ (3, 4),
827
+ (3, 4),
828
+ (4, 5),
829
+ (4, 5),
830
+ (4, 5),
831
+ (2, 7),
832
+ (2, 7),
833
+ (2, 7),
834
+ ]
835
+
836
+ G1.add_edges_from(edges1)
837
+ G2 = nx.relabel_nodes(G1, mapped)
838
+
839
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
840
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
841
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
842
+ assert m
843
+
844
+ # Obtain two non-isomorphic subgraphs from the graph
845
+ G2.add_edge(mapped[1], mapped[4])
846
+ H1 = nx.MultiGraph(G1.subgraph([2, 3, 4, 7]))
847
+ H2 = nx.MultiGraph(G2.subgraph([mapped[1], mapped[4], mapped[5], mapped[6]]))
848
+
849
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
850
+ assert not m
851
+
852
+ # Make them isomorphic
853
+ H1.remove_edge(3, 4)
854
+ H1.add_edges_from([(2, 3), (2, 4), (2, 4)])
855
+ H2.add_edges_from([(mapped[5], mapped[6]), (mapped[5], mapped[6])])
856
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
857
+ assert m
858
+
859
+ # Remove triangle edge
860
+ H1.remove_edges_from([(2, 3), (2, 3), (2, 3)])
861
+ H2.remove_edges_from([(mapped[5], mapped[4])] * 3)
862
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
863
+ assert m
864
+
865
+ # Change the edge orientation such that H1 is rotated H2
866
+ H1.remove_edges_from([(2, 7), (2, 7)])
867
+ H1.add_edges_from([(3, 4), (3, 4)])
868
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
869
+ assert m
870
+
871
+ # Add extra edges maintaining degree sequence, but in a non-symmetrical manner
872
+ H2.add_edge(mapped[5], mapped[1])
873
+ H1.add_edge(3, 4)
874
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
875
+ assert not m
876
+
877
+ def test_custom_multigraph2_different_labels(self):
878
+ G1 = nx.MultiGraph()
879
+
880
+ mapped = {1: "A", 2: "C", 3: "D", 4: "E", 5: "G", 7: "B", 6: "F"}
881
+ edges1 = [
882
+ (1, 2),
883
+ (1, 2),
884
+ (1, 5),
885
+ (1, 5),
886
+ (1, 5),
887
+ (5, 6),
888
+ (2, 3),
889
+ (2, 3),
890
+ (2, 4),
891
+ (3, 4),
892
+ (3, 4),
893
+ (4, 5),
894
+ (4, 5),
895
+ (4, 5),
896
+ (2, 7),
897
+ (2, 7),
898
+ (2, 7),
899
+ ]
900
+
901
+ G1.add_edges_from(edges1)
902
+ G2 = nx.relabel_nodes(G1, mapped)
903
+
904
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
905
+ nx.set_node_attributes(
906
+ G2,
907
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
908
+ "label",
909
+ )
910
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
911
+ assert m
912
+ assert m == mapped
913
+
914
+ # Re-structure G1
915
+ G1.remove_edge(2, 7)
916
+ G1.add_edge(5, 6)
917
+
918
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
919
+ assert not m
920
+
921
+ # Same for G2
922
+ G2.remove_edge("B", "C")
923
+ G2.add_edge("G", "F")
924
+
925
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
926
+ assert m
927
+ assert m == mapped
928
+
929
+ # Delete node from G1 and G2, keeping them isomorphic
930
+ G1.remove_node(3)
931
+ G2.remove_node("D")
932
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
933
+ assert m
934
+
935
+ # Change G1 edges
936
+ G1.remove_edge(1, 2)
937
+ G1.remove_edge(2, 7)
938
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
939
+ assert not m
940
+
941
+ # Make G2 identical to G1, but with different edge orientation and different labels
942
+ G2.add_edges_from([("A", "C"), ("C", "E"), ("C", "E")])
943
+ G2.remove_edges_from(
944
+ [("A", "G"), ("A", "G"), ("F", "G"), ("E", "G"), ("E", "G")]
945
+ )
946
+
947
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
948
+ assert not m
949
+
950
+ # Make all labels the same, so G1 and G2 are also isomorphic
951
+ for n1, n2 in zip(G1.nodes(), G2.nodes()):
952
+ G1.nodes[n1]["label"] = "blue"
953
+ G2.nodes[n2]["label"] = "blue"
954
+
955
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
956
+ assert m
957
+
958
+ def test_custom_multigraph3_same_labels(self):
959
+ G1 = nx.MultiGraph()
960
+
961
+ mapped = {1: 9, 2: 8, 3: 7, 4: 6, 5: 3, 8: 5, 9: 4, 7: 1, 6: 2}
962
+ edges1 = [
963
+ (1, 2),
964
+ (1, 3),
965
+ (1, 3),
966
+ (2, 3),
967
+ (2, 3),
968
+ (3, 4),
969
+ (4, 5),
970
+ (4, 7),
971
+ (4, 9),
972
+ (4, 9),
973
+ (4, 9),
974
+ (5, 8),
975
+ (5, 8),
976
+ (8, 9),
977
+ (8, 9),
978
+ (5, 6),
979
+ (6, 7),
980
+ (6, 7),
981
+ (6, 7),
982
+ (5, 2),
983
+ ]
984
+ G1.add_edges_from(edges1)
985
+ G2 = nx.relabel_nodes(G1, mapped)
986
+
987
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
988
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
989
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
990
+ assert m
991
+
992
+ # Connect nodes maintaining symmetry
993
+ G1.add_edges_from([(6, 9), (7, 8), (5, 8), (4, 9), (4, 9)])
994
+ G2.add_edges_from(
995
+ [
996
+ (mapped[6], mapped[8]),
997
+ (mapped[7], mapped[9]),
998
+ (mapped[5], mapped[8]),
999
+ (mapped[4], mapped[9]),
1000
+ (mapped[4], mapped[9]),
1001
+ ]
1002
+ )
1003
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1004
+ assert not m
1005
+
1006
+ # Make isomorphic
1007
+ G1.add_edges_from([(6, 8), (6, 8), (7, 9), (7, 9), (7, 9)])
1008
+ G2.add_edges_from(
1009
+ [
1010
+ (mapped[6], mapped[8]),
1011
+ (mapped[6], mapped[9]),
1012
+ (mapped[7], mapped[8]),
1013
+ (mapped[7], mapped[9]),
1014
+ (mapped[7], mapped[9]),
1015
+ ]
1016
+ )
1017
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1018
+ assert m
1019
+
1020
+ # Connect more nodes
1021
+ G1.add_edges_from([(2, 7), (2, 7), (3, 6), (3, 6)])
1022
+ G2.add_edges_from(
1023
+ [
1024
+ (mapped[2], mapped[7]),
1025
+ (mapped[2], mapped[7]),
1026
+ (mapped[3], mapped[6]),
1027
+ (mapped[3], mapped[6]),
1028
+ ]
1029
+ )
1030
+ G1.add_node(10)
1031
+ G2.add_node("Z")
1032
+ G1.nodes[10]["label"] = "blue"
1033
+ G2.nodes["Z"]["label"] = "blue"
1034
+
1035
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1036
+ assert m
1037
+
1038
+ # Connect the newly added node, to opposite sides of the graph
1039
+ G1.add_edges_from([(10, 1), (10, 5), (10, 8), (10, 10), (10, 10)])
1040
+ G2.add_edges_from(
1041
+ [
1042
+ ("Z", mapped[1]),
1043
+ ("Z", mapped[4]),
1044
+ ("Z", mapped[9]),
1045
+ ("Z", "Z"),
1046
+ ("Z", "Z"),
1047
+ ]
1048
+ )
1049
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1050
+ assert not m
1051
+
1052
+ # We connected the new node to opposite sides, so G1 must be symmetrical
1053
+ # to G2. Re-structure them to be so
1054
+ G1.remove_edges_from([(1, 3), (4, 9), (4, 9), (7, 9)])
1055
+ G2.remove_edges_from(
1056
+ [
1057
+ (mapped[1], mapped[3]),
1058
+ (mapped[4], mapped[9]),
1059
+ (mapped[4], mapped[9]),
1060
+ (mapped[7], mapped[9]),
1061
+ ]
1062
+ )
1063
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1064
+ assert m
1065
+
1066
+ # Get two subgraphs that are not isomorphic but are easy to make
1067
+ H1 = nx.Graph(G1.subgraph([2, 3, 4, 5, 6, 7, 10]))
1068
+ H2 = nx.Graph(
1069
+ G2.subgraph(
1070
+ [mapped[4], mapped[5], mapped[6], mapped[7], mapped[8], mapped[9], "Z"]
1071
+ )
1072
+ )
1073
+
1074
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1075
+ assert not m
1076
+
1077
+ # Restructure both to make them isomorphic
1078
+ H1.add_edges_from([(10, 2), (10, 6), (3, 6), (2, 7), (2, 6), (3, 7)])
1079
+ H2.add_edges_from(
1080
+ [("Z", mapped[7]), (mapped[6], mapped[9]), (mapped[7], mapped[8])]
1081
+ )
1082
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1083
+ assert m
1084
+
1085
+ # Remove one self-loop in H2
1086
+ H2.remove_edge("Z", "Z")
1087
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1088
+ assert not m
1089
+
1090
+ # Compensate in H1
1091
+ H1.remove_edge(10, 10)
1092
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1093
+ assert m
1094
+
1095
+ def test_custom_multigraph3_different_labels(self):
1096
+ G1 = nx.MultiGraph()
1097
+
1098
+ mapped = {1: 9, 2: 8, 3: 7, 4: 6, 5: 3, 8: 5, 9: 4, 7: 1, 6: 2}
1099
+ edges1 = [
1100
+ (1, 2),
1101
+ (1, 3),
1102
+ (1, 3),
1103
+ (2, 3),
1104
+ (2, 3),
1105
+ (3, 4),
1106
+ (4, 5),
1107
+ (4, 7),
1108
+ (4, 9),
1109
+ (4, 9),
1110
+ (4, 9),
1111
+ (5, 8),
1112
+ (5, 8),
1113
+ (8, 9),
1114
+ (8, 9),
1115
+ (5, 6),
1116
+ (6, 7),
1117
+ (6, 7),
1118
+ (6, 7),
1119
+ (5, 2),
1120
+ ]
1121
+
1122
+ G1.add_edges_from(edges1)
1123
+ G2 = nx.relabel_nodes(G1, mapped)
1124
+
1125
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
1126
+ nx.set_node_attributes(
1127
+ G2,
1128
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
1129
+ "label",
1130
+ )
1131
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1132
+ assert m
1133
+ assert m == mapped
1134
+
1135
+ # Delete edge maintaining isomorphism
1136
+ G1.remove_edge(4, 9)
1137
+ G2.remove_edge(4, 6)
1138
+
1139
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1140
+ assert m
1141
+ assert m == mapped
1142
+
1143
+ # Change edge orientation such that G1 mirrors G2
1144
+ G1.add_edges_from([(4, 9), (1, 2), (1, 2)])
1145
+ G1.remove_edges_from([(1, 3), (1, 3)])
1146
+ G2.add_edges_from([(3, 5), (7, 9)])
1147
+ G2.remove_edge(8, 9)
1148
+
1149
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1150
+ assert not m
1151
+
1152
+ # Make all labels the same, so G1 and G2 are also isomorphic
1153
+ for n1, n2 in zip(G1.nodes(), G2.nodes()):
1154
+ G1.nodes[n1]["label"] = "blue"
1155
+ G2.nodes[n2]["label"] = "blue"
1156
+
1157
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1158
+ assert m
1159
+
1160
+ G1.add_node(10)
1161
+ G2.add_node("Z")
1162
+ G1.nodes[10]["label"] = "green"
1163
+ G2.nodes["Z"]["label"] = "green"
1164
+
1165
+ # Add different number of edges between the new nodes and themselves
1166
+ G1.add_edges_from([(10, 10), (10, 10)])
1167
+ G2.add_edges_from([("Z", "Z")])
1168
+
1169
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1170
+ assert not m
1171
+
1172
+ # Make the number of self-edges equal
1173
+ G1.remove_edge(10, 10)
1174
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1175
+ assert m
1176
+
1177
+ # Connect the new node to the graph
1178
+ G1.add_edges_from([(10, 3), (10, 4)])
1179
+ G2.add_edges_from([("Z", 8), ("Z", 3)])
1180
+
1181
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1182
+ assert m
1183
+
1184
+ # Remove central node
1185
+ G1.remove_node(4)
1186
+ G2.remove_node(3)
1187
+ G1.add_edges_from([(5, 6), (5, 6), (5, 7)])
1188
+ G2.add_edges_from([(1, 6), (1, 6), (6, 2)])
1189
+
1190
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1191
+ assert m
1192
+
1193
+ def test_custom_multigraph4_same_labels(self):
1194
+ G1 = nx.MultiGraph()
1195
+ edges1 = [
1196
+ (1, 2),
1197
+ (1, 2),
1198
+ (2, 2),
1199
+ (2, 3),
1200
+ (3, 8),
1201
+ (3, 8),
1202
+ (3, 4),
1203
+ (4, 5),
1204
+ (4, 5),
1205
+ (4, 5),
1206
+ (4, 6),
1207
+ (3, 6),
1208
+ (3, 6),
1209
+ (6, 6),
1210
+ (8, 7),
1211
+ (7, 7),
1212
+ (8, 9),
1213
+ (9, 9),
1214
+ (8, 9),
1215
+ (8, 9),
1216
+ (5, 9),
1217
+ (10, 11),
1218
+ (11, 12),
1219
+ (12, 13),
1220
+ (11, 13),
1221
+ (10, 10),
1222
+ (10, 11),
1223
+ (11, 13),
1224
+ ]
1225
+
1226
+ mapped = {
1227
+ 1: "n",
1228
+ 2: "m",
1229
+ 3: "l",
1230
+ 4: "j",
1231
+ 5: "k",
1232
+ 6: "i",
1233
+ 7: "g",
1234
+ 8: "h",
1235
+ 9: "f",
1236
+ 10: "b",
1237
+ 11: "a",
1238
+ 12: "d",
1239
+ 13: "e",
1240
+ }
1241
+
1242
+ G1.add_edges_from(edges1)
1243
+ G2 = nx.relabel_nodes(G1, mapped)
1244
+
1245
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
1246
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
1247
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1248
+ assert m
1249
+
1250
+ # Add extra but corresponding edges to both graphs
1251
+ G1.add_edges_from([(2, 2), (2, 3), (2, 8), (3, 4)])
1252
+ G2.add_edges_from([("m", "m"), ("m", "l"), ("m", "h"), ("l", "j")])
1253
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1254
+ assert m
1255
+
1256
+ # Obtain subgraphs
1257
+ H1 = nx.MultiGraph(G1.subgraph([2, 3, 4, 6, 10, 11, 12, 13]))
1258
+ H2 = nx.MultiGraph(
1259
+ G2.subgraph(
1260
+ [
1261
+ mapped[2],
1262
+ mapped[3],
1263
+ mapped[8],
1264
+ mapped[9],
1265
+ mapped[10],
1266
+ mapped[11],
1267
+ mapped[12],
1268
+ mapped[13],
1269
+ ]
1270
+ )
1271
+ )
1272
+
1273
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1274
+ assert not m
1275
+
1276
+ # Make them isomorphic
1277
+ H2.remove_edges_from(
1278
+ [(mapped[3], mapped[2]), (mapped[9], mapped[8]), (mapped[2], mapped[2])]
1279
+ )
1280
+ H2.add_edges_from([(mapped[9], mapped[9]), (mapped[2], mapped[8])])
1281
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1282
+ assert m
1283
+
1284
+ # Re-structure the disconnected sub-graph
1285
+ H1.remove_node(12)
1286
+ H2.remove_node(mapped[12])
1287
+ H1.add_edge(13, 13)
1288
+ H2.add_edge(mapped[13], mapped[13])
1289
+
1290
+ # Connect the two disconnected components, forming a single graph
1291
+ H1.add_edges_from([(3, 13), (6, 11)])
1292
+ H2.add_edges_from([(mapped[8], mapped[10]), (mapped[2], mapped[11])])
1293
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1294
+ assert m
1295
+
1296
+ # Change orientation of self-loops in one graph, maintaining the degree sequence
1297
+ H1.remove_edges_from([(2, 2), (3, 6)])
1298
+ H1.add_edges_from([(6, 6), (2, 3)])
1299
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1300
+ assert not m
1301
+
1302
+ def test_custom_multigraph4_different_labels(self):
1303
+ G1 = nx.MultiGraph()
1304
+ edges1 = [
1305
+ (1, 2),
1306
+ (1, 2),
1307
+ (2, 2),
1308
+ (2, 3),
1309
+ (3, 8),
1310
+ (3, 8),
1311
+ (3, 4),
1312
+ (4, 5),
1313
+ (4, 5),
1314
+ (4, 5),
1315
+ (4, 6),
1316
+ (3, 6),
1317
+ (3, 6),
1318
+ (6, 6),
1319
+ (8, 7),
1320
+ (7, 7),
1321
+ (8, 9),
1322
+ (9, 9),
1323
+ (8, 9),
1324
+ (8, 9),
1325
+ (5, 9),
1326
+ (10, 11),
1327
+ (11, 12),
1328
+ (12, 13),
1329
+ (11, 13),
1330
+ ]
1331
+
1332
+ mapped = {
1333
+ 1: "n",
1334
+ 2: "m",
1335
+ 3: "l",
1336
+ 4: "j",
1337
+ 5: "k",
1338
+ 6: "i",
1339
+ 7: "g",
1340
+ 8: "h",
1341
+ 9: "f",
1342
+ 10: "b",
1343
+ 11: "a",
1344
+ 12: "d",
1345
+ 13: "e",
1346
+ }
1347
+
1348
+ G1.add_edges_from(edges1)
1349
+ G2 = nx.relabel_nodes(G1, mapped)
1350
+
1351
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
1352
+ nx.set_node_attributes(
1353
+ G2,
1354
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
1355
+ "label",
1356
+ )
1357
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1358
+ assert m == mapped
1359
+
1360
+ # Add extra but corresponding edges to both graphs
1361
+ G1.add_edges_from([(2, 2), (2, 3), (2, 8), (3, 4)])
1362
+ G2.add_edges_from([("m", "m"), ("m", "l"), ("m", "h"), ("l", "j")])
1363
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1364
+ assert m == mapped
1365
+
1366
+ # Obtain isomorphic subgraphs
1367
+ H1 = nx.MultiGraph(G1.subgraph([2, 3, 4, 6]))
1368
+ H2 = nx.MultiGraph(G2.subgraph(["m", "l", "j", "i"]))
1369
+
1370
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1371
+ assert m
1372
+
1373
+ # Delete the 3-clique, keeping only the path-graph. Also, H1 mirrors H2
1374
+ H1.remove_node(4)
1375
+ H2.remove_node("j")
1376
+ H1.remove_edges_from([(2, 2), (2, 3), (6, 6)])
1377
+ H2.remove_edges_from([("l", "i"), ("m", "m"), ("m", "m")])
1378
+
1379
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1380
+ assert not m
1381
+
1382
+ # Assign the same labels so that mirroring means isomorphic
1383
+ for n1, n2 in zip(H1.nodes(), H2.nodes()):
1384
+ H1.nodes[n1]["label"] = "red"
1385
+ H2.nodes[n2]["label"] = "red"
1386
+
1387
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1388
+ assert m
1389
+
1390
+ # Leave only one node with self-loop
1391
+ H1.remove_nodes_from([3, 6])
1392
+ H2.remove_nodes_from(["m", "l"])
1393
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1394
+ assert m
1395
+
1396
+ # Remove one self-loop from H1
1397
+ H1.remove_edge(2, 2)
1398
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1399
+ assert not m
1400
+
1401
+ # Same for H2
1402
+ H2.remove_edge("i", "i")
1403
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1404
+ assert m
1405
+
1406
+ # Compose H1 with the disconnected sub-graph of G1. Same for H2
1407
+ S1 = nx.compose(H1, nx.MultiGraph(G1.subgraph([10, 11, 12, 13])))
1408
+ S2 = nx.compose(H2, nx.MultiGraph(G2.subgraph(["a", "b", "d", "e"])))
1409
+
1410
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1411
+ assert m
1412
+
1413
+ # Connect the two components
1414
+ S1.add_edges_from([(13, 13), (13, 13), (2, 13)])
1415
+ S2.add_edges_from([("a", "a"), ("a", "a"), ("i", "e")])
1416
+ m = vf2pp_isomorphism(H1, H2, node_label="label")
1417
+ assert m
1418
+
1419
+ def test_custom_multigraph5_same_labels(self):
1420
+ # Lots of symmetries
1421
+ # 1
1422
+ # /|\
1423
+ # / 2 \
1424
+ # | / \ |
1425
+ # 5-6 3-4
1426
+ # | \ / |
1427
+ # \ 7 /
1428
+ # \|/
1429
+ # 8
1430
+ G1 = nx.MultiGraph()
1431
+ edges1 = [
1432
+ (1, 5),
1433
+ (1, 2),
1434
+ (1, 4),
1435
+ (2, 3),
1436
+ (2, 6),
1437
+ (3, 4),
1438
+ (3, 7),
1439
+ (4, 8),
1440
+ (5, 8),
1441
+ (5, 6),
1442
+ (6, 7),
1443
+ (7, 8),
1444
+ ]
1445
+ mapped = {1: "a", 2: "h", 3: "d", 4: "i", 5: "g", 6: "b", 7: "j", 8: "c"}
1446
+
1447
+ G1.add_edges_from(edges1)
1448
+ G2 = nx.relabel_nodes(G1, mapped)
1449
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
1450
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
1451
+
1452
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1453
+ assert m
1454
+
1455
+ # Add multiple edges and self-loops, maintaining isomorphism
1456
+ G1.add_edges_from(
1457
+ [(1, 2), (1, 2), (3, 7), (8, 8), (8, 8), (7, 8), (2, 3), (5, 6)]
1458
+ )
1459
+ G2.add_edges_from(
1460
+ [
1461
+ ("a", "h"),
1462
+ ("a", "h"),
1463
+ ("d", "j"),
1464
+ ("c", "c"),
1465
+ ("c", "c"),
1466
+ ("j", "c"),
1467
+ ("d", "h"),
1468
+ ("g", "b"),
1469
+ ]
1470
+ )
1471
+
1472
+ all_self_mappings = list(vf2pp_all_isomorphisms(G1, G1))
1473
+ assert len(all_self_mappings) == 1
1474
+
1475
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1476
+ assert m
1477
+
1478
+ # Make G2 to be the rotated G1
1479
+ G2.remove_edges_from(
1480
+ [
1481
+ ("a", "h"),
1482
+ ("a", "h"),
1483
+ ("d", "j"),
1484
+ ("c", "c"),
1485
+ ("c", "c"),
1486
+ ("j", "c"),
1487
+ ("d", "h"),
1488
+ ("g", "b"),
1489
+ ]
1490
+ )
1491
+ G2.add_edges_from(
1492
+ [
1493
+ ("d", "i"),
1494
+ ("a", "h"),
1495
+ ("g", "b"),
1496
+ ("g", "b"),
1497
+ ("i", "i"),
1498
+ ("i", "i"),
1499
+ ("b", "j"),
1500
+ ("d", "j"),
1501
+ ]
1502
+ )
1503
+
1504
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1505
+ assert m
1506
+
1507
+ def test_disconnected_multigraph_all_same_labels(self):
1508
+ G1 = nx.MultiGraph()
1509
+ G1.add_nodes_from(list(range(10)))
1510
+ G1.add_edges_from([(i, i) for i in range(10)])
1511
+
1512
+ mapped = {0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0}
1513
+ G2 = nx.relabel_nodes(G1, mapped)
1514
+
1515
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_same))), "label")
1516
+ nx.set_node_attributes(G2, dict(zip(G2, it.cycle(labels_same))), "label")
1517
+
1518
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1519
+ assert m
1520
+
1521
+ # Add self-loops to non-mapped nodes. Should be the same, as the graph is disconnected.
1522
+ G1.add_edges_from([(i, i) for i in range(5, 8)] * 3)
1523
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1524
+ assert not m
1525
+
1526
+ # Compensate in G2
1527
+ G2.add_edges_from([(i, i) for i in range(3)] * 3)
1528
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1529
+ assert m
1530
+
1531
+ # Add one more self-loop in G2
1532
+ G2.add_edges_from([(0, 0), (1, 1), (1, 1)])
1533
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1534
+ assert not m
1535
+
1536
+ # Compensate in G1
1537
+ G1.add_edges_from([(5, 5), (7, 7), (7, 7)])
1538
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1539
+ assert m
1540
+
1541
+ def test_disconnected_multigraph_all_different_labels(self):
1542
+ G1 = nx.MultiGraph()
1543
+ G1.add_nodes_from(list(range(10)))
1544
+ G1.add_edges_from([(i, i) for i in range(10)])
1545
+
1546
+ mapped = {0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0}
1547
+ G2 = nx.relabel_nodes(G1, mapped)
1548
+
1549
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
1550
+ nx.set_node_attributes(
1551
+ G2,
1552
+ dict(zip([mapped[n] for n in G1], it.cycle(labels_many))),
1553
+ "label",
1554
+ )
1555
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1556
+ assert m
1557
+ assert m == mapped
1558
+
1559
+ # Add self-loops to non-mapped nodes. Now it is not the same, as there are different labels
1560
+ G1.add_edges_from([(i, i) for i in range(5, 8)] * 3)
1561
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1562
+ assert not m
1563
+
1564
+ # Add self-loops to non mapped nodes in G2 as well
1565
+ G2.add_edges_from([(mapped[i], mapped[i]) for i in range(3)] * 7)
1566
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1567
+ assert not m
1568
+
1569
+ # Add self-loops to mapped nodes in G2
1570
+ G2.add_edges_from([(mapped[i], mapped[i]) for i in range(5, 8)] * 3)
1571
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1572
+ assert not m
1573
+
1574
+ # Add self-loops to G1 so that they are even in both graphs
1575
+ G1.add_edges_from([(i, i) for i in range(3)] * 7)
1576
+ m = vf2pp_isomorphism(G1, G2, node_label="label")
1577
+ assert m
1578
+
1579
+
1580
+ class TestDiGraphISOVF2pp:
1581
+ def test_wikipedia_graph(self):
1582
+ edges1 = [
1583
+ (1, 5),
1584
+ (1, 2),
1585
+ (1, 4),
1586
+ (3, 2),
1587
+ (6, 2),
1588
+ (3, 4),
1589
+ (7, 3),
1590
+ (4, 8),
1591
+ (5, 8),
1592
+ (6, 5),
1593
+ (6, 7),
1594
+ (7, 8),
1595
+ ]
1596
+ mapped = {1: "a", 2: "h", 3: "d", 4: "i", 5: "g", 6: "b", 7: "j", 8: "c"}
1597
+
1598
+ G1 = nx.DiGraph(edges1)
1599
+ G2 = nx.relabel_nodes(G1, mapped)
1600
+
1601
+ assert vf2pp_isomorphism(G1, G2) == mapped
1602
+
1603
+ # Change the direction of an edge
1604
+ G1.remove_edge(1, 5)
1605
+ G1.add_edge(5, 1)
1606
+ assert vf2pp_isomorphism(G1, G2) is None
1607
+
1608
+ def test_non_isomorphic_same_degree_sequence(self):
1609
+ r"""
1610
+ G1 G2
1611
+ 1-------------2 1-------------2
1612
+ | \ | | \ |
1613
+ | 5-------6 | | 5-------6 |
1614
+ | | | | | | | |
1615
+ | 8-------7 | | 8-------7 |
1616
+ | / | | \ |
1617
+ 4-------------3 4-------------3
1618
+ """
1619
+ edges1 = [
1620
+ (1, 5),
1621
+ (1, 2),
1622
+ (4, 1),
1623
+ (3, 2),
1624
+ (3, 4),
1625
+ (5, 8),
1626
+ (6, 5),
1627
+ (6, 7),
1628
+ (7, 8),
1629
+ (4, 8),
1630
+ ]
1631
+ edges2 = [
1632
+ (1, 5),
1633
+ (1, 2),
1634
+ (4, 1),
1635
+ (3, 2),
1636
+ (3, 4),
1637
+ (5, 8),
1638
+ (6, 5),
1639
+ (6, 7),
1640
+ (7, 8),
1641
+ (3, 7),
1642
+ ]
1643
+
1644
+ G1 = nx.DiGraph(edges1)
1645
+ G2 = nx.DiGraph(edges2)
1646
+ assert vf2pp_isomorphism(G1, G2) is None
1647
+
1648
+
1649
+ def test_isomorphvf2pp_multidigraphs():
1650
+ g = nx.MultiDiGraph({0: [1, 1, 2, 2, 3], 1: [2, 3, 3], 2: [3]})
1651
+ h = nx.MultiDiGraph({0: [1, 1, 2, 2, 3], 1: [2, 3, 3], 2: [3]})
1652
+ assert nx.vf2pp_is_isomorphic(g, h)
1653
+
1654
+ h = nx.MultiDiGraph({0: [1, 1, 2, 2, 3], 1: [2, 3, 3], 3: [2]})
1655
+ assert not nx.vf2pp_is_isomorphic(g, h)