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,3118 @@
1
+ import itertools as it
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+ from networkx import vf2pp_is_isomorphic, vf2pp_isomorphism
7
+ from networkx.algorithms.isomorphism.vf2pp import (
8
+ _consistent_PT,
9
+ _cut_PT,
10
+ _feasibility,
11
+ _find_candidates,
12
+ _find_candidates_Di,
13
+ _GraphParameters,
14
+ _initialize_parameters,
15
+ _matching_order,
16
+ _restore_Tinout,
17
+ _restore_Tinout_Di,
18
+ _StateParameters,
19
+ _update_Tinout,
20
+ )
21
+
22
+ labels_same = ["blue"]
23
+
24
+ labels_many = [
25
+ "white",
26
+ "red",
27
+ "blue",
28
+ "green",
29
+ "orange",
30
+ "black",
31
+ "purple",
32
+ "yellow",
33
+ "brown",
34
+ "cyan",
35
+ "solarized",
36
+ "pink",
37
+ "none",
38
+ ]
39
+
40
+
41
+ class TestNodeOrdering:
42
+ def test_empty_graph(self):
43
+ G1 = nx.Graph()
44
+ G2 = nx.Graph()
45
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
46
+ assert len(set(_matching_order(gparams))) == 0
47
+
48
+ def test_single_node(self):
49
+ G1 = nx.Graph()
50
+ G2 = nx.Graph()
51
+ G1.add_node(1)
52
+ G2.add_node(1)
53
+
54
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels_many))), "label")
55
+ nx.set_node_attributes(
56
+ G2,
57
+ dict(zip(G2, it.cycle(labels_many))),
58
+ "label",
59
+ )
60
+ l1, l2 = (
61
+ nx.get_node_attributes(G1, "label"),
62
+ nx.get_node_attributes(G2, "label"),
63
+ )
64
+
65
+ gparams = _GraphParameters(
66
+ G1,
67
+ G2,
68
+ l1,
69
+ l2,
70
+ nx.utils.groups(l1),
71
+ nx.utils.groups(l2),
72
+ nx.utils.groups(dict(G2.degree())),
73
+ )
74
+ m = _matching_order(gparams)
75
+ assert m == [1]
76
+
77
+ def test_matching_order(self):
78
+ labels = [
79
+ "blue",
80
+ "blue",
81
+ "red",
82
+ "red",
83
+ "red",
84
+ "red",
85
+ "green",
86
+ "green",
87
+ "green",
88
+ "yellow",
89
+ "purple",
90
+ "purple",
91
+ "blue",
92
+ "blue",
93
+ ]
94
+ G1 = nx.Graph(
95
+ [
96
+ (0, 1),
97
+ (0, 2),
98
+ (1, 2),
99
+ (2, 5),
100
+ (2, 4),
101
+ (1, 3),
102
+ (1, 4),
103
+ (3, 6),
104
+ (4, 6),
105
+ (6, 7),
106
+ (7, 8),
107
+ (9, 10),
108
+ (9, 11),
109
+ (11, 12),
110
+ (11, 13),
111
+ (12, 13),
112
+ (10, 13),
113
+ ]
114
+ )
115
+ G2 = G1.copy()
116
+ nx.set_node_attributes(G1, dict(zip(G1, it.cycle(labels))), "label")
117
+ nx.set_node_attributes(
118
+ G2,
119
+ dict(zip(G2, it.cycle(labels))),
120
+ "label",
121
+ )
122
+ l1, l2 = (
123
+ nx.get_node_attributes(G1, "label"),
124
+ nx.get_node_attributes(G2, "label"),
125
+ )
126
+ gparams = _GraphParameters(
127
+ G1,
128
+ G2,
129
+ l1,
130
+ l2,
131
+ nx.utils.groups(l1),
132
+ nx.utils.groups(l2),
133
+ nx.utils.groups(dict(G2.degree())),
134
+ )
135
+
136
+ expected = [9, 11, 10, 13, 12, 1, 2, 4, 0, 3, 6, 5, 7, 8]
137
+ assert _matching_order(gparams) == expected
138
+
139
+ def test_matching_order_all_branches(self):
140
+ G1 = nx.Graph(
141
+ [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 4), (3, 4)]
142
+ )
143
+ G1.add_node(5)
144
+ G2 = G1.copy()
145
+
146
+ G1.nodes[0]["label"] = "black"
147
+ G1.nodes[1]["label"] = "blue"
148
+ G1.nodes[2]["label"] = "blue"
149
+ G1.nodes[3]["label"] = "red"
150
+ G1.nodes[4]["label"] = "red"
151
+ G1.nodes[5]["label"] = "blue"
152
+
153
+ G2.nodes[0]["label"] = "black"
154
+ G2.nodes[1]["label"] = "blue"
155
+ G2.nodes[2]["label"] = "blue"
156
+ G2.nodes[3]["label"] = "red"
157
+ G2.nodes[4]["label"] = "red"
158
+ G2.nodes[5]["label"] = "blue"
159
+
160
+ l1, l2 = (
161
+ nx.get_node_attributes(G1, "label"),
162
+ nx.get_node_attributes(G2, "label"),
163
+ )
164
+ gparams = _GraphParameters(
165
+ G1,
166
+ G2,
167
+ l1,
168
+ l2,
169
+ nx.utils.groups(l1),
170
+ nx.utils.groups(l2),
171
+ nx.utils.groups(dict(G2.degree())),
172
+ )
173
+
174
+ expected = [0, 4, 1, 3, 2, 5]
175
+ assert _matching_order(gparams) == expected
176
+
177
+
178
+ class TestGraphCandidateSelection:
179
+ G1_edges = [
180
+ (1, 2),
181
+ (1, 4),
182
+ (1, 5),
183
+ (2, 3),
184
+ (2, 4),
185
+ (3, 4),
186
+ (4, 5),
187
+ (1, 6),
188
+ (6, 7),
189
+ (6, 8),
190
+ (8, 9),
191
+ (7, 9),
192
+ ]
193
+ mapped = {
194
+ 0: "x",
195
+ 1: "a",
196
+ 2: "b",
197
+ 3: "c",
198
+ 4: "d",
199
+ 5: "e",
200
+ 6: "f",
201
+ 7: "g",
202
+ 8: "h",
203
+ 9: "i",
204
+ }
205
+
206
+ def test_no_covered_neighbors_no_labels(self):
207
+ G1 = nx.Graph()
208
+ G1.add_edges_from(self.G1_edges)
209
+ G1.add_node(0)
210
+ G2 = nx.relabel_nodes(G1, self.mapped)
211
+
212
+ G1_degree = dict(G1.degree)
213
+ l1 = dict(G1.nodes(data="label", default=-1))
214
+ l2 = dict(G2.nodes(data="label", default=-1))
215
+ gparams = _GraphParameters(
216
+ G1,
217
+ G2,
218
+ l1,
219
+ l2,
220
+ nx.utils.groups(l1),
221
+ nx.utils.groups(l2),
222
+ nx.utils.groups(dict(G2.degree())),
223
+ )
224
+
225
+ m = {9: self.mapped[9], 1: self.mapped[1]}
226
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
227
+
228
+ T1 = {7, 8, 2, 4, 5}
229
+ T1_tilde = {0, 3, 6}
230
+ T2 = {"g", "h", "b", "d", "e"}
231
+ T2_tilde = {"x", "c", "f"}
232
+
233
+ sparams = _StateParameters(
234
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
235
+ )
236
+
237
+ u = 3
238
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
239
+ assert candidates == {self.mapped[u]}
240
+
241
+ u = 0
242
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
243
+ assert candidates == {self.mapped[u]}
244
+
245
+ m.pop(9)
246
+ m_rev.pop(self.mapped[9])
247
+
248
+ T1 = {2, 4, 5, 6}
249
+ T1_tilde = {0, 3, 7, 8, 9}
250
+ T2 = {"g", "h", "b", "d", "e", "f"}
251
+ T2_tilde = {"x", "c", "g", "h", "i"}
252
+
253
+ sparams = _StateParameters(
254
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
255
+ )
256
+
257
+ u = 7
258
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
259
+ assert candidates == {
260
+ self.mapped[u],
261
+ self.mapped[8],
262
+ self.mapped[3],
263
+ self.mapped[9],
264
+ }
265
+
266
+ def test_no_covered_neighbors_with_labels(self):
267
+ G1 = nx.Graph()
268
+ G1.add_edges_from(self.G1_edges)
269
+ G1.add_node(0)
270
+ G2 = nx.relabel_nodes(G1, self.mapped)
271
+
272
+ G1_degree = dict(G1.degree)
273
+ nx.set_node_attributes(
274
+ G1,
275
+ dict(zip(G1, it.cycle(labels_many))),
276
+ "label",
277
+ )
278
+ nx.set_node_attributes(
279
+ G2,
280
+ dict(
281
+ zip(
282
+ [self.mapped[n] for n in G1],
283
+ it.cycle(labels_many),
284
+ )
285
+ ),
286
+ "label",
287
+ )
288
+ l1 = dict(G1.nodes(data="label", default=-1))
289
+ l2 = dict(G2.nodes(data="label", default=-1))
290
+ gparams = _GraphParameters(
291
+ G1,
292
+ G2,
293
+ l1,
294
+ l2,
295
+ nx.utils.groups(l1),
296
+ nx.utils.groups(l2),
297
+ nx.utils.groups(dict(G2.degree())),
298
+ )
299
+
300
+ m = {9: self.mapped[9], 1: self.mapped[1]}
301
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
302
+
303
+ T1 = {7, 8, 2, 4, 5, 6}
304
+ T1_tilde = {0, 3}
305
+ T2 = {"g", "h", "b", "d", "e", "f"}
306
+ T2_tilde = {"x", "c"}
307
+
308
+ sparams = _StateParameters(
309
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
310
+ )
311
+
312
+ u = 3
313
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
314
+ assert candidates == {self.mapped[u]}
315
+
316
+ u = 0
317
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
318
+ assert candidates == {self.mapped[u]}
319
+
320
+ # Change label of disconnected node
321
+ G1.nodes[u]["label"] = "blue"
322
+ l1 = dict(G1.nodes(data="label", default=-1))
323
+ l2 = dict(G2.nodes(data="label", default=-1))
324
+ gparams = _GraphParameters(
325
+ G1,
326
+ G2,
327
+ l1,
328
+ l2,
329
+ nx.utils.groups(l1),
330
+ nx.utils.groups(l2),
331
+ nx.utils.groups(dict(G2.degree())),
332
+ )
333
+
334
+ # No candidate
335
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
336
+ assert candidates == set()
337
+
338
+ m.pop(9)
339
+ m_rev.pop(self.mapped[9])
340
+
341
+ T1 = {2, 4, 5, 6}
342
+ T1_tilde = {0, 3, 7, 8, 9}
343
+ T2 = {"b", "d", "e", "f"}
344
+ T2_tilde = {"x", "c", "g", "h", "i"}
345
+
346
+ sparams = _StateParameters(
347
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
348
+ )
349
+
350
+ u = 7
351
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
352
+ assert candidates == {self.mapped[u]}
353
+
354
+ G1.nodes[8]["label"] = G1.nodes[7]["label"]
355
+ G2.nodes[self.mapped[8]]["label"] = G1.nodes[7]["label"]
356
+ l1 = dict(G1.nodes(data="label", default=-1))
357
+ l2 = dict(G2.nodes(data="label", default=-1))
358
+ gparams = _GraphParameters(
359
+ G1,
360
+ G2,
361
+ l1,
362
+ l2,
363
+ nx.utils.groups(l1),
364
+ nx.utils.groups(l2),
365
+ nx.utils.groups(dict(G2.degree())),
366
+ )
367
+
368
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
369
+ assert candidates == {self.mapped[u], self.mapped[8]}
370
+
371
+ def test_covered_neighbors_no_labels(self):
372
+ G1 = nx.Graph()
373
+ G1.add_edges_from(self.G1_edges)
374
+ G1.add_node(0)
375
+ G2 = nx.relabel_nodes(G1, self.mapped)
376
+
377
+ G1_degree = dict(G1.degree)
378
+ l1 = dict(G1.nodes(data=None, default=-1))
379
+ l2 = dict(G2.nodes(data=None, default=-1))
380
+ gparams = _GraphParameters(
381
+ G1,
382
+ G2,
383
+ l1,
384
+ l2,
385
+ nx.utils.groups(l1),
386
+ nx.utils.groups(l2),
387
+ nx.utils.groups(dict(G2.degree())),
388
+ )
389
+
390
+ m = {9: self.mapped[9], 1: self.mapped[1]}
391
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
392
+
393
+ T1 = {7, 8, 2, 4, 5, 6}
394
+ T1_tilde = {0, 3}
395
+ T2 = {"g", "h", "b", "d", "e", "f"}
396
+ T2_tilde = {"x", "c"}
397
+
398
+ sparams = _StateParameters(
399
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
400
+ )
401
+
402
+ u = 5
403
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
404
+ assert candidates == {self.mapped[u]}
405
+
406
+ u = 6
407
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
408
+ assert candidates == {self.mapped[u], self.mapped[2]}
409
+
410
+ def test_covered_neighbors_with_labels(self):
411
+ G1 = nx.Graph()
412
+ G1.add_edges_from(self.G1_edges)
413
+ G1.add_node(0)
414
+ G2 = nx.relabel_nodes(G1, self.mapped)
415
+
416
+ G1_degree = dict(G1.degree)
417
+ nx.set_node_attributes(
418
+ G1,
419
+ dict(zip(G1, it.cycle(labels_many))),
420
+ "label",
421
+ )
422
+ nx.set_node_attributes(
423
+ G2,
424
+ dict(
425
+ zip(
426
+ [self.mapped[n] for n in G1],
427
+ it.cycle(labels_many),
428
+ )
429
+ ),
430
+ "label",
431
+ )
432
+ l1 = dict(G1.nodes(data="label", default=-1))
433
+ l2 = dict(G2.nodes(data="label", default=-1))
434
+ gparams = _GraphParameters(
435
+ G1,
436
+ G2,
437
+ l1,
438
+ l2,
439
+ nx.utils.groups(l1),
440
+ nx.utils.groups(l2),
441
+ nx.utils.groups(dict(G2.degree())),
442
+ )
443
+
444
+ m = {9: self.mapped[9], 1: self.mapped[1]}
445
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
446
+
447
+ T1 = {7, 8, 2, 4, 5, 6}
448
+ T1_tilde = {0, 3}
449
+ T2 = {"g", "h", "b", "d", "e", "f"}
450
+ T2_tilde = {"x", "c"}
451
+
452
+ sparams = _StateParameters(
453
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
454
+ )
455
+
456
+ u = 5
457
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
458
+ assert candidates == {self.mapped[u]}
459
+
460
+ u = 6
461
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
462
+ assert candidates == {self.mapped[u]}
463
+
464
+ # Assign to 2, the same label as 6
465
+ G1.nodes[2]["label"] = G1.nodes[u]["label"]
466
+ G2.nodes[self.mapped[2]]["label"] = G1.nodes[u]["label"]
467
+ l1 = dict(G1.nodes(data="label", default=-1))
468
+ l2 = dict(G2.nodes(data="label", default=-1))
469
+ gparams = _GraphParameters(
470
+ G1,
471
+ G2,
472
+ l1,
473
+ l2,
474
+ nx.utils.groups(l1),
475
+ nx.utils.groups(l2),
476
+ nx.utils.groups(dict(G2.degree())),
477
+ )
478
+
479
+ candidates = _find_candidates(u, gparams, sparams, G1_degree)
480
+ assert candidates == {self.mapped[u], self.mapped[2]}
481
+
482
+
483
+ class TestDiGraphCandidateSelection:
484
+ G1_edges = [
485
+ (1, 2),
486
+ (1, 4),
487
+ (5, 1),
488
+ (2, 3),
489
+ (4, 2),
490
+ (3, 4),
491
+ (4, 5),
492
+ (1, 6),
493
+ (6, 7),
494
+ (6, 8),
495
+ (8, 9),
496
+ (7, 9),
497
+ ]
498
+ mapped = {
499
+ 0: "x",
500
+ 1: "a",
501
+ 2: "b",
502
+ 3: "c",
503
+ 4: "d",
504
+ 5: "e",
505
+ 6: "f",
506
+ 7: "g",
507
+ 8: "h",
508
+ 9: "i",
509
+ }
510
+
511
+ def test_no_covered_neighbors_no_labels(self):
512
+ G1 = nx.DiGraph()
513
+ G1.add_edges_from(self.G1_edges)
514
+ G1.add_node(0)
515
+ G2 = nx.relabel_nodes(G1, self.mapped)
516
+
517
+ G1_degree = {
518
+ n: (in_degree, out_degree)
519
+ for (n, in_degree), (_, out_degree) in zip(G1.in_degree, G1.out_degree)
520
+ }
521
+
522
+ l1 = dict(G1.nodes(data="label", default=-1))
523
+ l2 = dict(G2.nodes(data="label", default=-1))
524
+ gparams = _GraphParameters(
525
+ G1,
526
+ G2,
527
+ l1,
528
+ l2,
529
+ nx.utils.groups(l1),
530
+ nx.utils.groups(l2),
531
+ nx.utils.groups(
532
+ {
533
+ node: (in_degree, out_degree)
534
+ for (node, in_degree), (_, out_degree) in zip(
535
+ G2.in_degree(), G2.out_degree()
536
+ )
537
+ }
538
+ ),
539
+ )
540
+
541
+ m = {9: self.mapped[9], 1: self.mapped[1]}
542
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
543
+
544
+ T1_out = {2, 4, 6}
545
+ T1_in = {5, 7, 8}
546
+ T1_tilde = {0, 3}
547
+ T2_out = {"b", "d", "f"}
548
+ T2_in = {"e", "g", "h"}
549
+ T2_tilde = {"x", "c"}
550
+
551
+ sparams = _StateParameters(
552
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
553
+ )
554
+
555
+ u = 3
556
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
557
+ assert candidates == {self.mapped[u]}
558
+
559
+ u = 0
560
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
561
+ assert candidates == {self.mapped[u]}
562
+
563
+ m.pop(9)
564
+ m_rev.pop(self.mapped[9])
565
+
566
+ T1_out = {2, 4, 6}
567
+ T1_in = {5}
568
+ T1_tilde = {0, 3, 7, 8, 9}
569
+ T2_out = {"b", "d", "f"}
570
+ T2_in = {"e"}
571
+ T2_tilde = {"x", "c", "g", "h", "i"}
572
+
573
+ sparams = _StateParameters(
574
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
575
+ )
576
+
577
+ u = 7
578
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
579
+ assert candidates == {self.mapped[u], self.mapped[8], self.mapped[3]}
580
+
581
+ def test_no_covered_neighbors_with_labels(self):
582
+ G1 = nx.DiGraph()
583
+ G1.add_edges_from(self.G1_edges)
584
+ G1.add_node(0)
585
+ G2 = nx.relabel_nodes(G1, self.mapped)
586
+
587
+ G1_degree = {
588
+ n: (in_degree, out_degree)
589
+ for (n, in_degree), (_, out_degree) in zip(G1.in_degree, G1.out_degree)
590
+ }
591
+ nx.set_node_attributes(
592
+ G1,
593
+ dict(zip(G1, it.cycle(labels_many))),
594
+ "label",
595
+ )
596
+ nx.set_node_attributes(
597
+ G2,
598
+ dict(
599
+ zip(
600
+ [self.mapped[n] for n in G1],
601
+ it.cycle(labels_many),
602
+ )
603
+ ),
604
+ "label",
605
+ )
606
+ l1 = dict(G1.nodes(data="label", default=-1))
607
+ l2 = dict(G2.nodes(data="label", default=-1))
608
+ gparams = _GraphParameters(
609
+ G1,
610
+ G2,
611
+ l1,
612
+ l2,
613
+ nx.utils.groups(l1),
614
+ nx.utils.groups(l2),
615
+ nx.utils.groups(
616
+ {
617
+ node: (in_degree, out_degree)
618
+ for (node, in_degree), (_, out_degree) in zip(
619
+ G2.in_degree(), G2.out_degree()
620
+ )
621
+ }
622
+ ),
623
+ )
624
+
625
+ m = {9: self.mapped[9], 1: self.mapped[1]}
626
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
627
+
628
+ T1_out = {2, 4, 6}
629
+ T1_in = {5, 7, 8}
630
+ T1_tilde = {0, 3}
631
+ T2_out = {"b", "d", "f"}
632
+ T2_in = {"e", "g", "h"}
633
+ T2_tilde = {"x", "c"}
634
+
635
+ sparams = _StateParameters(
636
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
637
+ )
638
+
639
+ u = 3
640
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
641
+ assert candidates == {self.mapped[u]}
642
+
643
+ u = 0
644
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
645
+ assert candidates == {self.mapped[u]}
646
+
647
+ # Change label of disconnected node
648
+ G1.nodes[u]["label"] = "blue"
649
+ l1 = dict(G1.nodes(data="label", default=-1))
650
+ l2 = dict(G2.nodes(data="label", default=-1))
651
+ gparams = _GraphParameters(
652
+ G1,
653
+ G2,
654
+ l1,
655
+ l2,
656
+ nx.utils.groups(l1),
657
+ nx.utils.groups(l2),
658
+ nx.utils.groups(
659
+ {
660
+ node: (in_degree, out_degree)
661
+ for (node, in_degree), (_, out_degree) in zip(
662
+ G2.in_degree(), G2.out_degree()
663
+ )
664
+ }
665
+ ),
666
+ )
667
+
668
+ # No candidate
669
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
670
+ assert candidates == set()
671
+
672
+ m.pop(9)
673
+ m_rev.pop(self.mapped[9])
674
+
675
+ T1_out = {2, 4, 6}
676
+ T1_in = {5}
677
+ T1_tilde = {0, 3, 7, 8, 9}
678
+ T2_out = {"b", "d", "f"}
679
+ T2_in = {"e"}
680
+ T2_tilde = {"x", "c", "g", "h", "i"}
681
+
682
+ sparams = _StateParameters(
683
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
684
+ )
685
+
686
+ u = 7
687
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
688
+ assert candidates == {self.mapped[u]}
689
+
690
+ G1.nodes[8]["label"] = G1.nodes[7]["label"]
691
+ G2.nodes[self.mapped[8]]["label"] = G1.nodes[7]["label"]
692
+ l1 = dict(G1.nodes(data="label", default=-1))
693
+ l2 = dict(G2.nodes(data="label", default=-1))
694
+ gparams = _GraphParameters(
695
+ G1,
696
+ G2,
697
+ l1,
698
+ l2,
699
+ nx.utils.groups(l1),
700
+ nx.utils.groups(l2),
701
+ nx.utils.groups(
702
+ {
703
+ node: (in_degree, out_degree)
704
+ for (node, in_degree), (_, out_degree) in zip(
705
+ G2.in_degree(), G2.out_degree()
706
+ )
707
+ }
708
+ ),
709
+ )
710
+
711
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
712
+ assert candidates == {self.mapped[u], self.mapped[8]}
713
+
714
+ def test_covered_neighbors_no_labels(self):
715
+ G1 = nx.DiGraph()
716
+ G1.add_edges_from(self.G1_edges)
717
+ G1.add_node(0)
718
+ G2 = nx.relabel_nodes(G1, self.mapped)
719
+
720
+ G1_degree = {
721
+ n: (in_degree, out_degree)
722
+ for (n, in_degree), (_, out_degree) in zip(G1.in_degree, G1.out_degree)
723
+ }
724
+
725
+ l1 = dict(G1.nodes(data=None, default=-1))
726
+ l2 = dict(G2.nodes(data=None, default=-1))
727
+ gparams = _GraphParameters(
728
+ G1,
729
+ G2,
730
+ l1,
731
+ l2,
732
+ nx.utils.groups(l1),
733
+ nx.utils.groups(l2),
734
+ nx.utils.groups(
735
+ {
736
+ node: (in_degree, out_degree)
737
+ for (node, in_degree), (_, out_degree) in zip(
738
+ G2.in_degree(), G2.out_degree()
739
+ )
740
+ }
741
+ ),
742
+ )
743
+
744
+ m = {9: self.mapped[9], 1: self.mapped[1]}
745
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
746
+
747
+ T1_out = {2, 4, 6}
748
+ T1_in = {5, 7, 8}
749
+ T1_tilde = {0, 3}
750
+ T2_out = {"b", "d", "f"}
751
+ T2_in = {"e", "g", "h"}
752
+ T2_tilde = {"x", "c"}
753
+
754
+ sparams = _StateParameters(
755
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
756
+ )
757
+
758
+ u = 5
759
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
760
+ assert candidates == {self.mapped[u]}
761
+
762
+ u = 6
763
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
764
+ assert candidates == {self.mapped[u]}
765
+
766
+ # Change the direction of an edge to make the degree orientation same as first candidate of u.
767
+ G1.remove_edge(4, 2)
768
+ G1.add_edge(2, 4)
769
+ G2.remove_edge("d", "b")
770
+ G2.add_edge("b", "d")
771
+
772
+ gparams = _GraphParameters(
773
+ G1,
774
+ G2,
775
+ l1,
776
+ l2,
777
+ nx.utils.groups(l1),
778
+ nx.utils.groups(l2),
779
+ nx.utils.groups(
780
+ {
781
+ node: (in_degree, out_degree)
782
+ for (node, in_degree), (_, out_degree) in zip(
783
+ G2.in_degree(), G2.out_degree()
784
+ )
785
+ }
786
+ ),
787
+ )
788
+
789
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
790
+ assert candidates == {self.mapped[u], self.mapped[2]}
791
+
792
+ def test_covered_neighbors_with_labels(self):
793
+ G1 = nx.DiGraph()
794
+ G1.add_edges_from(self.G1_edges)
795
+ G1.add_node(0)
796
+ G2 = nx.relabel_nodes(G1, self.mapped)
797
+
798
+ G1.remove_edge(4, 2)
799
+ G1.add_edge(2, 4)
800
+ G2.remove_edge("d", "b")
801
+ G2.add_edge("b", "d")
802
+
803
+ G1_degree = {
804
+ n: (in_degree, out_degree)
805
+ for (n, in_degree), (_, out_degree) in zip(G1.in_degree, G1.out_degree)
806
+ }
807
+
808
+ nx.set_node_attributes(
809
+ G1,
810
+ dict(zip(G1, it.cycle(labels_many))),
811
+ "label",
812
+ )
813
+ nx.set_node_attributes(
814
+ G2,
815
+ dict(
816
+ zip(
817
+ [self.mapped[n] for n in G1],
818
+ it.cycle(labels_many),
819
+ )
820
+ ),
821
+ "label",
822
+ )
823
+ l1 = dict(G1.nodes(data="label", default=-1))
824
+ l2 = dict(G2.nodes(data="label", default=-1))
825
+ gparams = _GraphParameters(
826
+ G1,
827
+ G2,
828
+ l1,
829
+ l2,
830
+ nx.utils.groups(l1),
831
+ nx.utils.groups(l2),
832
+ nx.utils.groups(
833
+ {
834
+ node: (in_degree, out_degree)
835
+ for (node, in_degree), (_, out_degree) in zip(
836
+ G2.in_degree(), G2.out_degree()
837
+ )
838
+ }
839
+ ),
840
+ )
841
+
842
+ m = {9: self.mapped[9], 1: self.mapped[1]}
843
+ m_rev = {self.mapped[9]: 9, self.mapped[1]: 1}
844
+
845
+ T1_out = {2, 4, 6}
846
+ T1_in = {5, 7, 8}
847
+ T1_tilde = {0, 3}
848
+ T2_out = {"b", "d", "f"}
849
+ T2_in = {"e", "g", "h"}
850
+ T2_tilde = {"x", "c"}
851
+
852
+ sparams = _StateParameters(
853
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
854
+ )
855
+
856
+ u = 5
857
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
858
+ assert candidates == {self.mapped[u]}
859
+
860
+ u = 6
861
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
862
+ assert candidates == {self.mapped[u]}
863
+
864
+ # Assign to 2, the same label as 6
865
+ G1.nodes[2]["label"] = G1.nodes[u]["label"]
866
+ G2.nodes[self.mapped[2]]["label"] = G1.nodes[u]["label"]
867
+ l1 = dict(G1.nodes(data="label", default=-1))
868
+ l2 = dict(G2.nodes(data="label", default=-1))
869
+ gparams = _GraphParameters(
870
+ G1,
871
+ G2,
872
+ l1,
873
+ l2,
874
+ nx.utils.groups(l1),
875
+ nx.utils.groups(l2),
876
+ nx.utils.groups(
877
+ {
878
+ node: (in_degree, out_degree)
879
+ for (node, in_degree), (_, out_degree) in zip(
880
+ G2.in_degree(), G2.out_degree()
881
+ )
882
+ }
883
+ ),
884
+ )
885
+
886
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
887
+ assert candidates == {self.mapped[u], self.mapped[2]}
888
+
889
+ # Change the direction of an edge to make the degree orientation same as first candidate of u.
890
+ G1.remove_edge(2, 4)
891
+ G1.add_edge(4, 2)
892
+ G2.remove_edge("b", "d")
893
+ G2.add_edge("d", "b")
894
+
895
+ gparams = _GraphParameters(
896
+ G1,
897
+ G2,
898
+ l1,
899
+ l2,
900
+ nx.utils.groups(l1),
901
+ nx.utils.groups(l2),
902
+ nx.utils.groups(
903
+ {
904
+ node: (in_degree, out_degree)
905
+ for (node, in_degree), (_, out_degree) in zip(
906
+ G2.in_degree(), G2.out_degree()
907
+ )
908
+ }
909
+ ),
910
+ )
911
+
912
+ candidates = _find_candidates_Di(u, gparams, sparams, G1_degree)
913
+ assert candidates == {self.mapped[u]}
914
+
915
+ def test_same_in_out_degrees_no_candidate(self):
916
+ g1 = nx.DiGraph([(4, 1), (4, 2), (3, 4), (5, 4), (6, 4)])
917
+ g2 = nx.DiGraph([(1, 4), (2, 4), (3, 4), (4, 5), (4, 6)])
918
+
919
+ l1 = dict(g1.nodes(data=None, default=-1))
920
+ l2 = dict(g2.nodes(data=None, default=-1))
921
+ gparams = _GraphParameters(
922
+ g1,
923
+ g2,
924
+ l1,
925
+ l2,
926
+ nx.utils.groups(l1),
927
+ nx.utils.groups(l2),
928
+ nx.utils.groups(
929
+ {
930
+ node: (in_degree, out_degree)
931
+ for (node, in_degree), (_, out_degree) in zip(
932
+ g2.in_degree(), g2.out_degree()
933
+ )
934
+ }
935
+ ),
936
+ )
937
+
938
+ g1_degree = {
939
+ n: (in_degree, out_degree)
940
+ for (n, in_degree), (_, out_degree) in zip(g1.in_degree, g1.out_degree)
941
+ }
942
+
943
+ m = {1: 1, 2: 2, 3: 3}
944
+ m_rev = m.copy()
945
+
946
+ T1_out = {4}
947
+ T1_in = {4}
948
+ T1_tilde = {5, 6}
949
+ T2_out = {4}
950
+ T2_in = {4}
951
+ T2_tilde = {5, 6}
952
+
953
+ sparams = _StateParameters(
954
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
955
+ )
956
+
957
+ u = 4
958
+ # despite the same in and out degree, there's no candidate for u=4
959
+ candidates = _find_candidates_Di(u, gparams, sparams, g1_degree)
960
+ assert candidates == set()
961
+ # Notice how the regular candidate selection method returns wrong result.
962
+ assert _find_candidates(u, gparams, sparams, g1_degree) == {4}
963
+
964
+
965
+ class TestGraphISOFeasibility:
966
+ def test_const_covered_neighbors(self):
967
+ G1 = nx.Graph([(0, 1), (1, 2), (3, 0), (3, 2)])
968
+ G2 = nx.Graph([("a", "b"), ("b", "c"), ("k", "a"), ("k", "c")])
969
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
970
+ sparams = _StateParameters(
971
+ {0: "a", 1: "b", 2: "c"},
972
+ {"a": 0, "b": 1, "c": 2},
973
+ None,
974
+ None,
975
+ None,
976
+ None,
977
+ None,
978
+ None,
979
+ None,
980
+ None,
981
+ )
982
+ u, v = 3, "k"
983
+ assert _consistent_PT(u, v, gparams, sparams)
984
+
985
+ def test_const_no_covered_neighbors(self):
986
+ G1 = nx.Graph([(0, 1), (1, 2), (3, 4), (3, 5)])
987
+ G2 = nx.Graph([("a", "b"), ("b", "c"), ("k", "w"), ("k", "z")])
988
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
989
+ sparams = _StateParameters(
990
+ {0: "a", 1: "b", 2: "c"},
991
+ {"a": 0, "b": 1, "c": 2},
992
+ None,
993
+ None,
994
+ None,
995
+ None,
996
+ None,
997
+ None,
998
+ None,
999
+ None,
1000
+ )
1001
+ u, v = 3, "k"
1002
+ assert _consistent_PT(u, v, gparams, sparams)
1003
+
1004
+ def test_const_mixed_covered_uncovered_neighbors(self):
1005
+ G1 = nx.Graph([(0, 1), (1, 2), (3, 0), (3, 2), (3, 4), (3, 5)])
1006
+ G2 = nx.Graph(
1007
+ [("a", "b"), ("b", "c"), ("k", "a"), ("k", "c"), ("k", "w"), ("k", "z")]
1008
+ )
1009
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1010
+ sparams = _StateParameters(
1011
+ {0: "a", 1: "b", 2: "c"},
1012
+ {"a": 0, "b": 1, "c": 2},
1013
+ None,
1014
+ None,
1015
+ None,
1016
+ None,
1017
+ None,
1018
+ None,
1019
+ None,
1020
+ None,
1021
+ )
1022
+ u, v = 3, "k"
1023
+ assert _consistent_PT(u, v, gparams, sparams)
1024
+
1025
+ def test_const_fail_cases(self):
1026
+ G1 = nx.Graph(
1027
+ [
1028
+ (0, 1),
1029
+ (1, 2),
1030
+ (10, 0),
1031
+ (10, 3),
1032
+ (10, 4),
1033
+ (10, 5),
1034
+ (10, 6),
1035
+ (4, 1),
1036
+ (5, 3),
1037
+ ]
1038
+ )
1039
+ G2 = nx.Graph(
1040
+ [
1041
+ ("a", "b"),
1042
+ ("b", "c"),
1043
+ ("k", "a"),
1044
+ ("k", "d"),
1045
+ ("k", "e"),
1046
+ ("k", "f"),
1047
+ ("k", "g"),
1048
+ ("e", "b"),
1049
+ ("f", "d"),
1050
+ ]
1051
+ )
1052
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1053
+ sparams = _StateParameters(
1054
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1055
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1056
+ None,
1057
+ None,
1058
+ None,
1059
+ None,
1060
+ None,
1061
+ None,
1062
+ None,
1063
+ None,
1064
+ )
1065
+ u, v = 10, "k"
1066
+ assert _consistent_PT(u, v, gparams, sparams)
1067
+
1068
+ # Delete one uncovered neighbor of u. Notice how it still passes the test.
1069
+ # Two reasons for this:
1070
+ # 1. If u, v had different degrees from the beginning, they wouldn't
1071
+ # be selected as candidates in the first place.
1072
+ # 2. Even if they are selected, consistency is basically 1-look-ahead,
1073
+ # meaning that we take into consideration the relation of the
1074
+ # candidates with their mapped neighbors. The node we deleted is
1075
+ # not a covered neighbor.
1076
+ # Such nodes will be checked by the cut_PT function, which is
1077
+ # basically the 2-look-ahead, checking the relation of the
1078
+ # candidates with T1, T2 (in which belongs the node we just deleted).
1079
+ G1.remove_node(6)
1080
+ assert _consistent_PT(u, v, gparams, sparams)
1081
+
1082
+ # Add one more covered neighbor of u in G1
1083
+ G1.add_edge(u, 2)
1084
+ assert not _consistent_PT(u, v, gparams, sparams)
1085
+
1086
+ # Compensate in G2
1087
+ G2.add_edge(v, "c")
1088
+ assert _consistent_PT(u, v, gparams, sparams)
1089
+
1090
+ # Add one more covered neighbor of v in G2
1091
+ G2.add_edge(v, "x")
1092
+ G1.add_node(7)
1093
+ sparams.mapping.update({7: "x"})
1094
+ sparams.reverse_mapping.update({"x": 7})
1095
+ assert not _consistent_PT(u, v, gparams, sparams)
1096
+
1097
+ # Compendate in G1
1098
+ G1.add_edge(u, 7)
1099
+ assert _consistent_PT(u, v, gparams, sparams)
1100
+
1101
+ @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph))
1102
+ def test_cut_inconsistent_labels(self, graph_type):
1103
+ G1 = graph_type(
1104
+ [
1105
+ (0, 1),
1106
+ (1, 2),
1107
+ (10, 0),
1108
+ (10, 3),
1109
+ (10, 4),
1110
+ (10, 5),
1111
+ (10, 6),
1112
+ (4, 1),
1113
+ (5, 3),
1114
+ ]
1115
+ )
1116
+ G2 = graph_type(
1117
+ [
1118
+ ("a", "b"),
1119
+ ("b", "c"),
1120
+ ("k", "a"),
1121
+ ("k", "d"),
1122
+ ("k", "e"),
1123
+ ("k", "f"),
1124
+ ("k", "g"),
1125
+ ("e", "b"),
1126
+ ("f", "d"),
1127
+ ]
1128
+ )
1129
+
1130
+ l1 = {n: "blue" for n in G1.nodes()}
1131
+ l2 = {n: "blue" for n in G2.nodes()}
1132
+ l1.update({6: "green"}) # Change the label of one neighbor of u
1133
+
1134
+ gparams = _GraphParameters(
1135
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1136
+ )
1137
+ sparams = _StateParameters(
1138
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1139
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1140
+ None,
1141
+ None,
1142
+ None,
1143
+ None,
1144
+ None,
1145
+ None,
1146
+ None,
1147
+ None,
1148
+ )
1149
+
1150
+ u, v = 10, "k"
1151
+ assert _cut_PT(u, v, gparams, sparams)
1152
+
1153
+ def test_cut_consistent_labels(self):
1154
+ G1 = nx.Graph(
1155
+ [
1156
+ (0, 1),
1157
+ (1, 2),
1158
+ (10, 0),
1159
+ (10, 3),
1160
+ (10, 4),
1161
+ (10, 5),
1162
+ (10, 6),
1163
+ (4, 1),
1164
+ (5, 3),
1165
+ ]
1166
+ )
1167
+ G2 = nx.Graph(
1168
+ [
1169
+ ("a", "b"),
1170
+ ("b", "c"),
1171
+ ("k", "a"),
1172
+ ("k", "d"),
1173
+ ("k", "e"),
1174
+ ("k", "f"),
1175
+ ("k", "g"),
1176
+ ("e", "b"),
1177
+ ("f", "d"),
1178
+ ]
1179
+ )
1180
+
1181
+ l1 = {n: "blue" for n in G1.nodes()}
1182
+ l2 = {n: "blue" for n in G2.nodes()}
1183
+
1184
+ gparams = _GraphParameters(
1185
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1186
+ )
1187
+ sparams = _StateParameters(
1188
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1189
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1190
+ {4, 5},
1191
+ None,
1192
+ {6},
1193
+ None,
1194
+ {"e", "f"},
1195
+ None,
1196
+ {"g"},
1197
+ None,
1198
+ )
1199
+
1200
+ u, v = 10, "k"
1201
+ assert not _cut_PT(u, v, gparams, sparams)
1202
+
1203
+ def test_cut_same_labels(self):
1204
+ G1 = nx.Graph(
1205
+ [
1206
+ (0, 1),
1207
+ (1, 2),
1208
+ (10, 0),
1209
+ (10, 3),
1210
+ (10, 4),
1211
+ (10, 5),
1212
+ (10, 6),
1213
+ (4, 1),
1214
+ (5, 3),
1215
+ ]
1216
+ )
1217
+ mapped = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 10: "k"}
1218
+ G2 = nx.relabel_nodes(G1, mapped)
1219
+ l1 = {n: "blue" for n in G1.nodes()}
1220
+ l2 = {n: "blue" for n in G2.nodes()}
1221
+
1222
+ gparams = _GraphParameters(
1223
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1224
+ )
1225
+ sparams = _StateParameters(
1226
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1227
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1228
+ {4, 5},
1229
+ None,
1230
+ {6},
1231
+ None,
1232
+ {"e", "f"},
1233
+ None,
1234
+ {"g"},
1235
+ None,
1236
+ )
1237
+
1238
+ u, v = 10, "k"
1239
+ assert not _cut_PT(u, v, gparams, sparams)
1240
+
1241
+ # Change intersection between G1[u] and T1, so it's not the same as the
1242
+ # one between G2[v] and T2
1243
+ G1.remove_edge(u, 4)
1244
+ assert _cut_PT(u, v, gparams, sparams)
1245
+
1246
+ # Compensate in G2
1247
+ G2.remove_edge(v, mapped[4])
1248
+ assert not _cut_PT(u, v, gparams, sparams)
1249
+
1250
+ # Change intersection between G2[v] and T2_tilde, so it's not the same
1251
+ # as the one between G1[u] and T1_tilde
1252
+ G2.remove_edge(v, mapped[6])
1253
+ assert _cut_PT(u, v, gparams, sparams)
1254
+
1255
+ # Compensate in G1
1256
+ G1.remove_edge(u, 6)
1257
+ assert not _cut_PT(u, v, gparams, sparams)
1258
+
1259
+ # Add disconnected nodes, which will form the new Ti_out
1260
+ G1.add_nodes_from([6, 7, 8])
1261
+ G2.add_nodes_from(["g", "y", "z"])
1262
+ sparams.T1_tilde.update({6, 7, 8})
1263
+ sparams.T2_tilde.update({"g", "y", "z"})
1264
+
1265
+ l1 = {n: "blue" for n in G1.nodes()}
1266
+ l2 = {n: "blue" for n in G2.nodes()}
1267
+ gparams = _GraphParameters(
1268
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1269
+ )
1270
+
1271
+ assert not _cut_PT(u, v, gparams, sparams)
1272
+
1273
+ # Add some new nodes to the mapping
1274
+ sparams.mapping.update({6: "g", 7: "y"})
1275
+ sparams.reverse_mapping.update({"g": 6, "y": 7})
1276
+
1277
+ # Add more nodes to T1, T2.
1278
+ G1.add_edges_from([(6, 20), (7, 20), (6, 21)])
1279
+ G2.add_edges_from([("g", "i"), ("g", "j"), ("y", "j")])
1280
+
1281
+ sparams.mapping.update({20: "j", 21: "i"})
1282
+ sparams.reverse_mapping.update({"j": 20, "i": 21})
1283
+ sparams.T1.update({20, 21})
1284
+ sparams.T2.update({"i", "j"})
1285
+ sparams.T1_tilde.difference_update({6, 7})
1286
+ sparams.T2_tilde.difference_update({"g", "y"})
1287
+
1288
+ assert not _cut_PT(u, v, gparams, sparams)
1289
+
1290
+ # Add nodes from the new T1 and T2, as neighbors of u and v respectively
1291
+ G1.add_edges_from([(u, 20), (u, 21)])
1292
+ G2.add_edges_from([(v, "i"), (v, "j")])
1293
+ l1 = {n: "blue" for n in G1.nodes()}
1294
+ l2 = {n: "blue" for n in G2.nodes()}
1295
+ gparams = _GraphParameters(
1296
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1297
+ )
1298
+
1299
+ assert not _cut_PT(u, v, gparams, sparams)
1300
+
1301
+ # Change the edges, maintaining the G1[u]-T1 intersection
1302
+ G1.remove_edge(u, 20)
1303
+ G1.add_edge(u, 4)
1304
+ assert not _cut_PT(u, v, gparams, sparams)
1305
+
1306
+ # Connect u to 8 which is still in T1_tilde
1307
+ G1.add_edge(u, 8)
1308
+ assert _cut_PT(u, v, gparams, sparams)
1309
+
1310
+ # Same for v and z, so that inters(G1[u], T1out) == inters(G2[v], T2out)
1311
+ G2.add_edge(v, "z")
1312
+ assert not _cut_PT(u, v, gparams, sparams)
1313
+
1314
+ def test_cut_different_labels(self):
1315
+ G1 = nx.Graph(
1316
+ [
1317
+ (0, 1),
1318
+ (1, 2),
1319
+ (1, 14),
1320
+ (0, 4),
1321
+ (1, 5),
1322
+ (2, 6),
1323
+ (3, 7),
1324
+ (3, 6),
1325
+ (4, 10),
1326
+ (4, 9),
1327
+ (6, 10),
1328
+ (20, 9),
1329
+ (20, 15),
1330
+ (20, 12),
1331
+ (20, 11),
1332
+ (12, 13),
1333
+ (11, 13),
1334
+ (20, 8),
1335
+ (20, 3),
1336
+ (20, 5),
1337
+ (20, 0),
1338
+ ]
1339
+ )
1340
+ mapped = {
1341
+ 0: "a",
1342
+ 1: "b",
1343
+ 2: "c",
1344
+ 3: "d",
1345
+ 4: "e",
1346
+ 5: "f",
1347
+ 6: "g",
1348
+ 7: "h",
1349
+ 8: "i",
1350
+ 9: "j",
1351
+ 10: "k",
1352
+ 11: "l",
1353
+ 12: "m",
1354
+ 13: "n",
1355
+ 14: "o",
1356
+ 15: "p",
1357
+ 20: "x",
1358
+ }
1359
+ G2 = nx.relabel_nodes(G1, mapped)
1360
+
1361
+ l1 = {n: "none" for n in G1.nodes()}
1362
+ l2 = {}
1363
+
1364
+ l1.update(
1365
+ {
1366
+ 9: "blue",
1367
+ 15: "blue",
1368
+ 12: "blue",
1369
+ 11: "green",
1370
+ 3: "green",
1371
+ 8: "red",
1372
+ 0: "red",
1373
+ 5: "yellow",
1374
+ }
1375
+ )
1376
+ l2.update({mapped[n]: l for n, l in l1.items()})
1377
+
1378
+ gparams = _GraphParameters(
1379
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1380
+ )
1381
+ sparams = _StateParameters(
1382
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1383
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1384
+ {4, 5, 6, 7, 14},
1385
+ None,
1386
+ {9, 10, 15, 12, 11, 13, 8},
1387
+ None,
1388
+ {"e", "f", "g", "h", "o"},
1389
+ None,
1390
+ {"j", "k", "l", "m", "n", "i", "p"},
1391
+ None,
1392
+ )
1393
+
1394
+ u, v = 20, "x"
1395
+ assert not _cut_PT(u, v, gparams, sparams)
1396
+
1397
+ # Change the orientation of the labels on neighbors of u compared to
1398
+ # neighbors of v. Leave the structure intact
1399
+ l1.update({9: "red"})
1400
+ assert _cut_PT(u, v, gparams, sparams)
1401
+
1402
+ # compensate in G2
1403
+ l2.update({mapped[9]: "red"})
1404
+ assert not _cut_PT(u, v, gparams, sparams)
1405
+
1406
+ # Change the intersection of G1[u] and T1
1407
+ G1.add_edge(u, 4)
1408
+ assert _cut_PT(u, v, gparams, sparams)
1409
+
1410
+ # Same for G2[v] and T2
1411
+ G2.add_edge(v, mapped[4])
1412
+ assert not _cut_PT(u, v, gparams, sparams)
1413
+
1414
+ # Change the intersection of G2[v] and T2_tilde
1415
+ G2.remove_edge(v, mapped[8])
1416
+ assert _cut_PT(u, v, gparams, sparams)
1417
+
1418
+ # Same for G1[u] and T1_tilde
1419
+ G1.remove_edge(u, 8)
1420
+ assert not _cut_PT(u, v, gparams, sparams)
1421
+
1422
+ # Place 8 and mapped[8] in T1 and T2 respectively, by connecting it to covered nodes
1423
+ G1.add_edge(8, 3)
1424
+ G2.add_edge(mapped[8], mapped[3])
1425
+ sparams.T1.add(8)
1426
+ sparams.T2.add(mapped[8])
1427
+ sparams.T1_tilde.remove(8)
1428
+ sparams.T2_tilde.remove(mapped[8])
1429
+
1430
+ assert not _cut_PT(u, v, gparams, sparams)
1431
+
1432
+ # Remove neighbor of u from T1
1433
+ G1.remove_node(5)
1434
+ l1.pop(5)
1435
+ sparams.T1.remove(5)
1436
+ assert _cut_PT(u, v, gparams, sparams)
1437
+
1438
+ # Same in G2
1439
+ G2.remove_node(mapped[5])
1440
+ l2.pop(mapped[5])
1441
+ sparams.T2.remove(mapped[5])
1442
+ assert not _cut_PT(u, v, gparams, sparams)
1443
+
1444
+ def test_feasibility_same_labels(self):
1445
+ G1 = nx.Graph(
1446
+ [
1447
+ (0, 1),
1448
+ (1, 2),
1449
+ (1, 14),
1450
+ (0, 4),
1451
+ (1, 5),
1452
+ (2, 6),
1453
+ (3, 7),
1454
+ (3, 6),
1455
+ (4, 10),
1456
+ (4, 9),
1457
+ (6, 10),
1458
+ (20, 9),
1459
+ (20, 15),
1460
+ (20, 12),
1461
+ (20, 11),
1462
+ (12, 13),
1463
+ (11, 13),
1464
+ (20, 8),
1465
+ (20, 2),
1466
+ (20, 5),
1467
+ (20, 0),
1468
+ ]
1469
+ )
1470
+ mapped = {
1471
+ 0: "a",
1472
+ 1: "b",
1473
+ 2: "c",
1474
+ 3: "d",
1475
+ 4: "e",
1476
+ 5: "f",
1477
+ 6: "g",
1478
+ 7: "h",
1479
+ 8: "i",
1480
+ 9: "j",
1481
+ 10: "k",
1482
+ 11: "l",
1483
+ 12: "m",
1484
+ 13: "n",
1485
+ 14: "o",
1486
+ 15: "p",
1487
+ 20: "x",
1488
+ }
1489
+ G2 = nx.relabel_nodes(G1, mapped)
1490
+
1491
+ l1 = {n: "blue" for n in G1.nodes()}
1492
+ l2 = {mapped[n]: "blue" for n in G1.nodes()}
1493
+
1494
+ gparams = _GraphParameters(
1495
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1496
+ )
1497
+ sparams = _StateParameters(
1498
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1499
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1500
+ {4, 5, 6, 7, 14},
1501
+ None,
1502
+ {9, 10, 15, 12, 11, 13, 8},
1503
+ None,
1504
+ {"e", "f", "g", "h", "o"},
1505
+ None,
1506
+ {"j", "k", "l", "m", "n", "i", "p"},
1507
+ None,
1508
+ )
1509
+
1510
+ u, v = 20, "x"
1511
+ assert not _cut_PT(u, v, gparams, sparams)
1512
+
1513
+ # Change structure in G2 such that, ONLY consistency is harmed
1514
+ G2.remove_edge(mapped[20], mapped[2])
1515
+ G2.add_edge(mapped[20], mapped[3])
1516
+
1517
+ # Consistency check fails, while the cutting rules are satisfied!
1518
+ assert not _cut_PT(u, v, gparams, sparams)
1519
+ assert not _consistent_PT(u, v, gparams, sparams)
1520
+
1521
+ # Compensate in G1 and make it consistent
1522
+ G1.remove_edge(20, 2)
1523
+ G1.add_edge(20, 3)
1524
+ assert not _cut_PT(u, v, gparams, sparams)
1525
+ assert _consistent_PT(u, v, gparams, sparams)
1526
+
1527
+ # ONLY fail the cutting check
1528
+ G2.add_edge(v, mapped[10])
1529
+ assert _cut_PT(u, v, gparams, sparams)
1530
+ assert _consistent_PT(u, v, gparams, sparams)
1531
+
1532
+ def test_feasibility_different_labels(self):
1533
+ G1 = nx.Graph(
1534
+ [
1535
+ (0, 1),
1536
+ (1, 2),
1537
+ (1, 14),
1538
+ (0, 4),
1539
+ (1, 5),
1540
+ (2, 6),
1541
+ (3, 7),
1542
+ (3, 6),
1543
+ (4, 10),
1544
+ (4, 9),
1545
+ (6, 10),
1546
+ (20, 9),
1547
+ (20, 15),
1548
+ (20, 12),
1549
+ (20, 11),
1550
+ (12, 13),
1551
+ (11, 13),
1552
+ (20, 8),
1553
+ (20, 2),
1554
+ (20, 5),
1555
+ (20, 0),
1556
+ ]
1557
+ )
1558
+ mapped = {
1559
+ 0: "a",
1560
+ 1: "b",
1561
+ 2: "c",
1562
+ 3: "d",
1563
+ 4: "e",
1564
+ 5: "f",
1565
+ 6: "g",
1566
+ 7: "h",
1567
+ 8: "i",
1568
+ 9: "j",
1569
+ 10: "k",
1570
+ 11: "l",
1571
+ 12: "m",
1572
+ 13: "n",
1573
+ 14: "o",
1574
+ 15: "p",
1575
+ 20: "x",
1576
+ }
1577
+ G2 = nx.relabel_nodes(G1, mapped)
1578
+
1579
+ l1 = {n: "none" for n in G1.nodes()}
1580
+ l2 = {}
1581
+
1582
+ l1.update(
1583
+ {
1584
+ 9: "blue",
1585
+ 15: "blue",
1586
+ 12: "blue",
1587
+ 11: "green",
1588
+ 2: "green",
1589
+ 8: "red",
1590
+ 0: "red",
1591
+ 5: "yellow",
1592
+ }
1593
+ )
1594
+ l2.update({mapped[n]: l for n, l in l1.items()})
1595
+
1596
+ gparams = _GraphParameters(
1597
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1598
+ )
1599
+ sparams = _StateParameters(
1600
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1601
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1602
+ {4, 5, 6, 7, 14},
1603
+ None,
1604
+ {9, 10, 15, 12, 11, 13, 8},
1605
+ None,
1606
+ {"e", "f", "g", "h", "o"},
1607
+ None,
1608
+ {"j", "k", "l", "m", "n", "i", "p"},
1609
+ None,
1610
+ )
1611
+
1612
+ u, v = 20, "x"
1613
+ assert not _cut_PT(u, v, gparams, sparams)
1614
+
1615
+ # Change structure in G2 such that, ONLY consistency is harmed
1616
+ G2.remove_edge(mapped[20], mapped[2])
1617
+ G2.add_edge(mapped[20], mapped[3])
1618
+ l2.update({mapped[3]: "green"})
1619
+
1620
+ # Consistency check fails, while the cutting rules are satisfied!
1621
+ assert not _cut_PT(u, v, gparams, sparams)
1622
+ assert not _consistent_PT(u, v, gparams, sparams)
1623
+
1624
+ # Compensate in G1 and make it consistent
1625
+ G1.remove_edge(20, 2)
1626
+ G1.add_edge(20, 3)
1627
+ l1.update({3: "green"})
1628
+ assert not _cut_PT(u, v, gparams, sparams)
1629
+ assert _consistent_PT(u, v, gparams, sparams)
1630
+
1631
+ # ONLY fail the cutting check
1632
+ l1.update({5: "red"})
1633
+ assert _cut_PT(u, v, gparams, sparams)
1634
+ assert _consistent_PT(u, v, gparams, sparams)
1635
+
1636
+
1637
+ class TestMultiGraphISOFeasibility:
1638
+ def test_const_covered_neighbors(self):
1639
+ G1 = nx.MultiGraph(
1640
+ [(0, 1), (0, 1), (1, 2), (3, 0), (3, 0), (3, 0), (3, 2), (3, 2)]
1641
+ )
1642
+ G2 = nx.MultiGraph(
1643
+ [
1644
+ ("a", "b"),
1645
+ ("a", "b"),
1646
+ ("b", "c"),
1647
+ ("k", "a"),
1648
+ ("k", "a"),
1649
+ ("k", "a"),
1650
+ ("k", "c"),
1651
+ ("k", "c"),
1652
+ ]
1653
+ )
1654
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1655
+ sparams = _StateParameters(
1656
+ {0: "a", 1: "b", 2: "c"},
1657
+ {"a": 0, "b": 1, "c": 2},
1658
+ None,
1659
+ None,
1660
+ None,
1661
+ None,
1662
+ None,
1663
+ None,
1664
+ None,
1665
+ None,
1666
+ )
1667
+ u, v = 3, "k"
1668
+ assert _consistent_PT(u, v, gparams, sparams)
1669
+
1670
+ def test_const_no_covered_neighbors(self):
1671
+ G1 = nx.MultiGraph([(0, 1), (0, 1), (1, 2), (3, 4), (3, 4), (3, 5)])
1672
+ G2 = nx.MultiGraph([("a", "b"), ("b", "c"), ("k", "w"), ("k", "w"), ("k", "z")])
1673
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1674
+ sparams = _StateParameters(
1675
+ {0: "a", 1: "b", 2: "c"},
1676
+ {"a": 0, "b": 1, "c": 2},
1677
+ None,
1678
+ None,
1679
+ None,
1680
+ None,
1681
+ None,
1682
+ None,
1683
+ None,
1684
+ None,
1685
+ )
1686
+ u, v = 3, "k"
1687
+ assert _consistent_PT(u, v, gparams, sparams)
1688
+
1689
+ def test_const_mixed_covered_uncovered_neighbors(self):
1690
+ G1 = nx.MultiGraph(
1691
+ [(0, 1), (1, 2), (3, 0), (3, 0), (3, 0), (3, 2), (3, 2), (3, 4), (3, 5)]
1692
+ )
1693
+ G2 = nx.MultiGraph(
1694
+ [
1695
+ ("a", "b"),
1696
+ ("b", "c"),
1697
+ ("k", "a"),
1698
+ ("k", "a"),
1699
+ ("k", "a"),
1700
+ ("k", "c"),
1701
+ ("k", "c"),
1702
+ ("k", "w"),
1703
+ ("k", "z"),
1704
+ ]
1705
+ )
1706
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1707
+ sparams = _StateParameters(
1708
+ {0: "a", 1: "b", 2: "c"},
1709
+ {"a": 0, "b": 1, "c": 2},
1710
+ None,
1711
+ None,
1712
+ None,
1713
+ None,
1714
+ None,
1715
+ None,
1716
+ None,
1717
+ None,
1718
+ )
1719
+ u, v = 3, "k"
1720
+ assert _consistent_PT(u, v, gparams, sparams)
1721
+
1722
+ def test_const_fail_cases(self):
1723
+ G1 = nx.MultiGraph(
1724
+ [
1725
+ (0, 1),
1726
+ (1, 2),
1727
+ (10, 0),
1728
+ (10, 0),
1729
+ (10, 0),
1730
+ (10, 3),
1731
+ (10, 3),
1732
+ (10, 4),
1733
+ (10, 5),
1734
+ (10, 6),
1735
+ (10, 6),
1736
+ (4, 1),
1737
+ (5, 3),
1738
+ ]
1739
+ )
1740
+ mapped = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 10: "k"}
1741
+ G2 = nx.relabel_nodes(G1, mapped)
1742
+
1743
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
1744
+ sparams = _StateParameters(
1745
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1746
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1747
+ None,
1748
+ None,
1749
+ None,
1750
+ None,
1751
+ None,
1752
+ None,
1753
+ None,
1754
+ None,
1755
+ )
1756
+ u, v = 10, "k"
1757
+ assert _consistent_PT(u, v, gparams, sparams)
1758
+
1759
+ # Delete one uncovered neighbor of u. Notice how it still passes the test.
1760
+ # Two reasons for this:
1761
+ # 1. If u, v had different degrees from the beginning, they wouldn't be
1762
+ # selected as candidates in the first place.
1763
+ # 2. Even if they are selected, consistency is basically 1-look-ahead,
1764
+ # meaning that we take into consideration the relation of the candidates
1765
+ # with their mapped neighbors. The node we deleted is not a covered
1766
+ # neighbor. Such nodes will be checked by the cut_PT function, which
1767
+ # is basically the 2-look-ahead, checking the relation of the candidates
1768
+ # with T1, T2 (in which belongs the node we just deleted).
1769
+ G1.remove_node(6)
1770
+ assert _consistent_PT(u, v, gparams, sparams)
1771
+
1772
+ # Add one more covered neighbor of u in G1
1773
+ G1.add_edge(u, 2)
1774
+ assert not _consistent_PT(u, v, gparams, sparams)
1775
+
1776
+ # Compensate in G2
1777
+ G2.add_edge(v, "c")
1778
+ assert _consistent_PT(u, v, gparams, sparams)
1779
+
1780
+ # Add one more covered neighbor of v in G2
1781
+ G2.add_edge(v, "x")
1782
+ G1.add_node(7)
1783
+ sparams.mapping.update({7: "x"})
1784
+ sparams.reverse_mapping.update({"x": 7})
1785
+ assert not _consistent_PT(u, v, gparams, sparams)
1786
+
1787
+ # Compendate in G1
1788
+ G1.add_edge(u, 7)
1789
+ assert _consistent_PT(u, v, gparams, sparams)
1790
+
1791
+ # Delete an edge between u and a covered neighbor
1792
+ G1.remove_edges_from([(u, 0), (u, 0)])
1793
+ assert not _consistent_PT(u, v, gparams, sparams)
1794
+
1795
+ # Compensate in G2
1796
+ G2.remove_edges_from([(v, mapped[0]), (v, mapped[0])])
1797
+ assert _consistent_PT(u, v, gparams, sparams)
1798
+
1799
+ # Remove an edge between v and a covered neighbor
1800
+ G2.remove_edge(v, mapped[3])
1801
+ assert not _consistent_PT(u, v, gparams, sparams)
1802
+
1803
+ # Compensate in G1
1804
+ G1.remove_edge(u, 3)
1805
+ assert _consistent_PT(u, v, gparams, sparams)
1806
+
1807
+ def test_cut_same_labels(self):
1808
+ G1 = nx.MultiGraph(
1809
+ [
1810
+ (0, 1),
1811
+ (1, 2),
1812
+ (10, 0),
1813
+ (10, 0),
1814
+ (10, 0),
1815
+ (10, 3),
1816
+ (10, 3),
1817
+ (10, 4),
1818
+ (10, 4),
1819
+ (10, 5),
1820
+ (10, 5),
1821
+ (10, 5),
1822
+ (10, 5),
1823
+ (10, 6),
1824
+ (10, 6),
1825
+ (4, 1),
1826
+ (5, 3),
1827
+ ]
1828
+ )
1829
+ mapped = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 10: "k"}
1830
+ G2 = nx.relabel_nodes(G1, mapped)
1831
+ l1 = {n: "blue" for n in G1.nodes()}
1832
+ l2 = {n: "blue" for n in G2.nodes()}
1833
+
1834
+ gparams = _GraphParameters(
1835
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1836
+ )
1837
+ sparams = _StateParameters(
1838
+ {0: "a", 1: "b", 2: "c", 3: "d"},
1839
+ {"a": 0, "b": 1, "c": 2, "d": 3},
1840
+ {4, 5},
1841
+ None,
1842
+ {6},
1843
+ None,
1844
+ {"e", "f"},
1845
+ None,
1846
+ {"g"},
1847
+ None,
1848
+ )
1849
+
1850
+ u, v = 10, "k"
1851
+ assert not _cut_PT(u, v, gparams, sparams)
1852
+
1853
+ # Remove one of the multiple edges between u and a neighbor
1854
+ G1.remove_edge(u, 4)
1855
+ assert _cut_PT(u, v, gparams, sparams)
1856
+
1857
+ # Compensate in G2
1858
+ G1.remove_edge(u, 4)
1859
+ G2.remove_edges_from([(v, mapped[4]), (v, mapped[4])])
1860
+ assert not _cut_PT(u, v, gparams, sparams)
1861
+
1862
+ # Change intersection between G2[v] and T2_tilde, so it's not the same
1863
+ # as the one between G1[u] and T1_tilde
1864
+ G2.remove_edge(v, mapped[6])
1865
+ assert _cut_PT(u, v, gparams, sparams)
1866
+
1867
+ # Compensate in G1
1868
+ G1.remove_edge(u, 6)
1869
+ assert not _cut_PT(u, v, gparams, sparams)
1870
+
1871
+ # Add more edges between u and neighbor which belongs in T1_tilde
1872
+ G1.add_edges_from([(u, 5), (u, 5), (u, 5)])
1873
+ assert _cut_PT(u, v, gparams, sparams)
1874
+
1875
+ # Compensate in G2
1876
+ G2.add_edges_from([(v, mapped[5]), (v, mapped[5]), (v, mapped[5])])
1877
+ assert not _cut_PT(u, v, gparams, sparams)
1878
+
1879
+ # Add disconnected nodes, which will form the new Ti_out
1880
+ G1.add_nodes_from([6, 7, 8])
1881
+ G2.add_nodes_from(["g", "y", "z"])
1882
+ G1.add_edges_from([(u, 6), (u, 6), (u, 6), (u, 8)])
1883
+ G2.add_edges_from([(v, "g"), (v, "g"), (v, "g"), (v, "z")])
1884
+
1885
+ sparams.T1_tilde.update({6, 7, 8})
1886
+ sparams.T2_tilde.update({"g", "y", "z"})
1887
+
1888
+ l1 = {n: "blue" for n in G1.nodes()}
1889
+ l2 = {n: "blue" for n in G2.nodes()}
1890
+ gparams = _GraphParameters(
1891
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1892
+ )
1893
+
1894
+ assert not _cut_PT(u, v, gparams, sparams)
1895
+
1896
+ # Add some new nodes to the mapping
1897
+ sparams.mapping.update({6: "g", 7: "y"})
1898
+ sparams.reverse_mapping.update({"g": 6, "y": 7})
1899
+
1900
+ # Add more nodes to T1, T2.
1901
+ G1.add_edges_from([(6, 20), (7, 20), (6, 21)])
1902
+ G2.add_edges_from([("g", "i"), ("g", "j"), ("y", "j")])
1903
+
1904
+ sparams.T1.update({20, 21})
1905
+ sparams.T2.update({"i", "j"})
1906
+ sparams.T1_tilde.difference_update({6, 7})
1907
+ sparams.T2_tilde.difference_update({"g", "y"})
1908
+
1909
+ assert not _cut_PT(u, v, gparams, sparams)
1910
+
1911
+ # Remove some edges
1912
+ G2.remove_edge(v, "g")
1913
+ assert _cut_PT(u, v, gparams, sparams)
1914
+
1915
+ G1.remove_edge(u, 6)
1916
+ G1.add_edge(u, 8)
1917
+ G2.add_edge(v, "z")
1918
+ assert not _cut_PT(u, v, gparams, sparams)
1919
+
1920
+ # Add nodes from the new T1 and T2, as neighbors of u and v respectively
1921
+ G1.add_edges_from([(u, 20), (u, 20), (u, 20), (u, 21)])
1922
+ G2.add_edges_from([(v, "i"), (v, "i"), (v, "i"), (v, "j")])
1923
+ l1 = {n: "blue" for n in G1.nodes()}
1924
+ l2 = {n: "blue" for n in G2.nodes()}
1925
+ gparams = _GraphParameters(
1926
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
1927
+ )
1928
+
1929
+ assert not _cut_PT(u, v, gparams, sparams)
1930
+
1931
+ # Change the edges
1932
+ G1.remove_edge(u, 20)
1933
+ G1.add_edge(u, 4)
1934
+ assert _cut_PT(u, v, gparams, sparams)
1935
+
1936
+ G2.remove_edge(v, "i")
1937
+ G2.add_edge(v, mapped[4])
1938
+ assert not _cut_PT(u, v, gparams, sparams)
1939
+
1940
+ def test_cut_different_labels(self):
1941
+ G1 = nx.MultiGraph(
1942
+ [
1943
+ (0, 1),
1944
+ (0, 1),
1945
+ (1, 2),
1946
+ (1, 2),
1947
+ (1, 14),
1948
+ (0, 4),
1949
+ (1, 5),
1950
+ (2, 6),
1951
+ (3, 7),
1952
+ (3, 6),
1953
+ (4, 10),
1954
+ (4, 9),
1955
+ (6, 10),
1956
+ (20, 9),
1957
+ (20, 9),
1958
+ (20, 9),
1959
+ (20, 15),
1960
+ (20, 15),
1961
+ (20, 12),
1962
+ (20, 11),
1963
+ (20, 11),
1964
+ (20, 11),
1965
+ (12, 13),
1966
+ (11, 13),
1967
+ (20, 8),
1968
+ (20, 8),
1969
+ (20, 3),
1970
+ (20, 3),
1971
+ (20, 5),
1972
+ (20, 5),
1973
+ (20, 5),
1974
+ (20, 0),
1975
+ (20, 0),
1976
+ (20, 0),
1977
+ ]
1978
+ )
1979
+ mapped = {
1980
+ 0: "a",
1981
+ 1: "b",
1982
+ 2: "c",
1983
+ 3: "d",
1984
+ 4: "e",
1985
+ 5: "f",
1986
+ 6: "g",
1987
+ 7: "h",
1988
+ 8: "i",
1989
+ 9: "j",
1990
+ 10: "k",
1991
+ 11: "l",
1992
+ 12: "m",
1993
+ 13: "n",
1994
+ 14: "o",
1995
+ 15: "p",
1996
+ 20: "x",
1997
+ }
1998
+ G2 = nx.relabel_nodes(G1, mapped)
1999
+
2000
+ l1 = {n: "none" for n in G1.nodes()}
2001
+ l2 = {}
2002
+
2003
+ l1.update(
2004
+ {
2005
+ 9: "blue",
2006
+ 15: "blue",
2007
+ 12: "blue",
2008
+ 11: "green",
2009
+ 3: "green",
2010
+ 8: "red",
2011
+ 0: "red",
2012
+ 5: "yellow",
2013
+ }
2014
+ )
2015
+ l2.update({mapped[n]: l for n, l in l1.items()})
2016
+
2017
+ gparams = _GraphParameters(
2018
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2019
+ )
2020
+ sparams = _StateParameters(
2021
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2022
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2023
+ {4, 5, 6, 7, 14},
2024
+ None,
2025
+ {9, 10, 15, 12, 11, 13, 8},
2026
+ None,
2027
+ {"e", "f", "g", "h", "o"},
2028
+ None,
2029
+ {"j", "k", "l", "m", "n", "i", "p"},
2030
+ None,
2031
+ )
2032
+
2033
+ u, v = 20, "x"
2034
+ assert not _cut_PT(u, v, gparams, sparams)
2035
+
2036
+ # Change the orientation of the labels on neighbors of u compared to
2037
+ # neighbors of v. Leave the structure intact
2038
+ l1.update({9: "red"})
2039
+ assert _cut_PT(u, v, gparams, sparams)
2040
+
2041
+ # compensate in G2
2042
+ l2.update({mapped[9]: "red"})
2043
+ assert not _cut_PT(u, v, gparams, sparams)
2044
+
2045
+ # Change the intersection of G1[u] and T1
2046
+ G1.add_edge(u, 4)
2047
+ assert _cut_PT(u, v, gparams, sparams)
2048
+
2049
+ # Same for G2[v] and T2
2050
+ G2.add_edge(v, mapped[4])
2051
+ assert not _cut_PT(u, v, gparams, sparams)
2052
+
2053
+ # Delete one from the multiple edges
2054
+ G2.remove_edge(v, mapped[8])
2055
+ assert _cut_PT(u, v, gparams, sparams)
2056
+
2057
+ # Same for G1[u] and T1_tilde
2058
+ G1.remove_edge(u, 8)
2059
+ assert not _cut_PT(u, v, gparams, sparams)
2060
+
2061
+ # Place 8 and mapped[8] in T1 and T2 respectively, by connecting it to covered nodes
2062
+ G1.add_edges_from([(8, 3), (8, 3), (8, u)])
2063
+ G2.add_edges_from([(mapped[8], mapped[3]), (mapped[8], mapped[3])])
2064
+ sparams.T1.add(8)
2065
+ sparams.T2.add(mapped[8])
2066
+ sparams.T1_tilde.remove(8)
2067
+ sparams.T2_tilde.remove(mapped[8])
2068
+
2069
+ assert _cut_PT(u, v, gparams, sparams)
2070
+
2071
+ # Fix uneven edges
2072
+ G1.remove_edge(8, u)
2073
+ assert not _cut_PT(u, v, gparams, sparams)
2074
+
2075
+ # Remove neighbor of u from T1
2076
+ G1.remove_node(5)
2077
+ l1.pop(5)
2078
+ sparams.T1.remove(5)
2079
+ assert _cut_PT(u, v, gparams, sparams)
2080
+
2081
+ # Same in G2
2082
+ G2.remove_node(mapped[5])
2083
+ l2.pop(mapped[5])
2084
+ sparams.T2.remove(mapped[5])
2085
+ assert not _cut_PT(u, v, gparams, sparams)
2086
+
2087
+ def test_feasibility_same_labels(self):
2088
+ G1 = nx.MultiGraph(
2089
+ [
2090
+ (0, 1),
2091
+ (0, 1),
2092
+ (1, 2),
2093
+ (1, 2),
2094
+ (1, 14),
2095
+ (0, 4),
2096
+ (1, 5),
2097
+ (2, 6),
2098
+ (3, 7),
2099
+ (3, 6),
2100
+ (4, 10),
2101
+ (4, 9),
2102
+ (6, 10),
2103
+ (20, 9),
2104
+ (20, 9),
2105
+ (20, 9),
2106
+ (20, 15),
2107
+ (20, 15),
2108
+ (20, 12),
2109
+ (20, 11),
2110
+ (20, 11),
2111
+ (20, 11),
2112
+ (12, 13),
2113
+ (11, 13),
2114
+ (20, 8),
2115
+ (20, 8),
2116
+ (20, 3),
2117
+ (20, 3),
2118
+ (20, 5),
2119
+ (20, 5),
2120
+ (20, 5),
2121
+ (20, 0),
2122
+ (20, 0),
2123
+ (20, 0),
2124
+ ]
2125
+ )
2126
+ mapped = {
2127
+ 0: "a",
2128
+ 1: "b",
2129
+ 2: "c",
2130
+ 3: "d",
2131
+ 4: "e",
2132
+ 5: "f",
2133
+ 6: "g",
2134
+ 7: "h",
2135
+ 8: "i",
2136
+ 9: "j",
2137
+ 10: "k",
2138
+ 11: "l",
2139
+ 12: "m",
2140
+ 13: "n",
2141
+ 14: "o",
2142
+ 15: "p",
2143
+ 20: "x",
2144
+ }
2145
+ G2 = nx.relabel_nodes(G1, mapped)
2146
+ l1 = {n: "blue" for n in G1.nodes()}
2147
+ l2 = {mapped[n]: "blue" for n in G1.nodes()}
2148
+
2149
+ gparams = _GraphParameters(
2150
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2151
+ )
2152
+ sparams = _StateParameters(
2153
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2154
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2155
+ {4, 5, 6, 7, 14},
2156
+ None,
2157
+ {9, 10, 15, 12, 11, 13, 8},
2158
+ None,
2159
+ {"e", "f", "g", "h", "o"},
2160
+ None,
2161
+ {"j", "k", "l", "m", "n", "i", "p"},
2162
+ None,
2163
+ )
2164
+
2165
+ u, v = 20, "x"
2166
+ assert not _cut_PT(u, v, gparams, sparams)
2167
+
2168
+ # Change structure in G2 such that, ONLY consistency is harmed
2169
+ G2.remove_edges_from([(mapped[20], mapped[3]), (mapped[20], mapped[3])])
2170
+ G2.add_edges_from([(mapped[20], mapped[2]), (mapped[20], mapped[2])])
2171
+
2172
+ # Consistency check fails, while the cutting rules are satisfied!
2173
+ assert not _cut_PT(u, v, gparams, sparams)
2174
+ assert not _consistent_PT(u, v, gparams, sparams)
2175
+
2176
+ # Compensate in G1 and make it consistent
2177
+ G1.remove_edges_from([(20, 3), (20, 3)])
2178
+ G1.add_edges_from([(20, 2), (20, 2)])
2179
+ assert not _cut_PT(u, v, gparams, sparams)
2180
+ assert _consistent_PT(u, v, gparams, sparams)
2181
+
2182
+ # ONLY fail the cutting check
2183
+ G2.add_edges_from([(v, mapped[10])] * 5)
2184
+ assert _cut_PT(u, v, gparams, sparams)
2185
+ assert _consistent_PT(u, v, gparams, sparams)
2186
+
2187
+ # Pass all tests
2188
+ G1.add_edges_from([(u, 10)] * 5)
2189
+ assert not _cut_PT(u, v, gparams, sparams)
2190
+ assert _consistent_PT(u, v, gparams, sparams)
2191
+
2192
+ def test_feasibility_different_labels(self):
2193
+ G1 = nx.MultiGraph(
2194
+ [
2195
+ (0, 1),
2196
+ (0, 1),
2197
+ (1, 2),
2198
+ (1, 2),
2199
+ (1, 14),
2200
+ (0, 4),
2201
+ (1, 5),
2202
+ (2, 6),
2203
+ (3, 7),
2204
+ (3, 6),
2205
+ (4, 10),
2206
+ (4, 9),
2207
+ (6, 10),
2208
+ (20, 9),
2209
+ (20, 9),
2210
+ (20, 9),
2211
+ (20, 15),
2212
+ (20, 15),
2213
+ (20, 12),
2214
+ (20, 11),
2215
+ (20, 11),
2216
+ (20, 11),
2217
+ (12, 13),
2218
+ (11, 13),
2219
+ (20, 8),
2220
+ (20, 8),
2221
+ (20, 2),
2222
+ (20, 2),
2223
+ (20, 5),
2224
+ (20, 5),
2225
+ (20, 5),
2226
+ (20, 0),
2227
+ (20, 0),
2228
+ (20, 0),
2229
+ ]
2230
+ )
2231
+ mapped = {
2232
+ 0: "a",
2233
+ 1: "b",
2234
+ 2: "c",
2235
+ 3: "d",
2236
+ 4: "e",
2237
+ 5: "f",
2238
+ 6: "g",
2239
+ 7: "h",
2240
+ 8: "i",
2241
+ 9: "j",
2242
+ 10: "k",
2243
+ 11: "l",
2244
+ 12: "m",
2245
+ 13: "n",
2246
+ 14: "o",
2247
+ 15: "p",
2248
+ 20: "x",
2249
+ }
2250
+ G2 = nx.relabel_nodes(G1, mapped)
2251
+ l1 = {n: "none" for n in G1.nodes()}
2252
+ l2 = {}
2253
+
2254
+ l1.update(
2255
+ {
2256
+ 9: "blue",
2257
+ 15: "blue",
2258
+ 12: "blue",
2259
+ 11: "green",
2260
+ 2: "green",
2261
+ 8: "red",
2262
+ 0: "red",
2263
+ 5: "yellow",
2264
+ }
2265
+ )
2266
+ l2.update({mapped[n]: l for n, l in l1.items()})
2267
+
2268
+ gparams = _GraphParameters(
2269
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2270
+ )
2271
+ sparams = _StateParameters(
2272
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2273
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2274
+ {4, 5, 6, 7, 14},
2275
+ None,
2276
+ {9, 10, 15, 12, 11, 13, 8},
2277
+ None,
2278
+ {"e", "f", "g", "h", "o"},
2279
+ None,
2280
+ {"j", "k", "l", "m", "n", "i", "p"},
2281
+ None,
2282
+ )
2283
+
2284
+ u, v = 20, "x"
2285
+ assert not _cut_PT(u, v, gparams, sparams)
2286
+
2287
+ # Change structure in G2 such that, ONLY consistency is harmed
2288
+ G2.remove_edges_from([(mapped[20], mapped[2]), (mapped[20], mapped[2])])
2289
+ G2.add_edges_from([(mapped[20], mapped[3]), (mapped[20], mapped[3])])
2290
+ l2.update({mapped[3]: "green"})
2291
+
2292
+ # Consistency check fails, while the cutting rules are satisfied!
2293
+ assert not _cut_PT(u, v, gparams, sparams)
2294
+ assert not _consistent_PT(u, v, gparams, sparams)
2295
+
2296
+ # Compensate in G1 and make it consistent
2297
+ G1.remove_edges_from([(20, 2), (20, 2)])
2298
+ G1.add_edges_from([(20, 3), (20, 3)])
2299
+ l1.update({3: "green"})
2300
+ assert not _cut_PT(u, v, gparams, sparams)
2301
+ assert _consistent_PT(u, v, gparams, sparams)
2302
+
2303
+ # ONLY fail the cutting check
2304
+ l1.update({5: "red"})
2305
+ assert _cut_PT(u, v, gparams, sparams)
2306
+ assert _consistent_PT(u, v, gparams, sparams)
2307
+
2308
+
2309
+ class TestDiGraphISOFeasibility:
2310
+ def test_const_covered_neighbors(self):
2311
+ G1 = nx.DiGraph([(0, 1), (1, 2), (0, 3), (2, 3)])
2312
+ G2 = nx.DiGraph([("a", "b"), ("b", "c"), ("a", "k"), ("c", "k")])
2313
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
2314
+ sparams = _StateParameters(
2315
+ {0: "a", 1: "b", 2: "c"},
2316
+ {"a": 0, "b": 1, "c": 2},
2317
+ None,
2318
+ None,
2319
+ None,
2320
+ None,
2321
+ None,
2322
+ None,
2323
+ None,
2324
+ None,
2325
+ )
2326
+ u, v = 3, "k"
2327
+ assert _consistent_PT(u, v, gparams, sparams)
2328
+
2329
+ def test_const_no_covered_neighbors(self):
2330
+ G1 = nx.DiGraph([(0, 1), (1, 2), (3, 4), (3, 5)])
2331
+ G2 = nx.DiGraph([("a", "b"), ("b", "c"), ("k", "w"), ("k", "z")])
2332
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
2333
+ sparams = _StateParameters(
2334
+ {0: "a", 1: "b", 2: "c"},
2335
+ {"a": 0, "b": 1, "c": 2},
2336
+ None,
2337
+ None,
2338
+ None,
2339
+ None,
2340
+ None,
2341
+ None,
2342
+ None,
2343
+ None,
2344
+ )
2345
+ u, v = 3, "k"
2346
+ assert _consistent_PT(u, v, gparams, sparams)
2347
+
2348
+ def test_const_mixed_covered_uncovered_neighbors(self):
2349
+ G1 = nx.DiGraph([(0, 1), (1, 2), (3, 0), (3, 2), (3, 4), (3, 5)])
2350
+ G2 = nx.DiGraph(
2351
+ [("a", "b"), ("b", "c"), ("k", "a"), ("k", "c"), ("k", "w"), ("k", "z")]
2352
+ )
2353
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
2354
+ sparams = _StateParameters(
2355
+ {0: "a", 1: "b", 2: "c"},
2356
+ {"a": 0, "b": 1, "c": 2},
2357
+ None,
2358
+ None,
2359
+ None,
2360
+ None,
2361
+ None,
2362
+ None,
2363
+ None,
2364
+ None,
2365
+ )
2366
+ u, v = 3, "k"
2367
+ assert _consistent_PT(u, v, gparams, sparams)
2368
+
2369
+ def test_const_fail_cases(self):
2370
+ G1 = nx.DiGraph(
2371
+ [
2372
+ (0, 1),
2373
+ (2, 1),
2374
+ (10, 0),
2375
+ (10, 3),
2376
+ (10, 4),
2377
+ (5, 10),
2378
+ (10, 6),
2379
+ (1, 4),
2380
+ (5, 3),
2381
+ ]
2382
+ )
2383
+ G2 = nx.DiGraph(
2384
+ [
2385
+ ("a", "b"),
2386
+ ("c", "b"),
2387
+ ("k", "a"),
2388
+ ("k", "d"),
2389
+ ("k", "e"),
2390
+ ("f", "k"),
2391
+ ("k", "g"),
2392
+ ("b", "e"),
2393
+ ("f", "d"),
2394
+ ]
2395
+ )
2396
+ gparams = _GraphParameters(G1, G2, None, None, None, None, None)
2397
+ sparams = _StateParameters(
2398
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2399
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2400
+ None,
2401
+ None,
2402
+ None,
2403
+ None,
2404
+ None,
2405
+ None,
2406
+ None,
2407
+ None,
2408
+ )
2409
+ u, v = 10, "k"
2410
+ assert _consistent_PT(u, v, gparams, sparams)
2411
+
2412
+ # Delete one uncovered neighbor of u. Notice how it still passes the
2413
+ # test. Two reasons for this:
2414
+ # 1. If u, v had different degrees from the beginning, they wouldn't
2415
+ # be selected as candidates in the first place.
2416
+ # 2. Even if they are selected, consistency is basically
2417
+ # 1-look-ahead, meaning that we take into consideration the
2418
+ # relation of the candidates with their mapped neighbors.
2419
+ # The node we deleted is not a covered neighbor.
2420
+ # Such nodes will be checked by the cut_PT function, which is
2421
+ # basically the 2-look-ahead, checking the relation of the
2422
+ # candidates with T1, T2 (in which belongs the node we just deleted).
2423
+ G1.remove_node(6)
2424
+ assert _consistent_PT(u, v, gparams, sparams)
2425
+
2426
+ # Add one more covered neighbor of u in G1
2427
+ G1.add_edge(u, 2)
2428
+ assert not _consistent_PT(u, v, gparams, sparams)
2429
+
2430
+ # Compensate in G2
2431
+ G2.add_edge(v, "c")
2432
+ assert _consistent_PT(u, v, gparams, sparams)
2433
+
2434
+ # Add one more covered neighbor of v in G2
2435
+ G2.add_edge(v, "x")
2436
+ G1.add_node(7)
2437
+ sparams.mapping.update({7: "x"})
2438
+ sparams.reverse_mapping.update({"x": 7})
2439
+ assert not _consistent_PT(u, v, gparams, sparams)
2440
+
2441
+ # Compensate in G1
2442
+ G1.add_edge(u, 7)
2443
+ assert _consistent_PT(u, v, gparams, sparams)
2444
+
2445
+ def test_cut_inconsistent_labels(self):
2446
+ G1 = nx.DiGraph(
2447
+ [
2448
+ (0, 1),
2449
+ (2, 1),
2450
+ (10, 0),
2451
+ (10, 3),
2452
+ (10, 4),
2453
+ (5, 10),
2454
+ (10, 6),
2455
+ (1, 4),
2456
+ (5, 3),
2457
+ ]
2458
+ )
2459
+ G2 = nx.DiGraph(
2460
+ [
2461
+ ("a", "b"),
2462
+ ("c", "b"),
2463
+ ("k", "a"),
2464
+ ("k", "d"),
2465
+ ("k", "e"),
2466
+ ("f", "k"),
2467
+ ("k", "g"),
2468
+ ("b", "e"),
2469
+ ("f", "d"),
2470
+ ]
2471
+ )
2472
+
2473
+ l1 = {n: "blue" for n in G1.nodes()}
2474
+ l2 = {n: "blue" for n in G2.nodes()}
2475
+ l1.update({5: "green"}) # Change the label of one neighbor of u
2476
+
2477
+ gparams = _GraphParameters(
2478
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2479
+ )
2480
+ sparams = _StateParameters(
2481
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2482
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2483
+ None,
2484
+ None,
2485
+ None,
2486
+ None,
2487
+ None,
2488
+ None,
2489
+ None,
2490
+ None,
2491
+ )
2492
+
2493
+ u, v = 10, "k"
2494
+ assert _cut_PT(u, v, gparams, sparams)
2495
+
2496
+ def test_cut_consistent_labels(self):
2497
+ G1 = nx.DiGraph(
2498
+ [
2499
+ (0, 1),
2500
+ (2, 1),
2501
+ (10, 0),
2502
+ (10, 3),
2503
+ (10, 4),
2504
+ (5, 10),
2505
+ (10, 6),
2506
+ (1, 4),
2507
+ (5, 3),
2508
+ ]
2509
+ )
2510
+ G2 = nx.DiGraph(
2511
+ [
2512
+ ("a", "b"),
2513
+ ("c", "b"),
2514
+ ("k", "a"),
2515
+ ("k", "d"),
2516
+ ("k", "e"),
2517
+ ("f", "k"),
2518
+ ("k", "g"),
2519
+ ("b", "e"),
2520
+ ("f", "d"),
2521
+ ]
2522
+ )
2523
+
2524
+ l1 = {n: "blue" for n in G1.nodes()}
2525
+ l2 = {n: "blue" for n in G2.nodes()}
2526
+
2527
+ gparams = _GraphParameters(
2528
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2529
+ )
2530
+ sparams = _StateParameters(
2531
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2532
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2533
+ {4},
2534
+ {5, 10},
2535
+ {6},
2536
+ None,
2537
+ {"e"},
2538
+ {"f", "k"},
2539
+ {"g"},
2540
+ None,
2541
+ )
2542
+
2543
+ u, v = 10, "k"
2544
+ assert not _cut_PT(u, v, gparams, sparams)
2545
+
2546
+ def test_cut_same_labels(self):
2547
+ G1 = nx.DiGraph(
2548
+ [
2549
+ (0, 1),
2550
+ (2, 1),
2551
+ (10, 0),
2552
+ (10, 3),
2553
+ (10, 4),
2554
+ (5, 10),
2555
+ (10, 6),
2556
+ (1, 4),
2557
+ (5, 3),
2558
+ ]
2559
+ )
2560
+ mapped = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 10: "k"}
2561
+ G2 = nx.relabel_nodes(G1, mapped)
2562
+ l1 = {n: "blue" for n in G1.nodes()}
2563
+ l2 = {n: "blue" for n in G2.nodes()}
2564
+
2565
+ gparams = _GraphParameters(
2566
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2567
+ )
2568
+ sparams = _StateParameters(
2569
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2570
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2571
+ {4},
2572
+ {5, 10},
2573
+ {6},
2574
+ None,
2575
+ {"e"},
2576
+ {"f", "k"},
2577
+ {"g"},
2578
+ None,
2579
+ )
2580
+
2581
+ u, v = 10, "k"
2582
+ assert not _cut_PT(u, v, gparams, sparams)
2583
+
2584
+ # Change intersection between G1[u] and T1_out, so it's not the same as
2585
+ # the one between G2[v] and T2_out
2586
+ G1.remove_edge(u, 4)
2587
+ assert _cut_PT(u, v, gparams, sparams)
2588
+
2589
+ # Compensate in G2
2590
+ G2.remove_edge(v, mapped[4])
2591
+ assert not _cut_PT(u, v, gparams, sparams)
2592
+
2593
+ # Change intersection between G1[u] and T1_in, so it's not the same as
2594
+ # the one between G2[v] and T2_in
2595
+ G1.remove_edge(5, u)
2596
+ assert _cut_PT(u, v, gparams, sparams)
2597
+
2598
+ # Compensate in G2
2599
+ G2.remove_edge(mapped[5], v)
2600
+ assert not _cut_PT(u, v, gparams, sparams)
2601
+
2602
+ # Change intersection between G2[v] and T2_tilde, so it's not the same
2603
+ # as the one between G1[u] and T1_tilde
2604
+ G2.remove_edge(v, mapped[6])
2605
+ assert _cut_PT(u, v, gparams, sparams)
2606
+
2607
+ # Compensate in G1
2608
+ G1.remove_edge(u, 6)
2609
+ assert not _cut_PT(u, v, gparams, sparams)
2610
+
2611
+ # Add disconnected nodes, which will form the new Ti_tilde
2612
+ G1.add_nodes_from([6, 7, 8])
2613
+ G2.add_nodes_from(["g", "y", "z"])
2614
+ sparams.T1_tilde.update({6, 7, 8})
2615
+ sparams.T2_tilde.update({"g", "y", "z"})
2616
+
2617
+ l1 = {n: "blue" for n in G1.nodes()}
2618
+ l2 = {n: "blue" for n in G2.nodes()}
2619
+ gparams = _GraphParameters(
2620
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2621
+ )
2622
+
2623
+ assert not _cut_PT(u, v, gparams, sparams)
2624
+
2625
+ def test_cut_different_labels(self):
2626
+ G1 = nx.DiGraph(
2627
+ [
2628
+ (0, 1),
2629
+ (1, 2),
2630
+ (14, 1),
2631
+ (0, 4),
2632
+ (1, 5),
2633
+ (2, 6),
2634
+ (3, 7),
2635
+ (3, 6),
2636
+ (10, 4),
2637
+ (4, 9),
2638
+ (6, 10),
2639
+ (20, 9),
2640
+ (20, 15),
2641
+ (20, 12),
2642
+ (20, 11),
2643
+ (12, 13),
2644
+ (11, 13),
2645
+ (20, 8),
2646
+ (20, 3),
2647
+ (20, 5),
2648
+ (0, 20),
2649
+ ]
2650
+ )
2651
+ mapped = {
2652
+ 0: "a",
2653
+ 1: "b",
2654
+ 2: "c",
2655
+ 3: "d",
2656
+ 4: "e",
2657
+ 5: "f",
2658
+ 6: "g",
2659
+ 7: "h",
2660
+ 8: "i",
2661
+ 9: "j",
2662
+ 10: "k",
2663
+ 11: "l",
2664
+ 12: "m",
2665
+ 13: "n",
2666
+ 14: "o",
2667
+ 15: "p",
2668
+ 20: "x",
2669
+ }
2670
+ G2 = nx.relabel_nodes(G1, mapped)
2671
+
2672
+ l1 = {n: "none" for n in G1.nodes()}
2673
+ l2 = {}
2674
+
2675
+ l1.update(
2676
+ {
2677
+ 9: "blue",
2678
+ 15: "blue",
2679
+ 12: "blue",
2680
+ 11: "green",
2681
+ 3: "green",
2682
+ 8: "red",
2683
+ 0: "red",
2684
+ 5: "yellow",
2685
+ }
2686
+ )
2687
+ l2.update({mapped[n]: l for n, l in l1.items()})
2688
+
2689
+ gparams = _GraphParameters(
2690
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2691
+ )
2692
+ sparams = _StateParameters(
2693
+ {0: "a", 1: "b", 2: "c", 3: "d"},
2694
+ {"a": 0, "b": 1, "c": 2, "d": 3},
2695
+ {4, 5, 6, 7, 20},
2696
+ {14, 20},
2697
+ {9, 10, 15, 12, 11, 13, 8},
2698
+ None,
2699
+ {"e", "f", "g", "x"},
2700
+ {"o", "x"},
2701
+ {"j", "k", "l", "m", "n", "i", "p"},
2702
+ None,
2703
+ )
2704
+
2705
+ u, v = 20, "x"
2706
+ assert not _cut_PT(u, v, gparams, sparams)
2707
+
2708
+ # Change the orientation of the labels on neighbors of u compared to
2709
+ # neighbors of v. Leave the structure intact
2710
+ l1.update({9: "red"})
2711
+ assert _cut_PT(u, v, gparams, sparams)
2712
+
2713
+ # compensate in G2
2714
+ l2.update({mapped[9]: "red"})
2715
+ assert not _cut_PT(u, v, gparams, sparams)
2716
+
2717
+ # Change the intersection of G1[u] and T1_out
2718
+ G1.add_edge(u, 4)
2719
+ assert _cut_PT(u, v, gparams, sparams)
2720
+
2721
+ # Same for G2[v] and T2_out
2722
+ G2.add_edge(v, mapped[4])
2723
+ assert not _cut_PT(u, v, gparams, sparams)
2724
+
2725
+ # Change the intersection of G1[u] and T1_in
2726
+ G1.add_edge(u, 14)
2727
+ assert _cut_PT(u, v, gparams, sparams)
2728
+
2729
+ # Same for G2[v] and T2_in
2730
+ G2.add_edge(v, mapped[14])
2731
+ assert not _cut_PT(u, v, gparams, sparams)
2732
+
2733
+ # Change the intersection of G2[v] and T2_tilde
2734
+ G2.remove_edge(v, mapped[8])
2735
+ assert _cut_PT(u, v, gparams, sparams)
2736
+
2737
+ # Same for G1[u] and T1_tilde
2738
+ G1.remove_edge(u, 8)
2739
+ assert not _cut_PT(u, v, gparams, sparams)
2740
+
2741
+ # Place 8 and mapped[8] in T1 and T2 respectively, by connecting it to covered nodes
2742
+ G1.add_edge(8, 3)
2743
+ G2.add_edge(mapped[8], mapped[3])
2744
+ sparams.T1.add(8)
2745
+ sparams.T2.add(mapped[8])
2746
+ sparams.T1_tilde.remove(8)
2747
+ sparams.T2_tilde.remove(mapped[8])
2748
+
2749
+ assert not _cut_PT(u, v, gparams, sparams)
2750
+
2751
+ # Remove neighbor of u from T1
2752
+ G1.remove_node(5)
2753
+ l1.pop(5)
2754
+ sparams.T1.remove(5)
2755
+ assert _cut_PT(u, v, gparams, sparams)
2756
+
2757
+ # Same in G2
2758
+ G2.remove_node(mapped[5])
2759
+ l2.pop(mapped[5])
2760
+ sparams.T2.remove(mapped[5])
2761
+ assert not _cut_PT(u, v, gparams, sparams)
2762
+
2763
+ def test_predecessor_T1_in_fail(self):
2764
+ G1 = nx.DiGraph(
2765
+ [(0, 1), (0, 3), (4, 0), (1, 5), (5, 2), (3, 6), (4, 6), (6, 5)]
2766
+ )
2767
+ mapped = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g"}
2768
+ G2 = nx.relabel_nodes(G1, mapped)
2769
+ l1 = {n: "blue" for n in G1.nodes()}
2770
+ l2 = {n: "blue" for n in G2.nodes()}
2771
+
2772
+ gparams = _GraphParameters(
2773
+ G1, G2, l1, l2, nx.utils.groups(l1), nx.utils.groups(l2), None
2774
+ )
2775
+ sparams = _StateParameters(
2776
+ {0: "a", 1: "b", 2: "c"},
2777
+ {"a": 0, "b": 1, "c": 2},
2778
+ {3, 5},
2779
+ {4, 5},
2780
+ {6},
2781
+ None,
2782
+ {"d", "f"},
2783
+ {"f"}, # mapped[4] is missing from T2_in
2784
+ {"g"},
2785
+ None,
2786
+ )
2787
+
2788
+ u, v = 6, "g"
2789
+ assert _cut_PT(u, v, gparams, sparams)
2790
+
2791
+ sparams.T2_in.add("e")
2792
+ assert not _cut_PT(u, v, gparams, sparams)
2793
+
2794
+
2795
+ class TestGraphTinoutUpdating:
2796
+ edges = [
2797
+ (1, 3),
2798
+ (2, 3),
2799
+ (3, 4),
2800
+ (4, 9),
2801
+ (4, 5),
2802
+ (3, 9),
2803
+ (5, 8),
2804
+ (5, 7),
2805
+ (8, 7),
2806
+ (6, 7),
2807
+ ]
2808
+ mapped = {
2809
+ 0: "x",
2810
+ 1: "a",
2811
+ 2: "b",
2812
+ 3: "c",
2813
+ 4: "d",
2814
+ 5: "e",
2815
+ 6: "f",
2816
+ 7: "g",
2817
+ 8: "h",
2818
+ 9: "i",
2819
+ }
2820
+ G1 = nx.Graph()
2821
+ G1.add_edges_from(edges)
2822
+ G1.add_node(0)
2823
+ G2 = nx.relabel_nodes(G1, mapping=mapped)
2824
+
2825
+ def test_updating(self):
2826
+ G2_degree = dict(self.G2.degree)
2827
+ gparams, sparams = _initialize_parameters(self.G1, self.G2, G2_degree)
2828
+ m, m_rev, T1, _, T1_tilde, _, T2, _, T2_tilde, _ = sparams
2829
+
2830
+ # Add node to the mapping
2831
+ m[4] = self.mapped[4]
2832
+ m_rev[self.mapped[4]] = 4
2833
+ _update_Tinout(4, self.mapped[4], gparams, sparams)
2834
+
2835
+ assert T1 == {3, 5, 9}
2836
+ assert T2 == {"c", "i", "e"}
2837
+ assert T1_tilde == {0, 1, 2, 6, 7, 8}
2838
+ assert T2_tilde == {"x", "a", "b", "f", "g", "h"}
2839
+
2840
+ # Add node to the mapping
2841
+ m[5] = self.mapped[5]
2842
+ m_rev.update({self.mapped[5]: 5})
2843
+ _update_Tinout(5, self.mapped[5], gparams, sparams)
2844
+
2845
+ assert T1 == {3, 9, 8, 7}
2846
+ assert T2 == {"c", "i", "h", "g"}
2847
+ assert T1_tilde == {0, 1, 2, 6}
2848
+ assert T2_tilde == {"x", "a", "b", "f"}
2849
+
2850
+ # Add node to the mapping
2851
+ m[6] = self.mapped[6]
2852
+ m_rev.update({self.mapped[6]: 6})
2853
+ _update_Tinout(6, self.mapped[6], gparams, sparams)
2854
+
2855
+ assert T1 == {3, 9, 8, 7}
2856
+ assert T2 == {"c", "i", "h", "g"}
2857
+ assert T1_tilde == {0, 1, 2}
2858
+ assert T2_tilde == {"x", "a", "b"}
2859
+
2860
+ # Add node to the mapping
2861
+ m[3] = self.mapped[3]
2862
+ m_rev.update({self.mapped[3]: 3})
2863
+ _update_Tinout(3, self.mapped[3], gparams, sparams)
2864
+
2865
+ assert T1 == {1, 2, 9, 8, 7}
2866
+ assert T2 == {"a", "b", "i", "h", "g"}
2867
+ assert T1_tilde == {0}
2868
+ assert T2_tilde == {"x"}
2869
+
2870
+ # Add node to the mapping
2871
+ m[0] = self.mapped[0]
2872
+ m_rev.update({self.mapped[0]: 0})
2873
+ _update_Tinout(0, self.mapped[0], gparams, sparams)
2874
+
2875
+ assert T1 == {1, 2, 9, 8, 7}
2876
+ assert T2 == {"a", "b", "i", "h", "g"}
2877
+ assert T1_tilde == set()
2878
+ assert T2_tilde == set()
2879
+
2880
+ def test_restoring(self):
2881
+ m = {0: "x", 3: "c", 4: "d", 5: "e", 6: "f"}
2882
+ m_rev = {"x": 0, "c": 3, "d": 4, "e": 5, "f": 6}
2883
+
2884
+ T1 = {1, 2, 7, 9, 8}
2885
+ T2 = {"a", "b", "g", "i", "h"}
2886
+ T1_tilde = set()
2887
+ T2_tilde = set()
2888
+
2889
+ gparams = _GraphParameters(self.G1, self.G2, {}, {}, {}, {}, {})
2890
+ sparams = _StateParameters(
2891
+ m, m_rev, T1, None, T1_tilde, None, T2, None, T2_tilde, None
2892
+ )
2893
+
2894
+ # Remove a node from the mapping
2895
+ m.pop(0)
2896
+ m_rev.pop("x")
2897
+ _restore_Tinout(0, self.mapped[0], gparams, sparams)
2898
+
2899
+ assert T1 == {1, 2, 7, 9, 8}
2900
+ assert T2 == {"a", "b", "g", "i", "h"}
2901
+ assert T1_tilde == {0}
2902
+ assert T2_tilde == {"x"}
2903
+
2904
+ # Remove a node from the mapping
2905
+ m.pop(6)
2906
+ m_rev.pop("f")
2907
+ _restore_Tinout(6, self.mapped[6], gparams, sparams)
2908
+
2909
+ assert T1 == {1, 2, 7, 9, 8}
2910
+ assert T2 == {"a", "b", "g", "i", "h"}
2911
+ assert T1_tilde == {0, 6}
2912
+ assert T2_tilde == {"x", "f"}
2913
+
2914
+ # Remove a node from the mapping
2915
+ m.pop(3)
2916
+ m_rev.pop("c")
2917
+ _restore_Tinout(3, self.mapped[3], gparams, sparams)
2918
+
2919
+ assert T1 == {7, 9, 8, 3}
2920
+ assert T2 == {"g", "i", "h", "c"}
2921
+ assert T1_tilde == {0, 6, 1, 2}
2922
+ assert T2_tilde == {"x", "f", "a", "b"}
2923
+
2924
+ # Remove a node from the mapping
2925
+ m.pop(5)
2926
+ m_rev.pop("e")
2927
+ _restore_Tinout(5, self.mapped[5], gparams, sparams)
2928
+
2929
+ assert T1 == {9, 3, 5}
2930
+ assert T2 == {"i", "c", "e"}
2931
+ assert T1_tilde == {0, 6, 1, 2, 7, 8}
2932
+ assert T2_tilde == {"x", "f", "a", "b", "g", "h"}
2933
+
2934
+ # Remove a node from the mapping
2935
+ m.pop(4)
2936
+ m_rev.pop("d")
2937
+ _restore_Tinout(4, self.mapped[4], gparams, sparams)
2938
+
2939
+ assert T1 == set()
2940
+ assert T2 == set()
2941
+ assert T1_tilde == set(self.G1.nodes())
2942
+ assert T2_tilde == set(self.G2.nodes())
2943
+
2944
+
2945
+ class TestDiGraphTinoutUpdating:
2946
+ edges = [
2947
+ (1, 3),
2948
+ (3, 2),
2949
+ (3, 4),
2950
+ (4, 9),
2951
+ (4, 5),
2952
+ (3, 9),
2953
+ (5, 8),
2954
+ (5, 7),
2955
+ (8, 7),
2956
+ (7, 6),
2957
+ ]
2958
+ mapped = {
2959
+ 0: "x",
2960
+ 1: "a",
2961
+ 2: "b",
2962
+ 3: "c",
2963
+ 4: "d",
2964
+ 5: "e",
2965
+ 6: "f",
2966
+ 7: "g",
2967
+ 8: "h",
2968
+ 9: "i",
2969
+ }
2970
+ G1 = nx.DiGraph(edges)
2971
+ G1.add_node(0)
2972
+ G2 = nx.relabel_nodes(G1, mapping=mapped)
2973
+
2974
+ def test_updating(self):
2975
+ G2_degree = {
2976
+ n: (in_degree, out_degree)
2977
+ for (n, in_degree), (_, out_degree) in zip(
2978
+ self.G2.in_degree, self.G2.out_degree
2979
+ )
2980
+ }
2981
+ gparams, sparams = _initialize_parameters(self.G1, self.G2, G2_degree)
2982
+ m, m_rev, T1_out, T1_in, T1_tilde, _, T2_out, T2_in, T2_tilde, _ = sparams
2983
+
2984
+ # Add node to the mapping
2985
+ m[4] = self.mapped[4]
2986
+ m_rev[self.mapped[4]] = 4
2987
+ _update_Tinout(4, self.mapped[4], gparams, sparams)
2988
+
2989
+ assert T1_out == {5, 9}
2990
+ assert T1_in == {3}
2991
+ assert T2_out == {"i", "e"}
2992
+ assert T2_in == {"c"}
2993
+ assert T1_tilde == {0, 1, 2, 6, 7, 8}
2994
+ assert T2_tilde == {"x", "a", "b", "f", "g", "h"}
2995
+
2996
+ # Add node to the mapping
2997
+ m[5] = self.mapped[5]
2998
+ m_rev[self.mapped[5]] = 5
2999
+ _update_Tinout(5, self.mapped[5], gparams, sparams)
3000
+
3001
+ assert T1_out == {9, 8, 7}
3002
+ assert T1_in == {3}
3003
+ assert T2_out == {"i", "g", "h"}
3004
+ assert T2_in == {"c"}
3005
+ assert T1_tilde == {0, 1, 2, 6}
3006
+ assert T2_tilde == {"x", "a", "b", "f"}
3007
+
3008
+ # Add node to the mapping
3009
+ m[6] = self.mapped[6]
3010
+ m_rev[self.mapped[6]] = 6
3011
+ _update_Tinout(6, self.mapped[6], gparams, sparams)
3012
+
3013
+ assert T1_out == {9, 8, 7}
3014
+ assert T1_in == {3, 7}
3015
+ assert T2_out == {"i", "g", "h"}
3016
+ assert T2_in == {"c", "g"}
3017
+ assert T1_tilde == {0, 1, 2}
3018
+ assert T2_tilde == {"x", "a", "b"}
3019
+
3020
+ # Add node to the mapping
3021
+ m[3] = self.mapped[3]
3022
+ m_rev[self.mapped[3]] = 3
3023
+ _update_Tinout(3, self.mapped[3], gparams, sparams)
3024
+
3025
+ assert T1_out == {9, 8, 7, 2}
3026
+ assert T1_in == {7, 1}
3027
+ assert T2_out == {"i", "g", "h", "b"}
3028
+ assert T2_in == {"g", "a"}
3029
+ assert T1_tilde == {0}
3030
+ assert T2_tilde == {"x"}
3031
+
3032
+ # Add node to the mapping
3033
+ m[0] = self.mapped[0]
3034
+ m_rev[self.mapped[0]] = 0
3035
+ _update_Tinout(0, self.mapped[0], gparams, sparams)
3036
+
3037
+ assert T1_out == {9, 8, 7, 2}
3038
+ assert T1_in == {7, 1}
3039
+ assert T2_out == {"i", "g", "h", "b"}
3040
+ assert T2_in == {"g", "a"}
3041
+ assert T1_tilde == set()
3042
+ assert T2_tilde == set()
3043
+
3044
+ def test_restoring(self):
3045
+ m = {0: "x", 3: "c", 4: "d", 5: "e", 6: "f"}
3046
+ m_rev = {"x": 0, "c": 3, "d": 4, "e": 5, "f": 6}
3047
+
3048
+ T1_out = {2, 7, 9, 8}
3049
+ T1_in = {1, 7}
3050
+ T2_out = {"b", "g", "i", "h"}
3051
+ T2_in = {"a", "g"}
3052
+ T1_tilde = set()
3053
+ T2_tilde = set()
3054
+
3055
+ gparams = _GraphParameters(self.G1, self.G2, {}, {}, {}, {}, {})
3056
+ sparams = _StateParameters(
3057
+ m, m_rev, T1_out, T1_in, T1_tilde, None, T2_out, T2_in, T2_tilde, None
3058
+ )
3059
+
3060
+ # Remove a node from the mapping
3061
+ m.pop(0)
3062
+ m_rev.pop("x")
3063
+ _restore_Tinout_Di(0, self.mapped[0], gparams, sparams)
3064
+
3065
+ assert T1_out == {2, 7, 9, 8}
3066
+ assert T1_in == {1, 7}
3067
+ assert T2_out == {"b", "g", "i", "h"}
3068
+ assert T2_in == {"a", "g"}
3069
+ assert T1_tilde == {0}
3070
+ assert T2_tilde == {"x"}
3071
+
3072
+ # Remove a node from the mapping
3073
+ m.pop(6)
3074
+ m_rev.pop("f")
3075
+ _restore_Tinout_Di(6, self.mapped[6], gparams, sparams)
3076
+
3077
+ assert T1_out == {2, 9, 8, 7}
3078
+ assert T1_in == {1}
3079
+ assert T2_out == {"b", "i", "h", "g"}
3080
+ assert T2_in == {"a"}
3081
+ assert T1_tilde == {0, 6}
3082
+ assert T2_tilde == {"x", "f"}
3083
+
3084
+ # Remove a node from the mapping
3085
+ m.pop(3)
3086
+ m_rev.pop("c")
3087
+ _restore_Tinout_Di(3, self.mapped[3], gparams, sparams)
3088
+
3089
+ assert T1_out == {9, 8, 7}
3090
+ assert T1_in == {3}
3091
+ assert T2_out == {"i", "h", "g"}
3092
+ assert T2_in == {"c"}
3093
+ assert T1_tilde == {0, 6, 1, 2}
3094
+ assert T2_tilde == {"x", "f", "a", "b"}
3095
+
3096
+ # Remove a node from the mapping
3097
+ m.pop(5)
3098
+ m_rev.pop("e")
3099
+ _restore_Tinout_Di(5, self.mapped[5], gparams, sparams)
3100
+
3101
+ assert T1_out == {9, 5}
3102
+ assert T1_in == {3}
3103
+ assert T2_out == {"i", "e"}
3104
+ assert T2_in == {"c"}
3105
+ assert T1_tilde == {0, 6, 1, 2, 8, 7}
3106
+ assert T2_tilde == {"x", "f", "a", "b", "h", "g"}
3107
+
3108
+ # Remove a node from the mapping
3109
+ m.pop(4)
3110
+ m_rev.pop("d")
3111
+ _restore_Tinout_Di(4, self.mapped[4], gparams, sparams)
3112
+
3113
+ assert T1_out == set()
3114
+ assert T1_in == set()
3115
+ assert T2_out == set()
3116
+ assert T2_in == set()
3117
+ assert T1_tilde == set(self.G1.nodes())
3118
+ assert T2_tilde == set(self.G2.nodes())