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.
- relationalai/config/config.py +47 -21
- relationalai/config/connections/__init__.py +5 -2
- relationalai/config/connections/duckdb.py +2 -2
- relationalai/config/connections/local.py +31 -0
- relationalai/config/connections/snowflake.py +0 -1
- relationalai/config/external/raiconfig_converter.py +235 -0
- relationalai/config/external/raiconfig_models.py +202 -0
- relationalai/config/external/utils.py +31 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +10 -8
- relationalai/semantics/backends/sql/sql_compiler.py +1 -4
- relationalai/semantics/experimental/__init__.py +0 -0
- relationalai/semantics/experimental/builder.py +295 -0
- relationalai/semantics/experimental/builtins.py +154 -0
- relationalai/semantics/frontend/base.py +67 -42
- relationalai/semantics/frontend/core.py +34 -6
- relationalai/semantics/frontend/front_compiler.py +209 -37
- relationalai/semantics/frontend/pprint.py +6 -2
- relationalai/semantics/metamodel/__init__.py +7 -0
- relationalai/semantics/metamodel/metamodel.py +2 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +58 -16
- relationalai/semantics/metamodel/pprint.py +6 -1
- relationalai/semantics/metamodel/rewriter.py +11 -7
- relationalai/semantics/metamodel/typer.py +116 -41
- relationalai/semantics/reasoners/__init__.py +11 -0
- relationalai/semantics/reasoners/graph/__init__.py +35 -0
- relationalai/semantics/reasoners/graph/core.py +9028 -0
- relationalai/semantics/std/__init__.py +30 -10
- relationalai/semantics/std/aggregates.py +641 -12
- relationalai/semantics/std/common.py +146 -13
- relationalai/semantics/std/constraints.py +71 -1
- relationalai/semantics/std/datetime.py +904 -21
- relationalai/semantics/std/decimals.py +143 -2
- relationalai/semantics/std/floats.py +57 -4
- relationalai/semantics/std/integers.py +98 -4
- relationalai/semantics/std/math.py +857 -35
- relationalai/semantics/std/numbers.py +216 -20
- relationalai/semantics/std/re.py +213 -5
- relationalai/semantics/std/strings.py +437 -44
- relationalai/shims/executor.py +60 -52
- relationalai/shims/fixtures.py +85 -0
- relationalai/shims/helpers.py +26 -2
- relationalai/shims/hoister.py +28 -9
- relationalai/shims/mm2v0.py +204 -173
- relationalai/tools/cli/cli.py +192 -10
- relationalai/tools/cli/components/progress_reader.py +1 -1
- relationalai/tools/cli/docs.py +394 -0
- relationalai/tools/debugger.py +11 -4
- relationalai/tools/qb_debugger.py +435 -0
- relationalai/tools/typer_debugger.py +1 -2
- relationalai/util/dataclasses.py +3 -5
- relationalai/util/docutils.py +1 -2
- relationalai/util/error.py +2 -5
- relationalai/util/python.py +23 -0
- relationalai/util/runtime.py +1 -2
- relationalai/util/schema.py +2 -4
- relationalai/util/structures.py +4 -2
- relationalai/util/tracing.py +8 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/METADATA +8 -5
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/RECORD +118 -95
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/WHEEL +1 -1
- v0/relationalai/__init__.py +1 -1
- v0/relationalai/clients/client.py +52 -18
- v0/relationalai/clients/exec_txn_poller.py +122 -0
- v0/relationalai/clients/local.py +23 -8
- v0/relationalai/clients/resources/azure/azure.py +36 -11
- v0/relationalai/clients/resources/snowflake/__init__.py +4 -4
- v0/relationalai/clients/resources/snowflake/cli_resources.py +12 -1
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py +124 -100
- v0/relationalai/clients/resources/snowflake/engine_service.py +381 -0
- v0/relationalai/clients/resources/snowflake/engine_state_handlers.py +35 -29
- v0/relationalai/clients/resources/snowflake/error_handlers.py +43 -2
- v0/relationalai/clients/resources/snowflake/snowflake.py +277 -179
- v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
- v0/relationalai/clients/types.py +5 -0
- v0/relationalai/errors.py +19 -1
- v0/relationalai/semantics/lqp/algorithms.py +173 -0
- v0/relationalai/semantics/lqp/builtins.py +199 -2
- v0/relationalai/semantics/lqp/executor.py +68 -37
- v0/relationalai/semantics/lqp/ir.py +28 -2
- v0/relationalai/semantics/lqp/model2lqp.py +215 -45
- v0/relationalai/semantics/lqp/passes.py +13 -658
- v0/relationalai/semantics/lqp/rewrite/__init__.py +12 -0
- v0/relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
- v0/relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
- v0/relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
- v0/relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
- v0/relationalai/semantics/lqp/rewrite/period_math.py +77 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
- v0/relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
- v0/relationalai/semantics/lqp/utils.py +11 -1
- v0/relationalai/semantics/lqp/validators.py +14 -1
- v0/relationalai/semantics/metamodel/builtins.py +2 -1
- v0/relationalai/semantics/metamodel/compiler.py +2 -1
- v0/relationalai/semantics/metamodel/dependency.py +12 -3
- v0/relationalai/semantics/metamodel/executor.py +11 -1
- v0/relationalai/semantics/metamodel/factory.py +2 -2
- v0/relationalai/semantics/metamodel/helpers.py +7 -0
- v0/relationalai/semantics/metamodel/ir.py +3 -2
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
- v0/relationalai/semantics/metamodel/typer/checker.py +6 -4
- v0/relationalai/semantics/metamodel/typer/typer.py +4 -3
- v0/relationalai/semantics/metamodel/visitor.py +4 -3
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +336 -86
- v0/relationalai/semantics/rel/compiler.py +2 -1
- v0/relationalai/semantics/rel/executor.py +3 -2
- v0/relationalai/semantics/tests/lqp/__init__.py +0 -0
- v0/relationalai/semantics/tests/lqp/algorithms.py +345 -0
- v0/relationalai/tools/cli.py +339 -186
- v0/relationalai/tools/cli_controls.py +216 -67
- v0/relationalai/tools/cli_helpers.py +410 -6
- v0/relationalai/util/format.py +5 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/entry_points.txt +0 -0
- {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:
|
|
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
|
+
|