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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (837) hide show
  1. frontend/debugger/dist/.gitignore +2 -0
  2. frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
  3. frontend/debugger/dist/assets/index-Cssla-O7.js +208 -0
  4. frontend/debugger/dist/assets/index-DlHsYx1V.css +9 -0
  5. frontend/debugger/dist/index.html +17 -0
  6. relationalai/__init__.py +256 -1
  7. relationalai/clients/__init__.py +18 -0
  8. relationalai/clients/client.py +912 -0
  9. relationalai/clients/config.py +673 -0
  10. relationalai/clients/direct_access_client.py +118 -0
  11. relationalai/clients/exec_txn_poller.py +91 -0
  12. relationalai/clients/hash_util.py +31 -0
  13. relationalai/clients/local.py +571 -0
  14. relationalai/clients/profile_polling.py +73 -0
  15. relationalai/clients/resources/__init__.py +8 -0
  16. relationalai/clients/resources/azure/azure.py +477 -0
  17. relationalai/clients/resources/snowflake/__init__.py +20 -0
  18. relationalai/clients/resources/snowflake/cli_resources.py +87 -0
  19. relationalai/clients/resources/snowflake/direct_access_resources.py +694 -0
  20. relationalai/clients/resources/snowflake/engine_state_handlers.py +309 -0
  21. relationalai/clients/resources/snowflake/error_handlers.py +199 -0
  22. relationalai/clients/resources/snowflake/export_procedure.py.jinja +249 -0
  23. relationalai/clients/resources/snowflake/resources_factory.py +99 -0
  24. relationalai/clients/resources/snowflake/snowflake.py +3190 -0
  25. relationalai/clients/resources/snowflake/use_index_poller.py +1019 -0
  26. relationalai/clients/resources/snowflake/use_index_resources.py +188 -0
  27. relationalai/clients/resources/snowflake/util.py +387 -0
  28. relationalai/clients/result_helpers.py +420 -0
  29. relationalai/clients/types.py +113 -0
  30. relationalai/clients/util.py +356 -0
  31. relationalai/debugging.py +389 -0
  32. relationalai/dsl.py +1749 -0
  33. relationalai/early_access/builder/__init__.py +30 -0
  34. relationalai/early_access/builder/builder/__init__.py +35 -0
  35. relationalai/early_access/builder/snowflake/__init__.py +12 -0
  36. relationalai/early_access/builder/std/__init__.py +25 -0
  37. relationalai/early_access/builder/std/decimals/__init__.py +12 -0
  38. relationalai/early_access/builder/std/integers/__init__.py +12 -0
  39. relationalai/early_access/builder/std/math/__init__.py +12 -0
  40. relationalai/early_access/builder/std/strings/__init__.py +14 -0
  41. relationalai/early_access/devtools/__init__.py +12 -0
  42. relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
  43. relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
  44. relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
  45. relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
  46. relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
  47. relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
  48. relationalai/early_access/dsl/bindings/common.py +402 -0
  49. relationalai/early_access/dsl/bindings/csv.py +170 -0
  50. relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
  51. relationalai/early_access/dsl/bindings/snowflake.py +64 -0
  52. relationalai/early_access/dsl/codegen/binder.py +411 -0
  53. relationalai/early_access/dsl/codegen/common.py +79 -0
  54. relationalai/early_access/dsl/codegen/helpers.py +23 -0
  55. relationalai/early_access/dsl/codegen/relations.py +700 -0
  56. relationalai/early_access/dsl/codegen/weaver.py +417 -0
  57. relationalai/early_access/dsl/core/builders/__init__.py +47 -0
  58. relationalai/early_access/dsl/core/builders/logic.py +19 -0
  59. relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
  60. relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
  61. relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
  62. relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
  63. relationalai/early_access/dsl/core/context.py +13 -0
  64. relationalai/early_access/dsl/core/cset.py +132 -0
  65. relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
  66. relationalai/early_access/dsl/core/exprs/relational.py +18 -0
  67. relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
  68. relationalai/early_access/dsl/core/instances.py +44 -0
  69. relationalai/early_access/dsl/core/logic/__init__.py +193 -0
  70. relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
  71. relationalai/early_access/dsl/core/logic/exists.py +223 -0
  72. relationalai/early_access/dsl/core/logic/helper.py +163 -0
  73. relationalai/early_access/dsl/core/namespaces.py +32 -0
  74. relationalai/early_access/dsl/core/relations.py +276 -0
  75. relationalai/early_access/dsl/core/rules.py +112 -0
  76. relationalai/early_access/dsl/core/std/__init__.py +45 -0
  77. relationalai/early_access/dsl/core/temporal/recall.py +6 -0
  78. relationalai/early_access/dsl/core/types/__init__.py +270 -0
  79. relationalai/early_access/dsl/core/types/concepts.py +128 -0
  80. relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
  81. relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
  82. relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
  83. relationalai/early_access/dsl/core/types/standard.py +92 -0
  84. relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
  85. relationalai/early_access/dsl/core/types/variables.py +203 -0
  86. relationalai/early_access/dsl/ir/compiler.py +318 -0
  87. relationalai/early_access/dsl/ir/executor.py +260 -0
  88. relationalai/early_access/dsl/ontologies/constraints.py +88 -0
  89. relationalai/early_access/dsl/ontologies/export.py +30 -0
  90. relationalai/early_access/dsl/ontologies/models.py +453 -0
  91. relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
  92. relationalai/early_access/dsl/ontologies/readings.py +60 -0
  93. relationalai/early_access/dsl/ontologies/relationships.py +322 -0
  94. relationalai/early_access/dsl/ontologies/roles.py +87 -0
  95. relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
  96. relationalai/early_access/dsl/orm/constraints.py +438 -0
  97. relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
  98. relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
  99. relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
  100. relationalai/early_access/dsl/orm/measures/measures.py +299 -0
  101. relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
  102. relationalai/early_access/dsl/orm/models.py +256 -0
  103. relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
  104. relationalai/early_access/dsl/orm/printer.py +469 -0
  105. relationalai/early_access/dsl/orm/reasoners.py +480 -0
  106. relationalai/early_access/dsl/orm/relations.py +19 -0
  107. relationalai/early_access/dsl/orm/relationships.py +251 -0
  108. relationalai/early_access/dsl/orm/types.py +42 -0
  109. relationalai/early_access/dsl/orm/utils.py +79 -0
  110. relationalai/early_access/dsl/orm/verb.py +204 -0
  111. relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
  112. relationalai/early_access/dsl/relations.py +170 -0
  113. relationalai/early_access/dsl/rulesets.py +69 -0
  114. relationalai/early_access/dsl/schemas/__init__.py +450 -0
  115. relationalai/early_access/dsl/schemas/builder.py +48 -0
  116. relationalai/early_access/dsl/schemas/comp_names.py +51 -0
  117. relationalai/early_access/dsl/schemas/components.py +203 -0
  118. relationalai/early_access/dsl/schemas/contexts.py +156 -0
  119. relationalai/early_access/dsl/schemas/exprs.py +89 -0
  120. relationalai/early_access/dsl/schemas/fragments.py +464 -0
  121. relationalai/early_access/dsl/serialization.py +79 -0
  122. relationalai/early_access/dsl/serialize/exporter.py +163 -0
  123. relationalai/early_access/dsl/snow/api.py +105 -0
  124. relationalai/early_access/dsl/snow/common.py +76 -0
  125. relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
  126. relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
  127. relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
  128. relationalai/early_access/dsl/types/__init__.py +40 -0
  129. relationalai/early_access/dsl/types/concepts.py +12 -0
  130. relationalai/early_access/dsl/types/entities.py +135 -0
  131. relationalai/early_access/dsl/types/values.py +17 -0
  132. relationalai/early_access/dsl/utils.py +102 -0
  133. relationalai/early_access/graphs/__init__.py +13 -0
  134. relationalai/early_access/lqp/__init__.py +12 -0
  135. relationalai/early_access/lqp/compiler/__init__.py +12 -0
  136. relationalai/early_access/lqp/constructors/__init__.py +18 -0
  137. relationalai/early_access/lqp/executor/__init__.py +12 -0
  138. relationalai/early_access/lqp/ir/__init__.py +12 -0
  139. relationalai/early_access/lqp/passes/__init__.py +12 -0
  140. relationalai/early_access/lqp/pragmas/__init__.py +12 -0
  141. relationalai/early_access/lqp/primitives/__init__.py +12 -0
  142. relationalai/early_access/lqp/types/__init__.py +12 -0
  143. relationalai/early_access/lqp/utils/__init__.py +12 -0
  144. relationalai/early_access/lqp/validators/__init__.py +12 -0
  145. relationalai/early_access/metamodel/__init__.py +58 -0
  146. relationalai/early_access/metamodel/builtins/__init__.py +12 -0
  147. relationalai/early_access/metamodel/compiler/__init__.py +12 -0
  148. relationalai/early_access/metamodel/dependency/__init__.py +12 -0
  149. relationalai/early_access/metamodel/factory/__init__.py +17 -0
  150. relationalai/early_access/metamodel/helpers/__init__.py +12 -0
  151. relationalai/early_access/metamodel/ir/__init__.py +14 -0
  152. relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
  153. relationalai/early_access/metamodel/typer/__init__.py +3 -0
  154. relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
  155. relationalai/early_access/metamodel/types/__init__.py +15 -0
  156. relationalai/early_access/metamodel/util/__init__.py +15 -0
  157. relationalai/early_access/metamodel/visitor/__init__.py +12 -0
  158. relationalai/early_access/rel/__init__.py +12 -0
  159. relationalai/early_access/rel/executor/__init__.py +12 -0
  160. relationalai/early_access/rel/rel_utils/__init__.py +12 -0
  161. relationalai/early_access/rel/rewrite/__init__.py +7 -0
  162. relationalai/early_access/solvers/__init__.py +19 -0
  163. relationalai/early_access/sql/__init__.py +11 -0
  164. relationalai/early_access/sql/executor/__init__.py +3 -0
  165. relationalai/early_access/sql/rewrite/__init__.py +3 -0
  166. relationalai/early_access/tests/logging/__init__.py +12 -0
  167. relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
  168. relationalai/early_access/tests/utils/__init__.py +12 -0
  169. relationalai/environments/__init__.py +35 -0
  170. relationalai/environments/base.py +381 -0
  171. relationalai/environments/colab.py +14 -0
  172. relationalai/environments/generic.py +71 -0
  173. relationalai/environments/ipython.py +68 -0
  174. relationalai/environments/jupyter.py +9 -0
  175. relationalai/environments/snowbook.py +169 -0
  176. relationalai/errors.py +2496 -0
  177. relationalai/experimental/SF.py +38 -0
  178. relationalai/experimental/inspect.py +47 -0
  179. relationalai/experimental/pathfinder/__init__.py +158 -0
  180. relationalai/experimental/pathfinder/api.py +160 -0
  181. relationalai/experimental/pathfinder/automaton.py +584 -0
  182. relationalai/experimental/pathfinder/bridge.py +226 -0
  183. relationalai/experimental/pathfinder/compiler.py +416 -0
  184. relationalai/experimental/pathfinder/datalog.py +214 -0
  185. relationalai/experimental/pathfinder/diagnostics.py +56 -0
  186. relationalai/experimental/pathfinder/filter.py +236 -0
  187. relationalai/experimental/pathfinder/glushkov.py +439 -0
  188. relationalai/experimental/pathfinder/options.py +265 -0
  189. relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +1951 -0
  190. relationalai/experimental/pathfinder/rpq.py +344 -0
  191. relationalai/experimental/pathfinder/transition.py +200 -0
  192. relationalai/experimental/pathfinder/utils.py +26 -0
  193. relationalai/experimental/paths/README.md +107 -0
  194. relationalai/experimental/paths/api.py +143 -0
  195. relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
  196. relationalai/experimental/paths/code_organization.md +2 -0
  197. relationalai/experimental/paths/examples/Movies.ipynb +16328 -0
  198. relationalai/experimental/paths/examples/basic_example.py +40 -0
  199. relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
  200. relationalai/experimental/paths/examples/movie_example.py +77 -0
  201. relationalai/experimental/paths/examples/movies_data/actedin.csv +193 -0
  202. relationalai/experimental/paths/examples/movies_data/directed.csv +45 -0
  203. relationalai/experimental/paths/examples/movies_data/follows.csv +7 -0
  204. relationalai/experimental/paths/examples/movies_data/movies.csv +39 -0
  205. relationalai/experimental/paths/examples/movies_data/person.csv +134 -0
  206. relationalai/experimental/paths/examples/movies_data/produced.csv +16 -0
  207. relationalai/experimental/paths/examples/movies_data/ratings.csv +10 -0
  208. relationalai/experimental/paths/examples/movies_data/wrote.csv +11 -0
  209. relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
  210. relationalai/experimental/paths/examples/paths_example.py +116 -0
  211. relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
  212. relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
  213. relationalai/experimental/paths/graph.py +185 -0
  214. relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
  215. relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
  216. relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
  217. relationalai/experimental/paths/path_algorithms/single.py +59 -0
  218. relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
  219. relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
  220. relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
  221. relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
  222. relationalai/experimental/paths/path_algorithms/usp.py +150 -0
  223. relationalai/experimental/paths/product_graph.py +93 -0
  224. relationalai/experimental/paths/rpq/automaton.py +584 -0
  225. relationalai/experimental/paths/rpq/diagnostics.py +56 -0
  226. relationalai/experimental/paths/rpq/rpq.py +378 -0
  227. relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
  228. relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
  229. relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
  230. relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
  231. relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
  232. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
  233. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
  234. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
  235. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
  236. relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
  237. relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
  238. relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
  239. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
  240. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
  241. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
  242. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
  243. relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
  244. relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
  245. relationalai/experimental/paths/tree_agg.py +168 -0
  246. relationalai/experimental/paths/utilities/iterators.py +27 -0
  247. relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
  248. relationalai/experimental/solvers.py +1087 -0
  249. relationalai/loaders/csv.py +195 -0
  250. relationalai/loaders/loader.py +177 -0
  251. relationalai/loaders/types.py +23 -0
  252. relationalai/rel_emitter.py +373 -0
  253. relationalai/rel_utils.py +185 -0
  254. relationalai/semantics/__init__.py +22 -146
  255. relationalai/semantics/designs/query_builder/identify_by.md +106 -0
  256. relationalai/semantics/devtools/benchmark_lqp.py +535 -0
  257. relationalai/semantics/devtools/compilation_manager.py +294 -0
  258. relationalai/semantics/devtools/extract_lqp.py +110 -0
  259. relationalai/semantics/internal/internal.py +3785 -0
  260. relationalai/semantics/internal/snowflake.py +325 -0
  261. relationalai/semantics/lqp/README.md +34 -0
  262. relationalai/semantics/lqp/builtins.py +16 -0
  263. relationalai/semantics/lqp/compiler.py +22 -0
  264. relationalai/semantics/lqp/constructors.py +68 -0
  265. relationalai/semantics/lqp/executor.py +469 -0
  266. relationalai/semantics/lqp/intrinsics.py +24 -0
  267. relationalai/semantics/lqp/model2lqp.py +877 -0
  268. relationalai/semantics/lqp/passes.py +680 -0
  269. relationalai/semantics/lqp/primitives.py +252 -0
  270. relationalai/semantics/lqp/result_helpers.py +202 -0
  271. relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
  272. relationalai/semantics/lqp/rewrite/cdc.py +216 -0
  273. relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
  274. relationalai/semantics/lqp/rewrite/extract_keys.py +512 -0
  275. relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
  276. relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
  277. relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
  278. relationalai/semantics/lqp/rewrite/splinter.py +76 -0
  279. relationalai/semantics/lqp/types.py +101 -0
  280. relationalai/semantics/lqp/utils.py +160 -0
  281. relationalai/semantics/lqp/validators.py +57 -0
  282. relationalai/semantics/metamodel/__init__.py +40 -6
  283. relationalai/semantics/metamodel/builtins.py +771 -205
  284. relationalai/semantics/metamodel/compiler.py +133 -0
  285. relationalai/semantics/metamodel/dependency.py +862 -0
  286. relationalai/semantics/metamodel/executor.py +61 -0
  287. relationalai/semantics/metamodel/factory.py +287 -0
  288. relationalai/semantics/metamodel/helpers.py +361 -0
  289. relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
  290. relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
  291. relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
  292. relationalai/semantics/metamodel/rewrite/flatten.py +554 -0
  293. relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
  294. relationalai/semantics/metamodel/typer/checker.py +353 -0
  295. relationalai/semantics/metamodel/typer/typer.py +1395 -0
  296. relationalai/semantics/metamodel/util.py +506 -0
  297. relationalai/semantics/reasoners/__init__.py +10 -0
  298. relationalai/semantics/reasoners/graph/README.md +620 -0
  299. relationalai/semantics/reasoners/graph/__init__.py +37 -0
  300. relationalai/semantics/reasoners/graph/core.py +9019 -0
  301. relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +797 -0
  302. relationalai/semantics/reasoners/graph/tests/README.md +21 -0
  303. relationalai/semantics/reasoners/optimization/__init__.py +68 -0
  304. relationalai/semantics/reasoners/optimization/common.py +88 -0
  305. relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
  306. relationalai/semantics/reasoners/optimization/solvers_pb.py +1414 -0
  307. relationalai/semantics/rel/builtins.py +40 -0
  308. relationalai/semantics/rel/compiler.py +989 -0
  309. relationalai/semantics/rel/executor.py +362 -0
  310. relationalai/semantics/rel/rel.py +482 -0
  311. relationalai/semantics/rel/rel_utils.py +276 -0
  312. relationalai/semantics/snowflake/__init__.py +3 -0
  313. relationalai/semantics/sql/compiler.py +2503 -0
  314. relationalai/semantics/sql/executor/duck_db.py +52 -0
  315. relationalai/semantics/sql/executor/result_helpers.py +64 -0
  316. relationalai/semantics/sql/executor/snowflake.py +149 -0
  317. relationalai/semantics/sql/rewrite/denormalize.py +222 -0
  318. relationalai/semantics/sql/rewrite/double_negation.py +49 -0
  319. relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
  320. relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
  321. relationalai/semantics/sql/sql.py +504 -0
  322. relationalai/semantics/std/__init__.py +40 -60
  323. relationalai/semantics/std/constraints.py +43 -37
  324. relationalai/semantics/std/datetime.py +135 -246
  325. relationalai/semantics/std/decimals.py +52 -45
  326. relationalai/semantics/std/floats.py +5 -13
  327. relationalai/semantics/std/integers.py +11 -26
  328. relationalai/semantics/std/math.py +112 -183
  329. relationalai/semantics/std/pragmas.py +11 -0
  330. relationalai/semantics/std/re.py +62 -80
  331. relationalai/semantics/std/std.py +14 -0
  332. relationalai/semantics/std/strings.py +60 -117
  333. relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
  334. relationalai/semantics/tests/test_snapshot_base.py +9 -0
  335. relationalai/semantics/tests/utils.py +46 -0
  336. relationalai/std/__init__.py +70 -0
  337. relationalai/tools/cli.py +1936 -0
  338. relationalai/tools/cli_controls.py +1826 -0
  339. relationalai/tools/cli_helpers.py +398 -0
  340. relationalai/tools/debugger.py +183 -289
  341. relationalai/tools/debugger_client.py +109 -0
  342. relationalai/tools/debugger_server.py +302 -0
  343. relationalai/tools/dev.py +685 -0
  344. relationalai/tools/notes +7 -0
  345. relationalai/tools/qb_debugger.py +425 -0
  346. relationalai/util/clean_up_databases.py +95 -0
  347. relationalai/util/format.py +106 -48
  348. relationalai/util/list_databases.py +9 -0
  349. relationalai/util/otel_configuration.py +26 -0
  350. relationalai/util/otel_handler.py +484 -0
  351. relationalai/util/snowflake_handler.py +88 -0
  352. relationalai/util/span_format_test.py +43 -0
  353. relationalai/util/span_tracker.py +207 -0
  354. relationalai/util/spans_file_handler.py +72 -0
  355. relationalai/util/tracing_handler.py +34 -0
  356. relationalai-0.13.1.dist-info/METADATA +74 -0
  357. relationalai-0.13.1.dist-info/RECORD +459 -0
  358. relationalai-0.13.1.dist-info/WHEEL +4 -0
  359. relationalai-0.13.1.dist-info/entry_points.txt +3 -0
  360. relationalai-0.13.1.dist-info/licenses/LICENSE +202 -0
  361. relationalai_test_util/__init__.py +4 -0
  362. relationalai_test_util/fixtures.py +233 -0
  363. relationalai_test_util/snapshot.py +252 -0
  364. relationalai_test_util/traceback.py +118 -0
  365. relationalai/config/__init__.py +0 -56
  366. relationalai/config/config.py +0 -289
  367. relationalai/config/config_fields.py +0 -86
  368. relationalai/config/connections/__init__.py +0 -46
  369. relationalai/config/connections/base.py +0 -23
  370. relationalai/config/connections/duckdb.py +0 -29
  371. relationalai/config/connections/snowflake.py +0 -243
  372. relationalai/config/external/__init__.py +0 -17
  373. relationalai/config/external/dbt_converter.py +0 -101
  374. relationalai/config/external/dbt_models.py +0 -93
  375. relationalai/config/external/snowflake_converter.py +0 -41
  376. relationalai/config/external/snowflake_models.py +0 -85
  377. relationalai/config/external/utils.py +0 -19
  378. relationalai/semantics/backends/lqp/annotations.py +0 -11
  379. relationalai/semantics/backends/sql/sql_compiler.py +0 -327
  380. relationalai/semantics/frontend/base.py +0 -1707
  381. relationalai/semantics/frontend/core.py +0 -179
  382. relationalai/semantics/frontend/front_compiler.py +0 -1313
  383. relationalai/semantics/frontend/pprint.py +0 -408
  384. relationalai/semantics/metamodel/metamodel.py +0 -437
  385. relationalai/semantics/metamodel/metamodel_analyzer.py +0 -519
  386. relationalai/semantics/metamodel/metamodel_compiler.py +0 -0
  387. relationalai/semantics/metamodel/pprint.py +0 -412
  388. relationalai/semantics/metamodel/rewriter.py +0 -266
  389. relationalai/semantics/metamodel/typer.py +0 -1378
  390. relationalai/semantics/std/aggregates.py +0 -149
  391. relationalai/semantics/std/common.py +0 -44
  392. relationalai/semantics/std/numbers.py +0 -86
  393. relationalai/shims/executor.py +0 -147
  394. relationalai/shims/helpers.py +0 -126
  395. relationalai/shims/hoister.py +0 -221
  396. relationalai/shims/mm2v0.py +0 -1290
  397. relationalai/tools/cli/__init__.py +0 -6
  398. relationalai/tools/cli/cli.py +0 -90
  399. relationalai/tools/cli/components/__init__.py +0 -5
  400. relationalai/tools/cli/components/progress_reader.py +0 -1524
  401. relationalai/tools/cli/components/utils.py +0 -58
  402. relationalai/tools/cli/config_template.py +0 -45
  403. relationalai/tools/cli/dev.py +0 -19
  404. relationalai/tools/typer_debugger.py +0 -93
  405. relationalai/util/dataclasses.py +0 -43
  406. relationalai/util/docutils.py +0 -40
  407. relationalai/util/error.py +0 -199
  408. relationalai/util/naming.py +0 -145
  409. relationalai/util/python.py +0 -35
  410. relationalai/util/runtime.py +0 -156
  411. relationalai/util/schema.py +0 -197
  412. relationalai/util/source.py +0 -185
  413. relationalai/util/structures.py +0 -163
  414. relationalai/util/tracing.py +0 -261
  415. relationalai-0.13.0.dev0.dist-info/METADATA +0 -46
  416. relationalai-0.13.0.dev0.dist-info/RECORD +0 -488
  417. relationalai-0.13.0.dev0.dist-info/WHEEL +0 -5
  418. relationalai-0.13.0.dev0.dist-info/entry_points.txt +0 -3
  419. relationalai-0.13.0.dev0.dist-info/top_level.txt +0 -2
  420. v0/relationalai/__init__.py +0 -216
  421. v0/relationalai/clients/__init__.py +0 -5
  422. v0/relationalai/clients/azure.py +0 -477
  423. v0/relationalai/clients/client.py +0 -912
  424. v0/relationalai/clients/config.py +0 -673
  425. v0/relationalai/clients/direct_access_client.py +0 -118
  426. v0/relationalai/clients/hash_util.py +0 -31
  427. v0/relationalai/clients/local.py +0 -571
  428. v0/relationalai/clients/profile_polling.py +0 -73
  429. v0/relationalai/clients/result_helpers.py +0 -420
  430. v0/relationalai/clients/snowflake.py +0 -3869
  431. v0/relationalai/clients/types.py +0 -113
  432. v0/relationalai/clients/use_index_poller.py +0 -980
  433. v0/relationalai/clients/util.py +0 -356
  434. v0/relationalai/debugging.py +0 -389
  435. v0/relationalai/dsl.py +0 -1749
  436. v0/relationalai/early_access/builder/__init__.py +0 -30
  437. v0/relationalai/early_access/builder/builder/__init__.py +0 -35
  438. v0/relationalai/early_access/builder/snowflake/__init__.py +0 -12
  439. v0/relationalai/early_access/builder/std/__init__.py +0 -25
  440. v0/relationalai/early_access/builder/std/decimals/__init__.py +0 -12
  441. v0/relationalai/early_access/builder/std/integers/__init__.py +0 -12
  442. v0/relationalai/early_access/builder/std/math/__init__.py +0 -12
  443. v0/relationalai/early_access/builder/std/strings/__init__.py +0 -14
  444. v0/relationalai/early_access/devtools/__init__.py +0 -12
  445. v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
  446. v0/relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
  447. v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
  448. v0/relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
  449. v0/relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
  450. v0/relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
  451. v0/relationalai/early_access/dsl/bindings/common.py +0 -402
  452. v0/relationalai/early_access/dsl/bindings/csv.py +0 -170
  453. v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
  454. v0/relationalai/early_access/dsl/bindings/snowflake.py +0 -64
  455. v0/relationalai/early_access/dsl/codegen/binder.py +0 -411
  456. v0/relationalai/early_access/dsl/codegen/common.py +0 -79
  457. v0/relationalai/early_access/dsl/codegen/helpers.py +0 -23
  458. v0/relationalai/early_access/dsl/codegen/relations.py +0 -700
  459. v0/relationalai/early_access/dsl/codegen/weaver.py +0 -417
  460. v0/relationalai/early_access/dsl/core/builders/__init__.py +0 -47
  461. v0/relationalai/early_access/dsl/core/builders/logic.py +0 -19
  462. v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
  463. v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
  464. v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
  465. v0/relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
  466. v0/relationalai/early_access/dsl/core/context.py +0 -13
  467. v0/relationalai/early_access/dsl/core/cset.py +0 -132
  468. v0/relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
  469. v0/relationalai/early_access/dsl/core/exprs/relational.py +0 -18
  470. v0/relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
  471. v0/relationalai/early_access/dsl/core/instances.py +0 -44
  472. v0/relationalai/early_access/dsl/core/logic/__init__.py +0 -193
  473. v0/relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
  474. v0/relationalai/early_access/dsl/core/logic/exists.py +0 -223
  475. v0/relationalai/early_access/dsl/core/logic/helper.py +0 -163
  476. v0/relationalai/early_access/dsl/core/namespaces.py +0 -32
  477. v0/relationalai/early_access/dsl/core/relations.py +0 -276
  478. v0/relationalai/early_access/dsl/core/rules.py +0 -112
  479. v0/relationalai/early_access/dsl/core/std/__init__.py +0 -45
  480. v0/relationalai/early_access/dsl/core/temporal/recall.py +0 -6
  481. v0/relationalai/early_access/dsl/core/types/__init__.py +0 -270
  482. v0/relationalai/early_access/dsl/core/types/concepts.py +0 -128
  483. v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
  484. v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
  485. v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
  486. v0/relationalai/early_access/dsl/core/types/standard.py +0 -92
  487. v0/relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
  488. v0/relationalai/early_access/dsl/core/types/variables.py +0 -203
  489. v0/relationalai/early_access/dsl/ir/compiler.py +0 -318
  490. v0/relationalai/early_access/dsl/ir/executor.py +0 -260
  491. v0/relationalai/early_access/dsl/ontologies/constraints.py +0 -88
  492. v0/relationalai/early_access/dsl/ontologies/export.py +0 -30
  493. v0/relationalai/early_access/dsl/ontologies/models.py +0 -453
  494. v0/relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
  495. v0/relationalai/early_access/dsl/ontologies/readings.py +0 -60
  496. v0/relationalai/early_access/dsl/ontologies/relationships.py +0 -322
  497. v0/relationalai/early_access/dsl/ontologies/roles.py +0 -87
  498. v0/relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
  499. v0/relationalai/early_access/dsl/orm/constraints.py +0 -438
  500. v0/relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
  501. v0/relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
  502. v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
  503. v0/relationalai/early_access/dsl/orm/measures/measures.py +0 -299
  504. v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
  505. v0/relationalai/early_access/dsl/orm/models.py +0 -256
  506. v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
  507. v0/relationalai/early_access/dsl/orm/printer.py +0 -469
  508. v0/relationalai/early_access/dsl/orm/reasoners.py +0 -480
  509. v0/relationalai/early_access/dsl/orm/relations.py +0 -19
  510. v0/relationalai/early_access/dsl/orm/relationships.py +0 -251
  511. v0/relationalai/early_access/dsl/orm/types.py +0 -42
  512. v0/relationalai/early_access/dsl/orm/utils.py +0 -79
  513. v0/relationalai/early_access/dsl/orm/verb.py +0 -204
  514. v0/relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
  515. v0/relationalai/early_access/dsl/relations.py +0 -170
  516. v0/relationalai/early_access/dsl/rulesets.py +0 -69
  517. v0/relationalai/early_access/dsl/schemas/__init__.py +0 -450
  518. v0/relationalai/early_access/dsl/schemas/builder.py +0 -48
  519. v0/relationalai/early_access/dsl/schemas/comp_names.py +0 -51
  520. v0/relationalai/early_access/dsl/schemas/components.py +0 -203
  521. v0/relationalai/early_access/dsl/schemas/contexts.py +0 -156
  522. v0/relationalai/early_access/dsl/schemas/exprs.py +0 -89
  523. v0/relationalai/early_access/dsl/schemas/fragments.py +0 -464
  524. v0/relationalai/early_access/dsl/serialization.py +0 -79
  525. v0/relationalai/early_access/dsl/serialize/exporter.py +0 -163
  526. v0/relationalai/early_access/dsl/snow/api.py +0 -104
  527. v0/relationalai/early_access/dsl/snow/common.py +0 -76
  528. v0/relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
  529. v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
  530. v0/relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
  531. v0/relationalai/early_access/dsl/types/__init__.py +0 -40
  532. v0/relationalai/early_access/dsl/types/concepts.py +0 -12
  533. v0/relationalai/early_access/dsl/types/entities.py +0 -135
  534. v0/relationalai/early_access/dsl/types/values.py +0 -17
  535. v0/relationalai/early_access/dsl/utils.py +0 -102
  536. v0/relationalai/early_access/graphs/__init__.py +0 -13
  537. v0/relationalai/early_access/lqp/__init__.py +0 -12
  538. v0/relationalai/early_access/lqp/compiler/__init__.py +0 -12
  539. v0/relationalai/early_access/lqp/constructors/__init__.py +0 -18
  540. v0/relationalai/early_access/lqp/executor/__init__.py +0 -12
  541. v0/relationalai/early_access/lqp/ir/__init__.py +0 -12
  542. v0/relationalai/early_access/lqp/passes/__init__.py +0 -12
  543. v0/relationalai/early_access/lqp/pragmas/__init__.py +0 -12
  544. v0/relationalai/early_access/lqp/primitives/__init__.py +0 -12
  545. v0/relationalai/early_access/lqp/types/__init__.py +0 -12
  546. v0/relationalai/early_access/lqp/utils/__init__.py +0 -12
  547. v0/relationalai/early_access/lqp/validators/__init__.py +0 -12
  548. v0/relationalai/early_access/metamodel/__init__.py +0 -58
  549. v0/relationalai/early_access/metamodel/builtins/__init__.py +0 -12
  550. v0/relationalai/early_access/metamodel/compiler/__init__.py +0 -12
  551. v0/relationalai/early_access/metamodel/dependency/__init__.py +0 -12
  552. v0/relationalai/early_access/metamodel/factory/__init__.py +0 -17
  553. v0/relationalai/early_access/metamodel/helpers/__init__.py +0 -12
  554. v0/relationalai/early_access/metamodel/ir/__init__.py +0 -14
  555. v0/relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
  556. v0/relationalai/early_access/metamodel/typer/__init__.py +0 -3
  557. v0/relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
  558. v0/relationalai/early_access/metamodel/types/__init__.py +0 -15
  559. v0/relationalai/early_access/metamodel/util/__init__.py +0 -15
  560. v0/relationalai/early_access/metamodel/visitor/__init__.py +0 -12
  561. v0/relationalai/early_access/rel/__init__.py +0 -12
  562. v0/relationalai/early_access/rel/executor/__init__.py +0 -12
  563. v0/relationalai/early_access/rel/rel_utils/__init__.py +0 -12
  564. v0/relationalai/early_access/rel/rewrite/__init__.py +0 -7
  565. v0/relationalai/early_access/solvers/__init__.py +0 -19
  566. v0/relationalai/early_access/sql/__init__.py +0 -11
  567. v0/relationalai/early_access/sql/executor/__init__.py +0 -3
  568. v0/relationalai/early_access/sql/rewrite/__init__.py +0 -3
  569. v0/relationalai/early_access/tests/logging/__init__.py +0 -12
  570. v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
  571. v0/relationalai/early_access/tests/utils/__init__.py +0 -12
  572. v0/relationalai/environments/__init__.py +0 -35
  573. v0/relationalai/environments/base.py +0 -381
  574. v0/relationalai/environments/colab.py +0 -14
  575. v0/relationalai/environments/generic.py +0 -71
  576. v0/relationalai/environments/ipython.py +0 -68
  577. v0/relationalai/environments/jupyter.py +0 -9
  578. v0/relationalai/environments/snowbook.py +0 -169
  579. v0/relationalai/errors.py +0 -2455
  580. v0/relationalai/experimental/SF.py +0 -38
  581. v0/relationalai/experimental/inspect.py +0 -47
  582. v0/relationalai/experimental/pathfinder/__init__.py +0 -158
  583. v0/relationalai/experimental/pathfinder/api.py +0 -160
  584. v0/relationalai/experimental/pathfinder/automaton.py +0 -584
  585. v0/relationalai/experimental/pathfinder/bridge.py +0 -226
  586. v0/relationalai/experimental/pathfinder/compiler.py +0 -416
  587. v0/relationalai/experimental/pathfinder/datalog.py +0 -214
  588. v0/relationalai/experimental/pathfinder/diagnostics.py +0 -56
  589. v0/relationalai/experimental/pathfinder/filter.py +0 -236
  590. v0/relationalai/experimental/pathfinder/glushkov.py +0 -439
  591. v0/relationalai/experimental/pathfinder/options.py +0 -265
  592. v0/relationalai/experimental/pathfinder/rpq.py +0 -344
  593. v0/relationalai/experimental/pathfinder/transition.py +0 -200
  594. v0/relationalai/experimental/pathfinder/utils.py +0 -26
  595. v0/relationalai/experimental/paths/api.py +0 -143
  596. v0/relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
  597. v0/relationalai/experimental/paths/examples/basic_example.py +0 -40
  598. v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
  599. v0/relationalai/experimental/paths/examples/movie_example.py +0 -77
  600. v0/relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
  601. v0/relationalai/experimental/paths/examples/paths_example.py +0 -116
  602. v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
  603. v0/relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
  604. v0/relationalai/experimental/paths/graph.py +0 -185
  605. v0/relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
  606. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
  607. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
  608. v0/relationalai/experimental/paths/path_algorithms/single.py +0 -59
  609. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
  610. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
  611. v0/relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
  612. v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
  613. v0/relationalai/experimental/paths/path_algorithms/usp.py +0 -150
  614. v0/relationalai/experimental/paths/product_graph.py +0 -93
  615. v0/relationalai/experimental/paths/rpq/automaton.py +0 -584
  616. v0/relationalai/experimental/paths/rpq/diagnostics.py +0 -56
  617. v0/relationalai/experimental/paths/rpq/rpq.py +0 -378
  618. v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
  619. v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
  620. v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
  621. v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
  622. v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
  623. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
  624. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
  625. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
  626. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
  627. v0/relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
  628. v0/relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
  629. v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
  630. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
  631. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
  632. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
  633. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
  634. v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
  635. v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
  636. v0/relationalai/experimental/paths/tree_agg.py +0 -168
  637. v0/relationalai/experimental/paths/utilities/iterators.py +0 -27
  638. v0/relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
  639. v0/relationalai/experimental/solvers.py +0 -1087
  640. v0/relationalai/loaders/csv.py +0 -195
  641. v0/relationalai/loaders/loader.py +0 -177
  642. v0/relationalai/loaders/types.py +0 -23
  643. v0/relationalai/rel_emitter.py +0 -373
  644. v0/relationalai/rel_utils.py +0 -185
  645. v0/relationalai/semantics/__init__.py +0 -29
  646. v0/relationalai/semantics/devtools/benchmark_lqp.py +0 -536
  647. v0/relationalai/semantics/devtools/compilation_manager.py +0 -294
  648. v0/relationalai/semantics/devtools/extract_lqp.py +0 -110
  649. v0/relationalai/semantics/internal/internal.py +0 -3785
  650. v0/relationalai/semantics/internal/snowflake.py +0 -324
  651. v0/relationalai/semantics/lqp/builtins.py +0 -16
  652. v0/relationalai/semantics/lqp/compiler.py +0 -22
  653. v0/relationalai/semantics/lqp/constructors.py +0 -68
  654. v0/relationalai/semantics/lqp/executor.py +0 -469
  655. v0/relationalai/semantics/lqp/intrinsics.py +0 -24
  656. v0/relationalai/semantics/lqp/model2lqp.py +0 -839
  657. v0/relationalai/semantics/lqp/passes.py +0 -680
  658. v0/relationalai/semantics/lqp/primitives.py +0 -252
  659. v0/relationalai/semantics/lqp/result_helpers.py +0 -202
  660. v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -57
  661. v0/relationalai/semantics/lqp/rewrite/cdc.py +0 -216
  662. v0/relationalai/semantics/lqp/rewrite/extract_common.py +0 -338
  663. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +0 -449
  664. v0/relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
  665. v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -314
  666. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -296
  667. v0/relationalai/semantics/lqp/rewrite/splinter.py +0 -76
  668. v0/relationalai/semantics/lqp/types.py +0 -101
  669. v0/relationalai/semantics/lqp/utils.py +0 -160
  670. v0/relationalai/semantics/lqp/validators.py +0 -57
  671. v0/relationalai/semantics/metamodel/__init__.py +0 -40
  672. v0/relationalai/semantics/metamodel/builtins.py +0 -774
  673. v0/relationalai/semantics/metamodel/compiler.py +0 -133
  674. v0/relationalai/semantics/metamodel/dependency.py +0 -862
  675. v0/relationalai/semantics/metamodel/executor.py +0 -61
  676. v0/relationalai/semantics/metamodel/factory.py +0 -287
  677. v0/relationalai/semantics/metamodel/helpers.py +0 -361
  678. v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
  679. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -210
  680. v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
  681. v0/relationalai/semantics/metamodel/rewrite/flatten.py +0 -549
  682. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -165
  683. v0/relationalai/semantics/metamodel/typer/checker.py +0 -353
  684. v0/relationalai/semantics/metamodel/typer/typer.py +0 -1395
  685. v0/relationalai/semantics/metamodel/util.py +0 -505
  686. v0/relationalai/semantics/reasoners/__init__.py +0 -10
  687. v0/relationalai/semantics/reasoners/graph/__init__.py +0 -37
  688. v0/relationalai/semantics/reasoners/graph/core.py +0 -9020
  689. v0/relationalai/semantics/reasoners/optimization/__init__.py +0 -68
  690. v0/relationalai/semantics/reasoners/optimization/common.py +0 -88
  691. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
  692. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1163
  693. v0/relationalai/semantics/rel/builtins.py +0 -40
  694. v0/relationalai/semantics/rel/compiler.py +0 -989
  695. v0/relationalai/semantics/rel/executor.py +0 -359
  696. v0/relationalai/semantics/rel/rel.py +0 -482
  697. v0/relationalai/semantics/rel/rel_utils.py +0 -276
  698. v0/relationalai/semantics/snowflake/__init__.py +0 -3
  699. v0/relationalai/semantics/sql/compiler.py +0 -2503
  700. v0/relationalai/semantics/sql/executor/duck_db.py +0 -52
  701. v0/relationalai/semantics/sql/executor/result_helpers.py +0 -64
  702. v0/relationalai/semantics/sql/executor/snowflake.py +0 -145
  703. v0/relationalai/semantics/sql/rewrite/denormalize.py +0 -222
  704. v0/relationalai/semantics/sql/rewrite/double_negation.py +0 -49
  705. v0/relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
  706. v0/relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
  707. v0/relationalai/semantics/sql/sql.py +0 -504
  708. v0/relationalai/semantics/std/__init__.py +0 -54
  709. v0/relationalai/semantics/std/constraints.py +0 -43
  710. v0/relationalai/semantics/std/datetime.py +0 -363
  711. v0/relationalai/semantics/std/decimals.py +0 -62
  712. v0/relationalai/semantics/std/floats.py +0 -7
  713. v0/relationalai/semantics/std/integers.py +0 -22
  714. v0/relationalai/semantics/std/math.py +0 -141
  715. v0/relationalai/semantics/std/pragmas.py +0 -11
  716. v0/relationalai/semantics/std/re.py +0 -83
  717. v0/relationalai/semantics/std/std.py +0 -14
  718. v0/relationalai/semantics/std/strings.py +0 -63
  719. v0/relationalai/semantics/tests/__init__.py +0 -0
  720. v0/relationalai/semantics/tests/test_snapshot_abstract.py +0 -143
  721. v0/relationalai/semantics/tests/test_snapshot_base.py +0 -9
  722. v0/relationalai/semantics/tests/utils.py +0 -46
  723. v0/relationalai/std/__init__.py +0 -70
  724. v0/relationalai/tools/__init__.py +0 -0
  725. v0/relationalai/tools/cli.py +0 -1940
  726. v0/relationalai/tools/cli_controls.py +0 -1826
  727. v0/relationalai/tools/cli_helpers.py +0 -390
  728. v0/relationalai/tools/debugger.py +0 -183
  729. v0/relationalai/tools/debugger_client.py +0 -109
  730. v0/relationalai/tools/debugger_server.py +0 -302
  731. v0/relationalai/tools/dev.py +0 -685
  732. v0/relationalai/tools/qb_debugger.py +0 -425
  733. v0/relationalai/util/clean_up_databases.py +0 -95
  734. v0/relationalai/util/format.py +0 -123
  735. v0/relationalai/util/list_databases.py +0 -9
  736. v0/relationalai/util/otel_configuration.py +0 -25
  737. v0/relationalai/util/otel_handler.py +0 -484
  738. v0/relationalai/util/snowflake_handler.py +0 -88
  739. v0/relationalai/util/span_format_test.py +0 -43
  740. v0/relationalai/util/span_tracker.py +0 -207
  741. v0/relationalai/util/spans_file_handler.py +0 -72
  742. v0/relationalai/util/tracing_handler.py +0 -34
  743. /relationalai/{semantics/frontend → analysis}/__init__.py +0 -0
  744. {v0/relationalai → relationalai}/analysis/mechanistic.py +0 -0
  745. {v0/relationalai → relationalai}/analysis/whynot.py +0 -0
  746. /relationalai/{shims → auth}/__init__.py +0 -0
  747. {v0/relationalai → relationalai}/auth/jwt_generator.py +0 -0
  748. {v0/relationalai → relationalai}/auth/oauth_callback_server.py +0 -0
  749. {v0/relationalai → relationalai}/auth/token_handler.py +0 -0
  750. {v0/relationalai → relationalai}/auth/util.py +0 -0
  751. {v0/relationalai/clients → relationalai/clients/resources/snowflake}/cache_store.py +0 -0
  752. {v0/relationalai → relationalai}/compiler.py +0 -0
  753. {v0/relationalai → relationalai}/dependencies.py +0 -0
  754. {v0/relationalai → relationalai}/docutils.py +0 -0
  755. {v0/relationalai/analysis → relationalai/early_access}/__init__.py +0 -0
  756. {v0/relationalai → relationalai}/early_access/dsl/__init__.py +0 -0
  757. {v0/relationalai/auth → relationalai/early_access/dsl/adapters}/__init__.py +0 -0
  758. {v0/relationalai/early_access → relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
  759. {v0/relationalai → relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
  760. {v0/relationalai/early_access/dsl/adapters → relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
  761. {v0/relationalai → relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
  762. {v0/relationalai/early_access/dsl/adapters/orm → relationalai/early_access/dsl/bindings}/__init__.py +0 -0
  763. {v0/relationalai/early_access/dsl/adapters/owl → relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
  764. {v0/relationalai/early_access/dsl/bindings → relationalai/early_access/dsl/codegen}/__init__.py +0 -0
  765. {v0/relationalai → relationalai}/early_access/dsl/constants.py +0 -0
  766. {v0/relationalai → relationalai}/early_access/dsl/core/__init__.py +0 -0
  767. {v0/relationalai → relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
  768. {v0/relationalai → relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
  769. {v0/relationalai → relationalai}/early_access/dsl/core/stack.py +0 -0
  770. {v0/relationalai/early_access/dsl/bindings/legacy → relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
  771. {v0/relationalai → relationalai}/early_access/dsl/core/utils.py +0 -0
  772. {v0/relationalai/early_access/dsl/codegen → relationalai/early_access/dsl/ir}/__init__.py +0 -0
  773. {v0/relationalai/early_access/dsl/core/temporal → relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
  774. {v0/relationalai → relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
  775. {v0/relationalai/early_access/dsl/ir → relationalai/early_access/dsl/orm}/__init__.py +0 -0
  776. {v0/relationalai/early_access/dsl/ontologies → relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
  777. {v0/relationalai → relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
  778. {v0/relationalai/early_access/dsl/orm → relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
  779. {v0/relationalai/early_access/dsl/orm/measures → relationalai/early_access/dsl/serialize}/__init__.py +0 -0
  780. {v0/relationalai → relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
  781. {v0/relationalai → relationalai}/early_access/dsl/serialize/model.py +0 -0
  782. {v0/relationalai/early_access/dsl/physical_metadata → relationalai/early_access/dsl/snow}/__init__.py +0 -0
  783. {v0/relationalai → relationalai}/early_access/tests/__init__.py +0 -0
  784. {v0/relationalai → relationalai}/environments/ci.py +0 -0
  785. {v0/relationalai → relationalai}/environments/hex.py +0 -0
  786. {v0/relationalai → relationalai}/environments/terminal.py +0 -0
  787. {v0/relationalai → relationalai}/experimental/__init__.py +0 -0
  788. {v0/relationalai → relationalai}/experimental/graphs.py +0 -0
  789. {v0/relationalai → relationalai}/experimental/paths/__init__.py +0 -0
  790. {v0/relationalai → relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
  791. {v0/relationalai → relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
  792. {v0/relationalai → relationalai}/experimental/paths/rpq/__init__.py +0 -0
  793. {v0/relationalai → relationalai}/experimental/paths/rpq/filter.py +0 -0
  794. {v0/relationalai → relationalai}/experimental/paths/rpq/glushkov.py +0 -0
  795. {v0/relationalai → relationalai}/experimental/paths/rpq/transition.py +0 -0
  796. {v0/relationalai → relationalai}/experimental/paths/utilities/__init__.py +0 -0
  797. {v0/relationalai → relationalai}/experimental/paths/utilities/utilities.py +0 -0
  798. {v0/relationalai/early_access/dsl/serialize → relationalai/loaders}/__init__.py +0 -0
  799. {v0/relationalai → relationalai}/metagen.py +0 -0
  800. {v0/relationalai → relationalai}/metamodel.py +0 -0
  801. {v0/relationalai → relationalai}/rel.py +0 -0
  802. {v0/relationalai → relationalai}/semantics/devtools/__init__.py +0 -0
  803. {v0/relationalai → relationalai}/semantics/internal/__init__.py +0 -0
  804. {v0/relationalai → relationalai}/semantics/internal/annotations.py +0 -0
  805. {v0/relationalai → relationalai}/semantics/lqp/__init__.py +0 -0
  806. {v0/relationalai → relationalai}/semantics/lqp/ir.py +0 -0
  807. {v0/relationalai → relationalai}/semantics/lqp/pragmas.py +0 -0
  808. {v0/relationalai → relationalai}/semantics/lqp/rewrite/__init__.py +0 -0
  809. {v0/relationalai → relationalai}/semantics/metamodel/dataflow.py +0 -0
  810. {v0/relationalai → relationalai}/semantics/metamodel/ir.py +0 -0
  811. {v0/relationalai → relationalai}/semantics/metamodel/rewrite/__init__.py +0 -0
  812. {v0/relationalai → relationalai}/semantics/metamodel/typer/__init__.py +0 -0
  813. {v0/relationalai → relationalai}/semantics/metamodel/types.py +0 -0
  814. {v0/relationalai → relationalai}/semantics/metamodel/visitor.py +0 -0
  815. {v0/relationalai → relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
  816. {v0/relationalai → relationalai}/semantics/rel/__init__.py +0 -0
  817. {v0/relationalai → relationalai}/semantics/sql/__init__.py +0 -0
  818. {v0/relationalai → relationalai}/semantics/sql/executor/__init__.py +0 -0
  819. {v0/relationalai → relationalai}/semantics/sql/rewrite/__init__.py +0 -0
  820. {v0/relationalai/early_access/dsl/snow → relationalai/semantics/tests}/__init__.py +0 -0
  821. {v0/relationalai → relationalai}/semantics/tests/logging.py +0 -0
  822. {v0/relationalai → relationalai}/std/aggregates.py +0 -0
  823. {v0/relationalai → relationalai}/std/dates.py +0 -0
  824. {v0/relationalai → relationalai}/std/graphs.py +0 -0
  825. {v0/relationalai → relationalai}/std/inspect.py +0 -0
  826. {v0/relationalai → relationalai}/std/math.py +0 -0
  827. {v0/relationalai → relationalai}/std/re.py +0 -0
  828. {v0/relationalai → relationalai}/std/strings.py +0 -0
  829. {v0/relationalai/loaders → relationalai/tools}/__init__.py +0 -0
  830. {v0/relationalai → relationalai}/tools/cleanup_snapshots.py +0 -0
  831. {v0/relationalai → relationalai}/tools/constants.py +0 -0
  832. {v0/relationalai → relationalai}/tools/query_utils.py +0 -0
  833. {v0/relationalai → relationalai}/tools/snapshot_viewer.py +0 -0
  834. {v0/relationalai → relationalai}/util/__init__.py +0 -0
  835. {v0/relationalai → relationalai}/util/constants.py +0 -0
  836. {v0/relationalai → relationalai}/util/graph.py +0 -0
  837. {v0/relationalai → relationalai}/util/timeout.py +0 -0
@@ -0,0 +1,1414 @@
1
+ """Solver model implementation supporting protobuf and CSV formats.
2
+
3
+ This module provides the SolverModelPB class for defining optimization and
4
+ constraint programming problems that are serialized and solved by external
5
+ solver engines. Supports both protobuf (default) and CSV (future) exchange formats.
6
+
7
+ Note: This protobuf-based implementation will be deprecated in favor of the
8
+ development version (solvers_dev.py) in future releases.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import textwrap
14
+ import time
15
+ import uuid
16
+ from typing import Any, Optional
17
+
18
+ from relationalai.experimental.solvers import Solver
19
+ from relationalai.semantics.internal import internal as b
20
+ from relationalai.semantics.rel.executor import RelExecutor
21
+ from relationalai.tools.constants import DEFAULT_QUERY_TIMEOUT_MINS
22
+ from relationalai.util.timeout import calc_remaining_timeout_minutes
23
+
24
+ from .common import make_name
25
+
26
+ # =============================================================================
27
+ # Solver ProtoBuf Format Constants and Helpers
28
+ # =============================================================================
29
+
30
+ # Variable type codes for the solver protobuf format
31
+ # cont: continuous (real-valued), int: integer, bin: binary (0/1)
32
+ _VARIABLE_TYPE_CODES: dict[str, int] = {
33
+ "cont": 40,
34
+ "int": 41,
35
+ "bin": 42,
36
+ }
37
+
38
+ # First-order operators: arithmetic and mathematical functions
39
+ _FIRST_ORDER_OPERATOR_CODES: dict[str, int] = {
40
+ "+": 10,
41
+ "-": 11,
42
+ "*": 12,
43
+ "/": 13,
44
+ "^": 14,
45
+ "abs": 20,
46
+ "exp": 21,
47
+ "log": 22,
48
+ "range": 50,
49
+ }
50
+
51
+ # First-order comparison operators: relational constraints
52
+ _FIRST_ORDER_COMPARISON_CODES: dict[str, int] = {
53
+ "=": 30,
54
+ "!=": 31,
55
+ "<=": 32,
56
+ ">=": 33,
57
+ "<": 34,
58
+ ">": 35,
59
+ "implies": 62,
60
+ }
61
+
62
+ # Higher-order operators: aggregation and global constraints
63
+ _HIGHER_ORDER_OPERATOR_CODES: dict[str, int] = {
64
+ "sum": 80,
65
+ "min": 82,
66
+ "max": 83,
67
+ "count": 84,
68
+ "all_different": 90,
69
+ }
70
+
71
+ # Mapping from bound type keywords to comparison operators
72
+ _BOUND_TO_COMPARISON_OPERATOR: dict[str, str] = {
73
+ "lower": ">=",
74
+ "upper": "<=",
75
+ "fixed": "=",
76
+ }
77
+
78
+
79
+ def _make_first_order_application_with_result(
80
+ operator_code: int, *args: Any
81
+ ) -> b.Expression:
82
+ """Create a first-order application with a result variable."""
83
+ return _make_first_order_application(operator_code, *args, b.String.ref("res"))
84
+
85
+
86
+ def _make_first_order_application(operator_code: int, *args: Any) -> b.Expression:
87
+ """Create a first-order application expression."""
88
+ if not (2 <= len(args) <= 4):
89
+ raise ValueError(
90
+ f"First-order application requires 2-4 arguments, but got {len(args)}."
91
+ )
92
+ result_ref = args[-1]
93
+ if not isinstance(result_ref, b.Ref):
94
+ raise TypeError(
95
+ f"Last argument must be a Ref, got {type(result_ref).__name__}."
96
+ )
97
+ if result_ref._thing != b.String:
98
+ result_ref = b.String.ref("res")
99
+ application_builtin = b.Relationship.builtins["rel_primitive_solverlib_fo_appl"]
100
+ # Wrap operands in TupleArg for the vararg solverlib_fo_appl primitive:
101
+ # fo_appl(op, (operands...), result)
102
+ return b.Expression(
103
+ application_builtin, operator_code, b.TupleArg(args[:-1]), result_ref
104
+ )
105
+
106
+
107
+ # =============================================================================
108
+ # Main Solver Model Class
109
+ # =============================================================================
110
+
111
+
112
+ class SolverModelPB:
113
+ """Solver model interface using protobuf format for optimization problems."""
114
+
115
+ def __init__(self, model: b.Model, num_type: str) -> None:
116
+ """Initialize solver model.
117
+
118
+ Args:
119
+ model: The RelationalAI model.
120
+ num_type: Variable type - 'cont' or 'int'.
121
+ """
122
+ if num_type not in ["cont", "int"]:
123
+ raise ValueError(
124
+ f"Invalid numerical type '{num_type}'. Must be 'cont' or 'int'."
125
+ )
126
+ self._model = model
127
+ self._num_type = num_type
128
+ self._id = next(b._global_id)
129
+ # Maps relationships to their corresponding variable concepts
130
+ self._variable_relationships: dict[b.Relationship, b.Concept] = {}
131
+ prefix_uppercase = f"SolverModel_{self._id}_"
132
+ prefix_lowercase = prefix_uppercase.lower()
133
+
134
+ # Create core concepts for model components
135
+ self.Variable = Variable = model.Concept(prefix_uppercase + "Variable")
136
+ self.MinObjective = model.Concept(prefix_uppercase + "MinObjective")
137
+ self.MaxObjective = model.Concept(prefix_uppercase + "MaxObjective")
138
+ self.Constraint = model.Concept(prefix_uppercase + "Constraint")
139
+ self._model_info = {
140
+ "num_variables": Variable,
141
+ "num_min_objectives": self.MinObjective,
142
+ "num_max_objectives": self.MaxObjective,
143
+ "num_constraints": self.Constraint,
144
+ }
145
+ # Add printed_expr property to objectives and constraints for human-readable output
146
+ for concept in [self.MinObjective, self.MaxObjective, self.Constraint]:
147
+ concept.printed_expr = model.Property(
148
+ f"{{{concept._name}}} has {{printed_expr:str}}"
149
+ )
150
+
151
+ # Create relationships for result extraction
152
+ result_type = "int" if num_type == "int" else "float"
153
+ self.result_info = model.Relationship(
154
+ "{key:str} has {value:str}", short_name=(prefix_lowercase + "result_info")
155
+ )
156
+ # TODO(coey) PyRel is not able to handle "Variable._name" instead of "var" below due
157
+ # to some internal naming bug; this leads to a "Unresolved Type" warning that we
158
+ # will have to live with for now
159
+ self.point = model.Property(
160
+ f"{{var}} has {{value:{result_type}}}",
161
+ short_name=(prefix_lowercase + "point"),
162
+ )
163
+ self.points = model.Property(
164
+ f"point {{i:int}} for {{var}} has {{value:{result_type}}}",
165
+ short_name=(prefix_lowercase + "points"),
166
+ )
167
+
168
+ # Install raw rel to work around lack of support for rel_primitive_solverlib_print_expr
169
+ install_rel = f"""
170
+ declare {self.MinObjective._name}
171
+ declare {self.MaxObjective._name}
172
+ declare {self.Constraint._name}
173
+
174
+ declare {prefix_lowercase}variable_name
175
+ declare {prefix_lowercase}minobjective_name
176
+ declare {prefix_lowercase}maxobjective_name
177
+ declare {prefix_lowercase}constraint_name
178
+ declare {prefix_lowercase}minobjective_serialized
179
+ declare {prefix_lowercase}maxobjective_serialized
180
+ declare {prefix_lowercase}constraint_serialized
181
+
182
+ def {prefix_lowercase}minobjective_printed_expr(h, s):
183
+ rel_primitive_solverlib_print_expr({prefix_lowercase}minobjective_serialized[h], {prefix_lowercase}variable_name, s)
184
+
185
+ def {prefix_lowercase}maxobjective_printed_expr(h, s):
186
+ rel_primitive_solverlib_print_expr({prefix_lowercase}maxobjective_serialized[h], {prefix_lowercase}variable_name, s)
187
+
188
+ def {prefix_lowercase}constraint_printed_expr(h, s):
189
+ rel_primitive_solverlib_print_expr({prefix_lowercase}constraint_serialized[h], {prefix_lowercase}variable_name, s)
190
+ """
191
+ b.define(b.RawSource("rel", textwrap.dedent(install_rel)))
192
+
193
+
194
+ # -------------------------------------------------------------------------
195
+ # Variable Handling
196
+ # -------------------------------------------------------------------------
197
+
198
+ def solve_for(
199
+ self,
200
+ expr,
201
+ where: Optional[list[Any]] = None,
202
+ populate: bool = True,
203
+ **kwargs: Any,
204
+ ) -> b.Concept:
205
+ """Define decision variables.
206
+
207
+ Args:
208
+ expr: Relationship or expression defining variables.
209
+ where: Optional grounding conditions.
210
+ populate: Whether to populate relationship with solver results.
211
+ **kwargs: Optional properties (name, type, lower, upper, fixed).
212
+
213
+ Returns:
214
+ Variable concept.
215
+ """
216
+ if where is None:
217
+ where = []
218
+ if isinstance(expr, b.Fragment):
219
+ # TODO(coey): Remove in future
220
+ raise ValueError(
221
+ "The select fragment argument to `solve_for` is deprecated. "
222
+ "Instead, use the `where = [conditions...]` kwarg to specify optional grounding conditions."
223
+ )
224
+ elif isinstance(expr, b.Expression):
225
+ relationship = expr._op
226
+ if not isinstance(relationship, b.Relationship):
227
+ raise TypeError(
228
+ f"Expression operator must be a Relationship, got {type(relationship).__name__}."
229
+ )
230
+ params = expr._params
231
+ elif isinstance(expr, b.Relationship):
232
+ relationship = expr
233
+ params = [
234
+ b.field_to_type(self._model, field) for field in relationship._fields
235
+ ]
236
+ else:
237
+ raise TypeError(
238
+ f"Invalid expression type for solve_for: {type(expr).__name__}. "
239
+ f"Expected Relationship or Expression."
240
+ )
241
+
242
+ if len(params) != len(relationship._fields):
243
+ raise ValueError(
244
+ f"Parameter count mismatch: Got {len(params)} params "
245
+ f"but relationship has {len(relationship._fields)} fields."
246
+ )
247
+ if relationship in self._variable_relationships:
248
+ raise ValueError(
249
+ f"Variables are already defined for relationship {relationship}."
250
+ )
251
+
252
+ # Create a specialized Variable concept for this relationship
253
+ # Each decision variable gets its own concept subtype
254
+ Var = self._model.Concept(
255
+ f"{self.Variable._name}_{str(relationship).replace('.', '_')}",
256
+ extends=[self.Variable],
257
+ )
258
+ self._variable_relationships[relationship] = Var
259
+
260
+ # Build field dict from relationship parameters (excluding the value field)
261
+ fields = {}
262
+ for i in range(len(params) - 1):
263
+ if i == 0 and relationship._parent is not None:
264
+ concept = relationship._parent
265
+ if not isinstance(concept, b.Concept):
266
+ raise TypeError(
267
+ f"Relationship parent must be a Concept, got {type(concept).__name__}."
268
+ )
269
+ else:
270
+ concept = params[i]
271
+ field_name = relationship._field_names[i]
272
+ # Prevent "Implicit Subtype Relationship" warnings by explicitly registering
273
+ # the relationship on the parent Variable concept before using it on subtypes
274
+ self.Variable._relationships[field_name] = self.Variable._get_relationship(
275
+ field_name
276
+ )
277
+ fields[field_name] = concept
278
+ var = Var.new(**fields)
279
+ b.define(var).where(*where)
280
+
281
+ # Handle optional variable properties
282
+ for key, value in kwargs.items():
283
+ if key == "name":
284
+ definition = self.Variable.name(var, make_name(value))
285
+ elif key == "type":
286
+ if not isinstance(value, str):
287
+ raise TypeError(
288
+ f"Variable 'type' must be a string, but got {type(value).__name__}."
289
+ )
290
+ if value not in _VARIABLE_TYPE_CODES:
291
+ valid_types = ", ".join(_VARIABLE_TYPE_CODES.keys())
292
+ raise ValueError(
293
+ f"Invalid variable type '{value}'. Valid types are: {valid_types}."
294
+ )
295
+ serialized_expr = _make_first_order_application_with_result(
296
+ _VARIABLE_TYPE_CODES[value], var
297
+ )
298
+ definition = self.Constraint.new(serialized=serialized_expr)
299
+ elif key in ("lower", "upper", "fixed"):
300
+ if not isinstance(value, (b.Producer, float, int)):
301
+ raise TypeError(
302
+ f"Variable '{key}' must be a number, but got {type(value).__name__}."
303
+ )
304
+ # Map bound types to comparison operators
305
+ operator = _BOUND_TO_COMPARISON_OPERATOR[key]
306
+ serialized_expr = _make_first_order_application_with_result(
307
+ _FIRST_ORDER_COMPARISON_CODES[operator], var, value
308
+ )
309
+ definition = self.Constraint.new(serialized=serialized_expr)
310
+ else:
311
+ raise ValueError(f"Invalid keyword argument '{key}' for solve_for.")
312
+ b.define(definition).where(*where)
313
+
314
+ if populate:
315
+ # Automatically populate the variable relationship with solver results
316
+ # This defines the original relationship to pull values from self.point after solving
317
+ value_ref = (b.Integer if self._num_type == "int" else b.Float).ref()
318
+ b.define(
319
+ relationship(
320
+ *[getattr(Var, field_name) for field_name in fields], value_ref
321
+ )
322
+ ).where(self.point(Var, value_ref))
323
+
324
+ return Var
325
+
326
+ # -------------------------------------------------------------------------
327
+ # Objective Functions
328
+ # -------------------------------------------------------------------------
329
+
330
+ def minimize(
331
+ self,
332
+ expr: b.Producer | float | int | b.Fragment,
333
+ name: Optional[str | list[str]] = None,
334
+ ) -> None:
335
+ """Add minimization objective.
336
+
337
+ Args:
338
+ expr: Expression to minimize.
339
+ name: Optional objective name.
340
+ """
341
+ return self._add_objective(self.MinObjective, expr, name)
342
+
343
+ def maximize(
344
+ self,
345
+ expr: b.Producer | float | int | b.Fragment,
346
+ name: Optional[str | list[str]] = None,
347
+ ) -> None:
348
+ """Add maximization objective.
349
+
350
+ Args:
351
+ expr: Expression to maximize.
352
+ name: Optional objective name.
353
+ """
354
+ return self._add_objective(self.MaxObjective, expr, name)
355
+
356
+ def _add_objective(
357
+ self,
358
+ objective_concept: b.Concept,
359
+ expr: b.Producer | float | int | b.Fragment,
360
+ name: Optional[str | list[str]],
361
+ ) -> None:
362
+ context = SymbolifyContext(self)
363
+ symbolic_expr = context.rewrite(expr)
364
+ if not isinstance(symbolic_expr, Symbolic):
365
+ # Expr is not symbolic (a constant) - wrap it as a trivial expression
366
+ symbolic_expr = _make_first_order_application_with_result(0, expr)
367
+ else:
368
+ # Unwrap the symbolic expression
369
+ unwrapped_expr = symbolic_expr.expr
370
+ # Check if it's a bare variable (needs wrapping for protobuf)
371
+ is_bare_variable = isinstance(unwrapped_expr, b.ConceptMember)
372
+ is_fragment_with_variable = (
373
+ isinstance(unwrapped_expr, b.Fragment)
374
+ and unwrapped_expr._select
375
+ and isinstance(unwrapped_expr._select[0], b.ConceptMember)
376
+ )
377
+ if is_bare_variable or is_fragment_with_variable:
378
+ # The protobuf format requires all objectives to be expressions, not bare variables
379
+ symbolic_expr = _make_first_order_application_with_result(
380
+ 0, unwrapped_expr
381
+ )
382
+ else:
383
+ symbolic_expr = unwrapped_expr
384
+
385
+ if isinstance(symbolic_expr, Symbolic):
386
+ raise ValueError(
387
+ "Internal error. Expression is still Symbolic after unwrapping."
388
+ )
389
+
390
+ objective = objective_concept.new(serialized=symbolic_expr)
391
+ definitions = [objective]
392
+ if name is not None:
393
+ definitions.append(objective.name(make_name(name)))
394
+ b.define(*definitions)
395
+
396
+ def satisfy(
397
+ self,
398
+ expr: b.Fragment,
399
+ check: bool = False,
400
+ name: Optional[str | list[str]] = None,
401
+ ) -> None:
402
+ """Add constraints.
403
+
404
+ Args:
405
+ expr: Fragment with require clause.
406
+ check: Whether to keep require in model validation.
407
+ name: Optional constraint name.
408
+ """
409
+ if not isinstance(expr, b.Fragment):
410
+ raise TypeError(
411
+ f"The satisfy method expects a Fragment, but got {type(expr).__name__}."
412
+ )
413
+ if not expr._require:
414
+ raise ValueError("Fragment for satisfy must have a require clause.")
415
+ if expr._select or expr._define:
416
+ raise ValueError(
417
+ "Fragment for satisfy must not have select or define clauses."
418
+ )
419
+ if not check:
420
+ # Remove the `require` from the model roots so it is not checked as an integrity constraint
421
+ b._remove_roots([expr])
422
+ context = SymbolifyContext(self)
423
+
424
+ symbolic_where_clauses = context.rewrite_where(*expr._where)
425
+ definitions = []
426
+ for requirement in expr._require:
427
+ symbolic_requirement = context.rewrite(requirement)
428
+ if not isinstance(symbolic_requirement, Symbolic):
429
+ raise ValueError(
430
+ f"Cannot symbolify requirement {requirement} in satisfy. "
431
+ f"The requirement must contain solver variables or expressions."
432
+ )
433
+ constraint = self.Constraint.new(serialized=symbolic_requirement.expr)
434
+ definitions.append(constraint)
435
+ if name is not None:
436
+ definitions.append(constraint.name(make_name(name)))
437
+ b.define(*definitions).where(*symbolic_where_clauses)
438
+
439
+ # -------------------------------------------------------------------------
440
+ # Model Inspection and Display
441
+ # -------------------------------------------------------------------------
442
+
443
+ @staticmethod
444
+ def _print_dataframe(df: Any) -> None:
445
+ """Print dataframe with consistent formatting for long strings."""
446
+ for row in df.itertuples(index=False):
447
+ print(" ".join(str(val) for val in row))
448
+ print()
449
+
450
+ def summarize(self) -> None:
451
+ """Print counts of variables, objectives, and constraints in the model."""
452
+ counts_df = b.select(
453
+ *[(b.count(item) | 0) for (_, item) in self._model_info.items()]
454
+ ).to_df()
455
+ if counts_df.shape != (1, 4):
456
+ raise ValueError("Unexpected counts dataframe shape.")
457
+ num_vars, num_min_objs, num_max_objs, num_constraints = counts_df.iloc[0]
458
+ print(
459
+ f"Solver model has {num_vars} variables, {num_min_objs} minimization objectives, {num_max_objs} maximization objectives, and {num_constraints} constraints."
460
+ )
461
+
462
+ def print(self, with_names: bool = False) -> None:
463
+ """Print model components.
464
+
465
+ Args:
466
+ with_names: Whether to print expression string names (if available).
467
+ """
468
+ # Print variables
469
+ var_df = b.select(self.Variable.name | "_").where(self.Variable).to_df()
470
+ if var_df.empty:
471
+ print("No variables defined.")
472
+ return
473
+ print("Solver model:")
474
+ print()
475
+ print(f"Variables ({var_df.shape[0]}):")
476
+ self._print_dataframe(var_df)
477
+
478
+ # Print components
479
+ components = [
480
+ (self.MinObjective, "Min objectives"),
481
+ (self.MaxObjective, "Max objectives"),
482
+ (self.Constraint, "Constraints"),
483
+ ]
484
+ printed_expr_ref = b.String.ref()
485
+ for component_concept, component_label in components:
486
+ selection = (
487
+ [component_concept.name | "", printed_expr_ref]
488
+ if with_names
489
+ else [printed_expr_ref]
490
+ )
491
+ component_df = (
492
+ b.select(*selection)
493
+ .where(component_concept.printed_expr(printed_expr_ref))
494
+ .to_df()
495
+ )
496
+ if not component_df.empty:
497
+ print(f"{component_label} ({component_df.shape[0]}):")
498
+ self._print_dataframe(component_df)
499
+
500
+ # -------------------------------------------------------------------------
501
+ # Solving and Result Handling
502
+ # -------------------------------------------------------------------------
503
+
504
+ def _export_model_to_csv(
505
+ self,
506
+ model_id: str,
507
+ executor: RelExecutor,
508
+ prefix_lowercase: str,
509
+ query_timeout_mins: Optional[int] = None
510
+ ) -> None:
511
+ """Export model to CSV files in Snowflake stage.
512
+
513
+ Args:
514
+ model_id: Unique model identifier for stage paths.
515
+ executor: RelExecutor instance.
516
+ prefix_lowercase: Prefix for relation names.
517
+ query_timeout_mins: Query timeout in minutes.
518
+ """
519
+ stage_base_no_txn = f"snowflake://APP_STATE.RAI_INTERNAL_STAGE/SOLVERS/job_{model_id}"
520
+
521
+ # Export all model relations using Rel-native export_csv in a single transaction
522
+ # Transformations (uuid_string, encode_base64) are done inline in the export query
523
+ export_rel = textwrap.dedent(f"""
524
+ // Get transaction ID for folder naming - solver service validates ownership
525
+ // Use uuid_string to get proper UUID format, then replace hyphens with underscores
526
+ def txn_id_str {{string_replace[uuid_string[current_transaction_id], "-", "_"]}}
527
+
528
+ // Define base path with txn_id in folder name: model_{{txn_id}}/
529
+ def base_path {{"{stage_base_no_txn}/model"}}
530
+
531
+ // Export variable_hash.csv - single column: HASH (UUID string)
532
+ // Transformation: convert Variable UInt128 to UUID string inline
533
+ def variable_hash_data(:HASH, v, h):
534
+ {self.Variable._name}(v) and uuid_string(v, h)
535
+
536
+ def export[:variable_hash]: {{export_csv[{{
537
+ (:path, base_path ++ "/variable_hash_" ++ txn_id_str ++ ".csv");
538
+ (:data, variable_hash_data);
539
+ (:compression, "gzip")
540
+ }}]}}
541
+
542
+ // Export variable_name.csv - columns: HASH (UUID string), VALUE (name string)
543
+ // Transformation: convert Variable UInt128 to UUID string inline
544
+ def variable_name_data(:HASH, v, h):
545
+ {prefix_lowercase}variable_name(v, _) and uuid_string(v, h)
546
+ def variable_name_data(:VALUE, v, name):
547
+ {prefix_lowercase}variable_name(v, name)
548
+
549
+ def export[:variable_name]: {{export_csv[{{
550
+ (:path, base_path ++ "/variable_name_" ++ txn_id_str ++ ".csv");
551
+ (:data, variable_name_data);
552
+ (:compression, "gzip")
553
+ }}]}}
554
+
555
+ // Export constraint.csv - single column: VALUE (base64 encoded constraint)
556
+ // Transformation: encode_base64 done inline
557
+ def constraint_data(:VALUE, c, e):
558
+ exists((s) |
559
+ {self.Constraint._name}(c) and
560
+ {prefix_lowercase}constraint_serialized(c, s) and
561
+ encode_base64(s, e))
562
+
563
+ def export[:constraint]: {{export_csv[{{
564
+ (:path, base_path ++ "/constraint_" ++ txn_id_str ++ ".csv");
565
+ (:data, constraint_data);
566
+ (:compression, "gzip")
567
+ }}]}}
568
+
569
+ // Export min_objective.csv - columns: HASH (UUID string), VALUE (base64 encoded)
570
+ // Transformations: uuid_string and encode_base64 done inline
571
+ def min_objective_data(:HASH, obj, h):
572
+ {self.MinObjective._name}(obj) and uuid_string(obj, h)
573
+ def min_objective_data(:VALUE, obj, e):
574
+ exists((s) |
575
+ {self.MinObjective._name}(obj) and
576
+ {prefix_lowercase}minobjective_serialized(obj, s) and
577
+ encode_base64(s, e))
578
+
579
+ def export[:min_objective]: {{export_csv[{{
580
+ (:path, base_path ++ "/min_objective_" ++ txn_id_str ++ ".csv");
581
+ (:data, min_objective_data);
582
+ (:compression, "gzip")
583
+ }}]}}
584
+
585
+ // Export max_objective.csv - columns: HASH (UUID string), VALUE (base64 encoded)
586
+ // Transformations: uuid_string and encode_base64 done inline
587
+ def max_objective_data(:HASH, obj, h):
588
+ {self.MaxObjective._name}(obj) and uuid_string(obj, h)
589
+ def max_objective_data(:VALUE, obj, e):
590
+ exists((s) |
591
+ {self.MaxObjective._name}(obj) and
592
+ {prefix_lowercase}maxobjective_serialized(obj, s) and
593
+ encode_base64(s, e))
594
+
595
+ def export[:max_objective]: {{export_csv[{{
596
+ (:path, base_path ++ "/max_objective_" ++ txn_id_str ++ ".csv");
597
+ (:data, max_objective_data);
598
+ (:compression, "gzip")
599
+ }}]}}
600
+ """)
601
+
602
+ executor.execute_raw(export_rel, readonly=False, query_timeout_mins=query_timeout_mins)
603
+
604
+ def _import_solver_results_from_csv(
605
+ self,
606
+ model_id: str,
607
+ executor: RelExecutor,
608
+ prefix_lowercase: str,
609
+ query_timeout_mins: Optional[int] = None
610
+ ) -> None:
611
+ """Import solver results from CSV files in Snowflake stage.
612
+
613
+ Loads and extracts CSV files in a single transaction to minimize overhead.
614
+
615
+ Args:
616
+ model_id: Unique model identifier for stage paths.
617
+ executor: RelExecutor instance.
618
+ prefix_lowercase: Prefix for relation names.
619
+ query_timeout_mins: Query timeout in minutes.
620
+ """
621
+ result_stage_base = f"snowflake://APP_STATE.RAI_INTERNAL_STAGE/SOLVERS/job_{model_id}/results"
622
+ value_parse_fn = "parse_int" if self._num_type == "int" else "parse_float"
623
+
624
+ # Single transaction: Load CSV files and extract/map results
625
+ # Use inline definitions to avoid needing declared relations
626
+ load_and_extract_rel = textwrap.dedent(f"""
627
+ // Define CSV loading inline (no declare needed)
628
+ // Load ancillary.csv - contains solver metadata (NAME, VALUE columns)
629
+ def ancillary_config[:path]: "{result_stage_base}/ancillary.csv.gz"
630
+ def ancillary_config[:syntax, :header_row]: 1
631
+ def ancillary_config[:schema, :NAME]: "string"
632
+ def ancillary_config[:schema, :VALUE]: "string"
633
+ def {prefix_lowercase}solver_ancillary_raw {{load_csv[ancillary_config]}}
634
+
635
+ // Load objective_values.csv - contains objective values (SOL_INDEX, VALUE columns)
636
+ def objective_values_config[:path]: "{result_stage_base}/objective_values.csv.gz"
637
+ def objective_values_config[:syntax, :header_row]: 1
638
+ def objective_values_config[:schema, :SOL_INDEX]: "string"
639
+ def objective_values_config[:schema, :VALUE]: "string"
640
+ def {prefix_lowercase}solver_objective_values_raw {{load_csv[objective_values_config]}}
641
+
642
+ // Load points.csv.gz - contains solution points (SOL_INDEX, VAR_HASH, VALUE columns)
643
+ def points_config[:path]: "{result_stage_base}/points.csv.gz"
644
+ def points_config[:syntax, :header_row]: 1
645
+ def points_config[:schema, :SOL_INDEX]: "string"
646
+ def points_config[:schema, :VAR_HASH]: "string"
647
+ def points_config[:schema, :VALUE]: "string"
648
+ def {prefix_lowercase}solver_points_raw {{load_csv[points_config]}}
649
+
650
+ // Clear existing result data
651
+ def delete[:{self.result_info._name}]: {self.result_info._name}
652
+ def delete[:{self.point._name}]: {self.point._name}
653
+ def delete[:{self.points._name}]: {self.points._name}
654
+
655
+ // Extract ancillary data (result info) - NAME and VALUE columns
656
+ def insert(:{self.result_info._name}, key, val): {{
657
+ exists((row) |
658
+ {prefix_lowercase}solver_ancillary_raw(:NAME, row, key) and
659
+ {prefix_lowercase}solver_ancillary_raw(:VALUE, row, val))
660
+ }}
661
+
662
+ // Extract objective value from objective_values CSV (first solution)
663
+ def insert(:{self.result_info._name}, "objective_value", val): {{
664
+ exists((row) |
665
+ {prefix_lowercase}solver_objective_values_raw(:SOL_INDEX, row, "1") and
666
+ {prefix_lowercase}solver_objective_values_raw(:VALUE, row, val))
667
+ }}
668
+
669
+ // Extract solution points from points.csv.gz into points property
670
+ // This file has SOL_INDEX, VAR_HASH, VALUE columns
671
+ // Convert CSV string index to Int128 for points property signature
672
+ // Convert value to Int128 (for int) or Float64 (for float)
673
+ def insert(:{self.points._name}, sol_idx_int128, var, val_converted): {{
674
+ exists((row, sol_idx_str, var_hash_str, val_str, sol_idx_int, val) |
675
+ {prefix_lowercase}solver_points_raw(:SOL_INDEX, row, sol_idx_str) and
676
+ {prefix_lowercase}solver_points_raw(:VAR_HASH, row, var_hash_str) and
677
+ {prefix_lowercase}solver_points_raw(:VALUE, row, val_str) and
678
+ parse_int(sol_idx_str, sol_idx_int) and
679
+ parse_uuid(var_hash_str, var) and
680
+ {value_parse_fn}(val_str, val) and
681
+ ::std::mirror::convert(std::mirror::typeof[Int128], sol_idx_int, sol_idx_int128) and
682
+ {'::std::mirror::convert(std::mirror::typeof[Int128], val, val_converted)' if self._num_type == 'int' else '::std::mirror::convert(std::mirror::typeof[Float64], val, val_converted)'})
683
+ }}
684
+
685
+ // Extract first solution into point property (default solution)
686
+ // Filter to SOL_INDEX = 1
687
+ def insert(:{self.point._name}, var, val_converted): {{
688
+ exists((row, var_hash_str, val_str, val) |
689
+ {prefix_lowercase}solver_points_raw(:SOL_INDEX, row, "1") and
690
+ {prefix_lowercase}solver_points_raw(:VAR_HASH, row, var_hash_str) and
691
+ {prefix_lowercase}solver_points_raw(:VALUE, row, val_str) and
692
+ parse_uuid(var_hash_str, var) and
693
+ {value_parse_fn}(val_str, val) and
694
+ {'::std::mirror::convert(std::mirror::typeof[Int128], val, val_converted)' if self._num_type == 'int' else '::std::mirror::convert(std::mirror::typeof[Float64], val, val_converted)'})
695
+ }}
696
+ """)
697
+
698
+ executor.execute_raw(load_and_extract_rel, readonly=False, query_timeout_mins=query_timeout_mins)
699
+
700
+ def _export_model_to_protobuf(
701
+ self,
702
+ model_uri: str,
703
+ executor: RelExecutor,
704
+ prefix_lowercase: str,
705
+ query_timeout_mins: Optional[int] = None
706
+ ) -> None:
707
+ """Export model to protobuf format in Snowflake stage.
708
+
709
+ Args:
710
+ model_uri: Snowflake URI for the protobuf file.
711
+ executor: RelExecutor instance.
712
+ prefix_lowercase: Prefix for relation names.
713
+ query_timeout_mins: Query timeout in minutes.
714
+ """
715
+ export_rel = f"""
716
+ // Collect all model components into a relation for serialization
717
+ def model_relation {{
718
+ (:variable, {self.Variable._name});
719
+ (:variable_name, {prefix_lowercase}variable_name);
720
+ (:min_objective, {prefix_lowercase}minobjective_serialized);
721
+ (:max_objective, {prefix_lowercase}maxobjective_serialized);
722
+ (:constraint, {prefix_lowercase}constraint_serialized);
723
+ }}
724
+
725
+ @no_diagnostics(:EXPERIMENTAL)
726
+ def model_string {{ rel_primitive_solverlib_model_string[model_relation] }}
727
+
728
+ ic model_not_empty("Solver model is empty.") requires not empty(model_string)
729
+
730
+ def config[:envelope, :content_type]: "application/octet-stream"
731
+ def config[:envelope, :payload, :data]: model_string
732
+ def config[:envelope, :payload, :path]: "{model_uri}"
733
+ def export {{ config }}
734
+ """
735
+ executor.execute_raw(
736
+ textwrap.dedent(export_rel),
737
+ query_timeout_mins=query_timeout_mins
738
+ )
739
+
740
+ def _import_solver_results_from_protobuf(
741
+ self,
742
+ job_id: str,
743
+ executor: RelExecutor,
744
+ query_timeout_mins: Optional[int] = None
745
+ ) -> None:
746
+ """Import solver results from protobuf format.
747
+
748
+ Args:
749
+ job_id: Job identifier for result location.
750
+ executor: RelExecutor instance.
751
+ query_timeout_mins: Query timeout in minutes.
752
+ """
753
+ extract_rel = f"""
754
+ def raw_result {{
755
+ load_binary["snowflake://APP_STATE.RAI_INTERNAL_STAGE/job-results/{job_id}/result.binpb"]
756
+ }}
757
+
758
+ ic result_not_empty("Solver result is empty.") requires not empty(raw_result)
759
+
760
+ @no_diagnostics(:EXPERIMENTAL)
761
+ def extracted {{ rel_primitive_solverlib_extract[raw_result] }}
762
+
763
+ def delete[:{self.result_info._name}]: {self.result_info._name}
764
+ def delete[:{self.point._name}]: {self.point._name}
765
+ def delete[:{self.points._name}]: {self.points._name}
766
+
767
+ def insert(:{self.result_info._name}, key, value):
768
+ exists((original_key) | string(extracted[original_key], value) and ::std::mirror::lower(original_key, key))
769
+ """
770
+
771
+ if self._num_type == "int":
772
+ insert_points_relation = f"""
773
+ def insert(:{self.point._name}, variable, value):
774
+ exists((float_value) | extracted(:point, variable, float_value) and
775
+ ::std::mirror::convert(std::mirror::typeof[Int128], float_value, value)
776
+ )
777
+ def insert(:{self.points._name}, point_index, variable, value):
778
+ exists((float_index, float_value) | extracted(:points, variable, float_index, float_value) and
779
+ ::std::mirror::convert(std::mirror::typeof[Int128], float_index, point_index) and
780
+ ::std::mirror::convert(std::mirror::typeof[Int128], float_value, value)
781
+ )
782
+ """
783
+ else:
784
+ insert_points_relation = f"""
785
+ def insert(:{self.point._name}, variable, value): extracted(:point, variable, value)
786
+ def insert(:{self.points._name}, point_index, variable, value):
787
+ exists((float_index) | extracted(:points, variable, float_index, value) and
788
+ ::std::mirror::convert(std::mirror::typeof[Int128], float_index, point_index)
789
+ )
790
+ """
791
+
792
+ executor.execute_raw(
793
+ textwrap.dedent(extract_rel) + textwrap.dedent(insert_points_relation),
794
+ readonly=False,
795
+ query_timeout_mins=query_timeout_mins
796
+ )
797
+
798
+ def solve(
799
+ self, solver: Solver, log_to_console: bool = False, **kwargs: Any
800
+ ) -> None:
801
+ """Solve the model.
802
+
803
+ Args:
804
+ solver: Solver instance.
805
+ log_to_console: Whether to show solver output.
806
+ **kwargs: Solver options and parameters.
807
+ """
808
+
809
+ use_csv_store = solver.engine_settings.get("store", {})\
810
+ .get("csv", {})\
811
+ .get("enabled", False)
812
+
813
+ print(f"Using {'csv' if use_csv_store else 'protobuf'} store...")
814
+
815
+ options = {**kwargs, "version": 1}
816
+
817
+ # Validate solver options
818
+ for option_key, option_value in options.items():
819
+ if not isinstance(option_key, str):
820
+ raise TypeError(
821
+ f"Solver option keys must be strings, but got {type(option_key).__name__} for key {option_key!r}."
822
+ )
823
+ if not isinstance(option_value, (int, float, str, bool)):
824
+ raise TypeError(
825
+ f"Solver option values must be int, float, str, or bool, "
826
+ f"but got {type(option_value).__name__} for option {option_key!r}."
827
+ )
828
+
829
+ executor = self._model._to_executor()
830
+ if not isinstance(executor, RelExecutor):
831
+ raise ValueError(f"Expected RelExecutor, got {type(executor).__name__}.")
832
+ prefix_lowercase = f"solvermodel_{self._id}_"
833
+
834
+ # Initialize timeout from config
835
+ query_timeout_mins = kwargs.get("query_timeout_mins", None)
836
+ config = self._model._config
837
+ if (
838
+ query_timeout_mins is None
839
+ and (
840
+ timeout_value := config.get(
841
+ "query_timeout_mins", DEFAULT_QUERY_TIMEOUT_MINS
842
+ )
843
+ )
844
+ is not None
845
+ ):
846
+ query_timeout_mins = int(timeout_value)
847
+ config_file_path = getattr(config, "file_path", None)
848
+ start_time = time.monotonic()
849
+
850
+ # Force evaluation of Variable concept before export
851
+ b.select(b.count(self.Variable)).to_df()
852
+
853
+ # Prepare payload for solver service
854
+ payload: dict[str, Any] = {"solver": solver.solver_name.lower(), "options": options}
855
+
856
+ if use_csv_store:
857
+ # CSV format: model and results are exchanged via CSV files
858
+ model_id = str(uuid.uuid4()).upper().replace('-', '_')
859
+ payload["model_uri"] = f"snowflake://SOLVERS/job_{model_id}/model"
860
+
861
+ print("Exporting model to CSV...")
862
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
863
+ start_time, query_timeout_mins, config_file_path=config_file_path
864
+ )
865
+ self._export_model_to_csv(model_id, executor, prefix_lowercase, remaining_timeout_minutes)
866
+ print("Model CSV export completed")
867
+
868
+ print("Execute solver job")
869
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
870
+ start_time, query_timeout_mins, config_file_path=config_file_path
871
+ )
872
+ solver._exec_job(payload, log_to_console=log_to_console, query_timeout_mins=remaining_timeout_minutes)
873
+
874
+ print("Loading and extracting solver results...")
875
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
876
+ start_time, query_timeout_mins, config_file_path=config_file_path
877
+ )
878
+ self._import_solver_results_from_csv(model_id, executor, prefix_lowercase, remaining_timeout_minutes)
879
+
880
+ else: # protobuf format
881
+ # Protobuf format: model and results are exchanged via binary protobuf
882
+ input_id = uuid.uuid4()
883
+ model_uri = f"snowflake://APP_STATE.RAI_INTERNAL_STAGE/job-inputs/solver/{input_id}/model.binpb"
884
+ sf_input_uri = f"snowflake://job-inputs/solver/{input_id}/model.binpb"
885
+ payload["model_uri"] = sf_input_uri
886
+
887
+ print("Export model...")
888
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
889
+ start_time, query_timeout_mins, config_file_path=config_file_path
890
+ )
891
+ self._export_model_to_protobuf(model_uri, executor, prefix_lowercase, remaining_timeout_minutes)
892
+
893
+ print("Execute solver job...")
894
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
895
+ start_time, query_timeout_mins, config_file_path=config_file_path
896
+ )
897
+ job_id = solver._exec_job(payload, log_to_console=log_to_console, query_timeout_mins=remaining_timeout_minutes)
898
+
899
+ print("Extract result...")
900
+ remaining_timeout_minutes = calc_remaining_timeout_minutes(
901
+ start_time, query_timeout_mins, config_file_path=config_file_path
902
+ )
903
+ self._import_solver_results_from_protobuf(job_id, executor, remaining_timeout_minutes)
904
+
905
+ print("Finished solve")
906
+ print()
907
+ return None
908
+
909
+ def load_point(self, point_index: int) -> None:
910
+ """Load a solution point.
911
+
912
+ Args:
913
+ point_index: Solution point index (0-based).
914
+ """
915
+ if not isinstance(point_index, int):
916
+ raise TypeError(
917
+ f"Point index must be an integer, but got {type(point_index).__name__}."
918
+ )
919
+ if point_index < 0:
920
+ raise ValueError(
921
+ f"Point index must be non-negative, but got {point_index}."
922
+ )
923
+ executor = self._model._to_executor()
924
+ if not isinstance(executor, RelExecutor):
925
+ raise ValueError(
926
+ f"Expected RelExecutor, but got {type(executor).__name__}."
927
+ )
928
+ load_point_relation = f"""
929
+ def delete[:{self.point._name}]: {self.point._name}
930
+ def insert(:{self.point._name}, variable, value): {self.points._name}(int128[{point_index}], variable, value)
931
+ """
932
+ executor.execute_raw(textwrap.dedent(load_point_relation), readonly=False)
933
+
934
+ def summarize_result(self) -> Any:
935
+ """Print solver result summary.
936
+
937
+ Returns:
938
+ DataFrame with result information.
939
+ """
940
+ info_keys_to_retrieve = [
941
+ "error",
942
+ "termination_status",
943
+ "solve_time_sec",
944
+ "objective_value",
945
+ "solver_version",
946
+ "result_count",
947
+ ]
948
+ key, value_ref = b.String.ref(), b.String.ref()
949
+ result_df = (
950
+ b.select(key, value_ref)
951
+ .where(self.result_info(key, value_ref), key.in_(info_keys_to_retrieve))
952
+ .to_df()
953
+ )
954
+ if result_df.empty:
955
+ raise ValueError(
956
+ "No result information is available. Has the model been solved?"
957
+ )
958
+ print("Solver result:")
959
+ print(result_df.to_string(index=False, header=False))
960
+ print()
961
+ return result_df
962
+
963
+ def variable_values(self, multiple: bool = False) -> b.Fragment:
964
+ """Retrieve variable values.
965
+
966
+ Args:
967
+ multiple: Whether to return all solution points.
968
+
969
+ Returns:
970
+ Fragment for selecting values.
971
+ """
972
+ variable_ref = self.Variable.ref()
973
+ value_ref = (b.Integer if self._num_type == "int" else b.Float).ref()
974
+ if multiple:
975
+ point_index = b.Integer.ref()
976
+ return b.select(point_index, variable_ref.name, value_ref).where(
977
+ self.points(point_index, variable_ref, value_ref)
978
+ )
979
+ return b.select(variable_ref.name, value_ref).where(
980
+ self.point(variable_ref, value_ref)
981
+ )
982
+
983
+ # Valid result info keys that can be accessed as attributes
984
+ _RESULT_INFO_KEYS = frozenset(
985
+ [
986
+ "error",
987
+ "termination_status",
988
+ "solver_version",
989
+ "printed_model",
990
+ "solve_time_sec",
991
+ "objective_value",
992
+ "result_count",
993
+ ]
994
+ )
995
+
996
+ def __getattr__(self, name: str) -> Any:
997
+ """Get result attribute (e.g., num_variables, termination_status, objective_value).
998
+
999
+ Args:
1000
+ name: Attribute name.
1001
+
1002
+ Returns:
1003
+ Attribute value or None.
1004
+ """
1005
+ # Try to get dataframe from model info or result info
1006
+ if name in self._model_info:
1007
+ result_df = b.select(b.count(self._model_info[name]) | 0).to_df()
1008
+ elif name in self._RESULT_INFO_KEYS:
1009
+ value_ref = b.String.ref()
1010
+ result_df = (
1011
+ b.select(value_ref).where(self.result_info(name, value_ref)).to_df()
1012
+ )
1013
+ else:
1014
+ return None
1015
+
1016
+ # Extract and convert scalar value
1017
+ if result_df.shape != (1, 1):
1018
+ raise ValueError(
1019
+ f"Expected exactly one value for attribute '{name}', "
1020
+ f"but got dataframe with shape {result_df.shape}."
1021
+ )
1022
+
1023
+ result_value = result_df.iloc[0, 0]
1024
+ if not isinstance(result_value, str):
1025
+ return result_value
1026
+
1027
+ # Convert string results to appropriate types
1028
+ if name == "solve_time_sec":
1029
+ return float(result_value)
1030
+ if name == "objective_value":
1031
+ return int(result_value) if self._num_type == "int" else float(result_value)
1032
+ if name == "result_count":
1033
+ return int(result_value)
1034
+ return result_value
1035
+
1036
+
1037
+ # =============================================================================
1038
+ # Symbolic Expression Classes
1039
+ # =============================================================================
1040
+
1041
+
1042
+ class Symbolic:
1043
+ """Wrapper for symbolified solver expressions."""
1044
+
1045
+ def __init__(self, expr: Any) -> None:
1046
+ if isinstance(expr, Symbolic):
1047
+ raise TypeError("Cannot wrap a Symbolic expression in another Symbolic.")
1048
+ self.expr = expr
1049
+
1050
+
1051
+ class SymbolifyContext:
1052
+ """Context for rewriting expressions into solver-compatible symbolic form."""
1053
+
1054
+ def __init__(self, solver_model: SolverModelPB) -> None:
1055
+ self.model = solver_model._model
1056
+ self.solver_model = solver_model
1057
+ # Maps original variables (or refs) to symbolic variables bound in where clauses
1058
+ self.variable_map: dict[Any, Any] = {}
1059
+
1060
+ # -------------------------------------------------------------------------
1061
+ # Public Rewriting Methods
1062
+ # -------------------------------------------------------------------------
1063
+
1064
+ def rewrite_where(self, *exprs: Any) -> list[Any]:
1065
+ """Rewrite where clause expressions.
1066
+
1067
+ Args:
1068
+ *exprs: Where clause expressions.
1069
+
1070
+ Returns:
1071
+ Rewritten expressions.
1072
+ """
1073
+ rewritten_expressions: list[Any] = []
1074
+ # Two-pass strategy: first handle variable relationships to populate variable_map,
1075
+ # then rewrite other expressions that may reference those variables
1076
+ # First pass: identify and handle variable relationship expressions
1077
+ for expression in exprs:
1078
+ if (
1079
+ isinstance(expression, b.Expression)
1080
+ and isinstance(expression._op, b.Relationship)
1081
+ and expression._op in self.solver_model._variable_relationships
1082
+ ):
1083
+ rewritten_expressions.append(
1084
+ self._handle_variable_relationship(expression)
1085
+ )
1086
+ else:
1087
+ rewritten_expressions.append(None)
1088
+ # Second pass: rewrite remaining non-variable expressions
1089
+ for i, expr in enumerate(exprs):
1090
+ if rewritten_expressions[i] is None:
1091
+ rewritten_expressions[i] = (
1092
+ expr
1093
+ if expr in self.variable_map
1094
+ else self._rewrite_nonsymbolic(expr)
1095
+ )
1096
+ return rewritten_expressions
1097
+
1098
+ def rewrite(self, expr: Any) -> Optional[Symbolic | Any]:
1099
+ """Rewrite expressions to symbolify solver variables."""
1100
+ if expr is None:
1101
+ return None
1102
+
1103
+ elif isinstance(expr, (int, float, str)):
1104
+ return None
1105
+
1106
+ elif isinstance(expr, b.ConceptFilter):
1107
+ concept = expr._op
1108
+ assert isinstance(concept, b.Concept)
1109
+ (ident, kwargs) = expr._params
1110
+ assert ident is None
1111
+ assert isinstance(kwargs, dict)
1112
+ new_kwargs = {}
1113
+ values_were_rewritten = False
1114
+ for key, value in kwargs.items():
1115
+ rewritten_value = self.rewrite(value)
1116
+ if isinstance(rewritten_value, Symbolic):
1117
+ raise ValueError(
1118
+ f"Cannot symbolify ConceptFilter argument {key} with symbolic value."
1119
+ )
1120
+ if rewritten_value is not None:
1121
+ values_were_rewritten = True
1122
+ new_kwargs[key] = rewritten_value
1123
+ else:
1124
+ new_kwargs[key] = value
1125
+ if values_were_rewritten:
1126
+ return b.ConceptFilter(concept, ident, new_kwargs)
1127
+ return None
1128
+
1129
+ elif isinstance(expr, (b.DataColumn, b.TypeRef, b.Concept)):
1130
+ return None
1131
+
1132
+ elif isinstance(expr, b.Alias):
1133
+ return self.rewrite(expr._thing)
1134
+
1135
+ elif isinstance(expr, b.Ref):
1136
+ if expr in self.variable_map:
1137
+ return Symbolic(self.variable_map[expr])
1138
+ thing = self.rewrite(expr._thing)
1139
+ if thing is not None:
1140
+ raise ValueError(
1141
+ f"Internal error. Ref._thing rewrite unexpectedly returned {thing}."
1142
+ )
1143
+ return None
1144
+
1145
+ elif isinstance(expr, b.Relationship):
1146
+ if expr in self.variable_map:
1147
+ return Symbolic(self.variable_map[expr])
1148
+ variable_result = self._get_variable_ref(expr, expr._parent)
1149
+ if variable_result is not None:
1150
+ self.variable_map[expr] = variable_result
1151
+ return Symbolic(variable_result)
1152
+ return None
1153
+
1154
+ elif isinstance(expr, b.RelationshipRef):
1155
+ if expr in self.variable_map:
1156
+ return Symbolic(self.variable_map[expr])
1157
+ relationship = expr._relationship
1158
+ if isinstance(relationship, b.Relationship):
1159
+ variable_result = self._get_variable_ref(relationship, expr._parent)
1160
+ if variable_result is not None:
1161
+ self.variable_map[expr] = variable_result
1162
+ return Symbolic(variable_result)
1163
+ rewritten_parent = self.rewrite(expr._parent)
1164
+ if isinstance(rewritten_parent, Symbolic):
1165
+ raise ValueError(
1166
+ "Internal error. RelationshipRef parent rewrite returned Symbolic."
1167
+ )
1168
+ if rewritten_parent is not None:
1169
+ return b.RelationshipRef(rewritten_parent, relationship)
1170
+ return None
1171
+
1172
+ elif isinstance(expr, b.RelationshipFieldRef):
1173
+ relationship = expr._relationship
1174
+ if not isinstance(relationship, b.Relationship):
1175
+ # TODO(coey): Handle relationship:RelationshipReading
1176
+ return None
1177
+
1178
+ # Rewrite the relationship reference
1179
+ relationship_expression = (
1180
+ relationship
1181
+ if expr._parent is None
1182
+ else b.RelationshipRef(expr._parent, relationship)
1183
+ )
1184
+ variable_result = self.rewrite(relationship_expression)
1185
+ if variable_result is None:
1186
+ return None
1187
+
1188
+ # Handle symbolic result - return as-is if it's the last field
1189
+ if isinstance(variable_result, Symbolic):
1190
+ if expr._field_ix == len(relationship._fields) - 1:
1191
+ return variable_result
1192
+ variable_result = variable_result.expr
1193
+
1194
+ return getattr(variable_result, relationship._field_names[expr._field_ix])
1195
+
1196
+ elif isinstance(expr, b.Expression):
1197
+ operator = self.rewrite(expr._op)
1198
+ if isinstance(operator, Symbolic):
1199
+ raise ValueError(
1200
+ "Internal error: Expression operator rewrite returned Symbolic."
1201
+ )
1202
+ params_were_rewritten = False
1203
+ has_symbolic_params = False
1204
+ params = []
1205
+ for param in expr._params:
1206
+ rewritten_param = self.rewrite(param)
1207
+ if isinstance(rewritten_param, Symbolic):
1208
+ has_symbolic_params = True
1209
+ params_were_rewritten = True
1210
+ params.append(rewritten_param.expr)
1211
+ elif rewritten_param is not None:
1212
+ params_were_rewritten = True
1213
+ params.append(rewritten_param)
1214
+ else:
1215
+ params.append(param)
1216
+ if operator is not None:
1217
+ if has_symbolic_params:
1218
+ raise NotImplementedError(
1219
+ f"Solver rewrites cannot handle expression {expr} "
1220
+ f"with both a symbolic operator and symbolic parameters."
1221
+ )
1222
+ return b.Expression(operator, *params)
1223
+ if not has_symbolic_params:
1224
+ return b.Expression(expr._op, *params)
1225
+ if not params_were_rewritten:
1226
+ return None
1227
+
1228
+ # Some arguments involve solver variables, so rewrite into solver protobuf format
1229
+ # This converts operations like x + y into fo_appl(ADD_OP, (x, y), res)
1230
+ if not has_symbolic_params:
1231
+ raise ValueError(
1232
+ "Internal error. Expected symbolic parameters but none were found."
1233
+ )
1234
+ if not isinstance(expr._op, b.Relationship):
1235
+ raise NotImplementedError(
1236
+ f"Solver rewrites cannot handle expression {expr} "
1237
+ f"with operator type {type(expr._op).__name__}."
1238
+ )
1239
+ operator_name = expr._op._name
1240
+ if not isinstance(operator_name, str):
1241
+ raise ValueError(
1242
+ f"Internal error. Operator name is {type(operator_name).__name__}, expected str."
1243
+ )
1244
+ if operator_name in _FIRST_ORDER_OPERATOR_CODES:
1245
+ return Symbolic(
1246
+ _make_first_order_application(
1247
+ _FIRST_ORDER_OPERATOR_CODES[operator_name], *params
1248
+ )
1249
+ )
1250
+ elif operator_name in _FIRST_ORDER_COMPARISON_CODES:
1251
+ return Symbolic(
1252
+ _make_first_order_application_with_result(
1253
+ _FIRST_ORDER_COMPARISON_CODES[operator_name], *params
1254
+ )
1255
+ )
1256
+ else:
1257
+ raise NotImplementedError(
1258
+ f"Solver rewrites cannot handle operator '{operator_name}'."
1259
+ )
1260
+
1261
+ elif isinstance(expr, b.Aggregate):
1262
+ # Only the last argument can be symbolic
1263
+ preceding_args = [self._rewrite_nonsymbolic(arg) for arg in expr._args[:-1]]
1264
+ group = [self._rewrite_nonsymbolic(arg) for arg in expr._group]
1265
+ # TODO(coey): Should this be done with a subcontext (for variable_map)?
1266
+ where = self.rewrite_where(*expr._where._where)
1267
+ rewritten = (
1268
+ preceding_args != expr._args[:-1]
1269
+ or group != expr._group
1270
+ or where != expr._where
1271
+ )
1272
+ symbolic_arg = self.rewrite(expr._args[-1])
1273
+ if symbolic_arg is None and not rewritten:
1274
+ return None
1275
+ if not isinstance(symbolic_arg, Symbolic):
1276
+ if symbolic_arg is None:
1277
+ symbolic_arg = expr._args[-1]
1278
+ aggregate_expr = b.Aggregate(expr._op, *preceding_args, symbolic_arg)
1279
+ return aggregate_expr.per(*group).where(*where)
1280
+
1281
+ # The last argument is symbolic - convert to higher-order application
1282
+ # Example: sum(x for x in variables) becomes ho_appl(..., x, SUM_OP)
1283
+ operator_name = expr._op._name
1284
+ if not isinstance(operator_name, str):
1285
+ raise ValueError(
1286
+ f"Internal error. Aggregate operator name is {type(operator_name).__name__}, expected str."
1287
+ )
1288
+ if operator_name not in _HIGHER_ORDER_OPERATOR_CODES:
1289
+ raise NotImplementedError(
1290
+ f"Solver rewrites cannot handle aggregate operator '{operator_name}'. "
1291
+ f"Supported operators: {', '.join(_HIGHER_ORDER_OPERATOR_CODES.keys())}"
1292
+ )
1293
+ higher_order_application_builtin = b.Relationship.builtins[
1294
+ "rel_primitive_solverlib_ho_appl"
1295
+ ]
1296
+ aggregate_expr = b.Aggregate(
1297
+ higher_order_application_builtin,
1298
+ *preceding_args,
1299
+ symbolic_arg.expr,
1300
+ _HIGHER_ORDER_OPERATOR_CODES[operator_name],
1301
+ )
1302
+ return Symbolic(aggregate_expr.per(*group).where(*where))
1303
+
1304
+ elif isinstance(expr, b.Union):
1305
+ # Return union of the symbolified expressions, if any are symbolic
1306
+ args_were_rewritten = False
1307
+ has_symbolic_args = False
1308
+ args = []
1309
+ for union_arg in expr._args:
1310
+ rewritten_arg = self.rewrite(union_arg)
1311
+ if isinstance(rewritten_arg, Symbolic):
1312
+ has_symbolic_args = True
1313
+ args.append(rewritten_arg.expr)
1314
+ elif rewritten_arg is not None:
1315
+ args_were_rewritten = True
1316
+ args.append(rewritten_arg)
1317
+ else:
1318
+ args.append(union_arg)
1319
+ if has_symbolic_args:
1320
+ return Symbolic(b.union(*args))
1321
+ elif args_were_rewritten:
1322
+ return b.union(*args)
1323
+ return None
1324
+
1325
+ elif isinstance(expr, b.Fragment):
1326
+ # Only support selects with one item
1327
+ if expr._define or expr._require:
1328
+ raise ValueError(
1329
+ "Solver rewrites do not support fragments with define or require clauses."
1330
+ )
1331
+ if len(expr._select) != 1:
1332
+ raise ValueError(
1333
+ f"Solver rewrites require fragments with exactly one select item, "
1334
+ f"but got {len(expr._select)}."
1335
+ )
1336
+ # TODO(coey): Should this be done with a subcontext (for variable_map)?
1337
+ where = self.rewrite_where(*expr._where)
1338
+ symbolic_select = self.rewrite(expr._select[0])
1339
+ if isinstance(symbolic_select, Symbolic):
1340
+ return Symbolic(b.select(symbolic_select.expr).where(*where))
1341
+ elif symbolic_select is not None:
1342
+ return b.select(symbolic_select).where(*where)
1343
+ return None
1344
+
1345
+ raise NotImplementedError(
1346
+ f"Solver rewrites cannot handle {expr} of type {type(expr).__name__}."
1347
+ )
1348
+
1349
+ # -------------------------------------------------------------------------
1350
+ # Private Helper Methods
1351
+ # -------------------------------------------------------------------------
1352
+
1353
+ def _handle_variable_relationship(self, expr: b.Expression) -> Any:
1354
+ """Create symbolic reference for variable relationship expression."""
1355
+ relationship = expr._op
1356
+ if not isinstance(relationship, b.Relationship):
1357
+ raise TypeError(
1358
+ f"Expected Relationship in variable expression, but got {type(relationship).__name__}."
1359
+ )
1360
+ params = expr._params
1361
+ if len(params) != len(relationship._fields):
1362
+ raise ValueError(
1363
+ f"Parameter count mismatch: Got {len(params)} params "
1364
+ f"but relationship has {len(relationship._fields)} fields."
1365
+ )
1366
+ last_param = params[-1]
1367
+ if isinstance(last_param, b.Alias):
1368
+ last_param = last_param._thing
1369
+ if not isinstance(last_param, (b.Concept, b.Ref)):
1370
+ raise TypeError(
1371
+ f"Last parameter must be a Concept or Ref, but got {type(last_param).__name__}."
1372
+ )
1373
+ # Extract and rewrite field parameters to build the symbolic variable reference
1374
+ # This maps the relationship fields to their grounding values
1375
+ fields = {}
1376
+ for i in range(len(params) - 1):
1377
+ rewritten_param = self.rewrite(params[i])
1378
+ assert not isinstance(rewritten_param, Symbolic)
1379
+ fields[relationship._field_names[i]] = (
1380
+ rewritten_param if rewritten_param is not None else params[i]
1381
+ )
1382
+ # Create new ref corresponding to the decision variable
1383
+ variable_ref = self.solver_model._variable_relationships[relationship].ref()
1384
+ self.variable_map[last_param] = variable_ref
1385
+ # Return new condition to ground the variable
1386
+ return b.where(
1387
+ *[
1388
+ getattr(variable_ref, field_name) == field_value
1389
+ for field_name, field_value in fields.items()
1390
+ ]
1391
+ )
1392
+
1393
+ def _rewrite_nonsymbolic(self, expr: Any) -> Any:
1394
+ """Rewrite expression ensuring non-symbolic result."""
1395
+ new_expr = self.rewrite(expr)
1396
+ if isinstance(new_expr, Symbolic):
1397
+ raise ValueError(
1398
+ f"Internal error. Non-symbolic rewrite unexpectedly returned Symbolic for {expr}."
1399
+ )
1400
+ return expr if new_expr is None else new_expr
1401
+
1402
+ def _get_variable_ref(
1403
+ self, relationship: b.Relationship, parent_producer: b.Producer | None
1404
+ ) -> Optional[Any]:
1405
+ """Get variable reference for relationship, or None if not a solver variable."""
1406
+ # Check if this relationship corresponds to a decision variable
1407
+ VariableConcept = self.solver_model._variable_relationships.get(relationship)
1408
+ if VariableConcept is None:
1409
+ return None
1410
+
1411
+ properties = {}
1412
+ if parent_producer is not None:
1413
+ properties[relationship._field_names[0]] = parent_producer
1414
+ return VariableConcept(VariableConcept.ref(), **properties)