diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2023-09-14 19:29:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-14 19:29:36 +0200 |
commit | 98ed3b6db5f4e51961a161050cc31c66015116e8 (patch) | |
tree | 8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java | |
parent | Merge pull request #38 from nagilooh/design-space-exploration (diff) | |
parent | Merge remote-tracking branch 'upstream/main' into partial-interpretation (diff) | |
download | refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip |
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
Diffstat (limited to 'subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java')
-rw-r--r-- | subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java new file mode 100644 index 00000000..9ee74207 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java | |||
@@ -0,0 +1,233 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.web.generator; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import org.eclipse.emf.common.util.URI; | ||
11 | import org.eclipse.xtext.diagnostics.Severity; | ||
12 | import org.eclipse.xtext.resource.IResourceFactory; | ||
13 | import org.eclipse.xtext.resource.XtextResourceSet; | ||
14 | import org.eclipse.xtext.service.OperationCanceledManager; | ||
15 | import org.eclipse.xtext.util.LazyStringInputStream; | ||
16 | import org.eclipse.xtext.validation.CheckMode; | ||
17 | import org.eclipse.xtext.validation.IResourceValidator; | ||
18 | import org.slf4j.Logger; | ||
19 | import org.slf4j.LoggerFactory; | ||
20 | import tools.refinery.language.model.problem.Problem; | ||
21 | import tools.refinery.language.semantics.metadata.MetadataCreator; | ||
22 | import tools.refinery.language.semantics.model.ModelInitializer; | ||
23 | import tools.refinery.language.web.semantics.PartialInterpretation2Json; | ||
24 | import tools.refinery.language.web.xtext.server.ThreadPoolExecutorServiceProvider; | ||
25 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; | ||
26 | import tools.refinery.store.dse.propagation.PropagationAdapter; | ||
27 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; | ||
28 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; | ||
29 | import tools.refinery.store.model.ModelStore; | ||
30 | import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; | ||
31 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
32 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | ||
33 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
34 | import tools.refinery.store.statecoding.StateCoderAdapter; | ||
35 | import tools.refinery.store.util.CancellationToken; | ||
36 | |||
37 | import java.io.IOException; | ||
38 | import java.util.Map; | ||
39 | import java.util.UUID; | ||
40 | import java.util.concurrent.*; | ||
41 | |||
42 | public class ModelGenerationWorker implements Runnable { | ||
43 | private static final Logger LOG = LoggerFactory.getLogger(ModelGenerationWorker.class); | ||
44 | |||
45 | private final UUID uuid = UUID.randomUUID(); | ||
46 | |||
47 | private PushWebDocument state; | ||
48 | |||
49 | private String text; | ||
50 | |||
51 | private volatile boolean timedOut; | ||
52 | |||
53 | private volatile boolean cancelled; | ||
54 | |||
55 | @Inject | ||
56 | private OperationCanceledManager operationCanceledManager; | ||
57 | |||
58 | @Inject | ||
59 | private Provider<XtextResourceSet> resourceSetProvider; | ||
60 | |||
61 | @Inject | ||
62 | private IResourceFactory resourceFactory; | ||
63 | |||
64 | @Inject | ||
65 | private IResourceValidator resourceValidator; | ||
66 | |||
67 | @Inject | ||
68 | private ModelInitializer initializer; | ||
69 | |||
70 | @Inject | ||
71 | private MetadataCreator metadataCreator; | ||
72 | |||
73 | @Inject | ||
74 | private PartialInterpretation2Json partialInterpretation2Json; | ||
75 | |||
76 | private final Object lockObject = new Object(); | ||
77 | |||
78 | private ExecutorService executorService; | ||
79 | |||
80 | private ScheduledExecutorService scheduledExecutorService; | ||
81 | |||
82 | private int randomSeed; | ||
83 | |||
84 | private long timeoutSec; | ||
85 | |||
86 | private Future<?> future; | ||
87 | |||
88 | private ScheduledFuture<?> timeoutFuture; | ||
89 | |||
90 | private final CancellationToken cancellationToken = () -> { | ||
91 | if (cancelled || Thread.interrupted()) { | ||
92 | operationCanceledManager.throwOperationCanceledException(); | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | @Inject | ||
97 | public void setExecutorServiceProvider(ThreadPoolExecutorServiceProvider provider) { | ||
98 | executorService = provider.get(ModelGenerationService.MODEL_GENERATION_EXECUTOR); | ||
99 | scheduledExecutorService = provider.getScheduled(ModelGenerationService.MODEL_GENERATION_TIMEOUT_EXECUTOR); | ||
100 | } | ||
101 | |||
102 | public void setState(PushWebDocument state, int randomSeed, long timeoutSec) { | ||
103 | this.state = state; | ||
104 | this.randomSeed = randomSeed; | ||
105 | this.timeoutSec = timeoutSec; | ||
106 | text = state.getText(); | ||
107 | } | ||
108 | |||
109 | public UUID getUuid() { | ||
110 | return uuid; | ||
111 | } | ||
112 | |||
113 | public void start() { | ||
114 | synchronized (lockObject) { | ||
115 | LOG.debug("Enqueueing model generation: {}", uuid); | ||
116 | future = executorService.submit(this); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | public void startTimeout() { | ||
121 | synchronized (lockObject) { | ||
122 | LOG.debug("Starting model generation: {}", uuid); | ||
123 | cancellationToken.checkCancelled(); | ||
124 | timeoutFuture = scheduledExecutorService.schedule(() -> cancel(true), timeoutSec, TimeUnit.SECONDS); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | // We catch {@code Throwable} to handle {@code OperationCancelledError}, but we rethrow fatal JVM errors. | ||
129 | @SuppressWarnings("squid:S1181") | ||
130 | @Override | ||
131 | public void run() { | ||
132 | startTimeout(); | ||
133 | notifyResult(new ModelGenerationStatusResult(uuid, "Initializing model generator")); | ||
134 | ModelGenerationResult result; | ||
135 | try { | ||
136 | result = doRun(); | ||
137 | } catch (Throwable e) { | ||
138 | if (operationCanceledManager.isOperationCanceledException(e)) { | ||
139 | var message = timedOut ? "Model generation timed out" : "Model generation cancelled"; | ||
140 | LOG.debug("{}: {}", message, uuid); | ||
141 | notifyResult(new ModelGenerationErrorResult(uuid, message)); | ||
142 | } else if (e instanceof Error error) { | ||
143 | // Make sure we don't try to recover from any fatal JVM errors. | ||
144 | throw error; | ||
145 | } else { | ||
146 | LOG.debug("Model generation error", e); | ||
147 | notifyResult(new ModelGenerationErrorResult(uuid, e.toString())); | ||
148 | } | ||
149 | return; | ||
150 | } | ||
151 | notifyResult(result); | ||
152 | } | ||
153 | |||
154 | private void notifyResult(ModelGenerationResult result) { | ||
155 | state.notifyPrecomputationListeners(ModelGenerationService.SERVICE_NAME, result); | ||
156 | } | ||
157 | |||
158 | public ModelGenerationResult doRun() throws IOException { | ||
159 | cancellationToken.checkCancelled(); | ||
160 | var resourceSet = resourceSetProvider.get(); | ||
161 | var uri = URI.createURI("__synthetic_" + uuid + ".problem"); | ||
162 | var resource = resourceFactory.createResource(uri); | ||
163 | resourceSet.getResources().add(resource); | ||
164 | var inputStream = new LazyStringInputStream(text); | ||
165 | resource.load(inputStream, Map.of()); | ||
166 | cancellationToken.checkCancelled(); | ||
167 | var issues = resourceValidator.validate(resource, CheckMode.ALL, () -> cancelled || Thread.interrupted()); | ||
168 | cancellationToken.checkCancelled(); | ||
169 | for (var issue : issues) { | ||
170 | if (issue.getSeverity() == Severity.ERROR) { | ||
171 | return new ModelGenerationErrorResult(uuid, "Validation error: " + issue.getMessage()); | ||
172 | } | ||
173 | } | ||
174 | if (resource.getContents().isEmpty() || !(resource.getContents().get(0) instanceof Problem problem)) { | ||
175 | return new ModelGenerationErrorResult(uuid, "Model generation problem not found"); | ||
176 | } | ||
177 | cancellationToken.checkCancelled(); | ||
178 | var storeBuilder = ModelStore.builder() | ||
179 | .cancellationToken(cancellationToken) | ||
180 | .with(ViatraModelQueryAdapter.builder()) | ||
181 | .with(PropagationAdapter.builder()) | ||
182 | .with(StateCoderAdapter.builder()) | ||
183 | .with(DesignSpaceExplorationAdapter.builder()) | ||
184 | .with(ReasoningAdapter.builder() | ||
185 | .requiredInterpretations(Concreteness.CANDIDATE)); | ||
186 | var modelSeed = initializer.createModel(problem, storeBuilder); | ||
187 | var store = storeBuilder.build(); | ||
188 | cancellationToken.checkCancelled(); | ||
189 | var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); | ||
190 | var initialVersion = model.commit(); | ||
191 | cancellationToken.checkCancelled(); | ||
192 | notifyResult(new ModelGenerationStatusResult(uuid, "Generating model")); | ||
193 | var bestFirst = new BestFirstStoreManager(store, 1); | ||
194 | bestFirst.startExploration(initialVersion, randomSeed); | ||
195 | cancellationToken.checkCancelled(); | ||
196 | var solutionStore = bestFirst.getSolutionStore(); | ||
197 | if (solutionStore.getSolutions().isEmpty()) { | ||
198 | return new ModelGenerationErrorResult(uuid, "Problem is unsatisfiable"); | ||
199 | } | ||
200 | notifyResult(new ModelGenerationStatusResult(uuid, "Saving generated model")); | ||
201 | model.restore(solutionStore.getSolutions().get(0).version()); | ||
202 | cancellationToken.checkCancelled(); | ||
203 | metadataCreator.setInitializer(initializer); | ||
204 | var nodesMetadata = metadataCreator.getNodesMetadata(model.getAdapter(ReasoningAdapter.class).getNodeCount(), | ||
205 | false); | ||
206 | cancellationToken.checkCancelled(); | ||
207 | var relationsMetadata = metadataCreator.getRelationsMetadata(); | ||
208 | cancellationToken.checkCancelled(); | ||
209 | var partialInterpretation = partialInterpretation2Json.getPartialInterpretation(initializer, model, | ||
210 | Concreteness.CANDIDATE, cancellationToken); | ||
211 | return new ModelGenerationSuccessResult(uuid, nodesMetadata, relationsMetadata, partialInterpretation); | ||
212 | } | ||
213 | |||
214 | public void cancel() { | ||
215 | cancel(false); | ||
216 | } | ||
217 | |||
218 | public void cancel(boolean timedOut) { | ||
219 | synchronized (lockObject) { | ||
220 | LOG.trace("Cancelling model generation: {}", uuid); | ||
221 | this.timedOut = timedOut; | ||
222 | cancelled = true; | ||
223 | if (future != null) { | ||
224 | future.cancel(true); | ||
225 | future = null; | ||
226 | } | ||
227 | if (timeoutFuture != null) { | ||
228 | timeoutFuture.cancel(true); | ||
229 | timeoutFuture = null; | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | } | ||