relationalai 0.12.2__py3-none-any.whl → 0.12.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/snowflake.py +117 -28
- relationalai/clients/use_index_poller.py +3 -0
- relationalai/experimental/solvers.py +18 -19
- relationalai/semantics/internal/snowflake.py +2 -3
- relationalai/semantics/lqp/executor.py +39 -9
- relationalai/semantics/lqp/model2lqp.py +0 -1
- relationalai/semantics/lqp/rewrite/extract_common.py +30 -8
- relationalai/semantics/metamodel/builtins.py +6 -6
- relationalai/semantics/metamodel/dependency.py +44 -21
- relationalai/semantics/metamodel/helpers.py +7 -6
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +1 -4
- relationalai/semantics/metamodel/rewrite/flatten.py +1 -13
- relationalai/semantics/reasoners/graph/core.py +803 -121
- relationalai/semantics/rel/executor.py +13 -6
- relationalai/semantics/sql/executor/snowflake.py +2 -2
- relationalai/semantics/std/math.py +2 -2
- {relationalai-0.12.2.dist-info → relationalai-0.12.4.dist-info}/METADATA +1 -1
- {relationalai-0.12.2.dist-info → relationalai-0.12.4.dist-info}/RECORD +21 -21
- {relationalai-0.12.2.dist-info → relationalai-0.12.4.dist-info}/WHEEL +0 -0
- {relationalai-0.12.2.dist-info → relationalai-0.12.4.dist-info}/entry_points.txt +0 -0
- {relationalai-0.12.2.dist-info → relationalai-0.12.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -96,8 +96,12 @@ class DependencyInfo():
|
|
|
96
96
|
# start with the cluster dependencies, because cluster represents the task we
|
|
97
97
|
# care about
|
|
98
98
|
queue.extend(cluster.dependencies)
|
|
99
|
+
seen = set()
|
|
99
100
|
while queue:
|
|
100
101
|
cluster = queue.pop()
|
|
102
|
+
if cluster.id in seen:
|
|
103
|
+
continue
|
|
104
|
+
seen.add(cluster.id)
|
|
101
105
|
deps.update(cluster.content)
|
|
102
106
|
queue.extend(cluster.dependencies)
|
|
103
107
|
|
|
@@ -625,13 +629,17 @@ class BindingAnalysis(visitor.Visitor):
|
|
|
625
629
|
# TODO: this is similar to what's done below in visit_lookup, modularize
|
|
626
630
|
if builtins.is_eq(child.relation):
|
|
627
631
|
x, y = child.args[0], child.args[1]
|
|
632
|
+
# Compute input/output vars of the equality
|
|
628
633
|
if isinstance(x, ir.Var) and not isinstance(y, ir.Var):
|
|
629
|
-
|
|
634
|
+
# Variable x is potentially grounded by other expressions at
|
|
635
|
+
# level in the Logical. If it is, then we should mark it as
|
|
636
|
+
# input (which is done later).
|
|
637
|
+
potentially_grounded.add((child, x, x))
|
|
630
638
|
elif not isinstance(x, ir.Var) and isinstance(y, ir.Var):
|
|
631
|
-
|
|
639
|
+
potentially_grounded.add((child, y, y))
|
|
632
640
|
elif isinstance(x, ir.Var) and isinstance(y, ir.Var):
|
|
633
641
|
# mark as potentially grounded, if any is grounded in other atoms then we later ground both
|
|
634
|
-
potentially_grounded.add((x, y))
|
|
642
|
+
potentially_grounded.add((child, x, y))
|
|
635
643
|
else:
|
|
636
644
|
# grounds only outputs
|
|
637
645
|
for idx, f in enumerate(child.relation.fields):
|
|
@@ -654,23 +662,33 @@ class BindingAnalysis(visitor.Visitor):
|
|
|
654
662
|
# grounds the output var
|
|
655
663
|
grounds.add(child.id_var)
|
|
656
664
|
|
|
665
|
+
# add child hoisted vars to grounded so that they can be picked up by the children
|
|
666
|
+
for child in node.body:
|
|
667
|
+
if isinstance(child, helpers.COMPOSITES):
|
|
668
|
+
grounds.update(helpers.hoisted_vars(child.hoisted))
|
|
669
|
+
|
|
670
|
+
# equalities where both sides are already grounded mean that both sides are input
|
|
671
|
+
for child, x, y in potentially_grounded:
|
|
672
|
+
if x in grounds and y in grounds:
|
|
673
|
+
self.input(child, x)
|
|
674
|
+
self.input(child, y)
|
|
675
|
+
|
|
657
676
|
# deal with potentially grounded vars up to a fixpoint
|
|
658
677
|
changed = True
|
|
659
678
|
while changed:
|
|
660
679
|
changed = False
|
|
661
|
-
for x, y in potentially_grounded:
|
|
680
|
+
for child, x, y in potentially_grounded:
|
|
662
681
|
if x in grounds and y not in grounds:
|
|
682
|
+
self.input(child, x)
|
|
683
|
+
self.output(child, y)
|
|
663
684
|
grounds.add(y)
|
|
664
685
|
changed = True
|
|
665
686
|
elif y in grounds and x not in grounds:
|
|
687
|
+
self.input(child, y)
|
|
688
|
+
self.output(child, x)
|
|
666
689
|
grounds.add(x)
|
|
667
690
|
changed = True
|
|
668
691
|
|
|
669
|
-
# add child hoisted vars to grounded so that they can be picked up by the children
|
|
670
|
-
for child in node.body:
|
|
671
|
-
if isinstance(child, helpers.COMPOSITES):
|
|
672
|
-
grounds.update(helpers.hoisted_vars(child.hoisted))
|
|
673
|
-
|
|
674
692
|
# now visit the children
|
|
675
693
|
self._grounded.append(grounds)
|
|
676
694
|
super().visit_logical(node, parent)
|
|
@@ -765,20 +783,25 @@ class BindingAnalysis(visitor.Visitor):
|
|
|
765
783
|
self.output(node, arg)
|
|
766
784
|
|
|
767
785
|
if builtins.is_eq(node.relation):
|
|
768
|
-
#
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
786
|
+
# Most cases are covered already at the parent level if the equality is part of
|
|
787
|
+
# a Logical. The remaining cases are when the equality is a child of a
|
|
788
|
+
# non-Logical, or if its variables are not ground elsewhere in the Logical.
|
|
789
|
+
if self.info.task_inputs(node) or self.info.task_outputs(node):
|
|
790
|
+
# already covered
|
|
791
|
+
pass
|
|
792
|
+
else:
|
|
793
|
+
x, y = node.args[0], node.args[1]
|
|
776
794
|
grounds = self._grounded[-1] if self._grounded else ordered_set()
|
|
777
|
-
|
|
778
|
-
if
|
|
779
|
-
self.
|
|
795
|
+
if isinstance(x, ir.Var):
|
|
796
|
+
if x in grounds:
|
|
797
|
+
self.input(node, x)
|
|
798
|
+
else:
|
|
799
|
+
self.output(node, x)
|
|
800
|
+
if isinstance(y, ir.Var):
|
|
801
|
+
if y in grounds:
|
|
802
|
+
self.input(node, y)
|
|
780
803
|
else:
|
|
781
|
-
self.
|
|
804
|
+
self.output(node, y)
|
|
782
805
|
else:
|
|
783
806
|
# register variables depending on the input flag of the relation bound to the lookup
|
|
784
807
|
for idx, f in enumerate(node.relation.fields):
|
|
@@ -348,12 +348,13 @@ def clone_task(task: T) -> T:
|
|
|
348
348
|
# if no childrean were stacked, we rewrote all fields of curr, so we can pop it and rewrite it
|
|
349
349
|
if not stacked_children:
|
|
350
350
|
stack.pop()
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
351
|
+
if curr.id not in cache:
|
|
352
|
+
children = []
|
|
353
|
+
for f in curr_fields:
|
|
354
|
+
children.append(from_cache(getattr(curr, f.name)))
|
|
355
|
+
# create a new prev_node with the cloned children
|
|
356
|
+
prev_node = curr.__class__(*children)
|
|
357
|
+
cache[curr.id] = prev_node
|
|
357
358
|
|
|
358
359
|
# the last node we processed is the rewritten original node
|
|
359
360
|
assert(isinstance(prev_node, type(task)))
|
|
@@ -58,10 +58,7 @@ class LogicalExtractor(Rewriter):
|
|
|
58
58
|
# compute the vars to be exposed by the extracted logical; those are keys (what
|
|
59
59
|
# makes the values unique) + the values (the hoisted variables)
|
|
60
60
|
exposed_vars = ordered_set()
|
|
61
|
-
|
|
62
|
-
# TODO - in the future we can analyze better these inputs to see if we can drop
|
|
63
|
-
# some and have narrower intermediate relations
|
|
64
|
-
exposed_vars.update(self.info.task_inputs(logical))
|
|
61
|
+
|
|
65
62
|
# if there are aggregations, make sure we don't expose the projected and input vars,
|
|
66
63
|
# but expose groupbys
|
|
67
64
|
for child in node.body:
|
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import cast, Optional, TypeVar
|
|
4
4
|
from typing import Tuple
|
|
5
5
|
|
|
6
|
-
from relationalai.semantics.metamodel import builtins, ir, factory as f, helpers, types
|
|
6
|
+
from relationalai.semantics.metamodel import builtins, ir, factory as f, helpers, types
|
|
7
7
|
from relationalai.semantics.metamodel.compiler import Pass, group_tasks
|
|
8
8
|
from relationalai.semantics.metamodel.util import OrderedSet, ordered_set, NameCache
|
|
9
9
|
from relationalai.semantics.metamodel import dependency
|
|
@@ -611,18 +611,6 @@ def set_union(s1: Optional[OrderedSet[T]], s2: Optional[OrderedSet[T]]) -> list:
|
|
|
611
611
|
return s2.get_list()
|
|
612
612
|
return []
|
|
613
613
|
|
|
614
|
-
def extractable(t: ir.Task):
|
|
615
|
-
"""
|
|
616
|
-
Whether this task is a Logical that will be extracted as a top level by this
|
|
617
|
-
pass, because it has an aggregation, effects, match, union, etc.
|
|
618
|
-
"""
|
|
619
|
-
extractable_types = (ir.Update, ir.Aggregate, ir.Match, ir.Union, ir.Rank)
|
|
620
|
-
return isinstance(t, ir.Logical) and len(visitor.collect_by_type(extractable_types, t)) > 0
|
|
621
|
-
|
|
622
|
-
def extractables(composites: OrderedSet[ir.Task]):
|
|
623
|
-
""" Filter the set of composites, keeping only the extractable ones. """
|
|
624
|
-
return list(filter(extractable, composites))
|
|
625
|
-
|
|
626
614
|
def negate(lookup: ir.Lookup, values: int):
|
|
627
615
|
"""
|
|
628
616
|
Return a negation of this reference, where the last `values` arguments are to
|