relationalai 0.13.2__py3-none-any.whl → 0.13.4__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/clients/client.py +3 -4
- relationalai/clients/exec_txn_poller.py +62 -31
- relationalai/clients/resources/snowflake/direct_access_resources.py +6 -5
- relationalai/clients/resources/snowflake/snowflake.py +54 -51
- relationalai/clients/resources/snowflake/use_index_poller.py +1 -1
- relationalai/semantics/internal/snowflake.py +5 -1
- relationalai/semantics/lqp/algorithms.py +173 -0
- relationalai/semantics/lqp/builtins.py +199 -2
- relationalai/semantics/lqp/executor.py +90 -41
- relationalai/semantics/lqp/export_rewriter.py +40 -0
- relationalai/semantics/lqp/ir.py +28 -2
- relationalai/semantics/lqp/model2lqp.py +218 -45
- relationalai/semantics/lqp/passes.py +13 -658
- relationalai/semantics/lqp/rewrite/__init__.py +12 -0
- relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +22 -10
- relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
- relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
- relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +31 -2
- relationalai/semantics/lqp/rewrite/period_math.py +77 -0
- relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
- relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
- relationalai/semantics/lqp/utils.py +11 -1
- relationalai/semantics/lqp/validators.py +14 -1
- relationalai/semantics/metamodel/builtins.py +2 -1
- relationalai/semantics/metamodel/compiler.py +2 -1
- relationalai/semantics/metamodel/dependency.py +12 -3
- relationalai/semantics/metamodel/executor.py +11 -1
- relationalai/semantics/metamodel/factory.py +2 -2
- relationalai/semantics/metamodel/helpers.py +7 -0
- relationalai/semantics/metamodel/ir.py +3 -2
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
- relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
- relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
- relationalai/semantics/metamodel/typer/checker.py +6 -4
- relationalai/semantics/metamodel/typer/typer.py +2 -5
- relationalai/semantics/metamodel/visitor.py +4 -3
- relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
- relationalai/semantics/reasoners/optimization/solvers_pb.py +3 -4
- relationalai/semantics/rel/compiler.py +2 -1
- relationalai/semantics/rel/executor.py +3 -2
- relationalai/semantics/tests/lqp/__init__.py +0 -0
- relationalai/semantics/tests/lqp/algorithms.py +345 -0
- relationalai/semantics/tests/test_snapshot_abstract.py +2 -1
- relationalai/tools/cli_controls.py +216 -67
- relationalai/util/format.py +5 -2
- {relationalai-0.13.2.dist-info → relationalai-0.13.4.dist-info}/METADATA +2 -2
- {relationalai-0.13.2.dist-info → relationalai-0.13.4.dist-info}/RECORD +52 -42
- {relationalai-0.13.2.dist-info → relationalai-0.13.4.dist-info}/WHEEL +0 -0
- {relationalai-0.13.2.dist-info → relationalai-0.13.4.dist-info}/entry_points.txt +0 -0
- {relationalai-0.13.2.dist-info → relationalai-0.13.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
from relationalai.semantics.metamodel import ir, builtins, helpers, types
|
|
2
2
|
from relationalai.semantics.metamodel.visitor import collect_by_type
|
|
3
|
-
from relationalai.semantics.metamodel.util import FrozenOrderedSet
|
|
4
|
-
from relationalai.semantics.
|
|
3
|
+
from relationalai.semantics.metamodel.util import FrozenOrderedSet, OrderedSet
|
|
4
|
+
from relationalai.semantics.metamodel.compiler import group_tasks
|
|
5
|
+
from relationalai.semantics.lqp import ir as lqp, utils, types as lqp_types
|
|
5
6
|
from relationalai.semantics.lqp.primitives import lqp_avg_op, lqp_operator, build_primitive
|
|
6
7
|
from relationalai.semantics.lqp.pragmas import pragma_to_lqp_name
|
|
7
8
|
from relationalai.semantics.lqp.types import meta_type_to_lqp
|
|
8
9
|
from relationalai.semantics.lqp.constructors import (
|
|
9
10
|
mk_abstraction, mk_and, mk_exists, mk_or, mk_pragma, mk_primitive,
|
|
10
|
-
mk_specialized_value, mk_type, mk_value, mk_attribute
|
|
11
|
+
mk_specialized_value, mk_type, mk_value, mk_attribute,
|
|
11
12
|
)
|
|
12
|
-
from relationalai.semantics.lqp.
|
|
13
|
+
from relationalai.semantics.lqp.algorithms import (
|
|
14
|
+
is_script, is_algorithm_script, is_algorithm_logical,
|
|
15
|
+
is_while_loop, is_while_script, construct_monoid, is_empty_instruction
|
|
16
|
+
)
|
|
17
|
+
from relationalai.semantics.lqp.builtins import (
|
|
18
|
+
has_global_annotation, get_upsert_annotation, get_monoid_annotation,
|
|
19
|
+
get_monus_annotation, has_assign_annotation, get_arity, supported_lqp_annotations
|
|
20
|
+
)
|
|
21
|
+
from relationalai.semantics.lqp.utils import TranslationCtx, ExportDescriptor, gen_unique_var, gen_rel_id
|
|
13
22
|
from relationalai.semantics.lqp.validators import assert_valid_input
|
|
14
23
|
from relationalai.semantics.lqp.rewrite.functional_dependencies import (
|
|
15
24
|
normalized_fd, contains_only_declarable_constraints
|
|
16
25
|
)
|
|
17
26
|
from decimal import Decimal as PyDecimal
|
|
18
27
|
from datetime import datetime, date, timezone
|
|
19
|
-
from typing import Tuple, cast, Union, Optional
|
|
28
|
+
from typing import Sequence, Tuple, cast, Union, Optional
|
|
20
29
|
from warnings import warn
|
|
21
30
|
import re
|
|
22
31
|
import uuid
|
|
@@ -42,8 +51,8 @@ def to_lqp(model: ir.Model, fragment_name: bytes, ctx: TranslationCtx) -> tuple[
|
|
|
42
51
|
reads.extend(_get_output_reads(ctx.output_ids))
|
|
43
52
|
|
|
44
53
|
export_info = None
|
|
45
|
-
if len(ctx.
|
|
46
|
-
export_filename, col_types, export_reads = _get_export_reads(ctx.
|
|
54
|
+
if len(ctx.export_descriptors) > 0:
|
|
55
|
+
export_filename, col_types, export_reads = _get_export_reads(ctx.export_descriptors)
|
|
47
56
|
reads.extend(export_reads)
|
|
48
57
|
export_info = (export_filename, col_types)
|
|
49
58
|
|
|
@@ -76,14 +85,17 @@ def _get_output_reads(output_ids: list[tuple[lqp.RelationId, str]]) -> list[lqp.
|
|
|
76
85
|
reads.append(lqp.Read(read_type=output, meta=None))
|
|
77
86
|
return reads
|
|
78
87
|
|
|
79
|
-
def _get_export_reads(
|
|
88
|
+
def _get_export_reads(descriptors: list[ExportDescriptor]) -> tuple[str, list, list[lqp.Read]]:
|
|
80
89
|
reads = []
|
|
81
90
|
csv_columns = []
|
|
82
91
|
col_info = []
|
|
83
|
-
for
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
for descriptor in sorted(descriptors, key=lambda x: x.column_number):
|
|
93
|
+
csv_columns.append(lqp.ExportCSVColumn(
|
|
94
|
+
column_name=descriptor.column_name,
|
|
95
|
+
column_data=descriptor.relation_id,
|
|
96
|
+
meta=None,
|
|
97
|
+
))
|
|
98
|
+
col_info.append((descriptor.column_name, descriptor.column_type))
|
|
87
99
|
|
|
88
100
|
# Generate a random name for the internal export path
|
|
89
101
|
export_filename = "export_" + str(uuid.uuid4()).replace("-", "_")
|
|
@@ -106,6 +118,8 @@ def _get_export_reads(export_ids: list[tuple[lqp.RelationId, int, lqp.Type]]) ->
|
|
|
106
118
|
def _translate_to_decls(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Declaration]:
|
|
107
119
|
if contains_only_declarable_constraints(rule):
|
|
108
120
|
return _translate_to_constraint_decls(ctx, rule)
|
|
121
|
+
elif is_algorithm_logical(rule):
|
|
122
|
+
return _translate_algorithms(ctx, rule)
|
|
109
123
|
else:
|
|
110
124
|
return _translate_to_standard_decl(ctx, rule)
|
|
111
125
|
|
|
@@ -128,8 +142,11 @@ def _translate_to_constraint_decls(ctx: TranslationCtx, rule: ir.Logical) -> lis
|
|
|
128
142
|
lqp_guard = mk_abstraction(lqp_typed_vars, mk_and(lqp_guard_atoms))
|
|
129
143
|
lqp_keys:list[lqp.Var] = [var for (var, _) in lqp_typed_keys] # type: ignore
|
|
130
144
|
lqp_values:list[lqp.Var] = [var for (var, _) in lqp_typed_values] # type: ignore
|
|
145
|
+
lqp_id = utils.lqp_hash(fd.canonical_str)
|
|
146
|
+
lqp_name:lqp.RelationId = lqp.RelationId(id=lqp_id, meta=None)
|
|
131
147
|
|
|
132
148
|
fd_decl = lqp.FunctionalDependency(
|
|
149
|
+
name=lqp_name,
|
|
133
150
|
guard=lqp_guard,
|
|
134
151
|
keys=lqp_keys,
|
|
135
152
|
values=lqp_values,
|
|
@@ -140,39 +157,161 @@ def _translate_to_constraint_decls(ctx: TranslationCtx, rule: ir.Logical) -> lis
|
|
|
140
157
|
|
|
141
158
|
return constraint_decls
|
|
142
159
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
def _translate_algorithms(ctx: TranslationCtx, task: ir.Logical) -> list[lqp.Declaration]:
|
|
161
|
+
assert is_algorithm_logical(task)
|
|
162
|
+
decls: list[lqp.Declaration] = []
|
|
163
|
+
for subtask in task.body:
|
|
164
|
+
assert is_algorithm_script(subtask), "Expected all subtasks to be algorithm scripts"
|
|
165
|
+
decls.extend(_translate_algorithm_script(ctx, subtask))
|
|
166
|
+
return decls
|
|
167
|
+
|
|
168
|
+
def _translate_algorithm_script(ctx: TranslationCtx, alg_task: ir.Sequence) -> list[lqp.Declaration]:
|
|
169
|
+
assert is_algorithm_script(alg_task), "Expected Sequence @algorithm @script "
|
|
170
|
+
|
|
171
|
+
alg_globals = _find_algorithm_global_relation_ids(ctx, alg_task)
|
|
172
|
+
alg_body = _translate_script(ctx, alg_task)
|
|
173
|
+
|
|
174
|
+
return [lqp.Algorithm(global_=alg_globals, body=alg_body, meta=None)]
|
|
175
|
+
|
|
176
|
+
def _find_algorithm_global_relation_ids(ctx: TranslationCtx, alg_task: ir.Sequence) -> list[lqp.RelationId]:
|
|
177
|
+
result = []
|
|
178
|
+
updates = collect_by_type(ir.Update, alg_task)
|
|
179
|
+
for update in updates:
|
|
180
|
+
if has_global_annotation(update):
|
|
181
|
+
bindings = _effect_bindings(update)
|
|
182
|
+
projection, _ = _translate_bindings(ctx, bindings)
|
|
183
|
+
rel_id = get_relation_id(ctx, update.relation, projection)
|
|
184
|
+
result.append(rel_id)
|
|
185
|
+
return list(dict.fromkeys(result))
|
|
186
|
+
|
|
187
|
+
def _translate_script(ctx: TranslationCtx, task: ir.Sequence) -> lqp.Script:
|
|
188
|
+
assert is_script(task), "Expected a @script Sequence"
|
|
189
|
+
|
|
190
|
+
constructs: list[lqp.Construct] = []
|
|
191
|
+
|
|
192
|
+
for subtask in task.tasks:
|
|
193
|
+
if is_empty_instruction(subtask):
|
|
194
|
+
constructs.append(_translate_empty_instruction(ctx, subtask))
|
|
195
|
+
elif isinstance(subtask, ir.Logical):
|
|
196
|
+
constructs.extend(_translate_instruction(ctx, subtask))
|
|
197
|
+
elif isinstance(subtask, ir.Break):
|
|
198
|
+
constructs.append(_translate_break_instruction(ctx, subtask))
|
|
199
|
+
elif is_while_loop(subtask):
|
|
200
|
+
constructs.append(_translate_while_loop(ctx, subtask))
|
|
201
|
+
else:
|
|
202
|
+
raise Exception(f"Unsupported script instruction: {subtask}")
|
|
203
|
+
|
|
204
|
+
return lqp.Script(constructs=constructs, meta=None)
|
|
205
|
+
|
|
206
|
+
def _translate_while_loop(ctx: TranslationCtx, task: ir.Loop) -> lqp.Loop:
|
|
207
|
+
assert is_while_loop(task), "Expected a @while Loop"
|
|
208
|
+
assert len(task.iter) == 0, "Temporalized loops not supported"
|
|
209
|
+
|
|
210
|
+
while_script_task = task.body
|
|
211
|
+
assert is_while_script(while_script_task), "The body of a @while Loop must be a @while @script Sequence"
|
|
212
|
+
body_script = _translate_script(ctx, while_script_task)
|
|
213
|
+
|
|
214
|
+
# No init instructions in the translation of PyRel Loops to to LQP loops
|
|
215
|
+
return lqp.Loop(init=[], body=body_script, meta=None)
|
|
216
|
+
|
|
217
|
+
def _translate_break_instruction(ctx: TranslationCtx, task: ir.Break) -> lqp.Construct:
|
|
218
|
+
body = _translate_to_formula(ctx, task.check)
|
|
219
|
+
|
|
220
|
+
ctx.break_rule_counter += 1
|
|
221
|
+
|
|
222
|
+
rel_id = gen_rel_id(ctx, "break_cond_" + str(ctx.break_rule_counter))
|
|
223
|
+
return lqp.Break(
|
|
224
|
+
name = rel_id,
|
|
225
|
+
body = mk_abstraction([], body),
|
|
226
|
+
attrs = [],
|
|
227
|
+
meta = None,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def _translate_empty_instruction(ctx: TranslationCtx, rule: ir.Logical) -> lqp.Instruction:
|
|
231
|
+
assert is_empty_instruction(rule)
|
|
232
|
+
updates = collect_by_type(ir.Update, rule)
|
|
233
|
+
assert len(updates) == 1
|
|
234
|
+
update = updates[0]
|
|
235
|
+
bindings = _effect_bindings(update)
|
|
236
|
+
|
|
237
|
+
# We need to make sure that variable names have a leading underscore
|
|
238
|
+
normalized_bindings:Sequence[ir.Var] = []
|
|
239
|
+
for v in bindings:
|
|
240
|
+
assert isinstance(v, ir.Var)
|
|
241
|
+
if not v.name.startswith("_"):
|
|
242
|
+
v = ir.Var(v.type, "_" + v.name)
|
|
243
|
+
normalized_bindings.append(v)
|
|
244
|
+
|
|
245
|
+
projection, eqs = _translate_bindings(ctx, normalized_bindings)
|
|
246
|
+
assert len(eqs) == 0
|
|
247
|
+
rel_id = get_relation_id(ctx, update.relation, projection)
|
|
248
|
+
abstraction = mk_abstraction(projection, mk_or([])) # empty body = false
|
|
249
|
+
return lqp.Assign(name = rel_id, body = abstraction, attrs = [], meta = None)
|
|
250
|
+
|
|
251
|
+
def _translate_instruction(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Instruction]:
|
|
252
|
+
effects = collect_by_type((ir.Update, ir.Output), rule)
|
|
147
253
|
|
|
148
|
-
# TODO: should this ever actually come in as input?
|
|
149
254
|
if len(effects) == 0:
|
|
150
255
|
return []
|
|
151
256
|
|
|
152
|
-
|
|
257
|
+
conjuncts = _translate_to_formula(ctx, rule)
|
|
258
|
+
res = []
|
|
259
|
+
for effect in effects:
|
|
260
|
+
assert isinstance(effect, ir.Update), f"Got an effect of type {type(effect)} in a loop, which is invalid."
|
|
153
261
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
262
|
+
bindings = _effect_bindings(effect)
|
|
263
|
+
projection, eqs = _translate_bindings(ctx, bindings)
|
|
264
|
+
|
|
265
|
+
eqs.append(conjuncts)
|
|
266
|
+
new_body = mk_and(eqs)
|
|
267
|
+
|
|
268
|
+
rel_id = get_relation_id(ctx, effect.relation, projection)
|
|
269
|
+
abstraction = mk_abstraction(projection, new_body)
|
|
270
|
+
|
|
271
|
+
upsert = get_upsert_annotation(effect)
|
|
272
|
+
monoid = get_monoid_annotation(effect)
|
|
273
|
+
monus = get_monus_annotation(effect)
|
|
274
|
+
|
|
275
|
+
if has_assign_annotation(effect):
|
|
276
|
+
res.append(lqp.Assign(name = rel_id, body = abstraction, attrs = [], meta = None))
|
|
277
|
+
elif upsert is not None:
|
|
278
|
+
res.append(lqp.Upsert(value_arity=get_arity(upsert), name = rel_id, body = abstraction, attrs = [], meta = None))
|
|
279
|
+
elif monoid is not None:
|
|
280
|
+
res.append(lqp.MonoidDef(
|
|
281
|
+
value_arity=get_arity(monoid),
|
|
282
|
+
monoid=construct_monoid(monoid),
|
|
283
|
+
name = rel_id,
|
|
284
|
+
body = abstraction,
|
|
285
|
+
attrs = [],
|
|
286
|
+
meta = None
|
|
287
|
+
))
|
|
288
|
+
elif monus is not None:
|
|
289
|
+
res.append(lqp.MonusDef(
|
|
290
|
+
value_arity=get_arity(monus),
|
|
291
|
+
monoid=construct_monoid(monus),
|
|
292
|
+
name = rel_id,
|
|
293
|
+
body = abstraction,
|
|
294
|
+
attrs = [],
|
|
295
|
+
meta = None
|
|
296
|
+
))
|
|
297
|
+
|
|
298
|
+
return res
|
|
159
299
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
aggr_body = mk_and(conjuncts)
|
|
163
|
-
conjuncts = []
|
|
164
|
-
for aggr in aggregates:
|
|
165
|
-
conjuncts.append(_translate_aggregate(ctx, aggr, aggr_body))
|
|
166
|
-
for rank in ranks:
|
|
167
|
-
conjuncts.append(_translate_rank(ctx, rank, aggr_body))
|
|
300
|
+
def _translate_to_standard_decl(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Declaration]:
|
|
301
|
+
effects = collect_by_type((ir.Output, ir.Update), rule)
|
|
168
302
|
|
|
169
|
-
|
|
303
|
+
# TODO: should this ever actually come in as input?
|
|
304
|
+
if len(effects) == 0:
|
|
305
|
+
return []
|
|
306
|
+
|
|
307
|
+
conjuncts = _translate_to_formula(ctx, rule)
|
|
308
|
+
return [_translate_effect(ctx, effect, conjuncts) for effect in effects]
|
|
170
309
|
|
|
171
310
|
def _translate_annotations(annotations: FrozenOrderedSet[ir.Annotation]) -> list[lqp.Attribute]:
|
|
172
311
|
attributes = []
|
|
173
312
|
for annotation in annotations:
|
|
174
313
|
|
|
175
|
-
if annotation.relation.name in
|
|
314
|
+
if annotation.relation.name in supported_lqp_annotations:
|
|
176
315
|
if any(not isinstance(a, ir.Literal) for a in annotation.args):
|
|
177
316
|
warn("LQP currently ignores annotation parameters with non-literal values")
|
|
178
317
|
continue
|
|
@@ -191,14 +330,11 @@ def _translate_annotations(annotations: FrozenOrderedSet[ir.Annotation]) -> list
|
|
|
191
330
|
def _translate_effect(ctx: TranslationCtx, effect: Union[ir.Output, ir.Update], body: lqp.Formula) -> lqp.Declaration:
|
|
192
331
|
bindings = _effect_bindings(effect)
|
|
193
332
|
|
|
194
|
-
def _is_export(e):
|
|
195
|
-
return isinstance(e, ir.Output) and builtins.export_annotation in e.annotations
|
|
196
|
-
|
|
197
333
|
if isinstance(effect, ir.Output):
|
|
198
334
|
projection, eqs, suffix = _translate_output_bindings(ctx, bindings)
|
|
199
335
|
meta_id = effect.id
|
|
200
336
|
|
|
201
|
-
if
|
|
337
|
+
if helpers.is_export(effect):
|
|
202
338
|
def_name = "export_relation" + suffix
|
|
203
339
|
else:
|
|
204
340
|
def_name = "output" + suffix
|
|
@@ -214,14 +350,23 @@ def _translate_effect(ctx: TranslationCtx, effect: Union[ir.Output, ir.Update],
|
|
|
214
350
|
new_body = mk_and(eqs)
|
|
215
351
|
|
|
216
352
|
# Context bookkeeping for exports and outputs
|
|
217
|
-
if
|
|
353
|
+
if helpers.is_export(effect):
|
|
218
354
|
# The row id is the first n-1 elements, and the actual data is the last element. Its
|
|
219
355
|
# type is stored in the first element of the tuple.
|
|
220
356
|
col_type = projection[-1][1]
|
|
221
357
|
_col_num_match = re.search(r"export_relation_col([0-9]+)", def_name)
|
|
222
358
|
assert _col_num_match, f"Could not find column number in suffix: {def_name}"
|
|
223
359
|
col_num = int(_col_num_match.group(1))
|
|
224
|
-
|
|
360
|
+
col_name = f"col{col_num}"
|
|
361
|
+
if isinstance(effect, ir.Output) and len(effect.aliases) > 0:
|
|
362
|
+
aliases_list = list(effect.aliases)
|
|
363
|
+
col_name = aliases_list[-1][0]
|
|
364
|
+
ctx.export_descriptors.append(ExportDescriptor(
|
|
365
|
+
relation_id=rel_id,
|
|
366
|
+
column_name=col_name,
|
|
367
|
+
column_number=col_num,
|
|
368
|
+
column_type=col_type,
|
|
369
|
+
))
|
|
225
370
|
elif isinstance(effect, ir.Output):
|
|
226
371
|
ctx.output_ids.append((rel_id, def_name))
|
|
227
372
|
|
|
@@ -544,7 +689,36 @@ def _translate_aggregate(ctx: TranslationCtx, aggr: ir.Aggregate, body: lqp.Form
|
|
|
544
689
|
|
|
545
690
|
def _translate_to_formula(ctx: TranslationCtx, task: ir.Task) -> lqp.Formula:
|
|
546
691
|
if isinstance(task, ir.Logical):
|
|
547
|
-
|
|
692
|
+
# For aggregates and ranks, the expected format is:
|
|
693
|
+
#
|
|
694
|
+
# Logical
|
|
695
|
+
# body_task1
|
|
696
|
+
# body_task2
|
|
697
|
+
# ...
|
|
698
|
+
# aggregate/rank task
|
|
699
|
+
#
|
|
700
|
+
# If we see that the Logical is in this format, it should be translated as an
|
|
701
|
+
# aggregate/rank node.
|
|
702
|
+
groups = group_tasks(task.body, {
|
|
703
|
+
"aggregates": ir.Aggregate,
|
|
704
|
+
"ranks": ir.Rank,
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
aggregates = groups.get("aggregates", OrderedSet[ir.Task]())
|
|
708
|
+
ranks = groups.get("ranks", OrderedSet[ir.Task]())
|
|
709
|
+
|
|
710
|
+
if aggregates or ranks:
|
|
711
|
+
conjuncts = []
|
|
712
|
+
body = mk_and([_translate_to_formula(ctx, t) for t in task.body])
|
|
713
|
+
for aggr in aggregates:
|
|
714
|
+
assert(isinstance(aggr, ir.Aggregate))
|
|
715
|
+
conjuncts.append(_translate_aggregate(ctx, aggr, body))
|
|
716
|
+
for rank in ranks:
|
|
717
|
+
assert(isinstance(rank, ir.Rank))
|
|
718
|
+
conjuncts.append(_translate_rank(ctx, rank, body))
|
|
719
|
+
else:
|
|
720
|
+
# If there are no aggregates or ranks, translate as a normal conjunction
|
|
721
|
+
conjuncts = [_translate_to_formula(ctx, child) for child in task.body]
|
|
548
722
|
return mk_and(conjuncts)
|
|
549
723
|
elif isinstance(task, ir.Lookup):
|
|
550
724
|
return _translate_to_atom(ctx, task)
|
|
@@ -564,14 +738,13 @@ def _translate_to_formula(ctx: TranslationCtx, task: ir.Task) -> lqp.Formula:
|
|
|
564
738
|
|
|
565
739
|
return mk_primitive("rel_primitive_hash_tuple_uint128", [v for v, _ in terms])
|
|
566
740
|
elif isinstance(task, ir.Union):
|
|
567
|
-
# TODO: handle hoisted vars if needed
|
|
568
741
|
disjs = [_translate_to_formula(ctx, child) for child in task.tasks]
|
|
569
742
|
return mk_or(disjs)
|
|
570
|
-
elif isinstance(task, (ir.
|
|
743
|
+
elif isinstance(task, (ir.Output, ir.Update)):
|
|
571
744
|
# Nothing to do here, handled in _translate_to_decls
|
|
572
745
|
return mk_and([])
|
|
573
|
-
elif isinstance(task, ir.Rank):
|
|
574
|
-
# Nothing to do here, handled
|
|
746
|
+
elif isinstance(task, (ir.Aggregate, ir.Rank)):
|
|
747
|
+
# Nothing to do here, handled at the Logical level
|
|
575
748
|
return mk_and([])
|
|
576
749
|
else:
|
|
577
750
|
raise NotImplementedError(f"Unknown task type (formula): {type(task)}")
|
|
@@ -666,7 +839,7 @@ def get_output_id(ctx: TranslationCtx, orig_name: str, metamodel_id: int) -> lqp
|
|
|
666
839
|
unique_name = ctx.output_names.get_name_by_id(metamodel_id, orig_name)
|
|
667
840
|
return utils.gen_rel_id(ctx, unique_name)
|
|
668
841
|
|
|
669
|
-
def _translate_bindings(ctx: TranslationCtx, bindings:
|
|
842
|
+
def _translate_bindings(ctx: TranslationCtx, bindings: Sequence[ir.Value]) -> Tuple[list[Tuple[lqp.Var, lqp.Type]], list[lqp.Formula]]:
|
|
670
843
|
lqp_vars = []
|
|
671
844
|
conjuncts = []
|
|
672
845
|
for binding in bindings:
|