relationalai 0.11.3__py3-none-any.whl → 0.11.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 +6 -1
- relationalai/clients/use_index_poller.py +349 -188
- relationalai/early_access/dsl/bindings/csv.py +2 -2
- relationalai/semantics/internal/internal.py +22 -4
- relationalai/semantics/lqp/executor.py +61 -12
- relationalai/semantics/lqp/intrinsics.py +23 -0
- relationalai/semantics/lqp/model2lqp.py +13 -4
- relationalai/semantics/lqp/passes.py +2 -3
- relationalai/semantics/lqp/primitives.py +12 -1
- relationalai/semantics/metamodel/builtins.py +8 -1
- relationalai/semantics/metamodel/factory.py +3 -2
- relationalai/semantics/reasoners/graph/core.py +54 -2
- relationalai/semantics/reasoners/optimization/solvers_dev.py +20 -1
- relationalai/semantics/reasoners/optimization/solvers_pb.py +24 -3
- relationalai/semantics/rel/compiler.py +5 -17
- relationalai/semantics/rel/executor.py +2 -2
- relationalai/semantics/rel/rel.py +6 -0
- relationalai/semantics/rel/rel_utils.py +8 -1
- relationalai/semantics/rel/rewrite/extract_common.py +153 -242
- relationalai/semantics/sql/compiler.py +120 -39
- relationalai/semantics/sql/executor/duck_db.py +21 -0
- relationalai/semantics/sql/rewrite/denormalize.py +4 -6
- relationalai/semantics/sql/rewrite/recursive_union.py +23 -3
- relationalai/semantics/sql/sql.py +27 -0
- relationalai/semantics/std/__init__.py +2 -1
- relationalai/semantics/std/datetime.py +4 -0
- relationalai/semantics/std/re.py +83 -0
- relationalai/semantics/std/strings.py +1 -1
- relationalai/tools/cli_controls.py +445 -60
- relationalai/util/format.py +78 -1
- {relationalai-0.11.3.dist-info → relationalai-0.11.4.dist-info}/METADATA +3 -2
- {relationalai-0.11.3.dist-info → relationalai-0.11.4.dist-info}/RECORD +35 -33
- {relationalai-0.11.3.dist-info → relationalai-0.11.4.dist-info}/WHEEL +0 -0
- {relationalai-0.11.3.dist-info → relationalai-0.11.4.dist-info}/entry_points.txt +0 -0
- {relationalai-0.11.3.dist-info → relationalai-0.11.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -125,7 +125,7 @@ OPERATORS = {
|
|
|
125
125
|
"ends_with": "::std::common::ends_with",
|
|
126
126
|
"contains": "::std::common::contains",
|
|
127
127
|
"substring": "::std::common::substring",
|
|
128
|
-
"num_chars": "
|
|
128
|
+
"num_chars": "::std::common::num_chars",
|
|
129
129
|
"like_match": "::std::common::like_match",
|
|
130
130
|
"lower": "::std::common::lowercase",
|
|
131
131
|
"upper": "::std::common::uppercase",
|
|
@@ -134,7 +134,14 @@ OPERATORS = {
|
|
|
134
134
|
"replace": "rel_primitive_replace",
|
|
135
135
|
"split": "::std::common::string_split",
|
|
136
136
|
"split_part": "::std::common::string_split",
|
|
137
|
+
|
|
138
|
+
# regex
|
|
137
139
|
"regex_match": "rel_primitive_regex_match",
|
|
140
|
+
"regex_match_all": "::std::common::regex_match_all",
|
|
141
|
+
"regex_search": "pyrel_regex_search",
|
|
142
|
+
"capture_group_by_index": "::std::common::capture_group_by_index",
|
|
143
|
+
"capture_group_by_name": "::std::common::capture_group_by_name",
|
|
144
|
+
"escape_regex_metachars": "::std::common::escape_regex_metachars",
|
|
138
145
|
|
|
139
146
|
# ints
|
|
140
147
|
"parse_int64": "::std::common::parse_int",
|
|
@@ -7,6 +7,7 @@ from relationalai.semantics.metamodel import ir, factory as f, helpers
|
|
|
7
7
|
from relationalai.semantics.metamodel.compiler import Pass, group_tasks
|
|
8
8
|
from relationalai.semantics.metamodel.util import OrderedSet, ordered_set
|
|
9
9
|
from relationalai.semantics.metamodel import dependency
|
|
10
|
+
from relationalai.semantics.metamodel import builtins
|
|
10
11
|
|
|
11
12
|
class ExtractCommon(Pass):
|
|
12
13
|
"""
|
|
@@ -35,6 +36,17 @@ class ExtractCommon(Pass):
|
|
|
35
36
|
Logical2 ...
|
|
36
37
|
"""
|
|
37
38
|
|
|
39
|
+
# The extraction plan heuristic is as follows:
|
|
40
|
+
#
|
|
41
|
+
# Given a set of binder tasks B and a set of extractable tasks E, we find:
|
|
42
|
+
# - A subset of common tasks C in B, and
|
|
43
|
+
# - A subset of exposed variables V output from tasks in C
|
|
44
|
+
# where:
|
|
45
|
+
# - The intersection of common dependencies of all tasks in E are contained in C
|
|
46
|
+
# (including transitive dependencies)
|
|
47
|
+
# - The union of input variables for all tasks in E intersected with the output
|
|
48
|
+
# variables of tasks in C are contained in V
|
|
49
|
+
|
|
38
50
|
#--------------------------------------------------
|
|
39
51
|
# Public API
|
|
40
52
|
#--------------------------------------------------
|
|
@@ -77,112 +89,124 @@ class ExtractCommon(Pass):
|
|
|
77
89
|
return task
|
|
78
90
|
|
|
79
91
|
def handle_logical(self, task: ir.Logical, ctx: Context):
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for child in task.body:
|
|
121
|
-
if child in groups["other"] or child not in plan.remaining_body or child in composites:
|
|
122
|
-
continue
|
|
123
|
-
remaining_vars.update(ctx.info.task_inputs(child))
|
|
124
|
-
remaining_vars.update(ctx.info.task_outputs(child))
|
|
125
|
-
remaining_vars = remaining_vars & plan.exposed_vars
|
|
126
|
-
|
|
127
|
-
# if the plan was not used in one of the cases above, ignore it completely, we
|
|
128
|
-
# are not extracting common nor distributing it around
|
|
129
|
-
if plan and not plan.distribute_common_reference and not len(composites) > 1:
|
|
130
|
-
plan = None
|
|
131
|
-
|
|
132
|
-
# recursively handle children
|
|
92
|
+
# Process the original body to find binders and extractables. The Flatten pass later
|
|
93
|
+
# will extract out both composites and effects, so we group them together here
|
|
94
|
+
groups = group_tasks(task.body, {
|
|
95
|
+
"binders": helpers.BINDERS,
|
|
96
|
+
"composites_and_effects": helpers.COMPOSITES + helpers.EFFECTS,
|
|
97
|
+
})
|
|
98
|
+
binders = groups["binders"]
|
|
99
|
+
composites_and_effects = groups["composites_and_effects"]
|
|
100
|
+
|
|
101
|
+
# the new body of the rewritten task
|
|
102
|
+
body:OrderedSet[ir.Task] = ordered_set()
|
|
103
|
+
|
|
104
|
+
# quick check to see if it's worth doing more analysis; we only want to extract
|
|
105
|
+
# common binders if there are multiple, and there are also multiple composites
|
|
106
|
+
# that will be extracted by the flatten pass later (so that they can share the
|
|
107
|
+
# extracted logic).
|
|
108
|
+
plan = None
|
|
109
|
+
if len(binders) > 1 and composites_and_effects:
|
|
110
|
+
extractables = flatten.extractables(composites_and_effects)
|
|
111
|
+
# only makes sense to extract common if at least one nested composite will be
|
|
112
|
+
# extracted during Flatten
|
|
113
|
+
if extractables:
|
|
114
|
+
# make a plan to extract common tasks from the logical
|
|
115
|
+
plan = self._create_extraction_plan(binders, composites_and_effects, extractables, ctx)
|
|
116
|
+
if plan and len(composites_and_effects) > 1:
|
|
117
|
+
# plan is worthwhile and there are multiple composites, extract the common body and add the connection to the body
|
|
118
|
+
exposed_vars = plan.exposed_vars.get_list()
|
|
119
|
+
plan.common_reference = f.lookup(helpers.extract(task, plan.common_body, exposed_vars, ctx.rewrite_ctx, "common"), exposed_vars)
|
|
120
|
+
# if we are not distributing the reference, add to the main body
|
|
121
|
+
if not plan.distribute_common_reference:
|
|
122
|
+
body.add(plan.common_reference)
|
|
123
|
+
|
|
124
|
+
# if we have a plan and will distribute the common reference, keep track of
|
|
125
|
+
# variables still needed by the remaining tasks, as they need to be hoisted by
|
|
126
|
+
# the remaining composites that get the common reference
|
|
127
|
+
remaining_vars = None
|
|
128
|
+
if plan and plan.distribute_common_reference:
|
|
129
|
+
# add variables hoisted by this logical that are in the exposed vars, to
|
|
130
|
+
# make sure they are hoisted all the way through
|
|
131
|
+
remaining_vars = OrderedSet.from_iterable(helpers.hoisted_vars(task.hoisted)) & plan.exposed_vars
|
|
133
132
|
for child in task.body:
|
|
134
|
-
|
|
135
|
-
if plan and child not in groups["other"] and child not in plan.remaining_body and child not in composites:
|
|
133
|
+
if child in groups["other"] or child not in plan.remaining_body or child in composites_and_effects:
|
|
136
134
|
continue
|
|
135
|
+
remaining_vars.update(ctx.info.task_inputs(child))
|
|
136
|
+
remaining_vars.update(ctx.info.task_outputs(child))
|
|
137
|
+
remaining_vars = remaining_vars & plan.exposed_vars
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
# if the plan was not used in one of the cases above, ignore it completely, we
|
|
140
|
+
# are not extracting common nor distributing it around
|
|
141
|
+
if plan and not plan.distribute_common_reference and not len(composites_and_effects) > 1:
|
|
142
|
+
plan = None
|
|
143
|
+
|
|
144
|
+
# recursively handle children
|
|
145
|
+
for child in task.body:
|
|
146
|
+
# skip children that were extracted
|
|
147
|
+
if plan and child not in groups["other"] and child not in plan.remaining_body and child not in composites_and_effects:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
# no plan or child is not a composite, so just add the handled to the body
|
|
151
|
+
if not plan or child not in composites_and_effects:
|
|
152
|
+
body.add(self.handle(child, ctx))
|
|
153
|
+
continue
|
|
142
154
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
#replacement_body.update(plan.local_dependencies[child])
|
|
168
|
-
|
|
169
|
-
if isinstance(replacement, ir.Logical):
|
|
170
|
-
# if the replacements is a logical, we can just add to the body
|
|
171
|
-
body.add(replacement.reconstruct(
|
|
172
|
-
replacement.engine,
|
|
173
|
-
tuple(hoisted.get_list()),
|
|
174
|
-
tuple(replacement_body.update(replacement.body).get_list()),
|
|
175
|
-
replacement.annotations
|
|
176
|
-
))
|
|
155
|
+
# there is a plan and the child is in composites, so...
|
|
156
|
+
replacement = self.handle(child, ctx)
|
|
157
|
+
|
|
158
|
+
# this child needs either extra local dependencies or the common reference
|
|
159
|
+
if child in plan.local_dependencies or plan.distribute_common_reference:
|
|
160
|
+
# the new body will have maybe the common reference and the local deps
|
|
161
|
+
replacement_body = ordered_set()
|
|
162
|
+
|
|
163
|
+
hoisted = OrderedSet()
|
|
164
|
+
if isinstance(replacement, ir.Logical):
|
|
165
|
+
# if replacement is a logical, just keep the same hoisted vars
|
|
166
|
+
hoisted.update(replacement.hoisted)
|
|
167
|
+
else:
|
|
168
|
+
# otherwise, we need to hoist the vars that are output from local deps
|
|
169
|
+
# and input to the replacement task
|
|
170
|
+
dep_outputs = OrderedSet()
|
|
171
|
+
for d in plan.local_dependencies.get(child, ordered_set()):
|
|
172
|
+
dep_outputs.update(ctx.info.task_outputs(d))
|
|
173
|
+
hoisted.update(dep_outputs & ctx.info.task_inputs(replacement))
|
|
174
|
+
|
|
175
|
+
if plan.distribute_common_reference:
|
|
176
|
+
if len(composites_and_effects) == 1:
|
|
177
|
+
# if there's a single composite, just insert the whole common body into it
|
|
178
|
+
replacement_body.update(plan.common_body)
|
|
177
179
|
else:
|
|
178
|
-
#
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
# otherwise insert a clone of the reference on the extracted rule
|
|
181
|
+
assert(plan.common_reference)
|
|
182
|
+
replacement_body.add(plan.common_reference.clone())
|
|
183
|
+
# add remaining vars to hoisted, making sure there's no duplicates (due to VarOrDefault)
|
|
184
|
+
hoisted_vars = helpers.hoisted_vars(hoisted)
|
|
185
|
+
if remaining_vars:
|
|
186
|
+
hoisted = OrderedSet.from_iterable(filter(lambda v: v not in hoisted_vars, remaining_vars)) | hoisted
|
|
187
|
+
|
|
188
|
+
if child in plan.local_dependencies:
|
|
189
|
+
for local_dep in plan.local_dependencies[child]:
|
|
190
|
+
replacement_body.add(local_dep.clone())
|
|
191
|
+
|
|
192
|
+
if isinstance(replacement, ir.Logical):
|
|
193
|
+
# if the replacements is a logical, we can just add to the body
|
|
194
|
+
body.add(replacement.reconstruct(
|
|
195
|
+
replacement.engine,
|
|
196
|
+
tuple(hoisted.get_list()),
|
|
197
|
+
tuple(replacement_body.update(replacement.body).get_list()),
|
|
198
|
+
replacement.annotations
|
|
199
|
+
))
|
|
181
200
|
else:
|
|
182
|
-
#
|
|
201
|
+
# Otherwise, wrap the local dependencies in a Lookup where the output
|
|
202
|
+
# variables are hoisted, and keep the computed replacement.
|
|
203
|
+
body.add(f.logical(replacement_body.get_list(), hoisted.get_list(), replacement.engine))
|
|
183
204
|
body.add(replacement)
|
|
205
|
+
else:
|
|
206
|
+
# child does not need extras in the body, just add it to the main body
|
|
207
|
+
body.add(replacement)
|
|
184
208
|
|
|
185
|
-
|
|
209
|
+
return ir.Logical(task.engine, task.hoisted, tuple(body))
|
|
186
210
|
|
|
187
211
|
@dataclass
|
|
188
212
|
class ExtractionPlan():
|
|
@@ -206,16 +230,23 @@ class ExtractCommon(Pass):
|
|
|
206
230
|
Compute a plan to extract tasks in this frame that are common dependencies
|
|
207
231
|
across these composite tasks.
|
|
208
232
|
"""
|
|
209
|
-
#
|
|
233
|
+
# If there are any pragma lookups, then don't extract anything. Pragma lookups are
|
|
234
|
+
# designed to control execution order, and extracting them may affect their
|
|
235
|
+
# semantics.
|
|
236
|
+
for b in binders:
|
|
237
|
+
if isinstance(b, ir.Lookup) and builtins.is_pragma(b.relation):
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
# Compute intersection of task dependencies
|
|
210
241
|
sample = composites.some()
|
|
211
242
|
deps = ctx.info.task_dependencies(sample)
|
|
212
243
|
if deps is None:
|
|
213
244
|
return None
|
|
214
245
|
# only get sibling dependencies
|
|
215
246
|
common_body = binders & deps
|
|
216
|
-
exposed_vars = OrderedSet.from_iterable(ctx.info.task_inputs(sample))
|
|
217
247
|
|
|
218
|
-
#
|
|
248
|
+
# For other composites, remove their sibling dependencies so that we end up with
|
|
249
|
+
# the intersection of dependencies
|
|
219
250
|
for composite in composites:
|
|
220
251
|
if composite is sample:
|
|
221
252
|
continue
|
|
@@ -226,11 +257,23 @@ class ExtractCommon(Pass):
|
|
|
226
257
|
for task in common_body:
|
|
227
258
|
if task not in deps:
|
|
228
259
|
common_body.remove(task)
|
|
260
|
+
|
|
261
|
+
# Compute union of input vars
|
|
262
|
+
# Start with the output vars of the common body. We only want to expose vars that
|
|
263
|
+
# are output from the common body
|
|
264
|
+
body_output_vars = OrderedSet()
|
|
265
|
+
for child in common_body:
|
|
266
|
+
body_output_vars.update(ctx.info.task_outputs(child))
|
|
267
|
+
|
|
268
|
+
# Compute the union of input vars across all composites, intersected with output
|
|
269
|
+
# vars of the common body
|
|
270
|
+
exposed_vars = OrderedSet.from_iterable(ctx.info.task_inputs(sample)) & body_output_vars
|
|
271
|
+
for composite in composites:
|
|
272
|
+
if composite is sample:
|
|
273
|
+
continue
|
|
229
274
|
# compute common input vars
|
|
230
|
-
t_inputs = ctx.info.task_inputs(composite)
|
|
231
|
-
|
|
232
|
-
if v not in t_inputs:
|
|
233
|
-
exposed_vars.remove(v)
|
|
275
|
+
t_inputs = OrderedSet.from_iterable(ctx.info.task_inputs(composite))
|
|
276
|
+
exposed_vars.update(t_inputs & body_output_vars)
|
|
234
277
|
|
|
235
278
|
# no vars in common, not worth to extract
|
|
236
279
|
if not exposed_vars:
|
|
@@ -263,7 +306,7 @@ class ExtractCommon(Pass):
|
|
|
263
306
|
for binder in binders:
|
|
264
307
|
if binder not in common_body:
|
|
265
308
|
remaining.add(binder)
|
|
266
|
-
deps = self._compute_local_dependencies(ctx, binders, binder,
|
|
309
|
+
deps = self._compute_local_dependencies(ctx, binders, binder, exposed_vars)
|
|
267
310
|
if deps:
|
|
268
311
|
remaining.update(deps)
|
|
269
312
|
|
|
@@ -271,16 +314,17 @@ class ExtractCommon(Pass):
|
|
|
271
314
|
# depends on it but it is not exposed by the vars
|
|
272
315
|
local_dependencies: dict[ir.Task, OrderedSet[ir.Task]] = dict()
|
|
273
316
|
for composite in composites:
|
|
274
|
-
local = self._compute_local_dependencies(ctx, binders, composite,
|
|
317
|
+
local = self._compute_local_dependencies(ctx, binders, composite, exposed_vars)
|
|
275
318
|
if local:
|
|
276
319
|
local_dependencies[composite] = local
|
|
277
320
|
|
|
278
321
|
# distribute the common reference only if all of the composites are extractable and there's nothing else remaining
|
|
279
322
|
distribute_common_reference = len(extractables) == len(composites) and not remaining
|
|
323
|
+
|
|
280
324
|
return ExtractCommon.ExtractionPlan(common_body, remaining, exposed_vars, local_dependencies, distribute_common_reference)
|
|
281
325
|
|
|
282
326
|
|
|
283
|
-
def _compute_local_dependencies(self, ctx: Context, binders: OrderedSet[ir.Task], composite: ir.Task,
|
|
327
|
+
def _compute_local_dependencies(self, ctx: Context, binders: OrderedSet[ir.Task], composite: ir.Task, exposed_vars: OrderedSet[ir.Var]):
|
|
284
328
|
"""
|
|
285
329
|
The tasks in common_body will be extracted into a logical that will expose the exposed_vars.
|
|
286
330
|
Compute which additional dependencies are needed specifically for this composite, because
|
|
@@ -298,145 +342,12 @@ class ExtractCommon(Pass):
|
|
|
298
342
|
if not vars_needed:
|
|
299
343
|
return None
|
|
300
344
|
|
|
301
|
-
# needs = ctx.info.local_dependencies(composite) & binders | common_body
|
|
302
|
-
# if needs:
|
|
303
|
-
# return needs
|
|
304
|
-
# return None
|
|
305
|
-
|
|
306
|
-
# this is a greedy algorithm that uses the first task in the common body that provides
|
|
307
|
-
# a variable needed; it may result in sub-optimal extraction, but should be correct
|
|
308
|
-
local_body = ordered_set()
|
|
309
|
-
while(vars_needed):
|
|
310
|
-
v = vars_needed.pop()
|
|
311
|
-
for x in common_body:
|
|
312
|
-
if x not in local_body:
|
|
313
|
-
# an x that is not yet in local_body can fulfill v
|
|
314
|
-
x_outputs = ctx.info.task_outputs(x)
|
|
315
|
-
if x_outputs and v in x_outputs:
|
|
316
|
-
# add it to local_body and add its outputs to vars exposed
|
|
317
|
-
local_body.add(x)
|
|
318
|
-
vars_exposed.add(x_outputs)
|
|
319
|
-
# but add its inputs the vars now needed
|
|
320
|
-
inputs = ctx.info.task_inputs(x)
|
|
321
|
-
if inputs:
|
|
322
|
-
vars_needed.update(inputs - vars_exposed)
|
|
323
|
-
return local_body
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def _create_extraction_plan_old(self, binders: OrderedSet[ir.Task], composites: OrderedSet[ir.Task], extractables: list[ir.Task], ctx: Context):
|
|
330
|
-
"""
|
|
331
|
-
Compute a plan to extract tasks in this frame that are common dependencies
|
|
332
|
-
across these composite tasks.
|
|
333
|
-
"""
|
|
334
|
-
# compute intersection of task dependencies and inputs
|
|
335
|
-
sample = composites.some()
|
|
336
|
-
deps = ctx.info.task_dependencies(sample)
|
|
337
|
-
if deps is None:
|
|
338
|
-
return None
|
|
339
|
-
# only get sibling dependencies
|
|
340
|
-
common_body = binders & deps
|
|
341
|
-
exposed_vars = OrderedSet.from_iterable(ctx.info.task_inputs(sample))
|
|
342
|
-
|
|
343
|
-
# now extract from the original sets
|
|
344
|
-
for composite in composites:
|
|
345
|
-
if composite is sample:
|
|
346
|
-
continue
|
|
347
|
-
|
|
348
|
-
# compute sibling dependencies
|
|
349
|
-
deps = ctx.info.task_dependencies(composite)
|
|
350
|
-
if deps:
|
|
351
|
-
for task in common_body:
|
|
352
|
-
if task not in deps:
|
|
353
|
-
common_body.remove(task)
|
|
354
|
-
# compute common input vars
|
|
355
|
-
t_inputs = ctx.info.task_inputs(composite)
|
|
356
|
-
for v in exposed_vars:
|
|
357
|
-
if v not in t_inputs:
|
|
358
|
-
exposed_vars.remove(v)
|
|
359
|
-
|
|
360
|
-
# no vars in common, not worth to extract
|
|
361
|
-
if not exposed_vars:
|
|
362
|
-
return None
|
|
363
|
-
|
|
364
|
-
# pull the transitive closure of the intersected tasks in the common body
|
|
365
|
-
chasing = True
|
|
366
|
-
while(chasing):
|
|
367
|
-
chasing = False
|
|
368
|
-
for task in common_body:
|
|
369
|
-
deps = ctx.info.task_dependencies(task)
|
|
370
|
-
if deps:
|
|
371
|
-
for hop in binders & deps:
|
|
372
|
-
if hop not in common_body:
|
|
373
|
-
common_body.add(hop)
|
|
374
|
-
chasing = True
|
|
375
|
-
|
|
376
|
-
# not useful to extract common tasks if there's a single one
|
|
377
|
-
if len(common_body) < 2:
|
|
378
|
-
return None
|
|
379
|
-
|
|
380
|
-
# check if some variable used in the common body is needed by some binder that is
|
|
381
|
-
# not going to be extracted. In that case, we need to expose this variable from the
|
|
382
|
-
# common body
|
|
383
|
-
common_vars = ordered_set()
|
|
384
|
-
for task in common_body:
|
|
385
|
-
common_vars.update(ctx.info.task_outputs(task))
|
|
386
|
-
common_vars = common_vars - exposed_vars
|
|
387
|
-
for v in common_vars:
|
|
388
|
-
for binder in binders:
|
|
389
|
-
if binder not in common_body and ctx.info.task_inputs(binder) and v in ctx.info.task_inputs(binder):
|
|
390
|
-
exposed_vars.add(v)
|
|
391
|
-
break
|
|
392
|
-
|
|
393
|
-
# check with of the original binders remain, and make sure their dependencies also stay
|
|
394
|
-
remaining = ordered_set()
|
|
395
|
-
for binder in binders:
|
|
396
|
-
if binder not in common_body:
|
|
397
|
-
remaining.add(binder)
|
|
398
|
-
deps = self._compute_local_dependencies_old(ctx, binder, common_body, exposed_vars)
|
|
399
|
-
if deps:
|
|
400
|
-
remaining.update(deps)
|
|
401
|
-
|
|
402
|
-
# for each composite, check if there are additional tasks needed, because the task
|
|
403
|
-
# depends on it but it is not exposed by the vars
|
|
404
|
-
local_dependencies: dict[ir.Task, OrderedSet[ir.Task]] = dict()
|
|
405
|
-
for composite in composites:
|
|
406
|
-
local = self._compute_local_dependencies_old(ctx, composite, common_body, exposed_vars)
|
|
407
|
-
if local:
|
|
408
|
-
local_dependencies[composite] = local
|
|
409
|
-
|
|
410
|
-
# distribute the common reference only if all of the composites are extractable and there's nothing else remaining
|
|
411
|
-
distribute_common_reference = len(extractables) == len(composites) and not remaining
|
|
412
|
-
# TODO: we are forcing this for now, but we should remove the whole analysis
|
|
413
|
-
distribute_common_reference = False
|
|
414
|
-
return ExtractCommon.ExtractionPlan(common_body, remaining, exposed_vars, local_dependencies, distribute_common_reference)
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
def _compute_local_dependencies_old(self, ctx: Context, composite: ir.Task, common_body: OrderedSet[ir.Task], exposed_vars: OrderedSet[ir.Var]):
|
|
418
|
-
"""
|
|
419
|
-
The tasks in common_body will be extracted into a logical that will expose the exposed_vars.
|
|
420
|
-
Compute which additional dependencies are needed specifically to this composite, because
|
|
421
|
-
it depends on some tasks that are extracted to common_body but not exposed by exposed_vars.
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
# vars exposed by exposed vars + tasks added to the local body
|
|
425
|
-
vars_exposed = OrderedSet.from_iterable(exposed_vars)
|
|
426
|
-
# working list of vars we still need to fulfill
|
|
427
|
-
inputs = ctx.info.task_inputs(composite)
|
|
428
|
-
if not inputs:
|
|
429
|
-
return None
|
|
430
|
-
vars_needed = (inputs - vars_exposed)
|
|
431
|
-
if not vars_needed:
|
|
432
|
-
return None
|
|
433
|
-
|
|
434
345
|
# this is a greedy algorithm that uses the first task in the common body that provides
|
|
435
346
|
# a variable needed; it may result in sub-optimal extraction, but should be correct
|
|
436
347
|
local_body = ordered_set()
|
|
437
348
|
while(vars_needed):
|
|
438
349
|
v = vars_needed.pop()
|
|
439
|
-
for x in
|
|
350
|
+
for x in binders:
|
|
440
351
|
if x not in local_body:
|
|
441
352
|
# an x that is not yet in local_body can fulfill v
|
|
442
353
|
x_outputs = ctx.info.task_outputs(x)
|