diff options
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java')
-rw-r--r-- | subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java new file mode 100644 index 00000000..96fda930 --- /dev/null +++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java | |||
@@ -0,0 +1,326 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Objects; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Function; | ||
19 | import java.util.function.Predicate; | ||
20 | import java.util.stream.Collectors; | ||
21 | import java.util.stream.Stream; | ||
22 | |||
23 | import org.eclipse.emf.ecore.EReference; | ||
24 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
25 | import tools.refinery.viatra.runtime.base.api.BaseIndexOptions; | ||
26 | import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension; | ||
27 | import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey; | ||
28 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
29 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
30 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
31 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
38 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter; | ||
39 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
40 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.AbstractTransitiveClosure; | ||
41 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
42 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
43 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
44 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
45 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection; | ||
46 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
47 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
48 | |||
49 | |||
50 | /** | ||
51 | * @author Grill Balázs | ||
52 | * @noreference This class is not intended to be referenced by clients. | ||
53 | */ | ||
54 | class PConstraintInfoInferrer { | ||
55 | |||
56 | private static final Predicate<PVariable> SINGLE_USE_VARIABLE = input -> input != null && input.getReferringConstraints().size() == 1; | ||
57 | |||
58 | private final boolean useIndex; | ||
59 | private final Function<IConstraintEvaluationContext, Double> costFunction; | ||
60 | private final EMFModelComprehension modelComprehension; | ||
61 | private final IQueryBackendContext context; | ||
62 | private final ResultProviderRequestor resultRequestor; | ||
63 | |||
64 | |||
65 | public PConstraintInfoInferrer(boolean useIndex, | ||
66 | IQueryBackendContext backendContext, | ||
67 | ResultProviderRequestor resultRequestor, | ||
68 | Function<IConstraintEvaluationContext, Double> costFunction) { | ||
69 | this.useIndex = useIndex; | ||
70 | this.context = backendContext; | ||
71 | this.resultRequestor = resultRequestor; | ||
72 | this.costFunction = costFunction; | ||
73 | this.modelComprehension = new EMFModelComprehension(new BaseIndexOptions()); | ||
74 | } | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Create all possible application condition for all constraint | ||
79 | * | ||
80 | * @param constraintSet the set of constraints | ||
81 | * @param runtimeContext the model dependent runtime contest | ||
82 | * @return a collection of the wrapper PConstraintInfo objects with all the allowed application conditions | ||
83 | */ | ||
84 | public List<PConstraintInfo> createPConstraintInfos(Set<PConstraint> constraintSet) { | ||
85 | List<PConstraintInfo> constraintInfos = new ArrayList<>(); | ||
86 | |||
87 | for (PConstraint pConstraint : constraintSet) { | ||
88 | createPConstraintInfoDispatch(constraintInfos, pConstraint); | ||
89 | } | ||
90 | return constraintInfos; | ||
91 | } | ||
92 | |||
93 | private void createPConstraintInfoDispatch(List<PConstraintInfo> resultList, PConstraint pConstraint){ | ||
94 | if(pConstraint instanceof ExportedParameter){ | ||
95 | createConstraintInfoExportedParameter(resultList, (ExportedParameter) pConstraint); | ||
96 | } else if(pConstraint instanceof TypeConstraint){ | ||
97 | createConstraintInfoTypeConstraint(resultList, (TypeConstraint)pConstraint); | ||
98 | } else if(pConstraint instanceof TypeFilterConstraint){ | ||
99 | createConstraintInfoTypeFilterConstraint(resultList, (TypeFilterConstraint)pConstraint); | ||
100 | } else if(pConstraint instanceof ConstantValue){ | ||
101 | createConstraintInfoConstantValue(resultList, (ConstantValue)pConstraint); | ||
102 | } else if (pConstraint instanceof Inequality){ | ||
103 | createConstraintInfoInequality(resultList, (Inequality) pConstraint); | ||
104 | } else if (pConstraint instanceof ExpressionEvaluation){ | ||
105 | createConstraintInfoExpressionEvaluation(resultList, (ExpressionEvaluation)pConstraint); | ||
106 | } else if (pConstraint instanceof AggregatorConstraint){ | ||
107 | createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((AggregatorConstraint) pConstraint).getResultVariable()); | ||
108 | } else if (pConstraint instanceof PatternMatchCounter){ | ||
109 | createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((PatternMatchCounter) pConstraint).getResultVariable()); | ||
110 | } else if (pConstraint instanceof PositivePatternCall){ | ||
111 | createConstraintInfoPositivePatternCall(resultList, (PositivePatternCall) pConstraint); | ||
112 | } else if (pConstraint instanceof AbstractTransitiveClosure) { | ||
113 | createConstraintInfoBinaryTransitiveClosure(resultList, (AbstractTransitiveClosure) pConstraint); | ||
114 | } else{ | ||
115 | createConstraintInfoGeneric(resultList, pConstraint); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | private void createConstraintInfoConstantValue(List<PConstraintInfo> resultList, | ||
120 | ConstantValue pConstraint) { | ||
121 | // A ConstantValue constraint has a single variable, which is allowed to be unbound | ||
122 | // (extending through ConstantValue is considered a cheap operation) | ||
123 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
124 | Set<? extends Set<PVariable>> bindings = Sets.powerSet(affectedVariables); | ||
125 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
126 | } | ||
127 | |||
128 | |||
129 | private void createConstraintInfoPositivePatternCall(List<PConstraintInfo> resultList, | ||
130 | PositivePatternCall pCall) { | ||
131 | // A pattern call can have any of its variables unbound | ||
132 | Set<PVariable> affectedVariables = pCall.getAffectedVariables(); | ||
133 | // IN parameters cannot be unbound and | ||
134 | // OUT parameters cannot be bound | ||
135 | Tuple variables = pCall.getVariablesTuple(); | ||
136 | final Set<PVariable> inVariables = new HashSet<>(); | ||
137 | Set<PVariable> inoutVariables = new HashSet<>(); | ||
138 | List<PParameter> parameters = pCall.getReferredQuery().getParameters(); | ||
139 | for(int i=0;i<parameters.size();i++){ | ||
140 | switch(parameters.get(i).getDirection()){ | ||
141 | case IN: | ||
142 | inVariables.add((PVariable) variables.get(i)); | ||
143 | break; | ||
144 | case INOUT: | ||
145 | inoutVariables.add((PVariable) variables.get(i)); | ||
146 | break; | ||
147 | case OUT: | ||
148 | default: | ||
149 | break; | ||
150 | |||
151 | } | ||
152 | } | ||
153 | Iterable<Set<PVariable>> bindings = Sets.powerSet(inoutVariables).stream() | ||
154 | .map(input -> Stream.concat(input.stream(), inVariables.stream()).collect(Collectors.toSet())) | ||
155 | .collect(Collectors.toSet()); | ||
156 | |||
157 | doCreateConstraintInfos(resultList, pCall, affectedVariables, bindings); | ||
158 | } | ||
159 | |||
160 | private void createConstraintInfoBinaryTransitiveClosure(List<PConstraintInfo> resultList, | ||
161 | AbstractTransitiveClosure closure) { | ||
162 | // A pattern call can have any of its variables unbound | ||
163 | |||
164 | List<PParameter> parameters = closure.getReferredQuery().getParameters(); | ||
165 | Tuple variables = closure.getVariablesTuple(); | ||
166 | |||
167 | Set<Set<PVariable>> bindings = new HashSet<>(); | ||
168 | PVariable firstVariable = (PVariable) variables.get(0); | ||
169 | PVariable secondVariable = (PVariable) variables.get(1); | ||
170 | // Check is always supported | ||
171 | bindings.add(new HashSet<>(Arrays.asList(firstVariable, secondVariable))); | ||
172 | // If first parameter is not bound mandatorily, it can be left out | ||
173 | if (parameters.get(0).getDirection() != PParameterDirection.IN) { | ||
174 | bindings.add(Collections.singleton(secondVariable)); | ||
175 | } | ||
176 | // If second parameter is not bound mandatorily, it can be left out | ||
177 | if (parameters.get(1).getDirection() != PParameterDirection.IN) { | ||
178 | bindings.add(Collections.singleton(firstVariable)); | ||
179 | } | ||
180 | |||
181 | doCreateConstraintInfos(resultList, closure, closure.getAffectedVariables(), bindings); | ||
182 | } | ||
183 | |||
184 | |||
185 | |||
186 | private void createConstraintInfoExportedParameter(List<PConstraintInfo> resultList, | ||
187 | ExportedParameter parameter) { | ||
188 | // In case of an exported parameter constraint, the parameter must be bound in order to execute | ||
189 | Set<PVariable> affectedVariables = parameter.getAffectedVariables(); | ||
190 | doCreateConstraintInfos(resultList, parameter, affectedVariables, Collections.singleton(affectedVariables)); | ||
191 | } | ||
192 | |||
193 | private void createConstraintInfoExpressionEvaluation(List<PConstraintInfo> resultList, | ||
194 | ExpressionEvaluation expressionEvaluation) { | ||
195 | // An expression evaluation can only have its output variable unbound. All other variables shall be bound | ||
196 | PVariable output = expressionEvaluation.getOutputVariable(); | ||
197 | Set<Set<PVariable>> bindings = new HashSet<>(); | ||
198 | Set<PVariable> affectedVariables = expressionEvaluation.getAffectedVariables(); | ||
199 | // All variables bound -> check | ||
200 | bindings.add(affectedVariables); | ||
201 | // Output variable is not bound -> extend | ||
202 | bindings.add(affectedVariables.stream().filter(var -> !Objects.equals(var, output)).collect(Collectors.toSet())); | ||
203 | doCreateConstraintInfos(resultList, expressionEvaluation, affectedVariables, bindings); | ||
204 | } | ||
205 | |||
206 | private void createConstraintInfoTypeFilterConstraint(List<PConstraintInfo> resultList, | ||
207 | TypeFilterConstraint filter){ | ||
208 | // In case of type filter, all affected variables must be bound in order to execute | ||
209 | Set<PVariable> affectedVariables = filter.getAffectedVariables(); | ||
210 | doCreateConstraintInfos(resultList, filter, affectedVariables, Collections.singleton(affectedVariables)); | ||
211 | } | ||
212 | |||
213 | private void createConstraintInfoInequality(List<PConstraintInfo> resultList, | ||
214 | Inequality inequality){ | ||
215 | // In case of inequality, all affected variables must be bound in order to execute | ||
216 | Set<PVariable> affectedVariables = inequality.getAffectedVariables(); | ||
217 | doCreateConstraintInfos(resultList, inequality, affectedVariables, Collections.singleton(affectedVariables)); | ||
218 | } | ||
219 | |||
220 | private void createConstraintInfoAggregatorConstraint(List<PConstraintInfo> resultList, | ||
221 | PConstraint pConstraint, PVariable resultVariable){ | ||
222 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
223 | |||
224 | // The only variables which can be unbound are single-use | ||
225 | Set<PVariable> canBeUnboundVariables = | ||
226 | Stream.concat(Stream.of(resultVariable), affectedVariables.stream().filter(SINGLE_USE_VARIABLE)).collect(Collectors.toSet()); | ||
227 | |||
228 | Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables); | ||
229 | |||
230 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * | ||
235 | * @param canBeUnboundVariables Variables which are allowed to be unbound | ||
236 | * @param affectedVariables All affected variables | ||
237 | * @return The set of possible bound variable sets | ||
238 | */ | ||
239 | private Set<Set<PVariable>> calculatePossibleBindings(Set<PVariable> canBeUnboundVariables, Set<PVariable> affectedVariables){ | ||
240 | final Set<PVariable> mustBindVariables = affectedVariables.stream().filter(input -> !canBeUnboundVariables.contains(input)).collect(Collectors.toSet()); | ||
241 | return Sets.powerSet(canBeUnboundVariables).stream() | ||
242 | .map(input -> { | ||
243 | //some variables have to be bound before executing this constraint | ||
244 | Set<PVariable> result= new HashSet<>(input); | ||
245 | result.addAll(mustBindVariables); | ||
246 | return result; | ||
247 | }) | ||
248 | .collect(Collectors.toSet()); | ||
249 | } | ||
250 | |||
251 | private void createConstraintInfoGeneric(List<PConstraintInfo> resultList, PConstraint pConstraint){ | ||
252 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
253 | |||
254 | // The only variables which can be unbound are single use variables | ||
255 | Set<PVariable> canBeUnboundVariables = affectedVariables.stream().filter(SINGLE_USE_VARIABLE).collect(Collectors.toSet()); | ||
256 | |||
257 | Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables); | ||
258 | |||
259 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
260 | } | ||
261 | |||
262 | private boolean canPerformInverseNavigation(EStructuralFeature feature){ | ||
263 | return ( // Feature has opposite (this only possible for references) | ||
264 | hasEOpposite(feature) | ||
265 | || | ||
266 | (feature instanceof EReference) && ((EReference)feature).isContainment() | ||
267 | || ( // Indexing is enabled, and the feature can be indexed (not a non-well-behaving derived feature). | ||
268 | useIndex && modelComprehension.representable(feature) | ||
269 | )); | ||
270 | } | ||
271 | |||
272 | private void createConstraintInfoTypeConstraint(List<PConstraintInfo> resultList, | ||
273 | TypeConstraint typeConstraint) { | ||
274 | Set<PVariable> affectedVariables = typeConstraint.getAffectedVariables(); | ||
275 | Set<? extends Set<PVariable>> bindings = null; | ||
276 | |||
277 | IInputKey inputKey = typeConstraint.getSupplierKey(); | ||
278 | if(inputKey.isEnumerable()){ | ||
279 | bindings = Sets.powerSet(affectedVariables); | ||
280 | }else{ | ||
281 | // For not enumerable types, this constraint can only be a check | ||
282 | bindings = Collections.singleton(affectedVariables); | ||
283 | } | ||
284 | |||
285 | if(inputKey instanceof EStructuralFeatureInstancesKey){ | ||
286 | final EStructuralFeature feature = ((EStructuralFeatureInstancesKey) inputKey).getEmfKey(); | ||
287 | if(!canPerformInverseNavigation(feature)){ | ||
288 | // When inverse navigation is not allowed or not possible, filter out operation masks, where | ||
289 | // the first variable would be free AND the feature is an EReference and has no EOpposite | ||
290 | bindings = excludeUnnavigableOperationMasks(typeConstraint, bindings); | ||
291 | } | ||
292 | } | ||
293 | doCreateConstraintInfos(resultList, typeConstraint, affectedVariables, bindings); | ||
294 | } | ||
295 | |||
296 | private void doCreateConstraintInfos(List<PConstraintInfo> constraintInfos, | ||
297 | PConstraint pConstraint, Set<PVariable> affectedVariables, Iterable<? extends Set<PVariable>> bindings) { | ||
298 | Set<PConstraintInfo> sameWithDifferentBindings = new HashSet<>(); | ||
299 | for (Set<PVariable> boundVariables : bindings) { | ||
300 | |||
301 | PConstraintInfo info = new PConstraintInfo(pConstraint, boundVariables, | ||
302 | affectedVariables.stream().filter(input -> !boundVariables.contains(input)).collect(Collectors.toSet()), | ||
303 | sameWithDifferentBindings, context, resultRequestor, costFunction); | ||
304 | constraintInfos.add(info); | ||
305 | sameWithDifferentBindings.add(info); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | private Set<Set<PVariable>> excludeUnnavigableOperationMasks(TypeConstraint typeConstraint, Set<? extends Set<PVariable>> bindings) { | ||
310 | PVariable firstVariable = typeConstraint.getVariableInTuple(0); | ||
311 | return bindings.stream().filter( | ||
312 | boundVariablesSet -> (boundVariablesSet.isEmpty() || boundVariablesSet.contains(firstVariable))) | ||
313 | .collect(Collectors.toSet()); | ||
314 | } | ||
315 | |||
316 | private boolean hasEOpposite(EStructuralFeature feature) { | ||
317 | if(feature instanceof EReference){ | ||
318 | EReference eOpposite = ((EReference) feature).getEOpposite(); | ||
319 | if(eOpposite != null){ | ||
320 | return true; | ||
321 | } | ||
322 | } | ||
323 | return false; | ||
324 | } | ||
325 | |||
326 | } | ||