diff options
Diffstat (limited to 'Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java')
-rw-r--r-- | Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java new file mode 100644 index 00000000..d1a41065 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java | |||
@@ -0,0 +1,338 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi 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 org.eclipse.viatra.dse.api; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.List; | ||
14 | import java.util.Objects; | ||
15 | import java.util.function.Consumer; | ||
16 | |||
17 | import org.eclipse.emf.common.notify.Notifier; | ||
18 | import org.eclipse.emf.edit.command.ChangeCommand; | ||
19 | import org.eclipse.emf.edit.domain.EditingDomain; | ||
20 | import org.eclipse.viatra.dse.base.DseIdPoolHelper; | ||
21 | import org.eclipse.viatra.dse.designspace.api.IBacktrackListener; | ||
22 | import org.eclipse.viatra.dse.objectives.Fitness; | ||
23 | import org.eclipse.viatra.dse.statecode.IStateCoder; | ||
24 | import org.eclipse.viatra.dse.statecode.IStateCoderFactory; | ||
25 | import org.eclipse.viatra.dse.util.EMFHelper; | ||
26 | import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; | ||
27 | import org.eclipse.viatra.query.runtime.api.IPatternMatch; | ||
28 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | ||
29 | import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher; | ||
30 | import org.eclipse.viatra.query.runtime.emf.EMFScope; | ||
31 | import org.eclipse.viatra.query.runtime.matchers.ViatraQueryRuntimeException; | ||
32 | import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; | ||
33 | import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; | ||
34 | |||
35 | import com.google.common.util.concurrent.UncheckedExecutionException; | ||
36 | |||
37 | /** | ||
38 | * A SolutionTrajectory represents a trajectory (i.e. sequence of transformation | ||
39 | * rule applications), which can transform the initial model to a desired state. | ||
40 | * An instance of this class holds the the actual rule sequence and the | ||
41 | * corresponding activation codes. Furthermore it can be used to perform the | ||
42 | * transformation on a given model (if possible). | ||
43 | * <p> | ||
44 | * It is also possible to undo the transformation if initialized with an editing | ||
45 | * domain. | ||
46 | * <p> | ||
47 | * The instance of this class can be reused for different models. | ||
48 | * | ||
49 | * @author Andras Szabolcs Nagy | ||
50 | * | ||
51 | */ | ||
52 | public class SolutionTrajectory { | ||
53 | |||
54 | private final List<Object> activationCodes; | ||
55 | private final List<BatchTransformationRule<?, ?>> transformationRules; | ||
56 | private final IStateCoderFactory stateCoderFactory; | ||
57 | private Fitness fitness; | ||
58 | private Solution solution; | ||
59 | |||
60 | private ViatraQueryEngine engine; | ||
61 | private Notifier model; | ||
62 | private EditingDomain editingDomain; | ||
63 | private IStateCoder stateCoder; | ||
64 | private IBacktrackListener listener; | ||
65 | |||
66 | private int currentIndex; | ||
67 | |||
68 | public SolutionTrajectory(final List<Object> activationCodes, | ||
69 | final List<BatchTransformationRule<?, ?>> transformationRules, final IStateCoderFactory stateCoderFactory, | ||
70 | final IBacktrackListener backtrackListener) { | ||
71 | Objects.requireNonNull(transformationRules, "Parameter transformationRules cannot be null!"); | ||
72 | Objects.requireNonNull(stateCoderFactory, "Parameter stateCoderFactory cannot be null!"); | ||
73 | Objects.requireNonNull(activationCodes, "Parameter activations cannot be null!"); | ||
74 | Preconditions.checkState(transformationRules.size() == activationCodes.size(), | ||
75 | "The two List parameters must be the same in size."); | ||
76 | |||
77 | this.activationCodes = activationCodes; | ||
78 | this.transformationRules = transformationRules; | ||
79 | this.stateCoderFactory = stateCoderFactory; | ||
80 | this.listener = backtrackListener; | ||
81 | currentIndex = 0; | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * Initialize this SolutionTrajectory for transforming the model along the | ||
86 | * trajectory. | ||
87 | * | ||
88 | * @param model The model. | ||
89 | * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. | ||
90 | */ | ||
91 | public void setModel(Notifier model) { | ||
92 | editingDomain = null; | ||
93 | EMFScope scope = new EMFScope(model); | ||
94 | this.engine = ViatraQueryEngine.on(scope); | ||
95 | this.model = model; | ||
96 | stateCoder = stateCoderFactory.createStateCoder(); | ||
97 | stateCoder.init(model); | ||
98 | currentIndex = 0; | ||
99 | DseIdPoolHelper.INSTANCE.disposeByThread(); | ||
100 | DseIdPoolHelper.INSTANCE.registerRules(rule -> { | ||
101 | int id = 0; | ||
102 | for (BatchTransformationRule<?, ?> r : transformationRules.subList(0, currentIndex)) { | ||
103 | if (r.equals(rule)) { | ||
104 | id++; | ||
105 | } | ||
106 | } | ||
107 | return id; | ||
108 | }, new HashSet<BatchTransformationRule<?, ?>>(transformationRules)); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Initialize this SolutionTrajectory for transforming the given model along the | ||
113 | * trajectory. | ||
114 | * <p> | ||
115 | * The transformation will be reversible by creating an {@link EditingDomain} on | ||
116 | * the model. | ||
117 | * | ||
118 | * @param modelRoot The root of the model. | ||
119 | * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. | ||
120 | */ | ||
121 | public void setModelWithEditingDomain(Notifier modelRoot) { | ||
122 | setModel(modelRoot); | ||
123 | editingDomain = EMFHelper.createEditingDomain(model); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Transforms the given model along the trajectory. | ||
128 | * | ||
129 | * @param modelRoot The root of the model. | ||
130 | * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. | ||
131 | */ | ||
132 | public void doTransformation(Notifier modelRoot) { | ||
133 | setModel(modelRoot); | ||
134 | doTransformation(); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * Transforms the given model along the trajectory. | ||
139 | * <p> | ||
140 | * The transformation will be reversible by creating an {@link EditingDomain} on | ||
141 | * the model. | ||
142 | * | ||
143 | * @param modelRoot The root of the model. | ||
144 | * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. | ||
145 | */ | ||
146 | public void doTransformationUndoable(Notifier modelRoot) { | ||
147 | setModelWithEditingDomain(modelRoot); | ||
148 | doTransformation(); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Transforms the given model along the trajectory. To initialize the model call | ||
153 | * the {@link SolutionTrajectory#setModel(Notifier)} method. | ||
154 | * | ||
155 | * @throws Exception If the activation to fire is not found. | ||
156 | * Possible problems: wrong model, bad state | ||
157 | * serializer. | ||
158 | * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. | ||
159 | */ | ||
160 | public void doTransformation() { | ||
161 | while (doNextTransformation()) | ||
162 | ; | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * Transforms the given model by one step to the solution (makes one step in the | ||
167 | * trajectory). To initialize the model call the | ||
168 | * {@link SolutionTrajectory#setModel(Notifier)} method. | ||
169 | * | ||
170 | * @throws ViatraQueryRuntimeException | ||
171 | */ | ||
172 | public boolean doNextTransformation() { | ||
173 | if (currentIndex >= activationCodes.size()) { | ||
174 | return false; | ||
175 | } else { | ||
176 | doNextTransformation(currentIndex); | ||
177 | currentIndex++; | ||
178 | return true; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | @SuppressWarnings("unchecked") | ||
183 | private void doNextTransformation(int index) { | ||
184 | Objects.requireNonNull(model, "The model cannot be null! Use the setModel method."); | ||
185 | |||
186 | // cast for the ".process(match)" method. | ||
187 | BatchTransformationRule<?, ?> tr = transformationRules.get(index); | ||
188 | Object activationCode = activationCodes.get(index); | ||
189 | |||
190 | ViatraQueryMatcher<?> matcher = tr.getPrecondition().getMatcher(engine); | ||
191 | |||
192 | boolean isActivationFound = false; | ||
193 | for (final IPatternMatch match : matcher.getAllMatches()) { | ||
194 | Object matchHash = stateCoder.createActivationCode(match); | ||
195 | if (matchHash.equals(activationCode)) { | ||
196 | @SuppressWarnings("rawtypes") | ||
197 | final Consumer action = tr.getAction(); | ||
198 | |||
199 | if (editingDomain == null) { | ||
200 | action.accept(match); | ||
201 | } else { | ||
202 | ChangeCommand cc = new ChangeCommand(model) { | ||
203 | @Override | ||
204 | protected void doExecute() { | ||
205 | action.accept(match); | ||
206 | } | ||
207 | }; | ||
208 | long start = System.nanoTime(); | ||
209 | editingDomain.getCommandStack().execute(cc); | ||
210 | listener.forwardWorked(System.nanoTime() - start); | ||
211 | } | ||
212 | |||
213 | isActivationFound = true; | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | if (!isActivationFound) { | ||
218 | throw new UncheckedExecutionException( | ||
219 | "Activation was not found for transformation! Possible cause: wrong model, bad state coder. index: " | ||
220 | + index + " Activation code: " + activationCode, | ||
221 | null); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * Call this method to undo the last transformation. | ||
227 | * | ||
228 | * @return True, if it was successful. | ||
229 | */ | ||
230 | public boolean undoLastTransformation() { | ||
231 | Objects.requireNonNull(editingDomain, "To be able to undo the transformation initialize with editing domain."); | ||
232 | long start = System.nanoTime(); | ||
233 | boolean result; | ||
234 | |||
235 | if (currentIndex > 0) { | ||
236 | try { | ||
237 | ((AdvancedViatraQueryEngine) engine).delayUpdatePropagation(() -> { | ||
238 | editingDomain.getCommandStack().undo(); | ||
239 | return null; | ||
240 | }); | ||
241 | } catch (InvocationTargetException e) { | ||
242 | throw new RuntimeException(e); | ||
243 | } | ||
244 | currentIndex--; | ||
245 | result = true; | ||
246 | } | ||
247 | result = false; | ||
248 | listener.backtrackWorked(System.nanoTime() - start); | ||
249 | return result; | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * Call this method to undo the transformation. | ||
254 | */ | ||
255 | public void undoTransformation() { | ||
256 | while (undoLastTransformation()) | ||
257 | ; | ||
258 | } | ||
259 | |||
260 | public List<Object> getActivationCodes() { | ||
261 | return activationCodes; | ||
262 | } | ||
263 | |||
264 | public List<BatchTransformationRule<?, ?>> getTransformationRules() { | ||
265 | return transformationRules; | ||
266 | } | ||
267 | |||
268 | public IStateCoderFactory getStateCoderFactory() { | ||
269 | return stateCoderFactory; | ||
270 | } | ||
271 | |||
272 | public ViatraQueryEngine getEngine() { | ||
273 | return engine; | ||
274 | } | ||
275 | |||
276 | public Notifier getModel() { | ||
277 | return model; | ||
278 | } | ||
279 | |||
280 | public IStateCoder getStateCoder() { | ||
281 | return stateCoder; | ||
282 | } | ||
283 | |||
284 | public int getCurrentIndex() { | ||
285 | return currentIndex; | ||
286 | } | ||
287 | |||
288 | public int getTrajectoryLength() { | ||
289 | return activationCodes.size(); | ||
290 | } | ||
291 | |||
292 | public Fitness getFitness() { | ||
293 | return fitness; | ||
294 | } | ||
295 | |||
296 | public void setFitness(Fitness fitness) { | ||
297 | this.fitness = fitness; | ||
298 | } | ||
299 | |||
300 | public String toPrettyString() { | ||
301 | StringBuilder sb = new StringBuilder(); | ||
302 | sb.append("Fitness: "); | ||
303 | sb.append(fitness.toString()); | ||
304 | sb.append(" | Trajectory ("); | ||
305 | sb.append(activationCodes.size()); | ||
306 | sb.append("): "); | ||
307 | for (Object object : activationCodes) { | ||
308 | sb.append(object.toString()); | ||
309 | sb.append(" | "); | ||
310 | } | ||
311 | return sb.toString(); | ||
312 | } | ||
313 | |||
314 | @Override | ||
315 | public int hashCode() { | ||
316 | return activationCodes.hashCode(); | ||
317 | } | ||
318 | |||
319 | @Override | ||
320 | public boolean equals(Object obj) { | ||
321 | if (this == obj) { | ||
322 | return true; | ||
323 | } | ||
324 | if (obj instanceof SolutionTrajectory) { | ||
325 | SolutionTrajectory that = (SolutionTrajectory) obj; | ||
326 | return activationCodes.equals(that.activationCodes); | ||
327 | } | ||
328 | return false; | ||
329 | } | ||
330 | |||
331 | public Solution getSolution() { | ||
332 | return solution; | ||
333 | } | ||
334 | |||
335 | public void setSolution(Solution solution) { | ||
336 | this.solution = solution; | ||
337 | } | ||
338 | } | ||