diff options
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner')
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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody; | ||
15 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | |||
19 | /** | ||
20 | * @author Zoltan Ujhelyi | ||
21 | * @since 1.7 | ||
22 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation; | ||
14 | |||
15 | /** | ||
16 | * @author Marton Bur | ||
17 | * | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import org.apache.log4j.Logger; | ||
20 | import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints; | ||
21 | import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody; | ||
23 | import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler; | ||
24 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
27 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PBodyNormalizer; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriter; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriterCacher; | ||
36 | import 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 | */ | ||
43 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints; | ||
20 | import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction; | ||
21 | import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
23 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
24 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
25 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
26 | import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; | ||
27 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
32 | import 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 | */ | ||
47 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | */ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.LinkedHashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.Function; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
24 | import 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 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
13 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.AbstractTransitiveClosure; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
26 | |||
27 | import java.util.*; | ||
28 | import java.util.function.Function; | ||
29 | import java.util.function.Predicate; | ||
30 | import java.util.stream.Collectors; | ||
31 | import 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 | */ | ||
38 | class 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Comparator; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.List; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | |||
20 | import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator; | ||
21 | import tools.refinery.viatra.runtime.matchers.algorithms.OrderedIterableMerge; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
24 | import 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 | */ | ||
36 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.compiler; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
20 | import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment; | ||
21 | import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.viatra.runtime.localsearch.operations.check.AggregatorCheck; | ||
23 | import tools.refinery.viatra.runtime.localsearch.operations.check.BinaryTransitiveClosureCheck; | ||
24 | import tools.refinery.viatra.runtime.localsearch.operations.check.CheckConstant; | ||
25 | import tools.refinery.viatra.runtime.localsearch.operations.check.CheckPositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.localsearch.operations.check.CountCheck; | ||
27 | import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionCheck; | ||
28 | import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionEvalCheck; | ||
29 | import tools.refinery.viatra.runtime.localsearch.operations.check.InequalityCheck; | ||
30 | import tools.refinery.viatra.runtime.localsearch.operations.check.NACOperation; | ||
31 | import tools.refinery.viatra.runtime.localsearch.operations.extend.AggregatorExtend; | ||
32 | import tools.refinery.viatra.runtime.localsearch.operations.extend.CountOperation; | ||
33 | import tools.refinery.viatra.runtime.localsearch.operations.extend.ExpressionEval; | ||
34 | import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendBinaryTransitiveClosure; | ||
35 | import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendConstant; | ||
36 | import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendPositivePatternCall; | ||
37 | import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation; | ||
38 | import tools.refinery.viatra.runtime.localsearch.planner.util.CompilerHelper; | ||
39 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
40 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
41 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
42 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
43 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
44 | import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; | ||
45 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
46 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
47 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
48 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
49 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
50 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
51 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
52 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
53 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
54 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall; | ||
55 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter; | ||
56 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
57 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure; | ||
58 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
59 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
60 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
61 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
62 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
63 | |||
64 | /** | ||
65 | * @author Zoltan Ujhelyi | ||
66 | * @since 1.7 | ||
67 | * | ||
68 | */ | ||
69 | public 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<ISearchOperation></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 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.localsearch.planner.compiler; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeCheck; | ||
13 | import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtend; | ||
14 | import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | |||
23 | import java.util.*; | ||
24 | |||
25 | /** | ||
26 | * @author Zoltan Ujhelyi | ||
27 | * @since 1.7 | ||
28 | * | ||
29 | */ | ||
30 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.compiler; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment; | ||
16 | import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference; | ||
17 | import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation; | ||
18 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import 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 | */ | ||
30 | public interface IOperationCompiler { | ||
31 | |||
32 | /** | ||
33 | * Compiles a plan of <code>POperation</code>s to a list of type <code>List<ISearchOperation></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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.cost; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
13 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import java.util.Collection; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import 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 | */ | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import 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 | ||
34 | public 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 | */ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
16 | import 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 | */ | ||
24 | public 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 | */ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.cost.impl; | ||
10 | |||
11 | import static tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper.min; | ||
12 | |||
13 | import java.util.ArrayList; | ||
14 | import java.util.Arrays; | ||
15 | import java.util.Collection; | ||
16 | import java.util.Collections; | ||
17 | import java.util.List; | ||
18 | import java.util.Map; | ||
19 | import java.util.Optional; | ||
20 | import java.util.Set; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider; | ||
23 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
24 | import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction; | ||
25 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
26 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall; | ||
38 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter; | ||
39 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
40 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure; | ||
41 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
42 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
43 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
44 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
45 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
46 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
47 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
48 | import 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 | */ | ||
60 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext; | ||
14 | import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
21 | import 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 | */ | ||
43 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
26 | |||
27 | /** | ||
28 | * | ||
29 | * Helper methods for compiling SubPlans | ||
30 | * | ||
31 | * @author Marton Bur | ||
32 | * | ||
33 | */ | ||
34 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.planner.util; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.localsearch.planner.PConstraintInfo; | ||
14 | |||
15 | /** | ||
16 | * @author Marton Bur | ||
17 | * | ||
18 | */ | ||
19 | public 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 | } | ||