aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java
diff options
context:
space:
mode:
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.java326
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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Objects;
17import java.util.Set;
18import java.util.function.Function;
19import java.util.function.Predicate;
20import java.util.stream.Collectors;
21import java.util.stream.Stream;
22
23import org.eclipse.emf.ecore.EReference;
24import org.eclipse.emf.ecore.EStructuralFeature;
25import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
26import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
27import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
28import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
29import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
30import tools.refinery.viatra.runtime.matchers.context.IInputKey;
31import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
32import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
33import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
36import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
37import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
38import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter;
39import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
40import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.AbstractTransitiveClosure;
41import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
42import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
43import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
44import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
45import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection;
46import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
47import 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 */
54class 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}