diff options
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java')
-rw-r--r-- | subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java new file mode 100644 index 00000000..da2fb432 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java | |||
@@ -0,0 +1,390 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.rete.construction.plancompiler; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.LinkedHashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.Map; | ||
19 | import java.util.Map.Entry; | ||
20 | import java.util.Set; | ||
21 | import java.util.SortedSet; | ||
22 | import java.util.TreeSet; | ||
23 | |||
24 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
35 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
36 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
37 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
38 | import tools.refinery.viatra.runtime.rete.recipes.EqualityFilterRecipe; | ||
39 | import tools.refinery.viatra.runtime.rete.recipes.IndexerBasedAggregatorRecipe; | ||
40 | import tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe; | ||
41 | import tools.refinery.viatra.runtime.rete.recipes.JoinRecipe; | ||
42 | import tools.refinery.viatra.runtime.rete.recipes.Mask; | ||
43 | import tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo; | ||
44 | import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe; | ||
45 | import tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe; | ||
46 | import tools.refinery.viatra.runtime.rete.recipes.RecipesFactory; | ||
47 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
48 | import tools.refinery.viatra.runtime.rete.recipes.SingleColumnAggregatorRecipe; | ||
49 | import tools.refinery.viatra.runtime.rete.recipes.TrimmerRecipe; | ||
50 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; | ||
51 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
52 | import tools.refinery.viatra.runtime.rete.traceability.CompiledSubPlan; | ||
53 | import tools.refinery.viatra.runtime.rete.traceability.PlanningTrace; | ||
54 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
55 | import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; | ||
56 | |||
57 | /** | ||
58 | * @author Bergmann Gabor | ||
59 | * | ||
60 | */ | ||
61 | public class CompilerHelper { | ||
62 | |||
63 | private CompilerHelper() {/*Utility class constructor*/} | ||
64 | |||
65 | static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; | ||
66 | |||
67 | /** | ||
68 | * Makes sure that all variables in the tuple are different so that it can be used as {@link CompiledSubPlan}. If a | ||
69 | * variable occurs multiple times, equality checks are applied and then the results are trimmed so that duplicates | ||
70 | * are hidden. If no manipulation is necessary, the original trace is returned. | ||
71 | * | ||
72 | * <p> | ||
73 | * to be used whenever a constraint introduces new variables. | ||
74 | */ | ||
75 | public static PlanningTrace checkAndTrimEqualVariables(SubPlan plan, final PlanningTrace coreTrace) { | ||
76 | // are variables in the constraint all different? | ||
77 | final List<PVariable> coreVariablesTuple = coreTrace.getVariablesTuple(); | ||
78 | final int constraintArity = coreVariablesTuple.size(); | ||
79 | final int distinctVariables = coreTrace.getPosMapping().size(); | ||
80 | if (constraintArity == distinctVariables) { | ||
81 | // all variables occur exactly once in tuple | ||
82 | return coreTrace; | ||
83 | } else { // apply equality checks and trim | ||
84 | |||
85 | // find the positions in the tuple for each variable | ||
86 | Map<PVariable, SortedSet<Integer>> posMultimap = new HashMap<PVariable, SortedSet<Integer>>(); | ||
87 | List<PVariable> trimmedVariablesTuple = new ArrayList<PVariable>(distinctVariables); | ||
88 | int[] trimIndices = new int[distinctVariables]; | ||
89 | for (int i = 0; i < constraintArity; ++i) { | ||
90 | final PVariable variable = coreVariablesTuple.get(i); | ||
91 | SortedSet<Integer> indexSet = posMultimap.get(variable); | ||
92 | if (indexSet == null) { // first occurrence of variable | ||
93 | indexSet = new TreeSet<Integer>(); | ||
94 | posMultimap.put(variable, indexSet); | ||
95 | |||
96 | // this is the first occurrence, set up trimming | ||
97 | trimIndices[trimmedVariablesTuple.size()] = i; | ||
98 | trimmedVariablesTuple.add(variable); | ||
99 | } | ||
100 | indexSet.add(i); | ||
101 | } | ||
102 | |||
103 | // construct equality checks for each variable occurring multiple times | ||
104 | PlanningTrace lastTrace = coreTrace; | ||
105 | for (Entry<PVariable, SortedSet<Integer>> entry : posMultimap.entrySet()) { | ||
106 | if (entry.getValue().size() > 1) { | ||
107 | EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); | ||
108 | equalityFilterRecipe.setParent(lastTrace.getRecipe()); | ||
109 | equalityFilterRecipe.getIndices().addAll(entry.getValue()); | ||
110 | lastTrace = new PlanningTrace(plan, coreVariablesTuple, equalityFilterRecipe, lastTrace); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | // trim so that each variable occurs only once | ||
115 | TrimmerRecipe trimmerRecipe = FACTORY.createTrimmerRecipe(); | ||
116 | trimmerRecipe.setParent(lastTrace.getRecipe()); | ||
117 | trimmerRecipe.setMask(tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper | ||
118 | .mask(constraintArity, trimIndices)); | ||
119 | return new PlanningTrace(plan, trimmedVariablesTuple, trimmerRecipe, lastTrace); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Extracts the variable list representation of the variables tuple. | ||
125 | */ | ||
126 | public static List<PVariable> convertVariablesTuple(EnumerablePConstraint constraint) { | ||
127 | return convertVariablesTuple(constraint.getVariablesTuple()); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Extracts the variable list representation of the variables tuple. | ||
132 | */ | ||
133 | public static List<PVariable> convertVariablesTuple(Tuple variablesTuple) { | ||
134 | List<PVariable> result = new ArrayList<PVariable>(); | ||
135 | for (Object o : variablesTuple.getElements()) | ||
136 | result.add((PVariable) o); | ||
137 | return result; | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * Returns a compiled indexer trace according to a mask | ||
142 | */ | ||
143 | public static RecipeTraceInfo makeIndexerTrace(SubPlan planToCompile, PlanningTrace parentTrace, TupleMask mask) { | ||
144 | final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); | ||
145 | if (parentRecipe instanceof IndexerBasedAggregatorRecipe | ||
146 | || parentRecipe instanceof SingleColumnAggregatorRecipe) | ||
147 | throw new IllegalArgumentException( | ||
148 | "Cannot take projection indexer of aggregator node at plan " + planToCompile); | ||
149 | IndexerRecipe recipe = RecipesHelper.projectionIndexerRecipe(parentRecipe, toRecipeMask(mask)); | ||
150 | // final List<PVariable> maskedVariables = mask.transform(parentTrace.getVariablesTuple()); | ||
151 | return new PlanningTrace(planToCompile, /* maskedVariables */ parentTrace.getVariablesTuple(), recipe, | ||
152 | parentTrace); | ||
153 | // TODO add specialized indexer trace info? | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Creates a trimmer that keeps selected variables only. | ||
158 | */ | ||
159 | protected static TrimmerRecipe makeTrimmerRecipe(final PlanningTrace compiledParent, | ||
160 | List<PVariable> projectedVariables) { | ||
161 | final Mask projectionMask = makeProjectionMask(compiledParent, projectedVariables); | ||
162 | final TrimmerRecipe trimmerRecipe = ReteRecipeCompiler.FACTORY.createTrimmerRecipe(); | ||
163 | trimmerRecipe.setParent(compiledParent.getRecipe()); | ||
164 | trimmerRecipe.setMask(projectionMask); | ||
165 | return trimmerRecipe; | ||
166 | } | ||
167 | |||
168 | public static Mask makeProjectionMask(final PlanningTrace compiledParent, Iterable<PVariable> projectedVariables) { | ||
169 | List<Integer> projectionSourceIndices = new ArrayList<Integer>(); | ||
170 | for (PVariable pVariable : projectedVariables) { | ||
171 | projectionSourceIndices.add(compiledParent.getPosMapping().get(pVariable)); | ||
172 | } | ||
173 | final Mask projectionMask = RecipesHelper.mask(compiledParent.getRecipe().getArity(), projectionSourceIndices); | ||
174 | return projectionMask; | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * @since 1.6 | ||
179 | */ | ||
180 | public static final class PosetTriplet { | ||
181 | public Mask coreMask; | ||
182 | public Mask posetMask; | ||
183 | public IPosetComparator comparator; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @since 1.6 | ||
188 | */ | ||
189 | public static PosetTriplet computePosetInfo(List<PVariable> variables, PBody body, IQueryMetaContext context) { | ||
190 | Map<PVariable, Set<IInputKey>> typeMap = TypeHelper.inferUnaryTypesFor(variables, body.getConstraints(), | ||
191 | context); | ||
192 | List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>(); | ||
193 | |||
194 | for (int i = 0; i < variables.size(); i++) { | ||
195 | keys.add(typeMap.get(variables.get(i))); | ||
196 | } | ||
197 | |||
198 | return computePosetInfo(keys, context); | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * @since 1.6 | ||
203 | */ | ||
204 | public static PosetTriplet computePosetInfo(List<PParameter> parameters, IQueryMetaContext context) { | ||
205 | List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>(); | ||
206 | for (int i = 0; i < parameters.size(); i++) { | ||
207 | IInputKey key = parameters.get(i).getDeclaredUnaryType(); | ||
208 | if (key == null) { | ||
209 | keys.add(Collections.emptySet()); | ||
210 | } else { | ||
211 | keys.add(Collections.singleton(parameters.get(i).getDeclaredUnaryType())); | ||
212 | } | ||
213 | } | ||
214 | return computePosetInfo(keys, context); | ||
215 | } | ||
216 | |||
217 | |||
218 | |||
219 | /** | ||
220 | * @since 1.6 | ||
221 | */ | ||
222 | public static PosetTriplet computePosetInfo(Iterable<Set<IInputKey>> keys, IQueryMetaContext context) { | ||
223 | PosetTriplet result = new PosetTriplet(); | ||
224 | List<Integer> coreIndices = new ArrayList<Integer>(); | ||
225 | List<Integer> posetIndices = new ArrayList<Integer>(); | ||
226 | List<IInputKey> filtered = new ArrayList<IInputKey>(); | ||
227 | boolean posetKey = false; | ||
228 | int index = -1; | ||
229 | |||
230 | for (Set<IInputKey> _keys : keys) { | ||
231 | ++index; | ||
232 | posetKey = false; | ||
233 | |||
234 | for (IInputKey key : _keys) { | ||
235 | if (key != null && context.isPosetKey(key)) { | ||
236 | posetKey = true; | ||
237 | filtered.add(key); | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | if (posetKey) { | ||
243 | posetIndices.add(index); | ||
244 | } else { | ||
245 | coreIndices.add(index); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | result.comparator = context.getPosetComparator(filtered); | ||
250 | result.coreMask = RecipesHelper.mask(index + 1, coreIndices); | ||
251 | result.posetMask = RecipesHelper.mask(index + 1, posetIndices); | ||
252 | |||
253 | return result; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Creates a recipe for a production node and the corresponding trace. | ||
258 | * <p> PRE: in case this is a recursion cutoff point (see {@link RecursionCutoffPoint}) | ||
259 | * and bodyFinalTraces will be filled later, | ||
260 | * the object yielded now by bodyFinalTraces.values() must return up-to-date results later | ||
261 | * @since 2.4 | ||
262 | */ | ||
263 | public static CompiledQuery makeQueryTrace(PQuery query, Map<PBody, RecipeTraceInfo> bodyFinalTraces, | ||
264 | Collection<ReteNodeRecipe> bodyFinalRecipes, QueryEvaluationHint hint, IQueryMetaContext context, | ||
265 | boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { | ||
266 | ProductionRecipe recipe = ReteRecipeCompiler.FACTORY.createProductionRecipe(); | ||
267 | |||
268 | // temporary solution to support the deprecated option for now | ||
269 | boolean deleteAndRederiveEvaluationDep = deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(hint); | ||
270 | |||
271 | recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | ||
272 | |||
273 | if (deleteAndRederiveEvaluationDep || (timelyEvaluation != null)) { | ||
274 | PosetTriplet triplet = computePosetInfo(query.getParameters(), context); | ||
275 | if (triplet.comparator != null) { | ||
276 | MonotonicityInfo info = FACTORY.createMonotonicityInfo(); | ||
277 | info.setCoreMask(triplet.coreMask); | ||
278 | info.setPosetMask(triplet.posetMask); | ||
279 | info.setPosetComparator(triplet.comparator); | ||
280 | recipe.setOptionalMonotonicityInfo(info); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | recipe.setPattern(query); | ||
285 | recipe.setPatternFQN(query.getFullyQualifiedName()); | ||
286 | recipe.setTraceInfo(recipe.getPatternFQN()); | ||
287 | recipe.getParents().addAll(bodyFinalRecipes); | ||
288 | for (int i = 0; i < query.getParameterNames().size(); ++i) { | ||
289 | recipe.getMappedIndices().put(query.getParameterNames().get(i), i); | ||
290 | } | ||
291 | |||
292 | return new CompiledQuery(recipe, bodyFinalTraces, query); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * Calculated index mappings for a join, based on the common variables of the two parent subplans. | ||
297 | * | ||
298 | * @author Gabor Bergmann | ||
299 | * | ||
300 | */ | ||
301 | public static class JoinHelper { | ||
302 | private TupleMask primaryMask; | ||
303 | private TupleMask secondaryMask; | ||
304 | private TupleMask complementerMask; | ||
305 | private RecipeTraceInfo primaryIndexer; | ||
306 | private RecipeTraceInfo secondaryIndexer; | ||
307 | private JoinRecipe naturalJoinRecipe; | ||
308 | private List<PVariable> naturalJoinVariablesTuple; | ||
309 | |||
310 | /** | ||
311 | * @pre enforceVariableCoincidences() has been called on both sides. | ||
312 | */ | ||
313 | public JoinHelper(SubPlan planToCompile, PlanningTrace primaryCompiled, PlanningTrace callTrace) { | ||
314 | super(); | ||
315 | |||
316 | Set<PVariable> primaryVariables = new LinkedHashSet<PVariable>(primaryCompiled.getVariablesTuple()); | ||
317 | Set<PVariable> secondaryVariables = new LinkedHashSet<PVariable>(callTrace.getVariablesTuple()); | ||
318 | int oldNodes = 0; | ||
319 | Set<Integer> introducingSecondaryIndices = new TreeSet<Integer>(); | ||
320 | for (PVariable var : secondaryVariables) { | ||
321 | if (primaryVariables.contains(var)) | ||
322 | oldNodes++; | ||
323 | else | ||
324 | introducingSecondaryIndices.add(callTrace.getPosMapping().get(var)); | ||
325 | } | ||
326 | List<Integer> primaryIndices = new ArrayList<Integer>(oldNodes); | ||
327 | List<Integer> secondaryIndices = new ArrayList<Integer>(oldNodes); | ||
328 | for (PVariable var : secondaryVariables) { | ||
329 | if (primaryVariables.contains(var)) { | ||
330 | primaryIndices.add(primaryCompiled.getPosMapping().get(var)); | ||
331 | secondaryIndices.add(callTrace.getPosMapping().get(var)); | ||
332 | } | ||
333 | } | ||
334 | Collection<Integer> complementerIndices = introducingSecondaryIndices; | ||
335 | |||
336 | primaryMask = TupleMask.fromSelectedIndices(primaryCompiled.getVariablesTuple().size(), primaryIndices); | ||
337 | secondaryMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), secondaryIndices); | ||
338 | complementerMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), complementerIndices); | ||
339 | |||
340 | primaryIndexer = makeIndexerTrace(planToCompile, primaryCompiled, primaryMask); | ||
341 | secondaryIndexer = makeIndexerTrace(planToCompile, callTrace, secondaryMask); | ||
342 | |||
343 | naturalJoinRecipe = FACTORY.createJoinRecipe(); | ||
344 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | ||
345 | naturalJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); | ||
346 | naturalJoinRecipe.setRightParentComplementaryMask(CompilerHelper.toRecipeMask(complementerMask)); | ||
347 | |||
348 | naturalJoinVariablesTuple = new ArrayList<PVariable>(primaryCompiled.getVariablesTuple()); | ||
349 | for (int complementerIndex : complementerMask.indices) | ||
350 | naturalJoinVariablesTuple.add(callTrace.getVariablesTuple().get(complementerIndex)); | ||
351 | } | ||
352 | |||
353 | public TupleMask getPrimaryMask() { | ||
354 | return primaryMask; | ||
355 | } | ||
356 | |||
357 | public TupleMask getSecondaryMask() { | ||
358 | return secondaryMask; | ||
359 | } | ||
360 | |||
361 | public TupleMask getComplementerMask() { | ||
362 | return complementerMask; | ||
363 | } | ||
364 | |||
365 | public RecipeTraceInfo getPrimaryIndexer() { | ||
366 | return primaryIndexer; | ||
367 | } | ||
368 | |||
369 | public RecipeTraceInfo getSecondaryIndexer() { | ||
370 | return secondaryIndexer; | ||
371 | } | ||
372 | |||
373 | public JoinRecipe getNaturalJoinRecipe() { | ||
374 | return naturalJoinRecipe; | ||
375 | } | ||
376 | |||
377 | public List<PVariable> getNaturalJoinVariablesTuple() { | ||
378 | return naturalJoinVariablesTuple; | ||
379 | } | ||
380 | |||
381 | } | ||
382 | |||
383 | /** | ||
384 | * @since 1.4 | ||
385 | */ | ||
386 | public static Mask toRecipeMask(TupleMask mask) { | ||
387 | return RecipesHelper.mask(mask.sourceWidth, mask.indices); | ||
388 | } | ||
389 | |||
390 | } | ||