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,677 +0,0 @@
1
- """
2
- Algorithm for testing d-separation in DAGs.
3
-
4
- *d-separation* is a test for conditional independence in probability
5
- distributions that can be factorized using DAGs. It is a purely
6
- graphical test that uses the underlying graph and makes no reference
7
- to the actual distribution parameters. See [1]_ for a formal
8
- definition.
9
-
10
- The implementation is based on the conceptually simple linear time
11
- algorithm presented in [2]_. Refer to [3]_, [4]_ for a couple of
12
- alternative algorithms.
13
-
14
- The functional interface in NetworkX consists of three functions:
15
-
16
- - `find_minimal_d_separator` returns a minimal d-separator set ``z``.
17
- That is, removing any node or nodes from it makes it no longer a d-separator.
18
- - `is_d_separator` checks if a given set is a d-separator.
19
- - `is_minimal_d_separator` checks if a given set is a minimal d-separator.
20
-
21
- D-separators
22
- ------------
23
-
24
- Here, we provide a brief overview of d-separation and related concepts that
25
- are relevant for understanding it:
26
-
27
- The ideas of d-separation and d-connection relate to paths being open or blocked.
28
-
29
- - A "path" is a sequence of nodes connected in order by edges. Unlike for most
30
- graph theory analysis, the direction of the edges is ignored. Thus the path
31
- can be thought of as a traditional path on the undirected version of the graph.
32
- - A "candidate d-separator" ``z`` is a set of nodes being considered as
33
- possibly blocking all paths between two prescribed sets ``x`` and ``y`` of nodes.
34
- We refer to each node in the candidate d-separator as "known".
35
- - A "collider" node on a path is a node that is a successor of its two neighbor
36
- nodes on the path. That is, ``c`` is a collider if the edge directions
37
- along the path look like ``... u -> c <- v ...``.
38
- - If a collider node or any of its descendants are "known", the collider
39
- is called an "open collider". Otherwise it is a "blocking collider".
40
- - Any path can be "blocked" in two ways. If the path contains a "known" node
41
- that is not a collider, the path is blocked. Also, if the path contains a
42
- collider that is not a "known" node, the path is blocked.
43
- - A path is "open" if it is not blocked. That is, it is open if every node is
44
- either an open collider or not a "known". Said another way, every
45
- "known" in the path is a collider and every collider is open (has a
46
- "known" as a inclusive descendant). The concept of "open path" is meant to
47
- demonstrate a probabilistic conditional dependence between two nodes given
48
- prescribed knowledge ("known" nodes).
49
- - Two sets ``x`` and ``y`` of nodes are "d-separated" by a set of nodes ``z``
50
- if all paths between nodes in ``x`` and nodes in ``y`` are blocked. That is,
51
- if there are no open paths from any node in ``x`` to any node in ``y``.
52
- Such a set ``z`` is a "d-separator" of ``x`` and ``y``.
53
- - A "minimal d-separator" is a d-separator ``z`` for which no node or subset
54
- of nodes can be removed with it still being a d-separator.
55
-
56
- The d-separator blocks some paths between ``x`` and ``y`` but opens others.
57
- Nodes in the d-separator block paths if the nodes are not colliders.
58
- But if a collider or its descendant nodes are in the d-separation set, the
59
- colliders are open, allowing a path through that collider.
60
-
61
- Illustration of D-separation with examples
62
- ------------------------------------------
63
-
64
- A pair of two nodes, ``u`` and ``v``, are d-connected if there is a path
65
- from ``u`` to ``v`` that is not blocked. That means, there is an open
66
- path from ``u`` to ``v``.
67
-
68
- For example, if the d-separating set is the empty set, then the following paths are
69
- open between ``u`` and ``v``:
70
-
71
- - u <- n -> v
72
- - u -> w -> ... -> n -> v
73
-
74
- If on the other hand, ``n`` is in the d-separating set, then ``n`` blocks
75
- those paths between ``u`` and ``v``.
76
-
77
- Colliders block a path if they and their descendants are not included
78
- in the d-separating set. An example of a path that is blocked when the
79
- d-separating set is empty is:
80
-
81
- - u -> w -> ... -> n <- v
82
-
83
- The node ``n`` is a collider in this path and is not in the d-separating set.
84
- So ``n`` blocks this path. However, if ``n`` or a descendant of ``n`` is
85
- included in the d-separating set, then the path through the collider
86
- at ``n`` (... -> n <- ...) is "open".
87
-
88
- D-separation is concerned with blocking all paths between nodes from ``x`` to ``y``.
89
- A d-separating set between ``x`` and ``y`` is one where all paths are blocked.
90
-
91
- D-separation and its applications in probability
92
- ------------------------------------------------
93
-
94
- D-separation is commonly used in probabilistic causal-graph models. D-separation
95
- connects the idea of probabilistic "dependence" with separation in a graph. If
96
- one assumes the causal Markov condition [5]_, (every node is conditionally
97
- independent of its non-descendants, given its parents) then d-separation implies
98
- conditional independence in probability distributions.
99
- Symmetrically, d-connection implies dependence.
100
-
101
- The intuition is as follows. The edges on a causal graph indicate which nodes
102
- influence the outcome of other nodes directly. An edge from u to v
103
- implies that the outcome of event ``u`` influences the probabilities for
104
- the outcome of event ``v``. Certainly knowing ``u`` changes predictions for ``v``.
105
- But also knowing ``v`` changes predictions for ``u``. The outcomes are dependent.
106
- Furthermore, an edge from ``v`` to ``w`` would mean that ``w`` and ``v`` are dependent
107
- and thus that ``u`` could indirectly influence ``w``.
108
-
109
- Without any knowledge about the system (candidate d-separating set is empty)
110
- a causal graph ``u -> v -> w`` allows all three nodes to be dependent. But
111
- if we know the outcome of ``v``, the conditional probabilities of outcomes for
112
- ``u`` and ``w`` are independent of each other. That is, once we know the outcome
113
- for ``v``, the probabilities for ``w`` do not depend on the outcome for ``u``.
114
- This is the idea behind ``v`` blocking the path if it is "known" (in the candidate
115
- d-separating set).
116
-
117
- The same argument works whether the direction of the edges are both
118
- left-going and when both arrows head out from the middle. Having a "known"
119
- node on a path blocks the collider-free path because those relationships
120
- make the conditional probabilities independent.
121
-
122
- The direction of the causal edges does impact dependence precisely in the
123
- case of a collider e.g. ``u -> v <- w``. In that situation, both ``u`` and ``w``
124
- influence ``v``. But they do not directly influence each other. So without any
125
- knowledge of any outcomes, ``u`` and ``w`` are independent. That is the idea behind
126
- colliders blocking the path. But, if ``v`` is known, the conditional probabilities
127
- of ``u`` and ``w`` can be dependent. This is the heart of Berkson's Paradox [6]_.
128
- For example, suppose ``u`` and ``w`` are boolean events (they either happen or do not)
129
- and ``v`` represents the outcome "at least one of ``u`` and ``w`` occur". Then knowing
130
- ``v`` is true makes the conditional probabilities of ``u`` and ``w`` dependent.
131
- Essentially, knowing that at least one of them is true raises the probability of
132
- each. But further knowledge that ``w`` is true (or false) change the conditional
133
- probability of ``u`` to either the original value or 1. So the conditional
134
- probability of ``u`` depends on the outcome of ``w`` even though there is no
135
- causal relationship between them. When a collider is known, dependence can
136
- occur across paths through that collider. This is the reason open colliders
137
- do not block paths.
138
-
139
- Furthermore, even if ``v`` is not "known", if one of its descendants is "known"
140
- we can use that information to know more about ``v`` which again makes
141
- ``u`` and ``w`` potentially dependent. Suppose the chance of ``n`` occurring
142
- is much higher when ``v`` occurs ("at least one of ``u`` and ``w`` occur").
143
- Then if we know ``n`` occurred, it is more likely that ``v`` occurred and that
144
- makes the chance of ``u`` and ``w`` dependent. This is the idea behind why
145
- a collider does no block a path if any descendant of the collider is "known".
146
-
147
- When two sets of nodes ``x`` and ``y`` are d-separated by a set ``z``,
148
- it means that given the outcomes of the nodes in ``z``, the probabilities
149
- of outcomes of the nodes in ``x`` are independent of the outcomes of the
150
- nodes in ``y`` and vice versa.
151
-
152
- Examples
153
- --------
154
- A Hidden Markov Model with 5 observed states and 5 hidden states
155
- where the hidden states have causal relationships resulting in
156
- a path results in the following causal network. We check that
157
- early states along the path are separated from late state in
158
- the path by the d-separator of the middle hidden state.
159
- Thus if we condition on the middle hidden state, the early
160
- state probabilities are independent of the late state outcomes.
161
-
162
- >>> G = nx.DiGraph()
163
- >>> G.add_edges_from(
164
- ... [
165
- ... ("H1", "H2"),
166
- ... ("H2", "H3"),
167
- ... ("H3", "H4"),
168
- ... ("H4", "H5"),
169
- ... ("H1", "O1"),
170
- ... ("H2", "O2"),
171
- ... ("H3", "O3"),
172
- ... ("H4", "O4"),
173
- ... ("H5", "O5"),
174
- ... ]
175
- ... )
176
- >>> x, y, z = ({"H1", "O1"}, {"H5", "O5"}, {"H3"})
177
- >>> nx.is_d_separator(G, x, y, z)
178
- True
179
- >>> nx.is_minimal_d_separator(G, x, y, z)
180
- True
181
- >>> nx.is_minimal_d_separator(G, x, y, z | {"O3"})
182
- False
183
- >>> z = nx.find_minimal_d_separator(G, x | y, {"O2", "O3", "O4"})
184
- >>> z == {"H2", "H4"}
185
- True
186
-
187
- If no minimal_d_separator exists, `None` is returned
188
-
189
- >>> other_z = nx.find_minimal_d_separator(G, x | y, {"H2", "H3"})
190
- >>> other_z is None
191
- True
192
-
193
-
194
- References
195
- ----------
196
-
197
- .. [1] Pearl, J. (2009). Causality. Cambridge: Cambridge University Press.
198
-
199
- .. [2] Darwiche, A. (2009). Modeling and reasoning with Bayesian networks.
200
- Cambridge: Cambridge University Press.
201
-
202
- .. [3] Shachter, Ross D. "Bayes-ball: The rational pastime (for
203
- determining irrelevance and requisite information in belief networks
204
- and influence diagrams)." In Proceedings of the Fourteenth Conference
205
- on Uncertainty in Artificial Intelligence (UAI), (pp. 480–487). 1998.
206
-
207
- .. [4] Koller, D., & Friedman, N. (2009).
208
- Probabilistic graphical models: principles and techniques. The MIT Press.
209
-
210
- .. [5] https://en.wikipedia.org/wiki/Causal_Markov_condition
211
-
212
- .. [6] https://en.wikipedia.org/wiki/Berkson%27s_paradox
213
-
214
- """
215
-
216
- from collections import deque
217
- from itertools import chain
218
-
219
- import networkx as nx
220
- from networkx.utils import UnionFind, not_implemented_for
221
-
222
- __all__ = [
223
- "is_d_separator",
224
- "is_minimal_d_separator",
225
- "find_minimal_d_separator",
226
- ]
227
-
228
-
229
- @not_implemented_for("undirected")
230
- @nx._dispatchable
231
- def is_d_separator(G, x, y, z):
232
- """Return whether node sets `x` and `y` are d-separated by `z`.
233
-
234
- Parameters
235
- ----------
236
- G : nx.DiGraph
237
- A NetworkX DAG.
238
-
239
- x : node or set of nodes
240
- First node or set of nodes in `G`.
241
-
242
- y : node or set of nodes
243
- Second node or set of nodes in `G`.
244
-
245
- z : node or set of nodes
246
- Potential separator (set of conditioning nodes in `G`). Can be empty set.
247
-
248
- Returns
249
- -------
250
- b : bool
251
- A boolean that is true if `x` is d-separated from `y` given `z` in `G`.
252
-
253
- Raises
254
- ------
255
- NetworkXError
256
- The *d-separation* test is commonly used on disjoint sets of
257
- nodes in acyclic directed graphs. Accordingly, the algorithm
258
- raises a :exc:`NetworkXError` if the node sets are not
259
- disjoint or if the input graph is not a DAG.
260
-
261
- NodeNotFound
262
- If any of the input nodes are not found in the graph,
263
- a :exc:`NodeNotFound` exception is raised
264
-
265
- Notes
266
- -----
267
- A d-separating set in a DAG is a set of nodes that
268
- blocks all paths between the two sets. Nodes in `z`
269
- block a path if they are part of the path and are not a collider,
270
- or a descendant of a collider. Also colliders that are not in `z`
271
- block a path. A collider structure along a path
272
- is ``... -> c <- ...`` where ``c`` is the collider node.
273
-
274
- https://en.wikipedia.org/wiki/Bayesian_network#d-separation
275
- """
276
- try:
277
- x = {x} if x in G else x
278
- y = {y} if y in G else y
279
- z = {z} if z in G else z
280
-
281
- intersection = x & y or x & z or y & z
282
- if intersection:
283
- raise nx.NetworkXError(
284
- f"The sets are not disjoint, with intersection {intersection}"
285
- )
286
-
287
- set_v = x | y | z
288
- if set_v - G.nodes:
289
- raise nx.NodeNotFound(f"The node(s) {set_v - G.nodes} are not found in G")
290
- except TypeError:
291
- raise nx.NodeNotFound("One of x, y, or z is not a node or a set of nodes in G")
292
-
293
- if not nx.is_directed_acyclic_graph(G):
294
- raise nx.NetworkXError("graph should be directed acyclic")
295
-
296
- # contains -> and <-> edges from starting node T
297
- forward_deque = deque([])
298
- forward_visited = set()
299
-
300
- # contains <- and - edges from starting node T
301
- backward_deque = deque(x)
302
- backward_visited = set()
303
-
304
- ancestors_or_z = set().union(*[nx.ancestors(G, node) for node in x]) | z | x
305
-
306
- while forward_deque or backward_deque:
307
- if backward_deque:
308
- node = backward_deque.popleft()
309
- backward_visited.add(node)
310
- if node in y:
311
- return False
312
- if node in z:
313
- continue
314
-
315
- # add <- edges to backward deque
316
- backward_deque.extend(G.pred[node].keys() - backward_visited)
317
- # add -> edges to forward deque
318
- forward_deque.extend(G.succ[node].keys() - forward_visited)
319
-
320
- if forward_deque:
321
- node = forward_deque.popleft()
322
- forward_visited.add(node)
323
- if node in y:
324
- return False
325
-
326
- # Consider if -> node <- is opened due to ancestor of node in z
327
- if node in ancestors_or_z:
328
- # add <- edges to backward deque
329
- backward_deque.extend(G.pred[node].keys() - backward_visited)
330
- if node not in z:
331
- # add -> edges to forward deque
332
- forward_deque.extend(G.succ[node].keys() - forward_visited)
333
-
334
- return True
335
-
336
-
337
- @not_implemented_for("undirected")
338
- @nx._dispatchable
339
- def find_minimal_d_separator(G, x, y, *, included=None, restricted=None):
340
- """Returns a minimal d-separating set between `x` and `y` if possible
341
-
342
- A d-separating set in a DAG is a set of nodes that blocks all
343
- paths between the two sets of nodes, `x` and `y`. This function
344
- constructs a d-separating set that is "minimal", meaning no nodes can
345
- be removed without it losing the d-separating property for `x` and `y`.
346
- If no d-separating sets exist for `x` and `y`, this returns `None`.
347
-
348
- In a DAG there may be more than one minimal d-separator between two
349
- sets of nodes. Minimal d-separators are not always unique. This function
350
- returns one minimal d-separator, or `None` if no d-separator exists.
351
-
352
- Uses the algorithm presented in [1]_. The complexity of the algorithm
353
- is :math:`O(m)`, where :math:`m` stands for the number of edges in
354
- the subgraph of G consisting of only the ancestors of `x` and `y`.
355
- For full details, see [1]_.
356
-
357
- Parameters
358
- ----------
359
- G : graph
360
- A networkx DAG.
361
- x : set | node
362
- A node or set of nodes in the graph.
363
- y : set | node
364
- A node or set of nodes in the graph.
365
- included : set | node | None
366
- A node or set of nodes which must be included in the found separating set,
367
- default is None, which means the empty set.
368
- restricted : set | node | None
369
- Restricted node or set of nodes to consider. Only these nodes can be in
370
- the found separating set, default is None meaning all nodes in ``G``.
371
-
372
- Returns
373
- -------
374
- z : set | None
375
- The minimal d-separating set, if at least one d-separating set exists,
376
- otherwise None.
377
-
378
- Raises
379
- ------
380
- NetworkXError
381
- Raises a :exc:`NetworkXError` if the input graph is not a DAG
382
- or if node sets `x`, `y`, and `included` are not disjoint.
383
-
384
- NodeNotFound
385
- If any of the input nodes are not found in the graph,
386
- a :exc:`NodeNotFound` exception is raised.
387
-
388
- References
389
- ----------
390
- .. [1] van der Zander, Benito, and Maciej Liśkiewicz. "Finding
391
- minimal d-separators in linear time and applications." In
392
- Uncertainty in Artificial Intelligence, pp. 637-647. PMLR, 2020.
393
- """
394
- if not nx.is_directed_acyclic_graph(G):
395
- raise nx.NetworkXError("graph should be directed acyclic")
396
-
397
- try:
398
- x = {x} if x in G else x
399
- y = {y} if y in G else y
400
-
401
- if included is None:
402
- included = set()
403
- elif included in G:
404
- included = {included}
405
-
406
- if restricted is None:
407
- restricted = set(G)
408
- elif restricted in G:
409
- restricted = {restricted}
410
-
411
- set_y = x | y | included | restricted
412
- if set_y - G.nodes:
413
- raise nx.NodeNotFound(f"The node(s) {set_y - G.nodes} are not found in G")
414
- except TypeError:
415
- raise nx.NodeNotFound(
416
- "One of x, y, included or restricted is not a node or set of nodes in G"
417
- )
418
-
419
- if not included <= restricted:
420
- raise nx.NetworkXError(
421
- f"Included nodes {included} must be in restricted nodes {restricted}"
422
- )
423
-
424
- intersection = x & y or x & included or y & included
425
- if intersection:
426
- raise nx.NetworkXError(
427
- f"The sets x, y, included are not disjoint. Overlap: {intersection}"
428
- )
429
-
430
- nodeset = x | y | included
431
- ancestors_x_y_included = nodeset.union(*[nx.ancestors(G, node) for node in nodeset])
432
-
433
- z_init = restricted & (ancestors_x_y_included - (x | y))
434
-
435
- x_closure = _reachable(G, x, ancestors_x_y_included, z_init)
436
- if x_closure & y:
437
- return None
438
-
439
- z_updated = z_init & (x_closure | included)
440
- y_closure = _reachable(G, y, ancestors_x_y_included, z_updated)
441
- return z_updated & (y_closure | included)
442
-
443
-
444
- @not_implemented_for("undirected")
445
- @nx._dispatchable
446
- def is_minimal_d_separator(G, x, y, z, *, included=None, restricted=None):
447
- """Determine if `z` is a minimal d-separator for `x` and `y`.
448
-
449
- A d-separator, `z`, in a DAG is a set of nodes that blocks
450
- all paths from nodes in set `x` to nodes in set `y`.
451
- A minimal d-separator is a d-separator `z` such that removing
452
- any subset of nodes makes it no longer a d-separator.
453
-
454
- Note: This function checks whether `z` is a d-separator AND is
455
- minimal. One can use the function `is_d_separator` to only check if
456
- `z` is a d-separator. See examples below.
457
-
458
- Parameters
459
- ----------
460
- G : nx.DiGraph
461
- A NetworkX DAG.
462
- x : node | set
463
- A node or set of nodes in the graph.
464
- y : node | set
465
- A node or set of nodes in the graph.
466
- z : node | set
467
- The node or set of nodes to check if it is a minimal d-separating set.
468
- The function :func:`is_d_separator` is called inside this function
469
- to verify that `z` is in fact a d-separator.
470
- included : set | node | None
471
- A node or set of nodes which must be included in the found separating set,
472
- default is ``None``, which means the empty set.
473
- restricted : set | node | None
474
- Restricted node or set of nodes to consider. Only these nodes can be in
475
- the found separating set, default is ``None`` meaning all nodes in ``G``.
476
-
477
- Returns
478
- -------
479
- bool
480
- Whether or not the set `z` is a minimal d-separator subject to
481
- `restricted` nodes and `included` node constraints.
482
-
483
- Examples
484
- --------
485
- >>> G = nx.path_graph([0, 1, 2, 3], create_using=nx.DiGraph)
486
- >>> G.add_node(4)
487
- >>> nx.is_minimal_d_separator(G, 0, 2, {1})
488
- True
489
- >>> # since {1} is the minimal d-separator, {1, 3, 4} is not minimal
490
- >>> nx.is_minimal_d_separator(G, 0, 2, {1, 3, 4})
491
- False
492
- >>> # alternatively, if we only want to check that {1, 3, 4} is a d-separator
493
- >>> nx.is_d_separator(G, 0, 2, {1, 3, 4})
494
- True
495
-
496
- Raises
497
- ------
498
- NetworkXError
499
- Raises a :exc:`NetworkXError` if the input graph is not a DAG.
500
-
501
- NodeNotFound
502
- If any of the input nodes are not found in the graph,
503
- a :exc:`NodeNotFound` exception is raised.
504
-
505
- References
506
- ----------
507
- .. [1] van der Zander, Benito, and Maciej Liśkiewicz. "Finding
508
- minimal d-separators in linear time and applications." In
509
- Uncertainty in Artificial Intelligence, pp. 637-647. PMLR, 2020.
510
-
511
- Notes
512
- -----
513
- This function works on verifying that a set is minimal and
514
- d-separating between two nodes. Uses criterion (a), (b), (c) on
515
- page 4 of [1]_. a) closure(`x`) and `y` are disjoint. b) `z` contains
516
- all nodes from `included` and is contained in the `restricted`
517
- nodes and in the union of ancestors of `x`, `y`, and `included`.
518
- c) the nodes in `z` not in `included` are contained in both
519
- closure(x) and closure(y). The closure of a set is the set of nodes
520
- connected to the set by a directed path in G.
521
-
522
- The complexity is :math:`O(m)`, where :math:`m` stands for the
523
- number of edges in the subgraph of G consisting of only the
524
- ancestors of `x` and `y`.
525
-
526
- For full details, see [1]_.
527
- """
528
- if not nx.is_directed_acyclic_graph(G):
529
- raise nx.NetworkXError("graph should be directed acyclic")
530
-
531
- try:
532
- x = {x} if x in G else x
533
- y = {y} if y in G else y
534
- z = {z} if z in G else z
535
-
536
- if included is None:
537
- included = set()
538
- elif included in G:
539
- included = {included}
540
-
541
- if restricted is None:
542
- restricted = set(G)
543
- elif restricted in G:
544
- restricted = {restricted}
545
-
546
- set_y = x | y | included | restricted
547
- if set_y - G.nodes:
548
- raise nx.NodeNotFound(f"The node(s) {set_y - G.nodes} are not found in G")
549
- except TypeError:
550
- raise nx.NodeNotFound(
551
- "One of x, y, z, included or restricted is not a node or set of nodes in G"
552
- )
553
-
554
- if not included <= z:
555
- raise nx.NetworkXError(
556
- f"Included nodes {included} must be in proposed separating set z {x}"
557
- )
558
- if not z <= restricted:
559
- raise nx.NetworkXError(
560
- f"Separating set {z} must be contained in restricted set {restricted}"
561
- )
562
-
563
- intersection = x.intersection(y) or x.intersection(z) or y.intersection(z)
564
- if intersection:
565
- raise nx.NetworkXError(
566
- f"The sets are not disjoint, with intersection {intersection}"
567
- )
568
-
569
- nodeset = x | y | included
570
- ancestors_x_y_included = nodeset.union(*[nx.ancestors(G, n) for n in nodeset])
571
-
572
- # criterion (a) -- check that z is actually a separator
573
- x_closure = _reachable(G, x, ancestors_x_y_included, z)
574
- if x_closure & y:
575
- return False
576
-
577
- # criterion (b) -- basic constraint; included and restricted already checked above
578
- if not (z <= ancestors_x_y_included):
579
- return False
580
-
581
- # criterion (c) -- check that z is minimal
582
- y_closure = _reachable(G, y, ancestors_x_y_included, z)
583
- if not ((z - included) <= (x_closure & y_closure)):
584
- return False
585
- return True
586
-
587
-
588
- @not_implemented_for("undirected")
589
- def _reachable(G, x, a, z):
590
- """Modified Bayes-Ball algorithm for finding d-connected nodes.
591
-
592
- Find all nodes in `a` that are d-connected to those in `x` by
593
- those in `z`. This is an implementation of the function
594
- `REACHABLE` in [1]_ (which is itself a modification of the
595
- Bayes-Ball algorithm [2]_) when restricted to DAGs.
596
-
597
- Parameters
598
- ----------
599
- G : nx.DiGraph
600
- A NetworkX DAG.
601
- x : node | set
602
- A node in the DAG, or a set of nodes.
603
- a : node | set
604
- A (set of) node(s) in the DAG containing the ancestors of `x`.
605
- z : node | set
606
- The node or set of nodes conditioned on when checking d-connectedness.
607
-
608
- Returns
609
- -------
610
- w : set
611
- The closure of `x` in `a` with respect to d-connectedness
612
- given `z`.
613
-
614
- References
615
- ----------
616
- .. [1] van der Zander, Benito, and Maciej Liśkiewicz. "Finding
617
- minimal d-separators in linear time and applications." In
618
- Uncertainty in Artificial Intelligence, pp. 637-647. PMLR, 2020.
619
-
620
- .. [2] Shachter, Ross D. "Bayes-ball: The rational pastime
621
- (for determining irrelevance and requisite information in
622
- belief networks and influence diagrams)." In Proceedings of the
623
- Fourteenth Conference on Uncertainty in Artificial Intelligence
624
- (UAI), (pp. 480–487). 1998.
625
- """
626
-
627
- def _pass(e, v, f, n):
628
- """Whether a ball entering node `v` along edge `e` passes to `n` along `f`.
629
-
630
- Boolean function defined on page 6 of [1]_.
631
-
632
- Parameters
633
- ----------
634
- e : bool
635
- Directed edge by which the ball got to node `v`; `True` iff directed into `v`.
636
- v : node
637
- Node where the ball is.
638
- f : bool
639
- Directed edge connecting nodes `v` and `n`; `True` iff directed `n`.
640
- n : node
641
- Checking whether the ball passes to this node.
642
-
643
- Returns
644
- -------
645
- b : bool
646
- Whether the ball passes or not.
647
-
648
- References
649
- ----------
650
- .. [1] van der Zander, Benito, and Maciej Liśkiewicz. "Finding
651
- minimal d-separators in linear time and applications." In
652
- Uncertainty in Artificial Intelligence, pp. 637-647. PMLR, 2020.
653
- """
654
- is_element_of_A = n in a
655
- # almost_definite_status = True # always true for DAGs; not so for RCGs
656
- collider_if_in_Z = v not in z or (e and not f)
657
- return is_element_of_A and collider_if_in_Z # and almost_definite_status
658
-
659
- queue = deque([])
660
- for node in x:
661
- if bool(G.pred[node]):
662
- queue.append((True, node))
663
- if bool(G.succ[node]):
664
- queue.append((False, node))
665
- processed = queue.copy()
666
-
667
- while any(queue):
668
- e, v = queue.popleft()
669
- preds = ((False, n) for n in G.pred[v])
670
- succs = ((True, n) for n in G.succ[v])
671
- f_n_pairs = chain(preds, succs)
672
- for f, n in f_n_pairs:
673
- if (f, n) not in processed and _pass(e, v, f, n):
674
- queue.append((f, n))
675
- processed.append((f, n))
676
-
677
- return {w for (_, w) in processed}