relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a5__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 (118) hide show
  1. relationalai/config/config.py +47 -21
  2. relationalai/config/connections/__init__.py +5 -2
  3. relationalai/config/connections/duckdb.py +2 -2
  4. relationalai/config/connections/local.py +31 -0
  5. relationalai/config/connections/snowflake.py +0 -1
  6. relationalai/config/external/raiconfig_converter.py +235 -0
  7. relationalai/config/external/raiconfig_models.py +202 -0
  8. relationalai/config/external/utils.py +31 -0
  9. relationalai/config/shims.py +1 -0
  10. relationalai/semantics/__init__.py +10 -8
  11. relationalai/semantics/backends/sql/sql_compiler.py +1 -4
  12. relationalai/semantics/experimental/__init__.py +0 -0
  13. relationalai/semantics/experimental/builder.py +295 -0
  14. relationalai/semantics/experimental/builtins.py +154 -0
  15. relationalai/semantics/frontend/base.py +67 -42
  16. relationalai/semantics/frontend/core.py +34 -6
  17. relationalai/semantics/frontend/front_compiler.py +209 -37
  18. relationalai/semantics/frontend/pprint.py +6 -2
  19. relationalai/semantics/metamodel/__init__.py +7 -0
  20. relationalai/semantics/metamodel/metamodel.py +2 -0
  21. relationalai/semantics/metamodel/metamodel_analyzer.py +58 -16
  22. relationalai/semantics/metamodel/pprint.py +6 -1
  23. relationalai/semantics/metamodel/rewriter.py +11 -7
  24. relationalai/semantics/metamodel/typer.py +116 -41
  25. relationalai/semantics/reasoners/__init__.py +11 -0
  26. relationalai/semantics/reasoners/graph/__init__.py +35 -0
  27. relationalai/semantics/reasoners/graph/core.py +9028 -0
  28. relationalai/semantics/std/__init__.py +30 -10
  29. relationalai/semantics/std/aggregates.py +641 -12
  30. relationalai/semantics/std/common.py +146 -13
  31. relationalai/semantics/std/constraints.py +71 -1
  32. relationalai/semantics/std/datetime.py +904 -21
  33. relationalai/semantics/std/decimals.py +143 -2
  34. relationalai/semantics/std/floats.py +57 -4
  35. relationalai/semantics/std/integers.py +98 -4
  36. relationalai/semantics/std/math.py +857 -35
  37. relationalai/semantics/std/numbers.py +216 -20
  38. relationalai/semantics/std/re.py +213 -5
  39. relationalai/semantics/std/strings.py +437 -44
  40. relationalai/shims/executor.py +60 -52
  41. relationalai/shims/fixtures.py +85 -0
  42. relationalai/shims/helpers.py +26 -2
  43. relationalai/shims/hoister.py +28 -9
  44. relationalai/shims/mm2v0.py +204 -173
  45. relationalai/tools/cli/cli.py +192 -10
  46. relationalai/tools/cli/components/progress_reader.py +1 -1
  47. relationalai/tools/cli/docs.py +394 -0
  48. relationalai/tools/debugger.py +11 -4
  49. relationalai/tools/qb_debugger.py +435 -0
  50. relationalai/tools/typer_debugger.py +1 -2
  51. relationalai/util/dataclasses.py +3 -5
  52. relationalai/util/docutils.py +1 -2
  53. relationalai/util/error.py +2 -5
  54. relationalai/util/python.py +23 -0
  55. relationalai/util/runtime.py +1 -2
  56. relationalai/util/schema.py +2 -4
  57. relationalai/util/structures.py +4 -2
  58. relationalai/util/tracing.py +8 -2
  59. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/METADATA +8 -5
  60. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/RECORD +118 -95
  61. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/WHEEL +1 -1
  62. v0/relationalai/__init__.py +1 -1
  63. v0/relationalai/clients/client.py +52 -18
  64. v0/relationalai/clients/exec_txn_poller.py +122 -0
  65. v0/relationalai/clients/local.py +23 -8
  66. v0/relationalai/clients/resources/azure/azure.py +36 -11
  67. v0/relationalai/clients/resources/snowflake/__init__.py +4 -4
  68. v0/relationalai/clients/resources/snowflake/cli_resources.py +12 -1
  69. v0/relationalai/clients/resources/snowflake/direct_access_resources.py +124 -100
  70. v0/relationalai/clients/resources/snowflake/engine_service.py +381 -0
  71. v0/relationalai/clients/resources/snowflake/engine_state_handlers.py +35 -29
  72. v0/relationalai/clients/resources/snowflake/error_handlers.py +43 -2
  73. v0/relationalai/clients/resources/snowflake/snowflake.py +277 -179
  74. v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
  75. v0/relationalai/clients/types.py +5 -0
  76. v0/relationalai/errors.py +19 -1
  77. v0/relationalai/semantics/lqp/algorithms.py +173 -0
  78. v0/relationalai/semantics/lqp/builtins.py +199 -2
  79. v0/relationalai/semantics/lqp/executor.py +68 -37
  80. v0/relationalai/semantics/lqp/ir.py +28 -2
  81. v0/relationalai/semantics/lqp/model2lqp.py +215 -45
  82. v0/relationalai/semantics/lqp/passes.py +13 -658
  83. v0/relationalai/semantics/lqp/rewrite/__init__.py +12 -0
  84. v0/relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
  85. v0/relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
  86. v0/relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
  87. v0/relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
  88. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
  89. v0/relationalai/semantics/lqp/rewrite/period_math.py +77 -0
  90. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
  91. v0/relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
  92. v0/relationalai/semantics/lqp/utils.py +11 -1
  93. v0/relationalai/semantics/lqp/validators.py +14 -1
  94. v0/relationalai/semantics/metamodel/builtins.py +2 -1
  95. v0/relationalai/semantics/metamodel/compiler.py +2 -1
  96. v0/relationalai/semantics/metamodel/dependency.py +12 -3
  97. v0/relationalai/semantics/metamodel/executor.py +11 -1
  98. v0/relationalai/semantics/metamodel/factory.py +2 -2
  99. v0/relationalai/semantics/metamodel/helpers.py +7 -0
  100. v0/relationalai/semantics/metamodel/ir.py +3 -2
  101. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
  102. v0/relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
  103. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
  104. v0/relationalai/semantics/metamodel/typer/checker.py +6 -4
  105. v0/relationalai/semantics/metamodel/typer/typer.py +4 -3
  106. v0/relationalai/semantics/metamodel/visitor.py +4 -3
  107. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
  108. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +336 -86
  109. v0/relationalai/semantics/rel/compiler.py +2 -1
  110. v0/relationalai/semantics/rel/executor.py +3 -2
  111. v0/relationalai/semantics/tests/lqp/__init__.py +0 -0
  112. v0/relationalai/semantics/tests/lqp/algorithms.py +345 -0
  113. v0/relationalai/tools/cli.py +339 -186
  114. v0/relationalai/tools/cli_controls.py +216 -67
  115. v0/relationalai/tools/cli_helpers.py +410 -6
  116. v0/relationalai/util/format.py +5 -2
  117. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/entry_points.txt +0 -0
  118. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/top_level.txt +0 -0
@@ -91,14 +91,10 @@ def distinct(*args:Value):
91
91
  model = _check_model()
92
92
  return model.distinct(*args)
93
93
 
94
- def not_(*args:Value):
94
+ def not_(*args:Statement):
95
95
  model = _check_model()
96
96
  return model.not_(*args)
97
97
 
98
- def literal(value, type: Concept|None = None) -> Literal:
99
- model = _check_model()
100
- return Literal(value, model, type)
101
-
102
98
  #------------------------------------------------------
103
99
  # Convenience types
104
100
  #------------------------------------------------------
@@ -120,13 +116,14 @@ Date = core.Date
120
116
  DateTime = core.DateTime
121
117
  # RawSource = core.RawSource
122
118
  # Hash = core.Hash
119
+ Error = core.Error
123
120
 
124
121
  #------------------------------------------------------
125
122
  # Convenience aggregates
126
123
  # NOTE: these should be deprecated
127
124
  #------------------------------------------------------
128
-
129
- from .std import aggregates as aggs
125
+ # Import after convenience functions are defined to avoid circular imports
126
+ from .std import aggregates as aggs # noqa: E402
130
127
 
131
128
  rank = aggs.rank
132
129
  limit = aggs.limit
@@ -147,7 +144,12 @@ per = aggs.per
147
144
  __all__ = [
148
145
  "Alias", "CoreLibrary", "Concept", "Data", "Distinct", "Expression", "Field", "FieldRef", "Fragment",
149
146
  "Library", "Literal", "Match", "Model", "New", "Not", "Chain", "Property", "Reading", "Relationship",
150
- "Table", "Union", "Value", "Variable", "core",
147
+ "Table", "Union", "Value", "Variable", "core", "Error",
148
+ "Any", "AnyEntity", "TypeVar", "EntityTypeVar",
149
+ "Number", "String", "Integer", "Int", "Int64", "Int128", "Float", "Decimal", "Bool", "Boolean", "Date", "DateTime",
150
+
151
+ "rank", "limit", "asc", "desc",
152
+ "count", "sum", "min", "max", "avg", "string_join", "per",
151
153
 
152
154
  "select", "define", "where", "require", "union", "data", "not_",
153
155
  ]
@@ -1,15 +1,12 @@
1
1
  from __future__ import annotations
2
2
  from collections import deque
3
3
  from dataclasses import dataclass, field
4
- from sqlglot import exp, select
5
4
 
6
5
  from ....util.structures import OrderedSet
7
6
  from ...metamodel.metamodel import Aggregate, Logical, Lookup, Model, Node, Relation, Task, Update, Var, Value
8
- from ...metamodel.pprint import pprint as mmpp
9
7
  from ...metamodel.rewriter import Rewriter, Walker
10
8
  from ...metamodel.metamodel_analyzer import Analyzer as MMAnalyzer
11
9
 
12
- from rich import print as rp
13
10
 
14
11
  #------------------------------------------------------
15
12
  # Simple flatten
@@ -286,8 +283,8 @@ class SQLCompiler:
286
283
  analysis.analyze(rule)
287
284
 
288
285
  analysis.construct_sccs()
289
- sccs = analysis.sccs
290
286
 
287
+ # sccs = analysis.sccs
291
288
  # print("\nStrongly Connected Components:")
292
289
  # for i, scc in enumerate(sccs, start=1):
293
290
  # tag = f" (recursive {scc.strategy(analysis)})" if scc.recursive else ""
File without changes
@@ -0,0 +1,295 @@
1
+ from typing import Any, Union, TypeAlias
2
+ from contextlib import contextmanager
3
+ from contextvars import ContextVar
4
+ from relationalai.semantics.frontend.base import Statement, Relationship, Model, Concept
5
+
6
+ __all__ = [
7
+ "procedure", "loop",
8
+ "ProcedureBuilder", "LoopBuilder", "Instruction",
9
+ "OrMonoid", "Monoid"
10
+ ]
11
+
12
+ #------------------------------------------------------
13
+ # Monoid Types (only Boolean monoid supported at the moment)
14
+ #------------------------------------------------------
15
+
16
+ class Monoid():
17
+ pass
18
+
19
+ class OrMonoid(Monoid):
20
+ pass
21
+
22
+ #------------------------------------------------------
23
+ # Instruction Types (tagged tuples or LoopBuilder)
24
+ #------------------------------------------------------
25
+
26
+ # Instructions are tagged tuples:
27
+ # ("assign", expr) - define a relation
28
+ # ("empty", relationship) - initialize relation to empty
29
+ # ("upsert", arity, expr) - upsert with arity (number of keys)
30
+ # ("monoid", arity, monoid, expr) - monoid add with arity (number of keys)
31
+ # ("monus", arity, monoid, expr) - monus delete with arity (number of keys)
32
+ # ("break", conditions) - break condition (tuple of conditions, treated as conjunction)
33
+ # ("loop", LoopBuilder) - a loop. could be over a relation (for-loop) or empty (while-loop)
34
+
35
+ Instruction: TypeAlias = tuple[str, Any] | tuple[str, int, Any] | tuple[str, int, Monoid, Any]
36
+
37
+ #------------------------------------------------------
38
+ # Context Variables for tracking current builder
39
+ #------------------------------------------------------
40
+
41
+ # The current instruction container (ProcedureBuilder or LoopBuilder)
42
+ _current_container: ContextVar[Union["ProcedureBuilder", "LoopBuilder", None]] = ContextVar(
43
+ 'current_container', default=None
44
+ )
45
+
46
+ def _register_instruction(instr: Instruction) -> None:
47
+ """Register an instruction with the current container if in context."""
48
+ container = _current_container.get()
49
+ if container is None:
50
+ raise RuntimeError("Instructions can only be used inside an procedure")
51
+ elif isinstance(container, ProcedureBuilder) or isinstance(container, LoopBuilder):
52
+ container._instructions.append(instr)
53
+
54
+ #------------------------------------------------------
55
+ # InstructionContext helper class
56
+ #------------------------------------------------------
57
+
58
+ class InstructionContext:
59
+ """Represents the context behind instructions that behave like insert/deletes."""
60
+
61
+ def __init__(self, arity: int | None = None, monoid=None):
62
+ self.arity = arity
63
+ self._monoid = monoid
64
+
65
+ def with_arity(self, arity: int):
66
+ assert self.arity is None, "Cannot redefine arity of an instruction"
67
+ return InstructionContext(arity=arity, monoid=self._monoid)
68
+
69
+ def with_monoid(self, monoid: Monoid):
70
+ assert self._monoid is None, "Cannot redefine monoid of an instruction"
71
+ return InstructionContext(arity=self.arity, monoid=monoid)
72
+
73
+ def upsert(self, expr: Statement) -> Instruction:
74
+ assert self.arity is not None, "Upsert must have an integer arity. Use `with_arity(x).upsert(...)`"
75
+ instr: Instruction = ("upsert", self.arity, expr)
76
+ _register_instruction(instr)
77
+ return instr
78
+
79
+ def monoid(self, expr: Statement) -> Instruction:
80
+ assert self.arity is not None, "Monoid must have an integer arity. Use `with_arity(x).monoid(...)`"
81
+ assert self._monoid is not None, "Monoid must have a monoid. Use `with_monoid(x).monoid(...)`"
82
+ instr: Instruction = ("monoid", self.arity, self._monoid, expr)
83
+ _register_instruction(instr)
84
+ return instr
85
+
86
+ def monus(self, expr: Statement) -> Instruction:
87
+ assert self.arity is not None, "Monus must have an integer arity. Use `with_arity(x).monus(...)`"
88
+ assert self._monoid is not None, "Monus must have a monoid. Use `with_monoid(x).monus(...)`"
89
+ instr: Instruction = ("monus", self.arity, self._monoid, expr)
90
+ _register_instruction(instr)
91
+ return instr
92
+
93
+ #------------------------------------------------------
94
+ # LoopBuilder
95
+ #------------------------------------------------------
96
+
97
+ class LoopBuilder:
98
+ """Represents a loop in the engine."""
99
+
100
+ def __init__(self, over: list[Union[Concept, Relationship]] = []):
101
+ # TODO: Currently looping over things is not supported.
102
+ self._over = over
103
+ self._instructions: list[Instruction] = []
104
+
105
+ def __str__(self):
106
+ return self._format(indent=0)
107
+
108
+ def _format(self, indent: int = 0) -> str:
109
+ prefix = " " * indent
110
+ lines = [f"{prefix}Loop:"]
111
+ for instr in self._instructions:
112
+ lines.append(self._format_instruction(instr, indent + 1))
113
+ return "\n".join(lines)
114
+
115
+ def _format_instruction(self, instr: Instruction, indent: int) -> str:
116
+ prefix = " " * indent
117
+ if isinstance(instr, LoopBuilder):
118
+ return instr._format(indent)
119
+ tag = instr[0]
120
+ if tag == "assign":
121
+ return f"{prefix}@assign {instr[1]}"
122
+ elif tag == "empty":
123
+ return f"{prefix}@empty {instr[1]}"
124
+ elif tag == "upsert":
125
+ return f"{prefix}@upsert:{instr[1]} {instr[2]}" # type: ignore[index]
126
+ elif tag == "monoid":
127
+ return f"{prefix}@monoid:{instr[1]}:{instr[2]} {instr[3]}" # type: ignore[index]
128
+ elif tag == "monus":
129
+ return f"{prefix}@monus:{instr[1]}:{instr[2]} {instr[3]}" # type: ignore[index]
130
+ elif tag == "break":
131
+ return f"{prefix}@break {instr[1]}"
132
+ else:
133
+ return f"{prefix}{instr}"
134
+
135
+ #------------------------------------------------------
136
+ # ProcedureBuilder
137
+ #------------------------------------------------------
138
+
139
+ class ProcedureBuilder:
140
+ """Builds an imperative procedure with global relationships and instructions.
141
+
142
+ An ProcedureBuilder represents a complete procedure containing:
143
+ - Global relationships (loop heads)
144
+ - A sequence of instructions (assign, empty, upsert, break, or loops)
145
+ """
146
+
147
+ def __init__(self, model: Model, *globals: Relationship):
148
+ self._model = model
149
+ self._globals: tuple[Relationship, ...] = globals
150
+ self._instructions: list[Instruction] = []
151
+
152
+ def assign(self, expr: Statement) -> Instruction:
153
+ instr: Instruction = ("assign", expr)
154
+ _register_instruction(instr)
155
+ return instr
156
+
157
+ def empty(self, relationship: Relationship) -> Instruction:
158
+ instr: Instruction = ("empty", relationship)
159
+ _register_instruction(instr)
160
+ return instr
161
+
162
+ def break_if(self, *conditions: Statement) -> Instruction:
163
+ instr: Instruction = ("break", conditions)
164
+ _register_instruction(instr)
165
+ return instr
166
+
167
+ def with_arity(self, arity: int) -> InstructionContext:
168
+ return InstructionContext(arity=arity)
169
+
170
+ def with_monoid(self, monoid: Monoid) -> InstructionContext:
171
+ return InstructionContext(monoid=monoid)
172
+
173
+ def loop(self, over: list[Union[Concept, Relationship]] = []):
174
+ return loop(over)
175
+
176
+ def __str__(self):
177
+ lines = ["Procedure:"]
178
+
179
+ # Print globals
180
+ if self._globals:
181
+ lines.append(" Globals:")
182
+ for rel in self._globals:
183
+ lines.append(f" {rel}")
184
+
185
+ # Print instructions
186
+ if self._instructions:
187
+ lines.append(" Instructions:")
188
+ for instr in self._instructions:
189
+ lines.append(self._format_instruction(instr, indent=2))
190
+
191
+ return "\n".join(lines)
192
+
193
+ def _format_instruction(self, instr: Instruction, indent: int) -> str:
194
+ prefix = " " * indent
195
+ if isinstance(instr, LoopBuilder):
196
+ return instr._format(indent)
197
+ tag = instr[0]
198
+ if tag == "assign":
199
+ return f"{prefix}@assign {instr[1]}"
200
+ elif tag == "empty":
201
+ return f"{prefix}@empty {instr[1]}"
202
+ elif tag == "upsert":
203
+ return f"{prefix}@upsert:{instr[1]} {instr[2]}" # type: ignore[index]
204
+ elif tag == "monoid":
205
+ return f"{prefix}@monoid:{instr[1]}:{instr[2]} {instr[3]}" # type: ignore[index]
206
+ elif tag == "monus":
207
+ return f"{prefix}@monus:{instr[1]}:{instr[2]} {instr[3]}" # type: ignore[index]
208
+ elif tag == "break":
209
+ return f"{prefix}@break {instr[1]}"
210
+ elif tag == "loop":
211
+ return f"{prefix}@loop {instr[1]}"
212
+ else:
213
+ return f"{prefix}{instr}"
214
+
215
+ #------------------------------------------------------
216
+ # Context Managers
217
+ #------------------------------------------------------
218
+
219
+ @contextmanager
220
+ def procedure(*globals: Relationship, model: Model | None = None):
221
+ """Context manager for defining an procedure with global relationships.
222
+
223
+ Usage:
224
+ with procedure(Reachable, Frontier):
225
+ assign(...)
226
+ empty(...)
227
+ with loop():
228
+ upsert(...)
229
+ break_if(...)
230
+
231
+ Parameters
232
+ ----------
233
+ *globals : Relationship
234
+ Relationships that are modified by the procedure. Important to specify for compilation.
235
+
236
+ Yields
237
+ ------
238
+ ProcedureBuilder
239
+ The procedure builder (can be used to access the built structure).
240
+ """
241
+ from .. import _check_model
242
+ model = _check_model() if model is None else model
243
+ builder = ProcedureBuilder(model, *globals)
244
+
245
+ model._procedure_fragment(builder)
246
+
247
+ token = _current_container.set(builder)
248
+ try:
249
+ yield builder
250
+ finally:
251
+ _current_container.reset(token)
252
+
253
+
254
+ @contextmanager
255
+ def loop(over: list[Union[Concept, Relationship]] = []):
256
+ """Context manager for defining a loop within an procedure.
257
+
258
+ Usage:
259
+ with procedure(...):
260
+ with loop():
261
+ # instructions inside the loop (while-loop)
262
+ assign(...)
263
+ break_if(...)
264
+
265
+ with procedure(...):
266
+ with loop(over=SomeRelation):
267
+ # instructions inside the loop (for-loop over relation)
268
+ ...
269
+
270
+ Can be nested for inner loops:
271
+ with procedure(...):
272
+ with loop():
273
+ with loop():
274
+ # nested loop
275
+ ...
276
+
277
+ Parameters
278
+ ----------
279
+ over : optional
280
+ Relationship to iterate over (for-loop). If None, creates a while-loop.
281
+ """
282
+ parent = _current_container.get()
283
+ if parent is None:
284
+ raise RuntimeError("loop() must be used inside procedure() or another loop()")
285
+
286
+ loop_builder = LoopBuilder(over=over)
287
+ instr: Instruction = ("loop", loop_builder)
288
+ # Register this loop as an instruction in the parent container
289
+ parent._instructions.append(instr)
290
+
291
+ token = _current_container.set(loop_builder)
292
+ try:
293
+ yield loop_builder
294
+ finally:
295
+ _current_container.reset(token)
@@ -0,0 +1,154 @@
1
+ from relationalai.semantics.metamodel import Relation, Annotation, Update, Node, Literal, Sequence, Loop
2
+ from relationalai.semantics.metamodel.builtins import builtins as bt
3
+ from relationalai.semantics.experimental.builder import Monoid, OrMonoid
4
+ from relationalai.semantics.metamodel.rewriter import Rewriter
5
+
6
+ __all__ = [
7
+ "LoopyInstructionAdder", "LoopyGlobalAdder",
8
+ "init_relation", "init_annotation", "mk_init",
9
+ "global_relation", "global_annotation", "mk_global",
10
+ "assign_relation", "assign_annotation", "mk_assign",
11
+ "upsert_relation", "upsert_annotation", "mk_upsert",
12
+ "monoid_relation", "monoid_annotation", "mk_monoid",
13
+ "monus_relation", "monus_annotation", "mk_monus",
14
+ "algorithm_relation", "algorithm_annotation", "mk_algorithm",
15
+ "script_relation", "script_annotation", "mk_script",
16
+ "while_relation", "while_annotation", "mk_while",
17
+ ]
18
+
19
+ # Loopy annotations
20
+
21
+ # LoopyIR annotation rewriters
22
+ class LoopyInstructionAdder(Rewriter):
23
+ def __init__(self, anno: Annotation):
24
+ self.anno = anno
25
+ super().__init__()
26
+
27
+ def apply_update(self, node: Node):
28
+ return self(node)
29
+
30
+ def update(self, node: Update):
31
+ new_annos = tuple(node.annotations) + (self.anno,)
32
+ return node.mut(annotations=new_annos)
33
+
34
+ class LoopyGlobalAdder(Rewriter):
35
+ def __init__(self, globals: list[Relation]):
36
+ self.globals = globals
37
+ super().__init__()
38
+
39
+ def apply_update(self, node: Node):
40
+ return self(node)
41
+
42
+ def update(self, node: Update):
43
+ if node.relation in self.globals:
44
+ new_annos = tuple(node.annotations) + (global_annotation(),)
45
+ return node.mut(annotations=new_annos)
46
+ else:
47
+ return node
48
+
49
+ # Init
50
+
51
+ init_relation = Relation("init")
52
+ def init_annotation():
53
+ return Annotation(relation=init_relation)
54
+
55
+ def mk_init(i: Node):
56
+ return LoopyInstructionAdder(init_annotation()).apply_update(i)
57
+
58
+ # Empty
59
+
60
+ empty_relation = Relation("empty")
61
+ def empty_annotation():
62
+ return Annotation(relation=empty_relation)
63
+
64
+ def mk_empty(i: Node):
65
+ return LoopyInstructionAdder(empty_annotation()).apply_update(i)
66
+
67
+ # Global
68
+
69
+ global_relation = Relation("global")
70
+ def global_annotation():
71
+ return Annotation(relation=global_relation)
72
+
73
+ def mk_global(globals: list[Relation], i: Node):
74
+ return LoopyGlobalAdder(globals).apply_update(i)
75
+
76
+ # Assign
77
+
78
+ assign_relation = Relation("assign")
79
+ def assign_annotation():
80
+ return Annotation(assign_relation)
81
+
82
+ def mk_assign(i: Node):
83
+ return LoopyInstructionAdder(assign_annotation()).apply_update(i)
84
+
85
+ # Upsert
86
+
87
+ upsert_relation = Relation("upsert")
88
+ def upsert_annotation(arity: int):
89
+ arity_val = Literal(bt.core.Integer, arity)
90
+ return Annotation(relation=upsert_relation, args=(arity_val,))
91
+
92
+ def mk_upsert(i: Node, arity: int):
93
+ return LoopyInstructionAdder(upsert_annotation(arity)).apply_update(i)
94
+
95
+ # Monoid conversion (in prep for monoid and monus instructions)
96
+
97
+ def convert_monoid(monoid: Monoid):
98
+ if isinstance(monoid, OrMonoid):
99
+ return (bt.core.Boolean, Literal(bt.core.String, "or"))
100
+ else:
101
+ raise RuntimeError("Only OrMonoid supported for now")
102
+
103
+ # Monoid
104
+
105
+ monoid_relation = Relation("monoid")
106
+ def monoid_annotation(arity: int, monoid: Monoid):
107
+ arity_val = Literal(bt.core.Integer, arity)
108
+ monoid_type, monoid_op = convert_monoid(monoid)
109
+ return Annotation(relation=monoid_relation, args=(arity_val, monoid_type, monoid_op))
110
+
111
+ def mk_monoid(i: Node, arity: int, monoid: Monoid):
112
+ return LoopyInstructionAdder(monoid_annotation(arity, monoid)).apply_update(i)
113
+
114
+ # Monus
115
+
116
+ monus_relation = Relation("monus")
117
+ def monus_annotation(arity: int, monoid: Monoid):
118
+ arity_val = Literal(bt.core.Integer, arity)
119
+ monoid_type, monoid_op = convert_monoid(monoid)
120
+ return Annotation(relation=monus_relation, args=(arity_val, monoid_type, monoid_op))
121
+
122
+ def mk_monus(i: Node, arity: int, monoid: Monoid):
123
+ return LoopyInstructionAdder(monus_annotation(arity, monoid)).apply_update(i)
124
+
125
+ # Algorithm
126
+
127
+ algorithm_relation = Relation("algorithm", ())
128
+ def algorithm_annotation():
129
+ return Annotation(relation=algorithm_relation)
130
+
131
+ def mk_algorithm(s: Sequence):
132
+ new_annos = tuple(s.annotations) + (algorithm_annotation(),)
133
+ return s.mut(annotations=new_annos)
134
+
135
+ # Script
136
+
137
+ script_relation = Relation("script", ())
138
+ def script_annotation():
139
+ return Annotation(relation=script_relation)
140
+
141
+ def mk_script(s: Sequence):
142
+ new_annos = tuple(s.annotations) + (script_annotation(),)
143
+ return s.mut(annotations=new_annos)
144
+
145
+ # While
146
+
147
+ while_relation = Relation("while", ())
148
+ def while_annotation():
149
+ return Annotation(relation=while_relation)
150
+
151
+ def mk_while(s: Sequence | Loop):
152
+ new_annos = tuple(s.annotations) + (while_annotation(),)
153
+ return s.mut(annotations=new_annos)
154
+