diff options
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java')
-rw-r--r-- | subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java | 257 |
1 files changed, 257 insertions, 0 deletions
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 | } | ||