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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constructing Metamodel IR with Algorithms
|
|
3
|
+
|
|
4
|
+
We introduce a set of programmatic constructs that provide a convenient syntax for
|
|
5
|
+
constructing PyRel's metamodel IR representations for Loopy algorithms. Importantly, these
|
|
6
|
+
macros construct a new model using PyRel declarations constructed with a _base model_. The
|
|
7
|
+
base model needs to be also used to declare all concepts and relationships.
|
|
8
|
+
|
|
9
|
+
Below we illustrate the use of these macros by constructing a simple reachability
|
|
10
|
+
algorithm, whose Rel-like pseudo-code is as follows:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
algorithm
|
|
14
|
+
setup
|
|
15
|
+
def edge = { (1,2); (2,3); (3,4) }
|
|
16
|
+
def source = { 1 }
|
|
17
|
+
end setup
|
|
18
|
+
@global empty reachable = {}
|
|
19
|
+
loop
|
|
20
|
+
def frontier = source
|
|
21
|
+
def reachable = frontier
|
|
22
|
+
while (true)
|
|
23
|
+
def next_frontier = frontier . edge
|
|
24
|
+
def frontier = next_frontier
|
|
25
|
+
monus frontier = reachable # frontier = frontier - reachable
|
|
26
|
+
upsert reachable = frontier # reachable = reachable ∪ frontier
|
|
27
|
+
break break_reachable = empty(frontier)
|
|
28
|
+
end while
|
|
29
|
+
end loop
|
|
30
|
+
end algorithm
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The PyRel's metamodel IR for the above algorithm is constructed with the utilities as
|
|
34
|
+
follows.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
base_model = Model("algorithm_builder", dry_run=True)
|
|
38
|
+
|
|
39
|
+
# Input (context) data
|
|
40
|
+
|
|
41
|
+
edge = base_model.Relationship("Edge from {source:int} to {target:int}")
|
|
42
|
+
source = base_model.Relationship("Source node {node:int}")
|
|
43
|
+
|
|
44
|
+
with algorithm(base_model):
|
|
45
|
+
setup(
|
|
46
|
+
define(edge(1,2), edge(2,3), edge(3,4), edge(4,1))),
|
|
47
|
+
define(source(1))
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# "local" variables and relations
|
|
51
|
+
n = Integer.ref()
|
|
52
|
+
m = Integer.ref()
|
|
53
|
+
reachable = base_model.Relationship("Reachable node {node:int}")
|
|
54
|
+
frontier = base_model.Relationship("Frontier node {node:int}")
|
|
55
|
+
next_frontier = base_model.Relationship("Next frontier node {node:int}")
|
|
56
|
+
|
|
57
|
+
global_(empty(define(reachable(n))))
|
|
58
|
+
assign(define(frontier(n)).where(source(n)))
|
|
59
|
+
assign(define(reachable(n)).where(frontier(n)))
|
|
60
|
+
with while_():
|
|
61
|
+
assign(define(next_frontier(m)).where(frontier(n), edge(n, m)))
|
|
62
|
+
assign(define(frontier(m)).where(next_frontier(m)))
|
|
63
|
+
monus(define(frontier(n)).where(reachable(n)))
|
|
64
|
+
upsert(0)(define(reachable(n)).where(frontier(n)))
|
|
65
|
+
break_(where(not_(frontier(n))))
|
|
66
|
+
|
|
67
|
+
# Prints the PyRel Metamodel (IR)
|
|
68
|
+
print(get_metamodel())
|
|
69
|
+
|
|
70
|
+
# Prints the LQP transaction
|
|
71
|
+
print(get_lqp_str())
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
from v0.relationalai.semantics import Model
|
|
75
|
+
from v0.relationalai.semantics.metamodel import factory, ir, types
|
|
76
|
+
from v0.relationalai.semantics.internal.internal import Fragment
|
|
77
|
+
from v0.relationalai.semantics.lqp.algorithms import (
|
|
78
|
+
mk_empty, mk_assign, mk_upsert, mk_global, mk_monus
|
|
79
|
+
)
|
|
80
|
+
from v0.relationalai.semantics.lqp.constructors import mk_transaction
|
|
81
|
+
from v0.relationalai.semantics.lqp.compiler import Compiler
|
|
82
|
+
from v0.relationalai.semantics.lqp import ir as lqp, builtins
|
|
83
|
+
from typing import cast, TypeGuard, Optional, Sequence
|
|
84
|
+
from lqp import print as lqp_print
|
|
85
|
+
import threading
|
|
86
|
+
from contextlib import contextmanager
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# While the constructors are very light-weight they enforce
|
|
90
|
+
# the following grammar for algorithms:
|
|
91
|
+
#
|
|
92
|
+
# <Algorithm> := with algorithm(base_model): <Script>
|
|
93
|
+
# <Script> := <Instruction>*
|
|
94
|
+
# <Instruction> := <BaseInstruction> | <Loop>
|
|
95
|
+
# <BaseInstruction> := [global_(] empty(Fragment) [)]
|
|
96
|
+
# | [global_(] assign(<Fragment>) [)]
|
|
97
|
+
# | break(<Fragment>)
|
|
98
|
+
# | upsert(<Int>)(<Fragment>)
|
|
99
|
+
# | monus(<Fragment>)
|
|
100
|
+
# <Loop> := with while_(): <Script>
|
|
101
|
+
#
|
|
102
|
+
# Note: global_ annotation can only be used on top-level empty and assign instructions at the
|
|
103
|
+
# top-level of the algorithm script.
|
|
104
|
+
|
|
105
|
+
_storage = threading.local()
|
|
106
|
+
|
|
107
|
+
def get_builder() -> 'AlgorithmBuilder':
|
|
108
|
+
""" Retrieves the thread-local AlgorithmBuilder instance."""
|
|
109
|
+
global _storage
|
|
110
|
+
if not(hasattr(_storage, "algorithm_builder")):
|
|
111
|
+
_storage.algorithm_builder = AlgorithmBuilder()
|
|
112
|
+
return _storage.algorithm_builder
|
|
113
|
+
|
|
114
|
+
def get_metamodel() -> ir.Model:
|
|
115
|
+
""" Retrieves the compiled metamodel IR for the previous algorithm. Can only be used
|
|
116
|
+
after an algorithm has been defined."""
|
|
117
|
+
return get_builder().get_metamodel()
|
|
118
|
+
|
|
119
|
+
def get_lqp_str() -> str:
|
|
120
|
+
""" Retrieves the LQP string representation for the previous algorithm. Can only be used
|
|
121
|
+
after an algorithm has been defined."""
|
|
122
|
+
return get_builder().get_lqp_str()
|
|
123
|
+
|
|
124
|
+
@contextmanager
|
|
125
|
+
def algorithm(model:Model):
|
|
126
|
+
""" Context manager for defining an algorithm on the given base model."""
|
|
127
|
+
get_builder().begin_algorithm(model)
|
|
128
|
+
yield
|
|
129
|
+
get_builder().end_algorithm()
|
|
130
|
+
|
|
131
|
+
@contextmanager
|
|
132
|
+
def while_():
|
|
133
|
+
""" Context manager for defining a while loop within an algorithm."""
|
|
134
|
+
get_builder().begin_while_loop()
|
|
135
|
+
yield
|
|
136
|
+
get_builder().end_while_loop()
|
|
137
|
+
|
|
138
|
+
def setup(*stmts:Fragment):
|
|
139
|
+
""" Defines the setup section of an algorithm: a collection of PyRel statement that
|
|
140
|
+
prepare input data for the algorithm."""
|
|
141
|
+
builder = get_builder()
|
|
142
|
+
assert len(builder.script_stacks) == 1, "setup can only be called at the top-level of an algorithm"
|
|
143
|
+
assert builder.setup_fragments is None, "setup can only be called once per algorithm"
|
|
144
|
+
builder.set_setup_fragments(stmts)
|
|
145
|
+
|
|
146
|
+
def global_(pos:int):
|
|
147
|
+
""" Marks a top-level `empty` or `assign` instruction as defining a global relation."""
|
|
148
|
+
assert type(pos) is int, "global_ can only be applied to empty and assign"
|
|
149
|
+
builder = get_builder()
|
|
150
|
+
assert len(builder.script_stacks) == 1, "global_ can only be applied to top-level instructions"
|
|
151
|
+
assert len(builder.script_stacks[0].instructions) == pos + 1
|
|
152
|
+
task = cast(ir.Task, mk_global(builder.script_stacks[0].instructions[pos]))
|
|
153
|
+
builder.script_stacks[0].instructions[pos] = task
|
|
154
|
+
builder.add_global_relation(task)
|
|
155
|
+
|
|
156
|
+
def empty(stmt) -> int:
|
|
157
|
+
""" Marks a PyRel statement as an assignment of empty relation. The statement must not
|
|
158
|
+
have a body (no where clause)."""
|
|
159
|
+
assert has_empty_body(stmt), "Empty instruction must have an empty body"
|
|
160
|
+
task = get_builder().compile_statement(stmt)
|
|
161
|
+
task = cast(ir.Task, mk_empty(task))
|
|
162
|
+
return get_builder().append_task(task)
|
|
163
|
+
|
|
164
|
+
def assign(stmt) -> int:
|
|
165
|
+
""" Marks a PyRel statement as an assignment instruction."""
|
|
166
|
+
task = get_builder().compile_statement(stmt)
|
|
167
|
+
task = cast(ir.Task, mk_assign(task))
|
|
168
|
+
return get_builder().append_task(task)
|
|
169
|
+
|
|
170
|
+
def upsert_with_arity(arity:int, stmt:Fragment):
|
|
171
|
+
task = get_builder().compile_statement(stmt)
|
|
172
|
+
task = cast(ir.Task, mk_upsert(task, arity))
|
|
173
|
+
get_builder().append_task(task)
|
|
174
|
+
|
|
175
|
+
def upsert(arity:int):
|
|
176
|
+
""" Marks a PyRel statement as an upsert instruction with the given arity."""
|
|
177
|
+
assert type(arity) is int and arity >= 0, "arity must be a non-negative integer"
|
|
178
|
+
return lambda stmt: upsert_with_arity(arity, stmt)
|
|
179
|
+
|
|
180
|
+
def monus(stmt: Fragment) -> int:
|
|
181
|
+
""" Marks a PyRel statement as a Boolean monus (set difference) instruction."""
|
|
182
|
+
task = get_builder().compile_statement(stmt)
|
|
183
|
+
task = cast(ir.Task, mk_monus(task, types.Bool, "or", 0))
|
|
184
|
+
return get_builder().append_task(task)
|
|
185
|
+
|
|
186
|
+
def break_(stmt):
|
|
187
|
+
""" Marks a PyRel statement as a break instruction. The statement must be headless (no define clause)."""
|
|
188
|
+
assert has_no_head(stmt), "Break instruction must have a headless fragment"
|
|
189
|
+
task = get_builder().compile_statement(stmt)
|
|
190
|
+
assert isinstance(task, ir.Logical)
|
|
191
|
+
break_condition = [cond for cond in task.body if not isinstance(cond, ir.Update)]
|
|
192
|
+
break_node = factory.break_(factory.logical(break_condition))
|
|
193
|
+
get_builder().append_task(break_node)
|
|
194
|
+
|
|
195
|
+
def has_empty_body(stmt) -> TypeGuard[Fragment]:
|
|
196
|
+
if not isinstance(stmt, Fragment):
|
|
197
|
+
return False
|
|
198
|
+
return len(stmt._where) == 0
|
|
199
|
+
|
|
200
|
+
def has_no_head(frag):
|
|
201
|
+
return len(frag._define) == 0
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class ScriptBuilder:
|
|
205
|
+
"""
|
|
206
|
+
Builder for Loopy scripts.
|
|
207
|
+
"""
|
|
208
|
+
def __init__(self):
|
|
209
|
+
self.instructions:list[ir.Task] = []
|
|
210
|
+
|
|
211
|
+
def add_task(self, instr:ir.Task) -> int:
|
|
212
|
+
self.instructions.append(instr)
|
|
213
|
+
return len(self.instructions) - 1
|
|
214
|
+
|
|
215
|
+
def build_script(self, annos:list[ir.Annotation]) -> ir.Sequence:
|
|
216
|
+
return factory.sequence(
|
|
217
|
+
tasks=self.instructions,
|
|
218
|
+
annos=[builtins.script_annotation()] + annos
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class AlgorithmBuilder:
|
|
223
|
+
"""
|
|
224
|
+
Builder for Loopy algorithms.
|
|
225
|
+
"""
|
|
226
|
+
def __init__(self):
|
|
227
|
+
self.script_stacks:list[ScriptBuilder] = []
|
|
228
|
+
self.compiled_model:Optional[ir.Model] = None
|
|
229
|
+
self.global_relations:list[str] = []
|
|
230
|
+
self.base_model:Optional[Model] = None
|
|
231
|
+
self.setup_fragments:Optional[list[Fragment]] = None
|
|
232
|
+
|
|
233
|
+
def begin_algorithm(self, base_model:Model):
|
|
234
|
+
self.base_model = base_model
|
|
235
|
+
self.script_stacks = [ScriptBuilder()]
|
|
236
|
+
self.compiled_model = None
|
|
237
|
+
self.global_relations:list[str] = []
|
|
238
|
+
self.setup_fragments:Optional[list[Fragment]] = None
|
|
239
|
+
|
|
240
|
+
def add_global_relation(self, task:ir.Task):
|
|
241
|
+
assert isinstance(task, ir.Logical)
|
|
242
|
+
for t in task.body:
|
|
243
|
+
if isinstance(t, ir.Update):
|
|
244
|
+
if t.relation.name not in self.global_relations:
|
|
245
|
+
self.global_relations.append(t.relation.name)
|
|
246
|
+
|
|
247
|
+
def set_setup_fragments(self, fragments:Sequence[Fragment]):
|
|
248
|
+
self.setup_fragments = list(fragments)
|
|
249
|
+
|
|
250
|
+
def compile_statement(self, stmt:Fragment) -> ir.Task:
|
|
251
|
+
assert self.base_model is not None
|
|
252
|
+
task = self.base_model._compiler.compile_task(stmt)
|
|
253
|
+
return task
|
|
254
|
+
|
|
255
|
+
def append_task(self, task:ir.Task) -> int:
|
|
256
|
+
assert len(self.script_stacks) > 0
|
|
257
|
+
return self.script_stacks[-1].add_task(task)
|
|
258
|
+
|
|
259
|
+
def begin_while_loop(self):
|
|
260
|
+
script_builder = ScriptBuilder()
|
|
261
|
+
self.script_stacks.append(script_builder)
|
|
262
|
+
|
|
263
|
+
def end_while_loop(self):
|
|
264
|
+
script_builder = self.script_stacks.pop()
|
|
265
|
+
while_script = script_builder.build_script([builtins.while_annotation()])
|
|
266
|
+
loop = factory.loop(while_script, annos=[builtins.while_annotation()])
|
|
267
|
+
self.append_task(loop)
|
|
268
|
+
|
|
269
|
+
def end_algorithm(self):
|
|
270
|
+
assert len(self.script_stacks) == 1
|
|
271
|
+
script_builder = self.script_stacks.pop()
|
|
272
|
+
algorithm_script = script_builder.build_script([builtins.algorithm_annotation()])
|
|
273
|
+
setup = self.compile_setup()
|
|
274
|
+
algorithm_logical = factory.logical(setup + [algorithm_script])
|
|
275
|
+
self.compiled_model = factory.compute_model(algorithm_logical)
|
|
276
|
+
|
|
277
|
+
def compile_setup(self) -> list[ir.Logical]:
|
|
278
|
+
if self.setup_fragments is None:
|
|
279
|
+
return []
|
|
280
|
+
assert self.setup_fragments is not None
|
|
281
|
+
assert self.base_model is not None
|
|
282
|
+
setup_tasks = []
|
|
283
|
+
for stmt in self.setup_fragments:
|
|
284
|
+
task = self.base_model._compiler.compile_task(stmt)
|
|
285
|
+
setup_tasks.append(task)
|
|
286
|
+
return setup_tasks
|
|
287
|
+
|
|
288
|
+
def get_metamodel(self) -> ir.Model:
|
|
289
|
+
""" Retrieves the compiled metamodel IR for the previous algorithm. """
|
|
290
|
+
metamodel = self.compiled_model
|
|
291
|
+
assert metamodel is not None, "No metamodel available. You must first define algorithm."
|
|
292
|
+
return metamodel
|
|
293
|
+
|
|
294
|
+
def get_lqp_str(self) -> str:
|
|
295
|
+
lqp = self.get_lqp()
|
|
296
|
+
options = lqp_print.ugly_config.copy()
|
|
297
|
+
options[str(lqp_print.PrettyOptions.PRINT_NAMES)] = True
|
|
298
|
+
options[str(lqp_print.PrettyOptions.PRINT_DEBUG)] = False
|
|
299
|
+
lqp_str = lqp_print.to_string(lqp, options)
|
|
300
|
+
return lqp_str
|
|
301
|
+
|
|
302
|
+
def get_lqp(self):
|
|
303
|
+
model = self.get_metamodel()
|
|
304
|
+
|
|
305
|
+
compiler = Compiler()
|
|
306
|
+
rewritten_model = compiler.rewrite(model)
|
|
307
|
+
write_epoch = compiler.do_compile(rewritten_model, {'fragment_id': b"f1"})[1]
|
|
308
|
+
|
|
309
|
+
define = cast(lqp.Define, write_epoch.writes[0].write_type)
|
|
310
|
+
debug_info = define.fragment.debug_info
|
|
311
|
+
|
|
312
|
+
read_epoch = self._build_read_epoch(debug_info)
|
|
313
|
+
|
|
314
|
+
transaction = mk_transaction([write_epoch, read_epoch])
|
|
315
|
+
|
|
316
|
+
return transaction
|
|
317
|
+
|
|
318
|
+
def _build_read_epoch(self, debug_info:lqp.DebugInfo) -> lqp.Epoch:
|
|
319
|
+
reads = []
|
|
320
|
+
|
|
321
|
+
relation_id:dict[str,lqp.RelationId] = dict()
|
|
322
|
+
for rel_id, rel_name in debug_info.id_to_orig_name.items():
|
|
323
|
+
if rel_name in self.global_relations:
|
|
324
|
+
relation_id[rel_name] = rel_id
|
|
325
|
+
|
|
326
|
+
global_relation_names = [rel for rel in self.global_relations if rel in relation_id]
|
|
327
|
+
|
|
328
|
+
for (i, rel_name) in enumerate(global_relation_names):
|
|
329
|
+
read = lqp.Read(
|
|
330
|
+
meta = None,
|
|
331
|
+
read_type = lqp.Output(
|
|
332
|
+
meta=None,
|
|
333
|
+
name=f"{rel_name}",
|
|
334
|
+
relation_id=relation_id[rel_name],
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
reads.append(read)
|
|
338
|
+
|
|
339
|
+
read_epoch = lqp.Epoch(
|
|
340
|
+
meta = None,
|
|
341
|
+
writes = [],
|
|
342
|
+
reads = reads,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
return read_epoch
|