relationalai 0.13.0.dev0__py3-none-any.whl → 0.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. frontend/debugger/dist/.gitignore +2 -0
  2. frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
  3. frontend/debugger/dist/assets/index-Cssla-O7.js +208 -0
  4. frontend/debugger/dist/assets/index-DlHsYx1V.css +9 -0
  5. frontend/debugger/dist/index.html +17 -0
  6. relationalai/__init__.py +256 -1
  7. relationalai/clients/__init__.py +18 -0
  8. relationalai/clients/client.py +947 -0
  9. relationalai/clients/config.py +673 -0
  10. relationalai/clients/direct_access_client.py +118 -0
  11. relationalai/clients/exec_txn_poller.py +91 -0
  12. relationalai/clients/hash_util.py +31 -0
  13. relationalai/clients/local.py +586 -0
  14. relationalai/clients/profile_polling.py +73 -0
  15. relationalai/clients/resources/__init__.py +8 -0
  16. relationalai/clients/resources/azure/azure.py +502 -0
  17. relationalai/clients/resources/snowflake/__init__.py +20 -0
  18. relationalai/clients/resources/snowflake/cli_resources.py +98 -0
  19. relationalai/clients/resources/snowflake/direct_access_resources.py +734 -0
  20. relationalai/clients/resources/snowflake/engine_service.py +381 -0
  21. relationalai/clients/resources/snowflake/engine_state_handlers.py +315 -0
  22. relationalai/clients/resources/snowflake/error_handlers.py +240 -0
  23. relationalai/clients/resources/snowflake/export_procedure.py.jinja +249 -0
  24. relationalai/clients/resources/snowflake/resources_factory.py +99 -0
  25. relationalai/clients/resources/snowflake/snowflake.py +3185 -0
  26. relationalai/clients/resources/snowflake/use_index_poller.py +1019 -0
  27. relationalai/clients/resources/snowflake/use_index_resources.py +188 -0
  28. relationalai/clients/resources/snowflake/util.py +387 -0
  29. relationalai/clients/result_helpers.py +420 -0
  30. relationalai/clients/types.py +118 -0
  31. relationalai/clients/util.py +356 -0
  32. relationalai/debugging.py +389 -0
  33. relationalai/dsl.py +1749 -0
  34. relationalai/early_access/builder/__init__.py +30 -0
  35. relationalai/early_access/builder/builder/__init__.py +35 -0
  36. relationalai/early_access/builder/snowflake/__init__.py +12 -0
  37. relationalai/early_access/builder/std/__init__.py +25 -0
  38. relationalai/early_access/builder/std/decimals/__init__.py +12 -0
  39. relationalai/early_access/builder/std/integers/__init__.py +12 -0
  40. relationalai/early_access/builder/std/math/__init__.py +12 -0
  41. relationalai/early_access/builder/std/strings/__init__.py +14 -0
  42. relationalai/early_access/devtools/__init__.py +12 -0
  43. relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
  44. relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
  45. relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
  46. relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
  47. relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
  48. relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
  49. relationalai/early_access/dsl/bindings/common.py +402 -0
  50. relationalai/early_access/dsl/bindings/csv.py +170 -0
  51. relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
  52. relationalai/early_access/dsl/bindings/snowflake.py +64 -0
  53. relationalai/early_access/dsl/codegen/binder.py +411 -0
  54. relationalai/early_access/dsl/codegen/common.py +79 -0
  55. relationalai/early_access/dsl/codegen/helpers.py +23 -0
  56. relationalai/early_access/dsl/codegen/relations.py +700 -0
  57. relationalai/early_access/dsl/codegen/weaver.py +417 -0
  58. relationalai/early_access/dsl/core/builders/__init__.py +47 -0
  59. relationalai/early_access/dsl/core/builders/logic.py +19 -0
  60. relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
  61. relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
  62. relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
  63. relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
  64. relationalai/early_access/dsl/core/context.py +13 -0
  65. relationalai/early_access/dsl/core/cset.py +132 -0
  66. relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
  67. relationalai/early_access/dsl/core/exprs/relational.py +18 -0
  68. relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
  69. relationalai/early_access/dsl/core/instances.py +44 -0
  70. relationalai/early_access/dsl/core/logic/__init__.py +193 -0
  71. relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
  72. relationalai/early_access/dsl/core/logic/exists.py +223 -0
  73. relationalai/early_access/dsl/core/logic/helper.py +163 -0
  74. relationalai/early_access/dsl/core/namespaces.py +32 -0
  75. relationalai/early_access/dsl/core/relations.py +276 -0
  76. relationalai/early_access/dsl/core/rules.py +112 -0
  77. relationalai/early_access/dsl/core/std/__init__.py +45 -0
  78. relationalai/early_access/dsl/core/temporal/recall.py +6 -0
  79. relationalai/early_access/dsl/core/types/__init__.py +270 -0
  80. relationalai/early_access/dsl/core/types/concepts.py +128 -0
  81. relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
  82. relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
  83. relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
  84. relationalai/early_access/dsl/core/types/standard.py +92 -0
  85. relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
  86. relationalai/early_access/dsl/core/types/variables.py +203 -0
  87. relationalai/early_access/dsl/ir/compiler.py +318 -0
  88. relationalai/early_access/dsl/ir/executor.py +260 -0
  89. relationalai/early_access/dsl/ontologies/constraints.py +88 -0
  90. relationalai/early_access/dsl/ontologies/export.py +30 -0
  91. relationalai/early_access/dsl/ontologies/models.py +453 -0
  92. relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
  93. relationalai/early_access/dsl/ontologies/readings.py +60 -0
  94. relationalai/early_access/dsl/ontologies/relationships.py +322 -0
  95. relationalai/early_access/dsl/ontologies/roles.py +87 -0
  96. relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
  97. relationalai/early_access/dsl/orm/constraints.py +438 -0
  98. relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
  99. relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
  100. relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
  101. relationalai/early_access/dsl/orm/measures/measures.py +299 -0
  102. relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
  103. relationalai/early_access/dsl/orm/models.py +256 -0
  104. relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
  105. relationalai/early_access/dsl/orm/printer.py +469 -0
  106. relationalai/early_access/dsl/orm/reasoners.py +480 -0
  107. relationalai/early_access/dsl/orm/relations.py +19 -0
  108. relationalai/early_access/dsl/orm/relationships.py +251 -0
  109. relationalai/early_access/dsl/orm/types.py +42 -0
  110. relationalai/early_access/dsl/orm/utils.py +79 -0
  111. relationalai/early_access/dsl/orm/verb.py +204 -0
  112. relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
  113. relationalai/early_access/dsl/relations.py +170 -0
  114. relationalai/early_access/dsl/rulesets.py +69 -0
  115. relationalai/early_access/dsl/schemas/__init__.py +450 -0
  116. relationalai/early_access/dsl/schemas/builder.py +48 -0
  117. relationalai/early_access/dsl/schemas/comp_names.py +51 -0
  118. relationalai/early_access/dsl/schemas/components.py +203 -0
  119. relationalai/early_access/dsl/schemas/contexts.py +156 -0
  120. relationalai/early_access/dsl/schemas/exprs.py +89 -0
  121. relationalai/early_access/dsl/schemas/fragments.py +464 -0
  122. relationalai/early_access/dsl/serialization.py +79 -0
  123. relationalai/early_access/dsl/serialize/exporter.py +163 -0
  124. relationalai/early_access/dsl/snow/api.py +105 -0
  125. relationalai/early_access/dsl/snow/common.py +76 -0
  126. relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
  127. relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
  128. relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
  129. relationalai/early_access/dsl/types/__init__.py +40 -0
  130. relationalai/early_access/dsl/types/concepts.py +12 -0
  131. relationalai/early_access/dsl/types/entities.py +135 -0
  132. relationalai/early_access/dsl/types/values.py +17 -0
  133. relationalai/early_access/dsl/utils.py +102 -0
  134. relationalai/early_access/graphs/__init__.py +13 -0
  135. relationalai/early_access/lqp/__init__.py +12 -0
  136. relationalai/early_access/lqp/compiler/__init__.py +12 -0
  137. relationalai/early_access/lqp/constructors/__init__.py +18 -0
  138. relationalai/early_access/lqp/executor/__init__.py +12 -0
  139. relationalai/early_access/lqp/ir/__init__.py +12 -0
  140. relationalai/early_access/lqp/passes/__init__.py +12 -0
  141. relationalai/early_access/lqp/pragmas/__init__.py +12 -0
  142. relationalai/early_access/lqp/primitives/__init__.py +12 -0
  143. relationalai/early_access/lqp/types/__init__.py +12 -0
  144. relationalai/early_access/lqp/utils/__init__.py +12 -0
  145. relationalai/early_access/lqp/validators/__init__.py +12 -0
  146. relationalai/early_access/metamodel/__init__.py +58 -0
  147. relationalai/early_access/metamodel/builtins/__init__.py +12 -0
  148. relationalai/early_access/metamodel/compiler/__init__.py +12 -0
  149. relationalai/early_access/metamodel/dependency/__init__.py +12 -0
  150. relationalai/early_access/metamodel/factory/__init__.py +17 -0
  151. relationalai/early_access/metamodel/helpers/__init__.py +12 -0
  152. relationalai/early_access/metamodel/ir/__init__.py +14 -0
  153. relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
  154. relationalai/early_access/metamodel/typer/__init__.py +3 -0
  155. relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
  156. relationalai/early_access/metamodel/types/__init__.py +15 -0
  157. relationalai/early_access/metamodel/util/__init__.py +15 -0
  158. relationalai/early_access/metamodel/visitor/__init__.py +12 -0
  159. relationalai/early_access/rel/__init__.py +12 -0
  160. relationalai/early_access/rel/executor/__init__.py +12 -0
  161. relationalai/early_access/rel/rel_utils/__init__.py +12 -0
  162. relationalai/early_access/rel/rewrite/__init__.py +7 -0
  163. relationalai/early_access/solvers/__init__.py +19 -0
  164. relationalai/early_access/sql/__init__.py +11 -0
  165. relationalai/early_access/sql/executor/__init__.py +3 -0
  166. relationalai/early_access/sql/rewrite/__init__.py +3 -0
  167. relationalai/early_access/tests/logging/__init__.py +12 -0
  168. relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
  169. relationalai/early_access/tests/utils/__init__.py +12 -0
  170. relationalai/environments/__init__.py +35 -0
  171. relationalai/environments/base.py +381 -0
  172. relationalai/environments/colab.py +14 -0
  173. relationalai/environments/generic.py +71 -0
  174. relationalai/environments/ipython.py +68 -0
  175. relationalai/environments/jupyter.py +9 -0
  176. relationalai/environments/snowbook.py +169 -0
  177. relationalai/errors.py +2496 -0
  178. relationalai/experimental/SF.py +38 -0
  179. relationalai/experimental/inspect.py +47 -0
  180. relationalai/experimental/pathfinder/__init__.py +158 -0
  181. relationalai/experimental/pathfinder/api.py +160 -0
  182. relationalai/experimental/pathfinder/automaton.py +584 -0
  183. relationalai/experimental/pathfinder/bridge.py +226 -0
  184. relationalai/experimental/pathfinder/compiler.py +416 -0
  185. relationalai/experimental/pathfinder/datalog.py +214 -0
  186. relationalai/experimental/pathfinder/diagnostics.py +56 -0
  187. relationalai/experimental/pathfinder/filter.py +236 -0
  188. relationalai/experimental/pathfinder/glushkov.py +439 -0
  189. relationalai/experimental/pathfinder/options.py +265 -0
  190. relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +1951 -0
  191. relationalai/experimental/pathfinder/rpq.py +344 -0
  192. relationalai/experimental/pathfinder/transition.py +200 -0
  193. relationalai/experimental/pathfinder/utils.py +26 -0
  194. relationalai/experimental/paths/README.md +107 -0
  195. relationalai/experimental/paths/api.py +143 -0
  196. relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
  197. relationalai/experimental/paths/code_organization.md +2 -0
  198. relationalai/experimental/paths/examples/Movies.ipynb +16328 -0
  199. relationalai/experimental/paths/examples/basic_example.py +40 -0
  200. relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
  201. relationalai/experimental/paths/examples/movie_example.py +77 -0
  202. relationalai/experimental/paths/examples/movies_data/actedin.csv +193 -0
  203. relationalai/experimental/paths/examples/movies_data/directed.csv +45 -0
  204. relationalai/experimental/paths/examples/movies_data/follows.csv +7 -0
  205. relationalai/experimental/paths/examples/movies_data/movies.csv +39 -0
  206. relationalai/experimental/paths/examples/movies_data/person.csv +134 -0
  207. relationalai/experimental/paths/examples/movies_data/produced.csv +16 -0
  208. relationalai/experimental/paths/examples/movies_data/ratings.csv +10 -0
  209. relationalai/experimental/paths/examples/movies_data/wrote.csv +11 -0
  210. relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
  211. relationalai/experimental/paths/examples/paths_example.py +116 -0
  212. relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
  213. relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
  214. relationalai/experimental/paths/graph.py +185 -0
  215. relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
  216. relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
  217. relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
  218. relationalai/experimental/paths/path_algorithms/single.py +59 -0
  219. relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
  220. relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
  221. relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
  222. relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
  223. relationalai/experimental/paths/path_algorithms/usp.py +150 -0
  224. relationalai/experimental/paths/product_graph.py +93 -0
  225. relationalai/experimental/paths/rpq/automaton.py +584 -0
  226. relationalai/experimental/paths/rpq/diagnostics.py +56 -0
  227. relationalai/experimental/paths/rpq/rpq.py +378 -0
  228. relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
  229. relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
  230. relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
  231. relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
  232. relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
  233. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
  234. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
  235. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
  236. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
  237. relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
  238. relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
  239. relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
  240. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
  241. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
  242. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
  243. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
  244. relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
  245. relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
  246. relationalai/experimental/paths/tree_agg.py +168 -0
  247. relationalai/experimental/paths/utilities/iterators.py +27 -0
  248. relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
  249. relationalai/experimental/solvers.py +1087 -0
  250. relationalai/loaders/csv.py +195 -0
  251. relationalai/loaders/loader.py +177 -0
  252. relationalai/loaders/types.py +23 -0
  253. relationalai/rel_emitter.py +373 -0
  254. relationalai/rel_utils.py +185 -0
  255. relationalai/semantics/__init__.py +22 -146
  256. relationalai/semantics/designs/query_builder/identify_by.md +106 -0
  257. relationalai/semantics/devtools/benchmark_lqp.py +535 -0
  258. relationalai/semantics/devtools/compilation_manager.py +294 -0
  259. relationalai/semantics/devtools/extract_lqp.py +110 -0
  260. relationalai/semantics/internal/internal.py +3785 -0
  261. relationalai/semantics/internal/snowflake.py +325 -0
  262. relationalai/semantics/lqp/README.md +34 -0
  263. relationalai/semantics/lqp/builtins.py +16 -0
  264. relationalai/semantics/lqp/compiler.py +22 -0
  265. relationalai/semantics/lqp/constructors.py +68 -0
  266. relationalai/semantics/lqp/executor.py +469 -0
  267. relationalai/semantics/lqp/intrinsics.py +24 -0
  268. relationalai/semantics/lqp/model2lqp.py +877 -0
  269. relationalai/semantics/lqp/passes.py +680 -0
  270. relationalai/semantics/lqp/primitives.py +252 -0
  271. relationalai/semantics/lqp/result_helpers.py +202 -0
  272. relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
  273. relationalai/semantics/lqp/rewrite/cdc.py +216 -0
  274. relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
  275. relationalai/semantics/lqp/rewrite/extract_keys.py +512 -0
  276. relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
  277. relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
  278. relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
  279. relationalai/semantics/lqp/rewrite/splinter.py +76 -0
  280. relationalai/semantics/lqp/types.py +101 -0
  281. relationalai/semantics/lqp/utils.py +160 -0
  282. relationalai/semantics/lqp/validators.py +57 -0
  283. relationalai/semantics/metamodel/__init__.py +40 -6
  284. relationalai/semantics/metamodel/builtins.py +771 -205
  285. relationalai/semantics/metamodel/compiler.py +133 -0
  286. relationalai/semantics/metamodel/dependency.py +862 -0
  287. relationalai/semantics/metamodel/executor.py +61 -0
  288. relationalai/semantics/metamodel/factory.py +287 -0
  289. relationalai/semantics/metamodel/helpers.py +361 -0
  290. relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
  291. relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
  292. relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
  293. relationalai/semantics/metamodel/rewrite/flatten.py +554 -0
  294. relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
  295. relationalai/semantics/metamodel/typer/checker.py +353 -0
  296. relationalai/semantics/metamodel/typer/typer.py +1399 -0
  297. relationalai/semantics/metamodel/util.py +506 -0
  298. relationalai/semantics/reasoners/__init__.py +10 -0
  299. relationalai/semantics/reasoners/graph/README.md +620 -0
  300. relationalai/semantics/reasoners/graph/__init__.py +37 -0
  301. relationalai/semantics/reasoners/graph/core.py +9019 -0
  302. relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +797 -0
  303. relationalai/semantics/reasoners/graph/tests/README.md +21 -0
  304. relationalai/semantics/reasoners/optimization/__init__.py +68 -0
  305. relationalai/semantics/reasoners/optimization/common.py +88 -0
  306. relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
  307. relationalai/semantics/reasoners/optimization/solvers_pb.py +1414 -0
  308. relationalai/semantics/rel/builtins.py +40 -0
  309. relationalai/semantics/rel/compiler.py +989 -0
  310. relationalai/semantics/rel/executor.py +362 -0
  311. relationalai/semantics/rel/rel.py +482 -0
  312. relationalai/semantics/rel/rel_utils.py +276 -0
  313. relationalai/semantics/snowflake/__init__.py +3 -0
  314. relationalai/semantics/sql/compiler.py +2503 -0
  315. relationalai/semantics/sql/executor/duck_db.py +52 -0
  316. relationalai/semantics/sql/executor/result_helpers.py +64 -0
  317. relationalai/semantics/sql/executor/snowflake.py +149 -0
  318. relationalai/semantics/sql/rewrite/denormalize.py +222 -0
  319. relationalai/semantics/sql/rewrite/double_negation.py +49 -0
  320. relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
  321. relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
  322. relationalai/semantics/sql/sql.py +504 -0
  323. relationalai/semantics/std/__init__.py +40 -60
  324. relationalai/semantics/std/constraints.py +43 -37
  325. relationalai/semantics/std/datetime.py +135 -246
  326. relationalai/semantics/std/decimals.py +52 -45
  327. relationalai/semantics/std/floats.py +5 -13
  328. relationalai/semantics/std/integers.py +11 -26
  329. relationalai/semantics/std/math.py +112 -183
  330. relationalai/semantics/std/pragmas.py +11 -0
  331. relationalai/semantics/std/re.py +62 -80
  332. relationalai/semantics/std/std.py +14 -0
  333. relationalai/semantics/std/strings.py +60 -117
  334. relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
  335. relationalai/semantics/tests/test_snapshot_base.py +9 -0
  336. relationalai/semantics/tests/utils.py +46 -0
  337. relationalai/std/__init__.py +70 -0
  338. relationalai/tools/cli.py +2089 -0
  339. relationalai/tools/cli_controls.py +1826 -0
  340. relationalai/tools/cli_helpers.py +802 -0
  341. relationalai/tools/debugger.py +183 -289
  342. relationalai/tools/debugger_client.py +109 -0
  343. relationalai/tools/debugger_server.py +302 -0
  344. relationalai/tools/dev.py +685 -0
  345. relationalai/tools/notes +7 -0
  346. relationalai/tools/qb_debugger.py +425 -0
  347. relationalai/util/clean_up_databases.py +95 -0
  348. relationalai/util/format.py +106 -48
  349. relationalai/util/list_databases.py +9 -0
  350. relationalai/util/otel_configuration.py +26 -0
  351. relationalai/util/otel_handler.py +484 -0
  352. relationalai/util/snowflake_handler.py +88 -0
  353. relationalai/util/span_format_test.py +43 -0
  354. relationalai/util/span_tracker.py +207 -0
  355. relationalai/util/spans_file_handler.py +72 -0
  356. relationalai/util/tracing_handler.py +34 -0
  357. relationalai-0.13.2.dist-info/METADATA +74 -0
  358. relationalai-0.13.2.dist-info/RECORD +460 -0
  359. relationalai-0.13.2.dist-info/WHEEL +4 -0
  360. relationalai-0.13.2.dist-info/entry_points.txt +3 -0
  361. relationalai-0.13.2.dist-info/licenses/LICENSE +202 -0
  362. relationalai_test_util/__init__.py +4 -0
  363. relationalai_test_util/fixtures.py +233 -0
  364. relationalai_test_util/snapshot.py +252 -0
  365. relationalai_test_util/traceback.py +118 -0
  366. relationalai/config/__init__.py +0 -56
  367. relationalai/config/config.py +0 -289
  368. relationalai/config/config_fields.py +0 -86
  369. relationalai/config/connections/__init__.py +0 -46
  370. relationalai/config/connections/base.py +0 -23
  371. relationalai/config/connections/duckdb.py +0 -29
  372. relationalai/config/connections/snowflake.py +0 -243
  373. relationalai/config/external/__init__.py +0 -17
  374. relationalai/config/external/dbt_converter.py +0 -101
  375. relationalai/config/external/dbt_models.py +0 -93
  376. relationalai/config/external/snowflake_converter.py +0 -41
  377. relationalai/config/external/snowflake_models.py +0 -85
  378. relationalai/config/external/utils.py +0 -19
  379. relationalai/semantics/backends/lqp/annotations.py +0 -11
  380. relationalai/semantics/backends/sql/sql_compiler.py +0 -327
  381. relationalai/semantics/frontend/base.py +0 -1707
  382. relationalai/semantics/frontend/core.py +0 -179
  383. relationalai/semantics/frontend/front_compiler.py +0 -1313
  384. relationalai/semantics/frontend/pprint.py +0 -408
  385. relationalai/semantics/metamodel/metamodel.py +0 -437
  386. relationalai/semantics/metamodel/metamodel_analyzer.py +0 -519
  387. relationalai/semantics/metamodel/metamodel_compiler.py +0 -0
  388. relationalai/semantics/metamodel/pprint.py +0 -412
  389. relationalai/semantics/metamodel/rewriter.py +0 -266
  390. relationalai/semantics/metamodel/typer.py +0 -1378
  391. relationalai/semantics/std/aggregates.py +0 -149
  392. relationalai/semantics/std/common.py +0 -44
  393. relationalai/semantics/std/numbers.py +0 -86
  394. relationalai/shims/executor.py +0 -147
  395. relationalai/shims/helpers.py +0 -126
  396. relationalai/shims/hoister.py +0 -221
  397. relationalai/shims/mm2v0.py +0 -1290
  398. relationalai/tools/cli/__init__.py +0 -6
  399. relationalai/tools/cli/cli.py +0 -90
  400. relationalai/tools/cli/components/__init__.py +0 -5
  401. relationalai/tools/cli/components/progress_reader.py +0 -1524
  402. relationalai/tools/cli/components/utils.py +0 -58
  403. relationalai/tools/cli/config_template.py +0 -45
  404. relationalai/tools/cli/dev.py +0 -19
  405. relationalai/tools/typer_debugger.py +0 -93
  406. relationalai/util/dataclasses.py +0 -43
  407. relationalai/util/docutils.py +0 -40
  408. relationalai/util/error.py +0 -199
  409. relationalai/util/naming.py +0 -145
  410. relationalai/util/python.py +0 -35
  411. relationalai/util/runtime.py +0 -156
  412. relationalai/util/schema.py +0 -197
  413. relationalai/util/source.py +0 -185
  414. relationalai/util/structures.py +0 -163
  415. relationalai/util/tracing.py +0 -261
  416. relationalai-0.13.0.dev0.dist-info/METADATA +0 -46
  417. relationalai-0.13.0.dev0.dist-info/RECORD +0 -488
  418. relationalai-0.13.0.dev0.dist-info/WHEEL +0 -5
  419. relationalai-0.13.0.dev0.dist-info/entry_points.txt +0 -3
  420. relationalai-0.13.0.dev0.dist-info/top_level.txt +0 -2
  421. v0/relationalai/__init__.py +0 -216
  422. v0/relationalai/clients/__init__.py +0 -5
  423. v0/relationalai/clients/azure.py +0 -477
  424. v0/relationalai/clients/client.py +0 -912
  425. v0/relationalai/clients/config.py +0 -673
  426. v0/relationalai/clients/direct_access_client.py +0 -118
  427. v0/relationalai/clients/hash_util.py +0 -31
  428. v0/relationalai/clients/local.py +0 -571
  429. v0/relationalai/clients/profile_polling.py +0 -73
  430. v0/relationalai/clients/result_helpers.py +0 -420
  431. v0/relationalai/clients/snowflake.py +0 -3869
  432. v0/relationalai/clients/types.py +0 -113
  433. v0/relationalai/clients/use_index_poller.py +0 -980
  434. v0/relationalai/clients/util.py +0 -356
  435. v0/relationalai/debugging.py +0 -389
  436. v0/relationalai/dsl.py +0 -1749
  437. v0/relationalai/early_access/builder/__init__.py +0 -30
  438. v0/relationalai/early_access/builder/builder/__init__.py +0 -35
  439. v0/relationalai/early_access/builder/snowflake/__init__.py +0 -12
  440. v0/relationalai/early_access/builder/std/__init__.py +0 -25
  441. v0/relationalai/early_access/builder/std/decimals/__init__.py +0 -12
  442. v0/relationalai/early_access/builder/std/integers/__init__.py +0 -12
  443. v0/relationalai/early_access/builder/std/math/__init__.py +0 -12
  444. v0/relationalai/early_access/builder/std/strings/__init__.py +0 -14
  445. v0/relationalai/early_access/devtools/__init__.py +0 -12
  446. v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
  447. v0/relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
  448. v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
  449. v0/relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
  450. v0/relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
  451. v0/relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
  452. v0/relationalai/early_access/dsl/bindings/common.py +0 -402
  453. v0/relationalai/early_access/dsl/bindings/csv.py +0 -170
  454. v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
  455. v0/relationalai/early_access/dsl/bindings/snowflake.py +0 -64
  456. v0/relationalai/early_access/dsl/codegen/binder.py +0 -411
  457. v0/relationalai/early_access/dsl/codegen/common.py +0 -79
  458. v0/relationalai/early_access/dsl/codegen/helpers.py +0 -23
  459. v0/relationalai/early_access/dsl/codegen/relations.py +0 -700
  460. v0/relationalai/early_access/dsl/codegen/weaver.py +0 -417
  461. v0/relationalai/early_access/dsl/core/builders/__init__.py +0 -47
  462. v0/relationalai/early_access/dsl/core/builders/logic.py +0 -19
  463. v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
  464. v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
  465. v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
  466. v0/relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
  467. v0/relationalai/early_access/dsl/core/context.py +0 -13
  468. v0/relationalai/early_access/dsl/core/cset.py +0 -132
  469. v0/relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
  470. v0/relationalai/early_access/dsl/core/exprs/relational.py +0 -18
  471. v0/relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
  472. v0/relationalai/early_access/dsl/core/instances.py +0 -44
  473. v0/relationalai/early_access/dsl/core/logic/__init__.py +0 -193
  474. v0/relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
  475. v0/relationalai/early_access/dsl/core/logic/exists.py +0 -223
  476. v0/relationalai/early_access/dsl/core/logic/helper.py +0 -163
  477. v0/relationalai/early_access/dsl/core/namespaces.py +0 -32
  478. v0/relationalai/early_access/dsl/core/relations.py +0 -276
  479. v0/relationalai/early_access/dsl/core/rules.py +0 -112
  480. v0/relationalai/early_access/dsl/core/std/__init__.py +0 -45
  481. v0/relationalai/early_access/dsl/core/temporal/recall.py +0 -6
  482. v0/relationalai/early_access/dsl/core/types/__init__.py +0 -270
  483. v0/relationalai/early_access/dsl/core/types/concepts.py +0 -128
  484. v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
  485. v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
  486. v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
  487. v0/relationalai/early_access/dsl/core/types/standard.py +0 -92
  488. v0/relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
  489. v0/relationalai/early_access/dsl/core/types/variables.py +0 -203
  490. v0/relationalai/early_access/dsl/ir/compiler.py +0 -318
  491. v0/relationalai/early_access/dsl/ir/executor.py +0 -260
  492. v0/relationalai/early_access/dsl/ontologies/constraints.py +0 -88
  493. v0/relationalai/early_access/dsl/ontologies/export.py +0 -30
  494. v0/relationalai/early_access/dsl/ontologies/models.py +0 -453
  495. v0/relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
  496. v0/relationalai/early_access/dsl/ontologies/readings.py +0 -60
  497. v0/relationalai/early_access/dsl/ontologies/relationships.py +0 -322
  498. v0/relationalai/early_access/dsl/ontologies/roles.py +0 -87
  499. v0/relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
  500. v0/relationalai/early_access/dsl/orm/constraints.py +0 -438
  501. v0/relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
  502. v0/relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
  503. v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
  504. v0/relationalai/early_access/dsl/orm/measures/measures.py +0 -299
  505. v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
  506. v0/relationalai/early_access/dsl/orm/models.py +0 -256
  507. v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
  508. v0/relationalai/early_access/dsl/orm/printer.py +0 -469
  509. v0/relationalai/early_access/dsl/orm/reasoners.py +0 -480
  510. v0/relationalai/early_access/dsl/orm/relations.py +0 -19
  511. v0/relationalai/early_access/dsl/orm/relationships.py +0 -251
  512. v0/relationalai/early_access/dsl/orm/types.py +0 -42
  513. v0/relationalai/early_access/dsl/orm/utils.py +0 -79
  514. v0/relationalai/early_access/dsl/orm/verb.py +0 -204
  515. v0/relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
  516. v0/relationalai/early_access/dsl/relations.py +0 -170
  517. v0/relationalai/early_access/dsl/rulesets.py +0 -69
  518. v0/relationalai/early_access/dsl/schemas/__init__.py +0 -450
  519. v0/relationalai/early_access/dsl/schemas/builder.py +0 -48
  520. v0/relationalai/early_access/dsl/schemas/comp_names.py +0 -51
  521. v0/relationalai/early_access/dsl/schemas/components.py +0 -203
  522. v0/relationalai/early_access/dsl/schemas/contexts.py +0 -156
  523. v0/relationalai/early_access/dsl/schemas/exprs.py +0 -89
  524. v0/relationalai/early_access/dsl/schemas/fragments.py +0 -464
  525. v0/relationalai/early_access/dsl/serialization.py +0 -79
  526. v0/relationalai/early_access/dsl/serialize/exporter.py +0 -163
  527. v0/relationalai/early_access/dsl/snow/api.py +0 -104
  528. v0/relationalai/early_access/dsl/snow/common.py +0 -76
  529. v0/relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
  530. v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
  531. v0/relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
  532. v0/relationalai/early_access/dsl/types/__init__.py +0 -40
  533. v0/relationalai/early_access/dsl/types/concepts.py +0 -12
  534. v0/relationalai/early_access/dsl/types/entities.py +0 -135
  535. v0/relationalai/early_access/dsl/types/values.py +0 -17
  536. v0/relationalai/early_access/dsl/utils.py +0 -102
  537. v0/relationalai/early_access/graphs/__init__.py +0 -13
  538. v0/relationalai/early_access/lqp/__init__.py +0 -12
  539. v0/relationalai/early_access/lqp/compiler/__init__.py +0 -12
  540. v0/relationalai/early_access/lqp/constructors/__init__.py +0 -18
  541. v0/relationalai/early_access/lqp/executor/__init__.py +0 -12
  542. v0/relationalai/early_access/lqp/ir/__init__.py +0 -12
  543. v0/relationalai/early_access/lqp/passes/__init__.py +0 -12
  544. v0/relationalai/early_access/lqp/pragmas/__init__.py +0 -12
  545. v0/relationalai/early_access/lqp/primitives/__init__.py +0 -12
  546. v0/relationalai/early_access/lqp/types/__init__.py +0 -12
  547. v0/relationalai/early_access/lqp/utils/__init__.py +0 -12
  548. v0/relationalai/early_access/lqp/validators/__init__.py +0 -12
  549. v0/relationalai/early_access/metamodel/__init__.py +0 -58
  550. v0/relationalai/early_access/metamodel/builtins/__init__.py +0 -12
  551. v0/relationalai/early_access/metamodel/compiler/__init__.py +0 -12
  552. v0/relationalai/early_access/metamodel/dependency/__init__.py +0 -12
  553. v0/relationalai/early_access/metamodel/factory/__init__.py +0 -17
  554. v0/relationalai/early_access/metamodel/helpers/__init__.py +0 -12
  555. v0/relationalai/early_access/metamodel/ir/__init__.py +0 -14
  556. v0/relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
  557. v0/relationalai/early_access/metamodel/typer/__init__.py +0 -3
  558. v0/relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
  559. v0/relationalai/early_access/metamodel/types/__init__.py +0 -15
  560. v0/relationalai/early_access/metamodel/util/__init__.py +0 -15
  561. v0/relationalai/early_access/metamodel/visitor/__init__.py +0 -12
  562. v0/relationalai/early_access/rel/__init__.py +0 -12
  563. v0/relationalai/early_access/rel/executor/__init__.py +0 -12
  564. v0/relationalai/early_access/rel/rel_utils/__init__.py +0 -12
  565. v0/relationalai/early_access/rel/rewrite/__init__.py +0 -7
  566. v0/relationalai/early_access/solvers/__init__.py +0 -19
  567. v0/relationalai/early_access/sql/__init__.py +0 -11
  568. v0/relationalai/early_access/sql/executor/__init__.py +0 -3
  569. v0/relationalai/early_access/sql/rewrite/__init__.py +0 -3
  570. v0/relationalai/early_access/tests/logging/__init__.py +0 -12
  571. v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
  572. v0/relationalai/early_access/tests/utils/__init__.py +0 -12
  573. v0/relationalai/environments/__init__.py +0 -35
  574. v0/relationalai/environments/base.py +0 -381
  575. v0/relationalai/environments/colab.py +0 -14
  576. v0/relationalai/environments/generic.py +0 -71
  577. v0/relationalai/environments/ipython.py +0 -68
  578. v0/relationalai/environments/jupyter.py +0 -9
  579. v0/relationalai/environments/snowbook.py +0 -169
  580. v0/relationalai/errors.py +0 -2455
  581. v0/relationalai/experimental/SF.py +0 -38
  582. v0/relationalai/experimental/inspect.py +0 -47
  583. v0/relationalai/experimental/pathfinder/__init__.py +0 -158
  584. v0/relationalai/experimental/pathfinder/api.py +0 -160
  585. v0/relationalai/experimental/pathfinder/automaton.py +0 -584
  586. v0/relationalai/experimental/pathfinder/bridge.py +0 -226
  587. v0/relationalai/experimental/pathfinder/compiler.py +0 -416
  588. v0/relationalai/experimental/pathfinder/datalog.py +0 -214
  589. v0/relationalai/experimental/pathfinder/diagnostics.py +0 -56
  590. v0/relationalai/experimental/pathfinder/filter.py +0 -236
  591. v0/relationalai/experimental/pathfinder/glushkov.py +0 -439
  592. v0/relationalai/experimental/pathfinder/options.py +0 -265
  593. v0/relationalai/experimental/pathfinder/rpq.py +0 -344
  594. v0/relationalai/experimental/pathfinder/transition.py +0 -200
  595. v0/relationalai/experimental/pathfinder/utils.py +0 -26
  596. v0/relationalai/experimental/paths/api.py +0 -143
  597. v0/relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
  598. v0/relationalai/experimental/paths/examples/basic_example.py +0 -40
  599. v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
  600. v0/relationalai/experimental/paths/examples/movie_example.py +0 -77
  601. v0/relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
  602. v0/relationalai/experimental/paths/examples/paths_example.py +0 -116
  603. v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
  604. v0/relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
  605. v0/relationalai/experimental/paths/graph.py +0 -185
  606. v0/relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
  607. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
  608. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
  609. v0/relationalai/experimental/paths/path_algorithms/single.py +0 -59
  610. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
  611. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
  612. v0/relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
  613. v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
  614. v0/relationalai/experimental/paths/path_algorithms/usp.py +0 -150
  615. v0/relationalai/experimental/paths/product_graph.py +0 -93
  616. v0/relationalai/experimental/paths/rpq/automaton.py +0 -584
  617. v0/relationalai/experimental/paths/rpq/diagnostics.py +0 -56
  618. v0/relationalai/experimental/paths/rpq/rpq.py +0 -378
  619. v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
  620. v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
  621. v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
  622. v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
  623. v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
  624. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
  625. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
  626. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
  627. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
  628. v0/relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
  629. v0/relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
  630. v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
  631. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
  632. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
  633. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
  634. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
  635. v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
  636. v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
  637. v0/relationalai/experimental/paths/tree_agg.py +0 -168
  638. v0/relationalai/experimental/paths/utilities/iterators.py +0 -27
  639. v0/relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
  640. v0/relationalai/experimental/solvers.py +0 -1087
  641. v0/relationalai/loaders/csv.py +0 -195
  642. v0/relationalai/loaders/loader.py +0 -177
  643. v0/relationalai/loaders/types.py +0 -23
  644. v0/relationalai/rel_emitter.py +0 -373
  645. v0/relationalai/rel_utils.py +0 -185
  646. v0/relationalai/semantics/__init__.py +0 -29
  647. v0/relationalai/semantics/devtools/benchmark_lqp.py +0 -536
  648. v0/relationalai/semantics/devtools/compilation_manager.py +0 -294
  649. v0/relationalai/semantics/devtools/extract_lqp.py +0 -110
  650. v0/relationalai/semantics/internal/internal.py +0 -3785
  651. v0/relationalai/semantics/internal/snowflake.py +0 -324
  652. v0/relationalai/semantics/lqp/builtins.py +0 -16
  653. v0/relationalai/semantics/lqp/compiler.py +0 -22
  654. v0/relationalai/semantics/lqp/constructors.py +0 -68
  655. v0/relationalai/semantics/lqp/executor.py +0 -469
  656. v0/relationalai/semantics/lqp/intrinsics.py +0 -24
  657. v0/relationalai/semantics/lqp/model2lqp.py +0 -839
  658. v0/relationalai/semantics/lqp/passes.py +0 -680
  659. v0/relationalai/semantics/lqp/primitives.py +0 -252
  660. v0/relationalai/semantics/lqp/result_helpers.py +0 -202
  661. v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -57
  662. v0/relationalai/semantics/lqp/rewrite/cdc.py +0 -216
  663. v0/relationalai/semantics/lqp/rewrite/extract_common.py +0 -338
  664. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +0 -449
  665. v0/relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
  666. v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -314
  667. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -296
  668. v0/relationalai/semantics/lqp/rewrite/splinter.py +0 -76
  669. v0/relationalai/semantics/lqp/types.py +0 -101
  670. v0/relationalai/semantics/lqp/utils.py +0 -160
  671. v0/relationalai/semantics/lqp/validators.py +0 -57
  672. v0/relationalai/semantics/metamodel/__init__.py +0 -40
  673. v0/relationalai/semantics/metamodel/builtins.py +0 -774
  674. v0/relationalai/semantics/metamodel/compiler.py +0 -133
  675. v0/relationalai/semantics/metamodel/dependency.py +0 -862
  676. v0/relationalai/semantics/metamodel/executor.py +0 -61
  677. v0/relationalai/semantics/metamodel/factory.py +0 -287
  678. v0/relationalai/semantics/metamodel/helpers.py +0 -361
  679. v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
  680. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -210
  681. v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
  682. v0/relationalai/semantics/metamodel/rewrite/flatten.py +0 -549
  683. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -165
  684. v0/relationalai/semantics/metamodel/typer/checker.py +0 -353
  685. v0/relationalai/semantics/metamodel/typer/typer.py +0 -1395
  686. v0/relationalai/semantics/metamodel/util.py +0 -505
  687. v0/relationalai/semantics/reasoners/__init__.py +0 -10
  688. v0/relationalai/semantics/reasoners/graph/__init__.py +0 -37
  689. v0/relationalai/semantics/reasoners/graph/core.py +0 -9020
  690. v0/relationalai/semantics/reasoners/optimization/__init__.py +0 -68
  691. v0/relationalai/semantics/reasoners/optimization/common.py +0 -88
  692. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
  693. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1163
  694. v0/relationalai/semantics/rel/builtins.py +0 -40
  695. v0/relationalai/semantics/rel/compiler.py +0 -989
  696. v0/relationalai/semantics/rel/executor.py +0 -359
  697. v0/relationalai/semantics/rel/rel.py +0 -482
  698. v0/relationalai/semantics/rel/rel_utils.py +0 -276
  699. v0/relationalai/semantics/snowflake/__init__.py +0 -3
  700. v0/relationalai/semantics/sql/compiler.py +0 -2503
  701. v0/relationalai/semantics/sql/executor/duck_db.py +0 -52
  702. v0/relationalai/semantics/sql/executor/result_helpers.py +0 -64
  703. v0/relationalai/semantics/sql/executor/snowflake.py +0 -145
  704. v0/relationalai/semantics/sql/rewrite/denormalize.py +0 -222
  705. v0/relationalai/semantics/sql/rewrite/double_negation.py +0 -49
  706. v0/relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
  707. v0/relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
  708. v0/relationalai/semantics/sql/sql.py +0 -504
  709. v0/relationalai/semantics/std/__init__.py +0 -54
  710. v0/relationalai/semantics/std/constraints.py +0 -43
  711. v0/relationalai/semantics/std/datetime.py +0 -363
  712. v0/relationalai/semantics/std/decimals.py +0 -62
  713. v0/relationalai/semantics/std/floats.py +0 -7
  714. v0/relationalai/semantics/std/integers.py +0 -22
  715. v0/relationalai/semantics/std/math.py +0 -141
  716. v0/relationalai/semantics/std/pragmas.py +0 -11
  717. v0/relationalai/semantics/std/re.py +0 -83
  718. v0/relationalai/semantics/std/std.py +0 -14
  719. v0/relationalai/semantics/std/strings.py +0 -63
  720. v0/relationalai/semantics/tests/__init__.py +0 -0
  721. v0/relationalai/semantics/tests/test_snapshot_abstract.py +0 -143
  722. v0/relationalai/semantics/tests/test_snapshot_base.py +0 -9
  723. v0/relationalai/semantics/tests/utils.py +0 -46
  724. v0/relationalai/std/__init__.py +0 -70
  725. v0/relationalai/tools/__init__.py +0 -0
  726. v0/relationalai/tools/cli.py +0 -1940
  727. v0/relationalai/tools/cli_controls.py +0 -1826
  728. v0/relationalai/tools/cli_helpers.py +0 -390
  729. v0/relationalai/tools/debugger.py +0 -183
  730. v0/relationalai/tools/debugger_client.py +0 -109
  731. v0/relationalai/tools/debugger_server.py +0 -302
  732. v0/relationalai/tools/dev.py +0 -685
  733. v0/relationalai/tools/qb_debugger.py +0 -425
  734. v0/relationalai/util/clean_up_databases.py +0 -95
  735. v0/relationalai/util/format.py +0 -123
  736. v0/relationalai/util/list_databases.py +0 -9
  737. v0/relationalai/util/otel_configuration.py +0 -25
  738. v0/relationalai/util/otel_handler.py +0 -484
  739. v0/relationalai/util/snowflake_handler.py +0 -88
  740. v0/relationalai/util/span_format_test.py +0 -43
  741. v0/relationalai/util/span_tracker.py +0 -207
  742. v0/relationalai/util/spans_file_handler.py +0 -72
  743. v0/relationalai/util/tracing_handler.py +0 -34
  744. /relationalai/{semantics/frontend → analysis}/__init__.py +0 -0
  745. {v0/relationalai → relationalai}/analysis/mechanistic.py +0 -0
  746. {v0/relationalai → relationalai}/analysis/whynot.py +0 -0
  747. /relationalai/{shims → auth}/__init__.py +0 -0
  748. {v0/relationalai → relationalai}/auth/jwt_generator.py +0 -0
  749. {v0/relationalai → relationalai}/auth/oauth_callback_server.py +0 -0
  750. {v0/relationalai → relationalai}/auth/token_handler.py +0 -0
  751. {v0/relationalai → relationalai}/auth/util.py +0 -0
  752. {v0/relationalai/clients → relationalai/clients/resources/snowflake}/cache_store.py +0 -0
  753. {v0/relationalai → relationalai}/compiler.py +0 -0
  754. {v0/relationalai → relationalai}/dependencies.py +0 -0
  755. {v0/relationalai → relationalai}/docutils.py +0 -0
  756. {v0/relationalai/analysis → relationalai/early_access}/__init__.py +0 -0
  757. {v0/relationalai → relationalai}/early_access/dsl/__init__.py +0 -0
  758. {v0/relationalai/auth → relationalai/early_access/dsl/adapters}/__init__.py +0 -0
  759. {v0/relationalai/early_access → relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
  760. {v0/relationalai → relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
  761. {v0/relationalai/early_access/dsl/adapters → relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
  762. {v0/relationalai → relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
  763. {v0/relationalai/early_access/dsl/adapters/orm → relationalai/early_access/dsl/bindings}/__init__.py +0 -0
  764. {v0/relationalai/early_access/dsl/adapters/owl → relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
  765. {v0/relationalai/early_access/dsl/bindings → relationalai/early_access/dsl/codegen}/__init__.py +0 -0
  766. {v0/relationalai → relationalai}/early_access/dsl/constants.py +0 -0
  767. {v0/relationalai → relationalai}/early_access/dsl/core/__init__.py +0 -0
  768. {v0/relationalai → relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
  769. {v0/relationalai → relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
  770. {v0/relationalai → relationalai}/early_access/dsl/core/stack.py +0 -0
  771. {v0/relationalai/early_access/dsl/bindings/legacy → relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
  772. {v0/relationalai → relationalai}/early_access/dsl/core/utils.py +0 -0
  773. {v0/relationalai/early_access/dsl/codegen → relationalai/early_access/dsl/ir}/__init__.py +0 -0
  774. {v0/relationalai/early_access/dsl/core/temporal → relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
  775. {v0/relationalai → relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
  776. {v0/relationalai/early_access/dsl/ir → relationalai/early_access/dsl/orm}/__init__.py +0 -0
  777. {v0/relationalai/early_access/dsl/ontologies → relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
  778. {v0/relationalai → relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
  779. {v0/relationalai/early_access/dsl/orm → relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
  780. {v0/relationalai/early_access/dsl/orm/measures → relationalai/early_access/dsl/serialize}/__init__.py +0 -0
  781. {v0/relationalai → relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
  782. {v0/relationalai → relationalai}/early_access/dsl/serialize/model.py +0 -0
  783. {v0/relationalai/early_access/dsl/physical_metadata → relationalai/early_access/dsl/snow}/__init__.py +0 -0
  784. {v0/relationalai → relationalai}/early_access/tests/__init__.py +0 -0
  785. {v0/relationalai → relationalai}/environments/ci.py +0 -0
  786. {v0/relationalai → relationalai}/environments/hex.py +0 -0
  787. {v0/relationalai → relationalai}/environments/terminal.py +0 -0
  788. {v0/relationalai → relationalai}/experimental/__init__.py +0 -0
  789. {v0/relationalai → relationalai}/experimental/graphs.py +0 -0
  790. {v0/relationalai → relationalai}/experimental/paths/__init__.py +0 -0
  791. {v0/relationalai → relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
  792. {v0/relationalai → relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
  793. {v0/relationalai → relationalai}/experimental/paths/rpq/__init__.py +0 -0
  794. {v0/relationalai → relationalai}/experimental/paths/rpq/filter.py +0 -0
  795. {v0/relationalai → relationalai}/experimental/paths/rpq/glushkov.py +0 -0
  796. {v0/relationalai → relationalai}/experimental/paths/rpq/transition.py +0 -0
  797. {v0/relationalai → relationalai}/experimental/paths/utilities/__init__.py +0 -0
  798. {v0/relationalai → relationalai}/experimental/paths/utilities/utilities.py +0 -0
  799. {v0/relationalai/early_access/dsl/serialize → relationalai/loaders}/__init__.py +0 -0
  800. {v0/relationalai → relationalai}/metagen.py +0 -0
  801. {v0/relationalai → relationalai}/metamodel.py +0 -0
  802. {v0/relationalai → relationalai}/rel.py +0 -0
  803. {v0/relationalai → relationalai}/semantics/devtools/__init__.py +0 -0
  804. {v0/relationalai → relationalai}/semantics/internal/__init__.py +0 -0
  805. {v0/relationalai → relationalai}/semantics/internal/annotations.py +0 -0
  806. {v0/relationalai → relationalai}/semantics/lqp/__init__.py +0 -0
  807. {v0/relationalai → relationalai}/semantics/lqp/ir.py +0 -0
  808. {v0/relationalai → relationalai}/semantics/lqp/pragmas.py +0 -0
  809. {v0/relationalai → relationalai}/semantics/lqp/rewrite/__init__.py +0 -0
  810. {v0/relationalai → relationalai}/semantics/metamodel/dataflow.py +0 -0
  811. {v0/relationalai → relationalai}/semantics/metamodel/ir.py +0 -0
  812. {v0/relationalai → relationalai}/semantics/metamodel/rewrite/__init__.py +0 -0
  813. {v0/relationalai → relationalai}/semantics/metamodel/typer/__init__.py +0 -0
  814. {v0/relationalai → relationalai}/semantics/metamodel/types.py +0 -0
  815. {v0/relationalai → relationalai}/semantics/metamodel/visitor.py +0 -0
  816. {v0/relationalai → relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
  817. {v0/relationalai → relationalai}/semantics/rel/__init__.py +0 -0
  818. {v0/relationalai → relationalai}/semantics/sql/__init__.py +0 -0
  819. {v0/relationalai → relationalai}/semantics/sql/executor/__init__.py +0 -0
  820. {v0/relationalai → relationalai}/semantics/sql/rewrite/__init__.py +0 -0
  821. {v0/relationalai/early_access/dsl/snow → relationalai/semantics/tests}/__init__.py +0 -0
  822. {v0/relationalai → relationalai}/semantics/tests/logging.py +0 -0
  823. {v0/relationalai → relationalai}/std/aggregates.py +0 -0
  824. {v0/relationalai → relationalai}/std/dates.py +0 -0
  825. {v0/relationalai → relationalai}/std/graphs.py +0 -0
  826. {v0/relationalai → relationalai}/std/inspect.py +0 -0
  827. {v0/relationalai → relationalai}/std/math.py +0 -0
  828. {v0/relationalai → relationalai}/std/re.py +0 -0
  829. {v0/relationalai → relationalai}/std/strings.py +0 -0
  830. {v0/relationalai/loaders → relationalai/tools}/__init__.py +0 -0
  831. {v0/relationalai → relationalai}/tools/cleanup_snapshots.py +0 -0
  832. {v0/relationalai → relationalai}/tools/constants.py +0 -0
  833. {v0/relationalai → relationalai}/tools/query_utils.py +0 -0
  834. {v0/relationalai → relationalai}/tools/snapshot_viewer.py +0 -0
  835. {v0/relationalai → relationalai}/util/__init__.py +0 -0
  836. {v0/relationalai → relationalai}/util/constants.py +0 -0
  837. {v0/relationalai → relationalai}/util/graph.py +0 -0
  838. {v0/relationalai → relationalai}/util/timeout.py +0 -0
@@ -0,0 +1,1951 @@
1
+ // Pathfinder package
2
+
3
+ // utilities (from model/utilities.rel)
4
+ doc"""
5
+ Contains utility methods for working with paths and methods identified as
6
+ of potential general interest (for consideration to be moved to other
7
+ libraries).
8
+ """
9
+ namespace pathfinder::utilities
10
+
11
+ doc"""
12
+ dynamic_prefix_sum[{List}]
13
+
14
+ Computes sums of each prefix of an enumerated list `List(i, val)` i.e.,
15
+ ```
16
+ dynamic_prefix_sum[List, i] = List[1] + ... + List[i].
17
+ ```
18
+ Assumes that `List` is a binary predicate: the first attribute takes consecutive integers
19
+ values starting from 1, the second attribute supports addition. Uses a dynamic-programming
20
+ approach that is particularly efficient for large lists.
21
+ """
22
+ @inline
23
+ def dynamic_prefix_sum({List}, i, val): _dynamic_prefix_sum(List, :result, i, val)
24
+
25
+ // Implementation of prefix sums using divide-and-conquer approach
26
+ @outline
27
+ module _dynamic_prefix_sum[{List}]
28
+ // Backward partial sums of exponentially growing lengths.
29
+ // B[i, j] = List[i - j + 1] + ... + List[i] for j∈{1,2,4,...} s.t. i|j
30
+ def B(i, 1, val): List(i, val)
31
+ def B(i, j, val):
32
+ exists((k) |
33
+ val = B[i, k] + B[i - k, k] and
34
+ j = 2 * k and
35
+ modulo[i, j] = 0
36
+ )
37
+
38
+ // Longest backward partial sum
39
+ def L[i, j]: (B[i, j], j = max[(k) : B(i, k, _)])
40
+
41
+ // Final result
42
+ def result(i, val): L(i, i, val)
43
+ def result(i, val): exists((j) | val = L[i, j] + result[i - j])
44
+ end
45
+
46
+ doc"""
47
+ linear_prefix_sum[{List}]
48
+
49
+ Computes sums of each prefix of an enumerated list `List(i, val)` i.e.,
50
+ ```
51
+ linear_prefix_sum[List, i] = List[1] + ... + List[i].
52
+ ```
53
+ Assumes that `List` is a binary predicate: the first attribute takes consecutive integers
54
+ values starting from 1, the second attribute supports addition. This implementation uses
55
+ the natural linear recursion approach.
56
+ """
57
+ @inline
58
+ def linear_prefix_sum({List}, i, val): _linear_prefix_sum(List, :result, i, val)
59
+
60
+ // Implementation of prefix sums using divide-and-conquer approach
61
+ @outline
62
+ module _linear_prefix_sum[{List}]
63
+ @function
64
+ def result(i, val) : List(i, val) and i = 1
65
+ def result(i, val) : exists( (w, v) | result(i-1, w) and List(i, v) and val = w+v )
66
+ end
67
+
68
+ doc"""
69
+ hash_paths[{Paths}]
70
+
71
+ Replaces the edge identifier of every path in the input collection `Paths` with a unique
72
+ _canonical_ identifier of the path. The canonical identifier of a path is computed by
73
+ hashing the path contents. In particular, if the input collection contains repeated
74
+ paths, they will be collapsed into a single path.
75
+ """
76
+ @inline
77
+ def hash_paths({Paths}, new_id, x...):
78
+ exists((old_id) | Paths(old_id, x...) and _path_hash_reduce(Paths[old_id], new_id))
79
+
80
+ // Computes the hash of a path ensuring proper handling of empty (single node) paths
81
+ // and explicitly determining the order of hashing node ids and edge labels.
82
+ @inline
83
+ def _path_hash_reduce[{Path}] :
84
+ rel_primitive_reduce[
85
+ murmurhash3f_with_seed,
86
+ uint128[0],
87
+ {(1, hash_reduce[Path[:node]]); (2, hash_reduce[Path[:edge_label]])}
88
+ ]
89
+
90
+
91
+ doc"""
92
+ hash_reduce[{R}]
93
+
94
+ Reduces a relation `R` into a single number (UInt128) representing its hash value.
95
+ """
96
+ @inline
97
+ def hash_reduce[{R}]: rel_primitive_reduce[murmurhash3f_with_seed, uint128[0], hash[R]]
98
+
99
+
100
+ doc"""
101
+ pivot_paths[{R}, {L}]
102
+
103
+ Pivots paths represented horizontally in a relation to a vertical representation. The
104
+ input relation `R` must have a fixed arity and the relation `L` is a relation
105
+ with a single tuple of strings to be used as labels for the edges in the path. Naturally,
106
+ `arity(L) = arity(R)-1`. Example usage:
107
+
108
+ ```
109
+ pivot_paths[{ (1,2,3); (3,2,4) }, {"a", "b"}]
110
+ ```
111
+
112
+ The implementation does not check that `R` is a relation with uniform arity, and that `L`
113
+ is a relation of arity one lesser with a single tuple of strings. Should those
114
+ assumptions be violated, the behavior is undefined.
115
+ """
116
+ @inline
117
+ def pivot_paths[{R}, {L}]: {
118
+ _pivot_paths[R, pivot[L], :paths]
119
+ }
120
+
121
+ // Builds a path set from a horizontal relation of node identifiers and a (vertical) edge of
122
+ // labels relation. The relation `R` must have a fixed arity and the relation `Labels` is a
123
+ // ordered list of strings to be used as labels for the edges in the path.
124
+ @outline
125
+ module _pivot_paths[{R}, {Labels}]
126
+ def path_count { count[R] }
127
+ def path_length { arity[R] }
128
+ def sorted_paths { sort[R] }
129
+ def dom_paths { range[1, path_count, 1] }
130
+ module paths[i in dom_paths]
131
+ def node(j, n): {
132
+ range(0, path_length-1, 1, j) and
133
+ pivot(sorted_paths[i], j+1, n)
134
+ }
135
+ def edge_label(j, l): {
136
+ range(1, path_length - 1, 1, j) and
137
+ Labels(j, l)
138
+ }
139
+ end
140
+ end
141
+
142
+
143
+ doc"""
144
+ canonical_index[i, j]
145
+
146
+ Defines the canonical index of a pair of positive integers `(i, j)` by mapping it
147
+ (bijectively) onto the set of positive integers. Its formulation proves that ℕ₊ ~ ℕ₊×ℕ₊.
148
+
149
+ The principle of the enumeration is visualized on the fragment below.
150
+
151
+ ```
152
+
153
+ +--+
154
+ 5 |11|
155
+ +--+--+
156
+ 4 | 7|12|
157
+ +--+--+--+
158
+ m 3 | 4| 8|13|
159
+ +--+--+--+--+
160
+ 2 | 2| 5| 9|14|
161
+ +--+--+--+--+--+
162
+ 1 | 1| 3| 6|10|15|
163
+ ∙--+--+--+--+--+-→
164
+ 1 2 3 4 5
165
+ n
166
+ ```
167
+ """
168
+ @inline
169
+ def canonical_index[m, n]: {
170
+ m + ((m + n - 2) * (m + n - 1 ) ÷ 2)
171
+ }
172
+
173
+ doc"""
174
+ reindex_nodes[{Paths}, path]
175
+
176
+ Implementations of the `find_paths` methods index the nodes by their distance from the
177
+ source node, thus enumerating them starting from 0. In Rel, however, it is customary to
178
+ enumerate lists starting from index 1.
179
+ """
180
+ @outline
181
+ module reindex_nodes[{Paths}, path]
182
+ def node(i in Int64, v): Paths(path, :node, i-1, v)
183
+ def edge_label(i, lab): Paths(path, :edge_label, i, lab)
184
+ end
185
+
186
+ doc"""
187
+ reindex_edges[{Paths}, path]
188
+
189
+ Implementations of the `find_paths` methods index the edges starting from 1. The following
190
+ method reindexes the edges starting from 0.
191
+ """
192
+ @outline
193
+ module reindex_edges[{Paths}, path]
194
+ def node(i in Int64, v): Paths(path, :node, i, v)
195
+ def edge_label(i, lab): Paths(path, :edge_label, i+1, lab)
196
+ end
197
+
198
+ doc"""
199
+ KleenePlus[{R}]
200
+
201
+ Higher-order Kleene star operator defining the transitive closure of the given binary
202
+ relation `R`.
203
+ """
204
+ @outline
205
+ def KleenePlus({R}, x, y):
206
+ R(x, y) or exists((z) | KleenePlus(R, x, z) and R(z, y))
207
+
208
+ doc"""
209
+ KleeneStar[{N}, {R}]
210
+
211
+ Higher-order Kleene star operator defining the transitive and reflexive closure of the
212
+ given binary relation `R` on the set of nodes `N` (the domain).
213
+ """
214
+ @inline
215
+ def KleeneStar({N}, {R}, x, y): (x = y and N(x)) or KleenePlus(R, x, y)
216
+
217
+ end // namespace pathfinder::utilities
218
+
219
+ // _internal/utilities (from model/_internal/utilities.rel)
220
+ namespace pathfinder::_internal::utilities
221
+
222
+ from ::pathfinder::utilities import
223
+ hash_paths, reindex_nodes, reindex_edges, dynamic_prefix_sum, linear_prefix_sum
224
+
225
+ //
226
+ // Return a single tuple deterministically from the relation `R`.
227
+ //
228
+ @inline
229
+ def some[{R}]: top[1, R, 1]
230
+
231
+ @inline
232
+ def invert_pg({PG}, q, p, a, v, u): PG(p, q, a, u, v)
233
+
234
+ doc"""
235
+ module walks_count[{Edge}, {Target}, {Config}]
236
+
237
+ ## Inputs
238
+ Target : target nodes `u`
239
+ Edge : `(u, v, a)`, denoting edge `u --> v` with label `a`
240
+ Config : configuration parameters
241
+ Config[:semantics] ∈ { :shortest_paths, :walks }
242
+
243
+ ## Outputs
244
+ num_walks : num_walks[u] is the number of shortest walks for the
245
+ shortest_paths semantics, or the number of walks for
246
+ all the other semantics, from u to a node in Target
247
+ """
248
+ @inline
249
+ def walks_count[{Edge}, {Target}, {Config}]: {
250
+ if_then_else[
251
+ Config(:semantics, :shortest_paths),
252
+ _walks_count[:shortest_paths, Edge, Target],
253
+ _walks_count[:max_path_length, Edge, Target]
254
+ ]
255
+ }
256
+
257
+
258
+ @outline
259
+ module _walks_count[:shortest_paths, {Edge}, {Target}]
260
+
261
+ def num_walks[u]: (1, Target(u))
262
+ def num_walks[u]: sum[[v, a]: num_walks[v] where Edge(u, v, a)]
263
+
264
+ end // _walks_count[:shortest_paths]
265
+
266
+
267
+ @outline
268
+ module _walks_count[:max_path_length, {Edge}, {Target}]
269
+
270
+ // boundary(u) holds if u does not have any outgoing edges.
271
+ def boundary(u): Target(u) and not Edge(u, _, _)
272
+ def boundary(u): Edge(_, u, _) and not Edge(u, _, _)
273
+
274
+
275
+ // num_walks counts the number of walks from a node u using dynamic programming from
276
+ // the nodes in the boundary. For a non-boundary node that is not a target node,
277
+ // the number of walks is equal to the sum of the number of walks from its
278
+ // children. For a non-boundary node that is a target node, the number of walks
279
+ // is equal to the sum of the number of walks from its children plus one, as
280
+ // there is one walk that ends in u.
281
+ def num_walks[u]: (1, boundary(u) and Target(u))
282
+ def num_walks[u]: (0, boundary(u) and not Target(u))
283
+ def num_walks[u]:
284
+ sum[[v, a] : num_walks[v] where Edge(u, v, a)] + 1 where not boundary(u) and Target(u)
285
+ def num_walks[u]:
286
+ sum[[v, a] : num_walks[v] where Edge(u, v, a)] where not boundary(u) and not Target(u)
287
+
288
+ end // _walks_count[:max_path_length]
289
+
290
+
291
+ doc"""
292
+ enumeration_helpers[{Edge}, {Source}, {Target}, {USP_Node_To_Node}, {NSP}, {Config}]
293
+
294
+ ## Inputs
295
+ Edge : (uu, vv, a), denoting edge uu --> vv with label a
296
+ Source : source nodes `uu`
297
+ Target : target nodes `vv`
298
+ USP_Node_To_Node : mapping that convert uu to u
299
+ NSP : NSP[uu] is the number of shortest walks for the shortest_paths semantics, or
300
+ the number of walks for all the other semantics, from uu to a node in Target
301
+ Config : configuration parameters
302
+ Config[:path_enum_partials] ∈ { :linear, :dynamic}
303
+
304
+ ## Outputs
305
+ paths : If the selector is :all, then paths is the set of all shortest paths
306
+ from Source to Target for the shortest_paths semantics, and all walks
307
+ from Source to Target of length at most Config[:max_path_length]
308
+ for the walks semantics.
309
+ If the selector is :limit, then paths is the set with the first
310
+ Config[:path_count] shortest paths from Source to Target for
311
+ the shortest_paths semantics, and the set with the first
312
+ Config[:path_count] walks from Source to Target of length at most
313
+ Config[:max_path_length] for the walks semantics.
314
+ total_walks : total number of shortest walks for the shortest_paths semantics,
315
+ or of walks for all the other semantics, from Source to Target
316
+ """
317
+ @outline
318
+ module enumeration_helpers[
319
+ {Edge}, {Source}, {Target}, {USP_Node_To_Node}, {NSP}, {Config}
320
+ ]
321
+
322
+ // (uu, i, vv, a) : the `i`th outbound edge from `uu` is labeled `a` and goes to `vv`
323
+ def neighbor(uu, i, vv, a): enumerate({(ww, _a): Edge(uu, ww, _a)}, i, vv, a)
324
+
325
+ // Decomposition of number of shortest paths that traverses nodes (and edges).
326
+ // Edge case is simple and we keep the relative position i of the edge.
327
+ def edge_nsp(uu, i, val): exists((vv) | val = NSP[vv] and neighbor(uu, i, vv, _))
328
+
329
+ // acc_nsp[uu, i] = the total number of shortest paths that traverse the first i
330
+ // outbound edges of uu.
331
+ @function
332
+ def acc_nsp[uu]: { compute_acc_nsp[Config[:path_enum_partials], edge_nsp[uu]] }
333
+ @inline
334
+ def compute_acc_nsp[:linear, {EdgeNsp}]: { linear_prefix_sum[EdgeNsp] }
335
+ @inline
336
+ def compute_acc_nsp[:dynamic, {EdgeNsp}]: { dynamic_prefix_sum[EdgeNsp] }
337
+
338
+
339
+ // Enumerate all sources, from s[1] to s[k], and then compute
340
+ // running_sum[i] := ∑_{j=1}^{i} nsp[s[j]].
341
+ // Binary prefix-sum is used because the array we are taking the prefix sum of is
342
+ // expected to be large.
343
+ def relevant_source(uu) : Source(uu) and NSP(uu, _)
344
+ def numbered_source { enumerate[relevant_source] }
345
+ def nsp_from_source[i]: NSP[numbered_source[i]]
346
+
347
+
348
+ def _running_sum { dynamic_prefix_sum[nsp_from_source] }
349
+
350
+ def running_sum { (_running_sum, not Config(:selector, :limit)) }
351
+ def running_sum(uu, v):
352
+ exists((w) |
353
+ w = _running_sum[uu] and
354
+ v = minimum[w, Config[:path_count]] and
355
+ Config(:selector, :limit)
356
+ )
357
+
358
+ // (uu, i, lo, hi): the path numbers assigned to the i-th outbound edge of uu are in the
359
+ // interval [lo, hi]
360
+ def nsp_interval(uu, i, lo, hi) : i = 1 and lo = 1 and acc_nsp(uu, i, hi)
361
+ def nsp_interval(uu, i, lo, hi) : lo = acc_nsp[uu, i - 1] + 1 and hi = acc_nsp[uu, i]
362
+
363
+ // (uu, lo, hi): uu is a source, the path numbers assigned to the shortest paths from uu
364
+ // to target nodes are in the interval [lo, hi]
365
+ def source_interval(uu, lo, hi) :
366
+ uu = numbered_source[1] and lo = 1 and hi = running_sum[1]
367
+ def source_interval(uu, lo, hi) :
368
+ exists( (i) |
369
+ uu = numbered_source[i] and
370
+ lo = running_sum[i - 1] + 1 and
371
+ hi = running_sum[i] and
372
+ lo <= hi
373
+ )
374
+
375
+ def paths_listing(vv, n in Int64, k in Int64, path_num):
376
+ exists((lo, hi) |
377
+ k = 0 and
378
+ source_interval(vv, lo, hi) and
379
+ range(lo, hi, 1, path_num) and
380
+ n = path_num - lo + 1
381
+ )
382
+ def paths_listing(vv, n in Int64, k in Int64, path_num):
383
+ exists((m, uu) |
384
+ paths_listing(uu, m, k - 1, path_num) and
385
+ path_routing(uu, m, vv, n)
386
+ )
387
+
388
+ def path_routing_with_hashed_label(uu, m, vv, n in Int64, h):
389
+ exists( (i, lo, hi) |
390
+ nsp_interval(uu, i, lo, hi) and
391
+ range(lo, hi, 1, m) and
392
+ neighbor(uu, i, vv, h) and
393
+ n = m - lo + 1 // this is the `n`-th path from vv to the targets
394
+ )
395
+
396
+ // We do not need the label to compute `paths_listing`; hence we project it out
397
+ def path_routing(uu, m, vv, n): path_routing_with_hashed_label(uu, m, vv, n, _)
398
+
399
+
400
+ // -----------------------------------------------------------------------------
401
+ // Extract the final answer
402
+ // `paths` contain all shortest paths from S to T that conform to Conn
403
+ def paths(path_num, :node, k, v):
404
+ exists((vv) | paths_listing(vv, _, k, path_num) and USP_Node_To_Node[vv] = v)
405
+ def paths(path_num, :edge_label, k in Int64, label):
406
+ exists((uu, m) |
407
+ paths_listing(uu, m, k - 1, path_num) and
408
+ path_routing_with_hashed_label(uu, m, _, _, label)
409
+ )
410
+
411
+ // Total number of shortest walks for the shortest_paths semantics,
412
+ // or of walks for all the other semantics, from Source to Target
413
+ def total_walks { sum[[uu] : (NSP[uu], relevant_source(uu))] }
414
+
415
+ end // _enumeration_helpers[:shortest_paths]
416
+
417
+
418
+ doc"""
419
+ post_process_paths[{Config}, {Paths}]
420
+
421
+ Applies all post-processing configuration options to the given set of paths.
422
+ """
423
+ @inline
424
+ def post_process_paths[{Config}, {Paths}] : _post_process_paths[{Config}, {Paths}, :paths]
425
+
426
+ @outline
427
+ module _post_process_paths[{Config}, {Paths}]
428
+ @inline
429
+ def input_paths { Paths }
430
+
431
+ @inline
432
+ def maybe_reindex_nodes { _reindex_nodes[Config[:node_indexing], input_paths] }
433
+ @inline
434
+ def _reindex_nodes[:zero_based, {_Paths}]: { _Paths }
435
+ @inline
436
+ def _reindex_nodes[:one_based, {_Paths}]: { reindex_nodes[_Paths] }
437
+
438
+ @inline
439
+ def maybe_reindex_edges { _reindex_edges[Config[:edge_indexing], maybe_reindex_nodes] }
440
+ @inline
441
+ def _reindex_edges[:zero_based, {_Paths}]: { reindex_edges[_Paths] }
442
+ @inline
443
+ def _reindex_edges[:one_based, {_Paths}]: { _Paths }
444
+
445
+ @inline
446
+ def maybe_hash_paths { _hash_paths[Config[:path_ids], maybe_reindex_edges] }
447
+ @inline
448
+ def _hash_paths[:canonical, {_Paths}]: { hash_paths[_Paths] }
449
+ @inline
450
+ def _hash_paths[:non_canonical, {_Paths}]: { _Paths }
451
+
452
+ @inline
453
+ def paths { maybe_hash_paths }
454
+
455
+ end // module _post_process_paths[{Config}, {Paths}]
456
+
457
+
458
+ doc"""
459
+ canonical_path_count[{Paths}]
460
+
461
+ Count the number of different paths in the set of paths Paths.
462
+ """
463
+ @inline
464
+ def canonical_path_count[{Paths}]: { _canonical_path_count[{Paths}, :total] }
465
+
466
+ @outline
467
+ module _canonical_path_count[{Paths}]
468
+
469
+ def _hash_paths { hash_paths[Paths] }
470
+
471
+ def total { count[(h): _hash_paths(h, _, _, _)] }
472
+
473
+ end // module _canonical_path_count[{Paths}]
474
+
475
+
476
+ //
477
+ // Auxiliary hash value used in recursive path construction to indicate a dummy automaton
478
+ // state. The value is chosen to be different from the hash values of the actual states.
479
+ //
480
+ @function
481
+ def aux_hash { uint128_hash_value_convert[murmurhash3f[murmurhash3f["__auxiliary__"]]] }
482
+
483
+ end // namespace pathfinder::_internal::utilities
484
+
485
+ // _internal/any_pair/one-sided (from model/_internal/any_pair/one-sided.rel)
486
+ namespace pathfinder::_internal
487
+
488
+ doc"""
489
+ ball[PG_Edge, PG_Source, PG_Target, Config]
490
+
491
+ ## Inputs
492
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
493
+ PG_Source : (u, p) denoting that u is a source node with a initial state p
494
+ PG_Target : (u, p) denoting that u is a target node with a final state p
495
+ Config : configuration parameters
496
+ Config[:search_radius] ∈ { :bounded, :unbounded }
497
+ :bounded means the ball is constructed from the source nodes until a target node
498
+ in the product graph is reached
499
+ :unbounded means the ball is constructed from the source nodes until no further
500
+ nodes in the product graph can be reached
501
+ Config[:max_path_length] specifies the maximum length of the paths to be
502
+ constructed under walks, simple paths or trails semantics (not to be
503
+ used under shortest_paths semantics)
504
+
505
+
506
+ ## Outputs
507
+ radius : the radius of the ball
508
+ usp_node_to_node : mapping that convert uu to u
509
+ pg_to_usp_node : mapping that convert (u, p, l) to uu
510
+ usp_to_pg_node : mapping that convert uu to (u, p, l)
511
+ usp_source : set uu of source nodes
512
+ usp_target : set vv of target nodes
513
+ """
514
+ @outline
515
+ module ball[:any_pair, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
516
+
517
+ def construction {
518
+ if_then_else[
519
+ Config(:semantics, :shortest_paths),
520
+ ball_undef_radius[Config[:search_radius], PG_Edge, PG_Source, PG_Target],
521
+ ball_def_radius[PG_Edge, PG_Source, PG_Target, Config[:max_path_length]]
522
+ ]
523
+ }
524
+
525
+ def pg_dist_from_S { construction[:pg_dist_from_S] }
526
+
527
+ def radius { construction[:radius] }
528
+
529
+ // Give each pg node a number, the corresponding usp node number
530
+ // usp_to_layered_pg_node[uu] = (u, p, l) if the uu-th node in the ball is (u, p, l)
531
+ def usp_to_pg_node { enumerate[pg_dist_from_S] }
532
+
533
+ @function
534
+ def usp_node_to_node(vv, v): { usp_to_pg_node(vv, v, _, _) }
535
+
536
+ // pg_to_usp_node[u, p, l] = uu if the uu-th node in the ball is (u, p, l)
537
+ @function @no_inline
538
+ def pg_to_usp_node(u, p, l, uu): usp_to_pg_node(uu, u, p, l)
539
+
540
+ // uu is a source node
541
+ def usp_source(uu):
542
+ exists((u, p) | PG_Source(u, p) and pg_to_usp_node(u, p, 0, uu))
543
+
544
+ // uu is a target node
545
+ def usp_target(uu):
546
+ exists((u, p) | PG_Target(u, p) and pg_to_usp_node(u, p, _, uu))
547
+
548
+ end // module ball
549
+
550
+
551
+ // =========================================================================================
552
+ // The ball is constructed from the source nodes until a target node in the product graph is
553
+ // reached. Returning:
554
+ // - `pg_dist_from_S[v, q]` is the distance from the closest source node to `(v, q)`.
555
+ // - `radius` is the distance from the closest target to the source nodes.
556
+ @outline
557
+ module ball_undef_radius[:bounded, {PG_Edge}, {PG_Source}, {PG_Target}]
558
+
559
+ // swap the order of the variables to match the expected order of variables
560
+ @function
561
+ def pg_dist_from_S(u, p, d): { _dist_from_S(d, u, p) }
562
+
563
+ // Iterative process of constructing the S-ball. Uses recursion with non-stratified
564
+ // negation to simulate while loop iteration: finish_iteration() and finish_loop()
565
+ // control the execution of the loop.
566
+ def radius {
567
+ max[{0;
568
+ radius;
569
+ {(radius + 1,
570
+ finish_iteration() and
571
+ not finish_loop())
572
+ }}
573
+ ]
574
+ }
575
+
576
+ // Predecessor nodes of the source ball
577
+ @force_dnf
578
+ def prev_dist_from_S(v, q):
579
+ exists((u, p) | _dist_from_S(radius - 1, u, p) and PG_Edge(p, q, _, u, v))
580
+
581
+ // Extending the source ball (if radius hash been incremented)
582
+ @force_dnf
583
+ def _dist_from_S(d, v, q): PG_Source(v, q) and d = 0
584
+ def _dist_from_S(d, v, q):
585
+ d = radius and
586
+ prev_dist_from_S(v, q) and
587
+ not exists((j) | range(0, radius-1, 1, j) and _dist_from_S(j, v, q))
588
+ def _dist_from_S(d, v, q): _dist_from_S(d, v, q)
589
+
590
+
591
+ // Iteration is complete if extending the S-ball has been considered
592
+ @inline
593
+ def finish_iteration(): _dist_from_S(radius, _, _)
594
+
595
+ // Loop is terminated when the S-ball reaches a target node
596
+ @inline
597
+ def finish_loop(): exists((u, p) | _dist_from_S(radius, u, p) and PG_Target(u, p))
598
+
599
+ end // module ball_undef_radius[:bounded]
600
+
601
+
602
+ // =========================================================================================
603
+ // The ball is constructed from the source nodes until no further nodes in the product graph
604
+ // can be reached. Returning:
605
+ // - `pg_dist_from_S[v, q]` is the distance from the closest source node to `(v, q)`.
606
+ // - `radius` is the distance from the closest target to the source nodes.
607
+ @outline
608
+ module ball_undef_radius[:unbounded, {PG_Edge}, {PG_Source}, {PG_Target}]
609
+
610
+ @force_dnf @function
611
+ def pg_dist_from_S[v, q]: {
612
+ min[ (len) :
613
+ PG_Source(v, q) and len = 0
614
+ or
615
+ min({[u, p] : pg_dist_from_S[u, p] + 1 where PG_Edge(p, q, _, u, v)}, len)
616
+ ]
617
+ }
618
+
619
+ def radius { min[[u, p] : pg_dist_from_S[u, p] where PG_Target(u, p)] }
620
+
621
+ end // module ball_undef_radius[:unbounded]
622
+
623
+
624
+ // =========================================================================================
625
+ // The ball of radius Max_Length centered at the source nodes is constructed.
626
+ // In this case, pg_dist_from_S(u, p, d) holds if d <= Max_Length and there exists a walk of
627
+ // length d from the source nodes to (u, p). Hence, pg_dist_from_S is not a function in
628
+ // this case, and the constructed ball is a multiset in the sense that the same node (u, p)
629
+ // can appear multiple times in the ball, each corresponding to a d such that
630
+ // pg_dist_from_S(u, p, d) holds.
631
+ @outline
632
+ module ball_def_radius[{PG_Edge}, {PG_Source}, {PG_Target}, {Max_Length}]
633
+
634
+ // swap the order of the variables to match the expected order of variables
635
+ def pg_dist_from_S(u, p, d): { _dist_from_S(d, u, p) }
636
+
637
+ @force_dnf
638
+ def _dist_from_S(d, v, q): PG_Source(v, q) and d = 0
639
+ def _dist_from_S(d, v, q):
640
+ d <= Max_Length and
641
+ exists((u, p) | _dist_from_S(d - 1, u, p) and PG_Edge(p, q, _, u, v))
642
+
643
+ def radius { max[[u, p] : pg_dist_from_S[u, p] where PG_Target(u, p)] }
644
+
645
+ end // module ball_def_radius
646
+
647
+ end // namespace pathfinder::_internal
648
+
649
+ // _internal/any_pair/two-sided (from model/_internal/any_pair/two-sided.rel)
650
+ namespace pathfinder::_internal
651
+
652
+ doc"""
653
+ two_sided_balls[PG_Edge, PG_Source, PG_Target]
654
+
655
+ ## Inputs
656
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
657
+ PG_Source : (u, p) denoting that u is a source node with initial state p
658
+ PG_Target : (u, p) denoting that u is a target node with final state p
659
+
660
+ ## Outputs
661
+ radius_S : the radius of the source ball
662
+ radius_T : the radius of the target ball
663
+ usp_to_pg_node : mapping that convert uu to (u, p, l)
664
+ pg_to_usp_node : mapping that convert (u, p, l) to uu
665
+ usp_source : set uu of source nodes
666
+ usp_target : set vv of target nodes
667
+ usp_middle : set uu of nodes where the two balls meet
668
+ """
669
+ @inline
670
+ def two_sided_balls[:any_pair, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
671
+ if_then_else[
672
+ Config(:semantics, :shortest_paths),
673
+ _two_sided_balls[:any_pair, :shortest_paths, PG_Edge, PG_Source, PG_Target, Config],
674
+ _two_sided_balls[:any_pair, :walks, PG_Edge, PG_Source, PG_Target, Config]
675
+ ]
676
+ }
677
+
678
+
679
+ @outline
680
+ module _two_sided_balls[
681
+ :any_pair, :shortest_paths, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}
682
+ ]
683
+
684
+ @function
685
+ def delta { radius_S - radius_T }
686
+
687
+ def shifted_pg_dist_to_T(d, v, q):
688
+ exists((l) | pg_dist_to_T(l, v, q) and d = l + delta)
689
+
690
+ @inline
691
+ def all_pg_nodes(v, q, d):
692
+ pg_dist_from_S(d, v, q) or shifted_pg_dist_to_T(d, v, q)
693
+
694
+ def usp_to_pg_node { enumerate[all_pg_nodes] }
695
+
696
+ @function
697
+ def source_pg_to_usp_node(u, p, d, uu) :
698
+ usp_to_pg_node(uu, u, p, d) and pg_dist_from_S(d, u, p)
699
+
700
+ @function
701
+ def target_pg_to_usp_node(u, p, d, uu) :
702
+ usp_to_pg_node(uu, u, p, d) and shifted_pg_dist_to_T(d, u, p)
703
+
704
+ @function
705
+ def usp_node_to_node(vv, v): { usp_to_pg_node(vv, v, _, _) }
706
+
707
+ def usp_source(uu) :
708
+ exists((u, p) | PG_Source(u, p) and uu = source_pg_to_usp_node[u, p, 0])
709
+
710
+ def usp_target(uu) :
711
+ exists((u, p) | PG_Target(u, p) and uu = target_pg_to_usp_node[u, p, delta])
712
+
713
+ def usp_middle(uu):
714
+ exists((u, p) | pg_middle(u, p) and uu = source_pg_to_usp_node[u, p, radius_S])
715
+
716
+ // Iterative process of constructing the balls. Uses recursion with non-stratified
717
+ // negation to simulate while loop iteration: finish_iteration() and finish_loop()
718
+ // control the execution of the loop.
719
+ @function
720
+ def radius_S {
721
+ max[{0;
722
+ radius_S;
723
+ {(radius_S + 1, // increment the diameter only if
724
+ delta_S_size < delta_T_size and // the ball size increase is smaller
725
+ finish_iteration() and
726
+ not finish_loop())
727
+ }
728
+ }]
729
+ }
730
+
731
+ @function
732
+ def radius_T {
733
+ max[{0;
734
+ radius_T;
735
+ {(radius_T + 1, // increment the diameter only if
736
+ delta_S_size >= delta_T_size and // the ball size increase is smaller
737
+ finish_iteration() and
738
+ not finish_loop())}
739
+ }]
740
+ }
741
+
742
+ // The sizes of the outermost layers of the respective balls at the current iteration
743
+ @inline
744
+ def delta_S_size { count[pg_dist_from_S[radius_S]] }
745
+ @inline
746
+ def delta_T_size { count[pg_dist_to_T[radius_T]] }
747
+
748
+ // Predecessor nodes of the source ball
749
+ @force_dnf
750
+ def prev_dist_from_S(v, q):
751
+ exists((u, p) |
752
+ pg_dist_from_S(radius_S - 1, u, p) and PG_Edge(p, q, _, u, v)
753
+ )
754
+
755
+ // Extending the source ball (if radius_S hash been incremented)
756
+ @force_dnf
757
+ def pg_dist_from_S(d, v, q): PG_Source(v, q) and d = 0
758
+ def pg_dist_from_S(d, v, q):
759
+ d = radius_S and
760
+ prev_dist_from_S(v, q) and
761
+ not exists((j) | range(0, radius_S - 1, 1, j) and pg_dist_from_S(j, v, q))
762
+ def pg_dist_from_S(d, v, q): pg_dist_from_S(d, v, q)
763
+
764
+ // Predecessor nodes of the target ball
765
+ @force_dnf
766
+ def prev_dist_to_T(u, p):
767
+ exists((v, q) |
768
+ pg_dist_to_T(radius_T - 1, v, q) and PG_Edge(p, q, _, u, v)
769
+ )
770
+
771
+ // Extending the target ball (if radius_T hash been incremented)
772
+ @force_dnf
773
+ def pg_dist_to_T(d, u, p): PG_Target(u, p) and d = 0
774
+ def pg_dist_to_T(d, u, p):
775
+ d = radius_T and
776
+ prev_dist_to_T(u, p) and
777
+ not exists((j) | range(0, radius_T - 1, 1, j) and pg_dist_to_T(j, u, p))
778
+ def pg_dist_to_T(d, u, p): pg_dist_to_T(d, u, p)
779
+
780
+ // Iteration is complete if extending both balls has been considered
781
+ def finish_iteration(): delta_S_size > 0 and delta_T_size > 0
782
+
783
+ // Loop is terminated when the two balls meet
784
+ def finish_loop():
785
+ exists((u, p) |
786
+ pg_dist_from_S(radius_S, u, p) and pg_dist_to_T(radius_T, u, p)
787
+ )
788
+
789
+ // Nodes of the product graph where the two balls meet.
790
+ def pg_middle(u, p): pg_dist_from_S(radius_S, u, p) and pg_dist_to_T(radius_T, u, p)
791
+
792
+ end // module _two_sided_balls[:any_pair, :shortest_paths]
793
+
794
+
795
+ @outline
796
+ module _two_sided_balls[
797
+ :any_pair, :walks, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}
798
+ ]
799
+
800
+ @function
801
+ def delta { Config[:max_path_length] + 1 }
802
+
803
+ def shifted_pg_dist_to_T(d, v, q):
804
+ exists((l) | pg_dist_to_T(l, v, q) and d = l - delta)
805
+
806
+ @inline
807
+ def all_pg_nodes(v, q, d):
808
+ pg_dist_from_S(d, v, q) or shifted_pg_dist_to_T(d, v, q)
809
+
810
+ def usp_to_pg_node { enumerate[all_pg_nodes] }
811
+
812
+ @function
813
+ def source_pg_to_usp_node(u, p, d, uu) :
814
+ usp_to_pg_node(uu, u, p, d) and pg_dist_from_S(d, u, p)
815
+
816
+ @function
817
+ def target_pg_to_usp_node(u, p, d, uu) :
818
+ usp_to_pg_node(uu, u, p, d) and shifted_pg_dist_to_T(d, u, p)
819
+
820
+ @function
821
+ def usp_node_to_node(vv, v): { usp_to_pg_node(vv, v, _, _) }
822
+
823
+ def usp_source(uu) :
824
+ exists((u, p) | PG_Source(u, p) and source_pg_to_usp_node(u, p, 0, uu))
825
+
826
+ def usp_target(uu) :
827
+ exists((u, p) |
828
+ PG_Target(u, p) and
829
+ (
830
+ target_pg_to_usp_node(u, p, -1*delta, uu) or
831
+ source_pg_to_usp_node(u, p, _, uu)
832
+ )
833
+ )
834
+
835
+ def source_usp_middle(uu) :
836
+ exists((u, p) | pg_middle(u, p) and source_pg_to_usp_node(u, p, radius_S, uu))
837
+
838
+ def target_usp_middle(uu) :
839
+ exists((u, p) | pg_middle(u, p) and target_pg_to_usp_node(u, p, _, uu))
840
+
841
+
842
+ def radius_S { trunc_divide[Config[:max_path_length] + 1, 2] }
843
+
844
+ def radius_T { Config[:max_path_length] - radius_S }
845
+
846
+ @force_dnf
847
+ def pg_dist_from_S(d, v, q): PG_Source(v, q) and d = 0
848
+ def pg_dist_from_S(d, v, q):
849
+ d <= radius_S and
850
+ exists((u, p) | pg_dist_from_S(d - 1, u, p) and PG_Edge(p, q, _, u, v))
851
+
852
+ @force_dnf
853
+ def pg_dist_to_T(d, v, q): PG_Target(v, q) and d = 0
854
+ def pg_dist_to_T(d, v, q):
855
+ d <= radius_T and
856
+ exists((u, p) | pg_dist_to_T(d - 1, u, p) and PG_Edge(q, p, _, v, u))
857
+
858
+ // Nodes of the product graph where the two balls meet.
859
+ def pg_middle(u, p): pg_dist_from_S(radius_S, u, p) and pg_dist_to_T(_, u, p)
860
+
861
+ end // module _two_sided_balls[:any_pair, :walks]
862
+
863
+ end // namespace pathfinder::_internal::any_pair
864
+
865
+ // _internal/for_each_source/one-sided (from model/_internal/for_each_source/one-sided.rel)
866
+ namespace pathfinder::_internal
867
+
868
+ doc"""
869
+ ball[PG_Edge, PG_Source, PG_Target, Config]
870
+
871
+ ## Inputs
872
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
873
+ PG_Source : (u, p) denoting that u is a source node with a state p
874
+ PG_Target : (u, p) denoting that u is a target node with a state p
875
+ Config : configuration parameters
876
+ Config[:search_radius] ∈ { :bounded, :unbounded }
877
+ :bounded means the ball is constructed from the source nodes until a target node
878
+ in the product graph is reached
879
+ :unbounded means the ball is constructed from the source nodes until no further
880
+ nodes in the product graph can be reached
881
+
882
+ ## Outputs
883
+ radius : the radius of the ball
884
+ usp_to_pg_node : mapping that convert uu to (u, p)
885
+ pg_to_usp_node : mapping that convert (u, p) to uu
886
+ dist_from_S : (d, uu) denotes uu is at distance d from the source nodes
887
+ usp_source : set uu of source nodes
888
+ usp_target : set vv of target nodes
889
+ """
890
+ @outline
891
+ module ball[:for_each_source, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
892
+
893
+ @inline
894
+ def search_radius { Config[:search_radius] } // :bounded or :unbounded
895
+
896
+ def pg_dist_from_S {_ball[search_radius, PG_Edge, PG_Source, PG_Target, :pg_dist_from_S] }
897
+
898
+ // Gather all pg nodes and give each of them a number, the corresponding usp node number
899
+ def all_pg_nodes(v, q) : pg_dist_from_S(_, _, v, q, _)
900
+
901
+ // ------------------------------------------------------------------
902
+ // Computing outputs relations
903
+
904
+ // usp_to_pg_node[uu] = (u, p) if uu-th node in all_pg_nodes is (u, p)
905
+ def usp_to_pg_node { enumerate[all_pg_nodes]}
906
+
907
+ // pg_to_usp_node[u,p] = uu if uu-th node in all_pg_nodes is (u, p)
908
+ @no_inline
909
+ def pg_to_usp_node(u, p, uu) : usp_to_pg_node(uu, u, p)
910
+
911
+ def dist_from_S(ss, d, uu):
912
+ exists ( (s, o, u, p) |
913
+ pg_dist_from_S(s, o, u, p, d) and
914
+ uu = pg_to_usp_node[u, p] and
915
+ ss = pg_to_usp_node[s, o]
916
+ )
917
+
918
+ def usp_source(uu) : exists((u, p) | PG_Source(u, p) and uu = pg_to_usp_node[u, p])
919
+
920
+ def usp_target(uu) :
921
+ exists((ss, u, p) |
922
+ PG_Target(u, p) and
923
+ uu = pg_to_usp_node[u, p] and
924
+ dist_from_S(ss, radius[ss], uu)
925
+ )
926
+
927
+ @function
928
+ def radius(ss, d) :
929
+ exists( (s, o) |
930
+ _ball(search_radius, PG_Edge, PG_Source, PG_Target, :radius, s, o, d) and
931
+ ss = pg_to_usp_node[s, o]
932
+ )
933
+ end // module ball
934
+
935
+ // =========================================================================================
936
+ // == Config[:search_radius] = :bounded ==
937
+ // For every source node `(s, o)`, a ball is expanded from the source node until a target
938
+ // node in the product graph is reached
939
+ @outline
940
+ module _ball[:bounded, {PG_Edge}, {PG_Source}, {PG_Target}]
941
+
942
+ // swap the order of the variables to match the expected order of variables
943
+ @function
944
+ def pg_dist_from_S(s, o, u, p, d): { _dist_from_S(s, o, d, u, p) }
945
+
946
+ // Iterative process of constructing the S-ball. Uses recursion with non-stratified
947
+ // negation to simulate while loop iteration: finish_iteration() and finish_loop()
948
+ // control the execution of the loop.
949
+ def radius[s,o]: {
950
+ max[[len] :
951
+ len = 0 and PG_Source(s, o)
952
+ or
953
+ len = radius[s, o]
954
+ or
955
+ len = radius[s, o] + 1 and finish_iteration(s, o) and not finish_loop(s, o)
956
+ ]
957
+ }
958
+
959
+ // Predecessor nodes of the source ball
960
+ def prev_dist_from_S(s, o, v, q):
961
+ exists((u, p) | _dist_from_S(s, o, radius[s, o] - 1, u, p) and PG_Edge(p, q, _, u, v))
962
+
963
+ // Extending the source ball (if radius hash been incremented)
964
+ @force_dnf
965
+ def _dist_from_S(s, o, len, v, q): PG_Source(s, o) and len = 0 and s = v and o = q
966
+ def _dist_from_S(s, o, len, v, q):
967
+ len = radius[s, o] and
968
+ prev_dist_from_S(s, o, v, q) and
969
+ not exists((j) | range(0, radius[s, o] - 1, 1, j) and _dist_from_S(s, o, j, v, q))
970
+ def _dist_from_S(s, o, len, v, q): _dist_from_S(s, o, len, v, q)
971
+
972
+
973
+ // Iteration is complete if extending the S-ball has been considered
974
+ @inline
975
+ def finish_iteration(s,o): _dist_from_S(s, o, radius[s,o], _, _)
976
+
977
+ // Loop is terminated when the S-ball reaches a target node
978
+ @inline
979
+ def finish_loop(s, o):
980
+ exists((u, p) | _dist_from_S(s, o, radius[s,o], u, p) and PG_Target(u, p))
981
+
982
+ end // module _ball[:bounded, {PG_Edge}, {PG_Source}, {PG_Target}]
983
+
984
+ // =========================================================================================
985
+ // == Config[:search_radius] = :unbounded ==
986
+ // The ball is constructed from the source nodes until no further nodes in the product graph
987
+ // can be reached. Returning:
988
+ // - `pg_dist_from_S[s, o, v, q]` = distance from the source node `(s, o)` to `(v, q)`.
989
+ // - `radius[s,o]` = distance from the closest target to the source `(s, o)`.
990
+ @outline
991
+ module _ball[:unbounded, {PG_Edge}, {PG_Source}, {PG_Target}]
992
+
993
+ @force_dnf @function
994
+ def pg_dist_from_S[s, o, v, q]: {
995
+ min[[len] :
996
+ PG_Source(s, o) and s=v and q=o and len = 0
997
+ or
998
+ min({[u, p] : pg_dist_from_S[s, o, u, p] + 1 where PG_Edge(p, q, _, u, v)}, len)
999
+ ]
1000
+ }
1001
+
1002
+ def radius[s, o]: {
1003
+ min[[u, p] : pg_dist_from_S[s, o, u, p] where PG_Target(u, p)]
1004
+ }
1005
+
1006
+ end // module _ball[:unbounded, {PG_Edge}, {PG_Source}, {PG_Target}, :unbounded]
1007
+
1008
+ end // namespace pathfinder::_internal
1009
+
1010
+ // _internal/any_pair/usp (from model/_internal/any_pair/usp.rel)
1011
+ namespace pathfinder::_internal
1012
+
1013
+ from ::pathfinder::_internal::utilities import invert_pg
1014
+
1015
+ // =========================================================================================
1016
+ // Config[:search_strategy] = :from_source
1017
+ @outline
1018
+ module _usp[:any_pair, :from_source, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1019
+
1020
+ def the_ball { ball[:any_pair, PG_Edge, PG_Source, PG_Target, Config] }
1021
+ def radius { the_ball[:radius] }
1022
+ def usp_source { the_ball[:usp_source] }
1023
+ def pg_to_usp_node { the_ball[:pg_to_usp_node] }
1024
+ def usp_node_to_node { the_ball[:usp_node_to_node] }
1025
+
1026
+ def _usp_target { the_ball[:usp_target] }
1027
+ def usp_target {
1028
+ if_then_else[
1029
+ Config(:selector, :limit),
1030
+ top[Config[:path_count], _usp_target, _],
1031
+ _usp_target
1032
+ ]
1033
+ }
1034
+
1035
+
1036
+ def usp_edge {
1037
+ usp_edge_from_pg[:any_pair, PG_Edge, usp_target, pg_to_usp_node, radius, Config]
1038
+ }
1039
+
1040
+ end // module _usp[:from_source]
1041
+
1042
+
1043
+ // =========================================================================================
1044
+ // Config[:search_strategy] = :from_target
1045
+ @outline
1046
+ module _usp[:any_pair, :from_target, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1047
+
1048
+ def the_ball { ball[:any_pair, invert_pg[PG_Edge], PG_Target, PG_Source, Config] }
1049
+
1050
+ def radius { the_ball[:radius] }
1051
+ // note the inversion of source and target:
1052
+ def usp_target { the_ball[:usp_source] }
1053
+ def pg_to_usp_node { the_ball[:pg_to_usp_node] }
1054
+ def usp_node_to_node { the_ball[:usp_node_to_node] }
1055
+
1056
+ def _usp_source { the_ball[:usp_target] }
1057
+ def usp_source {
1058
+ if_then_else[
1059
+ Config(:selector, :limit),
1060
+ top[Config[:path_count], _usp_source, _],
1061
+ _usp_source
1062
+ ]
1063
+ }
1064
+
1065
+
1066
+ def inverted_usp_edge {
1067
+ usp_edge_from_pg[
1068
+ :any_pair, invert_pg[PG_Edge], usp_source, pg_to_usp_node, radius, Config
1069
+ ]
1070
+ }
1071
+
1072
+ def usp_edge(uu, vv, a): inverted_usp_edge(vv, uu, a)
1073
+
1074
+ end // module _usp[:from_target]
1075
+
1076
+
1077
+ // =========================================================================================
1078
+ // Config[:search_strategy] = :from_both_sides
1079
+ @inline
1080
+ def _usp[:any_pair, :from_both_sides, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1081
+ if_then_else[
1082
+ Config(:semantics, :shortest_paths),
1083
+ _usp_both_sides_sp[:any_pair, PG_Edge, PG_Source, PG_Target, Config],
1084
+ _usp_both_sides_walks[:any_pair, PG_Edge, PG_Source, PG_Target, Config]
1085
+ ]
1086
+ }
1087
+
1088
+
1089
+ @outline
1090
+ module _usp_both_sides_sp[:any_pair, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1091
+
1092
+ def the_balls { two_sided_balls[:any_pair, PG_Edge, PG_Source, PG_Target, Config] }
1093
+ def radius_S { the_balls[:radius_S] }
1094
+ def usp_source { the_balls[:usp_source] }
1095
+ def usp_target { the_balls[:usp_target] }
1096
+ def source_pg_to_usp_node { the_balls[:source_pg_to_usp_node] }
1097
+ def target_pg_to_usp_node { the_balls[:target_pg_to_usp_node] }
1098
+ def usp_node_to_node { the_balls[:usp_node_to_node] }
1099
+
1100
+ def _usp_middle { the_balls[:usp_middle] }
1101
+ def usp_middle {
1102
+ if_then_else[
1103
+ Config(:selector, :limit),
1104
+ top[Config[:path_count], _usp_middle, _],
1105
+ _usp_middle
1106
+ ]
1107
+ }
1108
+
1109
+
1110
+ // ------------------------------------------------------------------
1111
+ // compute the [source to middle] and [target to middle] USP edges
1112
+ def usp_edge_S {
1113
+ usp_edge_from_pg[
1114
+ :any_pair, PG_Edge, usp_middle, source_pg_to_usp_node, radius_S, Config
1115
+ ]
1116
+ }
1117
+
1118
+ def inverted_usp_edge_T {
1119
+ usp_edge_from_pg[
1120
+ :any_pair, invert_pg[PG_Edge], usp_middle, target_pg_to_usp_node, radius_S, Config
1121
+ ]
1122
+ }
1123
+
1124
+ // ------------------------------------------------------------------
1125
+ // Their union is the USP edge set
1126
+ def usp_edge(uu, vv, a): usp_edge_S(uu, vv, a) or inverted_usp_edge_T(vv, uu, a)
1127
+
1128
+ end // module _usp_both_sides_sp[:any_pair]
1129
+
1130
+
1131
+ @outline
1132
+ module _usp_both_sides_walks[:any_pair, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1133
+
1134
+ def the_balls { two_sided_balls[:any_pair, PG_Edge, PG_Source, PG_Target, Config] }
1135
+ def radius_S { the_balls[:radius_S] }
1136
+ def radius_T { the_balls[:radius_T] }
1137
+ def source_pg_to_usp_node { the_balls[:source_pg_to_usp_node] }
1138
+ def target_pg_to_usp_node { the_balls[:target_pg_to_usp_node] }
1139
+ def usp_node_to_node { the_balls[:usp_node_to_node] }
1140
+
1141
+ def usp_source { the_balls[:usp_source] }
1142
+ def usp_target { the_balls[:usp_target] }
1143
+
1144
+ def _source_usp_middle { the_balls[:source_usp_middle] }
1145
+ def _source_usp_middle { usp_target }
1146
+
1147
+ def source_usp_middle {
1148
+ if_then_else[
1149
+ Config(:selector, :limit),
1150
+ top[
1151
+ Config[:path_count],
1152
+ {(uu) : _source_usp_middle(uu) and source_pg_to_usp_node(_, _, _, uu)},
1153
+ _
1154
+ ],
1155
+ _source_usp_middle
1156
+ ]
1157
+ }
1158
+
1159
+ def target_usp_middle { the_balls[:target_usp_middle] }
1160
+
1161
+
1162
+ // ------------------------------------------------------------------
1163
+ // compute the [source to middle] and [target to middle] USP edges
1164
+ def usp_edge_S {
1165
+ usp_edge_from_pg[
1166
+ :any_pair, PG_Edge, source_usp_middle, source_pg_to_usp_node, radius_S, Config
1167
+ ]
1168
+ }
1169
+
1170
+ def inverted_usp_edge_T {
1171
+ usp_edge_from_pg[
1172
+ :any_pair, invert_pg[PG_Edge], target_usp_middle, target_pg_to_usp_node, radius_S, Config
1173
+ ]
1174
+ }
1175
+
1176
+
1177
+ // ------------------------------------------------------------------
1178
+ // Their union is the USP edge set
1179
+ def usp_edge(uu, vv, a): usp_edge_S(uu, vv, a) or inverted_usp_edge_T(vv, uu, a)
1180
+ def usp_edge(uu, vv, a):
1181
+ Config(:semantics, :walks) and
1182
+ exists((ww, u, p) |
1183
+ inverted_usp_edge_T(vv, ww, a) and
1184
+ target_pg_to_usp_node(u, p, _, ww) and
1185
+ source_pg_to_usp_node(u, p, radius_S, uu)
1186
+ )
1187
+
1188
+ end // module _usp_both_sides_walks[:any_pair]
1189
+
1190
+
1191
+ // =========================================================================================
1192
+ // Helper functions for constructing the USP edges form the product graph. There are two
1193
+ // configuration parameters to consider:
1194
+ // - :on_the_fly means the USP edges are constructed on the fly
1195
+ // - :materialized means the product graph is pre-materialized before the USP edges are
1196
+ // constructed
1197
+ @inline
1198
+ def usp_edge_from_pg[
1199
+ :any_pair, {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Radius}, {Config}
1200
+ ]: {
1201
+ _usp_edge_from_pg[
1202
+ :any_pair,
1203
+ Config[:product_graph],
1204
+ PG_Edge,
1205
+ USP_Target,
1206
+ PG_To_USP_Node,
1207
+ Radius,
1208
+ :usp_edge
1209
+ ]
1210
+ }
1211
+
1212
+
1213
+ // -----------------------------------------------------------------------------------------
1214
+ // Config[:product_graph] = :on_the_fly
1215
+ @outline
1216
+ module _usp_edge_from_pg[
1217
+ :any_pair, :on_the_fly, {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Radius}
1218
+ ]
1219
+
1220
+ // The USP fragment is obtained by constructing the paths from the source nodes
1221
+ // to the target nodes.
1222
+ def usp_edge { usp_edge_back[_] }
1223
+
1224
+ @force_dnf
1225
+ def start_usp_edge_back(i, uu, vv, a):
1226
+ USP_Target(vv) and
1227
+ exists ( (u, p, v, q) |
1228
+ PG_Edge(p, q, a, u, v) and
1229
+ PG_To_USP_Node[v, q, i + 1] = vv and
1230
+ PG_To_USP_Node[u, p, i] = uu
1231
+ )
1232
+
1233
+ @force_dnf
1234
+ def step_usp_edge_back(i, uu, vv, a):
1235
+ usp_edge_back(i + 1, vv, _, _) and
1236
+ exists ( (u, p, v, q) |
1237
+ PG_Edge(p, q, a, u, v) and
1238
+ PG_To_USP_Node[v, q, i + 1] = vv and
1239
+ PG_To_USP_Node[u, p, i] = uu
1240
+ )
1241
+
1242
+ def usp_edge_back(i, uu, vv, a):
1243
+ start_usp_edge_back(i, uu, vv, a) and i < Radius
1244
+ or
1245
+ step_usp_edge_back(i, uu, vv, a)
1246
+
1247
+ end // module _usp_edge_from_pg[:on_the_fly]
1248
+
1249
+
1250
+ // -----------------------------------------------------------------------------------------
1251
+ // Config[:product_graph] = :materialized
1252
+ // The construction is almost identical to that in the path_usp_edge[:materialized] module,
1253
+ // thus we do not repeat the comments here.
1254
+ @outline
1255
+ module _usp_edge_from_pg[
1256
+ :any_pair, :materialized, {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Radius}
1257
+ ]
1258
+
1259
+ @function
1260
+ def _distance(uu, len): PG_To_USP_Node(_, _, len, uu)
1261
+
1262
+ // Pre-construct some relevant automaton edges on the index space
1263
+ @force_dnf
1264
+ def _relevant_automaton_edge(uu, vv, a):
1265
+ exists ( (u, p, v, q, len) |
1266
+ PG_Edge(p, q, a, u, v) and
1267
+ PG_To_USP_Node[u, p, len - 1] = uu and
1268
+ PG_To_USP_Node[v, q, len] = vv
1269
+ )
1270
+
1271
+ // (uu, len) -a-> (vv, len + 1) is an edge, and uu is reachable from S in len steps
1272
+ def _reachable_from_S(vv, uu, len, a):
1273
+ _relevant_automaton_edge(uu, vv, a) and _distance[uu] = len
1274
+
1275
+ // then, keep the edges uu --> vv where vv is reachable from S in len + 1 steps
1276
+ def _candidate_edge(vv, uu, a): exists ( (len) |
1277
+ _reachable_from_S(vv, uu, len, a) and _distance[vv] = len + 1 and len < Radius
1278
+ )
1279
+
1280
+ // The USP fragment is obtained by constructing the paths from the source nodes
1281
+ // to the target nodes. This recursion is now very simple
1282
+ def usp_edge(uu, vv, a):
1283
+ _candidate_edge(vv, uu, a) and USP_Target(vv)
1284
+ or
1285
+ _candidate_edge(vv, uu, a) and usp_edge(vv, _, _)
1286
+
1287
+ end // module _usp_edge_from_pg[:materialized]
1288
+
1289
+ end // namespace pathfinder::_internal
1290
+
1291
+ // _internal/for_each_source/usp (from model/_internal/for_each_source/usp.rel)
1292
+
1293
+ namespace pathfinder::_internal
1294
+
1295
+ from ::pathfinder::_internal::utilities import invert_pg
1296
+
1297
+
1298
+
1299
+ // =========================================================================================
1300
+ // Config[:search_strategy] = :from_source
1301
+ @outline
1302
+ module _usp[:for_each_source, :from_source, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1303
+
1304
+ def the_ball { ball[:for_each_source, PG_Edge, PG_Source, PG_Target, Config] }
1305
+ def dist_from_S { the_ball[:dist_from_S] }
1306
+ def radius { the_ball[:radius] }
1307
+ def usp_source { the_ball[:usp_source] }
1308
+ def usp_target { the_ball[:usp_target] }
1309
+ def pg_to_usp_node { the_ball[:pg_to_usp_node] }
1310
+ def usp_to_pg_node { the_ball[:usp_to_pg_node] }
1311
+
1312
+ def usp_edge {
1313
+ usp_edge_from_pg[:for_each_source, PG_Edge, usp_target, pg_to_usp_node, dist_from_S, radius, Config]
1314
+ }
1315
+
1316
+ def usp_node_to_node(vv, v): { usp_to_pg_node(vv, v, _) }
1317
+
1318
+ end // module _usp[:from_source]
1319
+
1320
+ // =========================================================================================
1321
+ // Helper functions for constructing the USP edges form the product graph
1322
+ @inline
1323
+ def usp_edge_from_pg[
1324
+ :for_each_source, {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Dist_From_S}, {Radius}, {Config}
1325
+ ]: {
1326
+ path_usp_edge[:for_each_source, Config[:product_graph],
1327
+ PG_Edge, USP_Target, PG_To_USP_Node, Dist_From_S, Radius, :usp_edge
1328
+ ]
1329
+ }
1330
+
1331
+ // -----------------------------------------------------------------------------------------
1332
+ // == Config[:product_graph] = :on_the_fly ==
1333
+ // Build the paths backward from the target to the source nodes. We compute the USP edge
1334
+ // relation "on the fly", starting from the targets and working our way back to the sources
1335
+ // using the `Dist_From_S` relation to guide where to go next.
1336
+ @outline
1337
+ module path_usp_edge[:for_each_source, :on_the_fly,
1338
+ {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Dist_From_S}, {Radius}
1339
+ ]
1340
+
1341
+ // The USP fragment is obtained by constructing the paths from the source nodes
1342
+ // to the target nodes.
1343
+ def usp_edge { usp_edge_back[_, _] }
1344
+
1345
+ @force_dnf
1346
+ def start_usp_edge_back(uu, vv, a):
1347
+ exists ( (u, p, v, q) |
1348
+ PG_Edge(p, q, a, u, v) and
1349
+ USP_Target(vv) and
1350
+ PG_To_USP_Node[v, q] = vv and
1351
+ PG_To_USP_Node[u, p] = uu
1352
+ )
1353
+
1354
+ @force_dnf
1355
+ def step_usp_edge_back(ss, len, uu, vv, a):
1356
+ exists ( (u, p, v, q) |
1357
+ usp_edge_back(ss, len + 1, vv, _, _) and
1358
+ PG_Edge(p, q, a, u, v) and
1359
+ PG_To_USP_Node[v, q] = vv and
1360
+ PG_To_USP_Node[u, p] = uu
1361
+ )
1362
+
1363
+ def usp_edge_back(ss, len, uu, vv, a):
1364
+ Dist_From_S(ss, len, uu) and start_usp_edge_back(uu, vv, a) and len = Radius[ss] - 1
1365
+ or
1366
+ Dist_From_S(ss, len, uu) and step_usp_edge_back(ss, len, uu, vv, a)
1367
+
1368
+ end // module usp_edge[:on_the_fly]
1369
+
1370
+ // -----------------------------------------------------------------------------------------
1371
+ // == Config[:product_graph] = :materialized ==
1372
+ @outline
1373
+ module path_usp_edge[:for_each_source, :materialized,
1374
+ {PG_Edge}, {USP_Target}, {PG_To_USP_Node}, {Dist_From_S}, {Radius}
1375
+ ]
1376
+
1377
+ @function
1378
+ def _distance(ss, uu, len): Dist_From_S(ss, len, uu)
1379
+
1380
+ // Pre-construct some relevant automaton edges on the index space
1381
+ @force_dnf
1382
+ def _relevant_automaton_edge(uu, vv, a):
1383
+ exists ( (u, p, v, q) |
1384
+ PG_Edge(p, q, a, u, v) and
1385
+ PG_To_USP_Node[u, p] = uu and
1386
+ PG_To_USP_Node[v, q] = vv
1387
+ )
1388
+
1389
+ // uu -a-> vv is an edge, and uu is reachable from ss in len steps
1390
+ def _reachable_from_S(ss, vv, uu, len, a):
1391
+ _relevant_automaton_edge(uu, vv, a) and _distance[ss, uu] = len
1392
+
1393
+ // then, keep the edges uu --> vv where vv is reachable from S in len + 1 steps
1394
+ def _candidate_edge(vv, uu, a):
1395
+ exists ( (ss, len) |
1396
+ _reachable_from_S(ss, vv, uu, len, a) and
1397
+ _distance[ss, vv] = len + 1 and
1398
+ len < Radius[ss]
1399
+ )
1400
+
1401
+ // The USP fragment is obtained by constructing the paths from the source nodes
1402
+ // to the target nodes. This recursion is now very simple
1403
+ def usp_edge(uu, vv, a):
1404
+ _candidate_edge(vv, uu, a) and USP_Target(vv)
1405
+ or
1406
+ _candidate_edge(vv, uu, a) and usp_edge(vv, _, _)
1407
+
1408
+ end // module usp_edge[:materialized]
1409
+
1410
+ end // namespace pathfinder::_internal
1411
+
1412
+ // _internal/all (from model/_internal/all.rel)
1413
+ namespace pathfinder::_internal
1414
+
1415
+ from ::pathfinder::_internal::utilities import
1416
+ walks_count, enumeration_helpers
1417
+
1418
+ doc"""
1419
+ all[Group, PG_Edge, PG_Source, PG_Target, Config]
1420
+
1421
+ ## Inputs
1422
+ Group : all or for_each_source
1423
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
1424
+ PG_Source: (u, p) denoting that u is a source node with state p
1425
+ PG_Target: (u, p) denoting that u is a target node with state p
1426
+ Config : configuration parameters
1427
+
1428
+ ## Outputs
1429
+ paths : all shortest paths from the source nodes to the target nodes in
1430
+ the graph defined by PG_Edge
1431
+ """
1432
+ @inline
1433
+ def all[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1434
+ _all[Group, PG_Edge, PG_Source, PG_Target, Config, :paths]
1435
+ }
1436
+
1437
+ @outline
1438
+ module _all[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1439
+
1440
+ def the_usp { usp[Group, PG_Edge, PG_Source, PG_Target, Config] }
1441
+
1442
+ def relevant_usp_source { the_usp[:relevant_usp_source] }
1443
+ def relevant_usp_target { the_usp[:relevant_usp_target] }
1444
+ def usp_edge { the_usp[:usp_edge]}
1445
+ def usp_node_to_node { the_usp[:usp_node_to_node] }
1446
+
1447
+
1448
+ // For a node u, num_walks[u] is the number of shortest walks for the shortest_paths
1449
+ // semantics, or the number of walks for all the other semantics, from the node u
1450
+ // to a target node
1451
+ def num_walks { walks_count[usp_edge, relevant_usp_target, Config, :num_walks] }
1452
+
1453
+
1454
+ // paths is the set of all shortest paths from PG_Source PG_Target for the
1455
+ // shortest_paths semantics, and the set of all walks from PG_Source to PG_Target
1456
+ // of length at most Config[:max_path_length] for the walks semantics.
1457
+ def paths { enumeration_helpers[
1458
+ usp_edge,
1459
+ relevant_usp_source,
1460
+ relevant_usp_target,
1461
+ usp_node_to_node,
1462
+ num_walks,
1463
+ Config,
1464
+ :paths
1465
+ ]
1466
+ }
1467
+
1468
+ end // module _all
1469
+
1470
+ end // namespace pathfinder::_internal
1471
+
1472
+ // _internal/usp (from model/_internal/usp.rel)
1473
+ namespace pathfinder::_internal
1474
+
1475
+ from ::pathfinder::_internal::utilities import invert_pg
1476
+
1477
+ doc"""
1478
+ usp[Group, PG_Edge, PG_Source, PG_Target, Config]
1479
+
1480
+ ## Inputs
1481
+ Group : all or for_each_source
1482
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
1483
+ PG_Source: (u, p) denoting that u is a source node with state p
1484
+ PG_Target: (u, p) denoting that u is a target node with state p
1485
+ Config : configuration parameters, including
1486
+ Config[:search_strategy] ∈ { :from_source, :from_target, :from_both_sides }
1487
+ Config[:product_graph] ∈ { :materialized, :on_the_fly }
1488
+
1489
+ ## Outputs
1490
+ relevant_usp_source: USP source nodes uu, a unique numbering for (u, p, l) pairs
1491
+ relevant_usp_target: USP target nodes vv, a unique numbering for (v, q, l) pairs
1492
+ usp_edge : (uu, vv, a), denoting USP edge uu --> vv with label a
1493
+ usp_node_to_node : mapping that convert uu to u
1494
+
1495
+ ## Algorithm
1496
+
1497
+ Depending on the configuration parameter `Config[:search_strategy]`, the "union of shortest
1498
+ paths" graph is constructed in one of the following ways:
1499
+ - `:from_source`: a "ball" / "sphere" is expanded in a breadth-first manner from the source
1500
+ nodes until a target node is reached
1501
+ - `:from_target`: a "ball" / "sphere" is expanded in a breadth-first manner from the target
1502
+ nodes until a source node is reached
1503
+ - `:from_both_sides`: two "balls" / "spheres" are expanded from the source and target nodes
1504
+ until they meet in the middle
1505
+
1506
+ The "ball" modules are responsible for constructing the balls, by computing the mappings
1507
+ `usp_to_pg_node` and `pg_to_usp_node` which convert between the USP node numbering and
1508
+ the product graph node numbering. We want to operate as much as possible in the USP node
1509
+ space, as it is more compact.
1510
+
1511
+ The `usp_edge` relation is constructed from the pg_to_usp_node relation via the
1512
+ `usp_edge_from_pg` module.
1513
+ """
1514
+ @outline
1515
+ module usp[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1516
+
1517
+ // internal computation:
1518
+ def _the_usp {
1519
+ _usp[Group, Config[:search_strategy], PG_Edge, PG_Source, PG_Target, Config]
1520
+ }
1521
+
1522
+ def _usp_source { _the_usp[:usp_source] }
1523
+ def _usp_target { _the_usp[:usp_target] }
1524
+
1525
+ // exposed API:
1526
+ def usp_edge { _the_usp[:usp_edge]}
1527
+ def usp_node_to_node { _the_usp[:usp_node_to_node] }
1528
+
1529
+ // A source node uu is relevant if it is included in the usp structure.
1530
+ // The additional condition usp_target(uu) is included to handle paths of
1531
+ // length zero, where a source node is relevant because it also serves as
1532
+ // a target node.
1533
+ def relevant_usp_source(uu): {
1534
+ _usp_source(uu) and (_usp_target(uu) or usp_edge(uu, _, _))
1535
+ }
1536
+
1537
+ // A target node uu is relevant if it is included in the usp structure.
1538
+ // The additional condition usp_source(uu) is included to handle paths of
1539
+ // length zero, where a target node is relevant because it also serves as
1540
+ // a source node.
1541
+ def relevant_usp_target(uu): {
1542
+ _usp_target(uu) and (_usp_source(uu) or usp_edge(_, uu, _))
1543
+ }
1544
+
1545
+ end // module usp
1546
+
1547
+ end // namespace pathfinder::_internal
1548
+
1549
+ // _internal/any_pair/single (from model/_internal/any_pair/single.rel)
1550
+ namespace pathfinder::_internal
1551
+
1552
+ from ::pathfinder::_internal::utilities import
1553
+ some, aux_hash, invert_pg
1554
+
1555
+
1556
+ @outline
1557
+ module _single[:any_pair, :from_both_sides, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1558
+
1559
+ def the_balls { two_sided_balls[:any_pair, PG_Edge, PG_Source, PG_Target, Config] }
1560
+
1561
+ def pg_dist_from_S { the_balls[:pg_dist_from_S] }
1562
+ def pg_dist_to_T { the_balls[:pg_dist_to_T] }
1563
+ def radius_S { the_balls[:radius_S] }
1564
+
1565
+ @inline
1566
+ def exists_middle {
1567
+ exists((u, p) | pg_dist_from_S(radius_S, u, p) and pg_dist_to_T(_, u, p))
1568
+ }
1569
+
1570
+ def path {
1571
+ if_then_else[
1572
+ exists_middle,
1573
+ _single_two_balls[PG_Edge, pg_dist_from_S, pg_dist_to_T, radius_S, :path],
1574
+ _single_one_ball[PG_Edge, PG_Target, pg_dist_from_S, :path]
1575
+ ]
1576
+ }
1577
+
1578
+ end // module _single[:any_pair, :from_both_sides]
1579
+
1580
+
1581
+ @outline
1582
+ module _single_two_balls[{PG_Edge}, {PG_Dist_From_S}, {PG_Dist_To_T}, {Radius_S}]
1583
+
1584
+ // Construct a single shortest path from the source to the target nodes
1585
+ def path(1, :node, i, v): path_from_S(i, v, _, _) or path_to_T(i, v, _, _)
1586
+ def path(1, :edge_label, i, label):
1587
+ path_from_S(i-1, _, _, label) or path_to_T(i, _, _, label)
1588
+
1589
+
1590
+ def radius_T {
1591
+ max[(i) :
1592
+ exists((u, p) | PG_Dist_From_S(Radius_S, u, p) and PG_Dist_To_T(i, u, p))
1593
+ ]
1594
+ }
1595
+
1596
+ // Pick a single middle node
1597
+ @function
1598
+ def single_candidate {
1599
+ some[(u, p) :
1600
+ PG_Dist_From_S(Radius_S, u, p) and PG_Dist_To_T(radius_T, u, p)
1601
+ ]
1602
+ }
1603
+
1604
+
1605
+ // Length of the shortest paths from the source to the target nodes
1606
+ def dist_S_to_T { Radius_S + radius_T }
1607
+
1608
+
1609
+ // Build a single shortest path from the source to the the selected middle node
1610
+ // by moving backward along edges in the product graph. With each path node
1611
+ // keep the corresponding node of the product graph.
1612
+ @function
1613
+ def path_from_S[i]:
1614
+ (single_candidate, aux_hash, i = Radius_S) // use dummy pg node `aux_hash`
1615
+
1616
+ @force_dnf
1617
+ def path_from_S[i]:
1618
+ some[
1619
+ (u, p, a): exists((q, v) |
1620
+ PG_Dist_From_S(i, u, p) and
1621
+ PG_Edge(p, q, a, u, v) and
1622
+ path_from_S(i + 1, v, q, _))
1623
+ ]
1624
+
1625
+
1626
+ // Build the shortest path from the selected middle node to the target node
1627
+ // by moving forward along edges in the product graph. With each path node
1628
+ // keep the corresponding node of the product graph.
1629
+ @function
1630
+ def path_to_T[i]:
1631
+ (single_candidate, aux_hash, i = Radius_S) // use dummy pg node
1632
+
1633
+ @force_dnf
1634
+ def path_to_T[i]:
1635
+ some[
1636
+ (v, q, a): exists((p, u) |
1637
+ PG_Dist_To_T(dist_S_to_T - i, v, q) and
1638
+ PG_Edge(p, q, a, u, v) and
1639
+ path_to_T(i - 1, u, p, _))
1640
+ ]
1641
+
1642
+ end // module _single_from_middle
1643
+
1644
+
1645
+ @outline
1646
+ module _single_one_ball[{PG_Edge}, {PG_Target}, {PG_Dist_From_S}]
1647
+
1648
+ // Construct a single shortest path from the source to the target nodes
1649
+ def path(1, :node, i, v): _path(i, v, _, _)
1650
+ def path(1, :edge_label, i, label): _path(i-1, _, _, label)
1651
+
1652
+
1653
+ def radius {
1654
+ max[(i) :
1655
+ exists((u, p) | PG_Dist_From_S(i, u, p) and PG_Target(u, p))
1656
+ ]
1657
+ }
1658
+
1659
+
1660
+ @function
1661
+ def _path[i]: (
1662
+ some[(u, p, a) :
1663
+ PG_Dist_From_S[u, p] = radius and PG_Target(u, p) and a = aux_hash
1664
+ ],
1665
+ i = radius
1666
+ )
1667
+ @force_dnf
1668
+ def _path[i]:
1669
+ some[
1670
+ (u, p, a): exists((q, v) |
1671
+ PG_Dist_From_S[u, p] = i and
1672
+ PG_Edge(p, q, a, u, v) and
1673
+ _path(i + 1, v, q, _))
1674
+ ]
1675
+
1676
+ end // module _single_from_middle
1677
+
1678
+
1679
+ @outline
1680
+ module _single[
1681
+ :any_pair, :from_source, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}
1682
+ ]
1683
+
1684
+ def the_ball { ball[:any_pair, PG_Edge, PG_Source, PG_Target, Config] }
1685
+
1686
+ def pg_dist_from_S { the_ball[:pg_dist_from_S] }
1687
+ def radius { the_ball[:radius] }
1688
+
1689
+
1690
+ // Construct a single shortest path from the source to the target nodes
1691
+ def path(1, :node, i, v): _path(i, v, _, _)
1692
+ def path(1, :edge_label, i, label): _path(i-1, _, _, label)
1693
+
1694
+ @function
1695
+ def _path[i]: (
1696
+ some[(u, p, a) :
1697
+ pg_dist_from_S[u, p] = radius and PG_Target(u, p) and a = aux_hash
1698
+ ],
1699
+ i = radius
1700
+ )
1701
+ @force_dnf
1702
+ def _path[i]:
1703
+ some[
1704
+ (u, p, a): exists((q, v) |
1705
+ pg_dist_from_S[u, p] = i and
1706
+ PG_Edge(p, q, a, u, v) and
1707
+ _path(i + 1, v, q, _))
1708
+ ]
1709
+
1710
+ end // module _single[:any_pair, :from_source]
1711
+
1712
+
1713
+ @outline
1714
+ module _single[
1715
+ :any_pair, :from_target, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}
1716
+ ]
1717
+
1718
+ def the_ball { ball[:any_pair, invert_pg[PG_Edge], PG_Target, PG_Source, Config] }
1719
+
1720
+ def pg_dist_from_S { the_ball[:pg_dist_from_S] }
1721
+ def radius { the_ball[:radius] }
1722
+
1723
+
1724
+ // Construct a single shortest path from the source to the target nodes
1725
+ def path(1, :node, i, v): _path(i, v, _, _)
1726
+ def path(1, :edge_label, i, label): _path(i, _, _, label)
1727
+
1728
+ @function
1729
+ def _path[i]: (
1730
+ some[(u, p, a) :
1731
+ pg_dist_from_S[u, p] = radius and PG_Source(u, p) and a = aux_hash
1732
+ ],
1733
+ i = 0
1734
+ )
1735
+ @force_dnf
1736
+ def _path[i]:
1737
+ some[
1738
+ (u, p, a): exists((q, v) |
1739
+ pg_dist_from_S[u, p] = radius - i and
1740
+ PG_Edge(q, p, a, v, u) and
1741
+ _path(i - 1, v, q, _))
1742
+ ]
1743
+
1744
+ end // module _single[:any_pair, :from_target]
1745
+
1746
+ end // namespace pathfinder::_internal
1747
+
1748
+ // _internal/single (from model/_internal/single.rel)
1749
+ namespace pathfinder::_internal
1750
+
1751
+ doc"""
1752
+ single[Group, PG_Edge, PG_Source, PG_Target, Config]
1753
+
1754
+ ## Inputs
1755
+ Group : all or for_each_source
1756
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
1757
+ PG_Source: (u, p) denoting that u is a source node with state p
1758
+ PG_Target: (u, p) denoting that u is a target node with state p
1759
+ Config : configuration parameters
1760
+
1761
+ ## Outputs
1762
+ path : a single shortest path from the source nodes to the target nodes in
1763
+ the graph defined by PG_Edge
1764
+ """
1765
+ @inline
1766
+ def single[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1767
+ _single[
1768
+ Group,
1769
+ Config[:search_strategy],
1770
+ PG_Edge,
1771
+ PG_Source,
1772
+ PG_Target,
1773
+ Config,
1774
+ :path
1775
+ ]
1776
+ }
1777
+
1778
+ end // namespace pathfinder::_internal
1779
+
1780
+ // _internal/limit (from model/_internal/limit.rel)
1781
+ namespace pathfinder::_internal
1782
+
1783
+ from ::pathfinder::_internal::utilities import
1784
+ walks_count, enumeration_helpers
1785
+
1786
+ doc"""
1787
+ limit[Group, PG_Edge, PG_Source, PG_Target, PG_Label, Config]
1788
+
1789
+ ## Inputs
1790
+ Group : all or for_each_source
1791
+ PG_Edge : (p, q, a, u, v), denoting (u, p) -> (v, q) with label a
1792
+ PG_Source: (u, p) denoting that u is a source node with state p
1793
+ PG_Target: (u, p) denoting that u is a target node with state p
1794
+ PG_Label : (a, lab), mapping hash label a to actual label lab of the PG edges
1795
+ Config : configuration parameters
1796
+
1797
+ ## Outputs
1798
+ paths : k shortest paths from the source nodes to the target nodes in
1799
+ the graph defined by PG_Edge, where k = Config[:path_count]
1800
+ """
1801
+ @inline
1802
+ def limit[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1803
+ _limit[Group, PG_Edge, PG_Source, PG_Target, Config, :paths]
1804
+ }
1805
+
1806
+ @outline
1807
+ module _limit[{Group}, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1808
+
1809
+ def the_usp { usp[Group, PG_Edge, PG_Source, PG_Target, Config] }
1810
+
1811
+ def relevant_usp_source { the_usp[:relevant_usp_source] }
1812
+ def relevant_usp_target { the_usp[:relevant_usp_target] }
1813
+ def usp_edge { the_usp[:usp_edge] }
1814
+ def usp_node_to_node { the_usp[:usp_node_to_node] }
1815
+
1816
+
1817
+ // For a node u, num_walks[u] is the number of shortest walks for the shortest_paths
1818
+ // semantics, or the number of walks for all the other semantics, from the node u
1819
+ // to a target node
1820
+ def num_walks { walks_count[usp_edge, relevant_usp_target, Config, :num_walks] }
1821
+
1822
+
1823
+ // paths is the set with the first Config[:path_count] shortest paths from PG_Source
1824
+ // PG_Target for the shortest_paths semantics, and the set with the first
1825
+ // Config[:path_count] walks from PG_Source to PG_Target of length at most
1826
+ // Config[:max_path_length] for the walks semantics.
1827
+ def paths { enumeration_helpers[
1828
+ usp_edge,
1829
+ relevant_usp_source,
1830
+ relevant_usp_target,
1831
+ usp_node_to_node,
1832
+ num_walks,
1833
+ Config,
1834
+ :paths
1835
+ ]
1836
+ }
1837
+
1838
+ end // module _all
1839
+
1840
+ end // namespace pathfinder::_internal
1841
+
1842
+ // config (from model/config.rel)
1843
+ namespace pathfinder
1844
+ module default_config
1845
+ def graph_type {:labeled}
1846
+ def semantics {:shortest_paths}
1847
+ def group {:any_pair}
1848
+ def selector {:all}
1849
+ def path_ids {:non_canonical}
1850
+ def node_indexing {:zero_based}
1851
+ def edge_indexing {:one_based}
1852
+ def debug {:debug_off}
1853
+ def product_graph {:materialized}
1854
+ def search_strategy {:from_both_sides}
1855
+ def path_enum_partials {:dynamic}
1856
+ def search_start_nodes {:multiple}
1857
+ def search_radius {:bounded}
1858
+ end
1859
+
1860
+ @inline
1861
+ def is_valid_config[{Config}]: {
1862
+ empty(config_errors[Config])
1863
+ }
1864
+
1865
+ @inline
1866
+ def config_errors[{Config}]: {
1867
+ "Illegal config keys: %(_print_symbols[_illegal_config_keys[Config]])" where
1868
+ not empty(_illegal_config_keys[Config])
1869
+ }
1870
+
1871
+ @inline
1872
+ def _illegal_config_keys[{Config}]: {
1873
+ diff[first[Config], first[default_config]]
1874
+ }
1875
+
1876
+ @inline
1877
+ def _print_symbols[{L}]: string_join[", ", enumerate[relname_string[L]]]
1878
+
1879
+ end
1880
+
1881
+ // pathfinder (from model/pathfinder.rel)
1882
+ namespace pathfinder
1883
+
1884
+ from ::pathfinder::_internal import all, single, limit
1885
+ from ::pathfinder::_internal::utilities import post_process_paths
1886
+
1887
+
1888
+ def version { "0.7.0" }
1889
+
1890
+ doc"""
1891
+
1892
+ find_paths[{Input}, {Config}]
1893
+
1894
+ Return paths connecting a set of source nodes to a set of target in a product graph,
1895
+ where these parameters are specified in the relation Input:
1896
+ - Input[:pg_source] is the set of source nodes in the product graph
1897
+ - Input[:pg_target] is the set of target nodes in the product graph
1898
+ - Input[:pg_graph] is the product graph
1899
+
1900
+ The semantics deciding _which_ paths are returned is determined by the
1901
+ configuration Config. The most basic configuration parameters include:
1902
+ - Config[:semantics] ∈ {:simple, :trail, :shortest_path, :walks}, with :shortest_path
1903
+ being the default.
1904
+ - Config[:group] ∈ {:any_pair, :for_each_source, :for_each_pair}, with :any_pair
1905
+ being the default.
1906
+ - Config[:selector] ∈ {:all, :single, :limit, :random, :uniform_random}, will :all
1907
+ being the default.
1908
+
1909
+ There are also other configuration parameters that can be used to control the behavior of
1910
+ the pathfinder algorithm, mostly for performance reasons. The full list of configuration
1911
+ parameters can be found in the documentation.
1912
+
1913
+ The output is a set of paths represented vertically with each path having a canonical
1914
+ identifier. Namely, the output is a relation paths with two specializations:
1915
+ * paths(path_id, :node, i, v) indicating that the i-th node of the path path_id is v.
1916
+ * paths(path_id, :edge_label, i, lab) indicating that the i-th edge of the path path_id
1917
+ is labeled with lab (the edge connects the (i-1)-th and i-th node of the path).
1918
+ """
1919
+ @track(:graphlib, :pathfinder_find_paths)
1920
+ @outline
1921
+ def find_paths[{Input}, {Config}]: {
1922
+ _find_paths[Input[:pg_graph], Input[:pg_source], Input[:pg_target], Config, :paths]
1923
+ }
1924
+
1925
+ @inline
1926
+ module _find_paths[{PG_Edge}, {PG_Source}, {PG_Target}, {Config}]
1927
+
1928
+ def raw_paths {
1929
+ _raw_paths[Config[:selector], PG_Edge, PG_Source, PG_Target, Config]
1930
+ }
1931
+
1932
+ def paths { post_process_paths[Config, raw_paths] }
1933
+
1934
+ end // _find_paths[{Conn}, {S}, {T}, {Config}]
1935
+
1936
+ @inline
1937
+ def _raw_paths[:all, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1938
+ all[Config[:group], PG_Edge, PG_Source, PG_Target, Config]
1939
+ }
1940
+
1941
+ @inline
1942
+ def _raw_paths[:single, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1943
+ single[Config[:group], PG_Edge, PG_Source, PG_Target, Config]
1944
+ }
1945
+
1946
+ @inline
1947
+ def _raw_paths[:limit, {PG_Edge}, {PG_Source}, {PG_Target}, {Config}]: {
1948
+ limit[Config[:group], PG_Edge, PG_Source, PG_Target, Config]
1949
+ }
1950
+
1951
+ end // namespace pathfinder