relationalai 0.13.0__py3-none-any.whl → 0.13.0.dev0__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 (836) hide show
  1. relationalai/__init__.py +1 -256
  2. relationalai/config/__init__.py +56 -0
  3. relationalai/config/config.py +289 -0
  4. relationalai/config/config_fields.py +86 -0
  5. relationalai/config/connections/__init__.py +46 -0
  6. relationalai/config/connections/base.py +23 -0
  7. relationalai/config/connections/duckdb.py +29 -0
  8. relationalai/config/connections/snowflake.py +243 -0
  9. relationalai/config/external/__init__.py +17 -0
  10. relationalai/config/external/dbt_converter.py +101 -0
  11. relationalai/config/external/dbt_models.py +93 -0
  12. relationalai/config/external/snowflake_converter.py +41 -0
  13. relationalai/config/external/snowflake_models.py +85 -0
  14. relationalai/config/external/utils.py +19 -0
  15. relationalai/semantics/__init__.py +146 -22
  16. relationalai/semantics/backends/lqp/annotations.py +11 -0
  17. relationalai/semantics/backends/sql/sql_compiler.py +327 -0
  18. relationalai/semantics/frontend/base.py +1707 -0
  19. relationalai/semantics/frontend/core.py +179 -0
  20. relationalai/semantics/frontend/front_compiler.py +1313 -0
  21. relationalai/semantics/frontend/pprint.py +408 -0
  22. relationalai/semantics/metamodel/__init__.py +6 -40
  23. relationalai/semantics/metamodel/builtins.py +205 -771
  24. relationalai/semantics/metamodel/metamodel.py +437 -0
  25. relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
  26. relationalai/semantics/metamodel/pprint.py +412 -0
  27. relationalai/semantics/metamodel/rewriter.py +266 -0
  28. relationalai/semantics/metamodel/typer.py +1378 -0
  29. relationalai/semantics/std/__init__.py +60 -40
  30. relationalai/semantics/std/aggregates.py +149 -0
  31. relationalai/semantics/std/common.py +44 -0
  32. relationalai/semantics/std/constraints.py +37 -43
  33. relationalai/semantics/std/datetime.py +246 -135
  34. relationalai/semantics/std/decimals.py +45 -52
  35. relationalai/semantics/std/floats.py +13 -5
  36. relationalai/semantics/std/integers.py +26 -11
  37. relationalai/semantics/std/math.py +183 -112
  38. relationalai/semantics/std/numbers.py +86 -0
  39. relationalai/semantics/std/re.py +80 -62
  40. relationalai/semantics/std/strings.py +117 -60
  41. relationalai/shims/executor.py +147 -0
  42. relationalai/shims/helpers.py +126 -0
  43. relationalai/shims/hoister.py +221 -0
  44. relationalai/shims/mm2v0.py +1290 -0
  45. relationalai/tools/cli/__init__.py +6 -0
  46. relationalai/tools/cli/cli.py +90 -0
  47. relationalai/tools/cli/components/__init__.py +5 -0
  48. relationalai/tools/cli/components/progress_reader.py +1524 -0
  49. relationalai/tools/cli/components/utils.py +58 -0
  50. relationalai/tools/cli/config_template.py +45 -0
  51. relationalai/tools/cli/dev.py +19 -0
  52. relationalai/tools/debugger.py +289 -183
  53. relationalai/tools/typer_debugger.py +93 -0
  54. relationalai/util/dataclasses.py +43 -0
  55. relationalai/util/docutils.py +40 -0
  56. relationalai/util/error.py +199 -0
  57. relationalai/util/format.py +48 -106
  58. relationalai/util/naming.py +145 -0
  59. relationalai/util/python.py +35 -0
  60. relationalai/util/runtime.py +156 -0
  61. relationalai/util/schema.py +197 -0
  62. relationalai/util/source.py +185 -0
  63. relationalai/util/structures.py +163 -0
  64. relationalai/util/tracing.py +261 -0
  65. relationalai-0.13.0.dev0.dist-info/METADATA +46 -0
  66. relationalai-0.13.0.dev0.dist-info/RECORD +488 -0
  67. relationalai-0.13.0.dev0.dist-info/WHEEL +5 -0
  68. relationalai-0.13.0.dev0.dist-info/entry_points.txt +3 -0
  69. relationalai-0.13.0.dev0.dist-info/top_level.txt +2 -0
  70. v0/relationalai/__init__.py +216 -0
  71. v0/relationalai/clients/__init__.py +5 -0
  72. v0/relationalai/clients/azure.py +477 -0
  73. v0/relationalai/clients/client.py +912 -0
  74. v0/relationalai/clients/config.py +673 -0
  75. v0/relationalai/clients/direct_access_client.py +118 -0
  76. v0/relationalai/clients/hash_util.py +31 -0
  77. v0/relationalai/clients/local.py +571 -0
  78. v0/relationalai/clients/profile_polling.py +73 -0
  79. v0/relationalai/clients/result_helpers.py +420 -0
  80. v0/relationalai/clients/snowflake.py +3869 -0
  81. v0/relationalai/clients/types.py +113 -0
  82. v0/relationalai/clients/use_index_poller.py +980 -0
  83. v0/relationalai/clients/util.py +356 -0
  84. v0/relationalai/debugging.py +389 -0
  85. v0/relationalai/dsl.py +1749 -0
  86. v0/relationalai/early_access/builder/__init__.py +30 -0
  87. v0/relationalai/early_access/builder/builder/__init__.py +35 -0
  88. v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
  89. v0/relationalai/early_access/builder/std/__init__.py +25 -0
  90. v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
  91. v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
  92. v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
  93. v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
  94. v0/relationalai/early_access/devtools/__init__.py +12 -0
  95. v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
  96. v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
  97. v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
  98. v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
  99. v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
  100. v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
  101. v0/relationalai/early_access/dsl/bindings/common.py +402 -0
  102. v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
  103. v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
  104. v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
  105. v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
  106. v0/relationalai/early_access/dsl/codegen/common.py +79 -0
  107. v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
  108. v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
  109. v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
  110. v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
  111. v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
  112. v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
  113. v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
  114. v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
  115. v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
  116. v0/relationalai/early_access/dsl/core/context.py +13 -0
  117. v0/relationalai/early_access/dsl/core/cset.py +132 -0
  118. v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
  119. v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
  120. v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
  121. v0/relationalai/early_access/dsl/core/instances.py +44 -0
  122. v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
  123. v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
  124. v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
  125. v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
  126. v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
  127. v0/relationalai/early_access/dsl/core/relations.py +276 -0
  128. v0/relationalai/early_access/dsl/core/rules.py +112 -0
  129. v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
  130. v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
  131. v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
  132. v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
  133. v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
  134. v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
  135. v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
  136. v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
  137. v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
  138. v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
  139. v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
  140. v0/relationalai/early_access/dsl/ir/executor.py +260 -0
  141. v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
  142. v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
  143. v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
  144. v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
  145. v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
  146. v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
  147. v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
  148. v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
  149. v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
  150. v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
  151. v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
  152. v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
  153. v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
  154. v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
  155. v0/relationalai/early_access/dsl/orm/models.py +256 -0
  156. v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
  157. v0/relationalai/early_access/dsl/orm/printer.py +469 -0
  158. v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
  159. v0/relationalai/early_access/dsl/orm/relations.py +19 -0
  160. v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
  161. v0/relationalai/early_access/dsl/orm/types.py +42 -0
  162. v0/relationalai/early_access/dsl/orm/utils.py +79 -0
  163. v0/relationalai/early_access/dsl/orm/verb.py +204 -0
  164. v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
  165. v0/relationalai/early_access/dsl/relations.py +170 -0
  166. v0/relationalai/early_access/dsl/rulesets.py +69 -0
  167. v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
  168. v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
  169. v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
  170. v0/relationalai/early_access/dsl/schemas/components.py +203 -0
  171. v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
  172. v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
  173. v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
  174. v0/relationalai/early_access/dsl/serialization.py +79 -0
  175. v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
  176. v0/relationalai/early_access/dsl/snow/api.py +104 -0
  177. v0/relationalai/early_access/dsl/snow/common.py +76 -0
  178. v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
  179. v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
  180. v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
  181. v0/relationalai/early_access/dsl/types/__init__.py +40 -0
  182. v0/relationalai/early_access/dsl/types/concepts.py +12 -0
  183. v0/relationalai/early_access/dsl/types/entities.py +135 -0
  184. v0/relationalai/early_access/dsl/types/values.py +17 -0
  185. v0/relationalai/early_access/dsl/utils.py +102 -0
  186. v0/relationalai/early_access/graphs/__init__.py +13 -0
  187. v0/relationalai/early_access/lqp/__init__.py +12 -0
  188. v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
  189. v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
  190. v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
  191. v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
  192. v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
  193. v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
  194. v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
  195. v0/relationalai/early_access/lqp/types/__init__.py +12 -0
  196. v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
  197. v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
  198. v0/relationalai/early_access/metamodel/__init__.py +58 -0
  199. v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
  200. v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
  201. v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
  202. v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
  203. v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
  204. v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
  205. v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
  206. v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
  207. v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
  208. v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
  209. v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
  210. v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
  211. v0/relationalai/early_access/rel/__init__.py +12 -0
  212. v0/relationalai/early_access/rel/executor/__init__.py +12 -0
  213. v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
  214. v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
  215. v0/relationalai/early_access/solvers/__init__.py +19 -0
  216. v0/relationalai/early_access/sql/__init__.py +11 -0
  217. v0/relationalai/early_access/sql/executor/__init__.py +3 -0
  218. v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
  219. v0/relationalai/early_access/tests/logging/__init__.py +12 -0
  220. v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
  221. v0/relationalai/early_access/tests/utils/__init__.py +12 -0
  222. v0/relationalai/environments/__init__.py +35 -0
  223. v0/relationalai/environments/base.py +381 -0
  224. v0/relationalai/environments/colab.py +14 -0
  225. v0/relationalai/environments/generic.py +71 -0
  226. v0/relationalai/environments/ipython.py +68 -0
  227. v0/relationalai/environments/jupyter.py +9 -0
  228. v0/relationalai/environments/snowbook.py +169 -0
  229. v0/relationalai/errors.py +2455 -0
  230. v0/relationalai/experimental/SF.py +38 -0
  231. v0/relationalai/experimental/inspect.py +47 -0
  232. v0/relationalai/experimental/pathfinder/__init__.py +158 -0
  233. v0/relationalai/experimental/pathfinder/api.py +160 -0
  234. v0/relationalai/experimental/pathfinder/automaton.py +584 -0
  235. v0/relationalai/experimental/pathfinder/bridge.py +226 -0
  236. v0/relationalai/experimental/pathfinder/compiler.py +416 -0
  237. v0/relationalai/experimental/pathfinder/datalog.py +214 -0
  238. v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
  239. v0/relationalai/experimental/pathfinder/filter.py +236 -0
  240. v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
  241. v0/relationalai/experimental/pathfinder/options.py +265 -0
  242. v0/relationalai/experimental/pathfinder/rpq.py +344 -0
  243. v0/relationalai/experimental/pathfinder/transition.py +200 -0
  244. v0/relationalai/experimental/pathfinder/utils.py +26 -0
  245. v0/relationalai/experimental/paths/api.py +143 -0
  246. v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
  247. v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
  248. v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
  249. v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
  250. v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
  251. v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
  252. v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
  253. v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
  254. v0/relationalai/experimental/paths/graph.py +185 -0
  255. v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
  256. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
  257. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
  258. v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
  259. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
  260. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
  261. v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
  262. v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
  263. v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
  264. v0/relationalai/experimental/paths/product_graph.py +93 -0
  265. v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
  266. v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
  267. v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
  268. v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
  269. v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
  270. v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
  271. v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
  272. v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
  273. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
  274. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
  275. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
  276. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
  277. v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
  278. v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
  279. v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
  280. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
  281. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
  282. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
  283. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
  284. v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
  285. v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
  286. v0/relationalai/experimental/paths/tree_agg.py +168 -0
  287. v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
  288. v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
  289. v0/relationalai/experimental/solvers.py +1087 -0
  290. v0/relationalai/loaders/__init__.py +0 -0
  291. v0/relationalai/loaders/csv.py +195 -0
  292. v0/relationalai/loaders/loader.py +177 -0
  293. v0/relationalai/loaders/types.py +23 -0
  294. v0/relationalai/rel_emitter.py +373 -0
  295. v0/relationalai/rel_utils.py +185 -0
  296. v0/relationalai/semantics/__init__.py +29 -0
  297. v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
  298. v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
  299. v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
  300. v0/relationalai/semantics/internal/internal.py +3785 -0
  301. v0/relationalai/semantics/internal/snowflake.py +324 -0
  302. v0/relationalai/semantics/lqp/builtins.py +16 -0
  303. v0/relationalai/semantics/lqp/compiler.py +22 -0
  304. v0/relationalai/semantics/lqp/constructors.py +68 -0
  305. v0/relationalai/semantics/lqp/executor.py +469 -0
  306. v0/relationalai/semantics/lqp/intrinsics.py +24 -0
  307. v0/relationalai/semantics/lqp/model2lqp.py +839 -0
  308. v0/relationalai/semantics/lqp/passes.py +680 -0
  309. v0/relationalai/semantics/lqp/primitives.py +252 -0
  310. v0/relationalai/semantics/lqp/result_helpers.py +202 -0
  311. v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
  312. v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
  313. v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
  314. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +449 -0
  315. v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
  316. v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
  317. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
  318. v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
  319. v0/relationalai/semantics/lqp/types.py +101 -0
  320. v0/relationalai/semantics/lqp/utils.py +160 -0
  321. v0/relationalai/semantics/lqp/validators.py +57 -0
  322. v0/relationalai/semantics/metamodel/__init__.py +40 -0
  323. v0/relationalai/semantics/metamodel/builtins.py +774 -0
  324. v0/relationalai/semantics/metamodel/compiler.py +133 -0
  325. v0/relationalai/semantics/metamodel/dependency.py +862 -0
  326. v0/relationalai/semantics/metamodel/executor.py +61 -0
  327. v0/relationalai/semantics/metamodel/factory.py +287 -0
  328. v0/relationalai/semantics/metamodel/helpers.py +361 -0
  329. v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
  330. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
  331. v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
  332. v0/relationalai/semantics/metamodel/rewrite/flatten.py +549 -0
  333. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
  334. v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
  335. v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
  336. v0/relationalai/semantics/metamodel/util.py +505 -0
  337. v0/relationalai/semantics/reasoners/__init__.py +10 -0
  338. v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
  339. v0/relationalai/semantics/reasoners/graph/core.py +9020 -0
  340. v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
  341. v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
  342. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
  343. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
  344. v0/relationalai/semantics/rel/builtins.py +40 -0
  345. v0/relationalai/semantics/rel/compiler.py +989 -0
  346. v0/relationalai/semantics/rel/executor.py +359 -0
  347. v0/relationalai/semantics/rel/rel.py +482 -0
  348. v0/relationalai/semantics/rel/rel_utils.py +276 -0
  349. v0/relationalai/semantics/snowflake/__init__.py +3 -0
  350. v0/relationalai/semantics/sql/compiler.py +2503 -0
  351. v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
  352. v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
  353. v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
  354. v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
  355. v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
  356. v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
  357. v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
  358. v0/relationalai/semantics/sql/sql.py +504 -0
  359. v0/relationalai/semantics/std/__init__.py +54 -0
  360. v0/relationalai/semantics/std/constraints.py +43 -0
  361. v0/relationalai/semantics/std/datetime.py +363 -0
  362. v0/relationalai/semantics/std/decimals.py +62 -0
  363. v0/relationalai/semantics/std/floats.py +7 -0
  364. v0/relationalai/semantics/std/integers.py +22 -0
  365. v0/relationalai/semantics/std/math.py +141 -0
  366. v0/relationalai/semantics/std/pragmas.py +11 -0
  367. v0/relationalai/semantics/std/re.py +83 -0
  368. v0/relationalai/semantics/std/std.py +14 -0
  369. v0/relationalai/semantics/std/strings.py +63 -0
  370. v0/relationalai/semantics/tests/__init__.py +0 -0
  371. v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
  372. v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
  373. v0/relationalai/semantics/tests/utils.py +46 -0
  374. v0/relationalai/std/__init__.py +70 -0
  375. v0/relationalai/tools/__init__.py +0 -0
  376. v0/relationalai/tools/cli.py +1940 -0
  377. v0/relationalai/tools/cli_controls.py +1826 -0
  378. v0/relationalai/tools/cli_helpers.py +390 -0
  379. v0/relationalai/tools/debugger.py +183 -0
  380. v0/relationalai/tools/debugger_client.py +109 -0
  381. v0/relationalai/tools/debugger_server.py +302 -0
  382. v0/relationalai/tools/dev.py +685 -0
  383. v0/relationalai/tools/qb_debugger.py +425 -0
  384. v0/relationalai/util/clean_up_databases.py +95 -0
  385. v0/relationalai/util/format.py +123 -0
  386. v0/relationalai/util/list_databases.py +9 -0
  387. v0/relationalai/util/otel_configuration.py +25 -0
  388. v0/relationalai/util/otel_handler.py +484 -0
  389. v0/relationalai/util/snowflake_handler.py +88 -0
  390. v0/relationalai/util/span_format_test.py +43 -0
  391. v0/relationalai/util/span_tracker.py +207 -0
  392. v0/relationalai/util/spans_file_handler.py +72 -0
  393. v0/relationalai/util/tracing_handler.py +34 -0
  394. frontend/debugger/dist/.gitignore +0 -2
  395. frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
  396. frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
  397. frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
  398. frontend/debugger/dist/index.html +0 -17
  399. relationalai/clients/__init__.py +0 -18
  400. relationalai/clients/client.py +0 -912
  401. relationalai/clients/config.py +0 -673
  402. relationalai/clients/direct_access_client.py +0 -118
  403. relationalai/clients/hash_util.py +0 -31
  404. relationalai/clients/local.py +0 -571
  405. relationalai/clients/profile_polling.py +0 -73
  406. relationalai/clients/resources/__init__.py +0 -8
  407. relationalai/clients/resources/azure/azure.py +0 -477
  408. relationalai/clients/resources/snowflake/__init__.py +0 -20
  409. relationalai/clients/resources/snowflake/cli_resources.py +0 -87
  410. relationalai/clients/resources/snowflake/direct_access_resources.py +0 -711
  411. relationalai/clients/resources/snowflake/engine_state_handlers.py +0 -309
  412. relationalai/clients/resources/snowflake/error_handlers.py +0 -199
  413. relationalai/clients/resources/snowflake/export_procedure.py.jinja +0 -249
  414. relationalai/clients/resources/snowflake/resources_factory.py +0 -99
  415. relationalai/clients/resources/snowflake/snowflake.py +0 -3083
  416. relationalai/clients/resources/snowflake/use_index_poller.py +0 -1011
  417. relationalai/clients/resources/snowflake/use_index_resources.py +0 -188
  418. relationalai/clients/resources/snowflake/util.py +0 -387
  419. relationalai/clients/result_helpers.py +0 -420
  420. relationalai/clients/types.py +0 -113
  421. relationalai/clients/util.py +0 -356
  422. relationalai/debugging.py +0 -389
  423. relationalai/dsl.py +0 -1749
  424. relationalai/early_access/builder/__init__.py +0 -30
  425. relationalai/early_access/builder/builder/__init__.py +0 -35
  426. relationalai/early_access/builder/snowflake/__init__.py +0 -12
  427. relationalai/early_access/builder/std/__init__.py +0 -25
  428. relationalai/early_access/builder/std/decimals/__init__.py +0 -12
  429. relationalai/early_access/builder/std/integers/__init__.py +0 -12
  430. relationalai/early_access/builder/std/math/__init__.py +0 -12
  431. relationalai/early_access/builder/std/strings/__init__.py +0 -14
  432. relationalai/early_access/devtools/__init__.py +0 -12
  433. relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
  434. relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
  435. relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
  436. relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
  437. relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
  438. relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
  439. relationalai/early_access/dsl/bindings/common.py +0 -402
  440. relationalai/early_access/dsl/bindings/csv.py +0 -170
  441. relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
  442. relationalai/early_access/dsl/bindings/snowflake.py +0 -64
  443. relationalai/early_access/dsl/codegen/binder.py +0 -411
  444. relationalai/early_access/dsl/codegen/common.py +0 -79
  445. relationalai/early_access/dsl/codegen/helpers.py +0 -23
  446. relationalai/early_access/dsl/codegen/relations.py +0 -700
  447. relationalai/early_access/dsl/codegen/weaver.py +0 -417
  448. relationalai/early_access/dsl/core/builders/__init__.py +0 -47
  449. relationalai/early_access/dsl/core/builders/logic.py +0 -19
  450. relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
  451. relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
  452. relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
  453. relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
  454. relationalai/early_access/dsl/core/context.py +0 -13
  455. relationalai/early_access/dsl/core/cset.py +0 -132
  456. relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
  457. relationalai/early_access/dsl/core/exprs/relational.py +0 -18
  458. relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
  459. relationalai/early_access/dsl/core/instances.py +0 -44
  460. relationalai/early_access/dsl/core/logic/__init__.py +0 -193
  461. relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
  462. relationalai/early_access/dsl/core/logic/exists.py +0 -223
  463. relationalai/early_access/dsl/core/logic/helper.py +0 -163
  464. relationalai/early_access/dsl/core/namespaces.py +0 -32
  465. relationalai/early_access/dsl/core/relations.py +0 -276
  466. relationalai/early_access/dsl/core/rules.py +0 -112
  467. relationalai/early_access/dsl/core/std/__init__.py +0 -45
  468. relationalai/early_access/dsl/core/temporal/recall.py +0 -6
  469. relationalai/early_access/dsl/core/types/__init__.py +0 -270
  470. relationalai/early_access/dsl/core/types/concepts.py +0 -128
  471. relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
  472. relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
  473. relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
  474. relationalai/early_access/dsl/core/types/standard.py +0 -92
  475. relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
  476. relationalai/early_access/dsl/core/types/variables.py +0 -203
  477. relationalai/early_access/dsl/ir/compiler.py +0 -318
  478. relationalai/early_access/dsl/ir/executor.py +0 -260
  479. relationalai/early_access/dsl/ontologies/constraints.py +0 -88
  480. relationalai/early_access/dsl/ontologies/export.py +0 -30
  481. relationalai/early_access/dsl/ontologies/models.py +0 -453
  482. relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
  483. relationalai/early_access/dsl/ontologies/readings.py +0 -60
  484. relationalai/early_access/dsl/ontologies/relationships.py +0 -322
  485. relationalai/early_access/dsl/ontologies/roles.py +0 -87
  486. relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
  487. relationalai/early_access/dsl/orm/constraints.py +0 -438
  488. relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
  489. relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
  490. relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
  491. relationalai/early_access/dsl/orm/measures/measures.py +0 -299
  492. relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
  493. relationalai/early_access/dsl/orm/models.py +0 -256
  494. relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
  495. relationalai/early_access/dsl/orm/printer.py +0 -469
  496. relationalai/early_access/dsl/orm/reasoners.py +0 -480
  497. relationalai/early_access/dsl/orm/relations.py +0 -19
  498. relationalai/early_access/dsl/orm/relationships.py +0 -251
  499. relationalai/early_access/dsl/orm/types.py +0 -42
  500. relationalai/early_access/dsl/orm/utils.py +0 -79
  501. relationalai/early_access/dsl/orm/verb.py +0 -204
  502. relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
  503. relationalai/early_access/dsl/relations.py +0 -170
  504. relationalai/early_access/dsl/rulesets.py +0 -69
  505. relationalai/early_access/dsl/schemas/__init__.py +0 -450
  506. relationalai/early_access/dsl/schemas/builder.py +0 -48
  507. relationalai/early_access/dsl/schemas/comp_names.py +0 -51
  508. relationalai/early_access/dsl/schemas/components.py +0 -203
  509. relationalai/early_access/dsl/schemas/contexts.py +0 -156
  510. relationalai/early_access/dsl/schemas/exprs.py +0 -89
  511. relationalai/early_access/dsl/schemas/fragments.py +0 -464
  512. relationalai/early_access/dsl/serialization.py +0 -79
  513. relationalai/early_access/dsl/serialize/exporter.py +0 -163
  514. relationalai/early_access/dsl/snow/api.py +0 -105
  515. relationalai/early_access/dsl/snow/common.py +0 -76
  516. relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
  517. relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
  518. relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
  519. relationalai/early_access/dsl/types/__init__.py +0 -40
  520. relationalai/early_access/dsl/types/concepts.py +0 -12
  521. relationalai/early_access/dsl/types/entities.py +0 -135
  522. relationalai/early_access/dsl/types/values.py +0 -17
  523. relationalai/early_access/dsl/utils.py +0 -102
  524. relationalai/early_access/graphs/__init__.py +0 -13
  525. relationalai/early_access/lqp/__init__.py +0 -12
  526. relationalai/early_access/lqp/compiler/__init__.py +0 -12
  527. relationalai/early_access/lqp/constructors/__init__.py +0 -18
  528. relationalai/early_access/lqp/executor/__init__.py +0 -12
  529. relationalai/early_access/lqp/ir/__init__.py +0 -12
  530. relationalai/early_access/lqp/passes/__init__.py +0 -12
  531. relationalai/early_access/lqp/pragmas/__init__.py +0 -12
  532. relationalai/early_access/lqp/primitives/__init__.py +0 -12
  533. relationalai/early_access/lqp/types/__init__.py +0 -12
  534. relationalai/early_access/lqp/utils/__init__.py +0 -12
  535. relationalai/early_access/lqp/validators/__init__.py +0 -12
  536. relationalai/early_access/metamodel/__init__.py +0 -58
  537. relationalai/early_access/metamodel/builtins/__init__.py +0 -12
  538. relationalai/early_access/metamodel/compiler/__init__.py +0 -12
  539. relationalai/early_access/metamodel/dependency/__init__.py +0 -12
  540. relationalai/early_access/metamodel/factory/__init__.py +0 -17
  541. relationalai/early_access/metamodel/helpers/__init__.py +0 -12
  542. relationalai/early_access/metamodel/ir/__init__.py +0 -14
  543. relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
  544. relationalai/early_access/metamodel/typer/__init__.py +0 -3
  545. relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
  546. relationalai/early_access/metamodel/types/__init__.py +0 -15
  547. relationalai/early_access/metamodel/util/__init__.py +0 -15
  548. relationalai/early_access/metamodel/visitor/__init__.py +0 -12
  549. relationalai/early_access/rel/__init__.py +0 -12
  550. relationalai/early_access/rel/executor/__init__.py +0 -12
  551. relationalai/early_access/rel/rel_utils/__init__.py +0 -12
  552. relationalai/early_access/rel/rewrite/__init__.py +0 -7
  553. relationalai/early_access/solvers/__init__.py +0 -19
  554. relationalai/early_access/sql/__init__.py +0 -11
  555. relationalai/early_access/sql/executor/__init__.py +0 -3
  556. relationalai/early_access/sql/rewrite/__init__.py +0 -3
  557. relationalai/early_access/tests/logging/__init__.py +0 -12
  558. relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
  559. relationalai/early_access/tests/utils/__init__.py +0 -12
  560. relationalai/environments/__init__.py +0 -35
  561. relationalai/environments/base.py +0 -381
  562. relationalai/environments/colab.py +0 -14
  563. relationalai/environments/generic.py +0 -71
  564. relationalai/environments/ipython.py +0 -68
  565. relationalai/environments/jupyter.py +0 -9
  566. relationalai/environments/snowbook.py +0 -169
  567. relationalai/errors.py +0 -2478
  568. relationalai/experimental/SF.py +0 -38
  569. relationalai/experimental/inspect.py +0 -47
  570. relationalai/experimental/pathfinder/__init__.py +0 -158
  571. relationalai/experimental/pathfinder/api.py +0 -160
  572. relationalai/experimental/pathfinder/automaton.py +0 -584
  573. relationalai/experimental/pathfinder/bridge.py +0 -226
  574. relationalai/experimental/pathfinder/compiler.py +0 -416
  575. relationalai/experimental/pathfinder/datalog.py +0 -214
  576. relationalai/experimental/pathfinder/diagnostics.py +0 -56
  577. relationalai/experimental/pathfinder/filter.py +0 -236
  578. relationalai/experimental/pathfinder/glushkov.py +0 -439
  579. relationalai/experimental/pathfinder/options.py +0 -265
  580. relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
  581. relationalai/experimental/pathfinder/rpq.py +0 -344
  582. relationalai/experimental/pathfinder/transition.py +0 -200
  583. relationalai/experimental/pathfinder/utils.py +0 -26
  584. relationalai/experimental/paths/README.md +0 -107
  585. relationalai/experimental/paths/api.py +0 -143
  586. relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
  587. relationalai/experimental/paths/code_organization.md +0 -2
  588. relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
  589. relationalai/experimental/paths/examples/basic_example.py +0 -40
  590. relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
  591. relationalai/experimental/paths/examples/movie_example.py +0 -77
  592. relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
  593. relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
  594. relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
  595. relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
  596. relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
  597. relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
  598. relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
  599. relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
  600. relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
  601. relationalai/experimental/paths/examples/paths_example.py +0 -116
  602. relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
  603. relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
  604. relationalai/experimental/paths/graph.py +0 -185
  605. relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
  606. relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
  607. relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
  608. relationalai/experimental/paths/path_algorithms/single.py +0 -59
  609. relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
  610. relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
  611. relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
  612. relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
  613. relationalai/experimental/paths/path_algorithms/usp.py +0 -150
  614. relationalai/experimental/paths/product_graph.py +0 -93
  615. relationalai/experimental/paths/rpq/automaton.py +0 -584
  616. relationalai/experimental/paths/rpq/diagnostics.py +0 -56
  617. relationalai/experimental/paths/rpq/rpq.py +0 -378
  618. relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
  619. relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
  620. relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
  621. relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
  622. relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
  623. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
  624. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
  625. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
  626. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
  627. relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
  628. relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
  629. relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
  630. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
  631. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
  632. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
  633. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
  634. relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
  635. relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
  636. relationalai/experimental/paths/tree_agg.py +0 -168
  637. relationalai/experimental/paths/utilities/iterators.py +0 -27
  638. relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
  639. relationalai/experimental/solvers.py +0 -1087
  640. relationalai/loaders/csv.py +0 -195
  641. relationalai/loaders/loader.py +0 -177
  642. relationalai/loaders/types.py +0 -23
  643. relationalai/rel_emitter.py +0 -373
  644. relationalai/rel_utils.py +0 -185
  645. relationalai/semantics/designs/query_builder/identify_by.md +0 -106
  646. relationalai/semantics/devtools/benchmark_lqp.py +0 -535
  647. relationalai/semantics/devtools/compilation_manager.py +0 -294
  648. relationalai/semantics/devtools/extract_lqp.py +0 -110
  649. relationalai/semantics/internal/internal.py +0 -3785
  650. relationalai/semantics/internal/snowflake.py +0 -325
  651. relationalai/semantics/lqp/README.md +0 -34
  652. relationalai/semantics/lqp/builtins.py +0 -16
  653. relationalai/semantics/lqp/compiler.py +0 -22
  654. relationalai/semantics/lqp/constructors.py +0 -68
  655. relationalai/semantics/lqp/executor.py +0 -469
  656. relationalai/semantics/lqp/intrinsics.py +0 -24
  657. relationalai/semantics/lqp/model2lqp.py +0 -877
  658. relationalai/semantics/lqp/passes.py +0 -680
  659. relationalai/semantics/lqp/primitives.py +0 -252
  660. relationalai/semantics/lqp/result_helpers.py +0 -202
  661. relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -57
  662. relationalai/semantics/lqp/rewrite/cdc.py +0 -216
  663. relationalai/semantics/lqp/rewrite/extract_common.py +0 -338
  664. relationalai/semantics/lqp/rewrite/extract_keys.py +0 -506
  665. relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
  666. relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -314
  667. relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -296
  668. relationalai/semantics/lqp/rewrite/splinter.py +0 -76
  669. relationalai/semantics/lqp/types.py +0 -101
  670. relationalai/semantics/lqp/utils.py +0 -160
  671. relationalai/semantics/lqp/validators.py +0 -57
  672. relationalai/semantics/metamodel/compiler.py +0 -133
  673. relationalai/semantics/metamodel/dependency.py +0 -862
  674. relationalai/semantics/metamodel/executor.py +0 -61
  675. relationalai/semantics/metamodel/factory.py +0 -287
  676. relationalai/semantics/metamodel/helpers.py +0 -361
  677. relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
  678. relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -210
  679. relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
  680. relationalai/semantics/metamodel/rewrite/flatten.py +0 -554
  681. relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -165
  682. relationalai/semantics/metamodel/typer/checker.py +0 -353
  683. relationalai/semantics/metamodel/typer/typer.py +0 -1395
  684. relationalai/semantics/metamodel/util.py +0 -506
  685. relationalai/semantics/reasoners/__init__.py +0 -10
  686. relationalai/semantics/reasoners/graph/README.md +0 -620
  687. relationalai/semantics/reasoners/graph/__init__.py +0 -37
  688. relationalai/semantics/reasoners/graph/core.py +0 -9019
  689. relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
  690. relationalai/semantics/reasoners/graph/tests/README.md +0 -21
  691. relationalai/semantics/reasoners/optimization/__init__.py +0 -68
  692. relationalai/semantics/reasoners/optimization/common.py +0 -88
  693. relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
  694. relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1163
  695. relationalai/semantics/rel/builtins.py +0 -40
  696. relationalai/semantics/rel/compiler.py +0 -989
  697. relationalai/semantics/rel/executor.py +0 -362
  698. relationalai/semantics/rel/rel.py +0 -482
  699. relationalai/semantics/rel/rel_utils.py +0 -276
  700. relationalai/semantics/snowflake/__init__.py +0 -3
  701. relationalai/semantics/sql/compiler.py +0 -2503
  702. relationalai/semantics/sql/executor/duck_db.py +0 -52
  703. relationalai/semantics/sql/executor/result_helpers.py +0 -64
  704. relationalai/semantics/sql/executor/snowflake.py +0 -149
  705. relationalai/semantics/sql/rewrite/denormalize.py +0 -222
  706. relationalai/semantics/sql/rewrite/double_negation.py +0 -49
  707. relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
  708. relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
  709. relationalai/semantics/sql/sql.py +0 -504
  710. relationalai/semantics/std/pragmas.py +0 -11
  711. relationalai/semantics/std/std.py +0 -14
  712. relationalai/semantics/tests/test_snapshot_abstract.py +0 -143
  713. relationalai/semantics/tests/test_snapshot_base.py +0 -9
  714. relationalai/semantics/tests/utils.py +0 -46
  715. relationalai/std/__init__.py +0 -70
  716. relationalai/tools/cli.py +0 -1936
  717. relationalai/tools/cli_controls.py +0 -1826
  718. relationalai/tools/cli_helpers.py +0 -398
  719. relationalai/tools/debugger_client.py +0 -109
  720. relationalai/tools/debugger_server.py +0 -302
  721. relationalai/tools/dev.py +0 -685
  722. relationalai/tools/notes +0 -7
  723. relationalai/tools/qb_debugger.py +0 -425
  724. relationalai/util/clean_up_databases.py +0 -95
  725. relationalai/util/list_databases.py +0 -9
  726. relationalai/util/otel_configuration.py +0 -26
  727. relationalai/util/otel_handler.py +0 -484
  728. relationalai/util/snowflake_handler.py +0 -88
  729. relationalai/util/span_format_test.py +0 -43
  730. relationalai/util/span_tracker.py +0 -207
  731. relationalai/util/spans_file_handler.py +0 -72
  732. relationalai/util/tracing_handler.py +0 -34
  733. relationalai-0.13.0.dist-info/METADATA +0 -74
  734. relationalai-0.13.0.dist-info/RECORD +0 -458
  735. relationalai-0.13.0.dist-info/WHEEL +0 -4
  736. relationalai-0.13.0.dist-info/entry_points.txt +0 -3
  737. relationalai-0.13.0.dist-info/licenses/LICENSE +0 -202
  738. relationalai_test_util/__init__.py +0 -4
  739. relationalai_test_util/fixtures.py +0 -229
  740. relationalai_test_util/snapshot.py +0 -252
  741. relationalai_test_util/traceback.py +0 -118
  742. /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
  743. /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
  744. /relationalai/{early_access → shims}/__init__.py +0 -0
  745. {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
  746. {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
  747. {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
  748. {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
  749. {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
  750. {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
  751. {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
  752. {relationalai → v0/relationalai}/auth/util.py +0 -0
  753. {relationalai/clients/resources/snowflake → v0/relationalai/clients}/cache_store.py +0 -0
  754. {relationalai → v0/relationalai}/compiler.py +0 -0
  755. {relationalai → v0/relationalai}/dependencies.py +0 -0
  756. {relationalai → v0/relationalai}/docutils.py +0 -0
  757. {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
  758. {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
  759. {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
  760. {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
  761. {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
  762. {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
  763. {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
  764. {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
  765. {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
  766. {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
  767. {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
  768. {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
  769. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
  770. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
  771. {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
  772. {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
  773. {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
  774. {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
  775. {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
  776. {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
  777. {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
  778. {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
  779. {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
  780. {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
  781. {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
  782. {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
  783. {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
  784. {relationalai/tools → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
  785. {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
  786. {relationalai → v0/relationalai}/environments/ci.py +0 -0
  787. {relationalai → v0/relationalai}/environments/hex.py +0 -0
  788. {relationalai → v0/relationalai}/environments/terminal.py +0 -0
  789. {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
  790. {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
  791. {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
  792. {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
  793. {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
  794. {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
  795. {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
  796. {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
  797. {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
  798. {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
  799. {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
  800. {relationalai → v0/relationalai}/metagen.py +0 -0
  801. {relationalai → v0/relationalai}/metamodel.py +0 -0
  802. {relationalai → v0/relationalai}/rel.py +0 -0
  803. {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
  804. {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
  805. {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
  806. {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
  807. {relationalai → v0/relationalai}/semantics/lqp/ir.py +0 -0
  808. {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
  809. {relationalai → v0/relationalai}/semantics/lqp/rewrite/__init__.py +0 -0
  810. {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
  811. {relationalai → v0/relationalai}/semantics/metamodel/ir.py +0 -0
  812. {relationalai → v0/relationalai}/semantics/metamodel/rewrite/__init__.py +0 -0
  813. {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
  814. {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
  815. {relationalai → v0/relationalai}/semantics/metamodel/visitor.py +0 -0
  816. {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
  817. {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
  818. {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
  819. {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
  820. {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
  821. {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
  822. {relationalai → v0/relationalai}/std/aggregates.py +0 -0
  823. {relationalai → v0/relationalai}/std/dates.py +0 -0
  824. {relationalai → v0/relationalai}/std/graphs.py +0 -0
  825. {relationalai → v0/relationalai}/std/inspect.py +0 -0
  826. {relationalai → v0/relationalai}/std/math.py +0 -0
  827. {relationalai → v0/relationalai}/std/re.py +0 -0
  828. {relationalai → v0/relationalai}/std/strings.py +0 -0
  829. {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
  830. {relationalai → v0/relationalai}/tools/constants.py +0 -0
  831. {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
  832. {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
  833. {relationalai → v0/relationalai}/util/__init__.py +0 -0
  834. {relationalai → v0/relationalai}/util/constants.py +0 -0
  835. {relationalai → v0/relationalai}/util/graph.py +0 -0
  836. {relationalai → v0/relationalai}/util/timeout.py +0 -0
@@ -0,0 +1,1707 @@
1
+ """Base frontend classes for RelationalAI semantics API.
2
+ """
3
+ from __future__ import annotations
4
+
5
+ import decimal
6
+ import math
7
+ import re
8
+ import datetime as dt
9
+ from typing import TYPE_CHECKING, Any, Generic, Iterable, Iterator, NoReturn, Optional, Sequence, Tuple, Type, TypeGuard, TypeVar, cast
10
+ import itertools
11
+ from more_itertools import peekable
12
+ from pandas import DataFrame
13
+ import pandas as pd
14
+ from enum import Enum, EnumMeta
15
+
16
+ from ...util import schema as schema_util
17
+ from ...util.structures import KeyedSet
18
+ from ...util.naming import Namer, sanitize
19
+ from ...util.python import pytype_to_concept_name
20
+ from ...util.source import SourcePos
21
+ from ...util.tracing import get_tracer
22
+ from ...util.error import err, exc, warn, source
23
+ from ...util.docutils import include_in_docs
24
+ from ..metamodel.builtins import builtins
25
+ from ..metamodel.metamodel import Model as mModel
26
+
27
+ tracer = get_tracer()
28
+ tracer.start_program()
29
+
30
+ #------------------------------------------------------
31
+ # Global ID Generator
32
+ #------------------------------------------------------
33
+
34
+ _global_id = peekable(itertools.count(0))
35
+
36
+ #------------------------------------------------------
37
+ # Helpers
38
+ #------------------------------------------------------
39
+
40
+ def _find_field(fields: list[Field], field: str|int|Concept) -> tuple[Field, int]|None:
41
+ resolved = None
42
+ if isinstance(field, int):
43
+ if not (0 <= field < len(fields)):
44
+ raise IndexError(f"Field index {field} out of range for relationship with {len(fields)} fields")
45
+ resolved = fields[field]
46
+ elif isinstance(field, str):
47
+ resolved = next((f for f in fields if f.name == field), None)
48
+ elif isinstance(field, Concept):
49
+ resolved = next((f for f in fields if f.type is field), None)
50
+ if not resolved:
51
+ return None
52
+ return resolved, fields.index(resolved)
53
+
54
+ def _find_concept(model: Model, passed_type: Any, extra_types: Sequence[Concept] = [], default:Concept|None = None) -> Concept | None:
55
+ if isinstance(passed_type, Concept):
56
+ return passed_type
57
+ if isinstance(passed_type, str):
58
+ for ext_type in extra_types:
59
+ if ext_type._name == passed_type:
60
+ return ext_type
61
+ name = pytype_to_concept_name.get(passed_type, passed_type)
62
+ if name.startswith("Number(") or name.startswith("Decimal("):
63
+ return cast(Any, CoreConcepts["Number"]).parse(name)
64
+ return model._find_concept(name)
65
+ #TODO: try to find a variable in the frame that is a concept with this name
66
+ return default
67
+
68
+ #------------------------------------------------------
69
+ # Primitive
70
+ #------------------------------------------------------
71
+
72
+ Primitive = str|int|float|bool
73
+
74
+ def is_primitive(value: Any) -> TypeGuard[str | int | float | bool]:
75
+ return isinstance(value, (str, int, float, bool))
76
+
77
+ #------------------------------------------------------
78
+ # DSLBase
79
+ #------------------------------------------------------
80
+
81
+ class DSLBase:
82
+ def __init__(self, model:Model):
83
+ self._model = model
84
+ self._id = next(_global_id)
85
+ self._source = SourcePos.new()
86
+
87
+ #------------------------------------------------------
88
+ # Hashing
89
+ #------------------------------------------------------
90
+
91
+ def to_dict_key(self) -> int|tuple[int, int]:
92
+ return self._id
93
+
94
+ def __hash__(self) -> NoReturn:
95
+ exc("Unhashable type", f"{self.__class__.__name__} objects are unhashable. Use `to_dict_key()` instead.", [source(self)])
96
+
97
+ #------------------------------------------------------
98
+ # DSLDict
99
+ #------------------------------------------------------
100
+
101
+ def dsl_key(obj:Any) -> Any:
102
+ if isinstance(obj, DSLBase):
103
+ return obj.to_dict_key()
104
+ elif isinstance(obj, tuple):
105
+ return tuple(dsl_key(o) for o in obj)
106
+ return obj
107
+
108
+ #------------------------------------------------------
109
+ # Variable
110
+ #------------------------------------------------------
111
+
112
+ class Variable(DSLBase):
113
+ #--------------------------------------------------
114
+ # Infix operator overloads
115
+ #--------------------------------------------------
116
+
117
+ def _bin_op(self, op, left, right) -> Expression:
118
+ res = CoreConcepts["Numeric"].ref()
119
+ return Expression(CoreRelationships[op], [left, right, res])
120
+
121
+ def __add__(self, other):
122
+ return self._bin_op("+", self, other)
123
+ def __radd__(self, other):
124
+ return self._bin_op("+", other, self)
125
+
126
+ def __mul__(self, other):
127
+ return self._bin_op("*", self, other)
128
+ def __rmul__(self, other):
129
+ return self._bin_op("*", other, self)
130
+
131
+ def __sub__(self, other):
132
+ return self._bin_op("-", self, other)
133
+ def __rsub__(self, other):
134
+ return self._bin_op("-", other, self)
135
+
136
+ def __truediv__(self, other):
137
+ return self._bin_op("/", self, other)
138
+ def __rtruediv__(self, other):
139
+ return self._bin_op("/", other, self)
140
+
141
+ def __floordiv__(self, other):
142
+ return self._bin_op("//", self, other)
143
+ def __rfloordiv__(self, other):
144
+ return self._bin_op("//", other, self)
145
+
146
+ def __pow__(self, other):
147
+ return self._bin_op("^", self, other)
148
+ def __rpow__(self, other):
149
+ return self._bin_op("^", other, self)
150
+
151
+ def __mod__(self, other):
152
+ return self._bin_op("%", self, other)
153
+ def __rmod__(self, other):
154
+ return self._bin_op("%", other, self)
155
+
156
+ def __neg__(self):
157
+ return self._bin_op("*", self, -1)
158
+
159
+ #--------------------------------------------------
160
+ # Filter overloads
161
+ #--------------------------------------------------
162
+
163
+ def _filter(self, op, left, right) -> Expression:
164
+ return Expression(CoreRelationships[op], [left, right])
165
+
166
+ def __gt__(self, other):
167
+ return self._filter(">", self, other)
168
+ def __ge__(self, other):
169
+ return self._filter(">=", self, other)
170
+ def __lt__(self, other):
171
+ return self._filter("<", self, other)
172
+ def __le__(self, other):
173
+ return self._filter("<=", self, other)
174
+ def __eq__(self, other) -> Any:
175
+ return self._filter("=", self, other)
176
+ def __ne__(self, other) -> Any:
177
+ return self._filter("!=", self, other)
178
+
179
+ #--------------------------------------------------
180
+ # And/Or
181
+ #--------------------------------------------------
182
+
183
+ def __or__(self, other) -> Match:
184
+ return Match(self._model, self, other)
185
+
186
+ def __and__(self, other) -> Fragment:
187
+ if isinstance(other, Fragment):
188
+ new = other.where()
189
+ new._where.insert(0, self)
190
+ return new
191
+ return self._model.where(self, other)
192
+
193
+ #------------------------------------------------------
194
+ # AsBool
195
+ #------------------------------------------------------
196
+
197
+ def as_bool(self) -> AsBool:
198
+ return AsBool(self)
199
+
200
+ #------------------------------------------------------
201
+ # Alias
202
+ #------------------------------------------------------
203
+
204
+ def alias(self, alias: str) -> Alias:
205
+ return Alias(self, alias)
206
+
207
+ #--------------------------------------------------
208
+ # in_
209
+ #--------------------------------------------------
210
+
211
+ def in_(self, values:Sequence[Value]|Variable):
212
+ if isinstance(values, Variable):
213
+ return self == values
214
+ if all(isinstance(v, (str, int, float, bool)) for v in values):
215
+ data_table = self._model.data([(v,) for v in values])
216
+ return self == data_table[0]
217
+ else:
218
+ return self == self._model.union(*values)
219
+
220
+ #--------------------------------------------------
221
+ # Check value
222
+ #--------------------------------------------------
223
+
224
+ def _check_value(self) -> bool:
225
+ return True
226
+
227
+ def _to_concept(self) -> Concept|None:
228
+ return None
229
+
230
+ #------------------------------------------------------
231
+ # Error handling
232
+ #------------------------------------------------------
233
+
234
+ def __bool__(self) -> NoReturn:
235
+ cur_source = self._source.block.source or ""
236
+ invalid = next((bool_check for bool_check in ["if ", "while ", " and ", " or "] if bool_check in cur_source), "bool check").strip()
237
+ mapped = {"and": "`&` or `,`", "or": "`|`", "if": "`where`"}
238
+ if m := mapped.get(invalid):
239
+ exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions. Use {m} instead.", [source(self)])
240
+ else:
241
+ exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions.", [source(self)])
242
+
243
+ if not TYPE_CHECKING:
244
+ def __iter__(self):
245
+ common_incorrect = ["sum", "min", "max"]
246
+ for agg in common_incorrect:
247
+ if agg in self._source.block.source:
248
+ exc(f"Invalid built-in", f"The Python built-in `{agg}()` was used instead of the RAI equivalent.", [
249
+ source(self),
250
+ f"Use [cyan]`relationalai.semantics.std.aggregates.{agg}`[/cyan] instead.",
251
+ ])
252
+ exc("Invalid iteration", f"Cannot iterate over {self.__class__.__name__} objects.", [
253
+ source(self)
254
+ ])
255
+
256
+ def __len__(self) -> NoReturn:
257
+ exc("Invalid operator", "Cannot use len() on model variables.", [source(self)])
258
+
259
+ #------------------------------------------------------
260
+ # Concept
261
+ #------------------------------------------------------
262
+
263
+ class Concept(Variable):
264
+ lookup_by_id: dict[int, Concept] = {}
265
+
266
+ def __init__(self, name: str, extends: list[Concept], identify_by: dict[str, Concept], model:Model):
267
+ super().__init__(model)
268
+ Concept.lookup_by_id[self._id] = self
269
+ self._name = name
270
+ clean_extends = []
271
+ for extend in extends:
272
+ if not isinstance(extend, Concept):
273
+ exc("Invalid extend", f"Cannot extend from non-Concept type: {extend}", [source(self)])
274
+ if "Number" in CoreConcepts and extend is CoreConcepts["Number"]:
275
+ clean_extends.append(extend.size(38, 14))
276
+ else:
277
+ clean_extends.append(extend)
278
+ self._extends = clean_extends
279
+ self._annotations: list[Expression|Relationship] = []
280
+ self._relationships = {}
281
+ self._identify_by:list[Relationship] = []
282
+ for id_name, id_val in identify_by.items():
283
+ if not isinstance(id_val, (Concept, Property)):
284
+ exc("Invalid identify_by", f"identify_by values must be Concepts, got: {type(id_val)}", [source(self)])
285
+ id_val = self._dot(id_name, with_type=id_val)
286
+ self._identify_by.append(id_val)
287
+
288
+ def __setattr__(self, name: str, value: Any) -> None:
289
+ if isinstance(value, Relationship):
290
+ key = name.lower()
291
+ if self._relationships.get(key) is not None:
292
+ exc("Duplicate relationship", f"Relationship '{name}' is already defined on concept '{self._name}'", [
293
+ source(self._relationships[key])
294
+ ])
295
+ if not value._short_name:
296
+ value._short_name = name
297
+ self._relationships[key] = value
298
+ else:
299
+ super().__setattr__(name, value)
300
+
301
+ def _dot_recur(self, name: str, with_type: Any = None) -> Relationship|None:
302
+ if name.lower() in self._relationships:
303
+ return self._relationships[name.lower()]
304
+ for ext in self._extends:
305
+ rel = ext._dot_recur(name, with_type)
306
+ if rel is not None:
307
+ return rel
308
+ # We should create the relationship on the root type if it's not
309
+ # found anywhere
310
+ if self._model is not CoreLibrary:
311
+ field_type = _find_concept(self._model, with_type, default=CoreConcepts["Any"])
312
+ assert field_type is not None
313
+ rel = self._relationships[name.lower()] = Property(fields=[
314
+ Field(self._name.lower(), self),
315
+ Field(name, field_type)
316
+ ], short_name=name, model=self._model)
317
+ return rel
318
+ return None
319
+
320
+ def _dot(self, name: str, with_type: Any = None) -> Relationship:
321
+ if self._model is CoreLibrary:
322
+ exc("Invalid relationship", f"Cannot access relationships on core concept '{self._name}'.", [source(self)])
323
+ if with_type is None:
324
+ with_type = CoreConcepts["Any"]
325
+ rel = self._dot_recur(name, with_type)
326
+ assert rel is not None
327
+ return rel
328
+
329
+ def __getattr__(self, item) -> Chain:
330
+ if item.startswith("_"):
331
+ return object.__getattribute__(self, item)
332
+ return Chain(self, self._dot(item))
333
+
334
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
335
+ return Expression(self, list(args), kwargs)
336
+
337
+ def new(self, *args: StatementAndSchema, **kwargs: Any) -> New:
338
+ return New(self, args, kwargs)
339
+
340
+ def to_identity(self, *args: Any, **kwargs: Any) -> New:
341
+ return New(self, args, kwargs, identity_only=True)
342
+
343
+ def identify_by(self, *properties: Property|Chain) -> Concept:
344
+ for prop in properties:
345
+ if isinstance(prop, Chain):
346
+ prop = prop._next
347
+ if not isinstance(prop, Property) and not (isinstance(prop, Reading) and isinstance(prop._relationship, Property)):
348
+ exc("Invalid identify_by", f"identify_by expects Properties, got: {type(prop).__name__}", [source(self)])
349
+ if prop._fields[0].type is not self:
350
+ exc("Invalid identify_by", f"Property {prop} have the first field be {self._name}", [source(self)])
351
+ self._identify_by.append(prop)
352
+ return self
353
+
354
+ def filter_by(self, **kwargs: Any) -> Expression:
355
+ return FilterBy(self, kwargs)
356
+
357
+ def require(self, *items: Variable|Fragment) -> Fragment:
358
+ return self._model.where(self).require(*items)
359
+
360
+ def ref(self, name="") -> Ref:
361
+ return Ref(self, name)
362
+
363
+ def annotate(self, *annos:Expression|Relationship) -> Concept:
364
+ self._annotations.extend(annos)
365
+ return self
366
+
367
+ def _to_concept(self) -> Concept:
368
+ return self
369
+
370
+ def __dir__(self):
371
+ default = set(super().__dir__())
372
+ return sorted(default.union(self._relationships.keys()))
373
+
374
+ def __format__(self, format_spec: str) -> str:
375
+ if not format_spec:
376
+ return f"{{{self._name}#{self._id}}}"
377
+ return f"{{{self._name}#{self._id}:{format_spec.strip()}}}"
378
+
379
+ #------------------------------------------------------
380
+ # NumberConcept
381
+ #------------------------------------------------------
382
+
383
+ class NumberConcept(Concept):
384
+ def __init__(self, name: str, precision: int, scale: int, model: Model):
385
+ super().__init__(name, extends=[CoreConcepts["Numeric"]], identify_by={}, model=model)
386
+ self._precision = precision
387
+ self._scale = scale
388
+
389
+ #------------------------------------------------------
390
+ # Ref
391
+ #------------------------------------------------------
392
+
393
+ class Ref(Variable):
394
+ def __init__(self, concept: Concept, name: str|None = None):
395
+ super().__init__(concept._model)
396
+ self._concept = concept
397
+ self._name = name or ("number" if (isinstance(concept, NumberConcept) or concept is CoreConcepts["Numeric"])
398
+ else concept._name)
399
+
400
+ def __getattr__(self, item):
401
+ if item.startswith("_"):
402
+ return object.__getattribute__(self, item)
403
+ return Chain(self, self._concept._dot(item))
404
+
405
+ def _to_concept(self) -> Concept:
406
+ return self._concept
407
+
408
+ def FilterBy(self, **kwargs: Any) -> Expression:
409
+ return FilterBy(self, kwargs)
410
+
411
+ #------------------------------------------------------
412
+ # Table
413
+ #------------------------------------------------------
414
+
415
+ class Table(Concept):
416
+ def __init__(self, name:str, schema: dict[str, Concept], model: Model):
417
+ super().__init__(name, [], {}, model)
418
+ self._known_columns = []
419
+ for col_name, col_type in schema.items():
420
+ if isinstance(col_type, Property):
421
+ col_rel = col_type
422
+ setattr(self, col_name, col_rel)
423
+ else:
424
+ col_rel = self._dot(col_name, with_type=col_type)
425
+ self._known_columns.append(col_rel)
426
+
427
+ @property
428
+ def _columns(self) -> list[Relationship]:
429
+ if self._known_columns:
430
+ return self._known_columns
431
+ schema = schema_util.fetch(self._name)
432
+ for col_name, col_type_name in schema.items():
433
+ col_type = _find_concept(self._model, col_type_name, default=CoreConcepts["Any"])
434
+ col_rel = self._dot(col_name, with_type=col_type)
435
+ self._known_columns.append(col_rel)
436
+ return self._known_columns
437
+
438
+ def __getitem__(self, index: int|str) -> Chain:
439
+ if isinstance(index, int):
440
+ if not (0 <= index < len(self._columns)):
441
+ raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
442
+ return Chain(self, self._columns[index])
443
+ col = next((c for c in self._columns if c._short_name == index), None)
444
+ if col is None:
445
+ raise KeyError(f"Column name '{index}' not found, columns have names {[c._short_name for c in self._columns]}")
446
+ return Chain(self, col)
447
+
448
+ def __iter__(self) -> Iterator[Relationship]:
449
+ return iter(self._columns)
450
+
451
+ def new(self, *args:StatementAndSchema, **kwargs: Any) -> NoReturn:
452
+ exc("Invalid new call", "Cannot create new instances of Tables.", [source(self)])
453
+
454
+ def to_identity(self, *args: Any, **kwargs: Any) -> NoReturn:
455
+ exc("Invalid identity call", "Cannot create identity instances of Tables.", [source(self)])
456
+
457
+ def to_schema(self, *, exclude: list[str] = []) -> TableSchema:
458
+ return TableSchema(self, exclude=exclude)
459
+
460
+ #------------------------------------------------------
461
+ # TableSchema
462
+ #------------------------------------------------------
463
+
464
+ class TableSchema(DSLBase):
465
+ def __init__(self, table: Table, exclude: list[str] = []):
466
+ super().__init__(table._model)
467
+ self._table = table
468
+ self.exclude = set([e.lower() for e in exclude])
469
+
470
+ def get_columns(self) -> list[Relationship]:
471
+ return [col for col in self._table._columns if col._short_name.lower() not in self.exclude]
472
+
473
+ #--------------------------------------------------
474
+ # DerivedTable
475
+ #--------------------------------------------------
476
+
477
+ class DerivedTable(Variable):
478
+ def __init__(self, model: Model):
479
+ super().__init__(model)
480
+ self.__cols = []
481
+
482
+ @property
483
+ def _columns(self):
484
+ if not self.__cols:
485
+ self.__cols = self._get_cols()
486
+ if not self.__cols:
487
+ raise ValueError(f"Cannot use {self.__class__.__name__} as it has no columns")
488
+ return self.__cols
489
+
490
+ def _check_value(self):
491
+ return bool(self._columns)
492
+
493
+ def __iter__(self) -> Iterator[DerivedColumn]:
494
+ return iter(self._columns)
495
+
496
+ def __getitem__(self, index: int|str) -> DerivedColumn:
497
+ if isinstance(index, int):
498
+ if not (0 <= index < len(self._columns)):
499
+ raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
500
+ return self._columns[index]
501
+ col = next((c for c in self._columns if c._name == index), None)
502
+ if not col:
503
+ raise KeyError(f"Column name '{index}' not found, columns have names {[c._name for c in self._columns]}")
504
+ return col
505
+
506
+ def _get_cols(self):
507
+ raise NotImplementedError()
508
+
509
+ #--------------------------------------------------
510
+ # DerivedColumn
511
+ #--------------------------------------------------
512
+
513
+ class DerivedColumn(Variable):
514
+ def __init__(self, table: DerivedTable, index: int, name: str|None = None, type_: Concept|None = None):
515
+ super().__init__(table._model)
516
+ self._table = table
517
+ self._index = index
518
+ self._name = name
519
+ self._type = type_
520
+ self._relationships = {}
521
+
522
+ def _dot(self, name: str) -> Relationship:
523
+ if self._type is not None:
524
+ return self._type._dot(name)
525
+ if name.lower() not in self._relationships:
526
+ self._relationships[name.lower()] = Property(fields=[
527
+ Field("entity", CoreConcepts["Any"]),
528
+ Field(name, CoreConcepts["Any"])
529
+ ], short_name=name, model=self._model)
530
+ return self._relationships[name.lower()]
531
+
532
+ def __getattr__(self, item):
533
+ if item.startswith("_"):
534
+ return object.__getattribute__(self, item)
535
+ return Chain(self, self._dot(item))
536
+
537
+ @staticmethod
538
+ def from_value(table: DerivedTable, index: int, var: Value) -> DerivedColumn:
539
+ name = None
540
+ if isinstance(var, Alias):
541
+ name = var._alias
542
+ if not isinstance(var, Variable):
543
+ var = Literal(var, table._model)
544
+ return DerivedColumn(table=table, index=index, name=name, type_=var._to_concept())
545
+
546
+ #------------------------------------------------------
547
+ # Field
548
+ #------------------------------------------------------
549
+
550
+ class Field:
551
+ def __init__(self, name: str, type_: Concept, is_input: bool = False, is_list: bool = False, source: SourcePos | None = None):
552
+ self._id = next(_global_id)
553
+ self.name = name
554
+ self.type = type_
555
+ self.is_input = is_input
556
+ self.is_list = is_list
557
+ self._source = source
558
+
559
+ @classmethod
560
+ def input(cls, name: str, concept: Concept, is_list: bool = False) -> Field:
561
+ return Field(name, concept, is_input=True, is_list=is_list)
562
+
563
+ def _match(self, other: Field) -> bool:
564
+ return self.name == other.name and self.type is other.type
565
+
566
+ #--------------------------------------------------
567
+ # Relationship
568
+ #--------------------------------------------------
569
+
570
+ class Relationship(Variable):
571
+ def __init__(self, model: Model, reading_str:str = "", fields: list[Field] = [], short_name: str = "", allow_no_fields: bool = False, overloads:list[list[Concept]]|None = None, is_unresolved: bool = False):
572
+ super().__init__(model)
573
+ if not reading_str and not fields and not allow_no_fields:
574
+ raise ValueError("Either reading_str or fields must be provided")
575
+ if not reading_str and fields:
576
+ reading_str = " and ".join([f"{{{f.type._name}#{f.type._id}:{f.name}}}" for f in fields[:-1]])
577
+ reading_str = f"{reading_str} has {{{fields[-1].type._name}#{fields[-1].type._id}:{fields[-1].name}}}"
578
+ parts = []
579
+ if not fields:
580
+ (fields, parts) = Reading.parse(model, reading_str, fields, _source=self)
581
+ self._fields = fields
582
+ if not fields and not allow_no_fields:
583
+ exc("Invalid Relationship", "A Relationship must have at least one field.", [source(self)])
584
+ self._readings = [Reading(model, self, reading_str, fields, parts)]
585
+ self._short_name = short_name
586
+ self._relationships = {}
587
+ self._annotations: list[Expression|Relationship] = []
588
+ self._overloads = overloads
589
+ self._is_unresolved = is_unresolved
590
+ for f in self._fields:
591
+ if f._source is None:
592
+ f._source = self._source
593
+
594
+ def _dot(self, name: str) -> Relationship:
595
+ field_type = self._fields[-1].type
596
+ if name.lower() in self._relationships:
597
+ return self._relationships[name.lower()]
598
+ if field_type is CoreConcepts["Any"]:
599
+ rel = Property(fields=[
600
+ Field(self._fields[-1].name, field_type),
601
+ Field(name, CoreConcepts["Any"])
602
+ ], short_name=name, model=self._model, is_unresolved=True)
603
+ self._relationships[name.lower()] = rel
604
+ return rel
605
+ return field_type._dot(name)
606
+
607
+ def __getattr__(self, item):
608
+ if item.startswith("_"):
609
+ return object.__getattribute__(self, item)
610
+ op = self._fields[-1].type._dot(item)
611
+ return Chain(self, op)
612
+
613
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
614
+ return Expression(self, list(args), kwargs, root=self)
615
+
616
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
617
+ resolved = _find_field(self._fields, field)
618
+ if not resolved:
619
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._fields]}")
620
+ return FieldRef(self, field, *resolved)
621
+
622
+ def alt(self, reading_str: str) -> Reading:
623
+ reading = Reading(self._model, self, reading_str)
624
+ self._readings.append(reading)
625
+ return reading
626
+
627
+ def annotate(self, *annos:Expression|Relationship) -> Relationship:
628
+ self._annotations.extend(annos)
629
+ return self
630
+
631
+ def _to_concept(self) -> Concept|None:
632
+ return self._fields[-1].type
633
+
634
+ def __dir__(self):
635
+ default = set(super().__dir__())
636
+ return sorted(default.union(self._fields[-1].type.__dir__()))
637
+
638
+ def to_df(self) -> DataFrame:
639
+ refs = [self[ix] for ix in range(len(self._fields))]
640
+ return self._model.select(*refs).where(self(*refs)).to_df()
641
+
642
+ def inspect(self):
643
+ print(self.to_df())
644
+
645
+ #------------------------------------------------------
646
+ # Reading
647
+ #------------------------------------------------------
648
+
649
+ class Reading(Relationship):
650
+ def __init__(self, model: Model, relationship: Relationship, reading_str: str, reading_fields: list[Field] = [], reading_parts: list[str|int] = []):
651
+ Variable.__init__(self, model)
652
+ self._relationship = relationship
653
+ if not reading_fields or not reading_parts:
654
+ parsed_fields, parsed_parts = Reading.parse(model, reading_str, reading_fields, _source=self)
655
+ reading_fields = reading_fields or parsed_fields
656
+ reading_parts = reading_parts or parsed_parts
657
+ # make sure we can find all these fields in the base relationship
658
+ index_map = {}
659
+ matched = []
660
+ for field_i, field in enumerate(reading_fields):
661
+ found = next(((i, f) for i, f in enumerate(relationship._fields) if f._match(field)), None)
662
+ if not found:
663
+ raise ValueError(f"Field {field.name}:{field.type._name} not found in relationship with fields {[f'{f.name}:{f.type._name}' for f in relationship._fields]}")
664
+ matched.append(found[1])
665
+ index_map[field_i] = found[0]
666
+ self._fields = matched
667
+ self._reading = reading_str
668
+ self._short_name = ""
669
+ self._parts:list[str|int] = [index_map.get(p, p) for p in reading_parts]
670
+ self._relationships = {}
671
+ self._annotations: list[Expression|Relationship] = []
672
+
673
+ #------------------------------------------------------
674
+ # Parse
675
+ #------------------------------------------------------
676
+
677
+ @classmethod
678
+ def parse(cls, model: Model, reading: str, known_fields: list[Field], _source=None) -> Tuple[list[Field], list[str|int]]:
679
+ # match <class 'foo'> which is the serialized form of a python type mistakenly passed instead of a concept
680
+ class_pattern = re.compile(r'<class \'(.*?)\'>')
681
+ match = class_pattern.search(reading)
682
+ if match:
683
+ extra: list = [source(_source)]
684
+ if match.group(1) in pytype_to_concept_name:
685
+ concept = pytype_to_concept_name[match.group(1)]
686
+ extra.append(f" Did you mean to use [cyan]relationalai.semantics.{concept}[/cyan]?")
687
+ exc("Invalid field type", f"The type '{match.group(1)}' is not a valid Concept.", extra)
688
+
689
+ # {Type} or {name:Type}, where Type can include Number(38,14)
690
+ pattern = re.compile(r'\{([a-zA-Z0-9_.#]+(?:(?:\([0-9]+,[0-9]+\))(?:[#0-9]+)?)?)(?::\s*([a-zA-Z0-9_.]+(?:\([0-9]+,[0-9]+\))?))?\}')
691
+
692
+ namer = Namer()
693
+ fields: list[Field] = []
694
+ parts: list[str|int] = []
695
+
696
+ last_end = 0
697
+ is_old_style = True
698
+ for m in pattern.finditer(reading):
699
+ # literal chunk before this match
700
+ parts.append(reading[last_end:m.start()])
701
+ field_name, field_type_name = m.group(1), m.group(2)
702
+ field_type_id = None
703
+ if "#" in field_name:
704
+ temp_name = field_type_name
705
+ field_type_name, field_type_id = field_name.split("#")
706
+ field_name = temp_name or sanitize(field_type_name.lower())
707
+ is_old_style = False
708
+
709
+ # if we don't have a type_name, then only a type was provided
710
+ if not field_type_name:
711
+ field_type_name = field_name
712
+ field_name = sanitize(field_name.lower())
713
+
714
+ field_name = namer.get_name(field_name)
715
+ field_type = Concept.lookup_by_id.get(int(field_type_id)) \
716
+ if field_type_id \
717
+ else _find_concept(model, field_type_name, extra_types=[f.type for f in known_fields])
718
+ if field_type is None:
719
+ exc("Unknown Concept", f"The Concept '{field_type_name}' couldn't be found in the model", [
720
+ source(_source),
721
+ ])
722
+
723
+ fields.append(Field(field_name, field_type))
724
+ parts.append(len(fields) - 1)
725
+ last_end = m.end()
726
+
727
+ # trailing literal after the final match
728
+ if(last_end < len(reading)):
729
+ parts.append(reading[last_end:])
730
+
731
+ if is_old_style and fields:
732
+ correct = []
733
+ for part in parts:
734
+ if isinstance(part, int):
735
+ if fields[part].type._name.lower() != fields[part].name:
736
+ correct.append(f"{{{fields[part].type._name}:{fields[part].name}}}")
737
+ else:
738
+ correct.append(f"{{{fields[part].type._name}}}")
739
+ else:
740
+ correct.append(part)
741
+ warn("Deprecated format", "Plain strings for Relationships/Properties is deprecated. Use an f-string instead.", [
742
+ source(_source),
743
+ f'For example: [cyan]f"{"".join(correct)}"',
744
+ ])
745
+
746
+ return fields, parts
747
+
748
+ #------------------------------------------------------
749
+ # Property
750
+ #------------------------------------------------------
751
+
752
+ class Property(Relationship):
753
+ pass
754
+
755
+ #------------------------------------------------------
756
+ # Literal
757
+ #------------------------------------------------------
758
+
759
+ class Literal(Variable):
760
+ def __init__(self, value: Any, model:Model, type: Concept|None = None):
761
+ super().__init__(model)
762
+ self._value = value
763
+ self._type = type if type is not None else self._get_type(value)
764
+
765
+ def _to_concept(self) -> Concept:
766
+ return self._type
767
+
768
+ @staticmethod
769
+ def _get_type(value: Any) -> Concept:
770
+ if type(value) is float and (math.isnan(value) or math.isinf(value)):
771
+ return CoreConcepts["Float"]
772
+ if type(value) is float:
773
+ fractional_digits = 0 if math.isnan(value) else min(len(str(value).split(".")[1]), 14)
774
+ return CoreConcepts["Decimal"].size(38, fractional_digits) # type: ignore
775
+ if type(value) is decimal.Decimal:
776
+ str_value = format(value, 'f')
777
+ if '.' in str_value:
778
+ _, fractional_part = str_value.split('.')
779
+ scale = min(len(fractional_part), 14)
780
+ else:
781
+ scale = 14
782
+ return CoreConcepts["Decimal"].size(38, scale) # type: ignore
783
+ if type(value) in pytype_to_concept_name:
784
+ return CoreConcepts[pytype_to_concept_name[type(value)]]
785
+ else:
786
+ raise NotImplementedError(f"Literal type not implemented for value type: {type(value)}")
787
+
788
+ #------------------------------------------------------
789
+ # Chain
790
+ #------------------------------------------------------
791
+
792
+ class Chain(Variable):
793
+ def __init__(self, start: Chain|Concept|Relationship|DerivedColumn|Table|Ref|FieldRef|Expression, next: Relationship, is_ref = False):
794
+ super().__init__(start._model)
795
+ self._start = start
796
+ self._next = next
797
+ self._is_ref = is_ref
798
+
799
+ def __getattr__(self, item):
800
+ if item.startswith("_"):
801
+ return object.__getattribute__(self._next, item)
802
+ next_rel = self._next._dot(item)
803
+ return Chain(self, next_rel)
804
+
805
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
806
+ last = self._next
807
+ assert not isinstance(last, Chain)
808
+ if len(last._fields) > len(args):
809
+ return Expression(last, [self._start, *args], kwargs, root=self)
810
+ return Expression(last, [*args], kwargs, root=self)
811
+
812
+ def to_dict_key(self) -> int|tuple:
813
+ if self._is_ref:
814
+ return self._id
815
+ return (self._start.to_dict_key(), self._next.to_dict_key())
816
+
817
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
818
+ resolved = _find_field(self._next._fields, field)
819
+ if not resolved:
820
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._next._fields]}")
821
+ return FieldRef(self, field, *resolved)
822
+
823
+ def ref(self) -> Chain:
824
+ return Chain(self._start, self._next, is_ref=True)
825
+
826
+ def alt(self, reading_str: str) -> Reading:
827
+ return self._next.alt(reading_str)
828
+
829
+ def annotate(self, *annos:Expression|Relationship) -> Relationship:
830
+ return self._next.annotate(*annos)
831
+
832
+ def _to_concept(self) -> Concept | None:
833
+ return self._next._to_concept()
834
+
835
+ #------------------------------------------------------
836
+ # Expression
837
+ #------------------------------------------------------
838
+
839
+ class Expression(Variable):
840
+ def __init__(self, op: Relationship|Concept, args: Sequence[Value], kwargs: dict|None = None, root: Chain|Relationship|Concept|None = None):
841
+ super().__init__(op._model)
842
+ self._op = op
843
+ self._has_output = isinstance(op, Concept) or (any(not f.is_input for f in op._fields))
844
+ self._auto_filled = False
845
+ self._root = root
846
+
847
+ # clean args
848
+ clean_args = []
849
+ for arg in args:
850
+ if isinstance(arg, (ModelEnum, Field, TupleVariable)):
851
+ pass
852
+ elif isinstance(arg, TableSchema):
853
+ exc("Invalid argument", "Cannot use a schema as an argument to an Expression.", [source(self)])
854
+ elif not isinstance(arg, Variable):
855
+ arg = Literal(arg, self._model)
856
+ elif isinstance(arg, (Match, Fragment)):
857
+ arg._columns
858
+ clean_args.append(arg)
859
+ self._args = clean_args
860
+
861
+ self._kwargs = kwargs or {}
862
+ # clean kwargs
863
+ for k, v in self._kwargs.items():
864
+ if not isinstance(v, Variable):
865
+ self._kwargs[k] = Literal(v, self._model)
866
+
867
+ if isinstance(op, Relationship):
868
+ op_len = len(op._fields)
869
+ arg_len = len(self._args)
870
+ if op_len - arg_len == 1:
871
+ self._args.append(op._fields[-1].type.ref(name=op._fields[-1].name))
872
+ self._auto_filled = True
873
+ elif op_len != arg_len:
874
+ dir = "Too few" if arg_len < op_len - 1 else "Too many"
875
+ exc(f"{dir} args", f"{op._short_name or 'Relationship'} requires {op_len - 1}-{op_len} arguments but got {arg_len}", [
876
+ source(self),
877
+ ])
878
+
879
+ def __getattr__(self, item):
880
+ if item.startswith("_"):
881
+ return object.__getattribute__(self, item)
882
+ rel = self._op._dot(item)
883
+ return Chain(self, rel)
884
+
885
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
886
+ if not isinstance(self._op, Relationship):
887
+ raise TypeError(f"Cannot index into Expression with non-Relationship: {self._op}")
888
+ resolved = _find_field(self._op._fields, field)
889
+ if not resolved:
890
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._op._fields]}")
891
+ return FieldRef(self, field, *resolved)
892
+
893
+ def _to_concept(self) -> Concept | None:
894
+ return self._args[-1]._to_concept() if self._args else self._op._to_concept()
895
+
896
+ #------------------------------------------------------
897
+ # New
898
+ #------------------------------------------------------
899
+
900
+ class New(Expression):
901
+ def __init__(self, concept: Concept, args: Sequence[Any], kwargs: dict[str, Any], identity_only: bool = False):
902
+ clean_args = []
903
+ row_ids = []
904
+ for arg in args:
905
+ if isinstance(arg, TableSchema):
906
+ row_ids.append(arg._table)
907
+ lower_case_kwargs = {k.lower() for k, v in kwargs.items()}
908
+ # add any keyword args that aren't already in kwargs
909
+ for col in arg.get_columns():
910
+ if col._short_name and col._short_name.lower() not in lower_case_kwargs:
911
+ kwargs[col._short_name] = col(arg._table)
912
+ else:
913
+ clean_args.append(arg)
914
+ for k in list(kwargs.keys()):
915
+ concept._dot(k)
916
+ super().__init__(concept, clean_args, kwargs)
917
+ self._identity_only = identity_only
918
+ self._row_ids = row_ids
919
+
920
+ #------------------------------------------------------
921
+ # FilterBy
922
+ #------------------------------------------------------
923
+
924
+ class FilterBy(Expression):
925
+ def __init__(self, item: Concept|Ref, kwargs: dict[str, Any]):
926
+ concept = item._concept if isinstance(item, Ref) else item
927
+ super().__init__(concept, [item], kwargs)
928
+
929
+ #------------------------------------------------------
930
+ # FieldRef
931
+ #------------------------------------------------------
932
+
933
+ class FieldRef(Variable):
934
+ def __init__(self, root: Chain|Relationship|Expression, field: str|int|Concept, resolved: Field, resolved_ix:int):
935
+ super().__init__(root._model)
936
+ self._root = root
937
+ self._field = field
938
+ self._resolved = resolved
939
+ self._resolved_ix = resolved_ix
940
+
941
+ def _to_concept(self) -> Concept:
942
+ return self._resolved.type
943
+
944
+ def __getattr__(self, item):
945
+ if item.startswith("_"):
946
+ return object.__getattribute__(self, item)
947
+ return Chain(self, self._resolved.type._dot(item))
948
+
949
+ def to_dict_key(self) -> tuple:
950
+ return (self._root.to_dict_key(), self._resolved._id)
951
+
952
+
953
+ #------------------------------------------------------
954
+ # MetaRef
955
+ #------------------------------------------------------
956
+
957
+ class MetaRef(Variable):
958
+ def __init__(self, target: Concept|Relationship|Field):
959
+ model = target._model if isinstance(target, DSLBase) else target.type._model
960
+ super().__init__(model)
961
+ self._target = target
962
+
963
+ #------------------------------------------------------
964
+ # TupleVariable
965
+ #------------------------------------------------------
966
+
967
+ class TupleVariable:
968
+ def __init__(self, items: Sequence[Value|Distinct]):
969
+ self._items = list(items)
970
+ self._source = SourcePos.new()
971
+
972
+ #------------------------------------------------------
973
+ # AsBool
974
+ #------------------------------------------------------
975
+
976
+ class AsBool(Variable):
977
+ def __init__(self, item: Variable):
978
+ super().__init__(item._model)
979
+ self._item = item
980
+
981
+ def _to_concept(self) -> Concept:
982
+ return CoreConcepts["Boolean"]
983
+
984
+ #------------------------------------------------------
985
+ # Alias
986
+ #------------------------------------------------------
987
+
988
+ class Alias(Variable):
989
+ def __init__(self, source: Variable, alias: str):
990
+ super().__init__(source._model)
991
+ self._source = source
992
+ self._alias = alias
993
+
994
+ def _to_concept(self) -> Concept|None:
995
+ return self._source._to_concept()
996
+
997
+ def __format__(self, format_spec: str) -> str:
998
+ if isinstance(self._source, Concept):
999
+ if format_spec:
1000
+ exc("Invalid alias", f"Alias already specifies an alias for this concept, you can remove `:{format_spec}`")
1001
+ return self._source.__format__(self._alias)
1002
+ return super().__format__(format_spec)
1003
+
1004
+ #------------------------------------------------------
1005
+ # Match
1006
+ #------------------------------------------------------
1007
+
1008
+ class Match(DerivedTable):
1009
+ def __init__(self, model:Model, *items: Statement):
1010
+ super().__init__(model)
1011
+ t = type(self)
1012
+ self._items = [
1013
+ x
1014
+ for item in items
1015
+ for x in (item._items if (type(item) is t and isinstance(item, Match)) else (item,))
1016
+ ] # flatten nested Matches/Unions
1017
+
1018
+ def _get_cols(self) -> list[DerivedColumn]:
1019
+ return [DerivedColumn(self, i) for i in range(self._arg_count())]
1020
+
1021
+ def _arg_count(self) -> int:
1022
+ counts = []
1023
+ for item in self._items:
1024
+ if isinstance(item, DerivedTable):
1025
+ try:
1026
+ counts.append(len(item._columns))
1027
+ except ValueError:
1028
+ counts.append(0)
1029
+ else:
1030
+ # Expressions with no output and Not are filters, do not count as returning values
1031
+ is_filter = isinstance(item, Not) or (isinstance(item, Expression) and not item._has_output)
1032
+ counts.append(0 if is_filter else 1)
1033
+ if not counts:
1034
+ return 0
1035
+ first = counts[0]
1036
+ if any(c != first for c in counts[1:]):
1037
+ exc("Inconsistent branches",
1038
+ f"All branches in a {self.__class__.__name__} must have the same number of returned values",
1039
+ [source(self)])
1040
+ return first
1041
+
1042
+ def __getattr__(self, item) -> Chain:
1043
+ if item.startswith("_"):
1044
+ return object.__getattribute__(self, item)
1045
+ return getattr(self._columns[-1], item)
1046
+
1047
+ #------------------------------------------------------
1048
+ # Union
1049
+ #------------------------------------------------------
1050
+
1051
+ class Union(Match):
1052
+ def __init__(self, model:Model, *items: Value):
1053
+ super().__init__(model, *items)
1054
+
1055
+ #------------------------------------------------------
1056
+ # Not
1057
+ #------------------------------------------------------
1058
+
1059
+ class Not(DSLBase):
1060
+ def __init__(self, *items: Value, model:Model):
1061
+ super().__init__(model)
1062
+ self._items = items
1063
+
1064
+ def __or__(self, other) -> Match:
1065
+ return Match(self._model, self, other)
1066
+
1067
+ def __and__(self, other) -> Fragment:
1068
+ if isinstance(other, Fragment):
1069
+ new = other.where()
1070
+ new._where.insert(0, self)
1071
+ return new
1072
+ return self._model.where(self, other)
1073
+
1074
+ #------------------------------------------------------
1075
+ # Distinct
1076
+ #------------------------------------------------------
1077
+
1078
+ class Distinct(DSLBase):
1079
+ def __init__(self, *items: Value, model:Model):
1080
+ super().__init__(model)
1081
+ self._items = items
1082
+
1083
+ #------------------------------------------------------
1084
+ # Aggregate
1085
+ #------------------------------------------------------
1086
+
1087
+ class Group(DSLBase):
1088
+ def __init__(self, *args: Value):
1089
+ model = [arg._model for arg in args if isinstance(arg, Variable)][0] if args else None
1090
+ super().__init__(model) # type: ignore
1091
+ self._args = list(args)
1092
+
1093
+ def _extend(self, args: Sequence[Value]) -> Group:
1094
+ new = Group(*self._args)
1095
+ new._args.extend(args)
1096
+ return new
1097
+
1098
+ def _clone(self):
1099
+ return Group(*self._args)
1100
+
1101
+ class Aggregate(Variable):
1102
+ def __init__(self, op: Relationship, *args: Value|Distinct, check_args: bool = True, distinct: bool = False):
1103
+ model = self._find_model(args) or op._model
1104
+ super().__init__(model)
1105
+ self._op = op
1106
+ self._where = Fragment(model)
1107
+ self._group = Group()
1108
+ self._args: list[Value] = []
1109
+ self._projection_args: list[Value] = []
1110
+ self._distinct = distinct
1111
+ if check_args:
1112
+ # unwrap distinct if present
1113
+ if any(isinstance(arg, Distinct) for arg in args):
1114
+ if len(args) != 1:
1115
+ exc("Invalid distinct", "Distinct must be applied to all arguments", [source(self)])
1116
+ assert isinstance(args[0], Distinct)
1117
+ args = args[0]._items
1118
+ self._distinct = True
1119
+
1120
+ args = cast(tuple[Value], args)
1121
+
1122
+ num_inputs = sum(f.is_input for f in op._fields)
1123
+ if len(args) < num_inputs:
1124
+ need = [f.name for f in op._fields if f.is_input][len(args):]
1125
+ exc("Missing argument",
1126
+ f"`{op._short_name or 'Relationship'}(..)` is missing: {', '.join(need)}",
1127
+ [source(self)])
1128
+
1129
+ self._projection_args = list(args[:-num_inputs] if num_inputs else args)
1130
+ supplied = iter(args[-num_inputs:] if num_inputs else [])
1131
+
1132
+ self._args = [
1133
+ (next(supplied) if f.is_input else f.type.ref(f.name))
1134
+ for f in op._fields
1135
+ ]
1136
+
1137
+ def _find_model(self, args: Sequence[Value|Distinct|TupleVariable]) -> Model|None:
1138
+ for arg in args:
1139
+ if isinstance(arg, (Variable, Distinct)):
1140
+ return arg._model
1141
+ elif isinstance(arg, TupleVariable):
1142
+ for item in arg._items:
1143
+ if isinstance(item, (Variable, Distinct)):
1144
+ return item._model
1145
+ return None
1146
+
1147
+
1148
+ def where(self, *args: Value) -> Aggregate:
1149
+ new = self._clone()
1150
+ new._where = new._where.where(*args)
1151
+ return new
1152
+
1153
+ def per(self, *args: Value) -> Aggregate:
1154
+ new = self._clone()
1155
+ new._group = new._group._extend(args)
1156
+ return new
1157
+
1158
+ def _clone(self):
1159
+ agg = Aggregate(self._op, check_args=False)
1160
+ agg._args = self._args
1161
+ agg._projection_args = self._projection_args
1162
+ agg._where = self._where
1163
+ agg._group = self._group
1164
+ agg._distinct = self._distinct
1165
+ return agg
1166
+
1167
+ #------------------------------------------------------
1168
+ # Data
1169
+ #------------------------------------------------------
1170
+
1171
+ class Data(Table):
1172
+ def __init__(self, df:DataFrame, model:Model):
1173
+ schema = {}
1174
+ for col in df.columns:
1175
+ _type = df[col].dtype
1176
+ if pd.api.types.is_datetime64_any_dtype(_type):
1177
+ col_type = "DateTime"
1178
+ elif pd.api.types.is_object_dtype(_type) and self._is_date_column(df[col]):
1179
+ col_type = "Date"
1180
+ else:
1181
+ col_type = pytype_to_concept_name.get(_type, "Any")
1182
+ if isinstance(col, int):
1183
+ col = f"col{col}"
1184
+ schema[col] = CoreConcepts[col_type]
1185
+ super().__init__("Data", schema, model)
1186
+ self._data = df
1187
+
1188
+ def _is_date_column(self, col) -> bool:
1189
+ sample = col.dropna()
1190
+ if sample.empty:
1191
+ return False
1192
+ sample_value = sample.iloc[0]
1193
+ return isinstance(sample_value, dt.date) and not isinstance(sample_value, dt.datetime)
1194
+
1195
+ @staticmethod
1196
+ def raw_to_df(data: DataFrame | list[tuple] | list[dict], columns:list[str]|None) -> DataFrame:
1197
+ if isinstance(data, DataFrame):
1198
+ return data
1199
+ if not data:
1200
+ return DataFrame()
1201
+ if isinstance(data, list):
1202
+ if isinstance(data[0], tuple):
1203
+ # Named tuple check
1204
+ if hasattr(data[0], '_fields'):
1205
+ return DataFrame([t._asdict() for t in data]) #type: ignore
1206
+ return DataFrame(data, columns=columns)
1207
+ elif isinstance(data[0], dict):
1208
+ return DataFrame(data)
1209
+ raise TypeError(f"Cannot convert {type(data)} to DataFrame. Use DataFrame, list of tuples, or list of dicts.")
1210
+
1211
+ #------------------------------------------------------
1212
+ # Enum
1213
+ #------------------------------------------------------
1214
+
1215
+ class ModelEnumMeta(EnumMeta):
1216
+ _concept: Concept
1217
+ def __setattr__(self, name: str, value: Any) -> None:
1218
+ if name.startswith("_") or isinstance(value, self):
1219
+ super().__setattr__(name, value)
1220
+ elif isinstance(value, (Relationship, Reading)):
1221
+ setattr(self._concept, name, value)
1222
+ else:
1223
+ raise AttributeError(f"Cannot set attribute {name} on {type(self).__name__}")
1224
+
1225
+ def __format__(self, format_spec: str) -> str:
1226
+ return format(self._concept, format_spec)
1227
+
1228
+ class ModelEnum(Enum, metaclass=ModelEnumMeta):
1229
+ _model:Model
1230
+
1231
+ def __init__(self, *args) -> None:
1232
+ super().__init__(*args)
1233
+ self._source = SourcePos.new()
1234
+
1235
+ def _compile_lookup(self):
1236
+ concept = getattr(self.__class__, "_concept")
1237
+ return concept.to_identity(name=self.name)
1238
+
1239
+ @classmethod
1240
+ def lookup(cls, value:Variable|str):
1241
+ concept = cls._concept
1242
+ return concept.to_identity(name=value)
1243
+
1244
+ # Python 3.10 doesn't correctly populate __members__ by the time it calls
1245
+ # __init_subclass__, so we need to initialize the members lazily when we
1246
+ # encounter the enum for the first time.
1247
+ @classmethod
1248
+ def _init_members(cls):
1249
+ if cls._has_inited_members:
1250
+ return
1251
+ c = cls._concept
1252
+ # Add the name and value attributes to the hashes we create for the enum
1253
+ members = [
1254
+ c.new(name=name, value=value.value)
1255
+ for name, value in cls.__members__.items()
1256
+ ]
1257
+ cls._model.define(*members)
1258
+ cls._has_inited_members = True
1259
+
1260
+ def __format__(self, format_spec: str) -> str:
1261
+ return format(self._concept, format_spec)
1262
+
1263
+ def create_enum_class(model: Model):
1264
+ class AttachedModelEnum(ModelEnum):
1265
+ def __init_subclass__(cls, **kwargs):
1266
+ super().__init_subclass__(**kwargs)
1267
+ # this is voodoo black magic that is doing meta meta programming where
1268
+ # we are plugging into anytime a new subtype of this class is created
1269
+ # and then creating a concept to represent the enum. This happens both
1270
+ # when you do `class Foo(Enum)` and when you do `Enum("Foo", [a, b, c])`
1271
+ cls._model = model
1272
+ c = model.Concept(
1273
+ cls.__name__,
1274
+ extends=[CoreConcepts["Enum"]],
1275
+ identify_by={"name": CoreConcepts["String"]}
1276
+ )
1277
+ model.enums.append(cls)
1278
+ model.enums_index[cls.__name__] = cls
1279
+ cls._has_inited_members = False
1280
+ cls._concept = c
1281
+
1282
+ return AttachedModelEnum
1283
+
1284
+ #------------------------------------------------------
1285
+ # Value
1286
+ #------------------------------------------------------
1287
+
1288
+ Value = Variable|Primitive|Field|TupleVariable
1289
+
1290
+ #------------------------------------------------------
1291
+ # Statement
1292
+ #------------------------------------------------------
1293
+
1294
+ Statement = Value | Group | Not | Distinct | Aggregate
1295
+ """Union of statement types."""
1296
+
1297
+ StatementAndSchema = Statement | TableSchema
1298
+
1299
+ #------------------------------------------------------
1300
+ # Fragment
1301
+ #------------------------------------------------------
1302
+
1303
+ @include_in_docs
1304
+ class Fragment(DerivedTable):
1305
+ """Composable chunk of a query with select/where/define state.
1306
+
1307
+ Parameters
1308
+ ----------
1309
+ model : Model
1310
+ The semantic model that provides type information and stores any
1311
+ definitions produced by this fragment.
1312
+ parent : Fragment, optional
1313
+ An existing fragment to inherit selection and filter state from when
1314
+ building chained queries.
1315
+ """
1316
+
1317
+ def __init__(self, model:Model, parent:Fragment|None=None):
1318
+ super().__init__(model)
1319
+ self._id = next(_global_id)
1320
+ self._select = []
1321
+ self._where = []
1322
+ self._require = []
1323
+ self._define = []
1324
+ self._order_by = []
1325
+ self._limit = 0
1326
+ self._model = model
1327
+ self._into:Optional[Table] = None
1328
+ self._is_into_update = False
1329
+ self._has_executed = False
1330
+ assert self._model, "Fragment must have a model"
1331
+
1332
+ self._parent = parent
1333
+ # self._source = runtime_env.get_source_pos()
1334
+ self._meta = {}
1335
+ self._annotations = []
1336
+ if parent is not None:
1337
+ self._select.extend(parent._select)
1338
+ self._where.extend(parent._where)
1339
+ self._require.extend(parent._require)
1340
+ self._define.extend(parent._define)
1341
+ self._order_by.extend(parent._order_by)
1342
+ self._limit = parent._limit
1343
+
1344
+
1345
+ def _add_items(self, items:Sequence[Statement], to_attr:list[Statement]):
1346
+ # TODO: ensure that you are _either_ a select, require, or then
1347
+ # not a mix of them
1348
+ model = self._model
1349
+
1350
+ # remove any existing rules that this consumes
1351
+ for item in itertools.chain(items, [self._parent]):
1352
+ if isinstance(item, Fragment) and item._is_effect():
1353
+ model._remove_rule(item)
1354
+
1355
+ to_attr.extend(items)
1356
+ if self._is_effect():
1357
+ model._add_rule(self)
1358
+
1359
+ return self
1360
+
1361
+ #------------------------------------------------------
1362
+ # Select arg handling
1363
+ #------------------------------------------------------
1364
+
1365
+ def _check_select_args(self, args:Sequence[StatementAndSchema]) -> Sequence[Statement]:
1366
+ clean_args = []
1367
+ for arg in args:
1368
+ # If you select x > y, treat that as AsBool(x > y)
1369
+ if isinstance(arg, Expression) and not arg._has_output:
1370
+ clean_args.append(AsBool(arg))
1371
+ elif isinstance(arg, TableSchema):
1372
+ for col in arg.get_columns():
1373
+ clean_args.append(col(arg._table))
1374
+ else:
1375
+ clean_args.append(arg)
1376
+ return clean_args
1377
+
1378
+ #------------------------------------------------------
1379
+ # Core API
1380
+ #------------------------------------------------------
1381
+
1382
+ def where(self, *args: Statement) -> Fragment:
1383
+ f = Fragment(self._model, parent=self)
1384
+ return f._add_items(args, f._where)
1385
+
1386
+ def select(self, *args: StatementAndSchema) -> Fragment:
1387
+ f = Fragment(self._model, parent=self)
1388
+ return f._add_items(self._check_select_args(args), f._select)
1389
+
1390
+ def require(self, *args: Statement) -> Fragment:
1391
+ f = Fragment(self._model, parent=self)
1392
+ return f._add_items(args, f._require)
1393
+
1394
+ def define(self, *args: Statement) -> Fragment:
1395
+ f = Fragment(self._model, parent=self)
1396
+ return f._add_items(args, f._define)
1397
+
1398
+ if not TYPE_CHECKING:
1399
+ def order_by(self, *args: Any) -> Fragment:
1400
+ exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
1401
+ source(self),
1402
+ "You can use [cyan]`relationalai.semantics.std.aggregates.rank`[/cyan] and select it as the first column as a temporary substitute.",
1403
+ ])
1404
+ f = Fragment(self._model, parent=self)
1405
+ return f._add_items(args, f._order_by)
1406
+
1407
+ def limit(self, n:int) -> Fragment:
1408
+ exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
1409
+ source(self),
1410
+ "You can use [cyan]`relationalai.semantics.std.aggregates.limit`[/cyan] in a where clause as a temporary substitute.",
1411
+ ])
1412
+ f = Fragment(self._model, parent=self)
1413
+ f._limit = n
1414
+ return f
1415
+
1416
+ # def meta(self, **kwargs: Any) -> Fragment:
1417
+ # self._meta.update(kwargs)
1418
+ # return self
1419
+
1420
+ def annotate(self, *annos:Expression|Relationship) -> Fragment:
1421
+ self._annotations.extend(annos)
1422
+ return self
1423
+
1424
+ #------------------------------------------------------
1425
+ # into
1426
+ #------------------------------------------------------
1427
+
1428
+ def into(self, table: Table, update=False) -> Fragment:
1429
+ f = Fragment(self._model, parent=self)
1430
+ f._into = table
1431
+ f._is_into_update = update
1432
+ self._model.exports.add(f)
1433
+ return f
1434
+
1435
+ #------------------------------------------------------
1436
+ # Execution
1437
+ #------------------------------------------------------
1438
+
1439
+ def exec(self):
1440
+ if self._has_executed:
1441
+ return
1442
+ if self._into is None:
1443
+ exc("Cannot execute", "Query must have an 'into' table specified to execute.", [source(self)])
1444
+ from relationalai.shims.executor import execute
1445
+ self._has_executed = True
1446
+ return execute(self, self._model, export_to=self._into._name, update=self._is_into_update)
1447
+
1448
+ def to_df(self):
1449
+ from relationalai.shims.executor import execute
1450
+ return execute(self, self._model)
1451
+
1452
+ def inspect(self):
1453
+ print(self.to_df())
1454
+
1455
+ #------------------------------------------------------
1456
+ # helpers
1457
+ #------------------------------------------------------
1458
+
1459
+ def _is_effect(self) -> bool:
1460
+ return bool(self._define or self._require)
1461
+
1462
+ def _is_where_only(self) -> bool:
1463
+ return not self._select and not self._define and not self._require and not self._order_by
1464
+
1465
+ #------------------------------------------------------
1466
+ # And/Or
1467
+ #------------------------------------------------------
1468
+
1469
+ def __or__(self, other) -> Match:
1470
+ return Match(self._model, self, other)
1471
+
1472
+ def __and__(self, other) -> Fragment:
1473
+ if not isinstance(other, Fragment):
1474
+ other = Fragment(self._model).where(other)
1475
+ if self._is_where_only() and other._is_where_only():
1476
+ return self.where(*other._where)
1477
+ elif self._is_where_only():
1478
+ return other.where(*self._where)
1479
+ elif other._is_where_only():
1480
+ return self.where(*other._where)
1481
+ else:
1482
+ raise Exception("Cannot AND two non-where-only fragments")
1483
+
1484
+ #------------------------------------------------------
1485
+ # DerivedTable cols
1486
+ #------------------------------------------------------
1487
+
1488
+ def _get_cols(self):
1489
+ return [DerivedColumn.from_value(self, i, col) for i, col in enumerate(self._select)]
1490
+
1491
+ #------------------------------------------------------
1492
+ # Marterialize
1493
+ #------------------------------------------------------
1494
+
1495
+ def to_metamodel(self):
1496
+ m = self._model
1497
+ return m._compiler.compile(self)
1498
+
1499
+ #------------------------------------------------------
1500
+ # Model
1501
+ #------------------------------------------------------
1502
+
1503
+ @include_in_docs
1504
+ class Model:
1505
+ """Class representing a semantic model.
1506
+
1507
+ Parameters
1508
+ ----------
1509
+ name : str, optional
1510
+ Name of the model.
1511
+ exclude_core : bool, optional
1512
+ If True, the core library will not be included by default.
1513
+ is_library : bool, optional
1514
+ If True, this model is a library and will not be added to the global
1515
+ list of models.
1516
+
1517
+ Examples
1518
+ --------
1519
+ Create a model object:
1520
+
1521
+ >>> from relationalai.semantics import Model
1522
+ >>> model = Model(name="MyModel")
1523
+ """
1524
+ all_models:list[Model] = []
1525
+
1526
+ def __init__(self, name: str = "", exclude_core: bool = False, is_library: bool = False):
1527
+ self.name = name
1528
+ self.defines:KeyedSet[Fragment] = KeyedSet(dsl_key)
1529
+ self.requires:KeyedSet[Fragment] = KeyedSet(dsl_key)
1530
+ self.exports:KeyedSet[Fragment] = KeyedSet(dsl_key)
1531
+ self.libraries = []
1532
+ self.concepts: list[Concept] = []
1533
+ self.tables: list[Table] = []
1534
+ self.relationships: list[Relationship] = []
1535
+ self.enums: list[Type[ModelEnum]] = []
1536
+ self.concept_index: dict[str, Concept] = {}
1537
+ self.table_index: dict[str, Table] = {}
1538
+ self.relationship_index: dict[str, Relationship] = {}
1539
+ self.enums_index: dict[str, Type[ModelEnum]] = {}
1540
+ self._source = SourcePos.new()
1541
+
1542
+ self.Enum = create_enum_class(self)
1543
+
1544
+ if not is_library:
1545
+ Model.all_models.append(self)
1546
+
1547
+ self.__compiler = None
1548
+ if not exclude_core:
1549
+ self.libraries.append(CoreLibrary)
1550
+
1551
+ #------------------------------------------------------
1552
+ # Internal
1553
+ #------------------------------------------------------
1554
+
1555
+ @property
1556
+ def _compiler(self):
1557
+ if not self.__compiler:
1558
+ from .front_compiler import FrontCompiler
1559
+ self.__compiler = FrontCompiler(self)
1560
+ return self.__compiler
1561
+
1562
+ def _find_concept(self, name: str) -> Concept|None:
1563
+ if name in self.concept_index:
1564
+ return self.concept_index[name]
1565
+ if name in self.table_index:
1566
+ return self.table_index[name]
1567
+ for lib in self.libraries:
1568
+ found = lib._find_concept(name)
1569
+ if found is not None:
1570
+ return found
1571
+
1572
+ def _remove_rule(self, fragment: Fragment) -> None:
1573
+ if fragment._define and fragment in self.defines:
1574
+ self.defines.remove(fragment)
1575
+ elif fragment._require and fragment in self.requires:
1576
+ self.requires.remove(fragment)
1577
+ else:
1578
+ raise ValueError("Fragment must have either define or require clauses to be removed as a rule")
1579
+
1580
+ def _add_rule(self, fragment: Fragment) -> None:
1581
+ if fragment._define and fragment not in self.defines:
1582
+ self.defines.add(fragment)
1583
+ elif fragment._require and fragment not in self.requires:
1584
+ self.requires.add(fragment)
1585
+ else:
1586
+ raise ValueError("Fragment must have either define or require clauses to be added as a rule")
1587
+
1588
+ #------------------------------------------------------
1589
+ # Primary API
1590
+ #------------------------------------------------------
1591
+
1592
+ def Concept(self, name: str, extends: list[Concept] = [], identify_by: dict[str, Property|Concept] = {}) -> Concept:
1593
+ c = Concept(name, extends=extends, identify_by=identify_by, model=self)
1594
+ self.concepts.append(c)
1595
+ self.concept_index[name] = c
1596
+ return c
1597
+
1598
+ def Table(self, path: str, schema: dict[str, Concept] = {}) -> Table:
1599
+ t = Table(path, schema=schema, model=self)
1600
+ self.tables.append(t)
1601
+ self.table_index[path] = t
1602
+ return t
1603
+
1604
+ def Relationship(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Relationship:
1605
+ r = Relationship(self, reading, fields, short_name)
1606
+ self.relationships.append(r)
1607
+ self.relationship_index[short_name] = r
1608
+ return r
1609
+
1610
+ def Property(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Property:
1611
+ p = Property(self, reading, fields, short_name)
1612
+ self.relationships.append(p)
1613
+ self.relationship_index[short_name] = p
1614
+ return p
1615
+
1616
+ def select(self, *args: StatementAndSchema) -> Fragment:
1617
+ f = Fragment(self)
1618
+ return f.select(*args)
1619
+
1620
+ def where(self, *args: Statement) -> Fragment:
1621
+ f = Fragment(self)
1622
+ return f.where(*args)
1623
+
1624
+ def require(self, *args: Statement) -> Fragment:
1625
+ f = Fragment(self)
1626
+ return f.require(*args)
1627
+
1628
+ def define(self, *args: Statement) -> Fragment:
1629
+ f = Fragment(self)
1630
+ return f.define(*args)
1631
+
1632
+ def union(self, *items: Value) -> Union:
1633
+ return Union(self, *items)
1634
+
1635
+ def data(self, data: DataFrame | list[tuple] | list[dict], columns: list[str]|None = None) -> Data:
1636
+ df = Data.raw_to_df(data, columns)
1637
+ return Data(df, self)
1638
+
1639
+ def not_(self, *items: Value) -> Not:
1640
+ return Not(*items, model=self)
1641
+
1642
+ def distinct(self, *items: Value) -> Distinct:
1643
+ return Distinct(*items, model=self)
1644
+
1645
+ #------------------------------------------------------
1646
+ # Meta
1647
+ #------------------------------------------------------
1648
+
1649
+ def to_metamodel(self) -> mModel:
1650
+ return self._compiler.compile_model(self)
1651
+
1652
+ #------------------------------------------------------
1653
+ # Library
1654
+ #------------------------------------------------------
1655
+
1656
+ class Library(Model):
1657
+ def __init__(self, name: str, exclude_core: bool = False):
1658
+ super().__init__(name=name, exclude_core=exclude_core, is_library=True)
1659
+
1660
+ def Type(self, name: str, super_types: list[Concept] = []) -> Concept:
1661
+ c = self.Concept(name, extends=super_types)
1662
+ self._register_builtin(c)
1663
+ return c
1664
+
1665
+ def Relation(self, name: str, fields: list[Field], overloads: list[list[Concept]]|None = None, annotations: list[Any]|None = None) -> Relationship:
1666
+ r = Relationship(self, "", fields, name, allow_no_fields=True, overloads=overloads)
1667
+ self.relationships.append(r)
1668
+ self.relationship_index[name] = r
1669
+ self._register_builtin(r)
1670
+ # TODO - deal with annotations
1671
+ return r
1672
+
1673
+ def _register_builtin(self, builtin: Concept|Relationship) -> None:
1674
+ """ Register a builtin concept or relationship in the global builtins registry. """
1675
+ builtins.register(self.name, self._compiler.to_relation(builtin))
1676
+
1677
+
1678
+ #------------------------------------------------------
1679
+ # Core Library
1680
+ #------------------------------------------------------
1681
+
1682
+ # Library is populated when relationalai.semantics.frontend.core is imported
1683
+ CoreLibrary: Library = Library("core", exclude_core=True)
1684
+ CoreRelationships = CoreLibrary.relationship_index
1685
+ CoreConcepts = CoreLibrary.concept_index
1686
+
1687
+
1688
+ #------------------------------------------------------
1689
+ # todo
1690
+ #------------------------------------------------------
1691
+
1692
+ """
1693
+ x new/to_identity/filter_by
1694
+ x capture all defines/requires
1695
+ x identify_by
1696
+ x figure out Table/DerivedTable
1697
+ x data node
1698
+ x in_
1699
+ x error on invalid iteration/sum/min/max
1700
+ x Aggregates
1701
+ x fill in std.agg functions when Thiago's lib stuff lands
1702
+ - Error?
1703
+ x rank stuff
1704
+ - validation checks for identify_by
1705
+ - unique
1706
+ - Enum?
1707
+ """