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.
- frontend/debugger/dist/.gitignore +2 -0
- frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
- frontend/debugger/dist/assets/index-Cssla-O7.js +208 -0
- frontend/debugger/dist/assets/index-DlHsYx1V.css +9 -0
- frontend/debugger/dist/index.html +17 -0
- relationalai/__init__.py +256 -1
- relationalai/clients/__init__.py +18 -0
- relationalai/clients/client.py +947 -0
- relationalai/clients/config.py +673 -0
- relationalai/clients/direct_access_client.py +118 -0
- relationalai/clients/exec_txn_poller.py +91 -0
- relationalai/clients/hash_util.py +31 -0
- relationalai/clients/local.py +586 -0
- relationalai/clients/profile_polling.py +73 -0
- relationalai/clients/resources/__init__.py +8 -0
- relationalai/clients/resources/azure/azure.py +502 -0
- relationalai/clients/resources/snowflake/__init__.py +20 -0
- relationalai/clients/resources/snowflake/cli_resources.py +98 -0
- relationalai/clients/resources/snowflake/direct_access_resources.py +734 -0
- relationalai/clients/resources/snowflake/engine_service.py +381 -0
- relationalai/clients/resources/snowflake/engine_state_handlers.py +315 -0
- relationalai/clients/resources/snowflake/error_handlers.py +240 -0
- relationalai/clients/resources/snowflake/export_procedure.py.jinja +249 -0
- relationalai/clients/resources/snowflake/resources_factory.py +99 -0
- relationalai/clients/resources/snowflake/snowflake.py +3185 -0
- relationalai/clients/resources/snowflake/use_index_poller.py +1019 -0
- relationalai/clients/resources/snowflake/use_index_resources.py +188 -0
- relationalai/clients/resources/snowflake/util.py +387 -0
- relationalai/clients/result_helpers.py +420 -0
- relationalai/clients/types.py +118 -0
- relationalai/clients/util.py +356 -0
- relationalai/debugging.py +389 -0
- relationalai/dsl.py +1749 -0
- relationalai/early_access/builder/__init__.py +30 -0
- relationalai/early_access/builder/builder/__init__.py +35 -0
- relationalai/early_access/builder/snowflake/__init__.py +12 -0
- relationalai/early_access/builder/std/__init__.py +25 -0
- relationalai/early_access/builder/std/decimals/__init__.py +12 -0
- relationalai/early_access/builder/std/integers/__init__.py +12 -0
- relationalai/early_access/builder/std/math/__init__.py +12 -0
- relationalai/early_access/builder/std/strings/__init__.py +14 -0
- relationalai/early_access/devtools/__init__.py +12 -0
- relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
- relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
- relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
- relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
- relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
- relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
- relationalai/early_access/dsl/bindings/common.py +402 -0
- relationalai/early_access/dsl/bindings/csv.py +170 -0
- relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
- relationalai/early_access/dsl/bindings/snowflake.py +64 -0
- relationalai/early_access/dsl/codegen/binder.py +411 -0
- relationalai/early_access/dsl/codegen/common.py +79 -0
- relationalai/early_access/dsl/codegen/helpers.py +23 -0
- relationalai/early_access/dsl/codegen/relations.py +700 -0
- relationalai/early_access/dsl/codegen/weaver.py +417 -0
- relationalai/early_access/dsl/core/builders/__init__.py +47 -0
- relationalai/early_access/dsl/core/builders/logic.py +19 -0
- relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
- relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
- relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
- relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
- relationalai/early_access/dsl/core/context.py +13 -0
- relationalai/early_access/dsl/core/cset.py +132 -0
- relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
- relationalai/early_access/dsl/core/exprs/relational.py +18 -0
- relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
- relationalai/early_access/dsl/core/instances.py +44 -0
- relationalai/early_access/dsl/core/logic/__init__.py +193 -0
- relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
- relationalai/early_access/dsl/core/logic/exists.py +223 -0
- relationalai/early_access/dsl/core/logic/helper.py +163 -0
- relationalai/early_access/dsl/core/namespaces.py +32 -0
- relationalai/early_access/dsl/core/relations.py +276 -0
- relationalai/early_access/dsl/core/rules.py +112 -0
- relationalai/early_access/dsl/core/std/__init__.py +45 -0
- relationalai/early_access/dsl/core/temporal/recall.py +6 -0
- relationalai/early_access/dsl/core/types/__init__.py +270 -0
- relationalai/early_access/dsl/core/types/concepts.py +128 -0
- relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
- relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
- relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
- relationalai/early_access/dsl/core/types/standard.py +92 -0
- relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
- relationalai/early_access/dsl/core/types/variables.py +203 -0
- relationalai/early_access/dsl/ir/compiler.py +318 -0
- relationalai/early_access/dsl/ir/executor.py +260 -0
- relationalai/early_access/dsl/ontologies/constraints.py +88 -0
- relationalai/early_access/dsl/ontologies/export.py +30 -0
- relationalai/early_access/dsl/ontologies/models.py +453 -0
- relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
- relationalai/early_access/dsl/ontologies/readings.py +60 -0
- relationalai/early_access/dsl/ontologies/relationships.py +322 -0
- relationalai/early_access/dsl/ontologies/roles.py +87 -0
- relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
- relationalai/early_access/dsl/orm/constraints.py +438 -0
- relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
- relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
- relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
- relationalai/early_access/dsl/orm/measures/measures.py +299 -0
- relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
- relationalai/early_access/dsl/orm/models.py +256 -0
- relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
- relationalai/early_access/dsl/orm/printer.py +469 -0
- relationalai/early_access/dsl/orm/reasoners.py +480 -0
- relationalai/early_access/dsl/orm/relations.py +19 -0
- relationalai/early_access/dsl/orm/relationships.py +251 -0
- relationalai/early_access/dsl/orm/types.py +42 -0
- relationalai/early_access/dsl/orm/utils.py +79 -0
- relationalai/early_access/dsl/orm/verb.py +204 -0
- relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
- relationalai/early_access/dsl/relations.py +170 -0
- relationalai/early_access/dsl/rulesets.py +69 -0
- relationalai/early_access/dsl/schemas/__init__.py +450 -0
- relationalai/early_access/dsl/schemas/builder.py +48 -0
- relationalai/early_access/dsl/schemas/comp_names.py +51 -0
- relationalai/early_access/dsl/schemas/components.py +203 -0
- relationalai/early_access/dsl/schemas/contexts.py +156 -0
- relationalai/early_access/dsl/schemas/exprs.py +89 -0
- relationalai/early_access/dsl/schemas/fragments.py +464 -0
- relationalai/early_access/dsl/serialization.py +79 -0
- relationalai/early_access/dsl/serialize/exporter.py +163 -0
- relationalai/early_access/dsl/snow/api.py +105 -0
- relationalai/early_access/dsl/snow/common.py +76 -0
- relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
- relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
- relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
- relationalai/early_access/dsl/types/__init__.py +40 -0
- relationalai/early_access/dsl/types/concepts.py +12 -0
- relationalai/early_access/dsl/types/entities.py +135 -0
- relationalai/early_access/dsl/types/values.py +17 -0
- relationalai/early_access/dsl/utils.py +102 -0
- relationalai/early_access/graphs/__init__.py +13 -0
- relationalai/early_access/lqp/__init__.py +12 -0
- relationalai/early_access/lqp/compiler/__init__.py +12 -0
- relationalai/early_access/lqp/constructors/__init__.py +18 -0
- relationalai/early_access/lqp/executor/__init__.py +12 -0
- relationalai/early_access/lqp/ir/__init__.py +12 -0
- relationalai/early_access/lqp/passes/__init__.py +12 -0
- relationalai/early_access/lqp/pragmas/__init__.py +12 -0
- relationalai/early_access/lqp/primitives/__init__.py +12 -0
- relationalai/early_access/lqp/types/__init__.py +12 -0
- relationalai/early_access/lqp/utils/__init__.py +12 -0
- relationalai/early_access/lqp/validators/__init__.py +12 -0
- relationalai/early_access/metamodel/__init__.py +58 -0
- relationalai/early_access/metamodel/builtins/__init__.py +12 -0
- relationalai/early_access/metamodel/compiler/__init__.py +12 -0
- relationalai/early_access/metamodel/dependency/__init__.py +12 -0
- relationalai/early_access/metamodel/factory/__init__.py +17 -0
- relationalai/early_access/metamodel/helpers/__init__.py +12 -0
- relationalai/early_access/metamodel/ir/__init__.py +14 -0
- relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
- relationalai/early_access/metamodel/typer/__init__.py +3 -0
- relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
- relationalai/early_access/metamodel/types/__init__.py +15 -0
- relationalai/early_access/metamodel/util/__init__.py +15 -0
- relationalai/early_access/metamodel/visitor/__init__.py +12 -0
- relationalai/early_access/rel/__init__.py +12 -0
- relationalai/early_access/rel/executor/__init__.py +12 -0
- relationalai/early_access/rel/rel_utils/__init__.py +12 -0
- relationalai/early_access/rel/rewrite/__init__.py +7 -0
- relationalai/early_access/solvers/__init__.py +19 -0
- relationalai/early_access/sql/__init__.py +11 -0
- relationalai/early_access/sql/executor/__init__.py +3 -0
- relationalai/early_access/sql/rewrite/__init__.py +3 -0
- relationalai/early_access/tests/logging/__init__.py +12 -0
- relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
- relationalai/early_access/tests/utils/__init__.py +12 -0
- relationalai/environments/__init__.py +35 -0
- relationalai/environments/base.py +381 -0
- relationalai/environments/colab.py +14 -0
- relationalai/environments/generic.py +71 -0
- relationalai/environments/ipython.py +68 -0
- relationalai/environments/jupyter.py +9 -0
- relationalai/environments/snowbook.py +169 -0
- relationalai/errors.py +2496 -0
- relationalai/experimental/SF.py +38 -0
- relationalai/experimental/inspect.py +47 -0
- relationalai/experimental/pathfinder/__init__.py +158 -0
- relationalai/experimental/pathfinder/api.py +160 -0
- relationalai/experimental/pathfinder/automaton.py +584 -0
- relationalai/experimental/pathfinder/bridge.py +226 -0
- relationalai/experimental/pathfinder/compiler.py +416 -0
- relationalai/experimental/pathfinder/datalog.py +214 -0
- relationalai/experimental/pathfinder/diagnostics.py +56 -0
- relationalai/experimental/pathfinder/filter.py +236 -0
- relationalai/experimental/pathfinder/glushkov.py +439 -0
- relationalai/experimental/pathfinder/options.py +265 -0
- relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +1951 -0
- relationalai/experimental/pathfinder/rpq.py +344 -0
- relationalai/experimental/pathfinder/transition.py +200 -0
- relationalai/experimental/pathfinder/utils.py +26 -0
- relationalai/experimental/paths/README.md +107 -0
- relationalai/experimental/paths/api.py +143 -0
- relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
- relationalai/experimental/paths/code_organization.md +2 -0
- relationalai/experimental/paths/examples/Movies.ipynb +16328 -0
- relationalai/experimental/paths/examples/basic_example.py +40 -0
- relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
- relationalai/experimental/paths/examples/movie_example.py +77 -0
- relationalai/experimental/paths/examples/movies_data/actedin.csv +193 -0
- relationalai/experimental/paths/examples/movies_data/directed.csv +45 -0
- relationalai/experimental/paths/examples/movies_data/follows.csv +7 -0
- relationalai/experimental/paths/examples/movies_data/movies.csv +39 -0
- relationalai/experimental/paths/examples/movies_data/person.csv +134 -0
- relationalai/experimental/paths/examples/movies_data/produced.csv +16 -0
- relationalai/experimental/paths/examples/movies_data/ratings.csv +10 -0
- relationalai/experimental/paths/examples/movies_data/wrote.csv +11 -0
- relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
- relationalai/experimental/paths/examples/paths_example.py +116 -0
- relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
- relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
- relationalai/experimental/paths/graph.py +185 -0
- relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
- relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
- relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
- relationalai/experimental/paths/path_algorithms/single.py +59 -0
- relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
- relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
- relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
- relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
- relationalai/experimental/paths/path_algorithms/usp.py +150 -0
- relationalai/experimental/paths/product_graph.py +93 -0
- relationalai/experimental/paths/rpq/automaton.py +584 -0
- relationalai/experimental/paths/rpq/diagnostics.py +56 -0
- relationalai/experimental/paths/rpq/rpq.py +378 -0
- relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
- relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
- relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
- relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
- relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
- relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
- relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
- relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
- relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
- relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
- relationalai/experimental/paths/tree_agg.py +168 -0
- relationalai/experimental/paths/utilities/iterators.py +27 -0
- relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
- relationalai/experimental/solvers.py +1087 -0
- relationalai/loaders/csv.py +195 -0
- relationalai/loaders/loader.py +177 -0
- relationalai/loaders/types.py +23 -0
- relationalai/rel_emitter.py +373 -0
- relationalai/rel_utils.py +185 -0
- relationalai/semantics/__init__.py +22 -146
- relationalai/semantics/designs/query_builder/identify_by.md +106 -0
- relationalai/semantics/devtools/benchmark_lqp.py +535 -0
- relationalai/semantics/devtools/compilation_manager.py +294 -0
- relationalai/semantics/devtools/extract_lqp.py +110 -0
- relationalai/semantics/internal/internal.py +3785 -0
- relationalai/semantics/internal/snowflake.py +325 -0
- relationalai/semantics/lqp/README.md +34 -0
- relationalai/semantics/lqp/builtins.py +16 -0
- relationalai/semantics/lqp/compiler.py +22 -0
- relationalai/semantics/lqp/constructors.py +68 -0
- relationalai/semantics/lqp/executor.py +469 -0
- relationalai/semantics/lqp/intrinsics.py +24 -0
- relationalai/semantics/lqp/model2lqp.py +877 -0
- relationalai/semantics/lqp/passes.py +680 -0
- relationalai/semantics/lqp/primitives.py +252 -0
- relationalai/semantics/lqp/result_helpers.py +202 -0
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
- relationalai/semantics/lqp/rewrite/cdc.py +216 -0
- relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
- relationalai/semantics/lqp/rewrite/extract_keys.py +512 -0
- relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
- relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
- relationalai/semantics/lqp/rewrite/splinter.py +76 -0
- relationalai/semantics/lqp/types.py +101 -0
- relationalai/semantics/lqp/utils.py +160 -0
- relationalai/semantics/lqp/validators.py +57 -0
- relationalai/semantics/metamodel/__init__.py +40 -6
- relationalai/semantics/metamodel/builtins.py +771 -205
- relationalai/semantics/metamodel/compiler.py +133 -0
- relationalai/semantics/metamodel/dependency.py +862 -0
- relationalai/semantics/metamodel/executor.py +61 -0
- relationalai/semantics/metamodel/factory.py +287 -0
- relationalai/semantics/metamodel/helpers.py +361 -0
- relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
- relationalai/semantics/metamodel/rewrite/flatten.py +554 -0
- relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
- relationalai/semantics/metamodel/typer/checker.py +353 -0
- relationalai/semantics/metamodel/typer/typer.py +1399 -0
- relationalai/semantics/metamodel/util.py +506 -0
- relationalai/semantics/reasoners/__init__.py +10 -0
- relationalai/semantics/reasoners/graph/README.md +620 -0
- relationalai/semantics/reasoners/graph/__init__.py +37 -0
- relationalai/semantics/reasoners/graph/core.py +9019 -0
- relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +797 -0
- relationalai/semantics/reasoners/graph/tests/README.md +21 -0
- relationalai/semantics/reasoners/optimization/__init__.py +68 -0
- relationalai/semantics/reasoners/optimization/common.py +88 -0
- relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
- relationalai/semantics/reasoners/optimization/solvers_pb.py +1414 -0
- relationalai/semantics/rel/builtins.py +40 -0
- relationalai/semantics/rel/compiler.py +989 -0
- relationalai/semantics/rel/executor.py +362 -0
- relationalai/semantics/rel/rel.py +482 -0
- relationalai/semantics/rel/rel_utils.py +276 -0
- relationalai/semantics/snowflake/__init__.py +3 -0
- relationalai/semantics/sql/compiler.py +2503 -0
- relationalai/semantics/sql/executor/duck_db.py +52 -0
- relationalai/semantics/sql/executor/result_helpers.py +64 -0
- relationalai/semantics/sql/executor/snowflake.py +149 -0
- relationalai/semantics/sql/rewrite/denormalize.py +222 -0
- relationalai/semantics/sql/rewrite/double_negation.py +49 -0
- relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
- relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
- relationalai/semantics/sql/sql.py +504 -0
- relationalai/semantics/std/__init__.py +40 -60
- relationalai/semantics/std/constraints.py +43 -37
- relationalai/semantics/std/datetime.py +135 -246
- relationalai/semantics/std/decimals.py +52 -45
- relationalai/semantics/std/floats.py +5 -13
- relationalai/semantics/std/integers.py +11 -26
- relationalai/semantics/std/math.py +112 -183
- relationalai/semantics/std/pragmas.py +11 -0
- relationalai/semantics/std/re.py +62 -80
- relationalai/semantics/std/std.py +14 -0
- relationalai/semantics/std/strings.py +60 -117
- relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
- relationalai/semantics/tests/test_snapshot_base.py +9 -0
- relationalai/semantics/tests/utils.py +46 -0
- relationalai/std/__init__.py +70 -0
- relationalai/tools/cli.py +2089 -0
- relationalai/tools/cli_controls.py +1826 -0
- relationalai/tools/cli_helpers.py +802 -0
- relationalai/tools/debugger.py +183 -289
- relationalai/tools/debugger_client.py +109 -0
- relationalai/tools/debugger_server.py +302 -0
- relationalai/tools/dev.py +685 -0
- relationalai/tools/notes +7 -0
- relationalai/tools/qb_debugger.py +425 -0
- relationalai/util/clean_up_databases.py +95 -0
- relationalai/util/format.py +106 -48
- relationalai/util/list_databases.py +9 -0
- relationalai/util/otel_configuration.py +26 -0
- relationalai/util/otel_handler.py +484 -0
- relationalai/util/snowflake_handler.py +88 -0
- relationalai/util/span_format_test.py +43 -0
- relationalai/util/span_tracker.py +207 -0
- relationalai/util/spans_file_handler.py +72 -0
- relationalai/util/tracing_handler.py +34 -0
- relationalai-0.13.2.dist-info/METADATA +74 -0
- relationalai-0.13.2.dist-info/RECORD +460 -0
- relationalai-0.13.2.dist-info/WHEEL +4 -0
- relationalai-0.13.2.dist-info/entry_points.txt +3 -0
- relationalai-0.13.2.dist-info/licenses/LICENSE +202 -0
- relationalai_test_util/__init__.py +4 -0
- relationalai_test_util/fixtures.py +233 -0
- relationalai_test_util/snapshot.py +252 -0
- relationalai_test_util/traceback.py +118 -0
- relationalai/config/__init__.py +0 -56
- relationalai/config/config.py +0 -289
- relationalai/config/config_fields.py +0 -86
- relationalai/config/connections/__init__.py +0 -46
- relationalai/config/connections/base.py +0 -23
- relationalai/config/connections/duckdb.py +0 -29
- relationalai/config/connections/snowflake.py +0 -243
- relationalai/config/external/__init__.py +0 -17
- relationalai/config/external/dbt_converter.py +0 -101
- relationalai/config/external/dbt_models.py +0 -93
- relationalai/config/external/snowflake_converter.py +0 -41
- relationalai/config/external/snowflake_models.py +0 -85
- relationalai/config/external/utils.py +0 -19
- relationalai/semantics/backends/lqp/annotations.py +0 -11
- relationalai/semantics/backends/sql/sql_compiler.py +0 -327
- relationalai/semantics/frontend/base.py +0 -1707
- relationalai/semantics/frontend/core.py +0 -179
- relationalai/semantics/frontend/front_compiler.py +0 -1313
- relationalai/semantics/frontend/pprint.py +0 -408
- relationalai/semantics/metamodel/metamodel.py +0 -437
- relationalai/semantics/metamodel/metamodel_analyzer.py +0 -519
- relationalai/semantics/metamodel/metamodel_compiler.py +0 -0
- relationalai/semantics/metamodel/pprint.py +0 -412
- relationalai/semantics/metamodel/rewriter.py +0 -266
- relationalai/semantics/metamodel/typer.py +0 -1378
- relationalai/semantics/std/aggregates.py +0 -149
- relationalai/semantics/std/common.py +0 -44
- relationalai/semantics/std/numbers.py +0 -86
- relationalai/shims/executor.py +0 -147
- relationalai/shims/helpers.py +0 -126
- relationalai/shims/hoister.py +0 -221
- relationalai/shims/mm2v0.py +0 -1290
- relationalai/tools/cli/__init__.py +0 -6
- relationalai/tools/cli/cli.py +0 -90
- relationalai/tools/cli/components/__init__.py +0 -5
- relationalai/tools/cli/components/progress_reader.py +0 -1524
- relationalai/tools/cli/components/utils.py +0 -58
- relationalai/tools/cli/config_template.py +0 -45
- relationalai/tools/cli/dev.py +0 -19
- relationalai/tools/typer_debugger.py +0 -93
- relationalai/util/dataclasses.py +0 -43
- relationalai/util/docutils.py +0 -40
- relationalai/util/error.py +0 -199
- relationalai/util/naming.py +0 -145
- relationalai/util/python.py +0 -35
- relationalai/util/runtime.py +0 -156
- relationalai/util/schema.py +0 -197
- relationalai/util/source.py +0 -185
- relationalai/util/structures.py +0 -163
- relationalai/util/tracing.py +0 -261
- relationalai-0.13.0.dev0.dist-info/METADATA +0 -46
- relationalai-0.13.0.dev0.dist-info/RECORD +0 -488
- relationalai-0.13.0.dev0.dist-info/WHEEL +0 -5
- relationalai-0.13.0.dev0.dist-info/entry_points.txt +0 -3
- relationalai-0.13.0.dev0.dist-info/top_level.txt +0 -2
- v0/relationalai/__init__.py +0 -216
- v0/relationalai/clients/__init__.py +0 -5
- v0/relationalai/clients/azure.py +0 -477
- v0/relationalai/clients/client.py +0 -912
- v0/relationalai/clients/config.py +0 -673
- v0/relationalai/clients/direct_access_client.py +0 -118
- v0/relationalai/clients/hash_util.py +0 -31
- v0/relationalai/clients/local.py +0 -571
- v0/relationalai/clients/profile_polling.py +0 -73
- v0/relationalai/clients/result_helpers.py +0 -420
- v0/relationalai/clients/snowflake.py +0 -3869
- v0/relationalai/clients/types.py +0 -113
- v0/relationalai/clients/use_index_poller.py +0 -980
- v0/relationalai/clients/util.py +0 -356
- v0/relationalai/debugging.py +0 -389
- v0/relationalai/dsl.py +0 -1749
- v0/relationalai/early_access/builder/__init__.py +0 -30
- v0/relationalai/early_access/builder/builder/__init__.py +0 -35
- v0/relationalai/early_access/builder/snowflake/__init__.py +0 -12
- v0/relationalai/early_access/builder/std/__init__.py +0 -25
- v0/relationalai/early_access/builder/std/decimals/__init__.py +0 -12
- v0/relationalai/early_access/builder/std/integers/__init__.py +0 -12
- v0/relationalai/early_access/builder/std/math/__init__.py +0 -12
- v0/relationalai/early_access/builder/std/strings/__init__.py +0 -14
- v0/relationalai/early_access/devtools/__init__.py +0 -12
- v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
- v0/relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
- v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
- v0/relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
- v0/relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
- v0/relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
- v0/relationalai/early_access/dsl/bindings/common.py +0 -402
- v0/relationalai/early_access/dsl/bindings/csv.py +0 -170
- v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
- v0/relationalai/early_access/dsl/bindings/snowflake.py +0 -64
- v0/relationalai/early_access/dsl/codegen/binder.py +0 -411
- v0/relationalai/early_access/dsl/codegen/common.py +0 -79
- v0/relationalai/early_access/dsl/codegen/helpers.py +0 -23
- v0/relationalai/early_access/dsl/codegen/relations.py +0 -700
- v0/relationalai/early_access/dsl/codegen/weaver.py +0 -417
- v0/relationalai/early_access/dsl/core/builders/__init__.py +0 -47
- v0/relationalai/early_access/dsl/core/builders/logic.py +0 -19
- v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
- v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
- v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
- v0/relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
- v0/relationalai/early_access/dsl/core/context.py +0 -13
- v0/relationalai/early_access/dsl/core/cset.py +0 -132
- v0/relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
- v0/relationalai/early_access/dsl/core/exprs/relational.py +0 -18
- v0/relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
- v0/relationalai/early_access/dsl/core/instances.py +0 -44
- v0/relationalai/early_access/dsl/core/logic/__init__.py +0 -193
- v0/relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
- v0/relationalai/early_access/dsl/core/logic/exists.py +0 -223
- v0/relationalai/early_access/dsl/core/logic/helper.py +0 -163
- v0/relationalai/early_access/dsl/core/namespaces.py +0 -32
- v0/relationalai/early_access/dsl/core/relations.py +0 -276
- v0/relationalai/early_access/dsl/core/rules.py +0 -112
- v0/relationalai/early_access/dsl/core/std/__init__.py +0 -45
- v0/relationalai/early_access/dsl/core/temporal/recall.py +0 -6
- v0/relationalai/early_access/dsl/core/types/__init__.py +0 -270
- v0/relationalai/early_access/dsl/core/types/concepts.py +0 -128
- v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
- v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
- v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
- v0/relationalai/early_access/dsl/core/types/standard.py +0 -92
- v0/relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
- v0/relationalai/early_access/dsl/core/types/variables.py +0 -203
- v0/relationalai/early_access/dsl/ir/compiler.py +0 -318
- v0/relationalai/early_access/dsl/ir/executor.py +0 -260
- v0/relationalai/early_access/dsl/ontologies/constraints.py +0 -88
- v0/relationalai/early_access/dsl/ontologies/export.py +0 -30
- v0/relationalai/early_access/dsl/ontologies/models.py +0 -453
- v0/relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
- v0/relationalai/early_access/dsl/ontologies/readings.py +0 -60
- v0/relationalai/early_access/dsl/ontologies/relationships.py +0 -322
- v0/relationalai/early_access/dsl/ontologies/roles.py +0 -87
- v0/relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
- v0/relationalai/early_access/dsl/orm/constraints.py +0 -438
- v0/relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
- v0/relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
- v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
- v0/relationalai/early_access/dsl/orm/measures/measures.py +0 -299
- v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
- v0/relationalai/early_access/dsl/orm/models.py +0 -256
- v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
- v0/relationalai/early_access/dsl/orm/printer.py +0 -469
- v0/relationalai/early_access/dsl/orm/reasoners.py +0 -480
- v0/relationalai/early_access/dsl/orm/relations.py +0 -19
- v0/relationalai/early_access/dsl/orm/relationships.py +0 -251
- v0/relationalai/early_access/dsl/orm/types.py +0 -42
- v0/relationalai/early_access/dsl/orm/utils.py +0 -79
- v0/relationalai/early_access/dsl/orm/verb.py +0 -204
- v0/relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
- v0/relationalai/early_access/dsl/relations.py +0 -170
- v0/relationalai/early_access/dsl/rulesets.py +0 -69
- v0/relationalai/early_access/dsl/schemas/__init__.py +0 -450
- v0/relationalai/early_access/dsl/schemas/builder.py +0 -48
- v0/relationalai/early_access/dsl/schemas/comp_names.py +0 -51
- v0/relationalai/early_access/dsl/schemas/components.py +0 -203
- v0/relationalai/early_access/dsl/schemas/contexts.py +0 -156
- v0/relationalai/early_access/dsl/schemas/exprs.py +0 -89
- v0/relationalai/early_access/dsl/schemas/fragments.py +0 -464
- v0/relationalai/early_access/dsl/serialization.py +0 -79
- v0/relationalai/early_access/dsl/serialize/exporter.py +0 -163
- v0/relationalai/early_access/dsl/snow/api.py +0 -104
- v0/relationalai/early_access/dsl/snow/common.py +0 -76
- v0/relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
- v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
- v0/relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
- v0/relationalai/early_access/dsl/types/__init__.py +0 -40
- v0/relationalai/early_access/dsl/types/concepts.py +0 -12
- v0/relationalai/early_access/dsl/types/entities.py +0 -135
- v0/relationalai/early_access/dsl/types/values.py +0 -17
- v0/relationalai/early_access/dsl/utils.py +0 -102
- v0/relationalai/early_access/graphs/__init__.py +0 -13
- v0/relationalai/early_access/lqp/__init__.py +0 -12
- v0/relationalai/early_access/lqp/compiler/__init__.py +0 -12
- v0/relationalai/early_access/lqp/constructors/__init__.py +0 -18
- v0/relationalai/early_access/lqp/executor/__init__.py +0 -12
- v0/relationalai/early_access/lqp/ir/__init__.py +0 -12
- v0/relationalai/early_access/lqp/passes/__init__.py +0 -12
- v0/relationalai/early_access/lqp/pragmas/__init__.py +0 -12
- v0/relationalai/early_access/lqp/primitives/__init__.py +0 -12
- v0/relationalai/early_access/lqp/types/__init__.py +0 -12
- v0/relationalai/early_access/lqp/utils/__init__.py +0 -12
- v0/relationalai/early_access/lqp/validators/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/__init__.py +0 -58
- v0/relationalai/early_access/metamodel/builtins/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/compiler/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/dependency/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/factory/__init__.py +0 -17
- v0/relationalai/early_access/metamodel/helpers/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/ir/__init__.py +0 -14
- v0/relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
- v0/relationalai/early_access/metamodel/typer/__init__.py +0 -3
- v0/relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
- v0/relationalai/early_access/metamodel/types/__init__.py +0 -15
- v0/relationalai/early_access/metamodel/util/__init__.py +0 -15
- v0/relationalai/early_access/metamodel/visitor/__init__.py +0 -12
- v0/relationalai/early_access/rel/__init__.py +0 -12
- v0/relationalai/early_access/rel/executor/__init__.py +0 -12
- v0/relationalai/early_access/rel/rel_utils/__init__.py +0 -12
- v0/relationalai/early_access/rel/rewrite/__init__.py +0 -7
- v0/relationalai/early_access/solvers/__init__.py +0 -19
- v0/relationalai/early_access/sql/__init__.py +0 -11
- v0/relationalai/early_access/sql/executor/__init__.py +0 -3
- v0/relationalai/early_access/sql/rewrite/__init__.py +0 -3
- v0/relationalai/early_access/tests/logging/__init__.py +0 -12
- v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
- v0/relationalai/early_access/tests/utils/__init__.py +0 -12
- v0/relationalai/environments/__init__.py +0 -35
- v0/relationalai/environments/base.py +0 -381
- v0/relationalai/environments/colab.py +0 -14
- v0/relationalai/environments/generic.py +0 -71
- v0/relationalai/environments/ipython.py +0 -68
- v0/relationalai/environments/jupyter.py +0 -9
- v0/relationalai/environments/snowbook.py +0 -169
- v0/relationalai/errors.py +0 -2455
- v0/relationalai/experimental/SF.py +0 -38
- v0/relationalai/experimental/inspect.py +0 -47
- v0/relationalai/experimental/pathfinder/__init__.py +0 -158
- v0/relationalai/experimental/pathfinder/api.py +0 -160
- v0/relationalai/experimental/pathfinder/automaton.py +0 -584
- v0/relationalai/experimental/pathfinder/bridge.py +0 -226
- v0/relationalai/experimental/pathfinder/compiler.py +0 -416
- v0/relationalai/experimental/pathfinder/datalog.py +0 -214
- v0/relationalai/experimental/pathfinder/diagnostics.py +0 -56
- v0/relationalai/experimental/pathfinder/filter.py +0 -236
- v0/relationalai/experimental/pathfinder/glushkov.py +0 -439
- v0/relationalai/experimental/pathfinder/options.py +0 -265
- v0/relationalai/experimental/pathfinder/rpq.py +0 -344
- v0/relationalai/experimental/pathfinder/transition.py +0 -200
- v0/relationalai/experimental/pathfinder/utils.py +0 -26
- v0/relationalai/experimental/paths/api.py +0 -143
- v0/relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
- v0/relationalai/experimental/paths/examples/basic_example.py +0 -40
- v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
- v0/relationalai/experimental/paths/examples/movie_example.py +0 -77
- v0/relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
- v0/relationalai/experimental/paths/examples/paths_example.py +0 -116
- v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
- v0/relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
- v0/relationalai/experimental/paths/graph.py +0 -185
- v0/relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
- v0/relationalai/experimental/paths/path_algorithms/single.py +0 -59
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
- v0/relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
- v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
- v0/relationalai/experimental/paths/path_algorithms/usp.py +0 -150
- v0/relationalai/experimental/paths/product_graph.py +0 -93
- v0/relationalai/experimental/paths/rpq/automaton.py +0 -584
- v0/relationalai/experimental/paths/rpq/diagnostics.py +0 -56
- v0/relationalai/experimental/paths/rpq/rpq.py +0 -378
- v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
- v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
- v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
- v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
- v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
- v0/relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
- v0/relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
- v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
- v0/relationalai/experimental/paths/tree_agg.py +0 -168
- v0/relationalai/experimental/paths/utilities/iterators.py +0 -27
- v0/relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
- v0/relationalai/experimental/solvers.py +0 -1087
- v0/relationalai/loaders/csv.py +0 -195
- v0/relationalai/loaders/loader.py +0 -177
- v0/relationalai/loaders/types.py +0 -23
- v0/relationalai/rel_emitter.py +0 -373
- v0/relationalai/rel_utils.py +0 -185
- v0/relationalai/semantics/__init__.py +0 -29
- v0/relationalai/semantics/devtools/benchmark_lqp.py +0 -536
- v0/relationalai/semantics/devtools/compilation_manager.py +0 -294
- v0/relationalai/semantics/devtools/extract_lqp.py +0 -110
- v0/relationalai/semantics/internal/internal.py +0 -3785
- v0/relationalai/semantics/internal/snowflake.py +0 -324
- v0/relationalai/semantics/lqp/builtins.py +0 -16
- v0/relationalai/semantics/lqp/compiler.py +0 -22
- v0/relationalai/semantics/lqp/constructors.py +0 -68
- v0/relationalai/semantics/lqp/executor.py +0 -469
- v0/relationalai/semantics/lqp/intrinsics.py +0 -24
- v0/relationalai/semantics/lqp/model2lqp.py +0 -839
- v0/relationalai/semantics/lqp/passes.py +0 -680
- v0/relationalai/semantics/lqp/primitives.py +0 -252
- v0/relationalai/semantics/lqp/result_helpers.py +0 -202
- v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -57
- v0/relationalai/semantics/lqp/rewrite/cdc.py +0 -216
- v0/relationalai/semantics/lqp/rewrite/extract_common.py +0 -338
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +0 -449
- v0/relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
- v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -314
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -296
- v0/relationalai/semantics/lqp/rewrite/splinter.py +0 -76
- v0/relationalai/semantics/lqp/types.py +0 -101
- v0/relationalai/semantics/lqp/utils.py +0 -160
- v0/relationalai/semantics/lqp/validators.py +0 -57
- v0/relationalai/semantics/metamodel/__init__.py +0 -40
- v0/relationalai/semantics/metamodel/builtins.py +0 -774
- v0/relationalai/semantics/metamodel/compiler.py +0 -133
- v0/relationalai/semantics/metamodel/dependency.py +0 -862
- v0/relationalai/semantics/metamodel/executor.py +0 -61
- v0/relationalai/semantics/metamodel/factory.py +0 -287
- v0/relationalai/semantics/metamodel/helpers.py +0 -361
- v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -210
- v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +0 -549
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -165
- v0/relationalai/semantics/metamodel/typer/checker.py +0 -353
- v0/relationalai/semantics/metamodel/typer/typer.py +0 -1395
- v0/relationalai/semantics/metamodel/util.py +0 -505
- v0/relationalai/semantics/reasoners/__init__.py +0 -10
- v0/relationalai/semantics/reasoners/graph/__init__.py +0 -37
- v0/relationalai/semantics/reasoners/graph/core.py +0 -9020
- v0/relationalai/semantics/reasoners/optimization/__init__.py +0 -68
- v0/relationalai/semantics/reasoners/optimization/common.py +0 -88
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1163
- v0/relationalai/semantics/rel/builtins.py +0 -40
- v0/relationalai/semantics/rel/compiler.py +0 -989
- v0/relationalai/semantics/rel/executor.py +0 -359
- v0/relationalai/semantics/rel/rel.py +0 -482
- v0/relationalai/semantics/rel/rel_utils.py +0 -276
- v0/relationalai/semantics/snowflake/__init__.py +0 -3
- v0/relationalai/semantics/sql/compiler.py +0 -2503
- v0/relationalai/semantics/sql/executor/duck_db.py +0 -52
- v0/relationalai/semantics/sql/executor/result_helpers.py +0 -64
- v0/relationalai/semantics/sql/executor/snowflake.py +0 -145
- v0/relationalai/semantics/sql/rewrite/denormalize.py +0 -222
- v0/relationalai/semantics/sql/rewrite/double_negation.py +0 -49
- v0/relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
- v0/relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
- v0/relationalai/semantics/sql/sql.py +0 -504
- v0/relationalai/semantics/std/__init__.py +0 -54
- v0/relationalai/semantics/std/constraints.py +0 -43
- v0/relationalai/semantics/std/datetime.py +0 -363
- v0/relationalai/semantics/std/decimals.py +0 -62
- v0/relationalai/semantics/std/floats.py +0 -7
- v0/relationalai/semantics/std/integers.py +0 -22
- v0/relationalai/semantics/std/math.py +0 -141
- v0/relationalai/semantics/std/pragmas.py +0 -11
- v0/relationalai/semantics/std/re.py +0 -83
- v0/relationalai/semantics/std/std.py +0 -14
- v0/relationalai/semantics/std/strings.py +0 -63
- v0/relationalai/semantics/tests/__init__.py +0 -0
- v0/relationalai/semantics/tests/test_snapshot_abstract.py +0 -143
- v0/relationalai/semantics/tests/test_snapshot_base.py +0 -9
- v0/relationalai/semantics/tests/utils.py +0 -46
- v0/relationalai/std/__init__.py +0 -70
- v0/relationalai/tools/__init__.py +0 -0
- v0/relationalai/tools/cli.py +0 -1940
- v0/relationalai/tools/cli_controls.py +0 -1826
- v0/relationalai/tools/cli_helpers.py +0 -390
- v0/relationalai/tools/debugger.py +0 -183
- v0/relationalai/tools/debugger_client.py +0 -109
- v0/relationalai/tools/debugger_server.py +0 -302
- v0/relationalai/tools/dev.py +0 -685
- v0/relationalai/tools/qb_debugger.py +0 -425
- v0/relationalai/util/clean_up_databases.py +0 -95
- v0/relationalai/util/format.py +0 -123
- v0/relationalai/util/list_databases.py +0 -9
- v0/relationalai/util/otel_configuration.py +0 -25
- v0/relationalai/util/otel_handler.py +0 -484
- v0/relationalai/util/snowflake_handler.py +0 -88
- v0/relationalai/util/span_format_test.py +0 -43
- v0/relationalai/util/span_tracker.py +0 -207
- v0/relationalai/util/spans_file_handler.py +0 -72
- v0/relationalai/util/tracing_handler.py +0 -34
- /relationalai/{semantics/frontend → analysis}/__init__.py +0 -0
- {v0/relationalai → relationalai}/analysis/mechanistic.py +0 -0
- {v0/relationalai → relationalai}/analysis/whynot.py +0 -0
- /relationalai/{shims → auth}/__init__.py +0 -0
- {v0/relationalai → relationalai}/auth/jwt_generator.py +0 -0
- {v0/relationalai → relationalai}/auth/oauth_callback_server.py +0 -0
- {v0/relationalai → relationalai}/auth/token_handler.py +0 -0
- {v0/relationalai → relationalai}/auth/util.py +0 -0
- {v0/relationalai/clients → relationalai/clients/resources/snowflake}/cache_store.py +0 -0
- {v0/relationalai → relationalai}/compiler.py +0 -0
- {v0/relationalai → relationalai}/dependencies.py +0 -0
- {v0/relationalai → relationalai}/docutils.py +0 -0
- {v0/relationalai/analysis → relationalai/early_access}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/__init__.py +0 -0
- {v0/relationalai/auth → relationalai/early_access/dsl/adapters}/__init__.py +0 -0
- {v0/relationalai/early_access → relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
- {v0/relationalai/early_access/dsl/adapters → relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
- {v0/relationalai/early_access/dsl/adapters/orm → relationalai/early_access/dsl/bindings}/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/adapters/owl → relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/bindings → relationalai/early_access/dsl/codegen}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/constants.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/core/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/core/stack.py +0 -0
- {v0/relationalai/early_access/dsl/bindings/legacy → relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/core/utils.py +0 -0
- {v0/relationalai/early_access/dsl/codegen → relationalai/early_access/dsl/ir}/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/core/temporal → relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
- {v0/relationalai/early_access/dsl/ir → relationalai/early_access/dsl/orm}/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/ontologies → relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
- {v0/relationalai/early_access/dsl/orm → relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/orm/measures → relationalai/early_access/dsl/serialize}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
- {v0/relationalai → relationalai}/early_access/dsl/serialize/model.py +0 -0
- {v0/relationalai/early_access/dsl/physical_metadata → relationalai/early_access/dsl/snow}/__init__.py +0 -0
- {v0/relationalai → relationalai}/early_access/tests/__init__.py +0 -0
- {v0/relationalai → relationalai}/environments/ci.py +0 -0
- {v0/relationalai → relationalai}/environments/hex.py +0 -0
- {v0/relationalai → relationalai}/environments/terminal.py +0 -0
- {v0/relationalai → relationalai}/experimental/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/graphs.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/rpq/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/rpq/filter.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/rpq/glushkov.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/rpq/transition.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/utilities/__init__.py +0 -0
- {v0/relationalai → relationalai}/experimental/paths/utilities/utilities.py +0 -0
- {v0/relationalai/early_access/dsl/serialize → relationalai/loaders}/__init__.py +0 -0
- {v0/relationalai → relationalai}/metagen.py +0 -0
- {v0/relationalai → relationalai}/metamodel.py +0 -0
- {v0/relationalai → relationalai}/rel.py +0 -0
- {v0/relationalai → relationalai}/semantics/devtools/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/internal/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/internal/annotations.py +0 -0
- {v0/relationalai → relationalai}/semantics/lqp/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/lqp/ir.py +0 -0
- {v0/relationalai → relationalai}/semantics/lqp/pragmas.py +0 -0
- {v0/relationalai → relationalai}/semantics/lqp/rewrite/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/dataflow.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/ir.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/rewrite/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/typer/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/types.py +0 -0
- {v0/relationalai → relationalai}/semantics/metamodel/visitor.py +0 -0
- {v0/relationalai → relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/rel/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/sql/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/sql/executor/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/sql/rewrite/__init__.py +0 -0
- {v0/relationalai/early_access/dsl/snow → relationalai/semantics/tests}/__init__.py +0 -0
- {v0/relationalai → relationalai}/semantics/tests/logging.py +0 -0
- {v0/relationalai → relationalai}/std/aggregates.py +0 -0
- {v0/relationalai → relationalai}/std/dates.py +0 -0
- {v0/relationalai → relationalai}/std/graphs.py +0 -0
- {v0/relationalai → relationalai}/std/inspect.py +0 -0
- {v0/relationalai → relationalai}/std/math.py +0 -0
- {v0/relationalai → relationalai}/std/re.py +0 -0
- {v0/relationalai → relationalai}/std/strings.py +0 -0
- {v0/relationalai/loaders → relationalai/tools}/__init__.py +0 -0
- {v0/relationalai → relationalai}/tools/cleanup_snapshots.py +0 -0
- {v0/relationalai → relationalai}/tools/constants.py +0 -0
- {v0/relationalai → relationalai}/tools/query_utils.py +0 -0
- {v0/relationalai → relationalai}/tools/snapshot_viewer.py +0 -0
- {v0/relationalai → relationalai}/util/__init__.py +0 -0
- {v0/relationalai → relationalai}/util/constants.py +0 -0
- {v0/relationalai → relationalai}/util/graph.py +0 -0
- {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
|