relationalai 0.12.11__py3-none-any.whl → 0.12.12__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.
@@ -29,8 +29,8 @@ def lqp_passes() -> list[Pass]:
29
29
  InferTypes(),
30
30
  DNFUnionSplitter(),
31
31
  ExtractKeys(),
32
- ExtractCommon(),
33
32
  FormatOutputs(),
33
+ ExtractCommon(), # Extracts tasks that will become common after Flatten into their own definition
34
34
  Flatten(),
35
35
  Splinter(), # Splits multi-headed rules into multiple rules
36
36
  QuantifyVars(), # Adds missing existentials
@@ -10,7 +10,7 @@ from relationalai.semantics.lqp.rewrite.functional_dependencies import (
10
10
  is_valid_unique_constraint, normalized_fd
11
11
  )
12
12
 
13
-
13
+ _DISABLE_CONSTRAINT_DECLARATIONS = True
14
14
 
15
15
  class AnnotateConstraints(Pass):
16
16
  """
@@ -36,6 +36,8 @@ class AnnotateConstraintsRewriter(DischargeConstraintsVisitor):
36
36
  """
37
37
 
38
38
  def _should_be_declarable_constraint(self, node: Require) -> bool:
39
+ if _DISABLE_CONSTRAINT_DECLARATIONS:
40
+ return False
39
41
  if not is_valid_unique_constraint(node):
40
42
  return False
41
43
  # Currently, we only declare non-structural functional dependencies.
@@ -3,11 +3,13 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from typing import Optional
5
5
  from relationalai.semantics.metamodel import ir, factory as f, helpers, visitor
6
- from relationalai.semantics.metamodel.compiler import Pass, group_tasks
6
+ from relationalai.semantics.metamodel.compiler import Pass
7
7
  from relationalai.semantics.metamodel.util import OrderedSet, ordered_set
8
8
  from relationalai.semantics.metamodel import dependency
9
9
  from relationalai.semantics.metamodel import builtins
10
10
 
11
+ from functools import reduce
12
+
11
13
  class ExtractCommon(Pass):
12
14
  """
13
15
  Pass to analyze Logical bodies and extract lookups in their own Logical if it makes
@@ -71,7 +73,7 @@ class ExtractCommon(Pass):
71
73
  )
72
74
 
73
75
  #--------------------------------------------------
74
- # IR handlers
76
+ # Extra classes
75
77
  #--------------------------------------------------
76
78
 
77
79
  class Context():
@@ -80,8 +82,26 @@ class ExtractCommon(Pass):
80
82
  self.info = dependency.analyze(model.root)
81
83
  self.options = options
82
84
 
85
+ @dataclass
86
+ class ExtractionPlan():
87
+ # tasks to extract to the body of the common logical
88
+ common_body: OrderedSet[ir.Task]
89
+ # tasks to remain in the original body
90
+ remaining_body: OrderedSet[ir.Task]
91
+ # variables to be exposed by the common logical
92
+ exposed_vars: OrderedSet[ir.Var]
93
+ # map from nested composite to the tasks in the common body that still need to be
94
+ # included in its body, because it contains variables not exposed by the common logical
95
+ local_dependencies: dict[ir.Task, OrderedSet[ir.Task]]
96
+ # a reference to the common connection created for this plan, if any
97
+ common_reference: Optional[ir.Lookup] = None
98
+
99
+ #--------------------------------------------------
100
+ # IR handlers
101
+ #--------------------------------------------------
102
+
83
103
  def handle(self, task: ir.Task, ctx: Context):
84
- # currently we only extract if it's a sequence of Logicals, but we could in the
104
+ # Currently we only extract if it's a sequence of Logicals, but we could in the
85
105
  # future support other intermediate nodes
86
106
  if isinstance(task, ir.Logical):
87
107
  return self.handle_logical(task, ctx)
@@ -89,66 +109,52 @@ class ExtractCommon(Pass):
89
109
  return task
90
110
 
91
111
  def handle_logical(self, task: ir.Logical, ctx: Context):
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
112
+ # Process the original body to find candidates for extraction. Each task is in one
113
+ # of three categories:
114
+ # - Binders: tasks that bind variables. These are candidates for extracting into
115
+ # the common body.
116
+ # - Flattenables: tasks that will later be extracted by the Flatten pass
117
+ # - Other: tasks that are neither binders nor flattenables; these will remain
118
+ # in the body as-is.
119
+
120
+ binders = ordered_set()
121
+ flattenables = ordered_set()
122
+ other = ordered_set()
123
+
124
+ for child in task.body:
125
+ if _is_binder(child):
126
+ binders.add(child)
127
+ elif _is_flattenable(ctx, child):
128
+ flattenables.add(child)
129
+ else:
130
+ other.add(child)
131
+
132
+ # The new body of the rewritten task
102
133
  body:OrderedSet[ir.Task] = ordered_set()
103
134
 
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
135
+ # Quick check to see if it's worth doing more analysis; we only want to extract
136
+ # common binders if there are multiple, and there are also multiple flattenables
106
137
  # that will be extracted by the flatten pass later (so that they can share the
107
138
  # extracted logic).
108
- plan = None
109
- if len(binders) > 1 and composites_and_effects:
110
- extractables = self._get_extractables(ctx, 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
132
- for child in task.body:
133
- if child in groups["other"] or child not in plan.remaining_body or child in composites_and_effects:
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
138
-
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
139
+ plan: Optional[ExtractCommon.ExtractionPlan] = None
140
+ if len(binders) > 1 and len(flattenables) > 1:
141
+ plan = _create_extraction_plan(ctx, binders, flattenables, other)
142
+ if plan:
143
+ # plan is worthwhile, extract the common body and add the connection to the body
144
+ exposed_vars = plan.exposed_vars.get_list()
145
+ plan.common_reference = f.lookup(helpers.extract(task, plan.common_body, exposed_vars, ctx.rewrite_ctx, "common"), exposed_vars)
146
+
147
+ # Add plan common reference to the body.
148
+ body.add(plan.common_reference)
143
149
 
144
150
  # recursively handle children
145
151
  for child in task.body:
146
152
  # 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:
153
+ if plan and child not in other and child not in plan.remaining_body and child not in flattenables:
148
154
  continue
149
155
 
150
156
  # 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:
157
+ if not plan or child not in flattenables:
152
158
  body.add(self.handle(child, ctx))
153
159
  continue
154
160
 
@@ -156,7 +162,7 @@ class ExtractCommon(Pass):
156
162
  replacement = self.handle(child, ctx)
157
163
 
158
164
  # this child needs either extra local dependencies or the common reference
159
- if child in plan.local_dependencies or plan.distribute_common_reference:
165
+ if child in plan.local_dependencies:
160
166
  # the new body will have maybe the common reference and the local deps
161
167
  replacement_body = ordered_set()
162
168
 
@@ -172,19 +178,6 @@ class ExtractCommon(Pass):
172
178
  dep_outputs.update(ctx.info.task_outputs(d))
173
179
  hoisted.update(dep_outputs & ctx.info.task_inputs(replacement))
174
180
 
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)
179
- else:
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
181
  if child in plan.local_dependencies:
189
182
  for local_dep in plan.local_dependencies[child]:
190
183
  replacement_body.add(local_dep.clone())
@@ -208,177 +201,138 @@ class ExtractCommon(Pass):
208
201
 
209
202
  return ir.Logical(task.engine, task.hoisted, tuple(body))
210
203
 
211
- @dataclass
212
- class ExtractionPlan():
213
- # tasks to extract to the body of the common logical
214
- common_body: OrderedSet[ir.Task]
215
- # tasks to remain in the original body
216
- remaining_body: OrderedSet[ir.Task]
217
- # variables to be exposed by the common logical
218
- exposed_vars: OrderedSet[ir.Var]
219
- # map from nested composite to the tasks in the common body that still need to be
220
- # included in its body, because it contains variables not exposed by the common logical
221
- local_dependencies: dict[ir.Task, OrderedSet[ir.Task]]
222
- # whether the common reference should be distributed to composites
223
- distribute_common_reference: bool
224
- # a reference to the common connection created for this plan, if any
225
- common_reference: Optional[ir.Lookup] = None
226
204
 
205
+ #--------------------------------------------------
206
+ # Utilities
207
+ #--------------------------------------------------
227
208
 
228
- def _create_extraction_plan(self, binders: OrderedSet[ir.Task], composites: OrderedSet[ir.Task], extractables: list[ir.Task], ctx: Context):
229
- """
230
- Compute a plan to extract tasks in this frame that are common dependencies
231
- across these composite tasks.
232
- """
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
241
- sample = composites.some()
242
- deps = ctx.info.task_dependencies(sample)
243
- if deps is None:
209
+ def _create_extraction_plan(ctx: ExtractCommon.Context, binders: OrderedSet[ir.Task], flattenables: OrderedSet[ir.Task], others: OrderedSet[ir.Task]) -> Optional[ExtractCommon.ExtractionPlan]:
210
+ """
211
+ Compute a plan to extract tasks in this frame that are common dependencies
212
+ across these composite tasks.
213
+ """
214
+ # If there are any pragma lookups, then don't extract anything. Pragma lookups are
215
+ # designed to control execution order, and extracting them may affect their
216
+ # semantics.
217
+ for b in binders:
218
+ if isinstance(b, ir.Lookup) and builtins.is_pragma(b.relation):
244
219
  return None
245
- # only get sibling dependencies
246
- common_body = binders & deps
247
-
248
- # For other composites, remove their sibling dependencies so that we end up with
249
- # the intersection of dependencies
250
- for composite in composites:
251
- if composite is sample:
252
- continue
253
220
 
254
- # compute sibling dependencies
255
- deps = ctx.info.task_dependencies(composite)
221
+ # Compute intersection of task dependencies
222
+ all_deps = [ctx.info.task_dependencies(f) for f in flattenables]
223
+ deps = reduce(lambda a, b: a & b, all_deps)
224
+ common_body = binders & deps
225
+
226
+ # We don't need to extract anything if there's only zero or one common tasks
227
+ if len(common_body) < 2:
228
+ return None
229
+
230
+ # Keep track of remaining tasks that are not extracted in the common body
231
+ remaining = ordered_set()
232
+
233
+ # Compute the vars that should be output from the common body. These are the union of
234
+ # all input vars across all non-extracted tasks, intersected with output vars of
235
+ # the common body.
236
+
237
+ # First, compute the output vars of the common body
238
+ common_body_output_vars = OrderedSet()
239
+ for child in common_body:
240
+ common_body_output_vars.update(ctx.info.task_outputs(child))
241
+
242
+ # Next, compute the union of the input vars of all non-extracted tasks
243
+ non_extracted_tasks = (binders - common_body) | flattenables | others
244
+ all_exposed_vars: list[OrderedSet[ir.Var]] = []
245
+ for t in non_extracted_tasks:
246
+ input_vars = ctx.info.task_inputs(t)
247
+ all_exposed_vars.append(input_vars if input_vars else OrderedSet())
248
+
249
+ exposed_vars = reduce(lambda a, b: a | b, all_exposed_vars) & common_body_output_vars
250
+
251
+ # If there are no vars in common, then it's not worth extracting
252
+ if not exposed_vars:
253
+ return None
254
+
255
+ # Make sure that all local dependencies of the common body are included in the common
256
+ # body. This is important for the safety of this rewrite.
257
+ for task in common_body:
258
+ local_deps = ctx.info.local_dependencies(task)
259
+ if local_deps:
260
+ common_body.update(local_deps & binders)
261
+
262
+ # check which of the original binders remain, and make sure their dependencies also stay
263
+ for binder in binders:
264
+ if binder not in common_body:
265
+ remaining.add(binder)
266
+ deps = _compute_local_dependencies(ctx, binders, binder, exposed_vars)
256
267
  if deps:
257
- for task in common_body:
258
- if task not in deps:
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 non-extracted tasks (basically
269
- # composites and binders left behind), intersected with output
270
- # vars of the common body
271
- exposed_vars = OrderedSet.from_iterable(ctx.info.task_inputs(sample)) & body_output_vars
272
- non_extracted_tasks = (binders - common_body) | composites
273
- for composite in non_extracted_tasks:
274
- if composite is sample:
275
- continue
276
- # compute common input vars
277
- t_inputs = OrderedSet.from_iterable(ctx.info.task_inputs(composite))
278
- exposed_vars.update(t_inputs & body_output_vars)
268
+ remaining.update(deps)
279
269
 
280
- # no vars in common, not worth to extract
281
- if not exposed_vars:
282
- return None
270
+ # for each composite, check if there are additional tasks needed, because the task
271
+ # depends on it but it is not exposed by the vars
272
+ local_dependencies: dict[ir.Task, OrderedSet[ir.Task]] = dict()
273
+ for flattenable in flattenables:
274
+ local = _compute_local_dependencies(ctx, binders, flattenable, exposed_vars)
275
+ if local:
276
+ local_dependencies[flattenable] = local
283
277
 
284
- for task in common_body:
285
- local_deps = ctx.info.local_dependencies(task)
286
- if local_deps:
287
- common_body.update(local_deps & binders)
278
+ return ExtractCommon.ExtractionPlan(common_body, remaining, exposed_vars, local_dependencies)
288
279
 
289
- # not useful to extract common tasks if there's a single one
290
- if len(common_body) < 2:
291
- return None
292
-
293
- # check if some variable used in the common body is needed by some binder that is
294
- # not going to be extracted. In that case, we need to expose this variable from the
295
- # common body
296
- common_vars = ordered_set()
297
- for task in common_body:
298
- common_vars.update(ctx.info.task_outputs(task))
299
- common_vars = common_vars - exposed_vars
300
- for v in common_vars:
301
- for binder in binders:
302
- if binder not in common_body and ctx.info.task_inputs(binder) and v in ctx.info.task_inputs(binder):
303
- exposed_vars.add(v)
304
- break
305
-
306
- # check which of the original binders remain, and make sure their dependencies also stay
307
- remaining = ordered_set()
308
- for binder in binders:
309
- if binder not in common_body:
310
- remaining.add(binder)
311
- deps = self._compute_local_dependencies(ctx, binders, binder, exposed_vars)
312
- if deps:
313
- remaining.update(deps)
314
-
315
- # for each composite, check if there are additional tasks needed, because the task
316
- # depends on it but it is not exposed by the vars
317
- local_dependencies: dict[ir.Task, OrderedSet[ir.Task]] = dict()
318
- for composite in composites:
319
- local = self._compute_local_dependencies(ctx, binders, composite, exposed_vars)
320
- if local:
321
- local_dependencies[composite] = local
322
-
323
- # distribute the common reference only if all of the composites are extractable and there's nothing else remaining
324
- distribute_common_reference = len(extractables) == len(composites) and not remaining
325
-
326
- return ExtractCommon.ExtractionPlan(common_body, remaining, exposed_vars, local_dependencies, distribute_common_reference)
327
-
328
- def _compute_local_dependencies(self, ctx: Context, binders: OrderedSet[ir.Task], composite: ir.Task, exposed_vars: OrderedSet[ir.Var]):
329
- """
330
- The tasks in common_body will be extracted into a logical that will expose the exposed_vars.
331
- Compute which additional dependencies are needed specifically for this composite, because
332
- it depends on some tasks that are extracted to common_body but not exposed by exposed_vars.
333
- """
334
-
335
- # working list of vars we still need to fulfill
336
- inputs = ctx.info.task_inputs(composite)
337
- if not inputs:
338
- return None
339
-
340
- # vars exposed by exposed vars + tasks added to the local body
341
- vars_exposed = OrderedSet.from_iterable(exposed_vars)
342
- vars_needed = (inputs - vars_exposed)
343
- if not vars_needed:
344
- return None
280
+ def _compute_local_dependencies(ctx: ExtractCommon.Context, binders: OrderedSet[ir.Task], composite: ir.Task, exposed_vars: OrderedSet[ir.Var]):
281
+ """
282
+ The tasks in common_body will be extracted into a logical that will expose the exposed_vars.
283
+ Compute which additional dependencies are needed specifically for this composite, because
284
+ it depends on some tasks that are extracted to common_body but not exposed by exposed_vars.
285
+ """
345
286
 
346
- # this is a greedy algorithm that uses the first task in the common body that provides
347
- # a variable needed; it may result in sub-optimal extraction, but should be correct
348
- local_body = ordered_set()
349
- while(vars_needed):
350
- v = vars_needed.pop()
351
- for x in binders:
352
- if x not in local_body:
353
- # an x that is not yet in local_body can fulfill v
354
- x_outputs = ctx.info.task_outputs(x)
355
- if x_outputs and v in x_outputs:
356
- # add it to local_body and add its outputs to vars exposed
357
- local_body.add(x)
358
- vars_exposed.add(x_outputs)
359
- # but add its inputs the vars now needed
360
- inputs = ctx.info.task_inputs(x)
361
- if inputs:
362
- vars_needed.update(inputs - vars_exposed)
363
- return local_body
364
-
365
- def _get_extractables(self, ctx: Context, composites: OrderedSet[ir.Task]):
366
- """
367
- Extractables are tasks that will eventually be extracted by the Flatten pass later.
368
- Given a set of tasks, return the extractable ones.
369
- """
370
- def _extractable(t: ir.Task):
371
- # With GNF outputs (i.e., wide_outputs = False), the output tasks will be
372
- # extracted into separate top-level single-column outputs.
373
- if isinstance(t, ir.Output) and not ctx.options.get("wide_outputs", False):
374
- return True
375
-
376
- extractable_types = (ir.Update, ir.Aggregate, ir.Match, ir.Union, ir.Rank)
377
- return isinstance(t, ir.Logical) and len(visitor.collect_by_type(extractable_types, t)) > 0
378
-
379
- extractables = []
380
- for t in composites:
381
- if _extractable(t):
382
- extractables.append(t)
383
-
384
- return extractables
287
+ # working list of vars we still need to fulfill
288
+ inputs = ctx.info.task_inputs(composite)
289
+ if not inputs:
290
+ return None
291
+
292
+ # vars exposed by exposed vars + tasks added to the local body
293
+ vars_exposed = OrderedSet.from_iterable(exposed_vars)
294
+ vars_needed = (inputs - vars_exposed)
295
+ if not vars_needed:
296
+ return None
297
+
298
+ # this is a greedy algorithm that uses the first task in the common body that provides
299
+ # a variable needed; it may result in sub-optimal extraction, but should be correct
300
+ local_body = ordered_set()
301
+ while(vars_needed):
302
+ v = vars_needed.pop()
303
+ for x in binders:
304
+ if x not in local_body:
305
+ # an x that is not yet in local_body can fulfill v
306
+ x_outputs = ctx.info.task_outputs(x)
307
+ if x_outputs and v in x_outputs:
308
+ # add it to local_body and add its outputs to vars exposed
309
+ local_body.add(x)
310
+ vars_exposed.add(x_outputs)
311
+ # but add its inputs the vars now needed
312
+ inputs = ctx.info.task_inputs(x)
313
+ if inputs:
314
+ vars_needed.update(inputs - vars_exposed)
315
+ return local_body
316
+
317
+ def _is_binder(task: ir.Task):
318
+ # If the task itself is a binder
319
+ if any(isinstance(task, binder) for binder in (ir.Lookup, ir.Construct, ir.Exists, ir.Data, ir.Not)):
320
+ return True
321
+
322
+ # If the task is a Logical containing only binders
323
+ if isinstance(task, ir.Logical) and all(_is_binder(c) for c in task.body):
324
+ return True
325
+
326
+ # If the task is a Union containing only binders
327
+ if isinstance(task, ir.Union) and all(_is_binder(c) for c in task.tasks):
328
+ return True
329
+
330
+ return False
331
+
332
+ def _is_flattenable(ctx: ExtractCommon.Context, task: ir.Task):
333
+ # Each output will be flattened into its own top-level def
334
+ if isinstance(task, ir.Output):
335
+ return True
336
+
337
+ extractable_types = (ir.Update, ir.Aggregate, ir.Match, ir.Rank)
338
+ return isinstance(task, ir.Logical) and len(visitor.collect_by_type(extractable_types, task)) > 0
@@ -140,13 +140,13 @@ class DNFExtractor(Visitor):
140
140
  for new_task in replacement_tasks:
141
141
  # copy to mutate
142
142
  new_body = list(body)
143
- new_body.append(new_task)
143
+ new_body.append(new_task.clone())
144
144
  new_replacement_bodies.append(new_body)
145
145
  replacement_bodies = new_replacement_bodies
146
146
 
147
147
  else:
148
148
  for new_body in replacement_bodies:
149
- new_body.append(task)
149
+ new_body.append(task.clone())
150
150
 
151
151
  replacement_tasks: list[ir.Task] = []
152
152
  for body in replacement_bodies:
@@ -211,6 +211,17 @@ class Flatten(Pass):
211
211
  if child in composites:
212
212
  all_composites_removed = False
213
213
 
214
+ # Filter out empty logicals from the body
215
+ new_body:OrderedSet[ir.Task] = ordered_set()
216
+ for b in body:
217
+ if isinstance(b, ir.Logical):
218
+ if not b.body:
219
+ # empty logical, skip
220
+ continue
221
+ new_body.add(b)
222
+
223
+ body = new_body
224
+
214
225
  # all children were extracted or all composites were removed without any effects
215
226
  # left and no outputs (so no way for outer dependencies), drop this logical
216
227
  if not body or (all_composites_removed and not any([isinstance(t, helpers.EFFECTS) for t in body]) and not ctx.info.task_outputs(task)):
@@ -33,8 +33,8 @@ class Compiler(c.Compiler):
33
33
  InferTypes(),
34
34
  DNFUnionSplitter(),
35
35
  ExtractKeys(),
36
- ExtractCommon(),
37
36
  FormatOutputs(),
37
+ ExtractCommon(),
38
38
  Flatten(),
39
39
  Splinter(),
40
40
  QuantifyVars(),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relationalai
3
- Version: 0.12.11
3
+ Version: 0.12.12
4
4
  Summary: RelationalAI Library and CLI
5
5
  Author-email: RelationalAI <support@relational.ai>
6
6
  License-File: LICENSE
@@ -314,7 +314,7 @@ relationalai/semantics/lqp/executor.py,sha256=GuPiSJpLaMYqjZOlVEFLOUilyuLcfkQgZ6
314
314
  relationalai/semantics/lqp/intrinsics.py,sha256=oKPIcW8PYgU-yPTO21iSF00RBsFKPFFP5MICe6izjKk,871
315
315
  relationalai/semantics/lqp/ir.py,sha256=6W9mUH0W7u5eIfF1S3o33uSOfQuM3UcqEkxrxpr1X_8,1867
316
316
  relationalai/semantics/lqp/model2lqp.py,sha256=PTe2PtHioWAclu6tdkCu5iGaHJxPIe7zIG1Ew72qxDo,36957
317
- relationalai/semantics/lqp/passes.py,sha256=i5LrS92Wn-Aa6Hase4UXlogUEGL_tUYHNAYDr16CVw0,28540
317
+ relationalai/semantics/lqp/passes.py,sha256=bpRCdssqcSlVw77tam6EIjI0BypDoFKGWJix8dTywhU,28621
318
318
  relationalai/semantics/lqp/pragmas.py,sha256=FzzldrJEAZ1AIcEw6D-FfaVg3CoahRYgPCFo7xHfg1g,375
319
319
  relationalai/semantics/lqp/primitives.py,sha256=9Hjow-Yp06jt0xatuUrH1dw0ErnzknIr9K0TB_AwdjU,11029
320
320
  relationalai/semantics/lqp/result_helpers.py,sha256=oYpLoTBnzsiyOVIWA2rLMHlgs7P7BoEkqthQ2aMosnk,10123
@@ -322,9 +322,9 @@ relationalai/semantics/lqp/types.py,sha256=3TZ61ybwNV8lDyUMujZIWNFz3Fgn4uifsJb8E
322
322
  relationalai/semantics/lqp/utils.py,sha256=iOoS-f8kyFjrgAnpK4cWDvAA-WmPgDRggSKUXm_JdTc,6317
323
323
  relationalai/semantics/lqp/validators.py,sha256=FlKMKclHj0L71QUtl0aqKknqksSWM-di4N9bjGDJvnY,1561
324
324
  relationalai/semantics/lqp/rewrite/__init__.py,sha256=V9ERED9qdh4VvY9Ud_M8Zn8lhVANdOGIgW03l55sGj0,492
325
- relationalai/semantics/lqp/rewrite/annotate_constraints.py,sha256=W0_pQYWuq7ocEWNp5rWnq7YjRuJwsEHan1gIrAiRCXU,2201
325
+ relationalai/semantics/lqp/rewrite/annotate_constraints.py,sha256=b_Ly4_80dQpRzWbeLC72JVfxzhwOPBpiCdEqtBiEiwM,2310
326
326
  relationalai/semantics/lqp/rewrite/cdc.py,sha256=I6DeMOZScx-3UAVoSCMn9cuOgLzwdvJVKNwsgFa6R_k,10390
327
- relationalai/semantics/lqp/rewrite/extract_common.py,sha256=sbihURqk4wtc1ekDWXWltq9LrO42XTLfOHl5D6nT5vw,18371
327
+ relationalai/semantics/lqp/rewrite/extract_common.py,sha256=ZRvmeYHN8JEkU-j3fRx1e0_JK-46n6NqhxtwZe6L10c,14690
328
328
  relationalai/semantics/lqp/rewrite/extract_keys.py,sha256=iSbwGQG9p8j-erknEwl2pZkunJEpXTlnL9ohr4KVS8M,19317
329
329
  relationalai/semantics/lqp/rewrite/function_annotations.py,sha256=9ZzLASvXh_OgQ04eup0AyoMIh2HxWHkoRETLm1-XtWs,4660
330
330
  relationalai/semantics/lqp/rewrite/functional_dependencies.py,sha256=4oQcVQtAGDqY850B1bNszigQopf6y9Y_CaUyWx42PtM,12718
@@ -344,9 +344,9 @@ relationalai/semantics/metamodel/util.py,sha256=cmSmeww34JVMqcFudwVAY820IPM2ETSE
344
344
  relationalai/semantics/metamodel/visitor.py,sha256=DFY0DACLhxlZ0e4p0vWqbK6ZJr_GWEvH66CU_HVuoTk,35527
345
345
  relationalai/semantics/metamodel/rewrite/__init__.py,sha256=9ONWFSdMPHkWpObDMSljt8DywhpFf4Ehsq1aT3fTPt8,344
346
346
  relationalai/semantics/metamodel/rewrite/discharge_constraints.py,sha256=0v613BqCLlo4sgWuZjcLSxxakp3d34mYWbG4ldhzGno,1949
347
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py,sha256=ZXX190gCKXhdB-Iyi4MGowc4FS9P0PIJTtTT0LrTr6A,7970
347
+ relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py,sha256=piV8FEr4GHKSDcCcsu_TLHJHrsX7blTq4TUvGpGrN8Q,7986
348
348
  relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py,sha256=vQ0-7t_GORskB1ZG50KuzM4phm6YNPvehfFn3v_LbgI,3354
349
- relationalai/semantics/metamodel/rewrite/flatten.py,sha256=SWvDA3NJqU0ZEJ3kzaXaJ6ZC9xeHuZ8Ju9S1jxU1Nyc,22189
349
+ relationalai/semantics/metamodel/rewrite/flatten.py,sha256=CMCFrMCPIQJs9Ln8TJBJYbZaw6EpIcyWh6N6HaZlLQA,22513
350
350
  relationalai/semantics/metamodel/rewrite/format_outputs.py,sha256=n0IxC3RL3UMly6MWsq342EGfL2yGj3vOgVG_wg7kt-o,6225
351
351
  relationalai/semantics/metamodel/typer/__init__.py,sha256=E3ydmhWRdm-cAqWsNR24_Qd3NcwiHx8ElO2tzNysAXc,143
352
352
  relationalai/semantics/metamodel/typer/checker.py,sha256=frY0gilDO6skbDiYFiIpDUOWyt9s9jAJsRBs848DcG0,19184
@@ -364,7 +364,7 @@ relationalai/semantics/reasoners/optimization/solvers_dev.py,sha256=lbw3c8Z6PlHR
364
364
  relationalai/semantics/reasoners/optimization/solvers_pb.py,sha256=ESwraHU9c4NCEVRZ16tnBZsUCmJg7lUhy-v0-GGq0qo,48000
365
365
  relationalai/semantics/rel/__init__.py,sha256=pMlVTC_TbQ45mP1LpzwFBBgPxpKc0H3uJDvvDXEWzvs,55
366
366
  relationalai/semantics/rel/builtins.py,sha256=kQToiELc4NnvCmXyFtu9CsGZNdTQtSzTB-nuyIfQcsM,1562
367
- relationalai/semantics/rel/compiler.py,sha256=-gyu_kL1p4Z-SXI2fwLXSbGfUy-ejQcKVrc66rVvOyg,43044
367
+ relationalai/semantics/rel/compiler.py,sha256=pFkEbuPKVd8AI4tiklcv06LbNnK8KfoV4FwmY9Lrhqo,43044
368
368
  relationalai/semantics/rel/executor.py,sha256=v-yHl9R8AV0AA2xnm5YZDzue83pr8j2Q97Ky1MKkU70,17309
369
369
  relationalai/semantics/rel/rel.py,sha256=9I_V6dQ83QRaLzq04Tt-KjBWhmNxNO3tFzeornBK4zc,15738
370
370
  relationalai/semantics/rel/rel_utils.py,sha256=EH-NBROA4vIJXajLKniapt4Dxt7cXSqY4NEjD-wD8Mc,9566
@@ -442,8 +442,8 @@ frontend/debugger/dist/index.html,sha256=0wIQ1Pm7BclVV1wna6Mj8OmgU73B9rSEGPVX-Wo
442
442
  frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png,sha256=tPXOEhOrM4tJyZVJQVBO_yFgNAlgooY38ZsjyrFstgg,620
443
443
  frontend/debugger/dist/assets/index-Cssla-O7.js,sha256=MxgIGfdKQyBWgufck1xYggQNhW5nj6BPjCF6Wleo-f0,298886
444
444
  frontend/debugger/dist/assets/index-DlHsYx1V.css,sha256=21pZtAjKCcHLFjbjfBQTF6y7QmOic-4FYaKNmwdNZVE,60141
445
- relationalai-0.12.11.dist-info/METADATA,sha256=Gaq2yLT7R1yQ5rf6a7IwR1fWRaFaZ-sMLV5cMdQmeWg,2563
446
- relationalai-0.12.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
447
- relationalai-0.12.11.dist-info/entry_points.txt,sha256=fo_oLFJih3PUgYuHXsk7RnCjBm9cqRNR--ab6DgI6-0,88
448
- relationalai-0.12.11.dist-info/licenses/LICENSE,sha256=pPyTVXFYhirkEW9VsnHIgUjT0Vg8_xsE6olrF5SIgpc,11343
449
- relationalai-0.12.11.dist-info/RECORD,,
445
+ relationalai-0.12.12.dist-info/METADATA,sha256=FraqrzfrL1kptZq9GjRHxJ0y0sfT2bpTuozGwYh7_7k,2563
446
+ relationalai-0.12.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
447
+ relationalai-0.12.12.dist-info/entry_points.txt,sha256=fo_oLFJih3PUgYuHXsk7RnCjBm9cqRNR--ab6DgI6-0,88
448
+ relationalai-0.12.12.dist-info/licenses/LICENSE,sha256=pPyTVXFYhirkEW9VsnHIgUjT0Vg8_xsE6olrF5SIgpc,11343
449
+ relationalai-0.12.12.dist-info/RECORD,,