From 4d365b54dad8d066bba2a2b1a05092b4802b9970 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 11 Sep 2023 19:22:26 +0200 Subject: feat: cancellation token for ModelStore --- .../semantics/model/ModelGenerationTest.java | 2 +- .../language/web/semantics/CancellableSeed.java | 99 ---------------------- .../language/web/semantics/SemanticsWorker.java | 9 +- .../propagation/impl/PropagationAdapterImpl.java | 3 + .../impl/rule/BoundPropagationRule.java | 3 + .../store/dse/strategy/BestFirstExplorer.java | 9 +- .../store/dse/transition/actions/BoundAction.java | 1 + .../DesignSpaceExplorationAdapterImpl.java | 4 + .../query/viatra/ViatraModelQueryBuilder.java | 3 - .../internal/ViatraModelQueryBuilderImpl.java | 10 +-- .../reasoning/scope/BoundScopePropagator.java | 7 ++ .../multiobject/MultiObjectInitializer.java | 16 ++-- .../typehierarchy/TypeHierarchyInitializer.java | 7 +- .../java/tools/refinery/store/model/Model.java | 4 +- .../tools/refinery/store/model/ModelStore.java | 2 + .../refinery/store/model/ModelStoreBuilder.java | 3 + .../refinery/store/model/internal/ModelImpl.java | 16 +++- .../model/internal/ModelStoreBuilderImpl.java | 21 ++++- .../store/model/internal/ModelStoreImpl.java | 15 +++- .../model/internal/VersionedInterpretation.java | 6 +- .../statecoding/StateCodeCalculatorFactory.java | 3 +- .../internal/StateCoderStoreAdapterImpl.java | 2 +- .../AbstractNeighbourhoodCalculator.java | 6 +- .../neighbourhood/LazyNeighbourhoodCalculator.java | 6 +- .../neighbourhood/NeighbourhoodCalculator.java | 8 +- .../refinery/store/util/CancellationToken.java | 13 +++ .../store/statecoding/EquivalenceTest.java | 3 +- .../store/statecoding/StateCoderBuildTest.java | 3 +- 28 files changed, 131 insertions(+), 153 deletions(-) delete mode 100644 subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/CancellableSeed.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java 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 ecd5d39c..d756099c 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 @@ -209,7 +209,7 @@ class ModelGenerationTest { error choiceHasNoIncoming(Choice c) <-> !target(_, c). - scope node = 50..60, Region = 5..10, Statechart = 1. + scope node = 200..210, Region = 10..*, Choice = 1..*, Statechart = 1. """); assertThat(parsedProblem.errors(), empty()); var problem = parsedProblem.problem(); diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/CancellableSeed.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/CancellableSeed.java deleted file mode 100644 index aa14f39d..00000000 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/CancellableSeed.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.language.web.semantics; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.reasoning.representation.PartialSymbol; -import tools.refinery.store.reasoning.seed.ModelSeed; -import tools.refinery.store.reasoning.seed.Seed; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.viatra.runtime.CancellationToken; - -import java.util.Set; - -class CancellableSeed implements Seed { - private final CancellationToken cancellationToken; - private final Seed seed; - - private CancellableSeed(CancellationToken cancellationToken, Seed seed) { - this.cancellationToken = cancellationToken; - this.seed = seed; - } - - @Override - public int arity() { - return seed.arity(); - } - - @Override - public Class valueType() { - return seed.valueType(); - } - - @Override - public T reducedValue() { - return seed.reducedValue(); - } - - @Override - public T get(Tuple key) { - return seed.get(key); - } - - @Override - public Cursor getCursor(T defaultValue, int nodeCount) { - return new CancellableCursor<>(cancellationToken, seed.getCursor(defaultValue, nodeCount)); - } - - public static ModelSeed wrap(CancellationToken cancellationToken, ModelSeed modelSeed) { - var builder = ModelSeed.builder(modelSeed.getNodeCount()); - for (var partialSymbol : modelSeed.getSeededSymbols()) { - wrap(cancellationToken, (PartialSymbol) partialSymbol, modelSeed, builder); - } - return builder.build(); - } - - private static void wrap(CancellationToken cancellationToken, PartialSymbol partialSymbol, - ModelSeed originalModelSeed, ModelSeed.Builder builder) { - var originalSeed = originalModelSeed.getSeed(partialSymbol); - builder.seed(partialSymbol, new CancellableSeed<>(cancellationToken, originalSeed)); - } - - private record CancellableCursor(CancellationToken cancellationToken, Cursor cursor) - implements Cursor { - @Override - public Tuple getKey() { - return cursor.getKey(); - } - - @Override - public T getValue() { - return cursor.getValue(); - } - - @Override - public boolean isTerminated() { - return cursor.isTerminated(); - } - - @Override - public boolean move() { - cancellationToken.checkCancelled(); - return cursor.move(); - } - - @Override - public boolean isDirty() { - return cursor.isDirty(); - } - - @Override - public Set getDependingMaps() { - return cursor.getDependingMaps(); - } - } -} diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java index c745d86e..33b1c4fb 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java @@ -34,7 +34,7 @@ import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.translator.TranslationException; import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; import tools.refinery.store.tuple.Tuple; -import tools.refinery.viatra.runtime.CancellationToken; +import tools.refinery.store.util.CancellationToken; import java.util.ArrayList; import java.util.TreeMap; @@ -74,8 +74,8 @@ class SemanticsWorker implements Callable { @Override public SemanticsResult call() { var builder = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder() - .cancellationToken(cancellationToken)) + .cancellationToken(cancellationToken) + .with(ViatraModelQueryAdapter.builder()) .with(PropagationAdapter.builder()) .with(ReasoningAdapter.builder() .requiredInterpretations(Concreteness.PARTIAL)); @@ -91,8 +91,7 @@ class SemanticsWorker implements Callable { cancellationToken.checkCancelled(); var store = builder.build(); cancellationToken.checkCancelled(); - var cancellableModelSeed = CancellableSeed.wrap(cancellationToken, modelSeed); - var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(cancellableModelSeed); + var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); cancellationToken.checkCancelled(); var partialInterpretation = getPartialInterpretation(initializer, model); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java index 586a8d7a..fdd19217 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java @@ -31,6 +31,7 @@ class PropagationAdapterImpl implements PropagationAdapter { PropagationResult result = PropagationResult.UNCHANGED; PropagationResult lastResult; do { + model.checkCancelled(); lastResult = propagateOne(); result = result.andThen(lastResult); } while (lastResult.isChanged()); @@ -40,6 +41,7 @@ class PropagationAdapterImpl implements PropagationAdapter { private PropagationResult propagateOne() { PropagationResult result = PropagationResult.UNCHANGED; for (int i = 0; i < boundPropagators.length; i++) { + model.checkCancelled(); var lastResult = propagateUntilFixedPoint(i); result = result.andThen(lastResult); if (result.isRejected()) { @@ -54,6 +56,7 @@ class PropagationAdapterImpl implements PropagationAdapter { PropagationResult result = PropagationResult.UNCHANGED; PropagationResult lastResult; do { + model.checkCancelled(); lastResult = propagator.propagateOne(); result = result.andThen(lastResult); } while (lastResult.isChanged()); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java index 6e6a78d2..a70292ad 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java @@ -13,10 +13,12 @@ import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.resultset.ResultSet; class BoundPropagationRule { + private final Model model; private final ResultSet resultSet; private final BoundAction action; public BoundPropagationRule(Model model, Rule rule) { + this.model = model; resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(rule.getPrecondition()); action = rule.createAction(model); } @@ -27,6 +29,7 @@ class BoundPropagationRule { } var cursor = resultSet.getAll(); while (cursor.move()) { + model.checkCancelled(); var result = action.fire(cursor.getKey()); if (!result) { return PropagationResult.REJECTED; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java index 4a75a3a6..5e2f8fa9 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java @@ -19,14 +19,9 @@ public class BestFirstExplorer extends BestFirstWorker { this.random = new Random(id); } - private boolean interrupted = false; - - public void interrupt() { - this.interrupted = true; - } - private boolean shouldRun() { - return !interrupted && !hasEnoughSolution(); + model.checkCancelled(); + return !hasEnoughSolution(); } public void explore() { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java index ed2ff33d..4da609fa 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java @@ -23,6 +23,7 @@ public class BoundAction { } public boolean fire(Tuple activation) { + model.checkCancelled(); if (this.activation != null) { throw new IllegalStateException("Reentrant firing is not allowed"); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java index e1a29d40..23325a1f 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java @@ -56,6 +56,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public boolean checkAccept() { for (var accept : this.accepts) { + model.checkCancelled(); if (!accept.isSatisfied()) { return false; } @@ -66,6 +67,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public boolean checkExclude() { for (var exclude : this.excludes) { + model.checkCancelled(); if (exclude.isSatisfied()) { return true; } @@ -75,6 +77,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public ObjectiveValue getObjectiveValue() { + model.checkCancelled(); if (objectives.size() == 1) { return ObjectiveValue.of(objectives.get(0).getValue()); } else if (objectives.size() == 2) { @@ -82,6 +85,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } else { double[] res = new double[objectives.size()]; for (int i = 0; i < objectives.size(); i++) { + model.checkCancelled(); res[i] = objectives.get(i).getValue(); } return ObjectiveValue.of(res); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java index 6b3be115..d19c3bb4 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java @@ -10,7 +10,6 @@ import tools.refinery.store.query.ModelQueryBuilder; import tools.refinery.store.query.dnf.AnyQuery; import tools.refinery.store.query.dnf.Dnf; import tools.refinery.store.query.rewriter.DnfRewriter; -import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; @@ -30,8 +29,6 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); - ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken); - @Override default ViatraModelQueryBuilder queries(AnyQuery... queries) { ModelQueryBuilder.super.queries(queries); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java index bb0630f3..c68152e3 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java @@ -17,7 +17,6 @@ import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; -import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.api.IQuerySpecification; import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; @@ -36,7 +35,6 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder queries = new LinkedHashSet<>(); @@ -86,12 +84,6 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder queries) { checkNotConfigured(); @@ -144,7 +136,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder countInterpretation; private final MPSolver solver; @@ -32,6 +33,7 @@ class BoundScopePropagator implements BoundPropagator { private boolean changed = true; public BoundScopePropagator(Model model, ScopePropagator storeAdapter) { + this.model = model; queryEngine = model.getAdapter(ModelQueryAdapter.class); countInterpretation = model.getInterpretation(storeAdapter.getCountSymbol()); solver = MPSolver.createSolver("GLOP"); @@ -41,6 +43,7 @@ class BoundScopePropagator implements BoundPropagator { var propagatorFactories = storeAdapter.getTypeScopePropagatorFactories(); propagators = new TypeScopePropagator[propagatorFactories.size()]; for (int i = 0; i < propagators.length; i++) { + model.checkCancelled(); propagators[i] = propagatorFactories.get(i).createPropagator(this); } } @@ -145,6 +148,7 @@ class BoundScopePropagator implements BoundPropagator { } changed = false; for (var propagator : propagators) { + model.checkCancelled(); propagator.updateBounds(); } var result = PropagationResult.UNCHANGED; @@ -167,6 +171,7 @@ class BoundScopePropagator implements BoundPropagator { } private PropagationResult checkEmptiness() { + model.checkCancelled(); var emptinessCheckingResult = solver.solve(); return switch (emptinessCheckingResult) { case OPTIMAL, UNBOUNDED -> PropagationResult.UNCHANGED; @@ -178,6 +183,7 @@ class BoundScopePropagator implements BoundPropagator { private PropagationResult propagateNode(int nodeId, MPVariable variable) { objective.setCoefficient(variable, 1); try { + model.checkCancelled(); objective.setMinimization(); var minimizationResult = solver.solve(); int lowerBound; @@ -191,6 +197,7 @@ class BoundScopePropagator implements BoundPropagator { .formatted(variable, minimizationResult)); } + model.checkCancelled(); objective.setMaximization(); var maximizationResult = solver.solve(); UpperCardinality upperBound; diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java index f11ab46b..89918155 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java @@ -30,9 +30,9 @@ class MultiObjectInitializer implements PartialModelInitializer { @Override public void initialize(Model model, ModelSeed modelSeed) { - var intervals = initializeIntervals(modelSeed); - initializeExists(intervals, modelSeed); - initializeEquals(intervals, modelSeed); + var intervals = initializeIntervals(model, modelSeed); + initializeExists(intervals, model, modelSeed); + initializeEquals(intervals, model, modelSeed); var countInterpretation = model.getInterpretation(countSymbol); var uniqueTable = new HashMap(); for (int i = 0; i < intervals.length; i++) { @@ -47,12 +47,13 @@ class MultiObjectInitializer implements PartialModelInitializer { } @NotNull - private CardinalityInterval[] initializeIntervals(ModelSeed modelSeed) { + private CardinalityInterval[] initializeIntervals(Model model, ModelSeed modelSeed) { var intervals = new CardinalityInterval[modelSeed.getNodeCount()]; if (modelSeed.containsSeed(MultiObjectTranslator.COUNT_SYMBOL)) { Arrays.fill(intervals, CardinalityIntervals.ONE); var cursor = modelSeed.getCursor(MultiObjectTranslator.COUNT_SYMBOL, CardinalityIntervals.ONE); while (cursor.move()) { + model.checkCancelled(); int i = cursor.getKey().get(0); checkNodeId(intervals, i); intervals[i] = cursor.getValue(); @@ -70,12 +71,13 @@ class MultiObjectInitializer implements PartialModelInitializer { return intervals; } - private void initializeExists(CardinalityInterval[] intervals, ModelSeed modelSeed) { + private void initializeExists(CardinalityInterval[] intervals, Model model, ModelSeed modelSeed) { if (!modelSeed.containsSeed(ReasoningAdapter.EXISTS_SYMBOL)) { return; } var cursor = modelSeed.getCursor(ReasoningAdapter.EXISTS_SYMBOL, TruthValue.UNKNOWN); while (cursor.move()) { + model.checkCancelled(); int i = cursor.getKey().get(0); checkNodeId(intervals, i); switch (cursor.getValue()) { @@ -89,13 +91,14 @@ class MultiObjectInitializer implements PartialModelInitializer { } } - private void initializeEquals(CardinalityInterval[] intervals, ModelSeed modelSeed) { + private void initializeEquals(CardinalityInterval[] intervals, Model model, ModelSeed modelSeed) { if (!modelSeed.containsSeed(ReasoningAdapter.EQUALS_SYMBOL)) { return; } var seed = modelSeed.getSeed(ReasoningAdapter.EQUALS_SYMBOL); var cursor = seed.getCursor(TruthValue.FALSE, modelSeed.getNodeCount()); while (cursor.move()) { + model.checkCancelled(); var key = cursor.getKey(); int i = key.get(0); int otherIndex = key.get(1); @@ -116,6 +119,7 @@ class MultiObjectInitializer implements PartialModelInitializer { } } for (int i = 0; i < intervals.length; i++) { + model.checkCancelled(); if (seed.get(Tuple.of(i, i)) == TruthValue.FALSE) { throw new TranslationException(ReasoningAdapter.EQUALS_SYMBOL, "Inconsistent equality for node " + i); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyInitializer.java index c74f1e78..233e43f0 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyInitializer.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyInitializer.java @@ -31,20 +31,23 @@ public class TypeHierarchyInitializer implements PartialModelInitializer { var inferredTypes = new InferredType[modelSeed.getNodeCount()]; Arrays.fill(inferredTypes, typeHierarchy.getUnknownType()); for (var type : typeHierarchy.getAllTypes()) { - initializeType(type, inferredTypes, modelSeed); + model.checkCancelled(); + initializeType(type, inferredTypes, model, modelSeed); } var typeInterpretation = model.getInterpretation(typeSymbol); var uniqueTable = new HashMap(); for (int i = 0; i < inferredTypes.length; i++) { + model.checkCancelled(); var uniqueType = uniqueTable.computeIfAbsent(inferredTypes[i], Function.identity()); typeInterpretation.put(Tuple.of(i), uniqueType); } } - private void initializeType(PartialRelation type, InferredType[] inferredTypes, ModelSeed modelSeed) { + private void initializeType(PartialRelation type, InferredType[] inferredTypes, Model model, ModelSeed modelSeed) { var cursor = modelSeed.getCursor(type, TruthValue.UNKNOWN); var analysisResult = typeHierarchy.getAnalysisResult(type); while (cursor.move()) { + model.checkCancelled(); var i = cursor.getKey().get(0); checkNodeId(inferredTypes, i); var value = cursor.getValue(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java index e2ab72e7..c4ce5207 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java @@ -8,11 +8,9 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.Version; import tools.refinery.store.map.Versioned; -import tools.refinery.store.model.internal.VersionedInterpretation; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; -import java.util.Map; import java.util.Optional; public interface Model extends Versioned { @@ -38,4 +36,6 @@ public interface Model extends Versioned { void addListener(ModelListener listener); void removeListener(ModelListener listener); + + void checkCancelled(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java index 89382b3a..61abf126 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java @@ -26,6 +26,8 @@ public interface ModelStore { T getAdapter(Class adapterType); + void checkCancelled(); + static ModelStoreBuilder builder() { return new ModelStoreBuilderImpl(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java index 8f652762..9b2b38c3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java @@ -8,12 +8,15 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapterBuilder; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.util.CancellationToken; import java.util.Collection; import java.util.List; import java.util.Optional; public interface ModelStoreBuilder { + ModelStoreBuilder cancellationToken(CancellationToken cancellationToken); + default ModelStoreBuilder symbols(AnySymbol... symbols) { return symbols(List.of(symbols)); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java index 2b12d5a6..d11e431b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java @@ -13,23 +13,26 @@ import tools.refinery.store.model.*; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelImpl implements Model { - private final ModelStore store; + private final ModelStoreImpl store; private Version state; private LinkedHashMap> interpretations; private final List adapters; private final List listeners = new ArrayList<>(); + private final CancellationToken cancellationToken; private boolean uncommittedChanges; private ModelAction pendingAction = ModelAction.NONE; private Version restoringToState = null; - ModelImpl(ModelStore store, Version state, int adapterCount) { + ModelImpl(ModelStoreImpl store, Version state, int adapterCount) { this.store = store; this.state = state; adapters = new ArrayList<>(adapterCount); + cancellationToken = store.getCancellationToken(); } void setInterpretations(LinkedHashMap> interpretations) { @@ -88,6 +91,7 @@ public class ModelImpl implements Model { @Override public Version commit() { + checkCancelled(); if (hasPendingAction()) { throw pendingActionError("commit"); } @@ -106,6 +110,7 @@ public class ModelImpl implements Model { Version[] interpretationVersions = new Version[interpretations.size()]; int j = 0; for (var interpretationEntry : interpretations.entrySet()) { + checkCancelled(); interpretationVersions[j++] = interpretationEntry.getValue().commit(); } ModelVersion modelVersion = new ModelVersion(interpretationVersions); @@ -125,6 +130,7 @@ public class ModelImpl implements Model { @Override public void restore(Version version) { + checkCancelled(); if (hasPendingAction()) { throw pendingActionError("restore to %s".formatted(version)); } @@ -140,6 +146,7 @@ public class ModelImpl implements Model { } int j = 0; for (var interpretation : interpretations.values()) { + checkCancelled(); interpretation.restore(ModelVersion.getInternalVersion(version, j++)); } @@ -187,4 +194,9 @@ public class ModelImpl implements Model { public void removeListener(ModelListener listener) { listeners.remove(listener); } + + @Override + public void checkCancelled() { + cancellationToken.checkCancelled(); + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java index 2dde7a4c..53ecde5e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java @@ -16,14 +16,28 @@ import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelStoreBuilderImpl implements ModelStoreBuilder { + private CancellationToken cancellationToken; private final LinkedHashSet allSymbols = new LinkedHashSet<>(); private final LinkedHashMap, List> equivalenceClasses = new LinkedHashMap<>(); private final List adapters = new ArrayList<>(); + @Override + public ModelStoreBuilder cancellationToken(CancellationToken cancellationToken) { + if (this.cancellationToken != null) { + throw new IllegalStateException("Cancellation token was already set"); + } + if (cancellationToken == null) { + throw new IllegalStateException("Cancellation token must not be null"); + } + this.cancellationToken = cancellationToken; + return this; + } + @Override public ModelStoreBuilder symbol(Symbol symbol) { if (!allSymbols.add(symbol)) { @@ -75,7 +89,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { for (var entry : equivalenceClasses.entrySet()) { createStores(stores, entry.getKey(), entry.getValue()); } - var modelStore = new ModelStoreImpl(stores, adapters.size()); + var modelStore = new ModelStoreImpl(stores, adapters.size(), cancellationToken == null ? + CancellationToken.NONE : cancellationToken); for (var adapterBuilder : adapters) { var storeAdapter = adapterBuilder.build(modelStore); modelStore.addAdapter(storeAdapter); @@ -86,8 +101,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { private void createStores(Map> stores, SymbolEquivalenceClass equivalenceClass, List symbols) { int size = symbols.size(); - VersionedMapStoreFactory mapFactory = VersionedMapStore - .builder() + VersionedMapStoreFactory mapFactory = VersionedMapStore + .builder() .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) .defaultValue(equivalenceClass.defaultValue()) .build(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java index a320a618..fd4cc160 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java @@ -14,16 +14,20 @@ import tools.refinery.store.model.ModelDiffCursor; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelStoreImpl implements ModelStore { private final LinkedHashMap> stores; private final List adapters; + private final CancellationToken cancellationToken; - ModelStoreImpl(LinkedHashMap> stores, int adapterCount) { + ModelStoreImpl(LinkedHashMap> stores, int adapterCount, + CancellationToken cancellationToken) { this.stores = stores; adapters = new ArrayList<>(adapterCount); + this.cancellationToken = cancellationToken; } @Override @@ -100,4 +104,13 @@ public class ModelStoreImpl implements ModelStore { void addAdapter(ModelStoreAdapter adapter) { adapters.add(adapter); } + + @Override + public void checkCancelled() { + cancellationToken.checkCancelled(); + } + + CancellationToken getCancellationToken() { + return cancellationToken; + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java index 71df3962..dcf0ad08 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java @@ -75,6 +75,7 @@ public abstract class VersionedInterpretation implements Interpretation { @Override public T put(Tuple key, T value) { checkKey(key); + model.checkCancelled(); model.markAsChanged(); var oldValue = map.put(key, value); valueChanged(key, oldValue, value, false); @@ -83,15 +84,12 @@ public abstract class VersionedInterpretation implements Interpretation { @Override public void putAll(Cursor cursor) { - if (listeners.isEmpty()) { - map.putAll(cursor); - return; - } model.markAsChanged(); if (cursor.getDependingMaps().contains(map)) { List keys = new ArrayList<>(); List values = new ArrayList<>(); while (cursor.move()) { + model.checkCancelled(); keys.add(cursor.getKey()); values.add(cursor.getValue()); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java index 04e17a13..809205e4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java @@ -7,9 +7,10 @@ package tools.refinery.store.statecoding; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import java.util.List; public interface StateCodeCalculatorFactory { - StateCodeCalculator create(List> interpretations, IntSet individuals); + StateCodeCalculator create(Model model, List> interpretations, IntSet individuals); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java index 89586bfb..2cb6f3c1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java @@ -68,7 +68,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { @Override public StateCoderAdapter createModelAdapter(Model model) { var interpretations = symbols.stream().map(model::getInterpretation).toList(); - var coder = codeCalculatorFactory.create(interpretations, individuals); + var coder = codeCalculatorFactory.create(model, interpretations, individuals); return new StateCoderAdapterImpl(this, coder, model); } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java index 5d390da2..4cef6786 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java @@ -10,6 +10,7 @@ import org.eclipse.collections.api.map.primitive.MutableIntLongMap; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.model.AnyInterpretation; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple0; @@ -17,13 +18,16 @@ import tools.refinery.store.tuple.Tuple0; import java.util.*; public abstract class AbstractNeighbourhoodCalculator { + protected final Model model; protected final List nullImpactValues; protected final LinkedHashMap impactValues; protected final MutableIntLongMap individualHashValues = IntLongMaps.mutable.empty(); protected static final long PRIME = 31; - protected AbstractNeighbourhoodCalculator(List interpretations, IntSet individuals) { + protected AbstractNeighbourhoodCalculator(Model model, List interpretations, + IntSet individuals) { + this.model = model; this.nullImpactValues = new ArrayList<>(); this.impactValues = new LinkedHashMap<>(); // Random isn't used for cryptographical purposes but just to assign distinguishable identifiers to symbols. diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java index c188a839..04335141 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java @@ -12,6 +12,7 @@ import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.AnyInterpretation; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.tuple.Tuple; @@ -19,8 +20,9 @@ import tools.refinery.store.tuple.Tuple; import java.util.List; public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { - public LazyNeighbourhoodCalculator(List interpretations, IntSet individuals) { - super(interpretations, individuals); + public LazyNeighbourhoodCalculator(Model model, List interpretations, + IntSet individuals) { + super(model, interpretations, individuals); } public StateCoderResult calculateCodes() { diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java index 1442c915..5e6de53b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java @@ -8,6 +8,7 @@ package tools.refinery.store.statecoding.neighbourhood; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderResult; @@ -21,17 +22,19 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp private ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); private ObjectCodeImpl nextObjectCode = new ObjectCodeImpl(); - public NeighbourhoodCalculator(List> interpretations, IntSet individuals) { - super(interpretations, individuals); + public NeighbourhoodCalculator(Model model, List> interpretations, IntSet individuals) { + super(model, interpretations, individuals); } public StateCoderResult calculateCodes() { + model.checkCancelled(); previousObjectCode.clear(); nextObjectCode.clear(); initializeWithIndividuals(previousObjectCode); int rounds = 0; do { + model.checkCancelled(); constructNextObjectCodes(previousObjectCode, nextObjectCode); var tempObjectCode = previousObjectCode; previousObjectCode = nextObjectCode; @@ -60,6 +63,7 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { for (var impactValueEntry : this.impactValues.entrySet()) { + model.checkCancelled(); Interpretation interpretation = (Interpretation) impactValueEntry.getKey(); var cursor = interpretation.getAll(); int arity = interpretation.getSymbol().arity(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java new file mode 100644 index 00000000..be294013 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.util; + +@FunctionalInterface +public interface CancellationToken { + CancellationToken NONE = () -> {}; + + void checkCancelled(); +} diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java index 3c35849e..f2d2f7b5 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java @@ -192,7 +192,8 @@ class EquivalenceTest { ModelStore store = ModelStore.builder() .symbols(person, age, friend, parents, population) .with(StateCoderAdapter.builder() - .stateCodeCalculatorFactory((p1, p2) -> calculator)) + .stateCodeCalculatorFactory((ignoredModel, ignoredInterpretations, ignoredIndividuals) -> + calculator)) .build(); var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java index 0b738005..0928aa8e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java @@ -124,7 +124,8 @@ class StateCoderBuildTest { var store = ModelStore.builder() .symbols(friend) .with(StateCoderAdapter.builder() - .stateCodeCalculatorFactory((interpretations, individuals) -> mock)) + .stateCodeCalculatorFactory((ignoredModel, ignoredInterpretations, ignoredIndividuals) -> + mock)) .build(); var model = store.createEmptyModel(); -- cgit v1.2.3-54-g00ecf