okstra 0.130.4 → 0.131.1

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 (365) hide show
  1. package/README.md +0 -1
  2. package/docs/architecture.md +2 -2
  3. package/docs/cli.md +1 -1
  4. package/docs/for-ai/README.md +0 -2
  5. package/docs/for-ai/skills/okstra-run.md +13 -2
  6. package/docs/for-ai/skills/okstra-setup.md +4 -1
  7. package/docs/project-structure-overview.md +5 -4
  8. package/docs/task-process/implementation-planning.md +13 -0
  9. package/docs/task-process/implementation.md +17 -2
  10. package/package.json +1 -1
  11. package/runtime/BUILD.json +2 -2
  12. package/runtime/agents/workers/report-writer-worker.md +7 -0
  13. package/runtime/bin/lib/okstra/usage.sh +4 -4
  14. package/runtime/prompts/lead/okstra-lead-contract.md +2 -2
  15. package/runtime/prompts/lead/plan-body-verification.md +26 -5
  16. package/runtime/prompts/lead/report-writer.md +6 -2
  17. package/runtime/prompts/profiles/_common-contract.md +2 -1
  18. package/runtime/prompts/profiles/_implementation-deliverable.md +6 -1
  19. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  20. package/runtime/prompts/profiles/_implementation-verifier.md +33 -10
  21. package/runtime/prompts/profiles/final-verification.md +21 -6
  22. package/runtime/prompts/profiles/implementation-planning.md +16 -4
  23. package/runtime/python/okstra_ctl/codex_dispatch.py +31 -0
  24. package/runtime/python/okstra_ctl/conformance.py +49 -11
  25. package/runtime/python/okstra_ctl/report_finalize.py +52 -1
  26. package/runtime/python/okstra_ctl/run.py +15 -3
  27. package/runtime/python/okstra_ctl/worker_prompt_contract.py +14 -6
  28. package/runtime/python/okstra_ctl/worker_prompt_policy.py +19 -1
  29. package/runtime/python/okstra_vendor/__init__.py +0 -44
  30. package/runtime/schemas/final-report-v1.0.schema.json +99 -5
  31. package/runtime/skills/okstra-run/SKILL.md +11 -4
  32. package/runtime/skills/okstra-setup/SKILL.md +3 -2
  33. package/runtime/skills/okstra-setup/references/project-config.md +16 -7
  34. package/runtime/templates/reports/final-report.template.md +15 -9
  35. package/runtime/templates/reports/i18n/en.json +1 -0
  36. package/runtime/templates/reports/i18n/ko.json +1 -0
  37. package/runtime/validators/lib/fixtures.sh +6 -0
  38. package/runtime/validators/validate-run.py +2019 -96
  39. package/src/cli-registry.mjs +0 -7
  40. package/src/commands/lifecycle/doctor.mjs +0 -9
  41. package/src/lib/skill-catalog.mjs +0 -1
  42. package/docs/for-ai/skills/okstra-graphify.md +0 -64
  43. package/runtime/python/okstra_ctl/graphify_cmd.py +0 -225
  44. package/runtime/python/okstra_vendor/graphify/.vendored-version +0 -1
  45. package/runtime/python/okstra_vendor/graphify/__init__.py +0 -28
  46. package/runtime/python/okstra_vendor/graphify/__main__.py +0 -1371
  47. package/runtime/python/okstra_vendor/graphify/analyze.py +0 -540
  48. package/runtime/python/okstra_vendor/graphify/benchmark.py +0 -129
  49. package/runtime/python/okstra_vendor/graphify/build.py +0 -107
  50. package/runtime/python/okstra_vendor/graphify/cache.py +0 -169
  51. package/runtime/python/okstra_vendor/graphify/cluster.py +0 -137
  52. package/runtime/python/okstra_vendor/graphify/detect.py +0 -510
  53. package/runtime/python/okstra_vendor/graphify/export.py +0 -1014
  54. package/runtime/python/okstra_vendor/graphify/extract.py +0 -3277
  55. package/runtime/python/okstra_vendor/graphify/hooks.py +0 -220
  56. package/runtime/python/okstra_vendor/graphify/ingest.py +0 -297
  57. package/runtime/python/okstra_vendor/graphify/manifest.py +0 -4
  58. package/runtime/python/okstra_vendor/graphify/report.py +0 -175
  59. package/runtime/python/okstra_vendor/graphify/security.py +0 -203
  60. package/runtime/python/okstra_vendor/graphify/serve.py +0 -373
  61. package/runtime/python/okstra_vendor/graphify/skill-aider.md +0 -1184
  62. package/runtime/python/okstra_vendor/graphify/skill-claw.md +0 -1184
  63. package/runtime/python/okstra_vendor/graphify/skill-codex.md +0 -1242
  64. package/runtime/python/okstra_vendor/graphify/skill-copilot.md +0 -1268
  65. package/runtime/python/okstra_vendor/graphify/skill-droid.md +0 -1239
  66. package/runtime/python/okstra_vendor/graphify/skill-kiro.md +0 -1183
  67. package/runtime/python/okstra_vendor/graphify/skill-opencode.md +0 -1238
  68. package/runtime/python/okstra_vendor/graphify/skill-trae.md +0 -1208
  69. package/runtime/python/okstra_vendor/graphify/skill-vscode.md +0 -253
  70. package/runtime/python/okstra_vendor/graphify/skill-windows.md +0 -1245
  71. package/runtime/python/okstra_vendor/graphify/skill.md +0 -1319
  72. package/runtime/python/okstra_vendor/graphify/transcribe.py +0 -182
  73. package/runtime/python/okstra_vendor/graphify/validate.py +0 -72
  74. package/runtime/python/okstra_vendor/graphify/watch.py +0 -188
  75. package/runtime/python/okstra_vendor/graphify/wiki.py +0 -214
  76. package/runtime/python/okstra_vendor/networkx/__init__.py +0 -62
  77. package/runtime/python/okstra_vendor/networkx/algorithms/__init__.py +0 -134
  78. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/__init__.py +0 -26
  79. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clique.py +0 -259
  80. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clustering_coefficient.py +0 -71
  81. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/connectivity.py +0 -412
  82. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/density.py +0 -396
  83. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/distance_measures.py +0 -150
  84. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/dominating_set.py +0 -149
  85. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/kcomponents.py +0 -369
  86. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/matching.py +0 -44
  87. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/maxcut.py +0 -143
  88. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/ramsey.py +0 -53
  89. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/steinertree.py +0 -265
  90. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/traveling_salesman.py +0 -1508
  91. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/treewidth.py +0 -255
  92. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/vertex_cover.py +0 -83
  93. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/__init__.py +0 -5
  94. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/connectivity.py +0 -122
  95. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/correlation.py +0 -302
  96. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/mixing.py +0 -255
  97. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/neighbor_degree.py +0 -160
  98. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/pairs.py +0 -127
  99. package/runtime/python/okstra_vendor/networkx/algorithms/asteroidal.py +0 -164
  100. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/__init__.py +0 -88
  101. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/basic.py +0 -322
  102. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/centrality.py +0 -290
  103. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/cluster.py +0 -289
  104. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/covering.py +0 -57
  105. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/edgelist.py +0 -360
  106. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/extendability.py +0 -105
  107. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/generators.py +0 -603
  108. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/link_analysis.py +0 -316
  109. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matching.py +0 -590
  110. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matrix.py +0 -232
  111. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/projection.py +0 -526
  112. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/redundancy.py +0 -112
  113. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/spectral.py +0 -69
  114. package/runtime/python/okstra_vendor/networkx/algorithms/boundary.py +0 -168
  115. package/runtime/python/okstra_vendor/networkx/algorithms/bridges.py +0 -205
  116. package/runtime/python/okstra_vendor/networkx/algorithms/broadcasting.py +0 -164
  117. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/__init__.py +0 -20
  118. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness.py +0 -591
  119. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness_subset.py +0 -236
  120. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/closeness.py +0 -282
  121. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness.py +0 -364
  122. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness_subset.py +0 -227
  123. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_closeness.py +0 -96
  124. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/degree_alg.py +0 -150
  125. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/dispersion.py +0 -107
  126. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/eigenvector.py +0 -357
  127. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/flow_matrix.py +0 -130
  128. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/group.py +0 -787
  129. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/harmonic.py +0 -88
  130. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/katz.py +0 -331
  131. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/laplacian.py +0 -150
  132. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/load.py +0 -200
  133. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/percolation.py +0 -128
  134. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/reaching.py +0 -209
  135. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/second_order.py +0 -141
  136. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/subgraph_alg.py +0 -361
  137. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/trophic.py +0 -181
  138. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/voterank_alg.py +0 -95
  139. package/runtime/python/okstra_vendor/networkx/algorithms/chains.py +0 -172
  140. package/runtime/python/okstra_vendor/networkx/algorithms/chordal.py +0 -443
  141. package/runtime/python/okstra_vendor/networkx/algorithms/clique.py +0 -818
  142. package/runtime/python/okstra_vendor/networkx/algorithms/cluster.py +0 -732
  143. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/__init__.py +0 -4
  144. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/equitable_coloring.py +0 -505
  145. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/greedy_coloring.py +0 -565
  146. package/runtime/python/okstra_vendor/networkx/algorithms/communicability_alg.py +0 -163
  147. package/runtime/python/okstra_vendor/networkx/algorithms/community/__init__.py +0 -28
  148. package/runtime/python/okstra_vendor/networkx/algorithms/community/asyn_fluid.py +0 -153
  149. package/runtime/python/okstra_vendor/networkx/algorithms/community/bipartitions.py +0 -354
  150. package/runtime/python/okstra_vendor/networkx/algorithms/community/centrality.py +0 -171
  151. package/runtime/python/okstra_vendor/networkx/algorithms/community/community_utils.py +0 -30
  152. package/runtime/python/okstra_vendor/networkx/algorithms/community/divisive.py +0 -216
  153. package/runtime/python/okstra_vendor/networkx/algorithms/community/kclique.py +0 -79
  154. package/runtime/python/okstra_vendor/networkx/algorithms/community/label_propagation.py +0 -338
  155. package/runtime/python/okstra_vendor/networkx/algorithms/community/leiden.py +0 -162
  156. package/runtime/python/okstra_vendor/networkx/algorithms/community/local.py +0 -220
  157. package/runtime/python/okstra_vendor/networkx/algorithms/community/louvain.py +0 -384
  158. package/runtime/python/okstra_vendor/networkx/algorithms/community/lukes.py +0 -227
  159. package/runtime/python/okstra_vendor/networkx/algorithms/community/modularity_max.py +0 -452
  160. package/runtime/python/okstra_vendor/networkx/algorithms/community/quality.py +0 -347
  161. package/runtime/python/okstra_vendor/networkx/algorithms/components/__init__.py +0 -6
  162. package/runtime/python/okstra_vendor/networkx/algorithms/components/attracting.py +0 -115
  163. package/runtime/python/okstra_vendor/networkx/algorithms/components/biconnected.py +0 -394
  164. package/runtime/python/okstra_vendor/networkx/algorithms/components/connected.py +0 -282
  165. package/runtime/python/okstra_vendor/networkx/algorithms/components/semiconnected.py +0 -71
  166. package/runtime/python/okstra_vendor/networkx/algorithms/components/strongly_connected.py +0 -359
  167. package/runtime/python/okstra_vendor/networkx/algorithms/components/weakly_connected.py +0 -196
  168. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/__init__.py +0 -11
  169. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/connectivity.py +0 -811
  170. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/cuts.py +0 -616
  171. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/disjoint_paths.py +0 -408
  172. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_augmentation.py +0 -1270
  173. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_kcomponents.py +0 -592
  174. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcomponents.py +0 -220
  175. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcutsets.py +0 -235
  176. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/stoerwagner.py +0 -152
  177. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/utils.py +0 -88
  178. package/runtime/python/okstra_vendor/networkx/algorithms/core.py +0 -588
  179. package/runtime/python/okstra_vendor/networkx/algorithms/covering.py +0 -142
  180. package/runtime/python/okstra_vendor/networkx/algorithms/cuts.py +0 -416
  181. package/runtime/python/okstra_vendor/networkx/algorithms/cycles.py +0 -1234
  182. package/runtime/python/okstra_vendor/networkx/algorithms/d_separation.py +0 -677
  183. package/runtime/python/okstra_vendor/networkx/algorithms/dag.py +0 -1392
  184. package/runtime/python/okstra_vendor/networkx/algorithms/distance_measures.py +0 -1095
  185. package/runtime/python/okstra_vendor/networkx/algorithms/distance_regular.py +0 -272
  186. package/runtime/python/okstra_vendor/networkx/algorithms/dominance.py +0 -142
  187. package/runtime/python/okstra_vendor/networkx/algorithms/dominating.py +0 -268
  188. package/runtime/python/okstra_vendor/networkx/algorithms/efficiency_measures.py +0 -167
  189. package/runtime/python/okstra_vendor/networkx/algorithms/euler.py +0 -470
  190. package/runtime/python/okstra_vendor/networkx/algorithms/flow/__init__.py +0 -11
  191. package/runtime/python/okstra_vendor/networkx/algorithms/flow/boykovkolmogorov.py +0 -370
  192. package/runtime/python/okstra_vendor/networkx/algorithms/flow/capacityscaling.py +0 -407
  193. package/runtime/python/okstra_vendor/networkx/algorithms/flow/dinitz_alg.py +0 -238
  194. package/runtime/python/okstra_vendor/networkx/algorithms/flow/edmondskarp.py +0 -241
  195. package/runtime/python/okstra_vendor/networkx/algorithms/flow/gomory_hu.py +0 -178
  196. package/runtime/python/okstra_vendor/networkx/algorithms/flow/maxflow.py +0 -611
  197. package/runtime/python/okstra_vendor/networkx/algorithms/flow/mincost.py +0 -356
  198. package/runtime/python/okstra_vendor/networkx/algorithms/flow/networksimplex.py +0 -662
  199. package/runtime/python/okstra_vendor/networkx/algorithms/flow/preflowpush.py +0 -425
  200. package/runtime/python/okstra_vendor/networkx/algorithms/flow/shortestaugmentingpath.py +0 -300
  201. package/runtime/python/okstra_vendor/networkx/algorithms/flow/utils.py +0 -194
  202. package/runtime/python/okstra_vendor/networkx/algorithms/graph_hashing.py +0 -435
  203. package/runtime/python/okstra_vendor/networkx/algorithms/graphical.py +0 -483
  204. package/runtime/python/okstra_vendor/networkx/algorithms/hierarchy.py +0 -57
  205. package/runtime/python/okstra_vendor/networkx/algorithms/hybrid.py +0 -196
  206. package/runtime/python/okstra_vendor/networkx/algorithms/isolate.py +0 -107
  207. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/__init__.py +0 -7
  208. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/ismags.py +0 -1306
  209. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorph.py +0 -336
  210. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorphvf2.py +0 -1262
  211. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/matchhelpers.py +0 -352
  212. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/temporalisomorphvf2.py +0 -308
  213. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tree_isomorphism.py +0 -264
  214. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2pp.py +0 -1102
  215. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2userfunc.py +0 -192
  216. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/__init__.py +0 -2
  217. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/hits_alg.py +0 -337
  218. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/pagerank_alg.py +0 -498
  219. package/runtime/python/okstra_vendor/networkx/algorithms/link_prediction.py +0 -687
  220. package/runtime/python/okstra_vendor/networkx/algorithms/lowest_common_ancestors.py +0 -280
  221. package/runtime/python/okstra_vendor/networkx/algorithms/matching.py +0 -1148
  222. package/runtime/python/okstra_vendor/networkx/algorithms/minors/__init__.py +0 -27
  223. package/runtime/python/okstra_vendor/networkx/algorithms/minors/contraction.py +0 -738
  224. package/runtime/python/okstra_vendor/networkx/algorithms/mis.py +0 -78
  225. package/runtime/python/okstra_vendor/networkx/algorithms/moral.py +0 -59
  226. package/runtime/python/okstra_vendor/networkx/algorithms/node_classification.py +0 -219
  227. package/runtime/python/okstra_vendor/networkx/algorithms/non_randomness.py +0 -155
  228. package/runtime/python/okstra_vendor/networkx/algorithms/operators/__init__.py +0 -4
  229. package/runtime/python/okstra_vendor/networkx/algorithms/operators/all.py +0 -324
  230. package/runtime/python/okstra_vendor/networkx/algorithms/operators/binary.py +0 -468
  231. package/runtime/python/okstra_vendor/networkx/algorithms/operators/product.py +0 -633
  232. package/runtime/python/okstra_vendor/networkx/algorithms/operators/unary.py +0 -77
  233. package/runtime/python/okstra_vendor/networkx/algorithms/perfect_graph.py +0 -73
  234. package/runtime/python/okstra_vendor/networkx/algorithms/planar_drawing.py +0 -464
  235. package/runtime/python/okstra_vendor/networkx/algorithms/planarity.py +0 -1463
  236. package/runtime/python/okstra_vendor/networkx/algorithms/polynomials.py +0 -306
  237. package/runtime/python/okstra_vendor/networkx/algorithms/reciprocity.py +0 -98
  238. package/runtime/python/okstra_vendor/networkx/algorithms/regular.py +0 -167
  239. package/runtime/python/okstra_vendor/networkx/algorithms/richclub.py +0 -138
  240. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/__init__.py +0 -5
  241. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/astar.py +0 -239
  242. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/dense.py +0 -264
  243. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/generic.py +0 -716
  244. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/unweighted.py +0 -625
  245. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/weighted.py +0 -2542
  246. package/runtime/python/okstra_vendor/networkx/algorithms/similarity.py +0 -2107
  247. package/runtime/python/okstra_vendor/networkx/algorithms/simple_paths.py +0 -966
  248. package/runtime/python/okstra_vendor/networkx/algorithms/smallworld.py +0 -404
  249. package/runtime/python/okstra_vendor/networkx/algorithms/smetric.py +0 -30
  250. package/runtime/python/okstra_vendor/networkx/algorithms/sparsifiers.py +0 -296
  251. package/runtime/python/okstra_vendor/networkx/algorithms/structuralholes.py +0 -374
  252. package/runtime/python/okstra_vendor/networkx/algorithms/summarization.py +0 -564
  253. package/runtime/python/okstra_vendor/networkx/algorithms/swap.py +0 -406
  254. package/runtime/python/okstra_vendor/networkx/algorithms/threshold.py +0 -981
  255. package/runtime/python/okstra_vendor/networkx/algorithms/time_dependent.py +0 -142
  256. package/runtime/python/okstra_vendor/networkx/algorithms/tournament.py +0 -406
  257. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/__init__.py +0 -5
  258. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/beamsearch.py +0 -90
  259. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/breadth_first_search.py +0 -576
  260. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/depth_first_search.py +0 -529
  261. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgebfs.py +0 -185
  262. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgedfs.py +0 -182
  263. package/runtime/python/okstra_vendor/networkx/algorithms/tree/__init__.py +0 -7
  264. package/runtime/python/okstra_vendor/networkx/algorithms/tree/branchings.py +0 -1042
  265. package/runtime/python/okstra_vendor/networkx/algorithms/tree/coding.py +0 -413
  266. package/runtime/python/okstra_vendor/networkx/algorithms/tree/decomposition.py +0 -88
  267. package/runtime/python/okstra_vendor/networkx/algorithms/tree/distance_measures.py +0 -219
  268. package/runtime/python/okstra_vendor/networkx/algorithms/tree/mst.py +0 -1281
  269. package/runtime/python/okstra_vendor/networkx/algorithms/tree/operations.py +0 -106
  270. package/runtime/python/okstra_vendor/networkx/algorithms/tree/recognition.py +0 -273
  271. package/runtime/python/okstra_vendor/networkx/algorithms/triads.py +0 -500
  272. package/runtime/python/okstra_vendor/networkx/algorithms/vitality.py +0 -76
  273. package/runtime/python/okstra_vendor/networkx/algorithms/voronoi.py +0 -86
  274. package/runtime/python/okstra_vendor/networkx/algorithms/walks.py +0 -77
  275. package/runtime/python/okstra_vendor/networkx/algorithms/wiener.py +0 -278
  276. package/runtime/python/okstra_vendor/networkx/classes/__init__.py +0 -13
  277. package/runtime/python/okstra_vendor/networkx/classes/coreviews.py +0 -435
  278. package/runtime/python/okstra_vendor/networkx/classes/digraph.py +0 -1363
  279. package/runtime/python/okstra_vendor/networkx/classes/filters.py +0 -95
  280. package/runtime/python/okstra_vendor/networkx/classes/function.py +0 -1549
  281. package/runtime/python/okstra_vendor/networkx/classes/graph.py +0 -2082
  282. package/runtime/python/okstra_vendor/networkx/classes/graphviews.py +0 -269
  283. package/runtime/python/okstra_vendor/networkx/classes/multidigraph.py +0 -977
  284. package/runtime/python/okstra_vendor/networkx/classes/multigraph.py +0 -1294
  285. package/runtime/python/okstra_vendor/networkx/classes/reportviews.py +0 -1447
  286. package/runtime/python/okstra_vendor/networkx/convert.py +0 -502
  287. package/runtime/python/okstra_vendor/networkx/convert_matrix.py +0 -1314
  288. package/runtime/python/okstra_vendor/networkx/drawing/__init__.py +0 -7
  289. package/runtime/python/okstra_vendor/networkx/drawing/layout.py +0 -2036
  290. package/runtime/python/okstra_vendor/networkx/drawing/nx_agraph.py +0 -470
  291. package/runtime/python/okstra_vendor/networkx/drawing/nx_latex.py +0 -570
  292. package/runtime/python/okstra_vendor/networkx/drawing/nx_pydot.py +0 -361
  293. package/runtime/python/okstra_vendor/networkx/drawing/nx_pylab.py +0 -2978
  294. package/runtime/python/okstra_vendor/networkx/exception.py +0 -131
  295. package/runtime/python/okstra_vendor/networkx/generators/__init__.py +0 -34
  296. package/runtime/python/okstra_vendor/networkx/generators/atlas.dat.gz +0 -0
  297. package/runtime/python/okstra_vendor/networkx/generators/atlas.py +0 -227
  298. package/runtime/python/okstra_vendor/networkx/generators/classic.py +0 -1091
  299. package/runtime/python/okstra_vendor/networkx/generators/cographs.py +0 -68
  300. package/runtime/python/okstra_vendor/networkx/generators/community.py +0 -1070
  301. package/runtime/python/okstra_vendor/networkx/generators/degree_seq.py +0 -886
  302. package/runtime/python/okstra_vendor/networkx/generators/directed.py +0 -572
  303. package/runtime/python/okstra_vendor/networkx/generators/duplication.py +0 -174
  304. package/runtime/python/okstra_vendor/networkx/generators/ego.py +0 -66
  305. package/runtime/python/okstra_vendor/networkx/generators/expanders.py +0 -499
  306. package/runtime/python/okstra_vendor/networkx/generators/geometric.py +0 -1037
  307. package/runtime/python/okstra_vendor/networkx/generators/harary_graph.py +0 -163
  308. package/runtime/python/okstra_vendor/networkx/generators/internet_as_graphs.py +0 -443
  309. package/runtime/python/okstra_vendor/networkx/generators/intersection.py +0 -125
  310. package/runtime/python/okstra_vendor/networkx/generators/interval_graph.py +0 -70
  311. package/runtime/python/okstra_vendor/networkx/generators/joint_degree_seq.py +0 -664
  312. package/runtime/python/okstra_vendor/networkx/generators/lattice.py +0 -405
  313. package/runtime/python/okstra_vendor/networkx/generators/line.py +0 -501
  314. package/runtime/python/okstra_vendor/networkx/generators/mycielski.py +0 -110
  315. package/runtime/python/okstra_vendor/networkx/generators/nonisomorphic_trees.py +0 -259
  316. package/runtime/python/okstra_vendor/networkx/generators/random_clustered.py +0 -117
  317. package/runtime/python/okstra_vendor/networkx/generators/random_graphs.py +0 -1416
  318. package/runtime/python/okstra_vendor/networkx/generators/small.py +0 -1070
  319. package/runtime/python/okstra_vendor/networkx/generators/social.py +0 -554
  320. package/runtime/python/okstra_vendor/networkx/generators/spectral_graph_forge.py +0 -120
  321. package/runtime/python/okstra_vendor/networkx/generators/stochastic.py +0 -54
  322. package/runtime/python/okstra_vendor/networkx/generators/sudoku.py +0 -131
  323. package/runtime/python/okstra_vendor/networkx/generators/time_series.py +0 -74
  324. package/runtime/python/okstra_vendor/networkx/generators/trees.py +0 -1070
  325. package/runtime/python/okstra_vendor/networkx/generators/triads.py +0 -94
  326. package/runtime/python/okstra_vendor/networkx/lazy_imports.py +0 -188
  327. package/runtime/python/okstra_vendor/networkx/linalg/__init__.py +0 -13
  328. package/runtime/python/okstra_vendor/networkx/linalg/algebraicconnectivity.py +0 -650
  329. package/runtime/python/okstra_vendor/networkx/linalg/attrmatrix.py +0 -466
  330. package/runtime/python/okstra_vendor/networkx/linalg/bethehessianmatrix.py +0 -77
  331. package/runtime/python/okstra_vendor/networkx/linalg/graphmatrix.py +0 -168
  332. package/runtime/python/okstra_vendor/networkx/linalg/laplacianmatrix.py +0 -512
  333. package/runtime/python/okstra_vendor/networkx/linalg/modularitymatrix.py +0 -166
  334. package/runtime/python/okstra_vendor/networkx/linalg/spectrum.py +0 -186
  335. package/runtime/python/okstra_vendor/networkx/readwrite/__init__.py +0 -17
  336. package/runtime/python/okstra_vendor/networkx/readwrite/adjlist.py +0 -330
  337. package/runtime/python/okstra_vendor/networkx/readwrite/edgelist.py +0 -489
  338. package/runtime/python/okstra_vendor/networkx/readwrite/gexf.py +0 -1084
  339. package/runtime/python/okstra_vendor/networkx/readwrite/gml.py +0 -879
  340. package/runtime/python/okstra_vendor/networkx/readwrite/graph6.py +0 -427
  341. package/runtime/python/okstra_vendor/networkx/readwrite/graphml.py +0 -1053
  342. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/__init__.py +0 -19
  343. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/adjacency.py +0 -156
  344. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/cytoscape.py +0 -190
  345. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/node_link.py +0 -261
  346. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tree.py +0 -137
  347. package/runtime/python/okstra_vendor/networkx/readwrite/leda.py +0 -108
  348. package/runtime/python/okstra_vendor/networkx/readwrite/multiline_adjlist.py +0 -393
  349. package/runtime/python/okstra_vendor/networkx/readwrite/p2g.py +0 -113
  350. package/runtime/python/okstra_vendor/networkx/readwrite/pajek.py +0 -286
  351. package/runtime/python/okstra_vendor/networkx/readwrite/sparse6.py +0 -379
  352. package/runtime/python/okstra_vendor/networkx/readwrite/text.py +0 -851
  353. package/runtime/python/okstra_vendor/networkx/relabel.py +0 -285
  354. package/runtime/python/okstra_vendor/networkx/utils/__init__.py +0 -8
  355. package/runtime/python/okstra_vendor/networkx/utils/backends.py +0 -2171
  356. package/runtime/python/okstra_vendor/networkx/utils/configs.py +0 -396
  357. package/runtime/python/okstra_vendor/networkx/utils/decorators.py +0 -1233
  358. package/runtime/python/okstra_vendor/networkx/utils/heaps.py +0 -338
  359. package/runtime/python/okstra_vendor/networkx/utils/mapped_queue.py +0 -297
  360. package/runtime/python/okstra_vendor/networkx/utils/misc.py +0 -703
  361. package/runtime/python/okstra_vendor/networkx/utils/random_sequence.py +0 -198
  362. package/runtime/python/okstra_vendor/networkx/utils/rcm.py +0 -159
  363. package/runtime/python/okstra_vendor/networkx/utils/union_find.py +0 -106
  364. package/runtime/skills/okstra-graphify/SKILL.md +0 -169
  365. package/src/commands/graphify.mjs +0 -32
@@ -1,1102 +0,0 @@
1
- """
2
- ***************
3
- VF2++ Algorithm
4
- ***************
5
-
6
- An implementation of the VF2++ algorithm [1]_ for Graph Isomorphism testing.
7
-
8
- The simplest interface to use this module is to call:
9
-
10
- `vf2pp_is_isomorphic`: to check whether two graphs are isomorphic.
11
- `vf2pp_isomorphism`: to obtain the node mapping between two graphs,
12
- in case they are isomorphic.
13
- `vf2pp_all_isomorphisms`: to generate all possible mappings between two graphs,
14
- if isomorphic.
15
-
16
- Introduction
17
- ------------
18
- The VF2++ algorithm, follows a similar logic to that of VF2, while also
19
- introducing new easy-to-check cutting rules and determining the optimal access
20
- order of nodes. It is also implemented in a non-recursive manner, which saves
21
- both time and space, when compared to its previous counterpart.
22
-
23
- The optimal node ordering is obtained after taking into consideration both the
24
- degree but also the label rarity of each node.
25
- This way we place the nodes that are more likely to match, first in the order,
26
- thus examining the most promising branches in the beginning.
27
- The rules also consider node labels, making it easier to prune unfruitful
28
- branches early in the process.
29
-
30
- Examples
31
- --------
32
-
33
- Suppose G1 and G2 are Isomorphic Graphs. Verification is as follows:
34
-
35
- Without node labels:
36
-
37
- >>> import networkx as nx
38
- >>> G1 = nx.path_graph(4)
39
- >>> G2 = nx.path_graph(4)
40
- >>> nx.vf2pp_is_isomorphic(G1, G2, node_label=None)
41
- True
42
- >>> nx.vf2pp_isomorphism(G1, G2, node_label=None)
43
- {1: 1, 2: 2, 0: 0, 3: 3}
44
-
45
- With node labels:
46
-
47
- >>> G1 = nx.path_graph(4)
48
- >>> G2 = nx.path_graph(4)
49
- >>> mapped = {1: 1, 2: 2, 3: 3, 0: 0}
50
- >>> nx.set_node_attributes(
51
- ... G1, dict(zip(G1, ["blue", "red", "green", "yellow"])), "label"
52
- ... )
53
- >>> nx.set_node_attributes(
54
- ... G2,
55
- ... dict(zip([mapped[u] for u in G1], ["blue", "red", "green", "yellow"])),
56
- ... "label",
57
- ... )
58
- >>> nx.vf2pp_is_isomorphic(G1, G2, node_label="label")
59
- True
60
- >>> nx.vf2pp_isomorphism(G1, G2, node_label="label")
61
- {1: 1, 2: 2, 0: 0, 3: 3}
62
-
63
- References
64
- ----------
65
- .. [1] Jüttner, Alpár & Madarasi, Péter. (2018). "VF2++—An improved subgraph
66
- isomorphism algorithm". Discrete Applied Mathematics. 242.
67
- https://doi.org/10.1016/j.dam.2018.02.018
68
-
69
- """
70
-
71
- import collections
72
-
73
- import networkx as nx
74
-
75
- __all__ = ["vf2pp_isomorphism", "vf2pp_is_isomorphic", "vf2pp_all_isomorphisms"]
76
-
77
- _GraphParameters = collections.namedtuple(
78
- "_GraphParameters",
79
- [
80
- "G1",
81
- "G2",
82
- "G1_labels",
83
- "G2_labels",
84
- "nodes_of_G1Labels",
85
- "nodes_of_G2Labels",
86
- "G2_nodes_of_degree",
87
- ],
88
- )
89
-
90
- _StateParameters = collections.namedtuple(
91
- "_StateParameters",
92
- [
93
- "mapping",
94
- "reverse_mapping",
95
- "T1",
96
- "T1_in",
97
- "T1_tilde",
98
- "T1_tilde_in",
99
- "T2",
100
- "T2_in",
101
- "T2_tilde",
102
- "T2_tilde_in",
103
- ],
104
- )
105
-
106
-
107
- @nx._dispatchable(graphs={"G1": 0, "G2": 1}, node_attrs={"node_label": "default_label"})
108
- def vf2pp_isomorphism(G1, G2, node_label=None, default_label=None):
109
- """Return an isomorphic mapping between `G1` and `G2` if it exists.
110
-
111
- Parameters
112
- ----------
113
- G1, G2 : NetworkX Graph or MultiGraph instances.
114
- The two graphs to check for isomorphism.
115
-
116
- node_label : str, optional
117
- The name of the node attribute to be used when comparing nodes.
118
- The default is `None`, meaning node attributes are not considered
119
- in the comparison. Any node that doesn't have the `node_label`
120
- attribute uses `default_label` instead.
121
-
122
- default_label : scalar
123
- Default value to use when a node doesn't have an attribute
124
- named `node_label`. Default is `None`.
125
-
126
- Returns
127
- -------
128
- dict or None
129
- Node mapping if the two graphs are isomorphic. None otherwise.
130
- """
131
- try:
132
- mapping = next(vf2pp_all_isomorphisms(G1, G2, node_label, default_label))
133
- return mapping
134
- except StopIteration:
135
- return None
136
-
137
-
138
- @nx._dispatchable(graphs={"G1": 0, "G2": 1}, node_attrs={"node_label": "default_label"})
139
- def vf2pp_is_isomorphic(G1, G2, node_label=None, default_label=None):
140
- """Examines whether G1 and G2 are isomorphic.
141
-
142
- Parameters
143
- ----------
144
- G1, G2 : NetworkX Graph or MultiGraph instances.
145
- The two graphs to check for isomorphism.
146
-
147
- node_label : str, optional
148
- The name of the node attribute to be used when comparing nodes.
149
- The default is `None`, meaning node attributes are not considered
150
- in the comparison. Any node that doesn't have the `node_label`
151
- attribute uses `default_label` instead.
152
-
153
- default_label : scalar
154
- Default value to use when a node doesn't have an attribute
155
- named `node_label`. Default is `None`.
156
-
157
- Returns
158
- -------
159
- bool
160
- True if the two graphs are isomorphic, False otherwise.
161
- """
162
- if vf2pp_isomorphism(G1, G2, node_label, default_label) is not None:
163
- return True
164
- return False
165
-
166
-
167
- @nx._dispatchable(graphs={"G1": 0, "G2": 1}, node_attrs={"node_label": "default_label"})
168
- def vf2pp_all_isomorphisms(G1, G2, node_label=None, default_label=None):
169
- """Yields all the possible mappings between G1 and G2.
170
-
171
- Parameters
172
- ----------
173
- G1, G2 : NetworkX Graph or MultiGraph instances.
174
- The two graphs to check for isomorphism.
175
-
176
- node_label : str, optional
177
- The name of the node attribute to be used when comparing nodes.
178
- The default is `None`, meaning node attributes are not considered
179
- in the comparison. Any node that doesn't have the `node_label`
180
- attribute uses `default_label` instead.
181
-
182
- default_label : scalar
183
- Default value to use when a node doesn't have an attribute
184
- named `node_label`. Default is `None`.
185
-
186
- Yields
187
- ------
188
- dict
189
- Isomorphic mapping between the nodes in `G1` and `G2`.
190
- """
191
- if G1.number_of_nodes() == 0 or G2.number_of_nodes() == 0:
192
- return False
193
-
194
- # Create the degree dicts based on graph type
195
- if G1.is_directed():
196
- G1_degree = {
197
- n: (in_degree, out_degree)
198
- for (n, in_degree), (_, out_degree) in zip(G1.in_degree, G1.out_degree)
199
- }
200
- G2_degree = {
201
- n: (in_degree, out_degree)
202
- for (n, in_degree), (_, out_degree) in zip(G2.in_degree, G2.out_degree)
203
- }
204
- else:
205
- G1_degree = dict(G1.degree)
206
- G2_degree = dict(G2.degree)
207
-
208
- if not G1.is_directed():
209
- find_candidates = _find_candidates
210
- restore_Tinout = _restore_Tinout
211
- else:
212
- find_candidates = _find_candidates_Di
213
- restore_Tinout = _restore_Tinout_Di
214
-
215
- # Check that both graphs have the same number of nodes and degree sequence
216
- if G1.order() != G2.order():
217
- return False
218
- if sorted(G1_degree.values()) != sorted(G2_degree.values()):
219
- return False
220
-
221
- # Initialize parameters and cache necessary information about degree and labels
222
- graph_params, state_params = _initialize_parameters(
223
- G1, G2, G2_degree, node_label, default_label
224
- )
225
-
226
- # Check if G1 and G2 have the same labels, and that number of nodes per label
227
- # is equal between the two graphs
228
- if not _precheck_label_properties(graph_params):
229
- return False
230
-
231
- # Calculate the optimal node ordering
232
- node_order = _matching_order(graph_params)
233
-
234
- # Initialize the stack
235
- stack = []
236
- candidates = iter(
237
- find_candidates(node_order[0], graph_params, state_params, G1_degree)
238
- )
239
- stack.append((node_order[0], candidates))
240
-
241
- mapping = state_params.mapping
242
- reverse_mapping = state_params.reverse_mapping
243
-
244
- # Index of the node from the order, currently being examined
245
- matching_node = 1
246
-
247
- while stack:
248
- current_node, candidate_nodes = stack[-1]
249
-
250
- try:
251
- candidate = next(candidate_nodes)
252
- except StopIteration:
253
- # If no remaining candidates, return to a previous state, and follow another branch
254
- stack.pop()
255
- matching_node -= 1
256
- if stack:
257
- # Pop the previously added u-v pair, and look for a different candidate _v for u
258
- popped_node1, _ = stack[-1]
259
- popped_node2 = mapping[popped_node1]
260
- mapping.pop(popped_node1)
261
- reverse_mapping.pop(popped_node2)
262
- restore_Tinout(popped_node1, popped_node2, graph_params, state_params)
263
- continue
264
-
265
- if _feasibility(current_node, candidate, graph_params, state_params):
266
- # Terminate if mapping is extended to its full
267
- if len(mapping) == G2.number_of_nodes() - 1:
268
- cp_mapping = mapping.copy()
269
- cp_mapping[current_node] = candidate
270
- yield cp_mapping
271
- continue
272
-
273
- # Feasibility rules pass, so extend the mapping and update the parameters
274
- mapping[current_node] = candidate
275
- reverse_mapping[candidate] = current_node
276
- _update_Tinout(current_node, candidate, graph_params, state_params)
277
- # Append the next node and its candidates to the stack
278
- candidates = iter(
279
- find_candidates(
280
- node_order[matching_node], graph_params, state_params, G1_degree
281
- )
282
- )
283
- stack.append((node_order[matching_node], candidates))
284
- matching_node += 1
285
-
286
-
287
- def _precheck_label_properties(graph_params):
288
- G1, G2, G1_labels, G2_labels, nodes_of_G1Labels, nodes_of_G2Labels, _ = graph_params
289
- if any(
290
- label not in nodes_of_G1Labels or len(nodes_of_G1Labels[label]) != len(nodes)
291
- for label, nodes in nodes_of_G2Labels.items()
292
- ):
293
- return False
294
- return True
295
-
296
-
297
- def _initialize_parameters(G1, G2, G2_degree, node_label=None, default_label=-1):
298
- """Initializes all the necessary parameters for VF2++
299
-
300
- Parameters
301
- ----------
302
- G1,G2: NetworkX Graph or MultiGraph instances.
303
- The two graphs to check for isomorphism or monomorphism
304
-
305
- G1_labels,G2_labels: dict
306
- The label of every node in G1 and G2 respectively
307
-
308
- Returns
309
- -------
310
- graph_params: namedtuple
311
- Contains all the Graph-related parameters:
312
-
313
- G1,G2
314
- G1_labels,G2_labels: dict
315
-
316
- state_params: namedtuple
317
- Contains all the State-related parameters:
318
-
319
- mapping: dict
320
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
321
-
322
- reverse_mapping: dict
323
- The reverse mapping as extended so far. Maps nodes from G2 to nodes of G1.
324
- It's basically "mapping" reversed
325
-
326
- T1, T2: set
327
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
328
- that are not in the mapping, but are neighbors of nodes that are.
329
-
330
- T1_out, T2_out: set
331
- Ti_out contains all the nodes from Gi, that are neither in the mapping
332
- nor in Ti
333
- """
334
- G1_labels = dict(G1.nodes(data=node_label, default=default_label))
335
- G2_labels = dict(G2.nodes(data=node_label, default=default_label))
336
-
337
- graph_params = _GraphParameters(
338
- G1,
339
- G2,
340
- G1_labels,
341
- G2_labels,
342
- nx.utils.groups(G1_labels),
343
- nx.utils.groups(G2_labels),
344
- nx.utils.groups(G2_degree),
345
- )
346
-
347
- T1, T1_in = set(), set()
348
- T2, T2_in = set(), set()
349
- if G1.is_directed():
350
- T1_tilde, T1_tilde_in = (
351
- set(G1.nodes()),
352
- set(),
353
- ) # todo: do we need Ti_tilde_in? What nodes does it have?
354
- T2_tilde, T2_tilde_in = set(G2.nodes()), set()
355
- else:
356
- T1_tilde, T1_tilde_in = set(G1.nodes()), set()
357
- T2_tilde, T2_tilde_in = set(G2.nodes()), set()
358
-
359
- state_params = _StateParameters(
360
- {},
361
- {},
362
- T1,
363
- T1_in,
364
- T1_tilde,
365
- T1_tilde_in,
366
- T2,
367
- T2_in,
368
- T2_tilde,
369
- T2_tilde_in,
370
- )
371
-
372
- return graph_params, state_params
373
-
374
-
375
- def _matching_order(graph_params):
376
- """The node ordering as introduced in VF2++.
377
-
378
- Notes
379
- -----
380
- Taking into account the structure of the Graph and the node labeling, the
381
- nodes are placed in an order such that, most of the unfruitful/infeasible
382
- branches of the search space can be pruned on high levels, significantly
383
- decreasing the number of visited states. The premise is that, the algorithm
384
- will be able to recognize inconsistencies early, proceeding to go deep into
385
- the search tree only if it's needed.
386
-
387
- Parameters
388
- ----------
389
- graph_params: namedtuple
390
- Contains:
391
-
392
- G1,G2: NetworkX Graph or MultiGraph instances.
393
- The two graphs to check for isomorphism or monomorphism.
394
-
395
- G1_labels,G2_labels: dict
396
- The label of every node in G1 and G2 respectively.
397
-
398
- Returns
399
- -------
400
- node_order: list
401
- The ordering of the nodes.
402
- """
403
- G1, G2, G1_labels, _, _, nodes_of_G2Labels, _ = graph_params
404
- if not G1 and not G2:
405
- return {}
406
-
407
- if G1.is_directed():
408
- G1 = G1.to_undirected(as_view=True)
409
-
410
- V1_unordered = set(G1.nodes())
411
- label_rarity = {label: len(nodes) for label, nodes in nodes_of_G2Labels.items()}
412
- used_degrees = {node: 0 for node in G1}
413
- node_order = []
414
-
415
- while V1_unordered:
416
- max_rarity = min(label_rarity[G1_labels[x]] for x in V1_unordered)
417
- rarest_nodes = [
418
- n for n in V1_unordered if label_rarity[G1_labels[n]] == max_rarity
419
- ]
420
- max_node = max(rarest_nodes, key=G1.degree)
421
-
422
- for dlevel_nodes in nx.bfs_layers(G1, max_node):
423
- nodes_to_add = dlevel_nodes.copy()
424
- while nodes_to_add:
425
- max_used_degree = max(used_degrees[n] for n in nodes_to_add)
426
- max_used_degree_nodes = [
427
- n for n in nodes_to_add if used_degrees[n] == max_used_degree
428
- ]
429
- max_degree = max(G1.degree[n] for n in max_used_degree_nodes)
430
- max_degree_nodes = [
431
- n for n in max_used_degree_nodes if G1.degree[n] == max_degree
432
- ]
433
- next_node = min(
434
- max_degree_nodes, key=lambda x: label_rarity[G1_labels[x]]
435
- )
436
-
437
- node_order.append(next_node)
438
- for node in G1.neighbors(next_node):
439
- used_degrees[node] += 1
440
-
441
- nodes_to_add.remove(next_node)
442
- label_rarity[G1_labels[next_node]] -= 1
443
- V1_unordered.discard(next_node)
444
-
445
- return node_order
446
-
447
-
448
- def _find_candidates(
449
- u, graph_params, state_params, G1_degree
450
- ): # todo: make the 4th argument the degree of u
451
- """Given node u of G1, finds the candidates of u from G2.
452
-
453
- Parameters
454
- ----------
455
- u: Graph node
456
- The node from G1 for which to find the candidates from G2.
457
-
458
- graph_params: namedtuple
459
- Contains all the Graph-related parameters:
460
-
461
- G1,G2: NetworkX Graph or MultiGraph instances.
462
- The two graphs to check for isomorphism or monomorphism
463
-
464
- G1_labels,G2_labels: dict
465
- The label of every node in G1 and G2 respectively
466
-
467
- state_params: namedtuple
468
- Contains all the State-related parameters:
469
-
470
- mapping: dict
471
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
472
-
473
- reverse_mapping: dict
474
- The reverse mapping as extended so far. Maps nodes from G2 to nodes
475
- of G1. It's basically "mapping" reversed
476
-
477
- T1, T2: set
478
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
479
- that are not in the mapping, but are neighbors of nodes that are.
480
-
481
- T1_tilde, T2_tilde: set
482
- Ti_tilde contains all the nodes from Gi, that are neither in the
483
- mapping nor in Ti
484
-
485
- Returns
486
- -------
487
- candidates: set
488
- The nodes from G2 which are candidates for u.
489
- """
490
- G1, G2, G1_labels, _, _, nodes_of_G2Labels, G2_nodes_of_degree = graph_params
491
- mapping, reverse_mapping, _, _, _, _, _, _, T2_tilde, _ = state_params
492
-
493
- covered_nbrs = [nbr for nbr in G1[u] if nbr in mapping]
494
- if not covered_nbrs:
495
- candidates = set(nodes_of_G2Labels[G1_labels[u]])
496
- candidates.intersection_update(G2_nodes_of_degree[G1_degree[u]])
497
- candidates.intersection_update(T2_tilde)
498
- candidates.difference_update(reverse_mapping)
499
- if G1.is_multigraph():
500
- candidates.difference_update(
501
- {
502
- node
503
- for node in candidates
504
- if G1.number_of_edges(u, u) != G2.number_of_edges(node, node)
505
- }
506
- )
507
- return candidates
508
-
509
- nbr1 = covered_nbrs[0]
510
- common_nodes = set(G2[mapping[nbr1]])
511
-
512
- for nbr1 in covered_nbrs[1:]:
513
- common_nodes.intersection_update(G2[mapping[nbr1]])
514
-
515
- common_nodes.difference_update(reverse_mapping)
516
- common_nodes.intersection_update(G2_nodes_of_degree[G1_degree[u]])
517
- common_nodes.intersection_update(nodes_of_G2Labels[G1_labels[u]])
518
- if G1.is_multigraph():
519
- common_nodes.difference_update(
520
- {
521
- node
522
- for node in common_nodes
523
- if G1.number_of_edges(u, u) != G2.number_of_edges(node, node)
524
- }
525
- )
526
- return common_nodes
527
-
528
-
529
- def _find_candidates_Di(u, graph_params, state_params, G1_degree):
530
- G1, G2, G1_labels, _, _, nodes_of_G2Labels, G2_nodes_of_degree = graph_params
531
- mapping, reverse_mapping, _, _, _, _, _, _, T2_tilde, _ = state_params
532
-
533
- covered_successors = [succ for succ in G1[u] if succ in mapping]
534
- covered_predecessors = [pred for pred in G1.pred[u] if pred in mapping]
535
-
536
- if not (covered_successors or covered_predecessors):
537
- candidates = set(nodes_of_G2Labels[G1_labels[u]])
538
- candidates.intersection_update(G2_nodes_of_degree[G1_degree[u]])
539
- candidates.intersection_update(T2_tilde)
540
- candidates.difference_update(reverse_mapping)
541
- if G1.is_multigraph():
542
- candidates.difference_update(
543
- {
544
- node
545
- for node in candidates
546
- if G1.number_of_edges(u, u) != G2.number_of_edges(node, node)
547
- }
548
- )
549
- return candidates
550
-
551
- if covered_successors:
552
- succ1 = covered_successors[0]
553
- common_nodes = set(G2.pred[mapping[succ1]])
554
-
555
- for succ1 in covered_successors[1:]:
556
- common_nodes.intersection_update(G2.pred[mapping[succ1]])
557
- else:
558
- pred1 = covered_predecessors.pop()
559
- common_nodes = set(G2[mapping[pred1]])
560
-
561
- for pred1 in covered_predecessors:
562
- common_nodes.intersection_update(G2[mapping[pred1]])
563
-
564
- common_nodes.difference_update(reverse_mapping)
565
- common_nodes.intersection_update(G2_nodes_of_degree[G1_degree[u]])
566
- common_nodes.intersection_update(nodes_of_G2Labels[G1_labels[u]])
567
- if G1.is_multigraph():
568
- common_nodes.difference_update(
569
- {
570
- node
571
- for node in common_nodes
572
- if G1.number_of_edges(u, u) != G2.number_of_edges(node, node)
573
- }
574
- )
575
- return common_nodes
576
-
577
-
578
- def _feasibility(node1, node2, graph_params, state_params):
579
- """Given a candidate pair of nodes u and v from G1 and G2 respectively,
580
- checks if it's feasible to extend the mapping, i.e. if u and v can be matched.
581
-
582
- Notes
583
- -----
584
- This function performs all the necessary checking by applying both consistency
585
- and cutting rules.
586
-
587
- Parameters
588
- ----------
589
- node1, node2: Graph node
590
- The candidate pair of nodes being checked for matching
591
-
592
- graph_params: namedtuple
593
- Contains all the Graph-related parameters:
594
-
595
- G1,G2: NetworkX Graph or MultiGraph instances.
596
- The two graphs to check for isomorphism or monomorphism
597
-
598
- G1_labels,G2_labels: dict
599
- The label of every node in G1 and G2 respectively
600
-
601
- state_params: namedtuple
602
- Contains all the State-related parameters:
603
-
604
- mapping: dict
605
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
606
-
607
- reverse_mapping: dict
608
- The reverse mapping as extended so far. Maps nodes from G2 to nodes
609
- of G1. It's basically "mapping" reversed
610
-
611
- T1, T2: set
612
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
613
- that are not in the mapping, but are neighbors of nodes that are.
614
-
615
- T1_out, T2_out: set
616
- Ti_out contains all the nodes from Gi, that are neither in the mapping
617
- nor in Ti
618
-
619
- Returns
620
- -------
621
- True if all checks are successful, False otherwise.
622
- """
623
- G1 = graph_params.G1
624
-
625
- if _cut_PT(node1, node2, graph_params, state_params):
626
- return False
627
-
628
- if G1.is_multigraph():
629
- if not _consistent_PT(node1, node2, graph_params, state_params):
630
- return False
631
-
632
- return True
633
-
634
-
635
- def _cut_PT(u, v, graph_params, state_params):
636
- """Implements the cutting rules for the ISO problem.
637
-
638
- Parameters
639
- ----------
640
- u, v: Graph node
641
- The two candidate nodes being examined.
642
-
643
- graph_params: namedtuple
644
- Contains all the Graph-related parameters:
645
-
646
- G1,G2: NetworkX Graph or MultiGraph instances.
647
- The two graphs to check for isomorphism or monomorphism
648
-
649
- G1_labels,G2_labels: dict
650
- The label of every node in G1 and G2 respectively
651
-
652
- state_params: namedtuple
653
- Contains all the State-related parameters:
654
-
655
- mapping: dict
656
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
657
-
658
- reverse_mapping: dict
659
- The reverse mapping as extended so far. Maps nodes from G2 to nodes
660
- of G1. It's basically "mapping" reversed
661
-
662
- T1, T2: set
663
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
664
- that are not in the mapping, but are neighbors of nodes that are.
665
-
666
- T1_tilde, T2_tilde: set
667
- Ti_out contains all the nodes from Gi, that are neither in the
668
- mapping nor in Ti
669
-
670
- Returns
671
- -------
672
- True if we should prune this branch, i.e. the node pair failed the cutting checks. False otherwise.
673
- """
674
- G1, G2, G1_labels, G2_labels, _, _, _ = graph_params
675
- (
676
- _,
677
- _,
678
- T1,
679
- T1_in,
680
- T1_tilde,
681
- _,
682
- T2,
683
- T2_in,
684
- T2_tilde,
685
- _,
686
- ) = state_params
687
-
688
- u_labels_predecessors, v_labels_predecessors = {}, {}
689
- if G1.is_directed():
690
- u_labels_predecessors = nx.utils.groups(
691
- {n1: G1_labels[n1] for n1 in G1.pred[u]}
692
- )
693
- v_labels_predecessors = nx.utils.groups(
694
- {n2: G2_labels[n2] for n2 in G2.pred[v]}
695
- )
696
-
697
- if set(u_labels_predecessors.keys()) != set(v_labels_predecessors.keys()):
698
- return True
699
-
700
- u_labels_successors = nx.utils.groups({n1: G1_labels[n1] for n1 in G1[u]})
701
- v_labels_successors = nx.utils.groups({n2: G2_labels[n2] for n2 in G2[v]})
702
-
703
- # if the neighbors of u, do not have the same labels as those of v, NOT feasible.
704
- if set(u_labels_successors.keys()) != set(v_labels_successors.keys()):
705
- return True
706
-
707
- for label, G1_nbh in u_labels_successors.items():
708
- G2_nbh = v_labels_successors[label]
709
-
710
- if G1.is_multigraph():
711
- # Check for every neighbor in the neighborhood, if u-nbr1 has same edges as v-nbr2
712
- u_nbrs_edges = sorted(G1.number_of_edges(u, x) for x in G1_nbh)
713
- v_nbrs_edges = sorted(G2.number_of_edges(v, x) for x in G2_nbh)
714
- if any(
715
- u_nbr_edges != v_nbr_edges
716
- for u_nbr_edges, v_nbr_edges in zip(u_nbrs_edges, v_nbrs_edges)
717
- ):
718
- return True
719
-
720
- if len(T1.intersection(G1_nbh)) != len(T2.intersection(G2_nbh)):
721
- return True
722
- if len(T1_tilde.intersection(G1_nbh)) != len(T2_tilde.intersection(G2_nbh)):
723
- return True
724
- if G1.is_directed() and len(T1_in.intersection(G1_nbh)) != len(
725
- T2_in.intersection(G2_nbh)
726
- ):
727
- return True
728
-
729
- if not G1.is_directed():
730
- return False
731
-
732
- for label, G1_pred in u_labels_predecessors.items():
733
- G2_pred = v_labels_predecessors[label]
734
-
735
- if G1.is_multigraph():
736
- # Check for every neighbor in the neighborhood, if u-nbr1 has same edges as v-nbr2
737
- u_pred_edges = sorted(G1.number_of_edges(u, x) for x in G1_pred)
738
- v_pred_edges = sorted(G2.number_of_edges(v, x) for x in G2_pred)
739
- if any(
740
- u_nbr_edges != v_nbr_edges
741
- for u_nbr_edges, v_nbr_edges in zip(u_pred_edges, v_pred_edges)
742
- ):
743
- return True
744
-
745
- if len(T1.intersection(G1_pred)) != len(T2.intersection(G2_pred)):
746
- return True
747
- if len(T1_tilde.intersection(G1_pred)) != len(T2_tilde.intersection(G2_pred)):
748
- return True
749
- if len(T1_in.intersection(G1_pred)) != len(T2_in.intersection(G2_pred)):
750
- return True
751
-
752
- return False
753
-
754
-
755
- def _consistent_PT(u, v, graph_params, state_params):
756
- """Checks the consistency of extending the mapping using the current node pair.
757
-
758
- Parameters
759
- ----------
760
- u, v: Graph node
761
- The two candidate nodes being examined.
762
-
763
- graph_params: namedtuple
764
- Contains all the Graph-related parameters:
765
-
766
- G1,G2: NetworkX Graph or MultiGraph instances.
767
- The two graphs to check for isomorphism or monomorphism
768
-
769
- G1_labels,G2_labels: dict
770
- The label of every node in G1 and G2 respectively
771
-
772
- state_params: namedtuple
773
- Contains all the State-related parameters:
774
-
775
- mapping: dict
776
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
777
-
778
- reverse_mapping: dict
779
- The reverse mapping as extended so far. Maps nodes from G2 to nodes of G1.
780
- It's basically "mapping" reversed
781
-
782
- T1, T2: set
783
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
784
- that are not in the mapping, but are neighbors of nodes that are.
785
-
786
- T1_out, T2_out: set
787
- Ti_out contains all the nodes from Gi, that are neither in the mapping
788
- nor in Ti
789
-
790
- Returns
791
- -------
792
- True if the pair passes all the consistency checks successfully. False otherwise.
793
- """
794
- G1, G2 = graph_params.G1, graph_params.G2
795
- mapping, reverse_mapping = state_params.mapping, state_params.reverse_mapping
796
-
797
- for neighbor in G1[u]:
798
- if neighbor in mapping:
799
- if G1.number_of_edges(u, neighbor) != G2.number_of_edges(
800
- v, mapping[neighbor]
801
- ):
802
- return False
803
-
804
- for neighbor in G2[v]:
805
- if neighbor in reverse_mapping:
806
- if G1.number_of_edges(u, reverse_mapping[neighbor]) != G2.number_of_edges(
807
- v, neighbor
808
- ):
809
- return False
810
-
811
- if not G1.is_directed():
812
- return True
813
-
814
- for predecessor in G1.pred[u]:
815
- if predecessor in mapping:
816
- if G1.number_of_edges(predecessor, u) != G2.number_of_edges(
817
- mapping[predecessor], v
818
- ):
819
- return False
820
-
821
- for predecessor in G2.pred[v]:
822
- if predecessor in reverse_mapping:
823
- if G1.number_of_edges(
824
- reverse_mapping[predecessor], u
825
- ) != G2.number_of_edges(predecessor, v):
826
- return False
827
-
828
- return True
829
-
830
-
831
- def _update_Tinout(new_node1, new_node2, graph_params, state_params):
832
- """Updates the Ti/Ti_out (i=1,2) when a new node pair u-v is added to the mapping.
833
-
834
- Notes
835
- -----
836
- This function should be called right after the feasibility checks are passed,
837
- and node1 is mapped to node2. The purpose of this function is to avoid brute
838
- force computing of Ti/Ti_out by iterating over all nodes of the graph and
839
- checking which nodes satisfy the necessary conditions. Instead, in every step
840
- of the algorithm we focus exclusively on the two nodes that are being added
841
- to the mapping, incrementally updating Ti/Ti_out.
842
-
843
- Parameters
844
- ----------
845
- new_node1, new_node2: Graph node
846
- The two new nodes, added to the mapping.
847
-
848
- graph_params: namedtuple
849
- Contains all the Graph-related parameters:
850
-
851
- G1,G2: NetworkX Graph or MultiGraph instances.
852
- The two graphs to check for isomorphism or monomorphism
853
-
854
- G1_labels,G2_labels: dict
855
- The label of every node in G1 and G2 respectively
856
-
857
- state_params: namedtuple
858
- Contains all the State-related parameters:
859
-
860
- mapping: dict
861
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
862
-
863
- reverse_mapping: dict
864
- The reverse mapping as extended so far. Maps nodes from G2 to nodes of G1.
865
- It's basically "mapping" reversed
866
-
867
- T1, T2: set
868
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
869
- that are not in the mapping, but are neighbors of nodes that are.
870
-
871
- T1_tilde, T2_tilde: set
872
- Ti_out contains all the nodes from Gi, that are neither in the mapping nor in Ti
873
- """
874
- G1, G2, _, _, _, _, _ = graph_params
875
- (
876
- mapping,
877
- reverse_mapping,
878
- T1,
879
- T1_in,
880
- T1_tilde,
881
- T1_tilde_in,
882
- T2,
883
- T2_in,
884
- T2_tilde,
885
- T2_tilde_in,
886
- ) = state_params
887
-
888
- uncovered_successors_G1 = {succ for succ in G1[new_node1] if succ not in mapping}
889
- uncovered_successors_G2 = {
890
- succ for succ in G2[new_node2] if succ not in reverse_mapping
891
- }
892
-
893
- # Add the uncovered neighbors of node1 and node2 in T1 and T2 respectively
894
- T1.update(uncovered_successors_G1)
895
- T2.update(uncovered_successors_G2)
896
- T1.discard(new_node1)
897
- T2.discard(new_node2)
898
-
899
- T1_tilde.difference_update(uncovered_successors_G1)
900
- T2_tilde.difference_update(uncovered_successors_G2)
901
- T1_tilde.discard(new_node1)
902
- T2_tilde.discard(new_node2)
903
-
904
- if not G1.is_directed():
905
- return
906
-
907
- uncovered_predecessors_G1 = {
908
- pred for pred in G1.pred[new_node1] if pred not in mapping
909
- }
910
- uncovered_predecessors_G2 = {
911
- pred for pred in G2.pred[new_node2] if pred not in reverse_mapping
912
- }
913
-
914
- T1_in.update(uncovered_predecessors_G1)
915
- T2_in.update(uncovered_predecessors_G2)
916
- T1_in.discard(new_node1)
917
- T2_in.discard(new_node2)
918
-
919
- T1_tilde.difference_update(uncovered_predecessors_G1)
920
- T2_tilde.difference_update(uncovered_predecessors_G2)
921
- T1_tilde.discard(new_node1)
922
- T2_tilde.discard(new_node2)
923
-
924
-
925
- def _restore_Tinout(popped_node1, popped_node2, graph_params, state_params):
926
- """Restores the previous version of Ti/Ti_out when a node pair is deleted
927
- from the mapping.
928
-
929
- Parameters
930
- ----------
931
- popped_node1, popped_node2: Graph node
932
- The two nodes deleted from the mapping.
933
-
934
- graph_params: namedtuple
935
- Contains all the Graph-related parameters:
936
-
937
- G1,G2: NetworkX Graph or MultiGraph instances.
938
- The two graphs to check for isomorphism or monomorphism
939
-
940
- G1_labels,G2_labels: dict
941
- The label of every node in G1 and G2 respectively
942
-
943
- state_params: namedtuple
944
- Contains all the State-related parameters:
945
-
946
- mapping: dict
947
- The mapping as extended so far. Maps nodes of G1 to nodes of G2
948
-
949
- reverse_mapping: dict
950
- The reverse mapping as extended so far. Maps nodes from G2 to nodes of G1.
951
- It's basically "mapping" reversed
952
-
953
- T1, T2: set
954
- Ti contains uncovered neighbors of covered nodes from Gi, i.e. nodes
955
- that are not in the mapping, but are neighbors of nodes that are.
956
-
957
- T1_tilde, T2_tilde: set
958
- Ti_out contains all the nodes from Gi, that are neither in the mapping
959
- nor in Ti
960
- """
961
- # If the node we want to remove from the mapping, has at least one covered
962
- # neighbor, add it to T1.
963
- G1, G2, _, _, _, _, _ = graph_params
964
- (
965
- mapping,
966
- reverse_mapping,
967
- T1,
968
- T1_in,
969
- T1_tilde,
970
- T1_tilde_in,
971
- T2,
972
- T2_in,
973
- T2_tilde,
974
- T2_tilde_in,
975
- ) = state_params
976
-
977
- is_added = False
978
- for neighbor in G1[popped_node1]:
979
- if neighbor in mapping:
980
- # if a neighbor of the excluded node1 is in the mapping, keep node1 in T1
981
- is_added = True
982
- T1.add(popped_node1)
983
- else:
984
- # check if its neighbor has another connection with a covered node.
985
- # If not, only then exclude it from T1
986
- if any(nbr in mapping for nbr in G1[neighbor]):
987
- continue
988
- T1.discard(neighbor)
989
- T1_tilde.add(neighbor)
990
-
991
- # Case where the node is not present in neither the mapping nor T1.
992
- # By definition, it should belong to T1_tilde
993
- if not is_added:
994
- T1_tilde.add(popped_node1)
995
-
996
- is_added = False
997
- for neighbor in G2[popped_node2]:
998
- if neighbor in reverse_mapping:
999
- is_added = True
1000
- T2.add(popped_node2)
1001
- else:
1002
- if any(nbr in reverse_mapping for nbr in G2[neighbor]):
1003
- continue
1004
- T2.discard(neighbor)
1005
- T2_tilde.add(neighbor)
1006
-
1007
- if not is_added:
1008
- T2_tilde.add(popped_node2)
1009
-
1010
-
1011
- def _restore_Tinout_Di(popped_node1, popped_node2, graph_params, state_params):
1012
- # If the node we want to remove from the mapping, has at least one covered neighbor, add it to T1.
1013
- G1, G2, _, _, _, _, _ = graph_params
1014
- (
1015
- mapping,
1016
- reverse_mapping,
1017
- T1,
1018
- T1_in,
1019
- T1_tilde,
1020
- T1_tilde_in,
1021
- T2,
1022
- T2_in,
1023
- T2_tilde,
1024
- T2_tilde_in,
1025
- ) = state_params
1026
-
1027
- is_added = False
1028
- for successor in G1[popped_node1]:
1029
- if successor in mapping:
1030
- # if a neighbor of the excluded node1 is in the mapping, keep node1 in T1
1031
- is_added = True
1032
- T1_in.add(popped_node1)
1033
- else:
1034
- # check if its neighbor has another connection with a covered node.
1035
- # If not, only then exclude it from T1
1036
- if not any(pred in mapping for pred in G1.pred[successor]):
1037
- T1.discard(successor)
1038
-
1039
- if not any(succ in mapping for succ in G1[successor]):
1040
- T1_in.discard(successor)
1041
-
1042
- if successor not in T1:
1043
- if successor not in T1_in:
1044
- T1_tilde.add(successor)
1045
-
1046
- for predecessor in G1.pred[popped_node1]:
1047
- if predecessor in mapping:
1048
- # if a neighbor of the excluded node1 is in the mapping, keep node1 in T1
1049
- is_added = True
1050
- T1.add(popped_node1)
1051
- else:
1052
- # check if its neighbor has another connection with a covered node.
1053
- # If not, only then exclude it from T1
1054
- if not any(pred in mapping for pred in G1.pred[predecessor]):
1055
- T1.discard(predecessor)
1056
-
1057
- if not any(succ in mapping for succ in G1[predecessor]):
1058
- T1_in.discard(predecessor)
1059
-
1060
- if not (predecessor in T1 or predecessor in T1_in):
1061
- T1_tilde.add(predecessor)
1062
-
1063
- # Case where the node is not present in neither the mapping nor T1.
1064
- # By definition it should belong to T1_tilde
1065
- if not is_added:
1066
- T1_tilde.add(popped_node1)
1067
-
1068
- is_added = False
1069
- for successor in G2[popped_node2]:
1070
- if successor in reverse_mapping:
1071
- is_added = True
1072
- T2_in.add(popped_node2)
1073
- else:
1074
- if not any(pred in reverse_mapping for pred in G2.pred[successor]):
1075
- T2.discard(successor)
1076
-
1077
- if not any(succ in reverse_mapping for succ in G2[successor]):
1078
- T2_in.discard(successor)
1079
-
1080
- if successor not in T2:
1081
- if successor not in T2_in:
1082
- T2_tilde.add(successor)
1083
-
1084
- for predecessor in G2.pred[popped_node2]:
1085
- if predecessor in reverse_mapping:
1086
- # if a neighbor of the excluded node1 is in the mapping, keep node1 in T1
1087
- is_added = True
1088
- T2.add(popped_node2)
1089
- else:
1090
- # check if its neighbor has another connection with a covered node.
1091
- # If not, only then exclude it from T1
1092
- if not any(pred in reverse_mapping for pred in G2.pred[predecessor]):
1093
- T2.discard(predecessor)
1094
-
1095
- if not any(succ in reverse_mapping for succ in G2[predecessor]):
1096
- T2_in.discard(predecessor)
1097
-
1098
- if not (predecessor in T2 or predecessor in T2_in):
1099
- T2_tilde.add(predecessor)
1100
-
1101
- if not is_added:
1102
- T2_tilde.add(popped_node2)