aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner')
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java38
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java23
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java140
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java257
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java41
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java142
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java278
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java283
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java430
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java101
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java53
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java63
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java22
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java91
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java49
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java413
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java95
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java209
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java26
19 files changed, 2754 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java
new file mode 100644
index 00000000..dfd9a3c8
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java
@@ -0,0 +1,38 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.Collection;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody;
15import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18
19/**
20 * @author Zoltan Ujhelyi
21 * @since 1.7
22 */
23public interface ILocalSearchPlanner {
24
25 /**
26 * Creates executable plans for the provided query. It is required to call one of the
27 * <code>initializePlanner()</code> methods before calling this method.
28 *
29 * @param querySpec
30 * @param boundParameters
31 * a set of bound parameters
32 * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the
33 * list of ISearchOperations
34 * @throws ViatraQueryRuntimeException
35 */
36 Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters);
37
38} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java
new file mode 100644
index 00000000..72218337
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java
@@ -0,0 +1,23 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.List;
12
13import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
14
15/**
16 * @author Marton Bur
17 *
18 */
19public interface ISearchPlanCodeGenerator {
20
21 void compile(List<List<ISearchOperation>> plans);
22
23}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java
new file mode 100644
index 00000000..f44be655
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java
@@ -0,0 +1,140 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import org.apache.log4j.Logger;
20import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody;
23import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
24import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
25import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
27import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
28import tools.refinery.viatra.runtime.matchers.psystem.PBody;
29import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
30import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
32import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
33import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PBodyNormalizer;
34import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriter;
35import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriterCacher;
36import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PQueryFlattener;
37
38/**
39 *
40 * @author Marton Bur
41 * @noreference This class is not intended to be referenced by clients.
42 */
43public class LocalSearchPlanner implements ILocalSearchPlanner {
44
45 // Externally set tools for planning
46 private final PDisjunctionRewriter preprocessor;
47 private final LocalSearchRuntimeBasedStrategy plannerStrategy;
48 private final IQueryRuntimeContext runtimeContext;
49 private final LocalSearchHints configuration;
50 private final IOperationCompiler operationCompiler;
51 private final IQueryBackendContext context;
52 private final ResultProviderRequestor resultRequestor;
53
54 /**
55 * @param resultRequestor
56 * @since 1.7
57 */
58 public LocalSearchPlanner(IQueryBackendContext backendContext, IOperationCompiler compiler, Logger logger,
59 final LocalSearchHints configuration, ResultProviderRequestor resultRequestor)
60 {
61
62 this.runtimeContext = backendContext.getRuntimeContext();
63 this.configuration = configuration;
64 this.operationCompiler = compiler;
65 this.resultRequestor = resultRequestor;
66 PQueryFlattener flattener = new PQueryFlattener(configuration.getFlattenCallPredicate());
67 /*
68 * TODO https://bugs.eclipse.org/bugs/show_bug.cgi?id=439358: The normalizer is initialized with the false
69 * parameter to turn off unary constraint elimination to work around an issue related to plan ordering: the
70 * current implementation of the feature target checking operations expect that the source types were checked
71 * before. However, this causes duplicate constraint checks in the search plan that might affect performance
72 * negatively.
73 */
74 PBodyNormalizer normalizer = new PBodyNormalizer(runtimeContext.getMetaContext()) {
75
76 @Override
77 protected boolean shouldCalculateImpliedTypes(PQuery query) {
78 return false;
79 }
80 };
81 preprocessor = new PDisjunctionRewriterCacher(flattener, normalizer);
82
83 plannerStrategy = new LocalSearchRuntimeBasedStrategy();
84
85 context = backendContext;
86 }
87
88 /**
89 * Creates executable plans for the provided query. It is required to call one of the
90 * <code>initializePlanner()</code> methods before calling this method.
91 *
92 * @param querySpec
93 * @param boundParameters
94 * a set of bound parameters
95 * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the
96 * list of ISearchOperations
97 */
98 @Override
99 public Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters) {
100 // 1. Preparation
101 preprocessor.setTraceCollector(configuration.getTraceCollector());
102 Set<PBody> normalizedBodies = preprocessor.rewrite(querySpec.getDisjunctBodies()).getBodies();
103
104 List<SearchPlanForBody> plansForBodies = new ArrayList<>(normalizedBodies.size());
105
106 for (PBody normalizedBody : normalizedBodies) {
107 // 2. Plan creation
108 // Context has matchers for the referred Queries (IQuerySpecifications)
109 Set<PVariable> boundVariables = calculatePatternAdornmentForPlanner(boundParameters, normalizedBody);
110 PlanState searchPlanInternal = plannerStrategy.plan(normalizedBody, boundVariables, context, resultRequestor, configuration);
111 SubPlan plan = plannerStrategy.convertPlan(boundVariables, searchPlanInternal);
112 // 3. PConstraint -> POperation compilation step
113 // * Pay extra caution to extend operations, when more than one variables are unbound
114 List<ISearchOperation> compiledOperations = operationCompiler.compile(plan, boundParameters);
115 // Store the variable mappings for the plans for debug purposes (traceability information)
116 SearchPlanForBody compiledPlan = new SearchPlanForBody(normalizedBody,
117 operationCompiler.getVariableMappings(), plan, compiledOperations,
118 operationCompiler.getDependencies(),
119 searchPlanInternal, searchPlanInternal.getCost());
120
121 plansForBodies.add(compiledPlan);
122 }
123
124 return plansForBodies;
125 }
126
127 private Set<PVariable> calculatePatternAdornmentForPlanner(Set<PParameter> boundParameters, PBody normalizedBody) {
128 Map<PParameter, PVariable> parameterMapping = new HashMap<>();
129 for (ExportedParameter constraint : normalizedBody.getSymbolicParameters()) {
130 parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable());
131 }
132 Set<PVariable> boundVariables = new HashSet<>();
133 for (PParameter parameter : boundParameters) {
134 PVariable mappedParameter = parameterMapping.get(parameter);
135 boundVariables.add(mappedParameter);
136 }
137 return boundVariables;
138 }
139
140}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java
new file mode 100644
index 00000000..1bebe37e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java
@@ -0,0 +1,257 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
20import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
21import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator;
22import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
23import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
24import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
25import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
26import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
27import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
28import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
29import tools.refinery.viatra.runtime.matchers.psystem.PBody;
30import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
32import tools.refinery.viatra.runtime.matchers.util.Sets;
33
34/**
35 * This class contains the logic for local search plan calculation based on costs of the operations.
36 * Its name refers to the fact that the strategy tries to use as much information as available about
37 * the model on which the matching is initiated. When no runtime info is available, it falls back to
38 * the information available from the metamodel durint operation cost calculation.
39 *
40 * The implementation is based on the paper "Gergely Varró, Frederik Deckwerth, Martin Wieber, and Andy Schürr:
41 * An algorithm for generating model-sensitive search plans for pattern matching on EMF models"
42 * (DOI: 10.1007/s10270-013-0372-2)
43 *
44 * @author Marton Bur
45 * @noreference This class is not intended to be referenced by clients.
46 */
47public class LocalSearchRuntimeBasedStrategy {
48
49 private final OperationCostComparator infoComparator = new OperationCostComparator();
50
51
52 /**
53 * Converts a plan to the standard format
54 */
55 protected SubPlan convertPlan(Set<PVariable> initialBoundVariables, PlanState searchPlan) {
56 PBody pBody;
57 pBody = searchPlan.getAssociatedPBody();
58
59 // Create a starting plan
60 SubPlanFactory subPlanFactory = new SubPlanFactory(pBody);
61
62 // We assume that the adornment (now the bound variables) is previously set
63 SubPlan plan = subPlanFactory.createSubPlan(new PStart(initialBoundVariables));
64
65 List<PConstraintInfo> operations = searchPlan.getOperations();
66 for (PConstraintInfo pConstraintPlanInfo : operations) {
67 PConstraint pConstraint = pConstraintPlanInfo.getConstraint();
68 plan = subPlanFactory.createSubPlan(new PApply(pConstraint), plan);
69 }
70
71 return subPlanFactory.createSubPlan(new PProject(pBody.getSymbolicParameterVariables()), plan);
72 }
73
74 /**
75 * The implementation of a local search-based algorithm to create a search plan for a flattened (and normalized)
76 * PBody
77 * @param pBody for which the plan is to be created
78 * @param initialBoundVariables variables that are known to have already assigned values
79 * @param context the backend context
80 * @param resultProviderRequestor requestor for accessing result providers of called patterns
81 * @param configuration the planner configuration
82 * @return the complete search plan for the given {@link PBody}
83 * @since 2.1
84 */
85 protected PlanState plan(PBody pBody, Set<PVariable> initialBoundVariables,
86 IQueryBackendContext context, final ResultProviderRequestor resultProviderRequestor,
87 LocalSearchHints configuration) {
88 final ICostFunction costFunction = configuration.getCostFunction();
89 PConstraintInfoInferrer pConstraintInfoInferrer = new PConstraintInfoInferrer(
90 configuration.isUseBase(), context, resultProviderRequestor, costFunction::apply);
91
92 // Create mask infos
93 Set<PConstraint> constraintSet = pBody.getConstraints();
94 List<PConstraintInfo> constraintInfos =
95 pConstraintInfoInferrer.createPConstraintInfos(constraintSet);
96
97 // Calculate the characteristic function
98 // The characteristic function tells whether a given adornment is backward reachable from the (B)* state, where
99 // each variable is bound.
100 // The characteristic function is represented as a set of set of variables
101 // TODO this calculation is not not implemented yet, thus the contents of the returned set is not considered later
102 List<Set<PVariable>> reachableBoundVariableSets = reachabilityAnalysis(pBody, constraintInfos);
103 int k = configuration.getRowCount();
104 PlanState searchPlan = calculateSearchPlan(pBody, initialBoundVariables, k, reachableBoundVariableSets, constraintInfos);
105 return searchPlan;
106 }
107
108 private PlanState calculateSearchPlan(PBody pBody, Set<PVariable> initialBoundVariables, int k,
109 List<Set<PVariable>> reachableBoundVariableSets, List<PConstraintInfo> allMaskInfos) {
110
111 List<PConstraintInfo> allPotentialExtendInfos = new ArrayList<>();
112 List<PConstraintInfo> allPotentialCheckInfos = new ArrayList<>();
113 Map<PVariable, List<PConstraintInfo>> checkOpsByVariables = new HashMap<>();
114 Map<PVariable, Collection<PConstraintInfo>> extendOpsByBoundVariables = new HashMap<>();
115
116 for (PConstraintInfo op : allMaskInfos) {
117 if (op.getFreeVariables().isEmpty()) { // CHECK
118 allPotentialCheckInfos.add(op);
119 } else { // EXTEND
120 allPotentialExtendInfos.add(op);
121 for (PVariable variable : op.getBoundVariables()) {
122 extendOpsByBoundVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op);
123 }
124 }
125 }
126 // For CHECKs only, we must start from lists that are ordered by the cost of the constraint application
127 Collections.sort(allPotentialCheckInfos, infoComparator); // costs are eagerly needed for check ops
128 for (PConstraintInfo op : allPotentialCheckInfos) {
129 for (PVariable variable : op.getBoundVariables()) {
130 checkOpsByVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op);
131 }
132 }
133 // costs are not needed for extend ops until they are first applied (TODO make cost computaiton on demand)
134
135
136 // rename for better understanding
137 Set<PVariable> boundVariables = initialBoundVariables;
138 Set<PVariable> freeVariables = Sets.difference(pBody.getUniqueVariables(), initialBoundVariables);
139
140 int variableCount = pBody.getUniqueVariables().size();
141 int n = freeVariables.size();
142
143 List<List<PlanState>> stateTable = initializeStateTable(k, n);
144
145 // Set initial state: begin with an empty operation list
146 PlanState initialState = new PlanState(pBody, boundVariables);
147
148 // Initial state creation, categorizes all operations; add present checks to operationsList
149 initialState.updateExtends(allPotentialExtendInfos);
150 initialState.applyChecks(allPotentialCheckInfos);
151 stateTable.get(n).add(0, initialState);
152
153 // stateTable.get(0) will contain the states with adornment B*
154 for (int i = n; i > 0; i--) {
155 for (int j = 0; j < k && j < stateTable.get(i).size(); j++) {
156 PlanState currentState = stateTable.get(i).get(j);
157
158 for (PConstraintInfo constraintInfo : currentState.getPresentExtends()) {
159 // for each present EXTEND operation
160 PlanState newState = calculateNextState(currentState, constraintInfo);
161 // also eagerly perform any CHECK operations that become applicable (extends still deferred)
162 newState.applyChecksBasedOnDelta(checkOpsByVariables);
163
164 if(currentState.getBoundVariables().size() == newState.getBoundVariables().size()){
165 // This means no variable binding was done, go on with the next constraint info
166 continue;
167 }
168 int i2 = variableCount - newState.getBoundVariables().size();
169
170 List<Integer> newIndices = determineIndices(stateTable, i2, newState, k);
171 int a = newIndices.get(0);
172 int c = newIndices.get(1);
173
174 if (checkInsertCondition(stateTable.get(i2), newState, reachableBoundVariableSets, a, c, k)) {
175 updateExtends(newState, currentState, extendOpsByBoundVariables); // preprocess next steps
176 insert(stateTable,i2, newState, a, c, k);
177 }
178 }
179 }
180 }
181
182 return stateTable.get(0).get(0);
183 }
184
185 private List<List<PlanState>> initializeStateTable(int k, int n) {
186 List<List<PlanState>> stateTable = new ArrayList<>();
187 // Initialize state table and fill it with null
188 for (int i = 0; i <= n ; i++) {
189 stateTable.add(new ArrayList<>());
190 }
191 return stateTable;
192 }
193
194 private void insert(List<List<PlanState>> stateTable, int idx, PlanState newState, int a, int c, int k) {
195 stateTable.get(idx).add(c, newState);
196 while(stateTable.get(idx).size() > k){
197 // Truncate back to size k when grows too big
198 stateTable.set(idx, stateTable.get(idx).subList(0, k));
199 }
200 }
201
202 private void updateExtends(PlanState newState, PlanState currentState,
203 Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables)
204 {
205 List<PConstraintInfo> presentExtends = currentState.getPresentExtends();
206
207 // Recategorize operations
208 newState.updateExtendsBasedOnDelta(presentExtends, extendOpsByBoundVariables);
209
210 return;
211 }
212
213 private boolean checkInsertCondition(List<PlanState> list, PlanState newState,
214 List<Set<PVariable>> reachableBoundVariableSets, int a, int c, int k) {
215// boolean isAmongBestK = (a == (k + 1)) && c < a && reachableBoundVariableSets.contains(newState.getBoundVariables());
216 boolean isAmongBestK = a == k && c < a ;
217 boolean isBetterThanCurrent = a < k && c <= a;
218
219 return isAmongBestK || isBetterThanCurrent;
220 }
221
222 private List<Integer> determineIndices(List<List<PlanState>> stateTable, int i2, PlanState newState, int k) {
223 int a = k;
224 int c = 0;
225 List<Integer> acIndices = new ArrayList<>();
226 for (int j = 0; j < k; j++) {
227 if (j < stateTable.get(i2).size()) {
228 PlanState stateInTable = stateTable.get(i2).get(j);
229 if (newState.getBoundVariables().equals(stateInTable.getBoundVariables())) {
230 // The new state has the same adornment as the stored one - they are not adornment disjoint
231 a = j;
232 }
233 if (newState.getCost() >= stateInTable.getCost()) {
234 c = j + 1;
235 }
236 } else {
237 break;
238 }
239 }
240
241 acIndices.add(a);
242 acIndices.add(c);
243 return acIndices;
244 }
245
246 private PlanState calculateNextState(PlanState currentState, PConstraintInfo constraintInfo) {
247 return currentState.cloneWithApplied(constraintInfo);
248 }
249
250 private List<Set<PVariable>> reachabilityAnalysis(PBody pBody, List<PConstraintInfo> constraintInfos) {
251 // TODO implement reachability analisys, also save/persist the results somewhere
252 List<Set<PVariable>> reachableBoundVariableSets = new ArrayList<>();
253 return reachableBoundVariableSets;
254 }
255
256
257}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java
new file mode 100644
index 00000000..b98dd12e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java
@@ -0,0 +1,41 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11
12/**
13 * Expresses the state of a PConstraint application
14 * condition with respect to a given adornment.
15 *
16 * @author Marton Bur
17 * @noreference This enum is not intended to be referenced by clients.
18 */
19public enum PConstraintCategory {
20 /*
21 * During plan creation an operation is considered a past
22 * operation, if an already bound variable is free in the
23 * mask of the operation.
24 * (Mask of the operation: the required binding state of
25 * the affected variables)
26 */
27 PAST,
28 /*
29 * The binding states of the variables in the operation
30 * mask correspond to the current binding states of the
31 * variables in the search plan
32 */
33 PRESENT,
34 /*
35 * There is at least one bound variable in the mask of
36 * a future operation that is still free at the current
37 * state of the plan. Also, a future operation can't be
38 * PAST.
39 */
40 FUTURE;
41}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java
new file mode 100644
index 00000000..c2c76ef2
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java
@@ -0,0 +1,142 @@
1/**
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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 */
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.Collections;
12import java.util.LinkedHashSet;
13import java.util.Set;
14import java.util.function.Function;
15
16import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
17import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
18import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
20import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
21import tools.refinery.viatra.runtime.matchers.psystem.PBody;
22import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
25
26/**
27 * Wraps a PConstraint together with information required for the planner. Currently contains information about the expected binding state of
28 * the affected variables also called application condition, and the cost of the enforcement, based on the meta and/or the runtime context.
29 *
30 * @author Marton Bur
31 * @noreference This class is not intended to be referenced by clients.
32 */
33public class PConstraintInfo implements IConstraintEvaluationContext {
34
35 private PConstraint constraint;
36 private Set<PVariable> boundMaskVariables;
37 private Set<PVariable> freeMaskVariables;
38 private Set<PConstraintInfo> sameWithDifferentBindings;
39 private IQueryRuntimeContext runtimeContext;
40 private QueryAnalyzer queryAnalyzer;
41 private IQueryResultProviderAccess resultProviderAccess;
42 private ResultProviderRequestor resultRequestor;
43
44 private Double cost;
45 private Function<IConstraintEvaluationContext, Double> costFunction;
46
47
48 /**
49 * Instantiates the wrapper
50 * @param constraintfor which the information is added and stored
51 * @param boundMaskVariables the bound variables in the operation mask
52 * @param freeMaskVariables the free variables in the operation mask
53 * @param sameWithDifferentBindings during the planning process, multiple operation adornments are considered for a constraint, so that it
54 * is represented by multiple plan infos. This parameter contains all plan infos that are for the same
55 * constraint, but with different adornment
56 * @param context the query backend context
57 */
58 public PConstraintInfo(PConstraint constraint, Set<PVariable> boundMaskVariables, Set<PVariable> freeMaskVariables,
59 Set<PConstraintInfo> sameWithDifferentBindings,
60 IQueryBackendContext context,
61 ResultProviderRequestor resultRequestor,
62 Function<IConstraintEvaluationContext, Double> costFunction) {
63 this.constraint = constraint;
64 this.costFunction = costFunction;
65 this.boundMaskVariables = new LinkedHashSet<>(boundMaskVariables);
66 this.freeMaskVariables = new LinkedHashSet<>(freeMaskVariables);
67 this.sameWithDifferentBindings = sameWithDifferentBindings;
68 this.resultRequestor = resultRequestor;
69 this.runtimeContext = context.getRuntimeContext();
70 this.queryAnalyzer = context.getQueryAnalyzer();
71 this.resultProviderAccess = context.getResultProviderAccess();
72
73 this.cost = null; // cost will be computed lazily (esp. important for pattern calls)
74 }
75
76 @Override
77 public IQueryRuntimeContext getRuntimeContext() {
78 return runtimeContext;
79 }
80
81 @Override
82 public QueryAnalyzer getQueryAnalyzer() {
83 return queryAnalyzer;
84 }
85
86 @Override
87 public PConstraint getConstraint() {
88 return constraint;
89 }
90
91 @Override
92 public Set<PVariable> getFreeVariables() {
93 return freeMaskVariables;
94 }
95
96 @Override
97 public Set<PVariable> getBoundVariables() {
98 return boundMaskVariables;
99 }
100
101 public Set<PConstraintInfo> getSameWithDifferentBindings() {
102 return sameWithDifferentBindings;
103 }
104
105 public double getCost() {
106 if (cost == null) {
107 // Calculate cost of the constraint based on its type
108 cost = costFunction.apply(this);
109 }
110 return cost;
111 }
112
113 public PConstraintCategory getCategory(PBody pBody, Set<PVariable> boundVariables) {
114 if (!Collections.disjoint(boundVariables, this.freeMaskVariables)) {
115 return PConstraintCategory.PAST;
116 } else if (!boundVariables.containsAll(this.boundMaskVariables)) {
117 return PConstraintCategory.FUTURE;
118 } else {
119 return PConstraintCategory.PRESENT;
120 }
121 }
122
123 @Override
124 public String toString() {
125 return String.format("%s, bound variables: %s, cost: \"%.2f\"", constraint.toString(), boundMaskVariables.toString(), cost);
126 }
127
128 /**
129 * @deprecated use {@link #resultProviderRequestor()}
130 */
131 @Override
132 @Deprecated
133 public IQueryResultProviderAccess resultProviderAccess() {
134 return resultProviderAccess;
135 }
136
137 @Override
138 public ResultProviderRequestor resultProviderRequestor() {
139 return resultRequestor;
140 }
141
142}
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..eeac07ce
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java
@@ -0,0 +1,278 @@
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 tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
12import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
13import tools.refinery.viatra.runtime.matchers.context.IInputKey;
14import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*;
18import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.AbstractTransitiveClosure;
19import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
20import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
23import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
25import tools.refinery.viatra.runtime.matchers.util.Sets;
26
27import java.util.*;
28import java.util.function.Function;
29import java.util.function.Predicate;
30import java.util.stream.Collectors;
31import java.util.stream.Stream;
32
33
34/**
35 * @author Grill Balázs
36 * @noreference This class is not intended to be referenced by clients.
37 */
38class PConstraintInfoInferrer {
39
40 private static final Predicate<PVariable> SINGLE_USE_VARIABLE = input -> input != null && input.getReferringConstraints().size() == 1;
41
42 private final boolean useIndex;
43 private final Function<IConstraintEvaluationContext, Double> costFunction;
44 private final IQueryBackendContext context;
45 private final ResultProviderRequestor resultRequestor;
46
47
48 public PConstraintInfoInferrer(boolean useIndex,
49 IQueryBackendContext backendContext,
50 ResultProviderRequestor resultRequestor,
51 Function<IConstraintEvaluationContext, Double> costFunction) {
52 this.useIndex = useIndex;
53 this.context = backendContext;
54 this.resultRequestor = resultRequestor;
55 this.costFunction = costFunction;
56 }
57
58
59 /**
60 * Create all possible application condition for all constraint
61 *
62 * @param constraintSet the set of constraints
63 * @return a collection of the wrapper PConstraintInfo objects with all the allowed application conditions
64 */
65 public List<PConstraintInfo> createPConstraintInfos(Set<PConstraint> constraintSet) {
66 List<PConstraintInfo> constraintInfos = new ArrayList<>();
67
68 for (PConstraint pConstraint : constraintSet) {
69 createPConstraintInfoDispatch(constraintInfos, pConstraint);
70 }
71 return constraintInfos;
72 }
73
74 private void createPConstraintInfoDispatch(List<PConstraintInfo> resultList, PConstraint pConstraint){
75 if(pConstraint instanceof ExportedParameter){
76 createConstraintInfoExportedParameter(resultList, (ExportedParameter) pConstraint);
77 } else if(pConstraint instanceof TypeConstraint){
78 createConstraintInfoTypeConstraint(resultList, (TypeConstraint)pConstraint);
79 } else if(pConstraint instanceof TypeFilterConstraint){
80 createConstraintInfoTypeFilterConstraint(resultList, (TypeFilterConstraint)pConstraint);
81 } else if(pConstraint instanceof ConstantValue){
82 createConstraintInfoConstantValue(resultList, (ConstantValue)pConstraint);
83 } else if (pConstraint instanceof Inequality){
84 createConstraintInfoInequality(resultList, (Inequality) pConstraint);
85 } else if (pConstraint instanceof ExpressionEvaluation){
86 createConstraintInfoExpressionEvaluation(resultList, (ExpressionEvaluation)pConstraint);
87 } else if (pConstraint instanceof AggregatorConstraint){
88 createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((AggregatorConstraint) pConstraint).getResultVariable());
89 } else if (pConstraint instanceof PatternMatchCounter){
90 createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((PatternMatchCounter) pConstraint).getResultVariable());
91 } else if (pConstraint instanceof PositivePatternCall){
92 createConstraintInfoPositivePatternCall(resultList, (PositivePatternCall) pConstraint);
93 } else if (pConstraint instanceof AbstractTransitiveClosure) {
94 createConstraintInfoBinaryTransitiveClosure(resultList, (AbstractTransitiveClosure) pConstraint);
95 } else{
96 createConstraintInfoGeneric(resultList, pConstraint);
97 }
98 }
99
100 private void createConstraintInfoConstantValue(List<PConstraintInfo> resultList,
101 ConstantValue pConstraint) {
102 // A ConstantValue constraint has a single variable, which is allowed to be unbound
103 // (extending through ConstantValue is considered a cheap operation)
104 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
105 Set<? extends Set<PVariable>> bindings = Sets.powerSet(affectedVariables);
106 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
107 }
108
109
110 private void createConstraintInfoPositivePatternCall(List<PConstraintInfo> resultList,
111 PositivePatternCall pCall) {
112 // A pattern call can have any of its variables unbound
113 Set<PVariable> affectedVariables = pCall.getAffectedVariables();
114 // IN parameters cannot be unbound and
115 // OUT parameters cannot be bound
116 Tuple variables = pCall.getVariablesTuple();
117 final Set<PVariable> inVariables = new HashSet<>();
118 Set<PVariable> inoutVariables = new HashSet<>();
119 List<PParameter> parameters = pCall.getReferredQuery().getParameters();
120 for(int i=0;i<parameters.size();i++){
121 switch(parameters.get(i).getDirection()){
122 case IN:
123 inVariables.add((PVariable) variables.get(i));
124 break;
125 case INOUT:
126 inoutVariables.add((PVariable) variables.get(i));
127 break;
128 case OUT:
129 default:
130 break;
131
132 }
133 }
134 Iterable<Set<PVariable>> bindings = Sets.powerSet(inoutVariables).stream()
135 .map(input -> Stream.concat(input.stream(), inVariables.stream()).collect(Collectors.toSet()))
136 .collect(Collectors.toSet());
137
138 doCreateConstraintInfos(resultList, pCall, affectedVariables, bindings);
139 }
140
141 private void createConstraintInfoBinaryTransitiveClosure(List<PConstraintInfo> resultList,
142 AbstractTransitiveClosure closure) {
143 // A pattern call can have any of its variables unbound
144
145 List<PParameter> parameters = closure.getReferredQuery().getParameters();
146 Tuple variables = closure.getVariablesTuple();
147
148 Set<Set<PVariable>> bindings = new HashSet<>();
149 PVariable firstVariable = (PVariable) variables.get(0);
150 PVariable secondVariable = (PVariable) variables.get(1);
151 // Check is always supported
152 bindings.add(new HashSet<>(Arrays.asList(firstVariable, secondVariable)));
153 // If first parameter is not bound mandatorily, it can be left out
154 if (parameters.get(0).getDirection() != PParameterDirection.IN) {
155 bindings.add(Collections.singleton(secondVariable));
156 }
157 // If second parameter is not bound mandatorily, it can be left out
158 if (parameters.get(1).getDirection() != PParameterDirection.IN) {
159 bindings.add(Collections.singleton(firstVariable));
160 }
161
162 doCreateConstraintInfos(resultList, closure, closure.getAffectedVariables(), bindings);
163 }
164
165
166
167 private void createConstraintInfoExportedParameter(List<PConstraintInfo> resultList,
168 ExportedParameter parameter) {
169 // In case of an exported parameter constraint, the parameter must be bound in order to execute
170 Set<PVariable> affectedVariables = parameter.getAffectedVariables();
171 doCreateConstraintInfos(resultList, parameter, affectedVariables, Collections.singleton(affectedVariables));
172 }
173
174 private void createConstraintInfoExpressionEvaluation(List<PConstraintInfo> resultList,
175 ExpressionEvaluation expressionEvaluation) {
176 // An expression evaluation can only have its output variable unbound. All other variables shall be bound
177 PVariable output = expressionEvaluation.getOutputVariable();
178 Set<Set<PVariable>> bindings = new HashSet<>();
179 Set<PVariable> affectedVariables = expressionEvaluation.getAffectedVariables();
180 // All variables bound -> check
181 bindings.add(affectedVariables);
182 // Output variable is not bound -> extend
183 bindings.add(affectedVariables.stream().filter(var -> !Objects.equals(var, output)).collect(Collectors.toSet()));
184 doCreateConstraintInfos(resultList, expressionEvaluation, affectedVariables, bindings);
185 }
186
187 private void createConstraintInfoTypeFilterConstraint(List<PConstraintInfo> resultList,
188 TypeFilterConstraint filter){
189 // In case of type filter, all affected variables must be bound in order to execute
190 Set<PVariable> affectedVariables = filter.getAffectedVariables();
191 doCreateConstraintInfos(resultList, filter, affectedVariables, Collections.singleton(affectedVariables));
192 }
193
194 private void createConstraintInfoInequality(List<PConstraintInfo> resultList,
195 Inequality inequality){
196 // In case of inequality, all affected variables must be bound in order to execute
197 Set<PVariable> affectedVariables = inequality.getAffectedVariables();
198 doCreateConstraintInfos(resultList, inequality, affectedVariables, Collections.singleton(affectedVariables));
199 }
200
201 private void createConstraintInfoAggregatorConstraint(List<PConstraintInfo> resultList,
202 PConstraint pConstraint, PVariable resultVariable){
203 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
204
205 // The only variables which can be unbound are single-use
206 Set<PVariable> canBeUnboundVariables =
207 Stream.concat(Stream.of(resultVariable), affectedVariables.stream().filter(SINGLE_USE_VARIABLE)).collect(Collectors.toSet());
208
209 Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables);
210
211 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
212 }
213
214 /**
215 *
216 * @param canBeUnboundVariables Variables which are allowed to be unbound
217 * @param affectedVariables All affected variables
218 * @return The set of possible bound variable sets
219 */
220 private Set<Set<PVariable>> calculatePossibleBindings(Set<PVariable> canBeUnboundVariables, Set<PVariable> affectedVariables){
221 final Set<PVariable> mustBindVariables = affectedVariables.stream().filter(input -> !canBeUnboundVariables.contains(input)).collect(Collectors.toSet());
222 return Sets.powerSet(canBeUnboundVariables).stream()
223 .map(input -> {
224 //some variables have to be bound before executing this constraint
225 Set<PVariable> result= new HashSet<>(input);
226 result.addAll(mustBindVariables);
227 return result;
228 })
229 .collect(Collectors.toSet());
230 }
231
232 private void createConstraintInfoGeneric(List<PConstraintInfo> resultList, PConstraint pConstraint){
233 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
234
235 // The only variables which can be unbound are single use variables
236 Set<PVariable> canBeUnboundVariables = affectedVariables.stream().filter(SINGLE_USE_VARIABLE).collect(Collectors.toSet());
237
238 Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables);
239
240 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
241 }
242
243 private void createConstraintInfoTypeConstraint(List<PConstraintInfo> resultList,
244 TypeConstraint typeConstraint) {
245 Set<PVariable> affectedVariables = typeConstraint.getAffectedVariables();
246 Set<? extends Set<PVariable>> bindings = null;
247
248 IInputKey inputKey = typeConstraint.getSupplierKey();
249 if(inputKey.isEnumerable()){
250 bindings = Sets.powerSet(affectedVariables);
251 }else{
252 // For not enumerable types, this constraint can only be a check
253 bindings = Collections.singleton(affectedVariables);
254 }
255
256 doCreateConstraintInfos(resultList, typeConstraint, affectedVariables, bindings);
257 }
258
259 private void doCreateConstraintInfos(List<PConstraintInfo> constraintInfos,
260 PConstraint pConstraint, Set<PVariable> affectedVariables, Iterable<? extends Set<PVariable>> bindings) {
261 Set<PConstraintInfo> sameWithDifferentBindings = new HashSet<>();
262 for (Set<PVariable> boundVariables : bindings) {
263
264 PConstraintInfo info = new PConstraintInfo(pConstraint, boundVariables,
265 affectedVariables.stream().filter(input -> !boundVariables.contains(input)).collect(Collectors.toSet()),
266 sameWithDifferentBindings, context, resultRequestor, costFunction);
267 constraintInfos.add(info);
268 sameWithDifferentBindings.add(info);
269 }
270 }
271
272 private Set<Set<PVariable>> excludeUnnavigableOperationMasks(TypeConstraint typeConstraint, Set<? extends Set<PVariable>> bindings) {
273 PVariable firstVariable = typeConstraint.getVariableInTuple(0);
274 return bindings.stream().filter(
275 boundVariablesSet -> (boundVariablesSet.isEmpty() || boundVariablesSet.contains(firstVariable)))
276 .collect(Collectors.toSet());
277 }
278}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java
new file mode 100644
index 00000000..e93b07bc
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java
@@ -0,0 +1,283 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19
20import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator;
21import tools.refinery.viatra.runtime.matchers.algorithms.OrderedIterableMerge;
22import tools.refinery.viatra.runtime.matchers.psystem.PBody;
23import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
24import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
25
26/**
27 * This class represents the state of the plan during planning.
28 *
29 * <p> A PlanState represents a sequence of operations (operationsList) and caches the computed cost
30 * for this operation sequence. The list and the cost are initialized in the constructor.
31 * However, #categorizeChecks() also updates the operations list (by suffixing checks)
32 *
33 * @author Marton Bur
34 * @noreference This class is not intended to be referenced by clients.
35 */
36public class PlanState {
37
38 private final PBody pBody;
39 private final List<PConstraintInfo> operationsList;
40 private final Set<PVariable> boundVariables;
41 private final Collection<PVariable> deltaVariables; /* bound since ancestor plan */
42 private final Set<PConstraint> enforcedConstraints;
43
44 private double cummulativeProduct;
45 private double cost;
46
47 private static Comparator<PConstraintInfo> infoComparator = new OperationCostComparator();
48
49 /*
50 * For a short explanation of past, present and future operations,
51 * see class
52 */
53 private List<PConstraintInfo> presentExtends;
54
55 /**
56 * Creates an initial state
57 */
58 public PlanState(PBody pBody, Set<PVariable> boundVariables) {
59
60 this(pBody, new ArrayList<>(), boundVariables, boundVariables /* also the delta */,
61 0.0 /* initial cost */, 1.0 /*initial branch count */);
62 }
63
64 public PlanState cloneWithApplied(PConstraintInfo op) {
65 // Create operation list based on the current state
66 ArrayList<PConstraintInfo> newOperationsList =
67 // pre-reserve excess capacity for later addition of CHECK ops
68 new ArrayList<>(pBody.getConstraints().size());
69 newOperationsList.addAll(this.getOperations());
70 newOperationsList.add(op);
71
72 // Bind the variables of the op
73 Collection<PVariable> deltaVariables = op.getFreeVariables();
74 Set<PVariable> allBoundVariables =
75 // pre-reserve exact capacity as variables are known
76 // (will not be affected by adding CHECK ops later)
77 new HashSet<>(this.getBoundVariables().size() + deltaVariables.size());
78 allBoundVariables.addAll(this.getBoundVariables());
79 allBoundVariables.addAll(deltaVariables);
80
81 PlanState newState = new PlanState(getAssociatedPBody(), newOperationsList, allBoundVariables, deltaVariables,
82 cost, cummulativeProduct);
83 newState.accountNewOperation(op);
84 return newState;
85 }
86
87 private PlanState(PBody pBody, List<PConstraintInfo> operationsList,
88 Set<PVariable> boundVariables, Collection<PVariable> deltaVariables,
89 double cost, double cummulativeProduct)
90 {
91 this.pBody = pBody;
92 this.operationsList = operationsList;
93 this.boundVariables = boundVariables;
94 this.enforcedConstraints = new HashSet<>();
95 this.deltaVariables = deltaVariables;
96 this.cost = cost;
97 this.cummulativeProduct = cummulativeProduct;
98 }
99
100 // NOT included for EXTEND: bind all variables of op
101 private void accountNewOperation(PConstraintInfo constraintInfo) {
102 this.enforcedConstraints.add(constraintInfo.getConstraint());
103 accountCost(constraintInfo);
104 }
105
106 private void accountCost(PConstraintInfo constraintInfo) {
107 double constraintCost = constraintInfo.getCost();
108 double branchFactor = constraintCost;
109 if (constraintCost > 0){
110 cost += cummulativeProduct * constraintCost;
111 cummulativeProduct *= branchFactor;
112 }
113 }
114
115
116 public Set<PConstraint> getEnforcedConstraints() {
117 return enforcedConstraints;
118 }
119
120 /**
121 * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded),
122 * immediately applicable ones (saved as presently viable extends),
123 * and not yet applicable ones (discarded).
124 *
125 * @param allPotentialExtendInfos all other extends that may be applicable
126 * to this plan state now or in the future;
127 * MUST consist of "extend" constraint applications only (at least one free variable)
128 */
129 public void updateExtends(Iterable<PConstraintInfo> allPotentialExtendInfos) {
130 presentExtends = new ArrayList<>();
131
132
133 // categorize future/present extend constraint infos
134 for (PConstraintInfo op : allPotentialExtendInfos) {
135 updateExtendInternal(op);
136 }
137 }
138
139 /**
140 * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded),
141 * immediately applicable ones (saved as presently viable extends),
142 * and not yet applicable ones (discarded).
143 *
144 * @param extendOpsByBoundVariables all EXTEND operations indexed by affected <i>bound</i> variables
145 * MUST consist of "extend" constraint applications only (at least one free variable)
146 */
147 public void updateExtendsBasedOnDelta(
148 Iterable<PConstraintInfo> previousPresentExtends,
149 Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables)
150 {
151 presentExtends = new ArrayList<>();
152 if (operationsList.isEmpty())
153 throw new IllegalStateException("Not applicable as starting step");
154
155 for (PConstraintInfo extend: previousPresentExtends) {
156 updateExtendInternal(extend);
157 }
158
159 Set<PConstraintInfo> affectedExtends = new HashSet<>();
160 for (PVariable variable : deltaVariables) {
161 // only those check ops may become applicable that have an affected variable in the delta
162 Collection<PConstraintInfo> extendsForVariable = extendOpsByBoundVariables.get(variable);
163 if (null != extendsForVariable) {
164 affectedExtends.addAll(extendsForVariable);
165 }
166 }
167 for (PConstraintInfo extend: affectedExtends) {
168 updateExtendInternal(extend);
169 }
170 }
171
172 private void updateExtendInternal(PConstraintInfo op) {
173 if(!enforcedConstraints.contains(op.getConstraint())) {
174 categorizeExtend(op);
175 }
176 }
177
178 /**
179 * Check operations that newly became applicable (see {@link #getDeltaVariables()})
180 * are appended to operations lists.
181 *
182 * <p> Will never discover degenerate checks (of PConstraints with zero variables),
183 * so must not use on initial state.
184 *
185 * @param allPotentialCheckInfos all CHECK operations
186 * MUST consist of "check" constraint applications only (no free variables)
187 * and must be iterable in decreasing order of cost
188 *
189 *
190 */
191 public void applyChecks(List<PConstraintInfo> allPotentialCheckInfos) {
192 applyChecksInternal(allPotentialCheckInfos);
193 }
194
195 /**
196 * Immediately applicable checks are appended to operations lists.
197 *
198 * @param checkOpsByVariables all CHECK operations indexed by affected variables
199 * MUST consist of "check" constraint applications only (no free variables)
200 * and each bucket must be iterable in decreasing order of cost
201 */
202 public void applyChecksBasedOnDelta(Map<PVariable, List<PConstraintInfo>> checkOpsByVariables) {
203 if (operationsList.isEmpty())
204 throw new IllegalStateException("Not applicable as starting step");
205
206 Iterable<PConstraintInfo> affectedChecks = Collections.emptyList();
207
208 for (PVariable variable : deltaVariables) {
209 // only those check ops may become applicable that have an affected variable in the delta
210 List<PConstraintInfo> checksForVariable = checkOpsByVariables.get(variable);
211 if (null != checksForVariable) {
212 affectedChecks = OrderedIterableMerge.mergeUniques(affectedChecks, checksForVariable, infoComparator);
213 }
214 }
215
216 // checks retain their order, no re-sorting needed
217 applyChecksInternal(affectedChecks);
218 }
219
220 private void applyChecksInternal(Iterable<PConstraintInfo> checks) {
221 for (PConstraintInfo checkInfo : checks) {
222 if (this.boundVariables.containsAll(checkInfo.getBoundVariables()) &&
223 !enforcedConstraints.contains(checkInfo.getConstraint()))
224 {
225 operationsList.add(checkInfo);
226 accountNewOperation(checkInfo);
227 }
228 }
229 }
230
231
232 private void categorizeExtend(PConstraintInfo constraintInfo) {
233 PConstraintCategory category = constraintInfo.getCategory(pBody, boundVariables);
234 if (category == PConstraintCategory.PRESENT) {
235 presentExtends.add(constraintInfo);
236 } else {
237 // do not categorize past/future operations
238 }
239 }
240
241
242 public PBody getAssociatedPBody() {
243 return pBody;
244 }
245
246 public List<PConstraintInfo> getOperations() {
247 return operationsList;
248 }
249
250 public Set<PVariable> getBoundVariables() {
251 return boundVariables;
252 }
253
254 /**
255 * @return the derived cost of the plan contained in the state
256 */
257 public double getCost() {
258 return cost;
259 }
260
261
262 /**
263 * @return cumulative branching factor
264 * @since 2.1
265 */
266 public double getCummulativeProduct() {
267 return cummulativeProduct;
268 }
269
270 public List<PConstraintInfo> getPresentExtends() {
271 return presentExtends;
272 }
273
274 /**
275 * Contains only those variables that are added by the newest extend
276 * (or the initially bound ones if no extend yet)
277 */
278 public Collection<PVariable> getDeltaVariables() {
279 return deltaVariables;
280 }
281
282
283}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java
new file mode 100644
index 00000000..73312dc9
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java
@@ -0,0 +1,430 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.compiler;
10
11import java.util.ArrayList;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.check.AggregatorCheck;
23import tools.refinery.viatra.runtime.localsearch.operations.check.BinaryTransitiveClosureCheck;
24import tools.refinery.viatra.runtime.localsearch.operations.check.CheckConstant;
25import tools.refinery.viatra.runtime.localsearch.operations.check.CheckPositivePatternCall;
26import tools.refinery.viatra.runtime.localsearch.operations.check.CountCheck;
27import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionCheck;
28import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionEvalCheck;
29import tools.refinery.viatra.runtime.localsearch.operations.check.InequalityCheck;
30import tools.refinery.viatra.runtime.localsearch.operations.check.NACOperation;
31import tools.refinery.viatra.runtime.localsearch.operations.extend.AggregatorExtend;
32import tools.refinery.viatra.runtime.localsearch.operations.extend.CountOperation;
33import tools.refinery.viatra.runtime.localsearch.operations.extend.ExpressionEval;
34import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendBinaryTransitiveClosure;
35import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendConstant;
36import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendPositivePatternCall;
37import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
38import tools.refinery.viatra.runtime.localsearch.planner.util.CompilerHelper;
39import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
40import tools.refinery.viatra.runtime.matchers.context.IInputKey;
41import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
42import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
43import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
44import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
45import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
46import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
47import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
48import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
49import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
50import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
51import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
52import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
53import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
54import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
55import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter;
56import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
57import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
58import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
59import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
60import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
61import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
62import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
63
64/**
65 * @author Zoltan Ujhelyi
66 * @since 1.7
67 *
68 */
69public abstract class AbstractOperationCompiler implements IOperationCompiler {
70
71 protected static final String UNSUPPORTED_TYPE_MESSAGE = "Unsupported type: ";
72
73 protected abstract void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
74
75 /**
76 * @throws ViatraQueryRuntimeException
77 */
78 protected abstract void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
79
80 /**
81 * @throws ViatraQueryRuntimeException
82 */
83 protected abstract void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
84
85 /**
86 * @since 2.0
87 * @throws ViatraQueryRuntimeException
88 */
89 protected abstract void createUnaryTypeCheck(IInputKey type, int position);
90
91 protected List<ISearchOperation> operations;
92 protected Set<CallWithAdornment> dependencies = new HashSet<>();
93 protected Map<PConstraint, Set<Integer>> variableBindings;
94 private Map<PVariable, Integer> variableMappings;
95 protected final IQueryRuntimeContext runtimeContext;
96
97 public AbstractOperationCompiler(IQueryRuntimeContext runtimeContext) {
98 this.runtimeContext = runtimeContext;
99 }
100
101 /**
102 * Compiles a plan of <code>POperation</code>s to a list of type <code>List&ltISearchOperation></code>
103 *
104 * @param plan
105 * @param boundParameters
106 * @return an ordered list of POperations that make up the compiled search plan
107 * @throws ViatraQueryRuntimeException
108 */
109 @Override
110 public List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters) {
111
112 variableMappings = CompilerHelper.createVariableMapping(plan);
113 variableBindings = CompilerHelper.cacheVariableBindings(plan, variableMappings, boundParameters);
114
115 operations = new ArrayList<>();
116
117 List<POperation> operationList = CompilerHelper.createOperationsList(plan);
118 for (POperation pOperation : operationList) {
119 compile(pOperation, variableMappings);
120 }
121
122 return operations;
123 }
124
125 private void compile(POperation pOperation, Map<PVariable, Integer> variableMapping) {
126
127 if (pOperation instanceof PApply) {
128 PApply pApply = (PApply) pOperation;
129 PConstraint pConstraint = pApply.getPConstraint();
130
131 if (isCheck(pConstraint, variableMapping)) {
132 // check
133 createCheckDispatcher(pConstraint, variableMapping);
134 } else {
135 // extend
136 createExtendDispatcher(pConstraint, variableMapping);
137 }
138
139 } else if (pOperation instanceof PStart) {
140 // nop
141 } else if (pOperation instanceof PProject) {
142 // nop
143 } else {
144 throw new QueryProcessingException("PStart, PApply or PProject was expected, received: " + pOperation.getClass(), null,"Unexpected POperation type", null);
145 }
146
147 }
148
149 private void createCheckDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) {
150
151
152 // DeferredPConstraint subclasses
153
154 // Equalities are normalized
155
156 if (pConstraint instanceof Inequality) {
157 createCheck((Inequality) pConstraint, variableMapping);
158 } else if (pConstraint instanceof PositivePatternCall){
159 createCheck((PositivePatternCall) pConstraint, variableMapping);
160 } else if (pConstraint instanceof NegativePatternCall) {
161 createCheck((NegativePatternCall) pConstraint,variableMapping);
162 } else if (pConstraint instanceof AggregatorConstraint) {
163 createCheck((AggregatorConstraint) pConstraint, variableMapping);
164 } else if (pConstraint instanceof PatternMatchCounter) {
165 createCheck((PatternMatchCounter) pConstraint, variableMapping);
166 } else if (pConstraint instanceof ExpressionEvaluation) {
167 createCheck((ExpressionEvaluation) pConstraint, variableMapping);
168 } else if (pConstraint instanceof TypeFilterConstraint) {
169 createCheck((TypeFilterConstraint) pConstraint,variableMapping);
170 } else if (pConstraint instanceof ExportedParameter) {
171 // Nothing to do here
172 } else
173
174 // EnumerablePConstraint subclasses
175
176 if (pConstraint instanceof BinaryTransitiveClosure) {
177 createCheck((BinaryTransitiveClosure) pConstraint, variableMapping);
178 } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) {
179 createCheck((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping);
180 } else if (pConstraint instanceof ConstantValue) {
181 createCheck((ConstantValue) pConstraint, variableMapping);
182 } else if (pConstraint instanceof TypeConstraint) {
183 createCheck((TypeConstraint) pConstraint,variableMapping);
184 } else {
185 String msg = "Unsupported Check constraint: "+pConstraint.toString();
186 throw new QueryProcessingException(msg, null, msg, null);
187 }
188
189 }
190
191 protected void createExtendDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) {
192
193 // DeferredPConstraint subclasses
194
195 // Equalities are normalized
196 if (pConstraint instanceof PositivePatternCall) {
197 createExtend((PositivePatternCall)pConstraint, variableMapping);
198 } else if (pConstraint instanceof AggregatorConstraint) {
199 createExtend((AggregatorConstraint) pConstraint, variableMapping);
200 } else if (pConstraint instanceof PatternMatchCounter) {
201 createExtend((PatternMatchCounter) pConstraint, variableMapping);
202 } else if (pConstraint instanceof ExpressionEvaluation) {
203 createExtend((ExpressionEvaluation) pConstraint, variableMapping);
204 } else if (pConstraint instanceof ExportedParameter) {
205 // ExportedParameters are compiled to NOP
206 } else
207
208 // EnumerablePConstraint subclasses
209
210 if (pConstraint instanceof ConstantValue) {
211 createExtend((ConstantValue) pConstraint, variableMapping);
212 } else if (pConstraint instanceof TypeConstraint) {
213 createExtend((TypeConstraint) pConstraint, variableMapping);
214 } else if (pConstraint instanceof BinaryTransitiveClosure) {
215 createExtend((BinaryTransitiveClosure)pConstraint, variableMapping);
216 } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) {
217 createExtend((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping);
218 } else {
219 String msg = "Unsupported Extend constraint: "+pConstraint.toString();
220 throw new QueryProcessingException(msg, null, msg, null);
221 }
222 }
223
224 private boolean isCheck(PConstraint pConstraint, final Map<PVariable, Integer> variableMapping) {
225 if (pConstraint instanceof NegativePatternCall){
226 return true;
227 }else if (pConstraint instanceof PositivePatternCall){
228 // Positive pattern call is check if all non-single used variables are bound
229 List<Integer> callVariables = pConstraint.getAffectedVariables().stream()
230 .filter(input -> input.getReferringConstraints().size() > 1)
231 .map(variableMapping::get)
232 .collect(Collectors.toList());
233 return variableBindings.get(pConstraint).containsAll(callVariables);
234 }else if (pConstraint instanceof AggregatorConstraint){
235 PVariable outputvar = ((AggregatorConstraint) pConstraint).getResultVariable();
236 return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
237 }else if (pConstraint instanceof PatternMatchCounter){
238 PVariable outputvar = ((PatternMatchCounter) pConstraint).getResultVariable();
239 return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
240 }else if (pConstraint instanceof ExpressionEvaluation){
241 PVariable outputvar = ((ExpressionEvaluation) pConstraint).getOutputVariable();
242 return outputvar == null || variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
243 } else {
244 // In other cases, all variables shall be bound to be a check
245 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
246 Set<Integer> varIndices = new HashSet<>();
247 for (PVariable variable : affectedVariables) {
248 varIndices.add(variableMapping.get(variable));
249 }
250 return variableBindings.get(pConstraint).containsAll(varIndices);
251 }
252 }
253
254 @Override
255 public Set<CallWithAdornment> getDependencies() {
256 return dependencies;
257 }
258
259 /**
260 * @return the cached variable bindings for the previously created plan
261 */
262 @Override
263 public Map<PVariable, Integer> getVariableMappings() {
264 return variableMappings;
265 }
266
267 protected void createCheck(PatternMatchCounter counter, Map<PVariable, Integer> variableMapping) {
268 CallInformation information = CallInformation.create(counter, variableMapping, variableBindings.get(counter));
269 operations.add(new CountCheck(information, variableMapping.get(counter.getResultVariable())));
270 dependencies.add(information.getCallWithAdornment());
271 }
272
273 protected void createCheck(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
274 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
275 operations.add(new CheckPositivePatternCall(information));
276 dependencies.add(information.getCallWithAdornment());
277 }
278
279 protected void createCheck(ConstantValue constant, Map<PVariable, Integer> variableMapping) {
280 int position = variableMapping.get(constant.getVariablesTuple().get(0));
281 operations.add(new CheckConstant(position, constant.getSupplierKey()));
282 }
283
284 protected void createCheck(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
285 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
286 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
287
288 //The second parameter is NOT bound during execution!
289 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet()));
290 operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, false));
291 dependencies.add(information.getCallWithAdornment());
292 }
293
294 /**
295 * @since 2.0
296 */
297 protected void createCheck(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
298 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
299 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
300
301 //The second parameter is NOT bound during execution!
302 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet()));
303 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition);
304 operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, true));
305 dependencies.add(information.getCallWithAdornment());
306 }
307
308 protected void createCheck(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) {
309 // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway
310 Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames();
311 Map<String, Integer> nameMap = new HashMap<>();
312
313 for (String pVariableName : inputParameterNames) {
314 PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName);
315 nameMap.put(pVariableName, variableMapping.get(pVariable));
316 }
317
318 // output variable can be null; if null it is an ExpressionCheck
319 if(expressionEvaluation.getOutputVariable() == null){
320 operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap));
321 } else {
322 operations.add(new ExpressionEvalCheck(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable())));
323 }
324 }
325
326 protected void createCheck(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) {
327 CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator));
328 operations.add(new AggregatorCheck(information, aggregator, variableMapping.get(aggregator.getResultVariable())));
329 dependencies.add(information.getCallWithAdornment());
330 }
331
332 protected void createCheck(NegativePatternCall negativePatternCall, Map<PVariable, Integer> variableMapping) {
333 CallInformation information = CallInformation.create(negativePatternCall, variableMapping, variableBindings.get(negativePatternCall));
334 operations.add(new NACOperation(information));
335 dependencies.add(information.getCallWithAdornment());
336 }
337
338 protected void createCheck(Inequality inequality, Map<PVariable, Integer> variableMapping) {
339 operations.add(new InequalityCheck(variableMapping.get(inequality.getWho()), variableMapping.get(inequality.getWithWhom())));
340 }
341
342 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
343 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
344 operations.add(new ExtendPositivePatternCall(information));
345 dependencies.add(information.getCallWithAdornment());
346 }
347
348 protected void createExtend(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
349 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
350 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
351
352 boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition);
353 boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition);
354
355 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure));
356
357 if (sourceBound && !targetBound) {
358 operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, false));
359 dependencies.add(information.getCallWithAdornment());
360 } else if (!sourceBound && targetBound) {
361 operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, false));
362 dependencies.add(information.getCallWithAdornment());
363 } else {
364 String msg = "Binary transitive closure not supported with two unbound parameters";
365 throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern());
366 }
367 }
368
369 /**
370 * @since 2.0
371 */
372 protected void createExtend(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
373 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
374 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
375
376 boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition);
377 boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition);
378
379 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure));
380
381 if (sourceBound && !targetBound) {
382 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition);
383 operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, true));
384 dependencies.add(information.getCallWithAdornment());
385 } else if (!sourceBound && targetBound) {
386 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), targetPosition);
387 operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, true));
388 dependencies.add(information.getCallWithAdornment());
389 } else {
390 String msg = "Binary reflective transitive closure not supported with two unbound parameters";
391 throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern());
392 }
393 }
394
395 protected void createExtend(ConstantValue constant, Map<PVariable, Integer> variableMapping) {
396 int position = variableMapping.get(constant.getVariablesTuple().get(0));
397 operations.add(new ExtendConstant(position, constant.getSupplierKey()));
398 }
399
400 protected void createExtend(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) {
401 // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway
402 Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames();
403 Map<String, Integer> nameMap = new HashMap<>();
404
405 for (String pVariableName : inputParameterNames) {
406 PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName);
407 nameMap.put(pVariableName, variableMapping.get(pVariable));
408 }
409
410 // output variable can be null; if null it is an ExpressionCheck
411 if(expressionEvaluation.getOutputVariable() == null){
412 operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap));
413 } else {
414 operations.add(new ExpressionEval(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable())));
415 }
416 }
417
418 protected void createExtend(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) {
419 CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator));
420 operations.add(new AggregatorExtend(information, aggregator, variableMapping.get(aggregator.getResultVariable())));
421 dependencies.add(information.getCallWithAdornment());
422 }
423
424 protected void createExtend(PatternMatchCounter patternMatchCounter, Map<PVariable, Integer> variableMapping) {
425 CallInformation information = CallInformation.create(patternMatchCounter, variableMapping, variableBindings.get(patternMatchCounter));
426 operations.add(new CountOperation(information, variableMapping.get(patternMatchCounter.getResultVariable())));
427 dependencies.add(information.getCallWithAdornment());
428 }
429
430} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java
new file mode 100644
index 00000000..d86982e9
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java
@@ -0,0 +1,101 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd.
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.viatra.runtime.localsearch.planner.compiler;
11
12import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeCheck;
13import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtend;
14import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
17import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
18import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
22
23import java.util.*;
24
25/**
26 * @author Zoltan Ujhelyi
27 * @since 1.7
28 *
29 */
30public class GenericOperationCompiler extends AbstractOperationCompiler {
31
32 public GenericOperationCompiler(IQueryRuntimeContext runtimeContext) {
33 super(runtimeContext);
34 }
35
36 @Override
37 protected void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
38 IInputKey inputKey = typeConstraint.getInputKey();
39 Tuple tuple = typeConstraint.getVariablesTuple();
40 int[] positions = new int[tuple.getSize()];
41 for (int i = 0; i < tuple.getSize(); i++) {
42 PVariable variable = (PVariable) tuple.get(i);
43 positions[i] = variableMapping.get(variable);
44 }
45 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions)));
46
47 }
48
49 @Override
50 protected void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
51 IInputKey inputKey = typeConstraint.getSupplierKey();
52 Tuple tuple = typeConstraint.getVariablesTuple();
53 int[] positions = new int[tuple.getSize()];
54 for (int i = 0; i < tuple.getSize(); i++) {
55 PVariable variable = (PVariable) tuple.get(i);
56 positions[i] = variableMapping.get(variable);
57 }
58 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions)));
59 }
60
61 @Override
62 protected void createUnaryTypeCheck(IInputKey inputKey, int position) {
63 int[] positions = new int[] {position};
64 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(1, positions)));
65 }
66
67 @Override
68 protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
69 IInputKey inputKey = typeConstraint.getSupplierKey();
70 Tuple tuple = typeConstraint.getVariablesTuple();
71
72 int[] positions = new int[tuple.getSize()];
73 List<Integer> boundVariableIndices = new ArrayList<>();
74 List<Integer> boundVariables = new ArrayList<>();
75 Set<Integer> unboundVariables = new HashSet<>();
76 for (int i = 0; i < tuple.getSize(); i++) {
77 PVariable variable = (PVariable) tuple.get(i);
78 Integer position = variableMapping.get(variable);
79 positions[i] = position;
80 if (variableBindings.get(typeConstraint).contains(position)) {
81 boundVariableIndices.add(i);
82 boundVariables.add(position);
83 } else {
84 unboundVariables.add(position);
85 }
86 }
87 TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices);
88 TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables);
89 // If multiple tuple elements from the indexer should be bound to the same variable, we must use a
90 // {@link GenericTypeExtend} check whether the tuple elements have the same value.
91 if (unboundVariables.size() == 1 && indexerMask.getSize() + 1 == indexerMask.getSourceWidth()) {
92 operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask, unboundVariables.iterator().next()));
93 } else {
94 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
95 }
96
97 }
98
99
100
101}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java
new file mode 100644
index 00000000..625a7eb2
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java
@@ -0,0 +1,53 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.compiler;
10
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
16import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
17import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
18import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
19import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
22
23/**
24 * An operation compiler is responsible for creating executable search plans from the subplan structure.
25 *
26 * @author Zoltan Ujhelyi
27 * @since 1.7
28 *
29 */
30public interface IOperationCompiler {
31
32 /**
33 * Compiles a plan of <code>POperation</code>s to a list of type <code>List&ltISearchOperation></code>
34 *
35 * @param plan
36 * @param boundParameters
37 * @return an ordered list of POperations that make up the compiled search plan
38 * @throws ViatraQueryRuntimeException
39 */
40 List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters);
41
42 /**
43 * Replaces previous method returning {@link MatcherReference}
44 * @since 2.1
45 */
46 Set<CallWithAdornment> getDependencies();
47
48 /**
49 * @return the cached variable bindings for the previously created plan
50 */
51 Map<PVariable, Integer> getVariableMappings();
52
53} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java
new file mode 100644
index 00000000..9b44612b
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java
@@ -0,0 +1,63 @@
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.cost;
10
11import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
12import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
13import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
14import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
15import java.util.Collection;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
18
19/**
20 * This interface denotes the evaluation context of a constraint, intended for cost estimation. Provides access to information
21 * on which the cost function can base its calculation.
22 *
23 * @author Grill Balázs
24 * @since 1.4
25 * @noimplement
26 */
27public interface IConstraintEvaluationContext {
28
29 /**
30 * Get the constraint to be evaluated
31 */
32 public PConstraint getConstraint();
33
34 /**
35 * Unbound variables at the time of evaluating the constraint
36 */
37 public Collection<PVariable> getFreeVariables();
38
39 /**
40 * Bound variables at the time of evaluating the constraint
41 */
42 public Collection<PVariable> getBoundVariables();
43
44 public IQueryRuntimeContext getRuntimeContext();
45
46 /**
47 * @since 1.5
48 */
49 public QueryAnalyzer getQueryAnalyzer();
50
51 /**
52 * @deprecated use {@link #resultProviderRequestor()}
53 * @since 1.5
54 */
55 @Deprecated
56 public IQueryResultProviderAccess resultProviderAccess();
57
58 /**
59 * @since 2.1
60 */
61 public ResultProviderRequestor resultProviderRequestor();
62
63}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java
new file mode 100644
index 00000000..4d9d0708
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java
@@ -0,0 +1,22 @@
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.cost;
10
11/**
12 * Common interface for cost function implementation
13 *
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public interface ICostFunction{
19
20 public double apply(IConstraintEvaluationContext input);
21
22}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java
new file mode 100644
index 00000000..df9292f0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java
@@ -0,0 +1,91 @@
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.cost.impl;
10
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import java.util.Set;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
18import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
19import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
25
26/**
27 * This cost function is intended to be used on hybrid configuration, with the strict restriction than any
28 * non-flattened positive pattern call is executed with Rete engine. This implementation provides the exact number
29 * of matches by invoking the result provider for the called pattern.
30 *
31 * @deprecated {@link StatisticsBasedConstraintCostFunction} should use {@link IQueryResultProvider#estimateCardinality(tools.refinery.viatra.runtime.matchers.tuple.TupleMask, org.eclipse.viatra.query.runtime.matchers.util.Accuracy)}
32 */
33@Deprecated
34public class HybridMatcherConstraintCostFunction extends IndexerBasedConstraintCostFunction {
35
36 @Override
37 protected double _calculateCost(PositivePatternCall patternCall, IConstraintEvaluationContext context) {
38 // Determine local constant constraints which is used to filter results
39 Tuple variables = patternCall.getVariablesTuple();
40 Set<Object> variablesSet = variables.getDistinctElements();
41 final Map<PVariable, Object> constantMap = new HashMap<>();
42 for (PConstraint _constraint : patternCall.getPSystem().getConstraints()) {
43 if (_constraint instanceof ConstantValue){
44 ConstantValue constraint = (ConstantValue) _constraint;
45 PVariable variable = (PVariable) constraint.getVariablesTuple().get(0);
46 if (variablesSet.contains(variable) && context.getBoundVariables().contains(variable)) {
47 constantMap.put(variable, constraint.getSupplierKey());
48 }
49 }
50 }
51
52 // Determine filter
53 Object[] filter = new Object[variables.getSize()];
54 for(int i=0; i < variables.getSize(); i++){
55 filter[i] = constantMap.get(variables.get(i));
56 }
57
58 // aggregate keys are the bound and not filtered variables
59 // These will be fixed in runtime, but unknown at planning time
60 // This is represented by indices to ease working with result tuples
61 final Map<Object, Integer> variableIndices = variables.invertIndex();
62 List<Integer> aggregateKeys = context.getBoundVariables().stream()
63 .filter(input -> !constantMap.containsKey(input))
64 .map(variableIndices::get)
65 .collect(Collectors.toList());
66
67 IQueryResultProvider resultProvider = context.resultProviderRequestor().requestResultProvider(patternCall, null);
68 Map<Tuple, Integer> aggregatedCounts = new HashMap<>();
69
70 // Iterate over all matches and count together matches that has equal values on
71 // aggregateKeys positions. The cost of the pattern call is considered to be the
72 // Maximum of these counted values
73
74 int result = 0;
75 // NOTE: a stream is not an iterable (cannot be iterated more than once), so to use it in a for-loop
76 // it has to be wrapped; in the following line a lambda is used to implement Iterable#iterator()
77 for (Tuple match : (Iterable<Tuple>) () -> resultProvider.getAllMatches(filter).iterator()) {
78 Tuple extracted = Tuples.flatTupleOf(aggregateKeys.stream().map(match::get).toArray());
79 int count = (aggregatedCounts.containsKey(extracted))
80 ? aggregatedCounts.get(extracted) + 1
81 : 1;
82 aggregatedCounts.put(extracted, count);
83 if (result < count) {
84 result = count;
85 }
86 }
87
88 return result;
89 }
90
91} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java
new file mode 100644
index 00000000..9e2c8680
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java
@@ -0,0 +1,49 @@
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.cost.impl;
10
11import java.util.Optional;
12
13import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
14import tools.refinery.viatra.runtime.matchers.context.IInputKey;
15import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
16import tools.refinery.viatra.runtime.matchers.util.Accuracy;
17
18/**
19 * Cost function which calculates cost based on the cardinality of items in the runtime model, provided by the base indexer
20 *
21 * @author Grill Balázs
22 * @since 1.4
23 */
24public class IndexerBasedConstraintCostFunction extends StatisticsBasedConstraintCostFunction {
25
26
27
28
29 /**
30 *
31 */
32 public IndexerBasedConstraintCostFunction() {
33 super();
34 }
35
36 /**
37 * @param inverseNavigationPenalty
38 * @since 2.1
39 */
40 public IndexerBasedConstraintCostFunction(double inverseNavigationPenalty) {
41 super(inverseNavigationPenalty);
42 }
43
44 @Override
45 public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) {
46 return input.getRuntimeContext().estimateCardinality(supplierKey, groupMask, requiredAccuracy);
47 }
48
49}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java
new file mode 100644
index 00000000..873be31d
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java
@@ -0,0 +1,413 @@
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.cost.impl;
10
11import static tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper.min;
12
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.List;
18import java.util.Map;
19import java.util.Optional;
20import java.util.Set;
21
22import tools.refinery.viatra.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
23import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
24import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
25import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
26import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
27import tools.refinery.viatra.runtime.matchers.context.IInputKey;
28import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
29import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
30import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
32import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
33import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
36import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
37import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
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.BinaryReflexiveTransitiveClosure;
41import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
42import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
43import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
44import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
45import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
46import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
47import tools.refinery.viatra.runtime.matchers.util.Accuracy;
48import tools.refinery.viatra.runtime.matchers.util.Preconditions;
49
50/**
51 * Cost function which calculates cost based on the cardinality of items in the runtime model
52 *
53 * <p> To provide custom statistics, override
54 * {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)}
55 * and {@link #bucketSize(IQueryReference, IConstraintEvaluationContext, TupleMask)}.
56 *
57 * @author Grill Balázs
58 * @since 1.4
59 */
60public abstract class StatisticsBasedConstraintCostFunction implements ICostFunction {
61 protected static final double MAX_COST = 250.0;
62
63 protected static final double DEFAULT_COST = StatisticsBasedConstraintCostFunction.MAX_COST - 100.0;
64
65 /**
66 * @since 2.1
67 */
68 public static final double INVERSE_NAVIGATION_PENALTY_DEFAULT = 0.10;
69 /**
70 * @since 2.1
71 */
72 public static final double INVERSE_NAVIGATION_PENALTY_GENERIC = 0.01;
73 /**
74 * @since 2.7
75 */
76 public static final double EVAL_UNWIND_EXTENSION_FACTOR = 3.0;
77
78 private final double inverseNavigationPenalty;
79
80
81 /**
82 * @since 2.1
83 */
84 public StatisticsBasedConstraintCostFunction(double inverseNavigationPenalty) {
85 super();
86 this.inverseNavigationPenalty = inverseNavigationPenalty;
87 }
88 public StatisticsBasedConstraintCostFunction() {
89 this(INVERSE_NAVIGATION_PENALTY_DEFAULT);
90 }
91
92 /**
93 * @deprecated call and implement {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)} instead
94 */
95 @Deprecated
96 public long countTuples(final IConstraintEvaluationContext input, final IInputKey supplierKey) {
97 return projectionSize(input, supplierKey, TupleMask.identity(supplierKey.getArity()), Accuracy.EXACT_COUNT).orElse(-1L);
98 }
99
100 /**
101 * Override this to provide custom statistics on edge/node counts.
102 * New implementors shall implement this instead of {@link #countTuples(IConstraintEvaluationContext, IInputKey)}
103 * @since 2.1
104 */
105 public Optional<Long> projectionSize(final IConstraintEvaluationContext input, final IInputKey supplierKey,
106 final TupleMask groupMask, Accuracy requiredAccuracy) {
107 long legacyCount = countTuples(input, supplierKey);
108 return legacyCount < 0 ? Optional.empty() : Optional.of(legacyCount);
109 }
110
111 /**
112 * Override this to provide custom estimates for match set sizes of called patterns.
113 * @since 2.1
114 */
115 public Optional<Double> bucketSize(final IQueryReference patternCall,
116 final IConstraintEvaluationContext input, TupleMask projMask) {
117 IQueryResultProvider resultProvider = input.resultProviderRequestor().requestResultProvider(patternCall, null);
118 // TODO hack: use LS cost instead of true bucket size estimate
119 if (resultProvider instanceof AbstractLocalSearchResultProvider) {
120 double estimatedCost = ((AbstractLocalSearchResultProvider) resultProvider).estimateCost(projMask);
121 return Optional.of(estimatedCost);
122 } else {
123 return resultProvider.estimateAverageBucketSize(projMask, Accuracy.APPROXIMATION);
124 }
125 }
126
127
128
129 @Override
130 public double apply(final IConstraintEvaluationContext input) {
131 return this.calculateCost(input.getConstraint(), input);
132 }
133
134 protected double _calculateCost(final ConstantValue constant, final IConstraintEvaluationContext input) {
135 return 0.0f;
136 }
137
138 protected double _calculateCost(final TypeConstraint constraint, final IConstraintEvaluationContext input) {
139 final Collection<PVariable> freeMaskVariables = input.getFreeVariables();
140 final Collection<PVariable> boundMaskVariables = input.getBoundVariables();
141 IInputKey supplierKey = constraint.getSupplierKey();
142 long arity = supplierKey.getArity();
143
144 if ((arity == 1)) {
145 // unary constraint
146 return calculateUnaryConstraintCost(constraint, input);
147 } else if ((arity == 2)) {
148 // binary constraint
149 PVariable srcVariable = ((PVariable) constraint.getVariablesTuple().get(0));
150 PVariable dstVariable = ((PVariable) constraint.getVariablesTuple().get(1));
151 boolean isInverse = false;
152 // Check if inverse navigation is needed along the edge
153 if ((freeMaskVariables.contains(srcVariable) && boundMaskVariables.contains(dstVariable))) {
154 isInverse = true;
155 }
156 double binaryExtendCost = calculateBinaryCost(supplierKey, srcVariable, dstVariable, isInverse, input);
157 // Make inverse navigation slightly more expensive than forward navigation
158 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=501078
159 return (isInverse) ? binaryExtendCost + inverseNavigationPenalty : binaryExtendCost;
160 } else {
161 // n-ary constraint
162 throw new UnsupportedOperationException("Cost calculation for arity " + arity + " is not implemented yet");
163 }
164 }
165
166
167 /**
168 * @deprecated use/implement {@link #calculateBinaryCost(IInputKey, PVariable, PVariable, boolean, IConstraintEvaluationContext)} instead
169 */
170 @Deprecated
171 protected double calculateBinaryExtendCost(final IInputKey supplierKey, final PVariable srcVariable,
172 final PVariable dstVariable, final boolean isInverse, long edgeCount /* TODO remove */,
173 final IConstraintEvaluationContext input) {
174 throw new UnsupportedOperationException();
175 }
176
177 /**
178 * @since 2.1
179 */
180 protected double calculateBinaryCost(final IInputKey supplierKey, final PVariable srcVariable,
181 final PVariable dstVariable, final boolean isInverse,
182 final IConstraintEvaluationContext input) {
183 final Collection<PVariable> freeMaskVariables = input.getFreeVariables();
184 final PConstraint constraint = input.getConstraint();
185
186// IQueryMetaContext metaContext = input.getRuntimeContext().getMetaContext();
187// Collection<InputKeyImplication> implications = metaContext.getImplications(supplierKey);
188
189 Optional<Long> edgeUpper = projectionSize(input, supplierKey, TupleMask.identity(2), Accuracy.BEST_UPPER_BOUND);
190 Optional<Long> srcUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.BEST_UPPER_BOUND);
191 Optional<Long> dstUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.BEST_UPPER_BOUND);
192
193 if (freeMaskVariables.contains(srcVariable) && freeMaskVariables.contains(dstVariable)) {
194 Double branchCount = edgeUpper.map(Long::doubleValue).orElse(
195 srcUpper.map(Long::doubleValue).orElse(DEFAULT_COST)
196 *
197 dstUpper.map(Long::doubleValue).orElse(DEFAULT_COST)
198 );
199 return branchCount;
200
201 } else {
202
203 Optional<Long> srcLower = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.APPROXIMATION);
204 Optional<Long> dstLower = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.APPROXIMATION);
205
206 List<Optional<Long>> nodeLower = Arrays.asList(srcLower, dstLower);
207 List<Optional<Long>> nodeUpper = Arrays.asList(srcUpper, dstUpper);
208
209 int from = isInverse ? 1 : 0;
210 int to = isInverse ? 0 : 1;
211
212 Optional<Double> costEstimate = Optional.empty();
213
214 if (!freeMaskVariables.contains(srcVariable) && !freeMaskVariables.contains(dstVariable)) {
215 // both variables bound, this is a simple check
216 costEstimate = min(costEstimate, 0.9);
217 } // TODO use bucket size estimation in the runtime context
218 costEstimate = min(costEstimate,
219 edgeUpper.flatMap(edges ->
220 nodeLower.get(from).map(fromNodes ->
221 // amortize edges over start nodes
222 (fromNodes == 0) ? 0.0 : (((double) edges) / fromNodes)
223 )));
224 if (navigatesThroughFunctionalDependencyInverse(input, constraint)) {
225 costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes ->
226 nodeLower.get(from).map(fromNodes ->
227 // due to a reverse functional dependency, the destination count is an upper bound for the edge count
228 (fromNodes == 0) ? 0.0 : ((double) toNodes) / fromNodes
229 )));
230 }
231 if (! edgeUpper.isPresent()) {
232 costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes ->
233 nodeLower.get(from).map(fromNodes ->
234 // If count is 0, no such element exists in the model, so there will be no branching
235 // TODO rethink, why dstNodeCount / srcNodeCount instead of dstNodeCount?
236 // The universally valid bound would be something like sparseEdgeEstimate = dstNodeCount + 1.0
237 // If we assume sparseness, we can reduce it by a SPARSENESS_FACTOR (e.g. 0.1).
238 // Alternatively, discount dstNodeCount * srcNodeCount on a SPARSENESS_EXPONENT (e.g 0.75) and then amortize over srcNodeCount.
239 fromNodes != 0 ? Math.max(1.0, ((double) toNodes) / fromNodes) : 1.0
240 )));
241 }
242 if (navigatesThroughFunctionalDependency(input, constraint)) {
243 // At most one destination value
244 costEstimate = min(costEstimate, 1.0);
245 }
246
247 return costEstimate.orElse(DEFAULT_COST);
248
249 }
250 }
251
252 /**
253 * @since 1.7
254 */
255 protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input,
256 final PConstraint constraint) {
257 return navigatesThroughFunctionalDependency(input, constraint, input.getBoundVariables(), input.getFreeVariables());
258 }
259 /**
260 * @since 2.1
261 */
262 protected boolean navigatesThroughFunctionalDependencyInverse(final IConstraintEvaluationContext input,
263 final PConstraint constraint) {
264 return navigatesThroughFunctionalDependency(input, constraint, input.getFreeVariables(), input.getBoundVariables());
265 }
266 /**
267 * @since 2.1
268 */
269 protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input,
270 final PConstraint constraint, Collection<PVariable> determining, Collection<PVariable> determined) {
271 final QueryAnalyzer queryAnalyzer = input.getQueryAnalyzer();
272 final Map<Set<PVariable>, Set<PVariable>> functionalDependencies = queryAnalyzer
273 .getFunctionalDependencies(Collections.singleton(constraint), false);
274 final Set<PVariable> impliedVariables = FunctionalDependencyHelper.closureOf(determining,
275 functionalDependencies);
276 return ((impliedVariables != null) && impliedVariables.containsAll(determined));
277 }
278
279 protected double calculateUnaryConstraintCost(final TypeConstraint constraint,
280 final IConstraintEvaluationContext input) {
281 PVariable variable = (PVariable) constraint.getVariablesTuple().get(0);
282 if (input.getBoundVariables().contains(variable)) {
283 return 0.9;
284 } else {
285 return projectionSize(input, constraint.getSupplierKey(), TupleMask.identity(1), Accuracy.APPROXIMATION)
286 .map(count -> 1.0 + count).orElse(DEFAULT_COST);
287 }
288 }
289
290 protected double _calculateCost(final ExportedParameter exportedParam, final IConstraintEvaluationContext input) {
291 return 0.0;
292 }
293
294 protected double _calculateCost(final TypeFilterConstraint exportedParam,
295 final IConstraintEvaluationContext input) {
296 return 0.0;
297 }
298
299 protected double _calculateCost(final PositivePatternCall patternCall, final IConstraintEvaluationContext input) {
300 final List<Integer> boundPositions = new ArrayList<>();
301 final List<PParameter> parameters = patternCall.getReferredQuery().getParameters();
302 for (int i = 0; (i < parameters.size()); i++) {
303 final PVariable variable = patternCall.getVariableInTuple(i);
304 if (input.getBoundVariables().contains(variable)) boundPositions.add(i);
305 }
306 TupleMask projMask = TupleMask.fromSelectedIndices(parameters.size(), boundPositions);
307
308 return bucketSize(patternCall, input, projMask).orElse(DEFAULT_COST);
309 }
310
311
312 /**
313 * @since 1.7
314 */
315 protected double _calculateCost(final ExpressionEvaluation evaluation, final IConstraintEvaluationContext input) {
316 // Even if there are multiple results here, if all output variable is bound eval unwind will not result in
317 // multiple branches in search graph
318 final double multiplier = evaluation.isUnwinding() && !input.getFreeVariables().isEmpty()
319 ? EVAL_UNWIND_EXTENSION_FACTOR
320 : 1.0;
321 return _calculateCost((PConstraint) evaluation, input) * multiplier;
322 }
323
324 /**
325 * @since 1.7
326 */
327 protected double _calculateCost(final Inequality inequality, final IConstraintEvaluationContext input) {
328 return _calculateCost((PConstraint)inequality, input);
329 }
330
331 /**
332 * @since 1.7
333 */
334 protected double _calculateCost(final AggregatorConstraint aggregator, final IConstraintEvaluationContext input) {
335 return _calculateCost((PConstraint)aggregator, input);
336 }
337
338 /**
339 * @since 1.7
340 */
341 protected double _calculateCost(final NegativePatternCall call, final IConstraintEvaluationContext input) {
342 return _calculateCost((PConstraint)call, input);
343 }
344
345 /**
346 * @since 1.7
347 */
348 protected double _calculateCost(final PatternMatchCounter counter, final IConstraintEvaluationContext input) {
349 return _calculateCost((PConstraint)counter, input);
350 }
351
352 /**
353 * @since 1.7
354 */
355 protected double _calculateCost(final BinaryTransitiveClosure closure, final IConstraintEvaluationContext input) {
356 // if (input.getFreeVariables().size() == 1) return 3.0;
357 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
358 }
359
360 /**
361 * @since 2.0
362 */
363 protected double _calculateCost(final BinaryReflexiveTransitiveClosure closure, final IConstraintEvaluationContext input) {
364 // if (input.getFreeVariables().size() == 1) return 3.0;
365 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
366 }
367
368 /**
369 * Default cost calculation strategy
370 */
371 protected double _calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) {
372 if (input.getFreeVariables().isEmpty()) {
373 return 1.0;
374 } else {
375 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
376 }
377 }
378
379 /**
380 * @throws ViatraQueryRuntimeException
381 */
382 public double calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) {
383 Preconditions.checkArgument(constraint != null, "Set constraint value correctly");
384 if (constraint instanceof ExportedParameter) {
385 return _calculateCost((ExportedParameter) constraint, input);
386 } else if (constraint instanceof TypeFilterConstraint) {
387 return _calculateCost((TypeFilterConstraint) constraint, input);
388 } else if (constraint instanceof ConstantValue) {
389 return _calculateCost((ConstantValue) constraint, input);
390 } else if (constraint instanceof PositivePatternCall) {
391 return _calculateCost((PositivePatternCall) constraint, input);
392 } else if (constraint instanceof TypeConstraint) {
393 return _calculateCost((TypeConstraint) constraint, input);
394 } else if (constraint instanceof ExpressionEvaluation) {
395 return _calculateCost((ExpressionEvaluation) constraint, input);
396 } else if (constraint instanceof Inequality) {
397 return _calculateCost((Inequality) constraint, input);
398 } else if (constraint instanceof AggregatorConstraint) {
399 return _calculateCost((AggregatorConstraint) constraint, input);
400 } else if (constraint instanceof NegativePatternCall) {
401 return _calculateCost((NegativePatternCall) constraint, input);
402 } else if (constraint instanceof PatternMatchCounter) {
403 return _calculateCost((PatternMatchCounter) constraint, input);
404 } else if (constraint instanceof BinaryTransitiveClosure) {
405 return _calculateCost((BinaryTransitiveClosure) constraint, input);
406 } else if (constraint instanceof BinaryReflexiveTransitiveClosure) {
407 return _calculateCost((BinaryReflexiveTransitiveClosure) constraint, input);
408 } else {
409 // Default cost calculation
410 return _calculateCost(constraint, input);
411 }
412 }
413}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java
new file mode 100644
index 00000000..a517af25
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java
@@ -0,0 +1,95 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Balazs Grill, Akos Horvath, Zoltan Ujhelyi, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner.cost.impl;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
14import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
19import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
20import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22
23/**
24 * This class can be used to calculate cost of application of a constraint with a given adornment.
25 *
26 * For now the logic is based on the following principles:
27 *
28 * <li>The transitive closures, NACs and count finds are the most expensive operations
29 *
30 * <li>The number of free variables increase the cost
31 *
32 * <li>If all the variables of a constraint are free, then its cost equals to twice the number of its parameter
33 * variables. This solves the problem of unnecessary iteration over instances at the beginning of a plan (thus causing
34 * very long run times when executing the plan) by applying constraints based on structural features as soon as
35 * possible.
36 *
37 * <br>
38 *
39 * @author Marton Bur
40 * @since 1.4
41 *
42 */
43public class VariableBindingBasedCostFunction implements ICostFunction {
44
45 // Static cost definitions
46 private static int MAX = 1000;
47 private static int exportedParameterCost = MAX - 20;
48 private static int binaryTransitiveClosureCost = MAX - 50;
49 private static int nacCost = MAX - 100;
50 private static int aggregatorCost = MAX - 200;
51 private static int constantCost = 0;
52
53 @Override
54 public double apply(IConstraintEvaluationContext input) {
55 PConstraint constraint = input.getConstraint();
56 Set<PVariable> affectedVariables = constraint.getAffectedVariables();
57
58 int cost = 0;
59
60 // For constants the cost is determined to be 0.0
61 // The following constraints should be checks:
62 // * Binary transitive closure
63 // * NAC
64 // * count
65 // * exported parameter - only a metadata
66 if (constraint instanceof ConstantValue) {
67 cost = constantCost;
68 } else if (constraint instanceof BinaryTransitiveClosure) {
69 cost = binaryTransitiveClosureCost;
70 } else if (constraint instanceof NegativePatternCall) {
71 cost = nacCost;
72 } else if (constraint instanceof AggregatorConstraint) {
73 cost = aggregatorCost;
74 } else if (constraint instanceof ExportedParameter) {
75 cost = exportedParameterCost;
76 } else {
77 // In case of other constraints count the number of unbound variables
78 for (PVariable pVariable : affectedVariables) {
79 if (input.getFreeVariables().contains(pVariable)) {
80 // For each free variable ('without-value-variable') increase cost
81 cost += 1;
82 }
83 }
84 if (cost == affectedVariables.size()) {
85 // If all the variables are free, double the cost.
86 // This ensures that iteration costs more
87 cost *= 2;
88 }
89
90 }
91
92 return Float.valueOf(cost);
93 }
94
95}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java
new file mode 100644
index 00000000..9b4e9ea5
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java
@@ -0,0 +1,209 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Daniel Segesdi, Akos Horvath, Zoltan Ujhelyi, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner.util;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
20import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
21import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
22import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
25import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
26
27/**
28 *
29 * Helper methods for compiling SubPlans
30 *
31 * @author Marton Bur
32 *
33 */
34public class CompilerHelper {
35
36 private CompilerHelper() {/*Utility class constructor*/}
37
38 private static boolean isUserSpecified(PVariable var) {
39 return !var.isVirtual() && !var.getName().startsWith("_");
40 }
41
42 public static Map<PVariable, Integer> createVariableMapping(SubPlan plan) {
43 Map<PVariable, Integer> variableMapping = new HashMap<>();
44
45 int variableNumber = 0;
46
47 // Important note: this list might contain duplications when parameters are made equal inside the pattern
48 // This is the expected and normal behavior
49 List<PVariable> symbolicParameterVariables = plan.getBody().getSymbolicParameterVariables();
50 for (PVariable pVariable : symbolicParameterVariables) {
51 if (!variableMapping.containsKey(pVariable)) {
52 variableMapping.put(pVariable, variableNumber++);
53 }
54 }
55
56 List<PVariable> allVariables = new ArrayList<>(plan.getBody().getUniqueVariables());
57 Collections.sort(allVariables, (left, right) -> {
58 boolean leftUserSpecified = isUserSpecified(left);
59 boolean rightUserSpecified = isUserSpecified(right);
60 if (leftUserSpecified && !rightUserSpecified) {
61 return -1;
62 } else if (!leftUserSpecified && rightUserSpecified) {
63 return +1;
64 } else {
65 return left.getName().compareTo(right.getName());
66 }
67 });
68 for (PVariable pVariable : allVariables) {
69 if (!variableMapping.containsKey(pVariable)) {
70 variableMapping.put(pVariable, variableNumber++);
71 }
72 }
73
74 return variableMapping;
75 }
76
77 public static Map<PConstraint, Set<Integer>> cacheVariableBindings(SubPlan plan,
78 Map<PVariable, Integer> variableMappings, Set<PParameter> adornment) {
79
80 Set<Integer> externallyBoundVariables = getVariableIndicesForParameters(plan, variableMappings,
81 adornment);
82
83 Map<PConstraint, Set<Integer>> variableBindings = new HashMap<>();
84
85 List<SubPlan> allPlansInHierarchy = getAllParentPlans(plan);
86 for (SubPlan subPlan : allPlansInHierarchy) {
87 POperation operation = subPlan.getOperation();
88
89 if (operation instanceof PApply) {
90 PConstraint pConstraint = ((PApply) operation).getPConstraint();
91 Set<Integer> boundVariableIndices = getParametersBoundByParentPlan(variableMappings, subPlan);
92 boundVariableIndices.addAll(externallyBoundVariables);
93
94 variableBindings.put(pConstraint, boundVariableIndices);
95 }
96 }
97 return variableBindings;
98 }
99
100 /**
101 * Returns the list of variable indexes that are bound by the parent plan.
102 */
103 private static Set<Integer> getParametersBoundByParentPlan(Map<PVariable, Integer> variableMappings,
104 SubPlan subPlan) {
105 if (!subPlan.getParentPlans().isEmpty()) {
106 SubPlan parentPlan = subPlan.getParentPlans().get(0);
107 Set<PConstraint> enforcedConstraints = parentPlan.getAllEnforcedConstraints();
108 Set<PVariable> affectedVariables = getAffectedVariables(enforcedConstraints);
109 return getVariableIndices(variableMappings, affectedVariables);
110 }
111 return Collections.emptySet();
112 }
113
114 /**
115 * @param plan
116 * @return all the ancestor plans including the given plan
117 */
118 private static List<SubPlan> getAllParentPlans(SubPlan plan) {
119 SubPlan currentPlan = plan;
120 List<SubPlan> allPlans = new ArrayList<>();
121 allPlans.add(plan);
122 while (!currentPlan.getParentPlans().isEmpty()) {
123 // In the local search it is assumed that only a single parent exists
124 currentPlan = currentPlan.getParentPlans().get(0);
125 allPlans.add(currentPlan);
126 }
127
128 return allPlans;
129 }
130
131 /**
132 * @param variableMappings
133 * the mapping between variables and their indices
134 * @param variables
135 * variables to get the indices for
136 * @return the set of variable indices for the given variables
137 */
138 private static Set<Integer> getVariableIndices(Map<PVariable, Integer> variableMappings,
139 Iterable<PVariable> variables) {
140 Set<Integer> variableIndices = new HashSet<>();
141 for (PVariable pVariable : variables) {
142 variableIndices.add(variableMappings.get(pVariable));
143 }
144 return variableIndices;
145 }
146
147 /**
148 * Returns all affected variables of the given PConstraints.
149 */
150 private static Set<PVariable> getAffectedVariables(Set<PConstraint> pConstraints) {
151 Set<PVariable> allDeducedVariables = new HashSet<>();
152 for (PConstraint pConstraint : pConstraints) {
153 allDeducedVariables.addAll(pConstraint.getAffectedVariables());
154 }
155 return allDeducedVariables;
156 }
157
158 /**
159 * Transforms the index of a parameter into the index of a variable of the normalized body.
160 *
161 * @param plan
162 * the SubPlan containing the original body and its parameters
163 * @param variableMappings
164 * the mapping of PVariables to their indices
165 * @param parameters
166 * a set of parameters
167 * @return the index of the variable corresponding to the parameter at the given index
168 */
169 private static Set<Integer> getVariableIndicesForParameters(SubPlan plan,
170 Map<PVariable, Integer> variableMappings, Set<PParameter> parameters) {
171 Map<PParameter, PVariable> parameterMapping = new HashMap<>();
172 for (ExportedParameter constraint : plan.getBody().getSymbolicParameters()) {
173 parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable());
174 }
175
176 Set<Integer> variableIndices = new HashSet<>();
177 for (PParameter parameter : parameters) {
178 PVariable parameterVariable = parameterMapping.get(parameter);
179 if (parameterVariable == null) {
180 // XXX In case of older (pre-1.4) VIATRA versions, PParameters were not stable, see bug 498348
181 parameterVariable = plan.getBody().getVariableByNameChecked(parameter.getName());
182 }
183 Integer variableIndex = variableMappings.get(parameterVariable);
184 variableIndices.add(variableIndex);
185 }
186 return variableIndices;
187 }
188
189 /**
190 * Extracts the operations from a SubPlan into a list of POperations in the order of execution
191 *
192 * @param plan
193 * the SubPlan from wich the POperations should be extracted
194 * @return list of POperations extracted from the <code>plan</code>
195 */
196 public static List<POperation> createOperationsList(SubPlan plan) {
197 List<POperation> operationsList = new ArrayList<>();
198 while (plan.getParentPlans().size() > 0) {
199 operationsList.add(plan.getOperation());
200 SubPlan parentPlan = plan.getParentPlans().get(0);
201 plan = parentPlan;
202 }
203 operationsList.add(plan.getOperation());
204
205 Collections.reverse(operationsList);
206 return operationsList;
207 }
208
209}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java
new file mode 100644
index 00000000..58f4fc41
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.localsearch.planner.util;
10
11import java.util.Comparator;
12
13import tools.refinery.viatra.runtime.localsearch.planner.PConstraintInfo;
14
15/**
16 * @author Marton Bur
17 *
18 */
19public class OperationCostComparator implements Comparator<PConstraintInfo>{
20
21 @Override
22 public int compare(PConstraintInfo o1, PConstraintInfo o2) {
23 return Double.compare(o1.getCost(), o2.getCost());
24 }
25
26}