diff options
author | 2023-09-10 23:07:11 +0200 | |
---|---|---|
committer | 2023-09-10 23:07:11 +0200 | |
commit | c7a86623b1589a3bd68a84a8d54a1eadc1aacefb (patch) | |
tree | 16fb5eb3d3b1282fecedefd9c7ae519162c540da | |
parent | feat: integrate DSE with partial interpretation (diff) | |
download | refinery-c7a86623b1589a3bd68a84a8d54a1eadc1aacefb.tar.gz refinery-c7a86623b1589a3bd68a84a8d54a1eadc1aacefb.tar.zst refinery-c7a86623b1589a3bd68a84a8d54a1eadc1aacefb.zip |
fix: VIATRA projection indexer error
When a projection indexer is constructed for a production node, the projection
memory is only populated if changes are being propagated. The cache doesn't get
populated even if changes are flushed afterwards. This not only returns invalid
query results, but also a duplicate deletion exception will be thrown when the
production node tries to delete a tuple from the index memory.
To counteract this issue, we enable update propagation while a matcher (and its
associated indexers) are being created.
6 files changed, 129 insertions, 40 deletions
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java index a383d043..eee2c4ae 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java | |||
@@ -67,15 +67,12 @@ class CountPropagationTest { | |||
67 | .put(Tuple.of(3), TruthValue.TRUE)) | 67 | .put(Tuple.of(3), TruthValue.TRUE)) |
68 | .build(); | 68 | .build(); |
69 | 69 | ||
70 | var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); | 70 | var initialModel = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); |
71 | var initialState = initialModel.commit(); | ||
72 | |||
73 | var model = store.createModelForState(initialState); | ||
71 | var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); | 74 | var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); |
72 | var propagationAdapter = model.getAdapter(PropagationAdapter.class); | 75 | var propagationAdapter = model.getAdapter(PropagationAdapter.class); |
73 | model.commit(); | ||
74 | |||
75 | reasoningAdapter.split(0); | ||
76 | assertThat(propagationAdapter.propagate(), is(PropagationResult.UNCHANGED)); | ||
77 | model.commit(); | ||
78 | |||
79 | reasoningAdapter.split(0); | 76 | reasoningAdapter.split(0); |
80 | assertThat(propagationAdapter.propagate(), is(PropagationResult.UNCHANGED)); | 77 | assertThat(propagationAdapter.propagate(), is(PropagationResult.UNCHANGED)); |
81 | } | 78 | } |
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java index 779e18ab..ecd5d39c 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java | |||
@@ -122,30 +122,30 @@ class ModelGenerationTest { | |||
122 | 122 | ||
123 | abstract class Vertex { | 123 | abstract class Vertex { |
124 | container Region[0..1] region opposite vertices | 124 | container Region[0..1] region opposite vertices |
125 | Transition[] outgoingTransition opposite source | 125 | contains Transition[] outgoingTransition opposite source |
126 | Transition[] incomingTransition opposite target | 126 | Transition[] incomingTransition opposite target |
127 | } | 127 | } |
128 | 128 | ||
129 | class Transition { | 129 | class Transition { |
130 | Vertex source opposite outgoingTransition | 130 | container Vertex[0..1] source opposite outgoingTransition |
131 | Vertex target opposite incomingTransition | 131 | Vertex target opposite incomingTransition |
132 | } | 132 | } |
133 | 133 | ||
134 | abstract class Pseudostate extends Vertex {} | 134 | abstract class Pseudostate extends Vertex. |
135 | 135 | ||
136 | abstract class RegularState extends Vertex {} | 136 | abstract class RegularState extends Vertex. |
137 | 137 | ||
138 | class Entry extends Pseudostate {} | 138 | class Entry extends Pseudostate. |
139 | 139 | ||
140 | class Exit extends Pseudostate {} | 140 | class Exit extends Pseudostate. |
141 | 141 | ||
142 | class Choice extends Pseudostate {} | 142 | class Choice extends Pseudostate. |
143 | 143 | ||
144 | class FinalState extends RegularState {} | 144 | class FinalState extends RegularState. |
145 | 145 | ||
146 | class State extends RegularState, CompositeElement {} | 146 | class State extends RegularState, CompositeElement. |
147 | 147 | ||
148 | class Statechart extends CompositeElement {} | 148 | class Statechart extends CompositeElement. |
149 | 149 | ||
150 | // Constraints | 150 | // Constraints |
151 | 151 | ||
@@ -209,7 +209,74 @@ class ModelGenerationTest { | |||
209 | error choiceHasNoIncoming(Choice c) <-> | 209 | error choiceHasNoIncoming(Choice c) <-> |
210 | !target(_, c). | 210 | !target(_, c). |
211 | 211 | ||
212 | scope node = 50..60, Statechart = 1. | 212 | scope node = 50..60, Region = 5..10, Statechart = 1. |
213 | """); | ||
214 | assertThat(parsedProblem.errors(), empty()); | ||
215 | var problem = parsedProblem.problem(); | ||
216 | |||
217 | var storeBuilder = ModelStore.builder() | ||
218 | .with(ViatraModelQueryAdapter.builder()) | ||
219 | // .with(ModelVisualizerAdapter.builder() | ||
220 | // .withOutputPath("test_output") | ||
221 | // .withFormat(FileFormat.DOT) | ||
222 | // .withFormat(FileFormat.SVG) | ||
223 | // .saveStates() | ||
224 | // .saveDesignSpace()) | ||
225 | .with(PropagationAdapter.builder()) | ||
226 | .with(StateCoderAdapter.builder()) | ||
227 | .with(DesignSpaceExplorationAdapter.builder()) | ||
228 | .with(ReasoningAdapter.builder()); | ||
229 | |||
230 | var modelSeed = modelInitializer.createModel(problem, storeBuilder); | ||
231 | |||
232 | var store = storeBuilder.build(); | ||
233 | |||
234 | var initialModel = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); | ||
235 | |||
236 | var initialVersion = initialModel.commit(); | ||
237 | |||
238 | var bestFirst = new BestFirstStoreManager(store, 1); | ||
239 | bestFirst.startExploration(initialVersion); | ||
240 | var resultStore = bestFirst.getSolutionStore(); | ||
241 | System.out.println("states size: " + resultStore.getSolutions().size()); | ||
242 | |||
243 | var model = store.createModelForState(resultStore.getSolutions().get(0).version()); | ||
244 | var interpretation = model.getAdapter(ReasoningAdapter.class) | ||
245 | .getPartialInterpretation(Concreteness.CANDIDATE, ReasoningAdapter.EXISTS_SYMBOL); | ||
246 | var cursor = interpretation.getAll(); | ||
247 | int max = -1; | ||
248 | var types = new LinkedHashMap<PartialRelation, Integer>(); | ||
249 | var typeInterpretation = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL); | ||
250 | while (cursor.move()) { | ||
251 | max = Math.max(max, cursor.getKey().get(0)); | ||
252 | var type = typeInterpretation.get(cursor.getKey()); | ||
253 | if (type != null) { | ||
254 | types.compute(type.candidateType(), (ignoredKey, oldValue) -> oldValue == null ? 1 : oldValue + 1); | ||
255 | } | ||
256 | } | ||
257 | System.out.println("Model size: " + (max + 1)); | ||
258 | System.out.println(types); | ||
259 | // initialModel.getAdapter(ModelVisualizerAdapter.class).visualize(bestFirst.getVisualizationStore()); | ||
260 | } | ||
261 | |||
262 | @Test | ||
263 | void filesystemTest() { | ||
264 | var parsedProblem = parseHelper.parse(""" | ||
265 | class Filesystem { | ||
266 | contains Entry root | ||
267 | } | ||
268 | |||
269 | abstract class Entry. | ||
270 | |||
271 | class Directory extends Entry { | ||
272 | contains Entry[] entries | ||
273 | } | ||
274 | |||
275 | class File extends Entry. | ||
276 | |||
277 | Filesystem(fs). | ||
278 | |||
279 | scope Filesystem += 0, Entry = 100. | ||
213 | """); | 280 | """); |
214 | assertThat(parsedProblem.errors(), empty()); | 281 | assertThat(parsedProblem.errors(), empty()); |
215 | var problem = parsedProblem.problem(); | 282 | var problem = parsedProblem.problem(); |
@@ -265,7 +332,7 @@ class ModelGenerationTest { | |||
265 | var test = injector.getInstance(ModelGenerationTest.class); | 332 | var test = injector.getInstance(ModelGenerationTest.class); |
266 | try { | 333 | try { |
267 | test.statechartTest(); | 334 | test.statechartTest(); |
268 | } catch (AssertionError e) { | 335 | } catch (Throwable e) { |
269 | e.printStackTrace(); | 336 | e.printStackTrace(); |
270 | } | 337 | } |
271 | } | 338 | } |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java index 5df861a6..d064ff2c 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java | |||
@@ -5,10 +5,10 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.query.viatra.internal.pquery; | 6 | package tools.refinery.store.query.viatra.internal.pquery; |
7 | 7 | ||
8 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
9 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
10 | import tools.refinery.store.query.term.Term; | 8 | import tools.refinery.store.query.term.Term; |
11 | import tools.refinery.store.query.term.Variable; | 9 | import tools.refinery.store.query.term.Variable; |
10 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
11 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
12 | 12 | ||
13 | import java.util.stream.Collectors; | 13 | import java.util.stream.Collectors; |
14 | 14 | ||
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java index 9dc739f1..5577faa3 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java | |||
@@ -5,11 +5,11 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.query.viatra.internal.update; | 6 | package tools.refinery.store.query.viatra.internal.update; |
7 | 7 | ||
8 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
9 | import tools.refinery.store.model.Interpretation; | 8 | import tools.refinery.store.model.Interpretation; |
10 | import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; | 9 | import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; |
11 | import tools.refinery.store.query.view.SymbolView; | 10 | import tools.refinery.store.query.view.SymbolView; |
12 | import tools.refinery.store.tuple.Tuple; | 11 | import tools.refinery.store.tuple.Tuple; |
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
13 | 13 | ||
14 | import java.util.Arrays; | 14 | import java.util.Arrays; |
15 | 15 | ||
@@ -27,18 +27,19 @@ public class TupleChangingViewUpdateListener<T> extends SymbolViewUpdateListener | |||
27 | boolean fromPresent = view.filter(key, fromValue); | 27 | boolean fromPresent = view.filter(key, fromValue); |
28 | boolean toPresent = view.filter(key, toValue); | 28 | boolean toPresent = view.filter(key, toValue); |
29 | if (fromPresent) { | 29 | if (fromPresent) { |
30 | var fromArray = view.forwardMap(key, fromValue); | ||
30 | if (toPresent) { // value change | 31 | if (toPresent) { // value change |
31 | var fromArray = view.forwardMap(key, fromValue); | ||
32 | var toArray = view.forwardMap(key, toValue); | 32 | var toArray = view.forwardMap(key, toValue); |
33 | if (!Arrays.equals(fromArray, toArray)) { | 33 | if (!Arrays.equals(fromArray, toArray)) { |
34 | processUpdate(Tuples.flatTupleOf(fromArray), false); | 34 | processUpdate(Tuples.flatTupleOf(fromArray), false); |
35 | processUpdate(Tuples.flatTupleOf(toArray), true); | 35 | processUpdate(Tuples.flatTupleOf(toArray), true); |
36 | } | 36 | } |
37 | } else { // fromValue disappears | 37 | } else { // fromValue disappears |
38 | processUpdate(Tuples.flatTupleOf(view.forwardMap(key, fromValue)), false); | 38 | processUpdate(Tuples.flatTupleOf(fromArray), false); |
39 | } | 39 | } |
40 | } else if (toPresent) { // toValue appears | 40 | } else if (toPresent) { // toValue appears |
41 | processUpdate(Tuples.flatTupleOf(view.forwardMap(key, toValue)), true); | 41 | var toArray = view.forwardMap(key, toValue); |
42 | processUpdate(Tuples.flatTupleOf(toArray), true); | ||
42 | } | 43 | } |
43 | } | 44 | } |
44 | } | 45 | } |
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java index 4c603a47..0f402b49 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java | |||
@@ -15,6 +15,7 @@ import tools.refinery.viatra.runtime.api.scope.QueryScope; | |||
15 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | 15 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; |
16 | 16 | ||
17 | import java.util.Set; | 17 | import java.util.Set; |
18 | import java.util.function.Supplier; | ||
18 | import java.util.stream.Collectors; | 19 | import java.util.stream.Collectors; |
19 | 20 | ||
20 | /** | 21 | /** |
@@ -147,4 +148,6 @@ public abstract class ViatraQueryEngine { | |||
147 | public abstract QueryScope getScope(); | 148 | public abstract QueryScope getScope(); |
148 | 149 | ||
149 | public abstract void flushChanges(); | 150 | public abstract void flushChanges(); |
151 | |||
152 | public abstract <T> T withFlushingChanges(Supplier<T> supplier); | ||
150 | } | 153 | } |
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java index 47a51629..5317a79e 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java | |||
@@ -41,6 +41,7 @@ import java.lang.ref.WeakReference; | |||
41 | import java.lang.reflect.InvocationTargetException; | 41 | import java.lang.reflect.InvocationTargetException; |
42 | import java.util.*; | 42 | import java.util.*; |
43 | import java.util.concurrent.Callable; | 43 | import java.util.concurrent.Callable; |
44 | import java.util.function.Supplier; | ||
44 | import java.util.stream.Collectors; | 45 | import java.util.stream.Collectors; |
45 | 46 | ||
46 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | 47 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; |
@@ -164,9 +165,27 @@ public final class ViatraQueryEngineImpl extends AdvancedViatraQueryEngine | |||
164 | } | 165 | } |
165 | delayMessageDelivery = false; | 166 | delayMessageDelivery = false; |
166 | try { | 167 | try { |
167 | for (IQueryBackend backend : this.queryBackends.values()) { | 168 | flushAllBackends(); |
168 | backend.flushUpdates(); | 169 | } finally { |
169 | } | 170 | delayMessageDelivery = true; |
171 | } | ||
172 | } | ||
173 | |||
174 | private void flushAllBackends() { | ||
175 | for (IQueryBackend backend : this.queryBackends.values()) { | ||
176 | backend.flushUpdates(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | @Override | ||
181 | public <T> T withFlushingChanges(Supplier<T> callback) { | ||
182 | if (!delayMessageDelivery) { | ||
183 | return callback.get(); | ||
184 | } | ||
185 | delayMessageDelivery = false; | ||
186 | try { | ||
187 | flushAllBackends(); | ||
188 | return callback.get(); | ||
170 | } finally { | 189 | } finally { |
171 | delayMessageDelivery = true; | 190 | delayMessageDelivery = true; |
172 | } | 191 | } |
@@ -186,18 +205,20 @@ public final class ViatraQueryEngineImpl extends AdvancedViatraQueryEngine | |||
186 | @Override | 205 | @Override |
187 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | 206 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( |
188 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalEvaluationHints) { | 207 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalEvaluationHints) { |
189 | IMatcherCapability capability = getRequestedCapability(querySpecification, optionalEvaluationHints); | 208 | return withFlushingChanges(() -> { |
190 | Matcher matcher = doGetExistingMatcher(querySpecification, capability); | 209 | IMatcherCapability capability = getRequestedCapability(querySpecification, optionalEvaluationHints); |
191 | if (matcher != null) { | 210 | Matcher matcher = doGetExistingMatcher(querySpecification, capability); |
192 | return matcher; | 211 | if (matcher != null) { |
193 | } | 212 | return matcher; |
194 | matcher = querySpecification.instantiate(); | 213 | } |
195 | 214 | matcher = querySpecification.instantiate(); | |
196 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; | 215 | |
197 | ((QueryResultWrapper) baseMatcher).setBackend(this, | 216 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; |
198 | getResultProvider(querySpecification, optionalEvaluationHints), capability); | 217 | ((QueryResultWrapper) baseMatcher).setBackend(this, |
199 | internalRegisterMatcher(querySpecification, baseMatcher); | 218 | getResultProvider(querySpecification, optionalEvaluationHints), capability); |
200 | return matcher; | 219 | internalRegisterMatcher(querySpecification, baseMatcher); |
220 | return matcher; | ||
221 | }); | ||
201 | } | 222 | } |
202 | 223 | ||
203 | @Override | 224 | @Override |