From 9de7ead9602c2e51448c3b6710d13eb5dd2f3b1f Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 07:59:17 +0200 Subject: restructured DSE framework, failing build --- .../store/dse/DesignSpaceExplorationAdapter.java | 68 ----- .../store/dse/DesignSpaceExplorationBuilder.java | 48 ---- .../dse/DesignSpaceExplorationStoreAdapter.java | 29 -- .../java/tools/refinery/store/dse/Strategy.java | 13 - .../refinery/store/dse/internal/Activation.java | 14 - .../DesignSpaceExplorationAdapterImpl.java | 294 --------------------- .../DesignSpaceExplorationBuilderImpl.java | 67 ----- .../DesignSpaceExplorationStoreAdapterImpl.java | 65 ----- .../store/dse/internal/TransformationRule.java | 97 ------- .../dse/modification/ModificationAdapter.java | 24 ++ .../dse/modification/ModificationBuilder.java | 11 + .../dse/modification/ModificationStoreAdapter.java | 11 + .../internal/ModificationAdapterImpl.java | 62 +++++ .../internal/ModificationBuilderImpl.java | 29 ++ .../internal/ModificationStoreAdapterImpl.java | 29 ++ .../AlwaysSatisfiedDummyHardObjective.java | 52 ---- .../AlwaysSatisfiedRandomHardObjective.java | 56 ---- .../store/dse/objectives/BaseObjective.java | 132 --------- .../refinery/store/dse/objectives/Comparators.java | 26 -- .../refinery/store/dse/objectives/Fitness.java | 46 ---- .../refinery/store/dse/objectives/Objective.java | 101 ------- .../dse/objectives/ObjectiveComparatorHelper.java | 60 ----- .../store/dse/strategy/BestFirstExplorer.java | 164 ++++++++++++ .../store/dse/strategy/BestFirstStoreManager.java | 71 +++++ .../store/dse/strategy/BestFirstStrategy.java | 203 -------------- .../store/dse/strategy/BestFirstWorker.java | 113 ++++++++ .../store/dse/strategy/DepthFirstStrategy.java | 92 ------- .../refinery/store/dse/strategy/SubmitResult.java | 14 + .../transition/DesignSpaceExplorationAdapter.java | 31 +++ .../transition/DesignSpaceExplorationBuilder.java | 59 +++++ .../DesignSpaceExplorationStoreAdapter.java | 27 ++ .../store/dse/transition/ObjectiveValue.java | 24 ++ .../store/dse/transition/ObjectiveValues.java | 69 +++++ .../store/dse/transition/Transformation.java | 43 +++ .../store/dse/transition/TransformationRule.java | 63 +++++ .../dse/transition/VersionWithObjectiveValue.java | 11 + .../DesignSpaceExplorationAdapterImpl.java | 90 +++++++ .../DesignSpaceExplorationBuilderImpl.java | 75 ++++++ .../DesignSpaceExplorationStoreAdapterImpl.java | 70 +++++ .../store/dse/transition/objectives/Criterion.java | 15 ++ .../transition/objectives/CriterionCalculator.java | 10 + .../store/dse/transition/objectives/Objective.java | 15 ++ .../transition/objectives/ObjectiveCalculator.java | 10 + .../dse/transition/objectives/QueryCriteria.java | 44 +++ .../dse/transition/objectives/QueryObjective.java | 44 +++ .../dse/transition/statespace/ActivationStore.java | 18 ++ .../statespace/EquivalenceClassStore.java | 16 ++ .../statespace/ObjectivePriorityQueue.java | 21 ++ .../dse/transition/statespace/SolutionStore.java | 17 ++ .../internal/AbstractEquivalenceClassStore.java | 47 ++++ .../internal/ActivationStoreBitVectorEntry.java | 46 ++++ .../statespace/internal/ActivationStoreEntry.java | 32 +++ .../statespace/internal/ActivationStoreImpl.java | 131 +++++++++ .../internal/ActivationStoreListEntry.java | 95 +++++++ .../statespace/internal/ActivationStoreWorker.java | 56 ++++ .../internal/CompleteEquivalenceClassStore.java | 107 ++++++++ .../internal/FastEquivalenceClassStore.java | 32 +++ .../internal/ObjectivePriorityQueueImpl.java | 74 ++++++ .../statespace/internal/SolutionStoreImpl.java | 53 ++++ .../tools/refinery/store/dse/CRAExamplesTest.java | 6 +- .../java/tools/refinery/store/dse/DebugTest.java | 4 +- .../store/dse/DesignSpaceExplorationTest.java | 3 +- .../refinery/store/dse/TransformationRuleTest.java | 3 +- .../statespace/internal/ActivationUnitTest.java | 72 +++++ 64 files changed, 2054 insertions(+), 1470 deletions(-) delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java deleted file mode 100644 index 5aed5298..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.map.Version; -import tools.refinery.store.dse.internal.Activation; -import tools.refinery.store.dse.internal.DesignSpaceExplorationBuilderImpl; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; - -import java.util.Collection; -import java.util.List; -import java.util.Random; - -public interface DesignSpaceExplorationAdapter extends ModelAdapter { - @Override - DesignSpaceExplorationStoreAdapter getStoreAdapter(); - - static DesignSpaceExplorationBuilder builder() { - return new DesignSpaceExplorationBuilderImpl(); - } - - Collection explore(); - - public int getModelSize(); - - public Tuple1 createObject(); - - public Tuple deleteObject(Tuple tuple); - - public boolean checkGlobalConstraints(); - - public boolean backtrack(); - - public boolean backtrack(String reason); - - public Fitness getFitness(); - - public void newSolution(); - - public int getDepth(); - - public Collection getUntraversedActivations(); - - public boolean fireActivation(Activation activation); - - public boolean fireRandomActivation(); - - public List getTrajectory(); - - public boolean isCurrentStateAlreadyTraversed(); - - public ObjectiveComparatorHelper getObjectiveComparatorHelper(); - - public void restoreTrajectory(List trajectory); - - public void setRandom(Random random); - - public void setRandom(long seed); - - public List getSolutions(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java deleted file mode 100644 index 4def5cb2..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.objectives.Objective; - -import java.util.Collection; -import java.util.List; - -public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { - return transformations(List.of(transformationRules)); - } - - default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { - transformationRules.forEach(this::transformation); - return this; - } - - default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) { - return globalConstraints(List.of(globalConstraints)); - } - - default DesignSpaceExplorationBuilder globalConstraints(Collection globalConstraints) { - globalConstraints.forEach(this::globalConstraint); - return this; - } - - default DesignSpaceExplorationBuilder objectives(Objective... objectives) { - return objectives(List.of(objectives)); - } - - default DesignSpaceExplorationBuilder objectives(Collection objectives) { - objectives.forEach(this::objective); - return this; - } - - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); - DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); - DesignSpaceExplorationBuilder objective(Objective objective); - DesignSpaceExplorationBuilder strategy(Strategy strategy); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java deleted file mode 100644 index 0252748d..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.dnf.RelationalQuery; - -import java.util.List; -import java.util.Set; - -public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { - - @Override - DesignSpaceExplorationAdapter createModelAdapter(Model model); - - Set getTransformationSpecifications(); - - Set getGlobalConstraints(); - - List getObjectives(); - - Strategy getStrategy(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java deleted file mode 100644 index c60a4410..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -public interface Strategy { - - void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); - - void explore(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java deleted file mode 100644 index 1893ce2e..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.tuple.Tuple; - -public record Activation(TransformationRule transformationRule, Tuple activation) { - public boolean fire() { - return transformationRule.fireActivation(activation); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java deleted file mode 100644 index 220f0b2d..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ /dev/null @@ -1,294 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.map.Version; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; -import tools.refinery.visualization.ModelVisualizerAdapter; - -import java.util.*; - -public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { - static final Symbol NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); - private final Model model; - private final ModelQueryAdapter queryEngine; - private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; - private final Set transformationRules; - private final Set globalConstraints; - private final List objectives; - private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); - private final Interpretation sizeInterpretation; - private final Strategy strategy; - - private ObjectiveComparatorHelper objectiveComparatorHelper; - private List trajectory = new ArrayList<>(); - private Map parents = new HashMap<>(); - private final List solutions = new ArrayList<>(); - private Map> statesAndTraversedActivations; - private Random random = new Random(); - private boolean isNewState = false; - private final boolean isVisualizationEnabled; - private final ModelVisualizerAdapter modelVisualizerAdapter; - - private final Map fitnessCache = new HashMap<>(); - - public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); - queryEngine = model.getAdapter(ModelQueryAdapter.class); - - globalConstraints = storeAdapter.getGlobalConstraints(); - for (var constraint : globalConstraints) { - globalConstraintResultSets.add(queryEngine.getResultSet(constraint)); - } - - transformationRules = storeAdapter.getTransformationSpecifications(); - for (var rule : transformationRules) { - rule.prepare(model, queryEngine); - } - - objectives = storeAdapter.getObjectives(); - statesAndTraversedActivations = new HashMap<>(); - strategy = storeAdapter.getStrategy(); - strategy.initialize(this); - modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); - isVisualizationEnabled = modelVisualizerAdapter != null; - - } - - public List getTrajectory() { - return new ArrayList<>(trajectory); - } - - @Override - public Model getModel() { - return model; - } - - @Override - public DesignSpaceExplorationStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - @Override - public List explore() { - var state = model.commit(); - trajectory.add(state); - strategy.explore(); - if (isVisualizationEnabled) { - modelVisualizerAdapter.visualize(); - } - return solutions; - } - - @Override - public int getModelSize() { - return sizeInterpretation.get(Tuple.of()); - } - - @Override - public Tuple1 createObject() { - var newNodeId = getModelSize(); - sizeInterpretation.put(Tuple.of(), newNodeId + 1); - return Tuple.of(newNodeId); - } - - @Override - public Tuple deleteObject(Tuple tuple) { - if (tuple.getSize() != 1) { - throw new IllegalArgumentException("Tuple size must be 1"); - } -// TODO: implement more efficient deletion -// if (tuple.get(0) == getModelSize() - 1) { -// sizeInterpretation.put(Tuple.of(), getModelSize() - 1); -// } - return tuple; - } - - @Override - public boolean checkGlobalConstraints() { - for (var resultSet : globalConstraintResultSets) { - if (resultSet.size() > 0) { - return false; - } - } - return true; - } - - @Override - public boolean backtrack() { - return backtrack(""); - } - @Override - public boolean backtrack(String reason) { - if (trajectory.size() < 2) { - return false; - } - var currentState = model.getState(); - if (!parents.containsKey(currentState)) { - return false; - } - if (isVisualizationEnabled) { - modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), - trajectory.get(trajectory.size() - 2), "backtrack(" + reason + ")"); - } - model.restore(parents.get(model.getState())); - trajectory.remove(trajectory.size() - 1); - return true; - } - - @Override - public void restoreTrajectory(List trajectory) { - model.restore(trajectory.get(trajectory.size() - 1)); -// if (isVisualizationEnabled) { -// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), -// trajectory.get(trajectory.size() - 1), "restore"); -// } - this.trajectory = new ArrayList<>(trajectory); - - } - - @Override - public void setRandom(Random random) { - this.random = random; - } - - @Override - public void setRandom(long seed) { - this.random = new Random(seed); - } - - @Override - public List getSolutions() { - return solutions; - } - - @Override - public Fitness getFitness() { - return fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness()); - } - - private Fitness calculateFitness() { - Fitness result = new Fitness(); - boolean satisfiesHardObjectives = true; - for (Objective objective : objectives) { - var fitness = objective.getFitness(this); - result.put(objective.getName(), fitness); - if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) { - satisfiesHardObjectives = false; - } - } - result.setSatisfiesHardObjectives(satisfiesHardObjectives); - - return result; - } - - @Override - public void newSolution() { - var state = model.getState(); - solutions.add(state); - if (isVisualizationEnabled) { - modelVisualizerAdapter.addSolution(state); - } - } - - @Override - public int getDepth() { - return trajectory.size() - 1; - } - - public LinkedHashSet getUntraversedActivations() { - var traversedActivations = statesAndTraversedActivations.get(model.getState()); - if (traversedActivations == null) { - return new LinkedHashSet<>(getAllActivations()); - } - else { - LinkedHashSet untraversedActivations = new LinkedHashSet<>(); - for (Activation activation : getAllActivations()) { - if (!traversedActivations.contains(activation)) { - untraversedActivations.add(activation); - } - } - return untraversedActivations; - } - } - - @Override - public boolean fireActivation(Activation activation) { - if (activation == null) { - return false; - } - var previousState = model.getState(); - if (!activation.fire()) { - return false; - } - statesAndTraversedActivations.computeIfAbsent(previousState, s -> new ArrayList<>()).add(activation); - var newState = model.commit(); - trajectory.add(newState); - parents.put(newState, previousState); - isNewState = !statesAndTraversedActivations.containsKey(newState); - if (isVisualizationEnabled) { - if (isNewState) { - modelVisualizerAdapter.addState(newState, getFitness().values()); - } - modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(), - activation.activation()); - } - return true; - } - - @Override - public boolean fireRandomActivation() { - var activations = getUntraversedActivations(); - if (activations.isEmpty()) { - return false; - } - int index = random.nextInt(activations.size()); - var iterator = activations.iterator(); - while (index-- > 0) { - iterator.next(); - } - var activationId = iterator.next(); - return fireActivation(activationId); - } - - public List getAllActivations() { - List result = new LinkedList<>(); - for (var rule : transformationRules) { - result.addAll(rule.getAllActivationsAsList()); - } - return result; - } - - public boolean isCurrentStateAlreadyTraversed() { - return !isNewState; - } - - public ObjectiveComparatorHelper getObjectiveComparatorHelper() { - if (objectiveComparatorHelper == null) { - objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); - } - return objectiveComparatorHelper; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java deleted file mode 100644 index 8f7056f2..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.DesignSpaceExplorationBuilder; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Objective; - -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; - -public class DesignSpaceExplorationBuilderImpl - extends AbstractModelAdapterBuilder - implements DesignSpaceExplorationBuilder { - private final LinkedHashSet transformationSpecifications = new LinkedHashSet<>(); - private final LinkedHashSet globalConstraints = new LinkedHashSet<>(); - private final List objectives = new LinkedList<>(); - private Strategy strategy; - - @Override - protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints, - objectives, strategy); - } - - @Override - public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) { - checkNotConfigured(); - transformationSpecifications.add(transformationRule); - return this; - } - - @Override - public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) { - checkNotConfigured(); - globalConstraints.add(globalConstraint); - return this; - } - - @Override - public DesignSpaceExplorationBuilder objective(Objective objective) { - checkNotConfigured(); - objectives.add(objective); - return this; - } - - @Override - public DesignSpaceExplorationBuilder strategy(Strategy strategy) { - checkNotConfigured(); - this.strategy = strategy; - return this; - } - - @Override - protected void doConfigure(ModelStoreBuilder storeBuilder) { - storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); - super.doConfigure(storeBuilder); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java deleted file mode 100644 index fea39886..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.dnf.RelationalQuery; - -import java.util.List; -import java.util.Set; - -public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { - private final ModelStore store; - private final Set transformationSpecifications; - private final Set globalConstraints; - private final List objectives; - private final Strategy strategy; - - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - Set transformationSpecifications, - Set globalConstraints, - List objectives, Strategy strategy) { - this.store = store; - this.transformationSpecifications = transformationSpecifications; - this.globalConstraints = globalConstraints; - this.objectives = objectives; - this.strategy = strategy; - } - - @Override - public ModelStore getStore() { - return store; - } - - @Override - public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { - return new DesignSpaceExplorationAdapterImpl(model, this); - } - - @Override - public Set getTransformationSpecifications() { - return transformationSpecifications; - } - - @Override - public Set getGlobalConstraints() { - return globalConstraints; - } - - @Override - public List getObjectives() { - return objectives; - } - - @Override - public Strategy getStrategy() { - return strategy; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java deleted file mode 100644 index 8123c0d6..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import org.eclipse.collections.api.block.procedure.Procedure; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.ActionFactory; -import tools.refinery.store.query.resultset.OrderedResultSet; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.*; - -public class TransformationRule { - - private final String name; - private final RelationalQuery precondition; - private final ActionFactory actionFactory; - private Procedure action; - private OrderedResultSet activations; - private Random random; - private ModelQueryAdapter queryEngine; - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { - this(name, precondition, actionFactory, new Random()); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { - this(name, precondition, actionFactory, new Random(seed)); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { - this.name = name; - this.precondition = precondition; - this.actionFactory = actionFactory; - this.random = random; - } - public boolean prepare(Model model, ModelQueryAdapter queryEngine) { - action = actionFactory.prepare(model); - this.queryEngine = queryEngine; - activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); - return true; - } - - public boolean fireActivation(Tuple activation) { - action.accept(activation); - queryEngine.flushChanges(); - return true; - } - - public boolean fireRandomActivation() { - return getRandomActivation().fire(); - } - - public String getName() { - return name; - } - - public RelationalQuery getPrecondition() { - return precondition; - } - - public ResultSet getAllActivationsAsResultSet() { - return activations; - } - - public Set getAllActivations() { - var result = new LinkedHashSet(); - var cursor = activations.getAll(); - while (cursor.move()) { - result.add(new Activation(this, cursor.getKey())); - } - return result; - } - - public List getAllActivationsAsList() { - var result = new ArrayList(); - var cursor = activations.getAll(); - while (cursor.move()) { - result.add(new Activation(this, cursor.getKey())); - } - return result; - } - - public Activation getRandomActivation() { - return new Activation(this, activations.getKey(random.nextInt(activations.size()))); - } - - public Activation getActivation(int index) { - return new Activation(this, activations.getKey(index)); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java new file mode 100644 index 00000000..f15c16e0 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.modification.internal.ModificationBuilderImpl; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +public interface ModificationAdapter extends ModelAdapter { + + int getModelSize(); + + Tuple1 createObject(); + + Tuple deleteObject(Tuple tuple); + + static ModificationBuilder builder() { + return new ModificationBuilderImpl(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java new file mode 100644 index 00000000..48c22bdf --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelAdapterBuilder; + +public interface ModificationBuilder extends ModelAdapterBuilder { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java new file mode 100644 index 00000000..144c4d05 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelStoreAdapter; + +public interface ModificationStoreAdapter extends ModelStoreAdapter { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java new file mode 100644 index 00000000..b2a80d71 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +public class ModificationAdapterImpl implements ModificationAdapter { + static final Symbol NEXT_ID = Symbol.of("NEXT_ID", 0, Integer.class, 0); + + final ModelStoreAdapter storeAdapter; + final Model model; + Interpretation nodeCountInterpretation; + + ModificationAdapterImpl(ModelStoreAdapter storeAdapter, Model model) { + this.storeAdapter = storeAdapter; + this.model = model; + this.nodeCountInterpretation = model.getInterpretation(NEXT_ID); + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public int getModelSize() { + return nodeCountInterpretation.get(Tuple.of()); + } + + @Override + public Tuple1 createObject() { + var newNodeId = getModelSize(); + nodeCountInterpretation.put(Tuple.of(), newNodeId + 1); + return Tuple.of(newNodeId); + } + + @Override + public Tuple deleteObject(Tuple tuple) { + if (tuple.getSize() != 1) { + throw new IllegalArgumentException("Tuple size must be 1"); + } +// TODO: implement more efficient deletion + if (tuple.get(0) == getModelSize() - 1) { + nodeCountInterpretation.put(Tuple.of(), getModelSize() - 1); + } + return tuple; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java new file mode 100644 index 00000000..c4d38d22 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.modification.ModificationBuilder; +import tools.refinery.store.dse.modification.ModificationStoreAdapter; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.statecoding.StateCoderBuilder; + +public class ModificationBuilderImpl extends AbstractModelAdapterBuilder implements ModificationBuilder { + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + storeBuilder.symbols(ModificationAdapterImpl.NEXT_ID); + storeBuilder.tryGetAdapter(StateCoderBuilder.class).ifPresent( + coderBuilder -> coderBuilder.exclude(ModificationAdapterImpl.NEXT_ID)); + super.doConfigure(storeBuilder); + } + + @Override + protected ModificationStoreAdapter doBuild(ModelStore store) { + return new ModificationStoreAdapterImpl(store); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java new file mode 100644 index 00000000..913cb33f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.modification.ModificationStoreAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; + +public class ModificationStoreAdapterImpl implements ModificationStoreAdapter { + ModelStore store; + + ModificationStoreAdapterImpl(ModelStore store) { + this.store = store; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java deleted file mode 100644 index afed75fd..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -/** - * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. - * - * @author Andras Szabolcs Nagy - * - */ -public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { - - private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; - - public AlwaysSatisfiedDummyHardObjective() { - super(DEFAULT_NAME); - } - - public AlwaysSatisfiedDummyHardObjective(String name) { - super(name); - } - - @Override - public Double getFitness(DesignSpaceExplorationAdapter context) { - return 0d; - } - - @Override - public boolean isHardObjective() { - return true; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - return true; - } - - @Override - public Objective createNew() { - return this; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java deleted file mode 100644 index 327d5e2f..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Random; - -/** - * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. - * - * @author Andras Szabolcs Nagy - * - */ -public class AlwaysSatisfiedRandomHardObjective extends BaseObjective { - - private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; - private static final Random random = new Random(0); - - public AlwaysSatisfiedRandomHardObjective() { - super(DEFAULT_NAME); - } - - public AlwaysSatisfiedRandomHardObjective(String name) { - super(name); - } - - @Override - public Double getFitness(DesignSpaceExplorationAdapter context) { -// return 0d; - return random.nextDouble(); - } - - @Override - public boolean isHardObjective() { - return true; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - return true; - } - - @Override - public Objective createNew() { - return this; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java deleted file mode 100644 index b76598fb..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; -import java.util.Objects; - -/** - * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name, - * comparator, level and fitness hard constraint. - * - * @author Andras Szabolcs Nagy - * - */ -public abstract class BaseObjective implements Objective { - - protected final String name; - protected Comparator comparator = Comparators.HIGHER_IS_BETTER; - - protected double fitnessConstraint; - protected boolean isThereFitnessConstraint = false; - protected Comparator fitnessConstraintComparator; - - protected BaseObjective(String name) { - Objects.requireNonNull(name, "Name of the objective cannot be null."); - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setComparator(Comparator comparator) { - this.comparator = comparator; - } - - @Override - public Comparator getComparator() { - return comparator; - } - - public BaseObjective withComparator(Comparator comparator) { - setComparator(comparator); - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @param fitnessConstraintComparator - * {@link Comparator} to determine if the current state is better than the given value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, - Comparator fitnessConstraintComparator) { - this.fitnessConstraint = fitnessConstraint; - this.fitnessConstraintComparator = fitnessConstraintComparator; - this.isThereFitnessConstraint = true; - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. The provided comparator will be used. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { - return withHardConstraintOnFitness(fitnessConstraint, null); - } - - @Override - public void init(DesignSpaceExplorationAdapter context) { - if (fitnessConstraintComparator == null) { - fitnessConstraintComparator = comparator; - } - } - - @Override - public boolean isHardObjective() { - return isThereFitnessConstraint; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - if (isThereFitnessConstraint) { - int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); - if (compare < 0) { - return false; - } - } - return true; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof BaseObjective baseObjective) { - return name.equals(baseObjective.getName()); - } - return false; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java deleted file mode 100644 index 181397b3..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.Comparator; - -public class Comparators { - - private Comparators() { /*Utility class constructor*/ } - - public static final Comparator HIGHER_IS_BETTER = Double::compareTo; - - public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); - - private static final Double ZERO = (double) 0; - - public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java deleted file mode 100644 index b1dc4442..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.HashMap; - -public class Fitness extends HashMap { - - private boolean satisfiesHardObjectives; - - public boolean isSatisfiesHardObjectives() { - return satisfiesHardObjectives; - } - - public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) { - this.satisfiesHardObjectives = satisfiesHardObjectives; - } - - @Override - public String toString() { - return super.toString() + " hardObjectives=" + satisfiesHardObjectives; - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (getClass() != other.getClass()) return false; - if (!super.equals(other)) return false; - return satisfiesHardObjectives == ((Fitness) other).satisfiesHardObjectives; - } - - @Override - public int hashCode() { - int h = super.hashCode(); - h = h * 31 + (satisfiesHardObjectives ? 1 : 0); - return h; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java deleted file mode 100644 index c7313622..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; - -/** - * - * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution - * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated - * value. - *

- * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard - * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. - *

- * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary - * for every new thread, and the {@code createNew} method should not return the same instance more than once. - * - * @author Andras Szabolcs Nagy - * - */ -public interface Objective { - - /** - * Returns the name of the objective. - * - * @return The name of the objective. - */ - String getName(); - - /** - * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to - * minimize or maximize (or minimize or maximize a delta from a given number). - * - * @param comparator The comparator. - */ - void setComparator(Comparator comparator); - - /** - * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is - * to minimize or maximize (or minimize or maximize a delta from a given number). - * - * @return The comparator. - */ - Comparator getComparator(); - - /** - * Calculates the value of the objective on a given solution (trajectory). - * - * @param context - * The {@link DesignSpaceExplorationAdapter} - * @return The objective value in double. - */ - Double getFitness(DesignSpaceExplorationAdapter context); - - /** - * Initializes the objective. It is called exactly once for every thread starts. - * - * @param context - * The {@link DesignSpaceExplorationAdapter}. - */ - void init(DesignSpaceExplorationAdapter context); - - /** - * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread - * save as they are called concurrently. - * - * @return An instance of the objective. - */ - Objective createNew(); - - /** - * Returns true if the objective is a hard objective. In such a case the method - * {@link Objective#satisfiesHardObjective(Double)} is called. - * - * @return True if the objective is a hard objective. - * @see Objective#satisfiesHardObjective(Double) - * @see Objective - */ - boolean isHardObjective(); - - /** - * Determines if the given fitness value satisfies the hard objective. - * - * @param fitness - * The fitness value of a solution. - * @return True if it satisfies the hard objective or it is a soft constraint. - * @see Objective - */ - boolean satisfiesHardObjective(Double fitness); - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java deleted file mode 100644 index eb03eeaf..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.List; - -/** - * This class is responsible to compare and sort fitness values. - * - * @author András Szabolcs Nagy - */ -public class ObjectiveComparatorHelper { - - private final List objectives; - - public ObjectiveComparatorHelper(List objectives) { - this.objectives = objectives; - } - - /** - * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better - * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates - * {@code o2}) and returns 0 if they are non-dominating each other. - */ - public int compare(Fitness o1, Fitness o2) { - - boolean o1HasBetterFitness = false; - boolean o2HasBetterFitness = false; - - for (Objective objective : objectives) { - String objectiveName = objective.getName(); - int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); - - if (sgn < 0) { - o2HasBetterFitness = true; - } - if (sgn > 0) { - o1HasBetterFitness = true; - } - if (o1HasBetterFitness && o2HasBetterFitness) { - break; - } - } - if (o2HasBetterFitness) { - return -1; - } else if (o1HasBetterFitness) { - return 1; - } - - return 0; - - } -} 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 new file mode 100644 index 00000000..72bbbc55 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.model.Model; + +import java.util.Random; + +public class BestFirstExplorer extends BestFirstWorker { + final int id; + Random random; + public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) { + super(storeManager, model); + this.id = id; + this.random = new Random(id); + } + + private boolean interrupted = false; + public void interrupt() { + this.interrupted = true; + } + + private boolean shouldRun() { + return !interrupted && !hasEnoughSolution(); + } + + public void explore() { + VersionWithObjectiveValue lastVisited = submit().newVersion(); + + mainLoop: while (shouldRun()) { + + if (lastVisited == null) { + var restored = this.restoreToBest(); + if(restored != null) { + lastVisited = restored; + } else { + return; + } + } + + boolean tryActivation = true; + while(tryActivation && shouldRun()) { + RandomVisitResult randomVisitResult = this.visitRandomUnvisited(random); + + tryActivation &= randomVisitResult.shouldRetry(); + var newSubmit = randomVisitResult.submitResult(); + if(newSubmit != null) { + if(!newSubmit.include()) { + restoreToLast(); + } else { + var newVisit = newSubmit.newVersion(); + int compareResult = compare(lastVisited,newVisit); + if(compareResult >= 0) { + lastVisited = newVisit; + continue mainLoop; + } + } + } + } + + //final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + /*boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + // Global constraint is not satisfied in the first state. Terminate. + return; + } + + final Fitness firstFitness = dseAdapter.getFitness(); + if (firstFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // First state is a solution. Terminate. + if (backTrackIfSolution) { + return; + } + } + + if (maxDepth == 0) { + return; + }*/ + + /* + var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); + trajectoriesToExplore.add(firstTrajectoryWithFitness); + TrajectoryWithFitness currentTrajectoryWithFitness = null; + */ +/* + Collection activations = dseAdapter.getUntraversedActivations(); + Iterator iterator = activations.iterator(); + + while (iterator.hasNext()) { + final Activation nextActivation = iterator.next(); + if (!iterator.hasNext()) { + // Last untraversed activation of the state. + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + } + + // Executing new activation + dseAdapter.fireActivation(nextActivation); + if (dseAdapter.isCurrentStateAlreadyTraversed()) { + // The new state is already visited. + dseAdapter.backtrack(); + } else if (!dseAdapter.checkGlobalConstraints()) { + // Global constraint is not satisfied. + dseAdapter.backtrack(); + } else { + final Fitness nextFitness = dseAdapter.getFitness(); + if (nextFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + var solutions = dseAdapter.getSolutions().size(); + if (solutions >= maxSolutions) { + return; + } + // Found a solution. + if (backTrackIfSolution) { + dseAdapter.backtrack(); + continue; + } + } + if (dseAdapter.getDepth() >= maxDepth) { + // Reached max depth. + dseAdapter.backtrack(); + continue; + } + + TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( + dseAdapter.getTrajectory(), nextFitness); + trajectoriesToExplore.add(nextTrajectoryWithFitness); + + int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, + nextTrajectoryWithFitness.fitness); + if (compare < 0) { + // Better fitness, moving on + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } else if (compare == 0) { + if (onlyBetterFirst) { + // Equally good fitness, backtrack + dseAdapter.backtrack(); + } else { + // Equally good fitness, moving on + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } + } else { + //"Worse fitness + currentTrajectoryWithFitness = null; + continue mainLoop; + } + } + } + + // State is fully traversed. + currentTrajectoryWithFitness = null; +*/ + } + // Interrupted. + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java new file mode 100644 index 00000000..d1fd7884 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; +import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreImpl; +import tools.refinery.store.dse.transition.statespace.internal.FastEquivalenceClassStore; +import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriorityQueueImpl; +import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public class BestFirstStoreManager { + ModelStore modelStore; + ObjectivePriorityQueue objectiveStore; + ActivationStore activationStore; + SolutionStore solutionStore; + EquivalenceClassStore equivalenceClassStore; + + public BestFirstStoreManager(ModelStore modelStore) { + this.modelStore = modelStore; + DesignSpaceExplorationStoreAdapter storeAdapter = + modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class); + + objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); + Procedure whenAllActivationsVisited = x -> objectiveStore.remove(x); + activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); + solutionStore = new SolutionStoreImpl(1); + equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { + @Override + protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { + objectiveStore.submit(version); + activationStore.markNewAsVisited(version, emptyActivations); + if(accept) { + solutionStore.submit(version); + } + } + }; + } + + ObjectivePriorityQueue getObjectiveStore() { + return objectiveStore; + } + + ActivationStore getActivationStore() { + return activationStore; + } + + SolutionStore getSolutionStore() { + return solutionStore; + } + + EquivalenceClassStore getEquivalenceClassStore() { + return equivalenceClassStore; + } + + public void startExploration(Version initial) { + BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial), 1); + bestFirstExplorer.explore(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java deleted file mode 100644 index 92d878ce..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ /dev/null @@ -1,203 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.strategy; - -import tools.refinery.store.map.Version; -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.internal.Activation; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; - -import java.util.*; - -public class BestFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth = Integer.MAX_VALUE; - private int maxSolutions = Integer.MAX_VALUE; - private boolean backTrackIfSolution = true; - private boolean onlyBetterFirst = false; - - private PriorityQueue trajectoriesToExplore; - - private record TrajectoryWithFitness(List trajectory, Fitness fitness) { - @Override - public String toString() { - return trajectory.toString() + fitness.toString(); - } - - @Override - public int hashCode() { - return trajectory.get(trajectory.size() - 1).hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TrajectoryWithFitness other) { - return Objects.equals(trajectory.get(trajectory.size() - 1), other.trajectory.get(other.trajectory.size() - 1)); -// return trajectory.equals(((TrajectoryWithFitness) obj).trajectory); - } - return false; - } - } - - public BestFirstStrategy withDepthLimit(int maxDepth) { - if (maxDepth >= 0) { - this.maxDepth = maxDepth; - } - return this; - } - - public BestFirstStrategy withSolutionLimit(int maxSolutions) { - if (maxSolutions >= 0) { - this.maxSolutions = maxSolutions; - } - return this; - } - - public BestFirstStrategy continueIfHardObjectivesFulfilled() { - backTrackIfSolution = false; - return this; - } - - public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { - onlyBetterFirst = true; - return this; - } - - @Override - public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - trajectoriesToExplore = new PriorityQueue<>(11, - (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); - } - - @Override - public void explore() { - if (maxSolutions == 0) { - return; - } - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - // Global constraint is not satisfied in the first state. Terminate. - return; - } - - final Fitness firstFitness = dseAdapter.getFitness(); - if (firstFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - // First state is a solution. Terminate. - if (backTrackIfSolution) { - return; - } - } - - if (maxDepth == 0) { - return; - } - - - var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); - trajectoriesToExplore.add(firstTrajectoryWithFitness); - TrajectoryWithFitness currentTrajectoryWithFitness = null; - - mainLoop: while (true) { - - if (currentTrajectoryWithFitness == null) { - if (trajectoriesToExplore.isEmpty()) { - // State space is fully traversed. - return; - } else { - currentTrajectoryWithFitness = trajectoriesToExplore.element(); - // New trajectory is chosen: " + currentTrajectoryWithFitness - dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); - } - } - - Collection activations = dseAdapter.getUntraversedActivations(); - Iterator iterator = activations.iterator(); - - - - while (iterator.hasNext()) { - final Activation nextActivation = iterator.next(); - if (!iterator.hasNext()) { - // Last untraversed activation of the state. - trajectoriesToExplore.remove(currentTrajectoryWithFitness); - } - - // Executing new activation - dseAdapter.fireActivation(nextActivation); - if (dseAdapter.isCurrentStateAlreadyTraversed()) { - // The new state is already visited. - dseAdapter.backtrack(); - } else if (!dseAdapter.checkGlobalConstraints()) { - // Global constraint is not satisfied. - dseAdapter.backtrack(); - } else { - final Fitness nextFitness = dseAdapter.getFitness(); - if (nextFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - var solutions = dseAdapter.getSolutions().size(); - if (solutions >= maxSolutions) { - return; - } - // Found a solution. - if (backTrackIfSolution) { - dseAdapter.backtrack(); - continue; - } - } - if (dseAdapter.getDepth() >= maxDepth) { - // Reached max depth. - dseAdapter.backtrack(); - continue; - } - - TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( - dseAdapter.getTrajectory(), nextFitness); - trajectoriesToExplore.add(nextTrajectoryWithFitness); - - int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, - nextTrajectoryWithFitness.fitness); - if (compare < 0) { - // Better fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } else if (compare == 0) { - if (onlyBetterFirst) { - // Equally good fitness, backtrack - dseAdapter.backtrack(); - } else { - // Equally good fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } - } else { - //"Worse fitness - currentTrajectoryWithFitness = null; - continue mainLoop; - } - } - } - - // State is fully traversed. - currentTrajectoryWithFitness = null; - - } - // Interrupted. - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java new file mode 100644 index 00000000..ea7fe43f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreWorker; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Model; +import tools.refinery.store.statecoding.StateCoderAdapter; + + +import java.util.Random; + +public class BestFirstWorker { + final BestFirstStoreManager storeManager; + final Model model; + final ActivationStoreWorker activationStoreWorker; + final StateCoderAdapter stateCoderAdapter; + final DesignSpaceExplorationAdapter explorationAdapter; + + public BestFirstWorker(BestFirstStoreManager storeManager, Model model) { + this.storeManager = storeManager; + this.model = model; + + explorationAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); + activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), + explorationAdapter.getTransformations()); + } + + private VersionWithObjectiveValue last = null; + + //public boolean isIncluded + + public SubmitResult submit() { + if (explorationAdapter.checkExclude()) { + last = null; + return new SubmitResult(false, false, null, null); + } + + Version version = model.commit(); + ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); + var res = new VersionWithObjectiveValue(version, objectiveValue); + var code = stateCoderAdapter.calculateStateCode(); + var accepted = explorationAdapter.checkAccept(); + + boolean isNew = storeManager.getEquivalenceClassStore().submit(res, code, + activationStoreWorker.calculateEmptyActivationSize(), accepted); + + last = new VersionWithObjectiveValue(version, objectiveValue); + return new SubmitResult(isNew, accepted, objectiveValue, last); + } + + public void restoreToLast() { + if (explorationAdapter.getModel().hasUncommittedChanges()) { + explorationAdapter.getModel().restore(last.version()); + } + } + + public VersionWithObjectiveValue restoreToBest() { + var bestVersion = storeManager.getObjectiveStore().getBest(); + if (bestVersion != null) { + this.model.restore(bestVersion.version()); + } + return bestVersion; + } + + public VersionWithObjectiveValue restoreToRandom(Random random) { + var randomVersion = storeManager.getObjectiveStore().getRandom(random); + last = randomVersion; + if (randomVersion != null) { + this.model.restore(randomVersion.version()); + } + return randomVersion; + } + + public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) { + return storeManager.getObjectiveStore().getComparator().compare(s1, s2); + } + + public boolean stateHasUnvisited() { + if (!model.hasUncommittedChanges()) { + return storeManager.getActivationStore().hasUnmarkedActivation(last); + } else { + throw new IllegalStateException("The model has uncommitted changes!"); + } + } + + record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) { + } + + public RandomVisitResult visitRandomUnvisited(Random random) { + if (!model.hasUncommittedChanges()) { + var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); + if (visitResult.successfulVisit()) { + return new RandomVisitResult(submit(), visitResult.mayHaveMore()); + } else { + return new RandomVisitResult(null, visitResult.mayHaveMore()); + } + } else { + throw new IllegalStateException("The model has uncommitted changes!"); + } + } + + public boolean hasEnoughSolution() { + return storeManager.solutionStore.hasEnoughSolution(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java deleted file mode 100644 index 0a0caa7e..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.strategy; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Fitness; - -public class DepthFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth = Integer.MAX_VALUE; - private int maxSolutions = Integer.MAX_VALUE; - private boolean backtrackFromSolution = true; - - public DepthFirstStrategy withDepthLimit(int maxDepth) { - if (maxDepth >= 0) { - this.maxDepth = maxDepth; - } - return this; - } - - public DepthFirstStrategy withSolutionLimit(int maxSolutions) { - if (maxSolutions >= 0) { - this.maxSolutions = maxSolutions; - } - return this; - } - - public DepthFirstStrategy continueIfHardObjectivesFulfilled() { - backtrackFromSolution = false; - return this; - } - - @Override - public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - } - - @Override - public void explore() { - if (maxSolutions == 0) { - return; - } - while (dseAdapter.getSolutions().size() < maxSolutions) { - if (!checkAndHandleGlobalConstraints()) { - return; - } - - Fitness fitness = dseAdapter.getFitness(); - if (fitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - if (backtrackFromSolution && !dseAdapter.backtrack()) { - return; - } - } - - if (!checkAndHandleDepth()) { - return; - } - - if (!backtrackToLastUntraversed()) { - return; - } - - if (!dseAdapter.fireRandomActivation()) { - return; - } - } - } - - private boolean checkAndHandleGlobalConstraints() { - return dseAdapter.checkGlobalConstraints() || dseAdapter.backtrack(); - } - - private boolean checkAndHandleDepth() { - return dseAdapter.getDepth() < maxDepth || dseAdapter.backtrack(); - } - - private boolean backtrackToLastUntraversed() { - while (dseAdapter.getUntraversedActivations().isEmpty()) { - if (!dseAdapter.backtrack()) { - return false; - } - } - return true; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java new file mode 100644 index 00000000..37d548d7 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +public record SubmitResult(boolean include, boolean accepted, ObjectiveValue objective, VersionWithObjectiveValue newVersion) { + + } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java new file mode 100644 index 00000000..37448309 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.transition.internal.DesignSpaceExplorationBuilderImpl; +import tools.refinery.store.map.Version; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationAdapter extends ModelAdapter { + + + + @Override + DesignSpaceExplorationStoreAdapter getStoreAdapter(); + + static DesignSpaceExplorationBuilder builder() { + return new DesignSpaceExplorationBuilderImpl(); + } + List getTransformations(); + boolean checkAccept(); + boolean checkExclude(); + ObjectiveValue getObjectiveValue(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java new file mode 100644 index 00000000..3855a20a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + + DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition); + default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRuleDefinitions) { + return transformations(List.of(transformationRuleDefinitions)); + } + + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + transformationRules.forEach(this::transformation); + return this; + } + + DesignSpaceExplorationBuilder accept(Criterion criteria); + + default DesignSpaceExplorationBuilder accept(Criterion... criteria) { + return accept(List.of(criteria)); + } + + default DesignSpaceExplorationBuilder accept(Collection criteria) { + criteria.forEach(this::accept); + return this; + } + + DesignSpaceExplorationBuilder exclude(Criterion criteria); + + default DesignSpaceExplorationBuilder exclude(Criterion... criteria) { + return exclude(List.of(criteria)); + } + + default DesignSpaceExplorationBuilder exclude(Collection criteria) { + criteria.forEach(this::exclude); + return this; + } + + DesignSpaceExplorationBuilder objective(Objective objective); + + default DesignSpaceExplorationBuilder objectives(Objective... objectives) { + return objectives(List.of(objectives)); + } + + default DesignSpaceExplorationBuilder objectives(Collection objectives) { + objectives.forEach(this::objective); + return this; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java new file mode 100644 index 00000000..5c8c7a4d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.model.Model; + +import java.util.List; + +public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter +{ + @Override + DesignSpaceExplorationAdapter createModelAdapter(Model model); + + List getTransformations(); + + List getAccepts(); + + List getExcludes(); + + List getObjectives(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java new file mode 100644 index 00000000..89ee61c8 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +public interface ObjectiveValue { + double get(int index); + int getSize(); + + static ObjectiveValue of(double v1) { + return new ObjectiveValues.ObjectiveValue1(v1); + } + + static ObjectiveValue of(double v1, double v2) { + return new ObjectiveValues.ObjectiveValue2(v1,v2); + } + + static ObjectiveValue of(double[] v) { + return new ObjectiveValues.ObjectiveValueN(v); + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java new file mode 100644 index 00000000..60913ff3 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import java.util.Arrays; + +public interface ObjectiveValues { + public record ObjectiveValue1(double value0) implements ObjectiveValue { + @Override + public double get(int index) { + if(index == 0) return value0; + else throw new IllegalArgumentException("No value at " + index); + } + + @Override + public int getSize() { + return 1; + } + } + public record ObjectiveValue2(double value0, double value1) implements ObjectiveValue { + @Override + public double get(int index) { + if(index == 0) return value0; + else if(index == 1) return value1; + else throw new IllegalArgumentException("No value at " + index); + } + + @Override + public int getSize() { + return 2; + } + } + public record ObjectiveValueN(double[] values) implements ObjectiveValue { + @Override + public double get(int index) { + return values[index]; + } + + @Override + public int getSize() { + return values().length; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ObjectiveValueN that = (ObjectiveValueN) o; + + return Arrays.equals(values, that.values); + } + + @Override + public int hashCode() { + return Arrays.hashCode(values); + } + + @Override + public String toString() { + return "ObjectiveValueN{" + + "values=" + Arrays.toString(values) + + '}'; + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java new file mode 100644 index 00000000..2cce738f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +public class Transformation { + private final TransformationRule definition; + + private final OrderedResultSet activations; + + private final Procedure action; + + public Transformation(TransformationRule definition, OrderedResultSet activations, Procedure action) { + this.definition = definition; + this.activations = activations; + this.action = action; + } + + public TransformationRule getDefinition() { + return definition; + } + + public ResultSet getAllActivationsAsResultSet() { + return activations; + } + + public Tuple getActivation(int index) { + return activations.getKey(index); + } + + public boolean fireActivation(Tuple activation) { + action.accept(activation); + //queryEngine.flushChanges(); + return true; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java new file mode 100644 index 00000000..d64a3db1 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.ActionFactory; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.*; + +public class TransformationRule { + + private final String name; + private final RelationalQuery precondition; + private final ActionFactory actionFactory; + + private Random random; + private ModelQueryAdapter queryEngine; + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { + this(name, precondition, actionFactory, new Random()); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { + this(name, precondition, actionFactory, new Random(seed)); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { + this.name = name; + this.precondition = precondition; + this.actionFactory = actionFactory; + this.random = random; + } + public void doConfigure(ModelStoreBuilder storeBuilder) { + var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); + queryBuilder.query(this.precondition); + } + + public Transformation prepare(Model model) { + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + var activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + var action = actionFactory.prepare(model); + return new Transformation(this,activations,action); + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java new file mode 100644 index 00000000..ca28e27f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.map.Version; + +public record VersionWithObjectiveValue(Version version, ObjectiveValue objectiveValue) { +} 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 new file mode 100644 index 00000000..e1a29d40 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; + +import java.util.List; + +public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { + final Model model; + final DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter; + + final List transformations; + final List accepts; + final List excludes; + final List objectives; + + public DesignSpaceExplorationAdapterImpl(Model model, + DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter, + List transformations, + List accepts, + List excludes, + List objectives) { + this.model = model; + this.designSpaceExplorationStoreAdapter = designSpaceExplorationStoreAdapter; + + this.transformations = transformations; + this.accepts = accepts; + this.excludes = excludes; + this.objectives = objectives; + } + + @Override + public Model getModel() { + return model; + } + + @Override + public DesignSpaceExplorationStoreAdapter getStoreAdapter() { + return designSpaceExplorationStoreAdapter; + } + + public List getTransformations() { + return transformations; + } + + @Override + public boolean checkAccept() { + for (var accept : this.accepts) { + if (!accept.isSatisfied()) { + return false; + } + } + return true; + } + + @Override + public boolean checkExclude() { + for (var exclude : this.excludes) { + if (exclude.isSatisfied()) { + return true; + } + } + return false; + } + + @Override + public ObjectiveValue getObjectiveValue() { + if (objectives.size() == 1) { + return ObjectiveValue.of(objectives.get(0).getValue()); + } else if (objectives.size() == 2) { + return ObjectiveValue.of(objectives.get(0).getValue(), objectives.get(1).getValue()); + } else { + double[] res = new double[objectives.size()]; + for (int i = 0; i < objectives.size(); i++) { + res[i] = objectives.get(i).getValue(); + } + return ObjectiveValue.of(res); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java new file mode 100644 index 00000000..4371cc03 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; + +public class DesignSpaceExplorationBuilderImpl + extends AbstractModelAdapterBuilder + implements DesignSpaceExplorationBuilder { + + LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); + LinkedHashSet accepts = new LinkedHashSet<>(); + LinkedHashSet excludes = new LinkedHashSet<>(); + LinkedHashSet objectives = new LinkedHashSet<>(); + + @Override + public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition) { + transformationRuleDefinitions.add(transformationRuleDefinition); + return this; + } + + @Override + public DesignSpaceExplorationBuilder accept(Criterion criteria) { + accepts.add(criteria); + return this; + } + + @Override + public DesignSpaceExplorationBuilder exclude(Criterion criteria) { + excludes.add(criteria); + return this; + } + + + @Override + public DesignSpaceExplorationBuilder objective(Objective objective) { + objectives.add(objective); + return this; + } + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + transformationRuleDefinitions.forEach(x -> x.doConfigure(storeBuilder)); + accepts.forEach(x -> x.doConfigure(storeBuilder)); + excludes.forEach(x -> x.doConfigure(storeBuilder)); + objectives.forEach(x -> x.doConfigure(storeBuilder)); + + super.doConfigure(storeBuilder); + } + + @Override + protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { + List transformationRuleDefinitiions1 = new ArrayList<>(transformationRuleDefinitions); + List accepts1 = new ArrayList<>(accepts); + List excludes1 = new ArrayList<>(excludes); + List objectives1 = new ArrayList<>(objectives); + + return new DesignSpaceExplorationStoreAdapterImpl(store, + transformationRuleDefinitiions1, accepts1, + excludes1, objectives1); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java new file mode 100644 index 00000000..3319e148 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; + +import java.util.List; + +public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { + protected final ModelStore store; + + protected final List transformationRuleDefinitions; + protected final List accepts; + protected final List excludes; + protected final List objectives; + + public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, + List transformationRuleDefinitions, + List accepts, List excludes, + List objectives) { + this.store = store; + + this.transformationRuleDefinitions = transformationRuleDefinitions; + this.accepts = accepts; + this.excludes = excludes; + this.objectives = objectives; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { + final List t = this.transformationRuleDefinitions.stream().map(x->x.prepare(model)).toList(); + final List a = this.accepts.stream().map(x->x.createCalculator(model)).toList(); + final List e = this.excludes.stream().map(x->x.createCalculator(model)).toList(); + final List o = this.objectives.stream().map(x->x.createCalculator(model)).toList(); + + return new DesignSpaceExplorationAdapterImpl(model, this, t, a, e, o); + } + @Override + public List getTransformations() { + return transformationRuleDefinitions; + } + @Override + public List getAccepts() { + return accepts; + } + @Override + public List getExcludes() { + return excludes; + } + @Override + public List getObjectives() { + return objectives; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java new file mode 100644 index 00000000..66ca6f5e --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; + +public interface Criterion { + default void doConfigure(ModelStoreBuilder storeBuilder) { + } + CriterionCalculator createCalculator(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java new file mode 100644 index 00000000..944ffed6 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +public interface CriterionCalculator { + boolean isSatisfied(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java new file mode 100644 index 00000000..b5924455 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; + +public interface Objective { + default void doConfigure(ModelStoreBuilder storeBuilder) { + } + ObjectiveCalculator createCalculator(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java new file mode 100644 index 00000000..f01b8de9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +public interface ObjectiveCalculator { + double getValue(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java new file mode 100644 index 00000000..e2260cca --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.AnyQuery; + +public class QueryCriteria implements Criterion { + protected final boolean acceptIfHasMatch; + protected final AnyQuery query; + + /** + * Criteria based on the existence of matches evaluated on the model. + * @param query The query evaluated on the model. + * @param acceptIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, + * the criteria satisfied if the query has no match on the model. + */ + public QueryCriteria(AnyQuery query, boolean acceptIfHasMatch) { + this.query = query; + this.acceptIfHasMatch = acceptIfHasMatch; + } + + @Override + public CriterionCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); + if(acceptIfHasMatch) { + return () -> resultSet.size() > 0; + } else { + return () -> resultSet.size() == 0; + } + } + + @Override + public void doConfigure(ModelStoreBuilder storeBuilder) { + Criterion.super.doConfigure(storeBuilder); + storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java new file mode 100644 index 00000000..dfddccfc --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.FunctionalQuery; + +public class QueryObjective implements Objective { + protected final FunctionalQuery objectiveFunction; + + public QueryObjective(FunctionalQuery objectiveFunction) { + this.objectiveFunction = objectiveFunction; + } + + @Override + public ObjectiveCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(objectiveFunction); + return () -> { + var cursor = resultSet.getAll(); + boolean hasElement = cursor.move(); + if(hasElement) { + double result = cursor.getValue().doubleValue(); + if(cursor.move()) { + throw new IllegalStateException("Query providing the objective function has multiple values!"); + } + return result; + } else { + throw new IllegalStateException("Query providing the objective function has no values!"); + } + }; + } + + @Override + public void doConfigure(ModelStoreBuilder storeBuilder) { + Objective.super.doConfigure(storeBuilder); + storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java new file mode 100644 index 00000000..52e0611d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +import java.util.Random; + +public interface ActivationStore { + record VisitResult(boolean successfulVisit, boolean mayHaveMore, int transformation, int activation) { } + VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes); + boolean hasUnmarkedActivation(VersionWithObjectiveValue version); + VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java new file mode 100644 index 00000000..bbe26fe5 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.statecoding.StateCoderResult; + +public interface EquivalenceClassStore { + boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, int[] emptyActivations, boolean accept); + boolean hasUnresolvedSymmetry(); + void resolveOneSymmetry(); + int getNumberOfUnresolvedSymmetries(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java new file mode 100644 index 00000000..df72c343 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +import java.util.Comparator; +import java.util.Random; + +public interface ObjectivePriorityQueue { + Comparator getComparator(); + void submit(VersionWithObjectiveValue versionWithObjectiveValue); + void remove(VersionWithObjectiveValue versionWithObjectiveValue); + int getSize(); + VersionWithObjectiveValue getBest(); + VersionWithObjectiveValue getRandom(Random random); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java new file mode 100644 index 00000000..d1bfaa79 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; + +import java.util.List; +import java.util.concurrent.Future; + +public interface SolutionStore { + boolean submit(VersionWithObjectiveValue version); + List getSolutions(); + boolean hasEnoughSolution(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java new file mode 100644 index 00000000..8466a0f3 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public abstract class AbstractEquivalenceClassStore implements EquivalenceClassStore { + protected final StateCoderStoreAdapter stateCoderStoreAdapter; + AbstractEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + this.stateCoderStoreAdapter = stateCoderStoreAdapter; + } + + protected int numberOfUnresolvedSymmetries = 0; + + protected abstract void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept); + protected abstract boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, + int[] emptyActivations, boolean accept); + + @Override + public synchronized boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, + int[] emptyActivations, boolean accept) { + boolean hasNewVersion = tryToAdd(stateCoderResult, version, emptyActivations, accept); + if (hasNewVersion) { + delegate(version, emptyActivations, accept); + return true; + } else { + numberOfUnresolvedSymmetries++; + return false; + } + } + + @Override + public boolean hasUnresolvedSymmetry() { + return numberOfUnresolvedSymmetries > 0; + } + + @Override + public int getNumberOfUnresolvedSymmetries() { + return numberOfUnresolvedSymmetries; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java new file mode 100644 index 00000000..ba243d7d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +public class ActivationStoreBitVectorEntry extends ActivationStoreEntry { + final int[] selected; + + ActivationStoreBitVectorEntry(int numberOfActivations) { + super(numberOfActivations); + this.selected = new int[(numberOfActivations / Integer.SIZE) + 1]; + } + + @Override + public int getNumberOfUnvisitedActivations() { + int visited = 0; + for (int i : selected) { + visited += Integer.bitCount(i); + } + return visited; + } + + private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE + private static final int ELEMENT_BITMASK = (1<> ELEMENT_POSITION; + final int selectedBit = position & ELEMENT_BITMASK; + if((selected[selectedElement] & selectedBit) == 0) { + selected[selectedElement] |= selectedBit; + return position; + } else { + if(position < this.numberOfActivations) { + position++; + } else { + position = 0; + } + } + } while(position != index); + throw new IllegalArgumentException("There is are no unvisited activations!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java new file mode 100644 index 00000000..f69b234c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +public abstract class ActivationStoreEntry { + protected final int numberOfActivations; + + ActivationStoreEntry(int numberOfActivations) { + this.numberOfActivations = numberOfActivations; + } + + public int getNumberOfVisitedActivations() { + return numberOfActivations; + } + + public abstract int getNumberOfUnvisitedActivations(); + public abstract int getAndAddActivationAfter(int index); + + // public abstract boolean contains(int activation) + // public abstract boolean add(int activation) + + public static ActivationStoreEntry create(int size) { + if(size <= Integer.SIZE*6) { + return new ActivationStoreBitVectorEntry(size); + } else { + return new ActivationStoreListEntry(size); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java new file mode 100644 index 00000000..1229ec15 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; + +import java.util.*; + +public class ActivationStoreImpl implements ActivationStore { + final int numberOfTransformations; + final Procedure actionWhenAllActivationVisited; + final Map> versionToActivations; + + public ActivationStoreImpl(final int numberOfTransformations, + Procedure actionWhenAllActivationVisited) { + this.numberOfTransformations = numberOfTransformations; + this.actionWhenAllActivationVisited = actionWhenAllActivationVisited; + versionToActivations = new HashMap<>(); + } + + public synchronized VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes) { + boolean[] successful = new boolean[]{false}; + var entries = versionToActivations.computeIfAbsent(to, x -> { + successful[0] = true; + List result = new ArrayList<>(emptyEntrySizes.length); + for(int emptyEntrySize : emptyEntrySizes) { + result.add(ActivationStoreListEntry.create(emptyEntrySize)); + } + return result; + }); + boolean hasMore = false; + for (var entry : entries) { + if (entry.getNumberOfUnvisitedActivations() > 0) { + hasMore = true; + break; + } + } + if(!hasMore) { + actionWhenAllActivationVisited.accept(to); + } + return new VisitResult(successful[0], hasMore, -1, -1); + } + + public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, int activationIndex) { + var entries = versionToActivations.get(from); + var entry = entries.get(transformationIndex); + final int unvisited = entry.getNumberOfUnvisitedActivations(); + + final boolean successfulVisit = unvisited > 0; + final boolean hasMoreInActivation = unvisited > 1; + final boolean hasMore; + final int transformation; + final int activation; + + if (successfulVisit) { + transformation = transformationIndex; + activation = entry.getAndAddActivationAfter(activationIndex); + + } else { + transformation = -1; + activation = -1; + } + + if(hasMoreInActivation) { + boolean hasMoreInOtherTransformation = false; + for (var e : entries) { + if (e != entry && e.getNumberOfVisitedActivations() > 0) { + hasMoreInOtherTransformation = true; + break; + } + } + hasMore = hasMoreInOtherTransformation; + } else { + hasMore = true; + } + + if(!hasMore) { + actionWhenAllActivationVisited.accept(from); + } + + return new VisitResult(false, hasMore, transformation, activation); + } + + @Override + public synchronized boolean hasUnmarkedActivation(VersionWithObjectiveValue version) { + var entries = versionToActivations.get(version); + boolean hasMore = false; + for (var entry : entries) { + if (entry.getNumberOfUnvisitedActivations() > 0) { + hasMore = true; + break; + } + } + return hasMore; + } + + @Override + public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) { + var entries = versionToActivations.get(version); + + int sum1 = 0; + for (var entry : entries) { + sum1 += entry.getNumberOfUnvisitedActivations(); + } + + int selected = random.nextInt(sum1); + int sum2 = 0; + int transformation = 0; + int activation = -1; + for (; transformation < entries.size(); transformation++) { + var entry = entries.get(transformation); + int unvisited = entry.getNumberOfUnvisitedActivations(); + if (selected < sum2 + unvisited) { + activation = sum2 + unvisited - selected; + break; + } else { + sum2 += unvisited; + } + } + if (activation == -1) { + throw new IllegalArgumentException("no unvisited"); + } + + return this.visitActivation(version, transformation, activation); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java new file mode 100644 index 00000000..9e25301f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList; + +public class ActivationStoreListEntry extends ActivationStoreEntry { + IntArrayList visitedActivations; + + ActivationStoreListEntry(int numberOfActivations) { + super(numberOfActivations); + visitedActivations = new IntArrayList(); + } + + @Override + public int getNumberOfUnvisitedActivations() { + return this.numberOfActivations - visitedActivations.size(); + } + + @Override + public int getAndAddActivationAfter(int index) { + // If it is empty, just add it. + if(this.visitedActivations.isEmpty()) { + this.visitedActivations.add(index); + return index; + } + + int position = getPosition(index); + + // If the index is not in the position, one can insert it + if(this.visitedActivations.get(position) != index) { + this.visitedActivations.addAtIndex(position,index); + return index; + } + + // Otherwise, get the next empty space between two elements + while(position + 2 < this.visitedActivations.size()) { + position++; + if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { + this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + } + } + + // Otherwise, try to add to the last space + int last = this.visitedActivations.get(this.visitedActivations.size()-1); + if(last 0) { + this.visitedActivations.add(0); + return 0; + } + + // Otherwise, get the next empty space between two elements + position = 0; + while(position + 2 < this.visitedActivations.size()) { + position++; + if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { + this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + } + } + + throw new IllegalArgumentException("There is are no unvisited activations!"); + } + + /** + * Returns the position of an index in the {@code visitedActivations}. If the collection contains the index, in + * returns its position, otherwise, it returns the position where the index need to be put. + * + * @param index Index of an activation. + * @return The position of the index. + */ + private int getPosition(int index) { + int left = 0; + int right = this.visitedActivations.size() - 1; + while (left <= right) { + final int middle = (right - left) / 2 + left; + final int middleElement = visitedActivations.get(middle); + if(middleElement == index) { + return middle; + } else if(middleElement < index) { + left = middle +1; + } else{ + right = middle-1; + } + } + return right+1; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java new file mode 100644 index 00000000..e05f5122 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.map.Version; + +import java.util.List; +import java.util.Random; + +public class ActivationStoreWorker { + final ActivationStore store; + final List transformations; + + public ActivationStoreWorker(ActivationStore store, List transformations) { + this.store = store; + this.transformations = transformations; + } + + public int[] calculateEmptyActivationSize() { + int[] result = new int[transformations.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = transformations.get(i).getAllActivationsAsResultSet().size(); + } + return result; + } + + + public ActivationStore.VisitResult fireRandomActivation(VersionWithObjectiveValue thisVersion, Random random) { + var result = store.getRandomAndMarkAsVisited(thisVersion, random); + if(result.successfulVisit()) { + int selectedTransformation = result.transformation(); + int selectedActivation = result.activation(); + + Transformation transformation = transformations.get(selectedTransformation); + var tuple = transformation.getActivation(selectedActivation); + + boolean success = transformation.fireActivation(tuple); + if(success) { + return result; + } else { + return new ActivationStore.VisitResult( + false, + result.mayHaveMore(), + selectedActivation, + selectedActivation); + } + } + return result; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java new file mode 100644 index 00000000..1555c3be --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; +import tools.refinery.store.statecoding.StateEquivalenceChecker; + +import java.util.ArrayList; + +public abstract class CompleteEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore { + + static class SymmetryStoreArray extends ArrayList { + final int[] activationSizes; + final boolean accept; + + SymmetryStoreArray(int[] activationSizes, boolean accept) { + super(); + this.activationSizes = activationSizes; + this.accept = accept; + } + } + + final IntObjectHashMap modelCode2Versions; + + protected CompleteEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + super(stateCoderStoreAdapter); + this.modelCode2Versions = new IntObjectHashMap<>(); + } + + @Override + protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, + int[] emptyActivations, boolean accept) { + int modelCode = stateCoderResult.modelCode(); + Object old = modelCode2Versions.updateValue( + modelCode, + () -> newVersion, + x -> { + if (x instanceof SymmetryStoreArray array) { + if(array.accept != accept || array.activationSizes != emptyActivations) { + this.delegate(newVersion,emptyActivations,accept); + return x; + } + array.add(newVersion); + return array; + } else { + SymmetryStoreArray result = new SymmetryStoreArray(emptyActivations, accept); + result.add((VersionWithObjectiveValue) x); + result.add(newVersion); + return result; + } + }); + return old == null; + } + + @Override + public void resolveOneSymmetry() { + var unresolvedSimilarity = getOneUnresolvedSymmetry(); + if (unresolvedSimilarity == null) { + return; + } + var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0), + unresolvedSimilarity.get(1)); + if (outcome != StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC) { + delegate(unresolvedSimilarity.get(1), unresolvedSimilarity.activationSizes, unresolvedSimilarity.accept); + } + } + + //record UnresolvedSymmetryResult + + private synchronized SymmetryStoreArray getOneUnresolvedSymmetry() { + if (numberOfUnresolvedSymmetries <= 0) { + return null; + } + + for (var entry : modelCode2Versions.keyValuesView()) { + int hash = entry.getOne(); + var value = entry.getTwo(); + if (value instanceof SymmetryStoreArray array) { + int size = array.size(); + var representative = array.get(0); + var similar = array.get(size - 1); + array.remove(size - 1); + + if (size <= 2) { + modelCode2Versions.put(hash, representative); + } + + var result = new SymmetryStoreArray(array.activationSizes, array.accept); + result.add(representative); + result.add(similar); + return result; + } + } + + return null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java new file mode 100644 index 00000000..75e117c0 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public abstract class FastEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore { + + final IntHashSet codes; + + public FastEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + super(stateCoderStoreAdapter); + this.codes = new IntHashSet(); + } + + @Override + protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, int[] emptyActivations, boolean accept) { + return this.codes.add(stateCoderResult.modelCode()); + } + + @Override + public void resolveOneSymmetry() { + throw new IllegalArgumentException("This equivalence storage is not prepared to resolve symmetries!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java new file mode 100644 index 00000000..249b22da --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.ObjectiveValues; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.map.Version; + +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Random; + +public class ObjectivePriorityQueueImpl implements ObjectivePriorityQueue { + public static final Comparator c1 = (o1, o2) -> Double.compare( + ((ObjectiveValues.ObjectiveValue1) o1.objectiveValue()).value0(), + ((ObjectiveValues.ObjectiveValue1) o2.objectiveValue()).value0()); + // TODO: support multi objective! + final PriorityQueue priorityQueue; + + public ObjectivePriorityQueueImpl(List objectives) { + + if(objectives.size() == 1) { + this.priorityQueue = new PriorityQueue<>(c1); + } else { + throw new UnsupportedOperationException("Only single objective comparator is implemented currently!"); + } + } + @Override + public Comparator getComparator() { + return c1; + } + + @Override + public void submit(VersionWithObjectiveValue versionWithObjectiveValue) { + priorityQueue.add(versionWithObjectiveValue); + } + + @Override + public void remove(VersionWithObjectiveValue versionWithObjectiveValue) { + priorityQueue.remove(versionWithObjectiveValue); + } + + @Override + public int getSize() { + return priorityQueue.size(); + } + + @Override + public VersionWithObjectiveValue getBest() { + var best = priorityQueue.peek(); + if (best != null) { + return best; + } else { + throw new IllegalArgumentException("The objective store is empty!"); + } + } + + @Override + public VersionWithObjectiveValue getRandom(Random random) { + int randomPosition = random.nextInt(getSize()); + for (VersionWithObjectiveValue entry : this.priorityQueue) { + if (randomPosition-- == 0) { + return entry; + } + } + throw new IllegalStateException("The priority queue is inconsistent!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java new file mode 100644 index 00000000..cc48864f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +public class SolutionStoreImpl implements SolutionStore { + final int maxNumberSolutions; + public static final int UNLIMITED = -1; + final SortedSet solutions; + + public SolutionStoreImpl(int maxNumberSolutions) { + this.maxNumberSolutions = maxNumberSolutions; + solutions = new TreeSet<>(ObjectivePriorityQueueImpl.c1); + } + + + @Override + public synchronized boolean submit(VersionWithObjectiveValue version) { + boolean removeLast = hasEnoughSolution(); + solutions.add(version); + if(removeLast) { + var last = solutions.last(); + solutions.remove(last); + return last != version; + } else { + return true; + } + } + + @Override + public List getSolutions() { + return new ArrayList<>(solutions); + } + + @Override + public boolean hasEnoughSolution() { + if (maxNumberSolutions == UNLIMITED) { + return false; + } else { + return solutions.size() >= maxNumberSolutions; + } + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 225de32e..b0b416e1 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -8,13 +8,13 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; @@ -282,5 +282,5 @@ class CRAExamplesTest { var states = dseAdapter.explore(); System.out.println("states size: " + states.size()); } - +*/ } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index c6da896c..461d9353 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -8,11 +8,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 91e33f4a..083f7ec3 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -6,10 +6,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.BestFirstStrategy; import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 5d24d712..1672b63a 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -7,10 +7,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java new file mode 100644 index 00000000..e7960a06 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class ActivationUnitTest { + private final static int SMALL_SIZE = 5; + + private static Stream entries() { + return Stream.of( + new ActivationStoreBitVectorEntry(SMALL_SIZE), + new ActivationStoreListEntry(SMALL_SIZE)); + } + + void addTest(ActivationStoreEntry entry, int elementsAdded) { + Assertions.assertEquals(elementsAdded,entry.getNumberOfVisitedActivations()); + Assertions.assertEquals(SMALL_SIZE-elementsAdded,entry.getNumberOfUnvisitedActivations()); + } + + @ParameterizedTest + @MethodSource("entries") + void testDifferent(ActivationStoreEntry entry) { + int elementsAdded = 0; + addTest(entry,elementsAdded); + Assertions.assertEquals(2, entry.getAndAddActivationAfter(2)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(3,entry.getAndAddActivationAfter(3)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(1,entry.getAndAddActivationAfter(1)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(4,entry.getAndAddActivationAfter(4)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(0,entry.getAndAddActivationAfter(0)); + addTest(entry,++elementsAdded); + } + + @ParameterizedTest + @MethodSource("entries") + void testSame(ActivationStoreEntry entry) { + int elementsAdded = 0; + addTest(entry,elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + } + + @ParameterizedTest + @MethodSource("entries") + void testFilling(ActivationStoreEntry entry) { + int elementsAdded = 0; + while(elementsAdded < SMALL_SIZE) { + entry.getAndAddActivationAfter(2); + elementsAdded++; + } + Assertions.assertThrows(IllegalArgumentException.class,()-> entry.getAndAddActivationAfter(2)); + } +} -- cgit v1.2.3-54-g00ecf From a00a2a961accd8ebbe34c3c1e50b6fa782e86ca4 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 07:59:17 +0200 Subject: restructured DSE framework, failing build --- .../internal/CompleteEquivalenceClassStore.java | 7 +- .../tools/refinery/store/dse/CRAExamplesTest.java | 289 +++-- .../java/tools/refinery/store/dse/DebugTest.java | 181 ++-- .../store/dse/DesignSpaceExplorationTest.java | 1128 ++++++++++---------- .../refinery/store/dse/TransformationRuleTest.java | 808 +++++++------- 5 files changed, 1188 insertions(+), 1225 deletions(-) (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java index 1555c3be..925e09a3 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java @@ -7,9 +7,6 @@ package tools.refinery.store.dse.transition.statespace.internal; import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; -import tools.refinery.store.dse.transition.statespace.ActivationStore; -import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; -import tools.refinery.store.dse.transition.statespace.SolutionStore; import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.statecoding.StateCoderStoreAdapter; @@ -68,8 +65,8 @@ public abstract class CompleteEquivalenceClassStore extends AbstractEquivalenceC if (unresolvedSimilarity == null) { return; } - var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0), - unresolvedSimilarity.get(1)); + var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0).version(), + unresolvedSimilarity.get(1).version()); if (outcome != StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC) { delegate(unresolvedSimilarity.get(1), unresolvedSimilarity.activationSizes, unresolvedSimilarity.accept); } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index b0b416e1..36517709 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,29 +5,20 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; import java.util.List; import static tools.refinery.store.query.literal.Literals.not; class CRAExamplesTest { + private static final Symbol name = Symbol.of("Name", 1, String.class); // private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -130,42 +121,42 @@ class CRAExamplesTest { }); }); - private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { -// var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var classElement = activation.get(0); - -// classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - dseAdapter.deleteObject(Tuple.of(classElement)); - }); - }); - - private static final TransformationRule createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classElementInterpretation = model.getInterpretation(classElement); -// var classesInterpretation = model.getInterpretation(classes); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var feature = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - classElementInterpretation.put(newClassElement, true); -// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); - }); - }); +// private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +//// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// // TODO: can we move dseAdapter outside? +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// var modelElement = activation.get(0); +// var classElement = activation.get(0); +// +//// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// dseAdapter.deleteObject(Tuple.of(classElement)); +// }); +// }); + +// private static final TransformationRule createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classElementInterpretation = model.getInterpretation(classElement); +//// var classesInterpretation = model.getInterpretation(classes); +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// // TODO: can we move dseAdapter outside? +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// var modelElement = activation.get(0); +// var feature = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// classElementInterpretation.put(newClassElement, true); +//// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); +// }); +// }); private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", moveFeaturePrecondition, @@ -181,106 +172,106 @@ class CRAExamplesTest { }); }); - @Test - @Disabled("This test is only for debugging purposes") - void craTest() { - var store = ModelStore.builder() - .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, - functionalDependency, name) - .with(ViatraModelQueryAdapter.builder() - .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, - deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, - moveFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") - .withFormat(FileFormat.DOT) - .withFormat(FileFormat.SVG) - .saveStates() - .saveDesignSpace() - ) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) - .objectives(new AlwaysSatisfiedRandomHardObjective()) -// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() - .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - -// var modelInterpretation = model.getInterpretation(classModel); - var nameInterpretation = model.getInterpretation(name); - var methodInterpretation = model.getInterpretation(method); - var attributeInterpretation = model.getInterpretation(attribute); - var dataDependencyInterpretation = model.getInterpretation(dataDependency); - var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); - -// var modelElement = dseAdapter.createObject(); - var method1 = dseAdapter.createObject(); - var method1Id = method1.get(0); - var method2 = dseAdapter.createObject(); - var method2Id = method2.get(0); - var method3 = dseAdapter.createObject(); - var method3Id = method3.get(0); - var method4 = dseAdapter.createObject(); - var method4Id = method4.get(0); - var attribute1 = dseAdapter.createObject(); - var attribute1Id = attribute1.get(0); - var attribute2 = dseAdapter.createObject(); - var attribute2Id = attribute2.get(0); - var attribute3 = dseAdapter.createObject(); - var attribute3Id = attribute3.get(0); - var attribute4 = dseAdapter.createObject(); - var attribute4Id = attribute4.get(0); - var attribute5 = dseAdapter.createObject(); - var attribute5Id = attribute5.get(0); - - nameInterpretation.put(method1, "M1"); - nameInterpretation.put(method2, "M2"); - nameInterpretation.put(method3, "M3"); - nameInterpretation.put(method4, "M4"); - nameInterpretation.put(attribute1, "A1"); - nameInterpretation.put(attribute2, "A2"); - nameInterpretation.put(attribute3, "A3"); - nameInterpretation.put(attribute4, "A4"); - nameInterpretation.put(attribute5, "A5"); - - - -// modelInterpretation.put(modelElement, true); - methodInterpretation.put(method1, true); - methodInterpretation.put(method2, true); - methodInterpretation.put(method3, true); - methodInterpretation.put(method4, true); - attributeInterpretation.put(attribute1, true); - attributeInterpretation.put(attribute2, true); - attributeInterpretation.put(attribute3, true); - attributeInterpretation.put(attribute4, true); - attributeInterpretation.put(attribute5, true); - - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); - - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); - - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - System.out.println("states size: " + states.size()); - } -*/ +// @Test +// @Disabled("This test is only for debugging purposes") +// void craTest() { +// var store = ModelStore.builder() +// .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, +// functionalDependency, name) +// .with(ViatraModelQueryAdapter.builder() +// .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, +// deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, +// moveFeaturePrecondition)) +// .with(ModelVisualizerAdapter.builder() +// .withOutputpath("test_output") +// .withFormat(FileFormat.DOT) +// .withFormat(FileFormat.SVG) +// .saveStates() +// .saveDesignSpace() +// ) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) +// .objectives(new AlwaysSatisfiedRandomHardObjective()) +//// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() +// .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() +//// .goOnOnlyIfFitnessIsBetter() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// dseAdapter.setRandom(1); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +//// var modelInterpretation = model.getInterpretation(classModel); +// var nameInterpretation = model.getInterpretation(name); +// var methodInterpretation = model.getInterpretation(method); +// var attributeInterpretation = model.getInterpretation(attribute); +// var dataDependencyInterpretation = model.getInterpretation(dataDependency); +// var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); +// +//// var modelElement = dseAdapter.createObject(); +// var method1 = dseAdapter.createObject(); +// var method1Id = method1.get(0); +// var method2 = dseAdapter.createObject(); +// var method2Id = method2.get(0); +// var method3 = dseAdapter.createObject(); +// var method3Id = method3.get(0); +// var method4 = dseAdapter.createObject(); +// var method4Id = method4.get(0); +// var attribute1 = dseAdapter.createObject(); +// var attribute1Id = attribute1.get(0); +// var attribute2 = dseAdapter.createObject(); +// var attribute2Id = attribute2.get(0); +// var attribute3 = dseAdapter.createObject(); +// var attribute3Id = attribute3.get(0); +// var attribute4 = dseAdapter.createObject(); +// var attribute4Id = attribute4.get(0); +// var attribute5 = dseAdapter.createObject(); +// var attribute5Id = attribute5.get(0); +// +// nameInterpretation.put(method1, "M1"); +// nameInterpretation.put(method2, "M2"); +// nameInterpretation.put(method3, "M3"); +// nameInterpretation.put(method4, "M4"); +// nameInterpretation.put(attribute1, "A1"); +// nameInterpretation.put(attribute2, "A2"); +// nameInterpretation.put(attribute3, "A3"); +// nameInterpretation.put(attribute4, "A4"); +// nameInterpretation.put(attribute5, "A5"); +// +// +// +//// modelInterpretation.put(modelElement, true); +// methodInterpretation.put(method1, true); +// methodInterpretation.put(method2, true); +// methodInterpretation.put(method3, true); +// methodInterpretation.put(method4, true); +// attributeInterpretation.put(attribute1, true); +// attributeInterpretation.put(attribute2, true); +// attributeInterpretation.put(attribute3, true); +// attributeInterpretation.put(attribute4, true); +// attributeInterpretation.put(attribute5, true); +// +// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); +// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); +// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); +// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); +// +// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); +// +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// System.out.println("states size: " + states.size()); +// } +//*/ } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 461d9353..1d757a5f 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -5,22 +5,9 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -42,88 +29,88 @@ class DebugTest { private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - @Test - @Disabled("This test is only for debugging purposes") - void BFSTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") - .withFormat(FileFormat.DOT) - .withFormat(FileFormat.SVG) - .saveStates() - .saveDesignSpace() - ) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .objectives(new AlwaysSatisfiedRandomHardObjective()) - .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var modelElement = dseAdapter.createObject(); - modelElementInterpretation.put(modelElement, true); - classElementInterpretation.put(modelElement, true); - queryEngine.flushChanges(); - - - var states = dseAdapter.explore(); - System.out.println("states size: " + states.size()); - - } +// @Test +// @Disabled("This test is only for debugging purposes") +// void BFSTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(ModelVisualizerAdapter.builder() +// .withOutputpath("test_output") +// .withFormat(FileFormat.DOT) +// .withFormat(FileFormat.SVG) +// .saveStates() +// .saveDesignSpace() +// ) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .objectives(new AlwaysSatisfiedRandomHardObjective()) +// .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +//// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +//// .goOnOnlyIfFitnessIsBetter() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// dseAdapter.setRandom(1); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var modelElement = dseAdapter.createObject(); +// modelElementInterpretation.put(modelElement, true); +// classElementInterpretation.put(modelElement, true); +// queryEngine.flushChanges(); +// +// +// var states = dseAdapter.explore(); +// System.out.println("states size: " + states.size()); +// +// } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 083f7ec3..f5f13433 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -5,21 +5,9 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; - -import static org.junit.jupiter.api.Assertions.assertEquals; class DesignSpaceExplorationTest { // private static final Symbol namedElement = Symbol.of("NamedElement", 1); @@ -46,563 +34,563 @@ class DesignSpaceExplorationTest { private static final AnySymbolView featuresView = new KeyOnlyView<>(features); private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - @Test - void createObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - assertEquals(0, newModelId); - assertEquals(1, newClass1Id); - assertEquals(2, newClass2Id); - assertEquals(3, newFieldId); - assertEquals(4, dseAdapter.getModelSize()); - } - - @Test - void deleteMiddleObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy())) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newObject0 = dseAdapter.createObject(); - var newObject0Id = newObject0.get(0); - var newObject1 = dseAdapter.createObject(); - var newObject1Id = newObject1.get(0); - var newObject2 = dseAdapter.createObject(); - var newObject2Id = newObject2.get(0); - var newObject3 = dseAdapter.createObject(); - var newObject3Id = newObject3.get(0); - - assertEquals(0, newObject0Id); - assertEquals(1, newObject1Id); - assertEquals(2, newObject2Id); - assertEquals(3, newObject3Id); - assertEquals(4, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject1); - assertEquals(4, dseAdapter.getModelSize()); - - var newObject4 = dseAdapter.createObject(); - var newObject4Id = newObject4.get(0); - assertEquals(4, newObject4Id); - assertEquals(5, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject4); - assertEquals(5, dseAdapter.getModelSize()); - } - - @Test - void DFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void DFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy().withDepthLimit(0) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void DFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(5, states.size()); - } - - @Test - void DFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - - @Test - void DFSSolutionLimitTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy().withSolutionLimit(222) - .continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(222, states.size()); - } - - @Test - void BeFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new BestFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void BeFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy().withDepthLimit(4) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void BeFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(5, states.size()); - } - - @Test - void BeFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - - @Test - void BeFSSolutionLimitTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy().withSolutionLimit(222) - .continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(222, states.size()); - } +// @Test +// void createObjectTest() { +// var store = ModelStore.builder() +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// assertEquals(0, dseAdapter.getModelSize()); +// +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// assertEquals(0, newModelId); +// assertEquals(1, newClass1Id); +// assertEquals(2, newClass2Id); +// assertEquals(3, newFieldId); +// assertEquals(4, dseAdapter.getModelSize()); +// } + +// @Test +// void deleteMiddleObjectTest() { +// var store = ModelStore.builder() +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy())) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// assertEquals(0, dseAdapter.getModelSize()); +// +// var newObject0 = dseAdapter.createObject(); +// var newObject0Id = newObject0.get(0); +// var newObject1 = dseAdapter.createObject(); +// var newObject1Id = newObject1.get(0); +// var newObject2 = dseAdapter.createObject(); +// var newObject2Id = newObject2.get(0); +// var newObject3 = dseAdapter.createObject(); +// var newObject3Id = newObject3.get(0); +// +// assertEquals(0, newObject0Id); +// assertEquals(1, newObject1Id); +// assertEquals(2, newObject2Id); +// assertEquals(3, newObject3Id); +// assertEquals(4, dseAdapter.getModelSize()); +// +// dseAdapter.deleteObject(newObject1); +// assertEquals(4, dseAdapter.getModelSize()); +// +// var newObject4 = dseAdapter.createObject(); +// var newObject4Id = newObject4.get(0); +// assertEquals(4, newObject4Id); +// assertEquals(5, dseAdapter.getModelSize()); +// +// dseAdapter.deleteObject(newObject4); +// assertEquals(5, dseAdapter.getModelSize()); +// } +// +// @Test +// void DFSTrivialTest() { +// var store = ModelStore.builder() +// .symbols(classModel) +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void DFSOneRuleTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(0) +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void DFSContinueTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(5, states.size()); +// } +// +// @Test +// void DFSCompletenessTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(2047, states.size()); +// } +// +// @Test +// void DFSSolutionLimitTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new DepthFirstStrategy().withSolutionLimit(222) +// .continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(222, states.size()); +// } +// +// @Test +// void BeFSTrivialTest() { +// var store = ModelStore.builder() +// .symbols(classModel) +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new BestFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void BeFSOneRuleTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new BestFirstStrategy().withDepthLimit(4) +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void BeFSContinueTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(5, states.size()); +// } +// +// @Test +// void BeFSCompletenessTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(2047, states.size()); +// } +// +// @Test +// void BeFSSolutionLimitTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new BestFirstStrategy().withSolutionLimit(222) +// .continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(222, states.size()); +// } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 1672b63a..43b04e0d 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -6,409 +6,409 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; - -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; - +// +//import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +//import tools.refinery.store.model.ModelStore; +//import tools.refinery.store.query.ModelQueryAdapter; +//import tools.refinery.store.query.dnf.Query; +//import tools.refinery.store.dse.transition.TransformationRule; +//import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +//import tools.refinery.store.query.view.AnySymbolView; +//import tools.refinery.store.query.view.KeyOnlyView; +//import tools.refinery.store.representation.Symbol; +//import tools.refinery.store.tuple.Tuple; +// +//import java.util.List; +//import java.util.Map; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static tools.refinery.store.query.literal.Literals.not; +//import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; +// class TransformationRuleTest { - - private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); - - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - @Test - void activationsTest() { - var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", - (builder, model, c, f) -> builder.clause( - classElementView.call(c), - classesView.call(model, c), - encapsulatesView.call(c, f) - )); - - var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) - -> builder.clause((model, c1) -> List.of( - classModelView.call(model), - featureView.call(f), - classElementView.call(c2), - featuresView.call(model, f), - classesView.call(model, c1), - not(assignFeaturePreconditionHelper.call(model, c2, f)), - not(encapsulatesView.call(c2, f)) - ))); - - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { - var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - - isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - }); - }); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, - deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - assignFeatureRule.prepare(model, queryEngine); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet(); - var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet(); - - assertResults(Map.of( - Tuple.of(newClass1Id, newFieldId), true, - Tuple.of(newClass2Id, newFieldId), true - ), assignFeatureRuleActivations); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRuleActivations); - } - - @Test - void randomActivationTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 78634L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule0.prepare(model, queryEngine); - deleteEmptyClassRule1.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - - var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); - var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule0.getAllActivationsAsResultSet()); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule1.getAllActivationsAsResultSet()); - - assertEquals(Tuple.of(newModelId, newClass2Id), activation0); - assertEquals(Tuple.of(newModelId, newClass1Id), activation1); - - } - - @Test - void fireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - - deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - } - - @Test - void randomFireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - } +// +// private static final Symbol classModel = Symbol.of("ClassModel", 1); +// private static final Symbol classElement = Symbol.of("ClassElement", 1); +// private static final Symbol feature = Symbol.of("Feature", 1); +// +// private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); +// private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); +// +// private static final Symbol features = Symbol.of("Features", 2); +// private static final Symbol classes = Symbol.of("Classes", 2); +// +// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); +// private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); +// private static final AnySymbolView featureView = new KeyOnlyView<>(feature); +// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); +// private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); +// private static final AnySymbolView featuresView = new KeyOnlyView<>(features); +// private static final AnySymbolView classesView = new KeyOnlyView<>(classes); +// +// @Test +// void activationsTest() { +// var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", +// (builder, model, c, f) -> builder.clause( +// classElementView.call(c), +// classesView.call(model, c), +// encapsulatesView.call(c, f) +// )); +// +// var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) +// -> builder.clause((model, c1) -> List.of( +// classModelView.call(model), +// featureView.call(f), +// classElementView.call(c2), +// featuresView.call(model, f), +// classesView.call(model, c1), +// not(assignFeaturePreconditionHelper.call(model, c2, f)), +// not(encapsulatesView.call(c2, f)) +// ))); +// +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", +// assignFeaturePrecondition, +// (model) -> { +// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); +// return ((Tuple activation) -> { +// var feature = activation.get(0); +// var classElement = activation.get(1); +// +// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); +// }); +// }); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }); +// +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, +// deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// assignFeatureRule.prepare(model, queryEngine); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet(); +// var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet(); +// +// assertResults(Map.of( +// Tuple.of(newClass1Id, newFieldId), true, +// Tuple.of(newClass2Id, newFieldId), true +// ), assignFeatureRuleActivations); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRuleActivations); +// } +// +// @Test +// void randomActivationTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 0L); +// +// TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 78634L); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule0.prepare(model, queryEngine); +// deleteEmptyClassRule1.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// +// var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); +// var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule0.getAllActivationsAsResultSet()); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule1.getAllActivationsAsResultSet()); +// +// assertEquals(Tuple.of(newModelId, newClass2Id), activation0); +// assertEquals(Tuple.of(newModelId, newClass1Id), activation1); +// +// } +// +// @Test +// void fireTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// +// deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), false, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// } +// +// @Test +// void randomFireTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 0L); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// deleteEmptyClassRule.fireRandomActivation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), false +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// deleteEmptyClassRule.fireRandomActivation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), false, +// Tuple.of(newModelId, newClass2Id), false +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// } } -- cgit v1.2.3-54-g00ecf From b65094147cfa2fc46f37916c2f5b52e723a6db34 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 20:04:37 +0200 Subject: fixed numerous issues with activation coder --- .../internal/ActivationStoreBitVectorEntry.java | 7 +- .../statespace/internal/ActivationStoreEntry.java | 8 +- .../internal/ActivationStoreListEntry.java | 35 ++++--- .../statespace/internal/ActivationUnitTest.java | 101 ++++++++++++++++----- 4 files changed, 110 insertions(+), 41 deletions(-) (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java index ba243d7d..24145d03 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java @@ -14,7 +14,7 @@ public class ActivationStoreBitVectorEntry extends ActivationStoreEntry { } @Override - public int getNumberOfUnvisitedActivations() { + public int getNumberOfVisitedActivations() { int visited = 0; for (int i : selected) { visited += Integer.bitCount(i); @@ -29,12 +29,13 @@ public class ActivationStoreBitVectorEntry extends ActivationStoreEntry { int position = index; do { final int selectedElement = position >> ELEMENT_POSITION; - final int selectedBit = position & ELEMENT_BITMASK; + final int selectedBit = 1<<(position & ELEMENT_BITMASK); + if((selected[selectedElement] & selectedBit) == 0) { selected[selectedElement] |= selectedBit; return position; } else { - if(position < this.numberOfActivations) { + if(position < this.numberOfActivations-1) { position++; } else { position = 0; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java index f69b234c..d7339805 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java @@ -12,11 +12,11 @@ public abstract class ActivationStoreEntry { this.numberOfActivations = numberOfActivations; } - public int getNumberOfVisitedActivations() { - return numberOfActivations; - } + public abstract int getNumberOfVisitedActivations(); - public abstract int getNumberOfUnvisitedActivations(); + public int getNumberOfUnvisitedActivations() { + return numberOfActivations - getNumberOfVisitedActivations(); + } public abstract int getAndAddActivationAfter(int index); // public abstract boolean contains(int activation) diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java index 9e25301f..3d4cbfdc 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java @@ -16,8 +16,8 @@ public class ActivationStoreListEntry extends ActivationStoreEntry { } @Override - public int getNumberOfUnvisitedActivations() { - return this.numberOfActivations - visitedActivations.size(); + public int getNumberOfVisitedActivations() { + return visitedActivations.size(); } @Override @@ -27,43 +27,52 @@ public class ActivationStoreListEntry extends ActivationStoreEntry { this.visitedActivations.add(index); return index; } + final int positionInSearch = getPosition(index); + int position = positionInSearch; - int position = getPosition(index); + // if the position is after the last, we can insert it at the end of the list + if(position == this.visitedActivations.size()) { + this.visitedActivations.add(index); + return index; + } else if(this.visitedActivations.get(position) != index) { + // If the index is not in the position, one can insert it - // If the index is not in the position, one can insert it - if(this.visitedActivations.get(position) != index) { this.visitedActivations.addAtIndex(position,index); return index; } // Otherwise, get the next empty space between two elements - while(position + 2 < this.visitedActivations.size()) { - position++; + while(position + 1 < this.visitedActivations.size()) { if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { - this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + int newElement = this.visitedActivations.get(position)+1; + this.visitedActivations.addAtIndex(position+1, newElement); + return newElement; } + position++; } // Otherwise, try to add to the last space int last = this.visitedActivations.get(this.visitedActivations.size()-1); - if(last 0) { - this.visitedActivations.add(0); + this.visitedActivations.addAtIndex(0,0); return 0; } // Otherwise, get the next empty space between two elements position = 0; - while(position + 2 < this.visitedActivations.size()) { - position++; + while(position < positionInSearch) { if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { - this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + int newElement = this.visitedActivations.get(position)+1; + this.visitedActivations.addAtIndex(position+1, newElement); + return newElement; } + position++; } throw new IllegalArgumentException("There is are no unvisited activations!"); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java index e7960a06..3a672b18 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java @@ -9,6 +9,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; import java.util.stream.Stream; class ActivationUnitTest { @@ -17,56 +22,110 @@ class ActivationUnitTest { private static Stream entries() { return Stream.of( new ActivationStoreBitVectorEntry(SMALL_SIZE), - new ActivationStoreListEntry(SMALL_SIZE)); + new ActivationStoreListEntry(SMALL_SIZE) + ); } void addTest(ActivationStoreEntry entry, int elementsAdded) { - Assertions.assertEquals(elementsAdded,entry.getNumberOfVisitedActivations()); - Assertions.assertEquals(SMALL_SIZE-elementsAdded,entry.getNumberOfUnvisitedActivations()); + Assertions.assertEquals(elementsAdded, entry.getNumberOfVisitedActivations()); + Assertions.assertEquals(SMALL_SIZE - elementsAdded, entry.getNumberOfUnvisitedActivations()); } @ParameterizedTest @MethodSource("entries") void testDifferent(ActivationStoreEntry entry) { int elementsAdded = 0; - addTest(entry,elementsAdded); + addTest(entry, elementsAdded); Assertions.assertEquals(2, entry.getAndAddActivationAfter(2)); - addTest(entry,++elementsAdded); - Assertions.assertEquals(3,entry.getAndAddActivationAfter(3)); - addTest(entry,++elementsAdded); - Assertions.assertEquals(1,entry.getAndAddActivationAfter(1)); - addTest(entry,++elementsAdded); - Assertions.assertEquals(4,entry.getAndAddActivationAfter(4)); - addTest(entry,++elementsAdded); - Assertions.assertEquals(0,entry.getAndAddActivationAfter(0)); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); + Assertions.assertEquals(3, entry.getAndAddActivationAfter(3)); + addTest(entry, ++elementsAdded); + Assertions.assertEquals(1, entry.getAndAddActivationAfter(1)); + addTest(entry, ++elementsAdded); + Assertions.assertEquals(4, entry.getAndAddActivationAfter(4)); + addTest(entry, ++elementsAdded); + Assertions.assertEquals(0, entry.getAndAddActivationAfter(0)); + addTest(entry, ++elementsAdded); } + @ParameterizedTest @MethodSource("entries") void testSame(ActivationStoreEntry entry) { int elementsAdded = 0; - addTest(entry,elementsAdded); + addTest(entry, 0); entry.getAndAddActivationAfter(2); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); entry.getAndAddActivationAfter(2); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); entry.getAndAddActivationAfter(2); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); entry.getAndAddActivationAfter(2); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); entry.getAndAddActivationAfter(2); - addTest(entry,++elementsAdded); + addTest(entry, ++elementsAdded); } @ParameterizedTest @MethodSource("entries") void testFilling(ActivationStoreEntry entry) { int elementsAdded = 0; - while(elementsAdded < SMALL_SIZE) { + while (elementsAdded < SMALL_SIZE) { entry.getAndAddActivationAfter(2); elementsAdded++; } - Assertions.assertThrows(IllegalArgumentException.class,()-> entry.getAndAddActivationAfter(2)); + Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2)); + } + + void randomDifferentTestCase(ActivationStoreEntry entry, int seed) { + List elements = new ArrayList<>(SMALL_SIZE); + for (int i = 0; i < SMALL_SIZE; i++) { + elements.add(i); + } + @SuppressWarnings("squid:S2245") + var random = new Random(seed); + Collections.shuffle(elements, random); + + for (int element : elements) { + entry.getAndAddActivationAfter(element); + } + Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2)); + } + + private static final int fuzzNumber = 20; + + @ParameterizedTest + @MethodSource("entryFactories") + void randomDifferentTest(Supplier entry) { + for (int i = 0; i < fuzzNumber; i++) { + randomDifferentTestCase(entry.get(), i); + } + } + + void randomSameTestCase(ActivationStoreEntry entry, int seed) { + + @SuppressWarnings("squid:S2245") + var random = new Random(seed); + + for (int i = 0; i < SMALL_SIZE; i++) { + entry.getAndAddActivationAfter(random.nextInt(SMALL_SIZE)); + } + + Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2)); + } + + @ParameterizedTest + @MethodSource("entryFactories") + void randomSameTest(Supplier entry) { + for (int i = 0; i < fuzzNumber; i++) { + randomSameTestCase(entry.get(), i); + } + } + + private static Stream> entryFactories() { + return Stream.of( + () -> new ActivationStoreBitVectorEntry(SMALL_SIZE), + () -> new ActivationStoreListEntry(SMALL_SIZE) + ); } } -- cgit v1.2.3-54-g00ecf From 2809374210839fb72f455d846cd483c711b5dcb0 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 5 Sep 2023 20:49:41 +0200 Subject: Add minor improvements and fixes for explorer --- .../internal/ModificationStoreAdapterImpl.java | 2 +- .../store/dse/strategy/BestFirstExplorer.java | 13 +- .../store/dse/strategy/BestFirstStoreManager.java | 4 +- .../store/dse/strategy/BestFirstWorker.java | 7 +- .../tools/refinery/store/dse/CRAExamplesTest.java | 283 +++++++++++---------- .../refinery/store/dse/tests/DummyCriterion.java | 17 ++ .../refinery/store/dse/tests/DummyObjective.java | 13 + 7 files changed, 194 insertions(+), 145 deletions(-) create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java index 913cb33f..62e4227b 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java @@ -24,6 +24,6 @@ public class ModificationStoreAdapterImpl implements ModificationStoreAdapter { @Override public ModelAdapter createModelAdapter(Model model) { - return null; + return new ModificationAdapterImpl(this, model); } } 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 72bbbc55..03a7fca3 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 @@ -5,7 +5,6 @@ */ package tools.refinery.store.dse.strategy; -import tools.refinery.store.dse.transition.ObjectiveValue; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.model.Model; @@ -32,13 +31,11 @@ public class BestFirstExplorer extends BestFirstWorker { public void explore() { VersionWithObjectiveValue lastVisited = submit().newVersion(); - mainLoop: while (shouldRun()) { + while (shouldRun()) { if (lastVisited == null) { - var restored = this.restoreToBest(); - if(restored != null) { - lastVisited = restored; - } else { + lastVisited = this.restoreToBest(); + if(lastVisited == null) { return; } } @@ -47,7 +44,7 @@ public class BestFirstExplorer extends BestFirstWorker { while(tryActivation && shouldRun()) { RandomVisitResult randomVisitResult = this.visitRandomUnvisited(random); - tryActivation &= randomVisitResult.shouldRetry(); + tryActivation = randomVisitResult.shouldRetry(); var newSubmit = randomVisitResult.submitResult(); if(newSubmit != null) { if(!newSubmit.include()) { @@ -57,7 +54,7 @@ public class BestFirstExplorer extends BestFirstWorker { int compareResult = compare(lastVisited,newVisit); if(compareResult >= 0) { lastVisited = newVisit; - continue mainLoop; + break; } } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java index d1fd7884..3795daa4 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -35,7 +35,7 @@ public class BestFirstStoreManager { objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); Procedure whenAllActivationsVisited = x -> objectiveStore.remove(x); activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); - solutionStore = new SolutionStoreImpl(1); + solutionStore = new SolutionStoreImpl(10); equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { @Override protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { @@ -56,7 +56,7 @@ public class BestFirstStoreManager { return activationStore; } - SolutionStore getSolutionStore() { + public SolutionStore getSolutionStore() { return solutionStore; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index ea7fe43f..4240fdf9 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -13,7 +13,6 @@ import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.StateCoderAdapter; - import java.util.Random; public class BestFirstWorker { @@ -31,6 +30,7 @@ public class BestFirstWorker { stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), explorationAdapter.getTransformations()); + } private VersionWithObjectiveValue last = null; @@ -48,12 +48,9 @@ public class BestFirstWorker { var res = new VersionWithObjectiveValue(version, objectiveValue); var code = stateCoderAdapter.calculateStateCode(); var accepted = explorationAdapter.checkAccept(); - boolean isNew = storeManager.getEquivalenceClassStore().submit(res, code, activationStoreWorker.calculateEmptyActivationSize(), accepted); - - last = new VersionWithObjectiveValue(version, objectiveValue); - return new SubmitResult(isNew, accepted, objectiveValue, last); + return new SubmitResult(isNew, accepted, objectiveValue, isNew ? res : null); } public void restoreToLast() { diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 36517709..f4c38b18 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,13 +5,28 @@ */ package tools.refinery.store.dse; -import tools.refinery.store.dse.transition.TransformationRule; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.strategy.BestFirstStoreManager; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.tests.DummyCriterion; +import tools.refinery.store.dse.tests.DummyObjective; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.FunctionalQuery; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.query.term.Variable; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderAdapter; import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; import java.util.List; @@ -121,42 +136,42 @@ class CRAExamplesTest { }); }); -// private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", -// deleteEmptyClassPrecondition, -// (model) -> { -//// var classesInterpretation = model.getInterpretation(classes); -// var classElementInterpretation = model.getInterpretation(classElement); -// return ((Tuple activation) -> { -// // TODO: can we move dseAdapter outside? -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -//// var modelElement = activation.get(0); -// var classElement = activation.get(0); -// -//// classesInterpretation.put(Tuple.of(modelElement, classElement), false); -// classElementInterpretation.put(Tuple.of(classElement), false); -// dseAdapter.deleteObject(Tuple.of(classElement)); -// }); -// }); - -// private static final TransformationRule createClassRule = new TransformationRule("CreateClass", -// createClassPrecondition, -// (model) -> { -// var classElementInterpretation = model.getInterpretation(classElement); -//// var classesInterpretation = model.getInterpretation(classes); -// var encapsulatesInterpretation = model.getInterpretation(encapsulates); -// return ((Tuple activation) -> { -// // TODO: can we move dseAdapter outside? -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -//// var modelElement = activation.get(0); -// var feature = activation.get(0); -// -// var newClassElement = dseAdapter.createObject(); -// var newClassElementId = newClassElement.get(0); -// classElementInterpretation.put(newClassElement, true); -//// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); -// encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); -// }); -// }); + private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { +// var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + // TODO: can we move modificationAdapter outside? + var modificationAdapter = model.getAdapter(ModificationAdapter.class); +// var modelElement = activation.get(0); + var classElement = activation.get(0); + +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + modificationAdapter.deleteObject(Tuple.of(classElement)); + }); + }); + + private static final TransformationRule createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classElementInterpretation = model.getInterpretation(classElement); +// var classesInterpretation = model.getInterpretation(classes); + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + return ((Tuple activation) -> { + // TODO: can we move modificationAdapter outside? + var modificationAdapter = model.getAdapter(ModificationAdapter.class); +// var modelElement = activation.get(0); + var feature = activation.get(0); + + var newClassElement = modificationAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + classElementInterpretation.put(newClassElement, true); +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); + }); + }); private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", moveFeaturePrecondition, @@ -172,16 +187,16 @@ class CRAExamplesTest { }); }); -// @Test + @Test // @Disabled("This test is only for debugging purposes") -// void craTest() { -// var store = ModelStore.builder() -// .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, -// functionalDependency, name) -// .with(ViatraModelQueryAdapter.builder() -// .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, -// deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, -// moveFeaturePrecondition)) + void craTest() { + var store = ModelStore.builder() + .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, + functionalDependency, name) + .with(ViatraModelQueryAdapter.builder() + .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, + deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, + moveFeaturePrecondition)) // .with(ModelVisualizerAdapter.builder() // .withOutputpath("test_output") // .withFormat(FileFormat.DOT) @@ -189,89 +204,99 @@ class CRAExamplesTest { // .saveStates() // .saveDesignSpace() // ) -// .with(DesignSpaceExplorationAdapter.builder() -// .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) + .with(StateCoderAdapter.builder()) + .with(ModificationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) + .objectives(new DummyObjective()) + .accept(new DummyCriterion(true)) + .exclude(new DummyCriterion(false)) // .objectives(new AlwaysSatisfiedRandomHardObjective()) -//// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() +// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() // .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() -//// .goOnOnlyIfFitnessIsBetter() -// )) -// .build(); -// -// var model = store.createEmptyModel(); -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -//// dseAdapter.setRandom(1); -// var queryEngine = model.getAdapter(ModelQueryAdapter.class); -// -//// var modelInterpretation = model.getInterpretation(classModel); -// var nameInterpretation = model.getInterpretation(name); -// var methodInterpretation = model.getInterpretation(method); -// var attributeInterpretation = model.getInterpretation(attribute); -// var dataDependencyInterpretation = model.getInterpretation(dataDependency); -// var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); -// -//// var modelElement = dseAdapter.createObject(); -// var method1 = dseAdapter.createObject(); -// var method1Id = method1.get(0); -// var method2 = dseAdapter.createObject(); -// var method2Id = method2.get(0); -// var method3 = dseAdapter.createObject(); -// var method3Id = method3.get(0); -// var method4 = dseAdapter.createObject(); -// var method4Id = method4.get(0); -// var attribute1 = dseAdapter.createObject(); -// var attribute1Id = attribute1.get(0); -// var attribute2 = dseAdapter.createObject(); -// var attribute2Id = attribute2.get(0); -// var attribute3 = dseAdapter.createObject(); -// var attribute3Id = attribute3.get(0); -// var attribute4 = dseAdapter.createObject(); -// var attribute4Id = attribute4.get(0); -// var attribute5 = dseAdapter.createObject(); -// var attribute5Id = attribute5.get(0); -// -// nameInterpretation.put(method1, "M1"); -// nameInterpretation.put(method2, "M2"); -// nameInterpretation.put(method3, "M3"); -// nameInterpretation.put(method4, "M4"); -// nameInterpretation.put(attribute1, "A1"); -// nameInterpretation.put(attribute2, "A2"); -// nameInterpretation.put(attribute3, "A3"); -// nameInterpretation.put(attribute4, "A4"); -// nameInterpretation.put(attribute5, "A5"); -// -// -// -//// modelInterpretation.put(modelElement, true); -// methodInterpretation.put(method1, true); -// methodInterpretation.put(method2, true); -// methodInterpretation.put(method3, true); -// methodInterpretation.put(method4, true); -// attributeInterpretation.put(attribute1, true); -// attributeInterpretation.put(attribute2, true); -// attributeInterpretation.put(attribute3, true); -// attributeInterpretation.put(attribute4, true); -// attributeInterpretation.put(attribute5, true); -// -// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); -// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); -// dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); -// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); -// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); -// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); -// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); -// -// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); -// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); -// functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); -// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); -// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); -// functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); -// -// queryEngine.flushChanges(); -// -// var states = dseAdapter.explore(); -// System.out.println("states size: " + states.size()); -// } -//*/ +// .goOnOnlyIfFitnessIsBetter()) + ) + .build(); + + var model = store.createEmptyModel(); +// modificationAdapter.setRandom(1); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + +// var modelInterpretation = model.getInterpretation(classModel); + var nameInterpretation = model.getInterpretation(name); + var methodInterpretation = model.getInterpretation(method); + var attributeInterpretation = model.getInterpretation(attribute); + var dataDependencyInterpretation = model.getInterpretation(dataDependency); + var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); + + var modificationAdapter = model.getAdapter(ModificationAdapter.class); + +// var modelElement = modificationAdapter.createObject(); + var method1 = modificationAdapter.createObject(); + var method1Id = method1.get(0); + var method2 = modificationAdapter.createObject(); + var method2Id = method2.get(0); + var method3 = modificationAdapter.createObject(); + var method3Id = method3.get(0); + var method4 = modificationAdapter.createObject(); + var method4Id = method4.get(0); + var attribute1 = modificationAdapter.createObject(); + var attribute1Id = attribute1.get(0); + var attribute2 = modificationAdapter.createObject(); + var attribute2Id = attribute2.get(0); + var attribute3 = modificationAdapter.createObject(); + var attribute3Id = attribute3.get(0); + var attribute4 = modificationAdapter.createObject(); + var attribute4Id = attribute4.get(0); + var attribute5 = modificationAdapter.createObject(); + var attribute5Id = attribute5.get(0); + + nameInterpretation.put(method1, "M1"); + nameInterpretation.put(method2, "M2"); + nameInterpretation.put(method3, "M3"); + nameInterpretation.put(method4, "M4"); + nameInterpretation.put(attribute1, "A1"); + nameInterpretation.put(attribute2, "A2"); + nameInterpretation.put(attribute3, "A3"); + nameInterpretation.put(attribute4, "A4"); + nameInterpretation.put(attribute5, "A5"); + + + +// modelInterpretation.put(modelElement, true); + methodInterpretation.put(method1, true); + methodInterpretation.put(method2, true); + methodInterpretation.put(method3, true); + methodInterpretation.put(method4, true); + attributeInterpretation.put(attribute1, true); + attributeInterpretation.put(attribute2, true); + attributeInterpretation.put(attribute3, true); + attributeInterpretation.put(attribute4, true); + attributeInterpretation.put(attribute5, true); + + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); + + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); + + var initialVersion = model.commit(); + queryEngine.flushChanges(); + + var bestFirst = new BestFirstStoreManager(store); + bestFirst.startExploration(initialVersion); + var resultStore = bestFirst.getSolutionStore(); + System.out.println("states size: " + resultStore.getSolutions().size()); + var modelVisualizerAdapter = model.getAdapter(ModelVisualizerAdapter.class); + modelVisualizerAdapter.visualize(); + } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java new file mode 100644 index 00000000..6bcbd1c3 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java @@ -0,0 +1,17 @@ +package tools.refinery.store.dse.tests; + +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.model.Model; + +public class DummyCriterion implements Criterion { + protected final boolean returnValue; + public DummyCriterion(boolean returnValue) { + this.returnValue = returnValue; + } + + @Override + public CriterionCalculator createCalculator(Model model) { + return () -> returnValue; + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java new file mode 100644 index 00000000..77a23d62 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java @@ -0,0 +1,13 @@ +package tools.refinery.store.dse.tests; + +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; + +public class DummyObjective implements Objective { + + @Override + public ObjectiveCalculator createCalculator(Model model) { + return () -> {return 0d;}; + } +} -- cgit v1.2.3-54-g00ecf From 167417e5c917732c8f6fd5292ec398a45c08ef6b Mon Sep 17 00:00:00 2001 From: nagilooh Date: Wed, 6 Sep 2023 17:52:45 +0200 Subject: Fix numerous issues with DSE --- .../tools/refinery/store/dse/strategy/BestFirstExplorer.java | 4 ++++ .../tools/refinery/store/dse/strategy/BestFirstWorker.java | 6 +++--- .../transition/statespace/internal/ActivationStoreImpl.java | 6 +++--- .../statespace/internal/ObjectivePriorityQueueImpl.java | 7 +------ .../dse/transition/statespace/internal/SolutionStoreImpl.java | 11 +++++------ 5 files changed, 16 insertions(+), 18 deletions(-) (limited to 'subprojects/store-dse') 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 03a7fca3..f82d7c06 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 @@ -58,6 +58,10 @@ public class BestFirstExplorer extends BestFirstWorker { } } } + else { + lastVisited = null; + break; + } } //final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index 4240fdf9..b8662d8d 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -45,12 +45,12 @@ public class BestFirstWorker { Version version = model.commit(); ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); - var res = new VersionWithObjectiveValue(version, objectiveValue); + last = new VersionWithObjectiveValue(version, objectiveValue); var code = stateCoderAdapter.calculateStateCode(); var accepted = explorationAdapter.checkAccept(); - boolean isNew = storeManager.getEquivalenceClassStore().submit(res, code, + boolean isNew = storeManager.getEquivalenceClassStore().submit(last, code, activationStoreWorker.calculateEmptyActivationSize(), accepted); - return new SubmitResult(isNew, accepted, objectiveValue, isNew ? res : null); + return new SubmitResult(isNew, accepted, objectiveValue, isNew ? last : null); } public void restoreToLast() { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java index 1229ec15..559c715a 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -66,7 +66,7 @@ public class ActivationStoreImpl implements ActivationStore { activation = -1; } - if(hasMoreInActivation) { + if(!hasMoreInActivation) { boolean hasMoreInOtherTransformation = false; for (var e : entries) { if (e != entry && e.getNumberOfVisitedActivations() > 0) { @@ -83,7 +83,7 @@ public class ActivationStoreImpl implements ActivationStore { actionWhenAllActivationVisited.accept(from); } - return new VisitResult(false, hasMore, transformation, activation); + return new VisitResult(successfulVisit, hasMore, transformation, activation); } @Override @@ -116,7 +116,7 @@ public class ActivationStoreImpl implements ActivationStore { var entry = entries.get(transformation); int unvisited = entry.getNumberOfUnvisitedActivations(); if (selected < sum2 + unvisited) { - activation = sum2 + unvisited - selected; + activation = sum2 + unvisited - selected - 1; break; } else { sum2 += unvisited; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java index 249b22da..c88a411f 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java @@ -53,12 +53,7 @@ public class ObjectivePriorityQueueImpl implements ObjectivePriorityQueue { @Override public VersionWithObjectiveValue getBest() { - var best = priorityQueue.peek(); - if (best != null) { - return best; - } else { - throw new IllegalArgumentException("The objective store is empty!"); - } + return priorityQueue.peek(); } @Override diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java index cc48864f..43548eaa 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java @@ -10,17 +10,17 @@ import tools.refinery.store.dse.transition.statespace.SolutionStore; import java.util.ArrayList; import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.PriorityQueue; + public class SolutionStoreImpl implements SolutionStore { final int maxNumberSolutions; public static final int UNLIMITED = -1; - final SortedSet solutions; + final PriorityQueue solutions; public SolutionStoreImpl(int maxNumberSolutions) { this.maxNumberSolutions = maxNumberSolutions; - solutions = new TreeSet<>(ObjectivePriorityQueueImpl.c1); + solutions = new PriorityQueue<>(ObjectivePriorityQueueImpl.c1.reversed()); } @@ -29,8 +29,7 @@ public class SolutionStoreImpl implements SolutionStore { boolean removeLast = hasEnoughSolution(); solutions.add(version); if(removeLast) { - var last = solutions.last(); - solutions.remove(last); + var last = solutions.poll(); return last != version; } else { return true; -- cgit v1.2.3-54-g00ecf From 895b26df7a806a2136c2f7a46d56b542326e561f Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 6 Sep 2023 19:05:10 +0200 Subject: feat(dse): transformation rule builder --- .../store-dse-visualization/build.gradle.kts | 13 + .../visualization/ModelVisualizerAdapter.java | 32 ++ .../visualization/ModelVisualizerBuilder.java | 16 + .../visualization/ModelVisualizerStoreAdapter.java | 22 ++ .../visualization/internal/FileFormat.java | 26 ++ .../internal/ModelVisualizeStoreAdapterImpl.java | 60 ++++ .../internal/ModelVisualizerAdapterImpl.java | 387 +++++++++++++++++++++ .../internal/ModelVisualizerBuilderImpl.java | 55 +++ .../tools/refinery/store/dse/ActionFactory.java | 15 - .../store/dse/modification/DanglingEdges.java | 12 + .../dse/modification/ModificationAdapter.java | 2 +- .../modification/actions/CreateActionLiteral.java | 43 +++ .../modification/actions/DeleteActionLiteral.java | 51 +++ .../actions/ModificationActionLiterals.java | 23 ++ .../internal/ModificationAdapterImpl.java | 55 ++- .../transition/DesignSpaceExplorationBuilder.java | 8 +- .../DesignSpaceExplorationStoreAdapter.java | 2 +- .../tools/refinery/store/dse/transition/Rule.java | 99 ++++++ .../refinery/store/dse/transition/RuleBuilder.java | 71 ++++ .../store/dse/transition/Transformation.java | 25 +- .../store/dse/transition/TransformationRule.java | 63 ---- .../transition/actions/AbstractActionLiteral.java | 9 + .../store/dse/transition/actions/Action.java | 132 +++++++ .../dse/transition/actions/ActionLiteral.java | 19 + .../dse/transition/actions/ActionLiterals.java | 33 ++ .../store/dse/transition/actions/BoundAction.java | 109 ++++++ .../dse/transition/actions/BoundActionLiteral.java | 16 + .../dse/transition/actions/PutActionLiteral.java | 64 ++++ .../dse/transition/callback/ActionCallback0.java | 15 + .../dse/transition/callback/ActionCallback1.java | 16 + .../dse/transition/callback/ActionCallback2.java | 16 + .../dse/transition/callback/ActionCallback3.java | 16 + .../dse/transition/callback/ActionCallback4.java | 16 + .../dse/transition/callback/RuleCallback0.java | 13 + .../dse/transition/callback/RuleCallback1.java | 14 + .../dse/transition/callback/RuleCallback2.java | 14 + .../dse/transition/callback/RuleCallback3.java | 14 + .../dse/transition/callback/RuleCallback4.java | 14 + .../DesignSpaceExplorationBuilderImpl.java | 30 +- .../DesignSpaceExplorationStoreAdapterImpl.java | 32 +- .../store/dse/transition/objectives/Criterion.java | 2 +- .../store/dse/transition/objectives/Objective.java | 2 +- .../dse/transition/objectives/QueryCriteria.java | 4 +- .../dse/transition/objectives/QueryObjective.java | 4 +- .../tools/refinery/store/dse/CRAExamplesTest.java | 54 ++- subprojects/store-reasoning/build.gradle.kts | 2 +- subprojects/visualization/build.gradle.kts | 13 - .../visualization/ModelVisualizerAdapter.java | 32 -- .../visualization/ModelVisualizerBuilder.java | 16 - .../visualization/ModelVisualizerStoreAdapter.java | 22 -- .../visualization/internal/FileFormat.java | 26 -- .../internal/ModelVisualizeStoreAdapterImpl.java | 60 ---- .../internal/ModelVisualizerAdapterImpl.java | 387 --------------------- .../internal/ModelVisualizerBuilderImpl.java | 55 --- 54 files changed, 1575 insertions(+), 776 deletions(-) create mode 100644 subprojects/store-dse-visualization/build.gradle.kts create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java delete mode 100644 subprojects/visualization/build.gradle.kts delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse-visualization/build.gradle.kts b/subprojects/store-dse-visualization/build.gradle.kts new file mode 100644 index 00000000..abad0491 --- /dev/null +++ b/subprojects/store-dse-visualization/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ + +plugins { + id("tools.refinery.gradle.java-library") +} + +dependencies { + api(project(":refinery-store-query")) +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java new file mode 100644 index 00000000..ae87d8ac --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.map.Version; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; + +import java.util.Collection; + +public interface ModelVisualizerAdapter extends ModelAdapter { + + ModelVisualizerStoreAdapter getStoreAdapter(); + static ModelVisualizerBuilder builder() { + return new ModelVisualizerBuilderImpl(); + } + + public void addTransition(Version from, Version to, String action); + + + public void addTransition(Version from, Version to, String action, Tuple activation); + public void addState(Version state); + public void addState(Version state, Collection fitness); + public void addState(Version state, String label); + public void addSolution(Version state); + public void visualize(); + +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java new file mode 100644 index 00000000..592f5fcf --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.visualization.internal.FileFormat; + +public interface ModelVisualizerBuilder extends ModelAdapterBuilder { + ModelVisualizerBuilder withOutputpath(String outputpath); + ModelVisualizerBuilder withFormat(FileFormat format); + ModelVisualizerBuilder saveDesignSpace(); + ModelVisualizerBuilder saveStates(); +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java new file mode 100644 index 00000000..46663b2a --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.visualization.internal.FileFormat; + +import java.util.Set; + +public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { + + String getOutputPath(); + + boolean isRenderDesignSpace(); + + boolean isRenderStates(); + + Set getFormats(); +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java new file mode 100644 index 00000000..c5dffeb2 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +public enum FileFormat { + BMP("bmp"), + DOT("dot"), + JPEG("jpg"), + PDF("pdf"), + PLAIN("plain"), + PNG("png"), + SVG("svg"); + + private final String format; + + FileFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java new file mode 100644 index 00000000..04be22d6 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +import java.util.Set; + +public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { + private final ModelStore store; + private final String outputPath; + private final boolean renderDesignSpace; + private final boolean renderStates; + private final Set formats; + + public ModelVisualizeStoreAdapterImpl(ModelStore store, String outputPath, Set formats, + boolean renderDesignSpace, boolean renderStates) { + this.store = store; + this.outputPath = outputPath; + this.formats = formats; + this.renderDesignSpace = renderDesignSpace; + this.renderStates = renderStates; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return new ModelVisualizerAdapterImpl(model, this); + } + + @Override + public String getOutputPath() { + return outputPath; + } + + @Override + public boolean isRenderDesignSpace() { + return renderDesignSpace; + } + + @Override + public boolean isRenderStates() { + return renderStates; + } + + @Override + public Set getFormats() { + return formats; + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java new file mode 100644 index 00000000..531969b4 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -0,0 +1,387 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + +public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { + private final Model model; + private final ModelVisualizerStoreAdapter storeAdapter; + private final Map> allInterpretations; + private final StringBuilder designSpaceBuilder = new StringBuilder(); + private final Map states = new HashMap<>(); + private int transitionCounter = 0; + private Integer numberOfStates = 0; + private final String outputPath; + private final Set formats; + private final boolean renderDesignSpace; + private final boolean renderStates; + + private static final Map truthValueToDot = Map.of( + TruthValue.TRUE, "1", + TruthValue.FALSE, "0", + TruthValue.UNKNOWN, "½", + TruthValue.ERROR, "E", + true, "1", + false, "0" + ); + + public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + this.outputPath = storeAdapter.getOutputPath(); + this.formats = storeAdapter.getFormats(); + if (formats.isEmpty()) { + formats.add(FileFormat.SVG); + } + this.renderDesignSpace = storeAdapter.isRenderDesignSpace(); + this.renderStates = storeAdapter.isRenderStates(); + + this.allInterpretations = new HashMap<>(); + for (var symbol : storeAdapter.getStore().getSymbols()) { + var arity = symbol.arity(); + if (arity < 1 || arity > 2) { + continue; + } + var interpretation = (Interpretation) model.getInterpretation(symbol); + allInterpretations.put(symbol, interpretation); + } + designSpaceBuilder.append("digraph designSpace {\n"); + designSpaceBuilder.append(""" + nodesep=0 + ranksep=5 + node[ + \tstyle=filled + \tfillcolor=white + ] + """); + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelVisualizerStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + private String createDotForCurrentModelState() { + + var unaryTupleToInterpretationsMap = new HashMap>>(); + + var sb = new StringBuilder(); + + sb.append("digraph model {\n"); + sb.append(""" + node [ + \tstyle="filled, rounded" + \tshape=plain + \tpencolor="#00000088" + \tfontname="Helvetica" + ] + """); + sb.append(""" + edge [ + \tlabeldistance=3 + \tfontname="Helvetica" + ] + """); + + for (var entry : allInterpretations.entrySet()) { + var key = entry.getKey(); + var arity = key.arity(); + var cursor = entry.getValue().getAll(); + if (arity == 1) { + while (cursor.move()) { + unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>()) + .add(entry.getValue()); + } + } else if (arity == 2) { + while (cursor.move()) { + var tuple = cursor.getKey(); + for (var i = 0; i < tuple.getSize(); i++) { + var id = tuple.get(i); + unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>()); + } + sb.append(drawEdge(cursor.getKey(), key, entry.getValue())); + } + } + } + for (var entry : unaryTupleToInterpretationsMap.entrySet()) { + sb.append(drawElement(entry)); + } + sb.append("}"); + return sb.toString(); + } + + private StringBuilder drawElement(Map.Entry>> entry) { + var sb = new StringBuilder(); + + var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\""; + + var key = entry.getKey(); + var id = key.get(0); + var mainLabel = String.valueOf(id); + var interpretations = entry.getValue(); + var backgroundColor = toBackgroundColorString(averageColor(interpretations)); + + sb.append(id); + sb.append(" [\n"); + sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n"); + sb.append("\tlabel="); + if (interpretations.isEmpty()) { + sb.append("<\n\t").append(mainLabel).append(""); + } + else { + sb.append("<\n\t\t") + .append(mainLabel).append("\n"); + for (var interpretation : interpretations) { + var rawValue = interpretation.get(key); + + if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) { + continue; + } + var color = "black"; + if (rawValue.equals(TruthValue.ERROR)) { + color = "red"; + } + var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString()); + var symbol = interpretation.getSymbol(); + + if (symbol.valueType() == String.class) { + value = "\"" + value + "\""; + } + sb.append("\t\t") + .append(interpretation.getSymbol().name()) + .append("") + .append("=").append(value) + .append("\n"); + } + } + sb.append("\t\t>\n"); + sb.append("]\n"); + + return sb; + } + + private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation interpretation) { + var value = interpretation.get(edge); + + if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) { + return ""; + } + + var sb = new StringBuilder(); + var style = "solid"; + var color = "black"; + if (value.equals(TruthValue.UNKNOWN)) { + style = "dotted"; + } + else if (value.equals(TruthValue.ERROR)) { + style = "dashed"; + color = "red"; + } + + var from = edge.get(0); + var to = edge.get(1); + var name = symbol.name(); + sb.append(from).append(" -> ").append(to) + .append(" [\n\tstyle=").append(style) + .append("\n\tcolor=").append(color) + .append("\n\tfontcolor=").append(color) + .append("\n\tlabel=\"").append(name) + .append("\"]\n"); + return sb.toString(); + } + + private String toBackgroundColorString(Integer[] backgroundColor) { + if (backgroundColor.length == 3) + return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]); + else if (backgroundColor.length == 4) + return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2], + backgroundColor[3]); + return null; + } + + private Integer[] typeColor(String name) { + var random = new Random(name.hashCode()); + return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; + } + + private Integer[] averageColor(Set> interpretations) { + if(interpretations.isEmpty()) { + return new Integer[]{256, 256, 256}; + } + // TODO: Only use interpretations where the value is not false (or unknown) + var symbols = interpretations.stream() + .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new); + + + + return new Integer[] { + Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue() + }; + } + + private String createDotForModelState(Version version) { + var currentVersion = model.getState(); + model.restore(version); + var graph = createDotForCurrentModelState(); + model.restore(currentVersion); + return graph; + } + + private boolean saveDot(String dot, String filePath) { + File file = new File(filePath); + file.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(dot); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + private boolean renderDot(String dot, String filePath) { + return renderDot(dot, FileFormat.SVG, filePath); + } + + private boolean renderDot(String dot, FileFormat format, String filePath) { + try { + Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); + + OutputStream osToProcess = process.getOutputStream(); + PrintWriter pwToProcess = new PrintWriter(osToProcess); + pwToProcess.write(dot); + pwToProcess.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public void addTransition(Version from, Version to, String action) { + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); + } + + @Override + public void addTransition(Version from, Version to, String action, Tuple activation) { + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); + + + for (int i = 0; i < activation.getSize(); i++) { + designSpaceBuilder.append(activation.get(i)); + if (i < activation.getSize() - 1) { + designSpaceBuilder.append(", "); + } + } + designSpaceBuilder.append("\"]\n"); + } + + @Override + public void addState(Version state) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + + @Override + public void addState(Version state, Collection fitness) { + var labelBuilder = new StringBuilder(); + for (var f : fitness) { + labelBuilder.append(f).append(", "); + } + addState(state, labelBuilder.toString()); + } + + @Override + public void addState(Version state, String label) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); + designSpaceBuilder.append(label); + designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + + @Override + public void addSolution(Version state) { + addState(state); + designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); + } + + private String buildDesignSpaceDot() { + designSpaceBuilder.append("}"); + return designSpaceBuilder.toString(); + } + + private boolean saveDesignSpace(String path) { + saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); + for (var entry : states.entrySet()) { + saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); + } + return true; + } + + private void renderDesignSpace(String path, Set formats) { + File filePath = new File(path); + filePath.mkdirs(); + if (renderStates) { + for (var entry : states.entrySet()) { + var stateId = entry.getValue(); + var stateDot = createDotForModelState(entry.getKey()); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(stateDot, path + "/" + stateId + ".dot"); + } + else { + renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); + } + } + } + } + if (renderDesignSpace) { + var designSpaceDot = buildDesignSpaceDot(); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(designSpaceDot, path + "/designSpace.dot"); + } + else { + renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + } + } + } + } + + @Override + public void visualize() { + renderDesignSpace(outputPath, formats); + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java new file mode 100644 index 00000000..e4d801d8 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerBuilder; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class ModelVisualizerBuilderImpl + extends AbstractModelAdapterBuilder + implements ModelVisualizerBuilder { + private String outputPath; + private boolean saveDesignSpace = false; + private boolean saveStates = false; + private Set formats = new LinkedHashSet<>(); + + @Override + protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { + return new ModelVisualizeStoreAdapterImpl(store, outputPath, formats, saveDesignSpace, saveStates); + } + + @Override + public ModelVisualizerBuilder withOutputpath(String outputpath) { + checkNotConfigured(); + this.outputPath = outputpath; + return this; + } + + @Override + public ModelVisualizerBuilder withFormat(FileFormat format) { + checkNotConfigured(); + this.formats.add(format); + return this; + } + + @Override + public ModelVisualizerBuilder saveDesignSpace() { + checkNotConfigured(); + this.saveDesignSpace = true; + return this; + } + + @Override + public ModelVisualizerBuilder saveStates() { + checkNotConfigured(); + this.saveStates = true; + return this; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java deleted file mode 100644 index 48a508b4..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.model.Model; -import tools.refinery.store.tuple.Tuple; - -import java.util.function.Consumer; - -public interface ActionFactory { - Consumer prepare(Model model); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java new file mode 100644 index 00000000..ac9d125b --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +public enum DanglingEdges { + IGNORE, + DELETE, + FAIL +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java index f15c16e0..58b60499 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java @@ -16,7 +16,7 @@ public interface ModificationAdapter extends ModelAdapter { Tuple1 createObject(); - Tuple deleteObject(Tuple tuple); + boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges); static ModificationBuilder builder() { return new ModificationBuilderImpl(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java new file mode 100644 index 00000000..5b86a5e1 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +public class CreateActionLiteral extends AbstractActionLiteral { + private final NodeVariable variable; + + public CreateActionLiteral(NodeVariable variable) { + + this.variable = variable; + } + + public NodeVariable getVariable() { + return variable; + } + + @Override + public List getInputVariables() { + return List.of(); + } + + @Override + public List getOutputVariables() { + return List.of(variable); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var adapter = model.getAdapter(ModificationAdapter.class); + return ignoredTuple -> adapter.createObject(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java new file mode 100644 index 00000000..18ad2b9d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.DanglingEdges; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class DeleteActionLiteral extends AbstractActionLiteral { + private final NodeVariable variable; + private final DanglingEdges danglingEdges; + + public DeleteActionLiteral(NodeVariable variable, DanglingEdges danglingEdges) { + + this.variable = variable; + this.danglingEdges = danglingEdges; + } + + public NodeVariable getVariable() { + return variable; + } + + public DanglingEdges getDanglingEdges() { + return danglingEdges; + } + + @Override + public List getInputVariables() { + return List.of(variable); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var adapter = model.getAdapter(ModificationAdapter.class); + return tuple -> adapter.deleteObject(tuple, danglingEdges) ? Tuple.of() : null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java new file mode 100644 index 00000000..31f50ac7 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.DanglingEdges; +import tools.refinery.store.query.term.NodeVariable; + +public class ModificationActionLiterals { + private ModificationActionLiterals() { + throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly"); + } + + public static CreateActionLiteral create(NodeVariable variable) { + return new CreateActionLiteral(variable); + } + + public static DeleteActionLiteral delete(NodeVariable variable, DanglingEdges danglingEdges) { + return new DeleteActionLiteral(variable, danglingEdges); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java index b2a80d71..4e77c462 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java @@ -6,6 +6,7 @@ package tools.refinery.store.dse.modification.internal; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.modification.DanglingEdges; import tools.refinery.store.dse.modification.ModificationAdapter; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; @@ -13,6 +14,8 @@ import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; +import java.util.HashSet; + public class ModificationAdapterImpl implements ModificationAdapter { static final Symbol NEXT_ID = Symbol.of("NEXT_ID", 0, Integer.class, 0); @@ -49,14 +52,56 @@ public class ModificationAdapterImpl implements ModificationAdapter { } @Override - public Tuple deleteObject(Tuple tuple) { + public boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges) { if (tuple.getSize() != 1) { throw new IllegalArgumentException("Tuple size must be 1"); } -// TODO: implement more efficient deletion - if (tuple.get(0) == getModelSize() - 1) { - nodeCountInterpretation.put(Tuple.of(), getModelSize() - 1); + int objectId = tuple.get(0); + if (danglingEdges == DanglingEdges.DELETE) { + deleteDanglingEdges(objectId); + } else if (danglingEdges == DanglingEdges.FAIL && hasDanglingEdges(objectId)) { + return false; + + } + int modelSize = getModelSize(); + if (objectId == modelSize - 1) { + nodeCountInterpretation.put(Tuple.of(), modelSize - 1); + } + return true; + } + + private void deleteDanglingEdges(int objectId) { + for (var symbol : model.getStore().getSymbols()) { + deleteDanglingEdges(objectId, (Symbol) symbol); + } + } + + private void deleteDanglingEdges(int objectId, Symbol symbol) { + var interpretation = model.getInterpretation(symbol); + var toDelete = new HashSet(); + int arity = symbol.arity(); + for (int i = 0; i < arity; i++) { + var cursor = interpretation.getAdjacent(i, objectId); + while (cursor.move()) { + toDelete.add(cursor.getKey()); + } + } + var defaultValue = symbol.defaultValue(); + for (var tuple : toDelete) { + interpretation.put(tuple, defaultValue); + } + } + + private boolean hasDanglingEdges(int objectId) { + for (var symbol : model.getStore().getSymbols()) { + var interpretation = model.getInterpretation(symbol); + int arity = symbol.arity(); + for (int i = 0; i < arity; i++) { + if (interpretation.getAdjacentSize(i, objectId) > 0) { + return true; + } + } } - return tuple; + return false; } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java index 3855a20a..800cf8f7 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java @@ -12,14 +12,16 @@ import tools.refinery.store.dse.transition.objectives.Objective; import java.util.Collection; import java.util.List; +// Builder pattern with methods returning {@code this} for convenience. +@SuppressWarnings("UnusedReturnValue") public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition); - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition); - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRuleDefinitions) { + default DesignSpaceExplorationBuilder transformations(Rule... transformationRuleDefinitions) { return transformations(List.of(transformationRuleDefinitions)); } - default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { transformationRules.forEach(this::transformation); return this; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java index 5c8c7a4d..fb082fae 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java @@ -17,7 +17,7 @@ public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter @Override DesignSpaceExplorationAdapter createModelAdapter(Model model); - List getTransformations(); + List getTransformations(); List getAccepts(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java new file mode 100644 index 00000000..ff45ed3e --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.dse.transition.actions.Action; +import tools.refinery.store.dse.transition.actions.BoundAction; +import tools.refinery.store.dse.transition.callback.*; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.dnf.RelationalQuery; + +public class Rule { + private final String name; + private final RelationalQuery precondition; + private final Action action; + + public Rule(String name, RelationalQuery precondition, Action action) { + if (precondition.arity() != action.getArity()) { + throw new IllegalArgumentException("Expected an action clause with %d parameters, got %d instead" + .formatted(precondition.arity(), action.getArity())); + } + this.name = name; + this.precondition = precondition; + this.action = action; + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + + public BoundAction createAction(Model model) { + return action.bindToModel(model); + } + + public static RuleBuilder builder(String name) { + return new RuleBuilder(name); + } + + public static RuleBuilder builder() { + return builder(null); + } + + public static Rule of(String name, RuleCallback0 callback) { + var builder = builder(name); + callback.accept(builder); + return builder.build(); + } + + public static Rule of(RuleCallback0 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback1 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1")); + return builder.build(); + } + + public static Rule of(RuleCallback1 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback2 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2")); + return builder.build(); + } + + public static Rule of(RuleCallback2 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback3 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3")); + return builder.build(); + } + + public static Rule of(RuleCallback3 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback4 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3"), + builder.parameter("p4")); + return builder.build(); + } + + public static Rule of(RuleCallback4 callback) { + return of(null, callback); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java new file mode 100644 index 00000000..865ac369 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.dse.transition.actions.Action; +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.dse.transition.callback.*; +import tools.refinery.store.query.dnf.AbstractQueryBuilder; +import tools.refinery.store.query.dnf.Dnf; +import tools.refinery.store.query.term.Variable; + +import java.util.List; + +public class RuleBuilder extends AbstractQueryBuilder { + private final String name; + private List action; + + RuleBuilder(String name) { + super(Dnf.builder(name == null ? null : name + "#precondition")); + this.name = name; + } + + @Override + protected RuleBuilder self() { + return this; + } + + public RuleBuilder action(ActionLiteral... literals) { + return action(List.of(literals)); + } + + public RuleBuilder action(List literals) { + if (this.action != null) { + throw new IllegalStateException("Actions have already been set"); + } + this.action = List.copyOf(literals); + return this; + } + + public RuleBuilder action(Action action) { + return action(action.getActionLiterals()); + } + + public RuleBuilder action(ActionCallback0 callback) { + return action(callback.toLiterals()); + } + + public RuleBuilder action(ActionCallback1 callback) { + return action(callback.toLiterals(Variable.of("v1"))); + } + + public RuleBuilder action(ActionCallback2 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"))); + } + + public RuleBuilder action(ActionCallback3 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3"))); + } + + public RuleBuilder action(ActionCallback4 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3"), Variable.of("v4"))); + } + + public Rule build() { + var precondition = dnfBuilder.build().asRelation(); + return new Rule(name, precondition, Action.ofPrecondition(precondition, action)); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java index ab9fda3e..0eeccbdf 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java @@ -5,26 +5,27 @@ */ package tools.refinery.store.dse.transition; +import tools.refinery.store.dse.transition.actions.BoundAction; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.resultset.OrderedResultSet; import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.tuple.Tuple; -import java.util.function.Consumer; - public class Transformation { - private final TransformationRule definition; - + private final Rule definition; private final OrderedResultSet activations; + private final BoundAction action; - private final Consumer action; - - public Transformation(TransformationRule definition, OrderedResultSet activations, Consumer action) { + public Transformation(Model model, Rule definition) { this.definition = definition; - this.activations = activations; - this.action = action; + var precondition = definition.getPrecondition(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + action = definition.createAction(model); } - public TransformationRule getDefinition() { + public Rule getDefinition() { return definition; } @@ -37,8 +38,6 @@ public class Transformation { } public boolean fireActivation(Tuple activation) { - action.accept(activation); - //queryEngine.flushChanges(); - return true; + return action.fire(activation); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java deleted file mode 100644 index d64a3db1..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.transition; - -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.ModelQueryBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.ActionFactory; -import tools.refinery.store.query.resultset.OrderedResultSet; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.*; - -public class TransformationRule { - - private final String name; - private final RelationalQuery precondition; - private final ActionFactory actionFactory; - - private Random random; - private ModelQueryAdapter queryEngine; - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { - this(name, precondition, actionFactory, new Random()); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { - this(name, precondition, actionFactory, new Random(seed)); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { - this.name = name; - this.precondition = precondition; - this.actionFactory = actionFactory; - this.random = random; - } - public void doConfigure(ModelStoreBuilder storeBuilder) { - var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); - queryBuilder.query(this.precondition); - } - - public Transformation prepare(Model model) { - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - var activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); - var action = actionFactory.prepare(model); - return new Transformation(this,activations,action); - } - - public String getName() { - return name; - } - - public RelationalQuery getPrecondition() { - return precondition; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java new file mode 100644 index 00000000..e30f06bb --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +public abstract class AbstractActionLiteral implements ActionLiteral { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java new file mode 100644 index 00000000..d63ddfdd --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; +import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dnf.SymbolicParameter; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.*; + +public class Action { + private final List parameters; + private final Set localVariables; + private final List actionLiterals; + private final int[] @Nullable [] inputAllocations; + private final int[] @Nullable [] outputAllocations; + + public Action(List parameters, List actionLiterals) { + this.parameters = List.copyOf(parameters); + this.actionLiterals = List.copyOf(actionLiterals); + var allocation = ObjectIntMaps.mutable.empty(); + int arity = parameters.size(); + for (int i = 0; i < arity; i++) { + allocation.put(parameters.get(i), i); + } + var mutableLocalVariables = new LinkedHashSet(); + int size = actionLiterals.size(); + inputAllocations = new int[size][]; + outputAllocations = new int[size][]; + for (int i = 0; i < size; i++) { + computeInputAllocation(i, parameters, allocation); + computeOutputAllocation(i, mutableLocalVariables, allocation); + } + this.localVariables = Collections.unmodifiableSet(mutableLocalVariables); + } + + private void computeInputAllocation(int actionIndex, List parameters, + MutableObjectIntMap allocation) { + var actionLiteral = actionLiterals.get(actionIndex); + var inputVariables = actionLiteral.getInputVariables(); + if (inputVariables.equals(parameters)) { + // Identity mappings use a {@code null} allocation to pass the activation tuple unchanged. + return; + } + var inputs = new int[inputVariables.size()]; + for (int i = 0; i < inputs.length; i++) { + var variable = inputVariables.get(i); + if (!allocation.containsKey(variable)) { + throw new IllegalArgumentException("Unbound input variable %s of action literal %s" + .formatted(variable, actionLiteral)); + } + inputs[i] = allocation.get(variable); + } + inputAllocations[actionIndex] = inputs; + } + + private void computeOutputAllocation(int actionIndex, Set mutableLocalVariable, + MutableObjectIntMap allocation) { + var actionLiteral = actionLiterals.get(actionIndex); + var outputVariables = actionLiteral.getOutputVariables(); + int size = outputVariables.size(); + if (size == 0) { + // Identity mappings use a {@code null} allocation to avoid iterating over the output tuple. + return; + } + if (size >= 2 && new HashSet<>(outputVariables).size() != size) { + throw new IllegalArgumentException("Action literal %s has duplicate output variables %s" + .formatted(actionLiteral, outputVariables)); + } + int arity = parameters.size(); + var outputs = new int[size]; + for (int i = 0; i < size; i++) { + var variable = outputVariables.get(i); + if (allocation.containsKey(variable)) { + throw new IllegalArgumentException("Output variable %s of action literal %s was already assigned" + .formatted(variable, actionLiteral)); + } + int variableId = mutableLocalVariable.size(); + allocation.put(variable, arity + variableId); + outputs[i] = variableId; + mutableLocalVariable.add(variable); + } + outputAllocations[actionIndex] = outputs; + } + + public List getParameters() { + return parameters; + } + + public int getArity() { + return parameters.size(); + } + + public Set getLocalVariables() { + return localVariables; + } + + public List getActionLiterals() { + return actionLiterals; + } + + int @Nullable [] getInputAllocation(int actionIndex) { + return inputAllocations[actionIndex]; + } + + int @Nullable [] getOutputAllocation(int actionIndex) { + return outputAllocations[actionIndex]; + } + + public BoundAction bindToModel(Model model) { + return new BoundAction(this, model); + } + + public static Action ofSymbolicParameters(List symbolicParameters, + List actionLiterals) { + var nodeVariables = symbolicParameters.stream() + .map(symbolicParameter -> symbolicParameter.getVariable().asNodeVariable()) + .toList(); + return new Action(nodeVariables, actionLiterals); + } + + public static Action ofPrecondition(RelationalQuery precondition, List actionLiterals) { + return ofSymbolicParameters(precondition.getDnf().getSymbolicParameters(), actionLiterals); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java new file mode 100644 index 00000000..a721ef73 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +public interface ActionLiteral { + List getInputVariables(); + + List getOutputVariables(); + + BoundActionLiteral bindToModel(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java new file mode 100644 index 00000000..275e1e25 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.representation.Symbol; + +import java.util.List; +import java.util.Objects; + +public final class ActionLiterals { + private ActionLiterals() { + throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly"); + } + + public static PutActionLiteral put(Symbol symbol, T value, NodeVariable... parameters) { + return new PutActionLiteral<>(symbol, value, List.of(parameters)); + } + + public static PutActionLiteral add(Symbol symbol, NodeVariable... parameters) { + if (!Objects.equals(symbol.defaultValue(), false)) { + throw new IllegalArgumentException("Use put to add a value to symbols other than two-valued logic"); + } + return put(symbol, true, parameters); + } + + public static PutActionLiteral remove(Symbol symbol, NodeVariable... parameters) { + return put(symbol, symbol.defaultValue(), parameters); + } +} 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 new file mode 100644 index 00000000..55f43735 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.model.Model; +import tools.refinery.store.tuple.Tuple; + +public class BoundAction { + private final Action action; + private final BoundActionLiteral[] boundLiterals; + private Tuple activation; + private final int[] localVariables; + + BoundAction(Action action, Model model) { + this.action = action; + var actionLiterals = action.getActionLiterals(); + int size = actionLiterals.size(); + boundLiterals = new BoundActionLiteral[size]; + for (int i = 0; i < size; i++) { + boundLiterals[i] = actionLiterals.get(i).bindToModel(model); + } + localVariables = new int[action.getLocalVariables().size()]; + } + + public boolean fire(Tuple activation) { + if (this.activation != null) { + throw new IllegalStateException("Reentrant firing is not allowed"); + } + this.activation = activation; + try { + int size = boundLiterals.length; + for (int i = 0; i < size; i++) { + var inputAllocation = action.getInputAllocation(i); + var boundLiteral = boundLiterals[i]; + var input = getInputTuple(inputAllocation); + var output = boundLiteral.fire(input); + if (output == null) { + return false; + } + var outputAllocation = this.action.getOutputAllocation(i); + setOutputTuple(outputAllocation, output); + } + } finally { + this.activation = null; + } + return true; + } + + private Tuple getInputTuple(int @Nullable [] inputAllocation) { + if (inputAllocation == null) { + // Identity allocation. + return activation; + } + return switch (inputAllocation.length) { + case 0 -> Tuple.of(); + case 1 -> Tuple.of(getInput(inputAllocation[0])); + case 2 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1])); + case 3 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]), + getInput(inputAllocation[2])); + case 4 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]), + getInput(inputAllocation[2]), getInput(inputAllocation[3])); + default -> { + var elements = new int[inputAllocation.length]; + for (var i = 0; i < inputAllocation.length; i++) { + elements[i] = getInput(inputAllocation[i]); + } + yield Tuple.of(elements); + } + }; + } + + private int getInput(int index) { + int arity = action.getArity(); + return index < arity ? activation.get(index) : localVariables[index - arity]; + } + + private void setOutputTuple(int @Nullable [] outputAllocation, Tuple output) { + if (outputAllocation == null || outputAllocation.length == 0) { + return; + } + switch (outputAllocation.length) { + case 1 -> localVariables[outputAllocation[0]] = output.get(0); + case 2 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + } + case 3 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + localVariables[outputAllocation[2]] = output.get(2); + } + case 4 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + localVariables[outputAllocation[2]] = output.get(2); + localVariables[outputAllocation[3]] = output.get(3); + } + default -> { + for (int i = 0; i < outputAllocation.length; i++) { + localVariables[outputAllocation[i]] = output.get(i); + } + } + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java new file mode 100644 index 00000000..09c3c58c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.tuple.Tuple; + +@FunctionalInterface +public interface BoundActionLiteral { + @Nullable + Tuple fire(@NotNull Tuple tuple); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java new file mode 100644 index 00000000..86288921 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class PutActionLiteral extends AbstractActionLiteral { + private final Symbol symbol; + private final List parameters; + private final T value; + + public PutActionLiteral(Symbol symbol, T value, List parameters) { + if (symbol.arity() != parameters.size()) { + throw new IllegalArgumentException("Expected %d parameters for symbol %s, got %d instead" + .formatted(symbol.arity(), symbol, parameters.size())); + } + if (value != null && !symbol.valueType().isInstance(value)) { + throw new IllegalArgumentException("Expected value of type %s for symbol %s, got %s of type %s instead" + .formatted(symbol.valueType().getName(), symbol, value, value.getClass().getName())); + } + this.symbol = symbol; + this.parameters = List.copyOf(parameters); + this.value = value; + } + + public Symbol getSymbol() { + return symbol; + } + + public List getParameters() { + return parameters; + } + + public T getValue() { + return value; + } + + @Override + public List getInputVariables() { + return getParameters(); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var interpretation = model.getInterpretation(symbol); + return tuple -> { + interpretation.put(tuple, value); + return Tuple.of(); + }; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java new file mode 100644 index 00000000..1190fdeb --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback0 { + List toLiterals(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java new file mode 100644 index 00000000..869f1a96 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback1 { + List toLiterals(NodeVariable v1); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java new file mode 100644 index 00000000..a648fc93 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback2 { + List toLiterals(NodeVariable v1, NodeVariable v2); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java new file mode 100644 index 00000000..a9b1d334 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback3 { + List toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java new file mode 100644 index 00000000..aef1351c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback4 { + List toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3, NodeVariable v4); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java new file mode 100644 index 00000000..538c23ba --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; + +@FunctionalInterface +public interface RuleCallback0 { + void accept(RuleBuilder builder); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java new file mode 100644 index 00000000..bd7bf4f5 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback1 { + void accept(RuleBuilder builder, NodeVariable p1); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java new file mode 100644 index 00000000..7b02b68a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback2 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java new file mode 100644 index 00000000..6f112d48 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback3 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java new file mode 100644 index 00000000..dbcf8567 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback4 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3, NodeVariable p4); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java index 4371cc03..a91f6870 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java @@ -7,13 +7,13 @@ package tools.refinery.store.dse.transition.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; -import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.dse.transition.objectives.Criterion; import tools.refinery.store.dse.transition.objectives.Objective; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryBuilder; -import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -21,13 +21,13 @@ public class DesignSpaceExplorationBuilderImpl extends AbstractModelAdapterBuilder implements DesignSpaceExplorationBuilder { - LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); + LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); LinkedHashSet accepts = new LinkedHashSet<>(); LinkedHashSet excludes = new LinkedHashSet<>(); LinkedHashSet objectives = new LinkedHashSet<>(); @Override - public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition) { + public DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition) { transformationRuleDefinitions.add(transformationRuleDefinition); return this; } @@ -53,23 +53,23 @@ public class DesignSpaceExplorationBuilderImpl @Override protected void doConfigure(ModelStoreBuilder storeBuilder) { - transformationRuleDefinitions.forEach(x -> x.doConfigure(storeBuilder)); - accepts.forEach(x -> x.doConfigure(storeBuilder)); - excludes.forEach(x -> x.doConfigure(storeBuilder)); - objectives.forEach(x -> x.doConfigure(storeBuilder)); + var queryEngine = storeBuilder.getAdapter(ModelQueryBuilder.class); + transformationRuleDefinitions.forEach(x -> queryEngine.query(x.getPrecondition())); + accepts.forEach(x -> x.configure(storeBuilder)); + excludes.forEach(x -> x.configure(storeBuilder)); + objectives.forEach(x -> x.configure(storeBuilder)); super.doConfigure(storeBuilder); } @Override protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - List transformationRuleDefinitiions1 = new ArrayList<>(transformationRuleDefinitions); - List accepts1 = new ArrayList<>(accepts); - List excludes1 = new ArrayList<>(excludes); - List objectives1 = new ArrayList<>(objectives); + List transformationRuleDefinitionsList = List.copyOf(transformationRuleDefinitions); + List acceptsList = List.copyOf(accepts); + List excludesList = List.copyOf(excludes); + List objectivesList = List.copyOf(objectives); - return new DesignSpaceExplorationStoreAdapterImpl(store, - transformationRuleDefinitiions1, accepts1, - excludes1, objectives1); + return new DesignSpaceExplorationStoreAdapterImpl(store, transformationRuleDefinitionsList, acceptsList, + excludesList, objectivesList); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java index 3319e148..bd85e7a6 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -7,7 +7,7 @@ package tools.refinery.store.dse.transition.internal; import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.dse.transition.Transformation; -import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.dse.transition.objectives.Criterion; import tools.refinery.store.dse.transition.objectives.CriterionCalculator; import tools.refinery.store.dse.transition.objectives.Objective; @@ -20,18 +20,16 @@ import java.util.List; public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { protected final ModelStore store; - protected final List transformationRuleDefinitions; + protected final List ruleDefinitions; protected final List accepts; protected final List excludes; protected final List objectives; - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - List transformationRuleDefinitions, - List accepts, List excludes, - List objectives) { + public DesignSpaceExplorationStoreAdapterImpl( + ModelStore store, List ruleDefinitions, List accepts, List excludes, + List objectives) { this.store = store; - - this.transformationRuleDefinitions = transformationRuleDefinitions; + this.ruleDefinitions = ruleDefinitions; this.accepts = accepts; this.excludes = excludes; this.objectives = objectives; @@ -44,25 +42,31 @@ public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplor @Override public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { - final List t = this.transformationRuleDefinitions.stream().map(x->x.prepare(model)).toList(); - final List a = this.accepts.stream().map(x->x.createCalculator(model)).toList(); - final List e = this.excludes.stream().map(x->x.createCalculator(model)).toList(); - final List o = this.objectives.stream().map(x->x.createCalculator(model)).toList(); + final List t = this.ruleDefinitions.stream() + .map(x -> new Transformation(model, x)) + .toList(); + final List a = this.accepts.stream().map(x -> x.createCalculator(model)).toList(); + final List e = this.excludes.stream().map(x -> x.createCalculator(model)).toList(); + final List o = this.objectives.stream().map(x -> x.createCalculator(model)).toList(); return new DesignSpaceExplorationAdapterImpl(model, this, t, a, e, o); } + @Override - public List getTransformations() { - return transformationRuleDefinitions; + public List getTransformations() { + return ruleDefinitions; } + @Override public List getAccepts() { return accepts; } + @Override public List getExcludes() { return excludes; } + @Override public List getObjectives() { return objectives; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java index 66ca6f5e..4365cf9c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java @@ -9,7 +9,7 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStoreBuilder; public interface Criterion { - default void doConfigure(ModelStoreBuilder storeBuilder) { + default void configure(ModelStoreBuilder storeBuilder) { } CriterionCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java index b5924455..d2476a2e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java @@ -9,7 +9,7 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStoreBuilder; public interface Objective { - default void doConfigure(ModelStoreBuilder storeBuilder) { + default void configure(ModelStoreBuilder storeBuilder) { } ObjectiveCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java index e2260cca..8d0a56cf 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java @@ -37,8 +37,8 @@ public class QueryCriteria implements Criterion { } @Override - public void doConfigure(ModelStoreBuilder storeBuilder) { - Criterion.super.doConfigure(storeBuilder); + public void configure(ModelStoreBuilder storeBuilder) { + Criterion.super.configure(storeBuilder); storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java index dfddccfc..9553e0e0 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -37,8 +37,8 @@ public class QueryObjective implements Objective { } @Override - public void doConfigure(ModelStoreBuilder storeBuilder) { - Objective.super.doConfigure(storeBuilder); + public void configure(ModelStoreBuilder storeBuilder) { + Objective.super.configure(storeBuilder); storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 36517709..0c50718e 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,13 +5,11 @@ */ package tools.refinery.store.dse; -import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; import java.util.List; @@ -107,19 +105,19 @@ class CRAExamplesTest { encapsulatesView.call(c1, f) )); - private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { -// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - -// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); - }); - }); +// private static final Rule assignFeatureRule = new Rule("AssignFeature", +// assignFeaturePrecondition, +// (model) -> { +//// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// var feature = activation.get(0); +// var classElement = activation.get(1); +// +//// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); +// encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); +// }); +// }); // private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", // deleteEmptyClassPrecondition, @@ -158,19 +156,19 @@ class CRAExamplesTest { // }); // }); - private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", - moveFeaturePrecondition, - (model) -> { - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var classElement1 = activation.get(0); - var classElement2 = activation.get(1); - var feature = activation.get(2); - - encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); - encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); - }); - }); +// private static final Rule moveFeatureRule = new Rule("MoveFeature", +// moveFeaturePrecondition, +// (model) -> { +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// var classElement1 = activation.get(0); +// var classElement2 = activation.get(1); +// var feature = activation.get(2); +// +// encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); +// encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); +// }); +// }); // @Test // @Disabled("This test is only for debugging purposes") diff --git a/subprojects/store-reasoning/build.gradle.kts b/subprojects/store-reasoning/build.gradle.kts index 5885da83..ed8355f3 100644 --- a/subprojects/store-reasoning/build.gradle.kts +++ b/subprojects/store-reasoning/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } dependencies { - api(project(":refinery-store-query")) + api(project(":refinery-store-dse")) testImplementation(testFixtures(project(":refinery-store-query"))) testImplementation(project(":refinery-store-query-viatra")) } diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts deleted file mode 100644 index abad0491..00000000 --- a/subprojects/visualization/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ - -plugins { - id("tools.refinery.gradle.java-library") -} - -dependencies { - api(project(":refinery-store-query")) -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java deleted file mode 100644 index ae87d8ac..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.map.Version; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; - -import java.util.Collection; - -public interface ModelVisualizerAdapter extends ModelAdapter { - - ModelVisualizerStoreAdapter getStoreAdapter(); - static ModelVisualizerBuilder builder() { - return new ModelVisualizerBuilderImpl(); - } - - public void addTransition(Version from, Version to, String action); - - - public void addTransition(Version from, Version to, String action, Tuple activation); - public void addState(Version state); - public void addState(Version state, Collection fitness); - public void addState(Version state, String label); - public void addSolution(Version state); - public void visualize(); - -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java deleted file mode 100644 index 592f5fcf..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.visualization.internal.FileFormat; - -public interface ModelVisualizerBuilder extends ModelAdapterBuilder { - ModelVisualizerBuilder withOutputpath(String outputpath); - ModelVisualizerBuilder withFormat(FileFormat format); - ModelVisualizerBuilder saveDesignSpace(); - ModelVisualizerBuilder saveStates(); -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java deleted file mode 100644 index 46663b2a..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.visualization.internal.FileFormat; - -import java.util.Set; - -public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { - - String getOutputPath(); - - boolean isRenderDesignSpace(); - - boolean isRenderStates(); - - Set getFormats(); -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java deleted file mode 100644 index c5dffeb2..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -public enum FileFormat { - BMP("bmp"), - DOT("dot"), - JPEG("jpg"), - PDF("pdf"), - PLAIN("plain"), - PNG("png"), - SVG("svg"); - - private final String format; - - FileFormat(String format) { - this.format = format; - } - - public String getFormat() { - return format; - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java deleted file mode 100644 index 04be22d6..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.visualization.ModelVisualizerStoreAdapter; - -import java.util.Set; - -public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { - private final ModelStore store; - private final String outputPath; - private final boolean renderDesignSpace; - private final boolean renderStates; - private final Set formats; - - public ModelVisualizeStoreAdapterImpl(ModelStore store, String outputPath, Set formats, - boolean renderDesignSpace, boolean renderStates) { - this.store = store; - this.outputPath = outputPath; - this.formats = formats; - this.renderDesignSpace = renderDesignSpace; - this.renderStates = renderStates; - } - - @Override - public ModelStore getStore() { - return store; - } - - @Override - public ModelAdapter createModelAdapter(Model model) { - return new ModelVisualizerAdapterImpl(model, this); - } - - @Override - public String getOutputPath() { - return outputPath; - } - - @Override - public boolean isRenderDesignSpace() { - return renderDesignSpace; - } - - @Override - public boolean isRenderStates() { - return renderStates; - } - - @Override - public Set getFormats() { - return formats; - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java deleted file mode 100644 index 531969b4..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.map.Version; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.representation.AnySymbol; -import tools.refinery.store.representation.TruthValue; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.ModelVisualizerStoreAdapter; - -import java.io.*; -import java.util.*; -import java.util.stream.Collectors; - -public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { - private final Model model; - private final ModelVisualizerStoreAdapter storeAdapter; - private final Map> allInterpretations; - private final StringBuilder designSpaceBuilder = new StringBuilder(); - private final Map states = new HashMap<>(); - private int transitionCounter = 0; - private Integer numberOfStates = 0; - private final String outputPath; - private final Set formats; - private final boolean renderDesignSpace; - private final boolean renderStates; - - private static final Map truthValueToDot = Map.of( - TruthValue.TRUE, "1", - TruthValue.FALSE, "0", - TruthValue.UNKNOWN, "½", - TruthValue.ERROR, "E", - true, "1", - false, "0" - ); - - public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - this.outputPath = storeAdapter.getOutputPath(); - this.formats = storeAdapter.getFormats(); - if (formats.isEmpty()) { - formats.add(FileFormat.SVG); - } - this.renderDesignSpace = storeAdapter.isRenderDesignSpace(); - this.renderStates = storeAdapter.isRenderStates(); - - this.allInterpretations = new HashMap<>(); - for (var symbol : storeAdapter.getStore().getSymbols()) { - var arity = symbol.arity(); - if (arity < 1 || arity > 2) { - continue; - } - var interpretation = (Interpretation) model.getInterpretation(symbol); - allInterpretations.put(symbol, interpretation); - } - designSpaceBuilder.append("digraph designSpace {\n"); - designSpaceBuilder.append(""" - nodesep=0 - ranksep=5 - node[ - \tstyle=filled - \tfillcolor=white - ] - """); - } - - @Override - public Model getModel() { - return model; - } - - @Override - public ModelVisualizerStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - private String createDotForCurrentModelState() { - - var unaryTupleToInterpretationsMap = new HashMap>>(); - - var sb = new StringBuilder(); - - sb.append("digraph model {\n"); - sb.append(""" - node [ - \tstyle="filled, rounded" - \tshape=plain - \tpencolor="#00000088" - \tfontname="Helvetica" - ] - """); - sb.append(""" - edge [ - \tlabeldistance=3 - \tfontname="Helvetica" - ] - """); - - for (var entry : allInterpretations.entrySet()) { - var key = entry.getKey(); - var arity = key.arity(); - var cursor = entry.getValue().getAll(); - if (arity == 1) { - while (cursor.move()) { - unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>()) - .add(entry.getValue()); - } - } else if (arity == 2) { - while (cursor.move()) { - var tuple = cursor.getKey(); - for (var i = 0; i < tuple.getSize(); i++) { - var id = tuple.get(i); - unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>()); - } - sb.append(drawEdge(cursor.getKey(), key, entry.getValue())); - } - } - } - for (var entry : unaryTupleToInterpretationsMap.entrySet()) { - sb.append(drawElement(entry)); - } - sb.append("}"); - return sb.toString(); - } - - private StringBuilder drawElement(Map.Entry>> entry) { - var sb = new StringBuilder(); - - var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\""; - - var key = entry.getKey(); - var id = key.get(0); - var mainLabel = String.valueOf(id); - var interpretations = entry.getValue(); - var backgroundColor = toBackgroundColorString(averageColor(interpretations)); - - sb.append(id); - sb.append(" [\n"); - sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n"); - sb.append("\tlabel="); - if (interpretations.isEmpty()) { - sb.append("<\n\t").append(mainLabel).append(""); - } - else { - sb.append("<\n\t\t") - .append(mainLabel).append("\n"); - for (var interpretation : interpretations) { - var rawValue = interpretation.get(key); - - if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) { - continue; - } - var color = "black"; - if (rawValue.equals(TruthValue.ERROR)) { - color = "red"; - } - var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString()); - var symbol = interpretation.getSymbol(); - - if (symbol.valueType() == String.class) { - value = "\"" + value + "\""; - } - sb.append("\t\t") - .append(interpretation.getSymbol().name()) - .append("") - .append("=").append(value) - .append("\n"); - } - } - sb.append("\t\t>\n"); - sb.append("]\n"); - - return sb; - } - - private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation interpretation) { - var value = interpretation.get(edge); - - if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) { - return ""; - } - - var sb = new StringBuilder(); - var style = "solid"; - var color = "black"; - if (value.equals(TruthValue.UNKNOWN)) { - style = "dotted"; - } - else if (value.equals(TruthValue.ERROR)) { - style = "dashed"; - color = "red"; - } - - var from = edge.get(0); - var to = edge.get(1); - var name = symbol.name(); - sb.append(from).append(" -> ").append(to) - .append(" [\n\tstyle=").append(style) - .append("\n\tcolor=").append(color) - .append("\n\tfontcolor=").append(color) - .append("\n\tlabel=\"").append(name) - .append("\"]\n"); - return sb.toString(); - } - - private String toBackgroundColorString(Integer[] backgroundColor) { - if (backgroundColor.length == 3) - return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]); - else if (backgroundColor.length == 4) - return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2], - backgroundColor[3]); - return null; - } - - private Integer[] typeColor(String name) { - var random = new Random(name.hashCode()); - return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; - } - - private Integer[] averageColor(Set> interpretations) { - if(interpretations.isEmpty()) { - return new Integer[]{256, 256, 256}; - } - // TODO: Only use interpretations where the value is not false (or unknown) - var symbols = interpretations.stream() - .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new); - - - - return new Integer[] { - Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), - Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), - Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue() - }; - } - - private String createDotForModelState(Version version) { - var currentVersion = model.getState(); - model.restore(version); - var graph = createDotForCurrentModelState(); - model.restore(currentVersion); - return graph; - } - - private boolean saveDot(String dot, String filePath) { - File file = new File(filePath); - file.getParentFile().mkdirs(); - - try (FileWriter writer = new FileWriter(file)) { - writer.write(dot); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } - - private boolean renderDot(String dot, String filePath) { - return renderDot(dot, FileFormat.SVG, filePath); - } - - private boolean renderDot(String dot, FileFormat format, String filePath) { - try { - Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); - - OutputStream osToProcess = process.getOutputStream(); - PrintWriter pwToProcess = new PrintWriter(osToProcess); - pwToProcess.write(dot); - pwToProcess.close(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - public void addTransition(Version from, Version to, String action) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); - } - - @Override - public void addTransition(Version from, Version to, String action, Tuple activation) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); - - - for (int i = 0; i < activation.getSize(); i++) { - designSpaceBuilder.append(activation.get(i)); - if (i < activation.getSize() - 1) { - designSpaceBuilder.append(", "); - } - } - designSpaceBuilder.append("\"]\n"); - } - - @Override - public void addState(Version state) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addState(Version state, Collection fitness) { - var labelBuilder = new StringBuilder(); - for (var f : fitness) { - labelBuilder.append(f).append(", "); - } - addState(state, labelBuilder.toString()); - } - - @Override - public void addState(Version state, String label) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); - designSpaceBuilder.append(label); - designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addSolution(Version state) { - addState(state); - designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); - } - - private String buildDesignSpaceDot() { - designSpaceBuilder.append("}"); - return designSpaceBuilder.toString(); - } - - private boolean saveDesignSpace(String path) { - saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); - for (var entry : states.entrySet()) { - saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); - } - return true; - } - - private void renderDesignSpace(String path, Set formats) { - File filePath = new File(path); - filePath.mkdirs(); - if (renderStates) { - for (var entry : states.entrySet()) { - var stateId = entry.getValue(); - var stateDot = createDotForModelState(entry.getKey()); - for (var format : formats) { - if (format == FileFormat.DOT) { - saveDot(stateDot, path + "/" + stateId + ".dot"); - } - else { - renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); - } - } - } - } - if (renderDesignSpace) { - var designSpaceDot = buildDesignSpaceDot(); - for (var format : formats) { - if (format == FileFormat.DOT) { - saveDot(designSpaceDot, path + "/designSpace.dot"); - } - else { - renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); - } - } - } - } - - @Override - public void visualize() { - renderDesignSpace(outputPath, formats); - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java deleted file mode 100644 index e4d801d8..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.visualization.ModelVisualizerBuilder; - -import java.util.LinkedHashSet; -import java.util.Set; - -public class ModelVisualizerBuilderImpl - extends AbstractModelAdapterBuilder - implements ModelVisualizerBuilder { - private String outputPath; - private boolean saveDesignSpace = false; - private boolean saveStates = false; - private Set formats = new LinkedHashSet<>(); - - @Override - protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { - return new ModelVisualizeStoreAdapterImpl(store, outputPath, formats, saveDesignSpace, saveStates); - } - - @Override - public ModelVisualizerBuilder withOutputpath(String outputpath) { - checkNotConfigured(); - this.outputPath = outputpath; - return this; - } - - @Override - public ModelVisualizerBuilder withFormat(FileFormat format) { - checkNotConfigured(); - this.formats.add(format); - return this; - } - - @Override - public ModelVisualizerBuilder saveDesignSpace() { - checkNotConfigured(); - this.saveDesignSpace = true; - return this; - } - - @Override - public ModelVisualizerBuilder saveStates() { - checkNotConfigured(); - this.saveStates = true; - return this; - } -} -- cgit v1.2.3-54-g00ecf From ccf1da01870d77b99e6a1a3b78e58caf9c9ed83e Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Thu, 7 Sep 2023 01:22:52 +0200 Subject: Sample tests for the dse adapter --- .../dse/transition/objectives/QueryCriteria.java | 12 +- .../store/dse/transition/TransitionTests.java | 151 +++++++++++++++++++++ 2 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java index e2260cca..e6dddd53 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java @@ -12,27 +12,27 @@ import tools.refinery.store.query.ModelQueryBuilder; import tools.refinery.store.query.dnf.AnyQuery; public class QueryCriteria implements Criterion { - protected final boolean acceptIfHasMatch; + protected final boolean satisfiedIfHasMatch; protected final AnyQuery query; /** * Criteria based on the existence of matches evaluated on the model. * @param query The query evaluated on the model. - * @param acceptIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, + * @param satisfiedIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, * the criteria satisfied if the query has no match on the model. */ - public QueryCriteria(AnyQuery query, boolean acceptIfHasMatch) { + public QueryCriteria(AnyQuery query, boolean satisfiedIfHasMatch) { this.query = query; - this.acceptIfHasMatch = acceptIfHasMatch; + this.satisfiedIfHasMatch = satisfiedIfHasMatch; } @Override public CriterionCalculator createCalculator(Model model) { var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); - if(acceptIfHasMatch) { + if(satisfiedIfHasMatch) { return () -> resultSet.size() > 0; } else { - return () -> resultSet.size() == 0; + return () -> resultSet.size() == 0; } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java new file mode 100644 index 00000000..b89360cb --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.transition.objectives.QueryCriteria; +import tools.refinery.store.dse.transition.objectives.QueryObjective; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.FunctionalQuery; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.literal.CallPolarity; +import tools.refinery.store.query.literal.Literals; +import tools.refinery.store.query.term.Variable; +import tools.refinery.store.query.term.int_.IntTerms; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class TransitionBuildTests { + Symbol person = new Symbol<>("Person", 1, Boolean.class, false); + Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); + AnySymbolView personView = new KeyOnlyView<>(person); + AnySymbolView friendView = new KeyOnlyView<>(friend); + + /*RelationalQuery areNotFriends = Query.of("areNotFriends", + (builder, p1, p2) -> builder.clause( + personView.call(p1), + personView.call(p2), + not(friendView.call(p1, p2))))*/ + + RelationalQuery moreThan3Friends = Query.of("moreThan3Friends", + (builder, tooMuchFriends) -> builder.clause(Integer.class, (numberOfFriends) -> List.of( + numberOfFriends.assign(friendView.count(tooMuchFriends, Variable.of())), + Literals.assume(IntTerms.less(IntTerms.constant(3), numberOfFriends)), + personView.call(tooMuchFriends) + ))); + + RelationalQuery somebodyHasNoFriend = Query.of("somebodyHasNoFriend", + (builder, lonely) -> builder.clause( + personView.call(lonely), + friendView.call(CallPolarity.NEGATIVE, lonely, Variable.of()) + )); + FunctionalQuery numberOfFriends = FunctionalQuery.of(Integer.class, + (builder, output) -> builder.clause(output.assign(friendView.count(Variable.of(), Variable.of())))); + + @Test + void acceptTest() { + Model model = getModel(); + + var dse = model.getAdapter(DesignSpaceExplorationAdapter.class); + var query = model.getAdapter(ModelQueryAdapter.class); + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + assertTrue(dse.checkAccept()); + personI.put(Tuple.of(1), true); + personI.put(Tuple.of(2), true); + + query.flushChanges(); + + assertFalse(dse.checkAccept()); + friendI.put(Tuple.of(1, 2), true); + friendI.put(Tuple.of(2, 1), true); + + query.flushChanges(); + + assertTrue(dse.checkAccept()); + } + + @Test + void includeTest() { + Model model = getModel(); + + var dse = model.getAdapter(DesignSpaceExplorationAdapter.class); + var query = model.getAdapter(ModelQueryAdapter.class); + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + assertFalse(dse.checkExclude()); + + personI.put(Tuple.of(1), true); + friendI.put(Tuple.of(1, 2), true); + friendI.put(Tuple.of(1, 3), true); + friendI.put(Tuple.of(1, 4), true); + + query.flushChanges(); + assertFalse(dse.checkExclude()); + + personI.put(Tuple.of(5), true); + friendI.put(Tuple.of(1, 5), true); + + query.flushChanges(); + assertTrue(dse.checkExclude()); + + friendI.put(Tuple.of(1, 2), false); + + query.flushChanges(); + assertFalse(dse.checkExclude()); + } + + @Test + void objectiveTest() { + Model model = getModel(); + + var dse = model.getAdapter(DesignSpaceExplorationAdapter.class); + var query = model.getAdapter(ModelQueryAdapter.class); + var friendI = model.getInterpretation(friend); + + assertEquals(0.0, dse.getObjectiveValue().get(0), 0.01); + + friendI.put(Tuple.of(1, 2), true); + + query.flushChanges(); + assertEquals(1.0, dse.getObjectiveValue().get(0), 0.01); + + friendI.put(Tuple.of(1, 3), true); + friendI.put(Tuple.of(1, 4), true); + + query.flushChanges(); + assertEquals(3.0, dse.getObjectiveValue().get(0), 0.01); + } + + private Model getModel() { + var store = ModelStore.builder() + .symbols(person, friend) + .with(ViatraModelQueryAdapter.builder()) + .with(StateCoderAdapter.builder()) + .with(ModificationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .objective(new QueryObjective(numberOfFriends)) + .exclude(new QueryCriteria(moreThan3Friends, true)) + .accept(new QueryCriteria(somebodyHasNoFriend, false))) + .build(); + + return store.createEmptyModel(); + } +} -- cgit v1.2.3-54-g00ecf From 5bfea37c2d5af9e2dda7725a75cc073ca8283cfa Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Thu, 7 Sep 2023 02:07:35 +0200 Subject: missing synchronized statements and flush added --- .../tools/refinery/store/dse/strategy/BestFirstWorker.java | 6 +++++- .../transition/statespace/internal/ActivationStoreImpl.java | 2 +- .../statespace/internal/FastEquivalenceClassStore.java | 5 +++-- .../statespace/internal/ObjectivePriorityQueueImpl.java | 11 +++++------ 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index b8662d8d..f28505c3 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -11,6 +11,7 @@ import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreWorker; import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.statecoding.StateCoderAdapter; import java.util.Random; @@ -21,6 +22,7 @@ public class BestFirstWorker { final ActivationStoreWorker activationStoreWorker; final StateCoderAdapter stateCoderAdapter; final DesignSpaceExplorationAdapter explorationAdapter; + final ModelQueryAdapter queryAdapter; public BestFirstWorker(BestFirstStoreManager storeManager, Model model) { this.storeManager = storeManager; @@ -28,9 +30,9 @@ public class BestFirstWorker { explorationAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); + queryAdapter = model.getAdapter(ModelQueryAdapter.class); activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), explorationAdapter.getTransformations()); - } private VersionWithObjectiveValue last = null; @@ -93,7 +95,9 @@ public class BestFirstWorker { public RandomVisitResult visitRandomUnvisited(Random random) { if (!model.hasUncommittedChanges()) { + queryAdapter.flushChanges(); var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); + if (visitResult.successfulVisit()) { return new RandomVisitResult(submit(), visitResult.mayHaveMore()); } else { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java index 559c715a..fbd5b17f 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -29,7 +29,7 @@ public class ActivationStoreImpl implements ActivationStore { successful[0] = true; List result = new ArrayList<>(emptyEntrySizes.length); for(int emptyEntrySize : emptyEntrySizes) { - result.add(ActivationStoreListEntry.create(emptyEntrySize)); + result.add(ActivationStoreEntry.create(emptyEntrySize)); } return result; }); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java index 75e117c0..faeedba5 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java @@ -15,13 +15,14 @@ public abstract class FastEquivalenceClassStore extends AbstractEquivalenceClass final IntHashSet codes; - public FastEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + protected FastEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { super(stateCoderStoreAdapter); this.codes = new IntHashSet(); } @Override - protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, int[] emptyActivations, boolean accept) { + protected synchronized boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, + int[] emptyActivations, boolean accept) { return this.codes.add(stateCoderResult.modelCode()); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java index c88a411f..2f3e142a 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java @@ -9,7 +9,6 @@ import tools.refinery.store.dse.transition.ObjectiveValues; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.dse.transition.objectives.Objective; import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; -import tools.refinery.store.map.Version; import java.util.Comparator; import java.util.List; @@ -37,27 +36,27 @@ public class ObjectivePriorityQueueImpl implements ObjectivePriorityQueue { } @Override - public void submit(VersionWithObjectiveValue versionWithObjectiveValue) { + public synchronized void submit(VersionWithObjectiveValue versionWithObjectiveValue) { priorityQueue.add(versionWithObjectiveValue); } @Override - public void remove(VersionWithObjectiveValue versionWithObjectiveValue) { + public synchronized void remove(VersionWithObjectiveValue versionWithObjectiveValue) { priorityQueue.remove(versionWithObjectiveValue); } @Override - public int getSize() { + public synchronized int getSize() { return priorityQueue.size(); } @Override - public VersionWithObjectiveValue getBest() { + public synchronized VersionWithObjectiveValue getBest() { return priorityQueue.peek(); } @Override - public VersionWithObjectiveValue getRandom(Random random) { + public synchronized VersionWithObjectiveValue getRandom(Random random) { int randomPosition = random.nextInt(getSize()); for (VersionWithObjectiveValue entry : this.priorityQueue) { if (randomPosition-- == 0) { -- cgit v1.2.3-54-g00ecf From fd1ba7c8e9877552a293ae11aee76179ac3f0d68 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 7 Sep 2023 11:57:29 +0200 Subject: Add visualization to restructured DSE --- .../store/dse/strategy/BestFirstExplorer.java | 3 ++ .../store/dse/strategy/BestFirstWorker.java | 36 ++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'subprojects/store-dse') 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 f82d7c06..9a03b0b8 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 @@ -36,6 +36,7 @@ public class BestFirstExplorer extends BestFirstWorker { if (lastVisited == null) { lastVisited = this.restoreToBest(); if(lastVisited == null) { + visualizerAdapter.visualize(); return; } } @@ -161,5 +162,7 @@ public class BestFirstExplorer extends BestFirstWorker { */ } // Interrupted. + visualizerAdapter.visualize(); + } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index b8662d8d..b1c51506 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -11,7 +11,9 @@ import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreWorker; import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.visualization.ModelVisualizerAdapter; import java.util.Random; @@ -21,6 +23,9 @@ public class BestFirstWorker { final ActivationStoreWorker activationStoreWorker; final StateCoderAdapter stateCoderAdapter; final DesignSpaceExplorationAdapter explorationAdapter; + final ViatraModelQueryAdapter queryAdapter; + final ModelVisualizerAdapter visualizerAdapter; + final boolean isVisualizationEnabled; public BestFirstWorker(BestFirstStoreManager storeManager, Model model) { this.storeManager = storeManager; @@ -30,7 +35,10 @@ public class BestFirstWorker { stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), explorationAdapter.getTransformations()); - + visualizerAdapter = model.getAdapter(ModelVisualizerAdapter.class); + queryAdapter = model.getAdapter(ViatraModelQueryAdapter.class); + System.out.println("visualizerAdapter = " + visualizerAdapter); + isVisualizationEnabled = visualizerAdapter != null; } private VersionWithObjectiveValue last = null; @@ -44,6 +52,7 @@ public class BestFirstWorker { } Version version = model.commit(); + queryAdapter.flushChanges(); ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); last = new VersionWithObjectiveValue(version, objectiveValue); var code = stateCoderAdapter.calculateStateCode(); @@ -55,14 +64,22 @@ public class BestFirstWorker { public void restoreToLast() { if (explorationAdapter.getModel().hasUncommittedChanges()) { + var oldVersion = model.getState(); explorationAdapter.getModel().restore(last.version()); + if (isVisualizationEnabled) { + visualizerAdapter.addTransition(oldVersion, last.version(), ""); + } } } public VersionWithObjectiveValue restoreToBest() { var bestVersion = storeManager.getObjectiveStore().getBest(); if (bestVersion != null) { + var oldVersion = model.getState(); this.model.restore(bestVersion.version()); + if (isVisualizationEnabled) { + visualizerAdapter.addTransition(oldVersion, last.version(), ""); + } } return bestVersion; } @@ -95,7 +112,22 @@ public class BestFirstWorker { if (!model.hasUncommittedChanges()) { var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); if (visitResult.successfulVisit()) { - return new RandomVisitResult(submit(), visitResult.mayHaveMore()); + Version oldVersion = null; + if (isVisualizationEnabled) { + oldVersion = last.version(); + } + var submitResult = submit(); + if (isVisualizationEnabled) { + + Version newVersion = null; + if (submitResult.newVersion() != null) { + newVersion = submitResult.newVersion().version(); + visualizerAdapter.addState(newVersion, submitResult.newVersion().objectiveValue().toString()); + visualizerAdapter.addSolution(newVersion); + } + visualizerAdapter.addTransition(oldVersion, newVersion, ""); + } + return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); } else { return new RandomVisitResult(null, visitResult.mayHaveMore()); } -- cgit v1.2.3-54-g00ecf From 741ff77e0e841cdf3fd893e1fb3d1ccccc6a83e4 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 7 Sep 2023 17:34:37 +0200 Subject: fix issues with DSE framework --- .../store/dse/strategy/BestFirstExplorer.java | 2 - .../store/dse/strategy/BestFirstStoreManager.java | 22 ++- .../store/dse/strategy/BestFirstWorker.java | 64 +++---- .../statespace/EquivalenceClassStore.java | 1 + .../internal/AbstractEquivalenceClassStore.java | 7 + .../statespace/internal/ActivationStoreImpl.java | 7 +- .../statespace/internal/ActivationStoreWorker.java | 1 - .../internal/FastEquivalenceClassStore.java | 4 + .../tools/refinery/store/dse/CRAExamplesTest.java | 28 ++- .../java/tools/refinery/store/dse/DebugTest.java | 190 ++++++++++++--------- .../refinery/store/dse/tests/DummyCriterion.java | 5 + .../refinery/store/dse/tests/DummyObjective.java | 5 + .../store/dse/tests/DummyRandomCriterion.java | 25 +++ .../store/dse/tests/DummyRandomObjective.java | 23 +++ .../visualization/ModelVisualizerAdapter.java | 13 +- .../internal/ModelVisualizerAdapterImpl.java | 61 +------ .../statespace/VisualizationStore.java | 13 ++ .../internal/VisualizationStoreImpl.java | 46 +++++ 18 files changed, 310 insertions(+), 207 deletions(-) create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomCriterion.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomObjective.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/VisualizationStore.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/internal/VisualizationStoreImpl.java (limited to 'subprojects/store-dse') 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 9a03b0b8..a2b6268f 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 @@ -36,7 +36,6 @@ public class BestFirstExplorer extends BestFirstWorker { if (lastVisited == null) { lastVisited = this.restoreToBest(); if(lastVisited == null) { - visualizerAdapter.visualize(); return; } } @@ -162,7 +161,6 @@ public class BestFirstExplorer extends BestFirstWorker { */ } // Interrupted. - visualizerAdapter.visualize(); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java index 3795daa4..0b9aae9c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -18,14 +18,20 @@ import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriority import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl; import tools.refinery.store.map.Version; import tools.refinery.store.model.ModelStore; +import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.statecoding.StateCoderStoreAdapter; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; +import tools.refinery.visualization.statespace.VisualizationStore; +import tools.refinery.visualization.statespace.internal.VisualizationStoreImpl; public class BestFirstStoreManager { + ModelStore modelStore; ObjectivePriorityQueue objectiveStore; ActivationStore activationStore; SolutionStore solutionStore; EquivalenceClassStore equivalenceClassStore; + VisualizationStore visualizationStore; public BestFirstStoreManager(ModelStore modelStore) { this.modelStore = modelStore; @@ -35,17 +41,17 @@ public class BestFirstStoreManager { objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); Procedure whenAllActivationsVisited = x -> objectiveStore.remove(x); activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); - solutionStore = new SolutionStoreImpl(10); + solutionStore = new SolutionStoreImpl(50); equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { @Override protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { - objectiveStore.submit(version); - activationStore.markNewAsVisited(version, emptyActivations); - if(accept) { - solutionStore.submit(version); - } + throw new UnsupportedOperationException("This equivalence storage is not prepared to resolve symmetries!"); } }; + visualizationStore = new VisualizationStoreImpl(); + } + public ModelStore getModelStore() { + return modelStore; } ObjectivePriorityQueue getObjectiveStore() { @@ -64,6 +70,10 @@ public class BestFirstStoreManager { return equivalenceClassStore; } + public VisualizationStore getVisualizationStore() { + return visualizationStore; + } + public void startExploration(Version initial) { BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial), 1); bestFirstExplorer.explore(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index 0f1702ea..f1bec14f 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -13,7 +13,7 @@ import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.statecoding.StateCoderAdapter; -import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.statespace.VisualizationStore; import java.util.Random; @@ -24,7 +24,7 @@ public class BestFirstWorker { final StateCoderAdapter stateCoderAdapter; final DesignSpaceExplorationAdapter explorationAdapter; final ModelQueryAdapter queryAdapter; - final ModelVisualizerAdapter visualizerAdapter; + final VisualizationStore visualizationStore; final boolean isVisualizationEnabled; public BestFirstWorker(BestFirstStoreManager storeManager, Model model) { @@ -36,9 +36,8 @@ public class BestFirstWorker { queryAdapter = model.getAdapter(ModelQueryAdapter.class); activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), explorationAdapter.getTransformations()); - visualizerAdapter = model.getAdapter(ModelVisualizerAdapter.class); - System.out.println("visualizerAdapter = " + visualizerAdapter); - isVisualizationEnabled = visualizerAdapter != null; + visualizationStore = storeManager.getVisualizationStore(); + isVisualizationEnabled = visualizationStore != null; } private VersionWithObjectiveValue last = null; @@ -47,28 +46,41 @@ public class BestFirstWorker { public SubmitResult submit() { if (explorationAdapter.checkExclude()) { - last = null; return new SubmitResult(false, false, null, null); } - Version version = model.commit(); - queryAdapter.flushChanges(); - ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); - last = new VersionWithObjectiveValue(version, objectiveValue); var code = stateCoderAdapter.calculateStateCode(); - var accepted = explorationAdapter.checkAccept(); - boolean isNew = storeManager.getEquivalenceClassStore().submit(last, code, - activationStoreWorker.calculateEmptyActivationSize(), accepted); - return new SubmitResult(isNew, accepted, objectiveValue, isNew ? last : null); + + boolean isNew = storeManager.getEquivalenceClassStore().submit(code); + if (isNew) { + Version version = model.commit(); + ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); + var versionWithObjectiveValue = new VersionWithObjectiveValue(version, objectiveValue); + last = versionWithObjectiveValue; + var accepted = explorationAdapter.checkAccept(); + + storeManager.getObjectiveStore().submit(versionWithObjectiveValue); + storeManager.getActivationStore().markNewAsVisited(versionWithObjectiveValue, activationStoreWorker.calculateEmptyActivationSize()); + if(accepted) { + storeManager.solutionStore.submit(versionWithObjectiveValue); + } + + if (isVisualizationEnabled) { + visualizationStore.addState(last.version(), last.objectiveValue().toString()); + if (accepted) { + visualizationStore.addSolution(last.version()); + } + } + + return new SubmitResult(true, accepted, objectiveValue, last); + } + + return new SubmitResult(false, false, null, null); } public void restoreToLast() { if (explorationAdapter.getModel().hasUncommittedChanges()) { - var oldVersion = model.getState(); explorationAdapter.getModel().restore(last.version()); - if (isVisualizationEnabled) { - visualizerAdapter.addTransition(oldVersion, last.version(), ""); - } } } @@ -78,7 +90,7 @@ public class BestFirstWorker { var oldVersion = model.getState(); this.model.restore(bestVersion.version()); if (isVisualizationEnabled) { - visualizerAdapter.addTransition(oldVersion, last.version(), ""); + visualizationStore.addTransition(oldVersion, last.version(), ""); } } return bestVersion; @@ -105,7 +117,7 @@ public class BestFirstWorker { } } - record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) { + public record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) { } public RandomVisitResult visitRandomUnvisited(Random random) { @@ -119,15 +131,9 @@ public class BestFirstWorker { oldVersion = last.version(); } var submitResult = submit(); - if (isVisualizationEnabled) { - - Version newVersion = null; - if (submitResult.newVersion() != null) { - newVersion = submitResult.newVersion().version(); - visualizerAdapter.addState(newVersion, submitResult.newVersion().objectiveValue().toString()); - visualizerAdapter.addSolution(newVersion); - } - visualizerAdapter.addTransition(oldVersion, newVersion, ""); + if (isVisualizationEnabled && submitResult.newVersion() != null) { + var newVersion = submitResult.newVersion().version(); + visualizationStore.addTransition(oldVersion, newVersion, ""); } return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); } else { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java index bbe26fe5..28d1488b 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java @@ -10,6 +10,7 @@ import tools.refinery.store.statecoding.StateCoderResult; public interface EquivalenceClassStore { boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, int[] emptyActivations, boolean accept); + boolean submit(StateCoderResult stateCoderResult); boolean hasUnresolvedSymmetry(); void resolveOneSymmetry(); int getNumberOfUnresolvedSymmetries(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java index 8466a0f3..b5087e86 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java @@ -22,6 +22,13 @@ public abstract class AbstractEquivalenceClassStore implements EquivalenceClassS protected abstract boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, int[] emptyActivations, boolean accept); + public abstract boolean tryToAdd(StateCoderResult stateCoderResult); + + @Override + public boolean submit(StateCoderResult stateCoderResult) { + return tryToAdd(stateCoderResult); + } + @Override public synchronized boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, int[] emptyActivations, boolean accept) { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java index fbd5b17f..9186741f 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -69,7 +69,7 @@ public class ActivationStoreImpl implements ActivationStore { if(!hasMoreInActivation) { boolean hasMoreInOtherTransformation = false; for (var e : entries) { - if (e != entry && e.getNumberOfVisitedActivations() > 0) { + if (e != entry && e.getNumberOfUnvisitedActivations() > 0) { hasMoreInOtherTransformation = true; break; } @@ -108,6 +108,11 @@ public class ActivationStoreImpl implements ActivationStore { sum1 += entry.getNumberOfUnvisitedActivations(); } + if(sum1 == 0) { + this.actionWhenAllActivationVisited.accept(version); + return new VisitResult(false, false, -1, -1); + } + int selected = random.nextInt(sum1); int sum2 = 0; int transformation = 0; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java index e05f5122..881b133c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java @@ -8,7 +8,6 @@ package tools.refinery.store.dse.transition.statespace.internal; import tools.refinery.store.dse.transition.Transformation; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.dse.transition.statespace.ActivationStore; -import tools.refinery.store.map.Version; import java.util.List; import java.util.Random; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java index faeedba5..6eba87d4 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java @@ -26,6 +26,10 @@ public abstract class FastEquivalenceClassStore extends AbstractEquivalenceClass return this.codes.add(stateCoderResult.modelCode()); } + public synchronized boolean tryToAdd(StateCoderResult stateCoderResult) { + return this.codes.add(stateCoderResult.modelCode()); + } + @Override public void resolveOneSymmetry() { throw new IllegalArgumentException("This equivalence storage is not prepared to resolve symmetries!"); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index f4c38b18..831b9ff0 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -9,6 +9,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.modification.ModificationAdapter; import tools.refinery.store.dse.strategy.BestFirstStoreManager; +import tools.refinery.store.dse.tests.DummyRandomCriterion; +import tools.refinery.store.dse.tests.DummyRandomObjective; import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.dse.tests.DummyCriterion; import tools.refinery.store.dse.tests.DummyObjective; @@ -188,7 +190,7 @@ class CRAExamplesTest { }); @Test -// @Disabled("This test is only for debugging purposes") + @Disabled("This test is only for debugging purposes") void craTest() { var store = ModelStore.builder() .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, @@ -197,24 +199,20 @@ class CRAExamplesTest { .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, moveFeaturePrecondition)) -// .with(ModelVisualizerAdapter.builder() -// .withOutputpath("test_output") -// .withFormat(FileFormat.DOT) -// .withFormat(FileFormat.SVG) -// .saveStates() -// .saveDesignSpace() -// ) + .with(ModelVisualizerAdapter.builder() + .withOutputpath("test_output") + .withFormat(FileFormat.DOT) + .withFormat(FileFormat.SVG) + .saveStates() + .saveDesignSpace() + ) .with(StateCoderAdapter.builder()) .with(ModificationAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) - .objectives(new DummyObjective()) - .accept(new DummyCriterion(true)) + .objectives(new DummyRandomObjective()) + .accept(new DummyRandomCriterion()) .exclude(new DummyCriterion(false)) -// .objectives(new AlwaysSatisfiedRandomHardObjective()) -// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() -// .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter()) ) .build(); @@ -296,7 +294,5 @@ class CRAExamplesTest { bestFirst.startExploration(initialVersion); var resultStore = bestFirst.getSolutionStore(); System.out.println("states size: " + resultStore.getSolutions().size()); - var modelVisualizerAdapter = model.getAdapter(ModelVisualizerAdapter.class); - modelVisualizerAdapter.visualize(); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 1d757a5f..87c3892a 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -5,9 +5,27 @@ */ package tools.refinery.store.dse; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.strategy.BestFirstStoreManager; +import tools.refinery.store.dse.tests.DummyCriterion; +import tools.refinery.store.dse.tests.DummyObjective; +import tools.refinery.store.dse.tests.DummyRandomCriterion; +import tools.refinery.store.dse.tests.DummyRandomObjective; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -29,88 +47,92 @@ class DebugTest { private static final AnySymbolView classesView = new KeyOnlyView<>(classes); -// @Test -// @Disabled("This test is only for debugging purposes") -// void BFSTest() { -// var createClassPrecondition = Query.of("CreateClassPrecondition", -// (builder, model) -> builder.clause( -// classModelView.call(model) -// )); -// -// var createClassRule = new TransformationRule("CreateClass", -// createClassPrecondition, -// (model) -> { -// var classesInterpretation = model.getInterpretation(classes); -// var classElementInterpretation = model.getInterpretation(classElement); -// return ((Tuple activation) -> { -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); -// -// var newClassElement = dseAdapter.createObject(); -// var newClassElementId = newClassElement.get(0); -// -// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); -// classElementInterpretation.put(Tuple.of(newClassElementId), true); -// }); -// }); -// -// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", -// (builder, model) -> builder.clause( -// classModelView.call(model) -// )); -// -// var createFeatureRule = new TransformationRule("CreateFeature", -// createFeaturePrecondition, -// (model) -> { -// var featuresInterpretation = model.getInterpretation(features); -// var featureInterpretation = model.getInterpretation(feature); -// return ((Tuple activation) -> { -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); -// -// var newClassElement = dseAdapter.createObject(); -// var newClassElementId = newClassElement.get(0); -// -// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); -// featureInterpretation.put(Tuple.of(newClassElementId), true); -// }); -// }); -// -// var store = ModelStore.builder() -// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) -// .with(ViatraModelQueryAdapter.builder() -// .queries(createClassPrecondition, createFeaturePrecondition)) -// .with(ModelVisualizerAdapter.builder() -// .withOutputpath("test_output") -// .withFormat(FileFormat.DOT) -// .withFormat(FileFormat.SVG) -// .saveStates() -// .saveDesignSpace() -// ) -// .with(DesignSpaceExplorationAdapter.builder() -// .transformations(createClassRule, createFeatureRule) -// .objectives(new AlwaysSatisfiedRandomHardObjective()) -// .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -//// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -//// .goOnOnlyIfFitnessIsBetter() -// )) -// .build(); -// -// var model = store.createEmptyModel(); -// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -//// dseAdapter.setRandom(1); -// var queryEngine = model.getAdapter(ModelQueryAdapter.class); -// -// var modelElementInterpretation = model.getInterpretation(classModel); -// var classElementInterpretation = model.getInterpretation(classElement); -// var modelElement = dseAdapter.createObject(); -// modelElementInterpretation.put(modelElement, true); -// classElementInterpretation.put(modelElement, true); -// queryEngine.flushChanges(); -// -// -// var states = dseAdapter.explore(); -// System.out.println("states size: " + states.size()); -// -// } + @Test + @Disabled("This test is only for debugging purposes") + void BFSTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(ModificationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(ModificationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder() + .withOutputpath("test_output") + .withFormat(FileFormat.DOT) + .withFormat(FileFormat.SVG) + .saveStates() + .saveDesignSpace() + ) + .with(StateCoderAdapter.builder()) + .with(ModificationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .objectives(new DummyRandomObjective()) + .accept(new DummyRandomCriterion()) + .exclude(new DummyRandomCriterion()) + ) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(ModificationAdapter.class); +// dseAdapter.setRandom(1); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var modelElement = dseAdapter.createObject(); + modelElementInterpretation.put(modelElement, true); + classElementInterpretation.put(modelElement, true); + var initialVersion = model.commit(); + queryEngine.flushChanges(); + + + var bestFirst = new BestFirstStoreManager(store); + bestFirst.startExploration(initialVersion); + var resultStore = bestFirst.getSolutionStore(); + System.out.println("states size: " + resultStore.getSolutions().size()); + + } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java index 6bcbd1c3..2d5a0f36 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.tests; import tools.refinery.store.dse.transition.objectives.Criterion; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java index 77a23d62..f5d281aa 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.tests; import tools.refinery.store.dse.transition.objectives.Objective; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomCriterion.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomCriterion.java new file mode 100644 index 00000000..71fd4403 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomCriterion.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.tests; + +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.model.Model; + +import java.util.Random; + +public class DummyRandomCriterion implements Criterion { + + @SuppressWarnings("squid:S2245") + private static final Random random = new Random(9856654); + public DummyRandomCriterion() { + } + + @Override + public CriterionCalculator createCalculator(Model model) { + return random::nextBoolean; + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomObjective.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomObjective.java new file mode 100644 index 00000000..219e74c6 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomObjective.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.tests; + +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; + +import java.util.Random; + +public class DummyRandomObjective implements Objective { + + @SuppressWarnings("squid:S2245") + private static final Random random = new Random(9856654); + + @Override + public ObjectiveCalculator createCalculator(Model model) { + return random::nextDouble; + } +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java index ae87d8ac..ad80bbc6 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -9,6 +9,7 @@ import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.Version; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; +import tools.refinery.visualization.statespace.VisualizationStore; import java.util.Collection; @@ -18,15 +19,5 @@ public interface ModelVisualizerAdapter extends ModelAdapter { static ModelVisualizerBuilder builder() { return new ModelVisualizerBuilderImpl(); } - - public void addTransition(Version from, Version to, String action); - - - public void addTransition(Version from, Version to, String action, Tuple activation); - public void addState(Version state); - public void addState(Version state, Collection fitness); - public void addState(Version state, String label); - public void addSolution(Version state); - public void visualize(); - + void visualize(VisualizationStore visualizationStore); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 531969b4..031b2e6b 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -13,6 +13,7 @@ import tools.refinery.store.representation.TruthValue; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; import tools.refinery.visualization.ModelVisualizerStoreAdapter; +import tools.refinery.visualization.statespace.VisualizationStore; import java.io.*; import java.util.*; @@ -281,62 +282,6 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return true; } - @Override - public void addTransition(Version from, Version to, String action) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); - } - - @Override - public void addTransition(Version from, Version to, String action, Tuple activation) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); - - - for (int i = 0; i < activation.getSize(); i++) { - designSpaceBuilder.append(activation.get(i)); - if (i < activation.getSize() - 1) { - designSpaceBuilder.append(", "); - } - } - designSpaceBuilder.append("\"]\n"); - } - - @Override - public void addState(Version state) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addState(Version state, Collection fitness) { - var labelBuilder = new StringBuilder(); - for (var f : fitness) { - labelBuilder.append(f).append(", "); - } - addState(state, labelBuilder.toString()); - } - - @Override - public void addState(Version state, String label) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); - designSpaceBuilder.append(label); - designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addSolution(Version state) { - addState(state); - designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); - } - private String buildDesignSpaceDot() { designSpaceBuilder.append("}"); return designSpaceBuilder.toString(); @@ -381,7 +326,9 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } @Override - public void visualize() { + public void visualize(VisualizationStore visualizationStore) { + this.designSpaceBuilder.append(visualizationStore.getDesignSpaceStringBuilder()); + this.states.putAll(visualizationStore.getStates()); renderDesignSpace(outputPath, formats); } } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/VisualizationStore.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/VisualizationStore.java new file mode 100644 index 00000000..0bf9d4a5 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/VisualizationStore.java @@ -0,0 +1,13 @@ +package tools.refinery.visualization.statespace; + +import tools.refinery.store.map.Version; + +import java.util.Map; + +public interface VisualizationStore { + void addState(Version state, String label); + void addSolution(Version state); + void addTransition(Version from, Version to, String label); + StringBuilder getDesignSpaceStringBuilder(); + Map getStates(); +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/internal/VisualizationStoreImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/internal/VisualizationStoreImpl.java new file mode 100644 index 00000000..13794667 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/statespace/internal/VisualizationStoreImpl.java @@ -0,0 +1,46 @@ +package tools.refinery.visualization.statespace.internal; + +import tools.refinery.store.map.Version; +import tools.refinery.visualization.statespace.VisualizationStore; + +import java.util.HashMap; +import java.util.Map; + +public class VisualizationStoreImpl implements VisualizationStore { + + private final Map states = new HashMap<>(); + private int transitionCounter = 0; + private Integer numberOfStates = 0; + private final StringBuilder designSpaceBuilder = new StringBuilder(); + + @Override + public synchronized void addState(Version state, String label) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); + designSpaceBuilder.append(label); + designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + + @Override + public synchronized void addSolution(Version state) { + designSpaceBuilder.append(states.get(state)).append(" [peripheries = 2]\n"); + } + + @Override + public synchronized void addTransition(Version from, Version to, String label) { + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(label).append("\"]\n"); + } + + public synchronized StringBuilder getDesignSpaceStringBuilder() { + return designSpaceBuilder; + } + + @Override + public Map getStates() { + return states; + } +} -- cgit v1.2.3-54-g00ecf From a128e05f18a101983d331d0819008740b521d6df Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 7 Sep 2023 18:04:42 +0200 Subject: feat: declarative DSE rules and model refinement --- .../transition/DesignSpaceExplorationAdapter.java | 11 +-- .../dse/transition/objectives/AndCriterion.java | 53 +++++++++++++ .../transition/objectives/CompositeCriterion.java | 43 ++++++++++ .../transition/objectives/CompositeObjective.java | 69 ++++++++++++++++ .../dse/transition/objectives/CountObjective.java | 47 +++++++++++ .../store/dse/transition/objectives/Criteria.java | 47 +++++++++++ .../store/dse/transition/objectives/Criterion.java | 7 ++ .../store/dse/transition/objectives/Objective.java | 8 ++ .../dse/transition/objectives/Objectives.java | 41 ++++++++++ .../dse/transition/objectives/OrCriterion.java | 53 +++++++++++++ .../dse/transition/objectives/QueryCriteria.java | 44 ----------- .../dse/transition/objectives/QueryCriterion.java | 59 ++++++++++++++ .../dse/transition/objectives/QueryObjective.java | 15 ++-- .../scope/internal/LowerTypeScopePropagator.java | 40 ++++++++-- .../store/reasoning/scope/internal/MultiView.java | 23 ------ .../scope/internal/ScopePropagatorAdapterImpl.java | 22 ++++-- .../scope/internal/ScopePropagatorBuilderImpl.java | 11 +-- .../scope/internal/TypeScopePropagator.java | 12 ++- .../scope/internal/UpperTypeScopePropagator.java | 9 ++- .../refinery/store/reasoning/ReasoningAdapter.java | 3 + .../refinery/store/reasoning/ReasoningBuilder.java | 12 +++ .../reasoning/actions/FocusActionLiteral.java | 48 +++++++++++ .../reasoning/actions/MergeActionLiteral.java | 60 ++++++++++++++ .../reasoning/actions/PartialActionLiterals.java | 38 +++++++++ .../reasoning/internal/ReasoningAdapterImpl.java | 29 +++++++ .../reasoning/internal/ReasoningBuilderImpl.java | 18 ++++- .../translator/PartialRelationTranslator.java | 92 +++++++++++++++++++++- .../translator/PartialSymbolTranslator.java | 60 +++++++++++++- .../ContainmentHierarchyTranslator.java | 42 +++++++++- .../DirectedCrossReferenceTranslator.java | 18 ++++- .../UndirectedCrossReferenceTranslator.java | 18 ++++- .../multiobject/MultiObjectTranslator.java | 35 ++++++-- .../translator/multiobject/MultiView.java | 23 ++++++ .../InvalidMultiplicityErrorTranslator.java | 35 ++++++-- .../translator/predicate/PredicateTranslator.java | 9 ++- .../typehierarchy/TypeHierarchyTranslator.java | 34 +++++++- 36 files changed, 1047 insertions(+), 141 deletions(-) create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/AndCriterion.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeCriterion.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CountObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criteria.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objectives.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/OrCriterion.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriterion.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/MultiView.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/FocusActionLiteral.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/MergeActionLiteral.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java index 37448309..d326f1dd 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java @@ -7,25 +7,22 @@ package tools.refinery.store.dse.transition; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.dse.transition.internal.DesignSpaceExplorationBuilderImpl; -import tools.refinery.store.map.Version; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; -import java.util.Collection; import java.util.List; public interface DesignSpaceExplorationAdapter extends ModelAdapter { - - - @Override DesignSpaceExplorationStoreAdapter getStoreAdapter(); static DesignSpaceExplorationBuilder builder() { return new DesignSpaceExplorationBuilderImpl(); } + List getTransformations(); + boolean checkAccept(); + boolean checkExclude(); + ObjectiveValue getObjectiveValue(); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/AndCriterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/AndCriterion.java new file mode 100644 index 00000000..0ad2b7a4 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/AndCriterion.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.literal.Reduction; + +import java.util.ArrayList; +import java.util.Collection; + +public final class AndCriterion extends CompositeCriterion { + AndCriterion(Collection criteria) { + super(criteria); + } + + @Override + public Reduction getReduction(ModelStore store) { + for (var criterion : getCriteria()) { + var reduction = criterion.getReduction(store); + if (reduction == Reduction.ALWAYS_FALSE) { + return Reduction.ALWAYS_FALSE; + } else if (reduction == Reduction.NOT_REDUCIBLE) { + return Reduction.NOT_REDUCIBLE; + } + } + return Reduction.ALWAYS_TRUE; + } + + @Override + public CriterionCalculator createCalculator(Model model) { + var calculators = new ArrayList(); + for (var criterion : getCriteria()) { + var reduction = criterion.getReduction(model.getStore()); + if (reduction == Reduction.ALWAYS_FALSE) { + return () -> false; + } else if (reduction == Reduction.NOT_REDUCIBLE) { + calculators.add(criterion.createCalculator(model)); + } + } + return () -> { + for (var calculator : calculators) { + if (!calculator.isSatisfied()) { + return false; + } + } + return true; + }; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeCriterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeCriterion.java new file mode 100644 index 00000000..5746cc7e --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeCriterion.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.literal.Reduction; + +import java.util.*; + +public abstract sealed class CompositeCriterion implements Criterion permits AndCriterion, OrCriterion { + private final List criteria; + + protected CompositeCriterion(Collection criteria) { + var deDuplicatedCriteria = new LinkedHashSet(); + for (var criterion : criteria) { + if (criterion.getClass() == this.getClass()) { + var childCriteria = ((CompositeCriterion) criterion).getCriteria(); + deDuplicatedCriteria.addAll(childCriteria); + } else { + deDuplicatedCriteria.add(criterion); + } + } + this.criteria = List.copyOf(deDuplicatedCriteria); + } + + public List getCriteria() { + return criteria; + } + + @Override + public abstract Reduction getReduction(ModelStore store); + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + for (var criterion : criteria) { + criterion.configure(storeBuilder); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeObjective.java new file mode 100644 index 00000000..192a824b --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeObjective.java @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class CompositeObjective implements Objective { + private final List objectives; + + CompositeObjective(Collection objectives) { + var unwrappedObjectives = new ArrayList(); + for (var objective : objectives) { + if (objective instanceof CompositeObjective compositeObjective) { + unwrappedObjectives.addAll(compositeObjective.getObjectives()); + } else { + unwrappedObjectives.add(objective); + } + } + this.objectives = Collections.unmodifiableList(unwrappedObjectives); + } + + public List getObjectives() { + return objectives; + } + + @Override + public boolean isAlwaysZero(ModelStore store) { + for (var objective : objectives) { + if (!objective.isAlwaysZero(store)) { + return false; + } + } + return true; + } + + @Override + public ObjectiveCalculator createCalculator(Model model) { + var calculators = new ArrayList(); + for (var objective : objectives) { + if (!objective.isAlwaysZero(model.getStore())) { + calculators.add(objective.createCalculator(model)); + } + } + return () -> { + double value = 0; + for (var calculator : calculators) { + value += calculator.getValue(); + } + return value; + }; + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + for (var objective : objectives) { + objective.configure(storeBuilder); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CountObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CountObjective.java new file mode 100644 index 00000000..fbd05ded --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CountObjective.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.ModelQueryStoreAdapter; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.literal.Reduction; + +public class CountObjective implements Objective { + private final RelationalQuery query; + private final double weight; + + public CountObjective(RelationalQuery query) { + this(query, 1); + } + + public CountObjective(RelationalQuery query, double weight) { + this.query = query; + this.weight = weight; + } + + @Override + public boolean isAlwaysZero(ModelStore store) { + var queryStore = store.getAdapter(ModelQueryStoreAdapter.class); + var canonicalQuery = queryStore.getCanonicalQuery(query); + return canonicalQuery.getDnf().getReduction() == Reduction.ALWAYS_FALSE; + } + + @Override + public ObjectiveCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); + return () -> resultSet.size() * weight; + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criteria.java new file mode 100644 index 00000000..0e4ec5c9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criteria.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.query.dnf.AnyQuery; + +import java.util.Collection; +import java.util.List; + +public final class Criteria { + private Criteria() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static QueryCriterion whenHasMatch(AnyQuery query) { + return new QueryCriterion(query, true); + } + + public static QueryCriterion whenNoMatch(AnyQuery query) { + return new QueryCriterion(query, false); + } + + public static Criterion and(Criterion... criteria) { + return and(List.of(criteria)); + } + + public static Criterion and(Collection criteria) { + if (criteria.size() == 1) { + return criteria.iterator().next(); + } + return new AndCriterion(criteria); + } + + public static Criterion or(Criterion... criteria) { + return or(List.of(criteria)); + } + + public static Criterion or(Collection criteria) { + if (criteria.size() == 1) { + return criteria.iterator().next(); + } + return new OrCriterion(criteria); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java index 4365cf9c..c827f20e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java @@ -6,10 +6,17 @@ package tools.refinery.store.dse.transition.objectives; import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.literal.Reduction; public interface Criterion { default void configure(ModelStoreBuilder storeBuilder) { } + + default Reduction getReduction(ModelStore store) { + return Reduction.NOT_REDUCIBLE; + } + CriterionCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java index d2476a2e..49c34d87 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java @@ -6,10 +6,18 @@ package tools.refinery.store.dse.transition.objectives; import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; public interface Objective { default void configure(ModelStoreBuilder storeBuilder) { } + + // The name {@code isAlwaysZero} is more straightforward than something like {@code canBeNonZero}. + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + default boolean isAlwaysZero(ModelStore store) { + return false; + } + ObjectiveCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objectives.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objectives.java new file mode 100644 index 00000000..e552d14c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objectives.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.query.dnf.FunctionalQuery; +import tools.refinery.store.query.dnf.RelationalQuery; + +import java.util.Collection; +import java.util.List; + +public final class Objectives { + private Objectives() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static CountObjective count(RelationalQuery query, double weight) { + return new CountObjective(query, weight); + } + + public static CountObjective count(RelationalQuery query) { + return new CountObjective(query); + } + + public static QueryObjective value(FunctionalQuery query) { + return new QueryObjective(query); + } + + public static Objective sum(Objective... objectives) { + return sum(List.of(objectives)); + } + + public static Objective sum(Collection objectives) { + if (objectives.size() == 1) { + return objectives.iterator().next(); + } + return new CompositeObjective(objectives); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/OrCriterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/OrCriterion.java new file mode 100644 index 00000000..7a8d7778 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/OrCriterion.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.literal.Reduction; + +import java.util.ArrayList; +import java.util.Collection; + +public final class OrCriterion extends CompositeCriterion { + OrCriterion(Collection criteria) { + super(criteria); + } + + @Override + public Reduction getReduction(ModelStore store) { + for (var criterion : getCriteria()) { + var reduction = criterion.getReduction(store); + if (reduction == Reduction.ALWAYS_TRUE) { + return Reduction.ALWAYS_TRUE; + } else if (reduction == Reduction.NOT_REDUCIBLE) { + return Reduction.NOT_REDUCIBLE; + } + } + return Reduction.ALWAYS_FALSE; + } + + @Override + public CriterionCalculator createCalculator(Model model) { + var calculators = new ArrayList(); + for (var criterion : getCriteria()) { + var reduction = criterion.getReduction(model.getStore()); + if (reduction == Reduction.ALWAYS_TRUE) { + return () -> true; + } else if (reduction == Reduction.NOT_REDUCIBLE) { + calculators.add(criterion.createCalculator(model)); + } + } + return () -> { + for (var calculator : calculators) { + if (calculator.isSatisfied()) { + return true; + } + } + return false; + }; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java deleted file mode 100644 index 8d0a56cf..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.transition.objectives; - -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.ModelQueryBuilder; -import tools.refinery.store.query.dnf.AnyQuery; - -public class QueryCriteria implements Criterion { - protected final boolean acceptIfHasMatch; - protected final AnyQuery query; - - /** - * Criteria based on the existence of matches evaluated on the model. - * @param query The query evaluated on the model. - * @param acceptIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, - * the criteria satisfied if the query has no match on the model. - */ - public QueryCriteria(AnyQuery query, boolean acceptIfHasMatch) { - this.query = query; - this.acceptIfHasMatch = acceptIfHasMatch; - } - - @Override - public CriterionCalculator createCalculator(Model model) { - var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); - if(acceptIfHasMatch) { - return () -> resultSet.size() > 0; - } else { - return () -> resultSet.size() == 0; - } - } - - @Override - public void configure(ModelStoreBuilder storeBuilder) { - Criterion.super.configure(storeBuilder); - storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriterion.java new file mode 100644 index 00000000..e15e4e41 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriterion.java @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.ModelQueryStoreAdapter; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.literal.Reduction; + +public class QueryCriterion implements Criterion { + protected final boolean satisfiedIfHasMatch; + protected final AnyQuery query; + + /** + * Criteria based on the existence of matches evaluated on the model. + * + * @param query The query evaluated on the model. + * @param satisfiedIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, + * the criteria satisfied if the query has no match on the model. + */ + public QueryCriterion(AnyQuery query, boolean satisfiedIfHasMatch) { + this.query = query; + this.satisfiedIfHasMatch = satisfiedIfHasMatch; + } + + @Override + public Reduction getReduction(ModelStore store) { + var queryStore = store.getAdapter(ModelQueryStoreAdapter.class); + var canonicalQuery = queryStore.getCanonicalQuery(query); + var reduction = canonicalQuery.getDnf().getReduction(); + if (satisfiedIfHasMatch) { + return reduction; + } + return reduction.negate(); + } + + @Override + public CriterionCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); + if (satisfiedIfHasMatch) { + return () -> resultSet.size() > 0; + } else { + return () -> resultSet.size() == 0; + } + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + Criterion.super.configure(storeBuilder); + storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java index 9553e0e0..9f4bb536 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -15,6 +15,10 @@ public class QueryObjective implements Objective { protected final FunctionalQuery objectiveFunction; public QueryObjective(FunctionalQuery objectiveFunction) { + if (objectiveFunction.arity() != 0) { + throw new IllegalArgumentException("Objective functions must have 0 parameters, got %d instead" + .formatted(objectiveFunction.arity())); + } this.objectiveFunction = objectiveFunction; } @@ -23,22 +27,15 @@ public class QueryObjective implements Objective { var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(objectiveFunction); return () -> { var cursor = resultSet.getAll(); - boolean hasElement = cursor.move(); - if(hasElement) { - double result = cursor.getValue().doubleValue(); - if(cursor.move()) { - throw new IllegalStateException("Query providing the objective function has multiple values!"); - } - return result; - } else { + if (!cursor.move()) { throw new IllegalStateException("Query providing the objective function has no values!"); } + return cursor.getValue().doubleValue(); }; } @Override public void configure(ModelStoreBuilder storeBuilder) { - Objective.super.configure(storeBuilder); storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction); } } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java index b1c421b7..393c4b72 100644 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java @@ -5,43 +5,55 @@ */ package tools.refinery.store.reasoning.scope.internal; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.objectives.Criteria; +import tools.refinery.store.dse.transition.objectives.Objectives; +import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.dnf.AnyQuery; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.term.Variable; +import tools.refinery.store.reasoning.ReasoningBuilder; +import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; import tools.refinery.store.reasoning.representation.PartialRelation; import java.util.Collection; import java.util.List; +import static tools.refinery.store.query.literal.Literals.check; +import static tools.refinery.store.query.term.int_.IntTerms.*; import static tools.refinery.store.reasoning.literal.PartialLiterals.may; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; class LowerTypeScopePropagator extends TypeScopePropagator { private final int lowerBound; private LowerTypeScopePropagator(ScopePropagatorAdapterImpl adapter, int lowerBound, RelationalQuery allQuery, - RelationalQuery multiQuery) { + RelationalQuery multiQuery) { super(adapter, allQuery, multiQuery); this.lowerBound = lowerBound; } @Override public void updateBounds() { - constraint.setLb(lowerBound - getSingleCount()); + constraint.setLb((lowerBound - getSingleCount())); } - public static class Factory implements TypeScopePropagator.Factory { + public static class Factory extends TypeScopePropagator.Factory { + private final PartialRelation type; private final int lowerBound; private final RelationalQuery allMay; private final RelationalQuery multiMay; - public Factory(RelationalQuery multi, PartialRelation type, int lowerBound) { + public Factory(PartialRelation type, int lowerBound) { + this.type = type; this.lowerBound = lowerBound; allMay = Query.of(type.name() + "#may", (builder, instance) -> builder.clause( may(type.call(instance)) )); multiMay = Query.of(type.name() + "#multiMay", (builder, instance) -> builder.clause( may(type.call(instance)), - multi.call(instance) + MULTI_VIEW.call(instance) )); } @@ -51,8 +63,24 @@ class LowerTypeScopePropagator extends TypeScopePropagator { } @Override - public Collection getQueries() { + protected Collection getQueries() { return List.of(allMay, multiMay); } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + super.configure(storeBuilder); + + var requiredObjects = Query.of(type.name() + "#required", Integer.class, (builder, output) -> builder + .clause(Integer.class, currentCount -> List.of( + new CountCandidateLowerBoundLiteral(currentCount, type, List.of(Variable.of())), + output.assign(sub(currentCount, constant(lowerBound))), + check(greater(currentCount, constant(0))) + ))); + + storeBuilder.getAdapter(ReasoningBuilder.class).objective(Objectives.value(requiredObjects)); + storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class).ifPresent(dseBuilder -> + dseBuilder.accept(Criteria.whenNoMatch(requiredObjects))); + } } } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/MultiView.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/MultiView.java deleted file mode 100644 index cea4e07d..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/MultiView.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import tools.refinery.store.query.view.TuplePreservingView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.representation.cardinality.CardinalityInterval; -import tools.refinery.store.representation.cardinality.CardinalityIntervals; -import tools.refinery.store.tuple.Tuple; - -class MultiView extends TuplePreservingView { - protected MultiView(Symbol symbol) { - super(symbol, "multi"); - } - - @Override - protected boolean doFilter(Tuple key, CardinalityInterval value) { - return !CardinalityIntervals.ONE.equals(value); - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java index c257df6b..99c501ce 100644 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java @@ -85,12 +85,7 @@ class ScopePropagatorAdapterImpl implements ScopePropagatorAdapter { int nodeId = key.get(0); if ((toValue == null || toValue.equals(CardinalityIntervals.ONE))) { if (fromValue != null && !fromValue.equals(CardinalityIntervals.ONE)) { - var variable = variables.get(nodeId); - if (variable == null || !activeVariables.remove(nodeId)) { - throw new AssertionError("Variable not active: " + nodeId); - } - variable.setBounds(0, 0); - markAsChanged(); + removeActiveVariable(toValue, nodeId); } return; } @@ -115,6 +110,21 @@ class ScopePropagatorAdapterImpl implements ScopePropagatorAdapter { } } + private void removeActiveVariable(CardinalityInterval toValue, int nodeId) { + var variable = variables.get(nodeId); + if (variable == null || !activeVariables.remove(nodeId)) { + throw new AssertionError("Variable not active: " + nodeId); + } + if (toValue == null) { + variable.setBounds(0, 0); + } else { + // Until queries are flushed and the constraints can be properly updated, + // the variable corresponding to the (previous) multi-object has to stand in for a single object. + variable.setBounds(1, 1); + } + markAsChanged(); + } + MPConstraint makeConstraint() { return solver.makeConstraint(); } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java index 11ca7381..531a7440 100644 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java @@ -9,8 +9,6 @@ import com.google.ortools.Loader; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.ModelQueryBuilder; -import tools.refinery.store.query.dnf.Query; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.scope.ScopePropagatorBuilder; import tools.refinery.store.reasoning.scope.ScopePropagatorStoreAdapter; @@ -60,25 +58,22 @@ public class ScopePropagatorBuilderImpl extends AbstractModelAdapterBuilder builder.clause( - new MultiView(countSymbol).call(instance))); typeScopePropagatorFactories = new ArrayList<>(scopes.size()); for (var entry : scopes.entrySet()) { var type = entry.getKey(); var bounds = entry.getValue(); if (bounds.lowerBound() > 0) { - var lowerFactory = new LowerTypeScopePropagator.Factory(multiQuery, type, bounds.lowerBound()); + var lowerFactory = new LowerTypeScopePropagator.Factory(type, bounds.lowerBound()); typeScopePropagatorFactories.add(lowerFactory); } if (bounds.upperBound() instanceof FiniteUpperCardinality finiteUpperCardinality) { - var upperFactory = new UpperTypeScopePropagator.Factory(multiQuery, type, + var upperFactory = new UpperTypeScopePropagator.Factory(type, finiteUpperCardinality.finiteUpperBound()); typeScopePropagatorFactories.add(upperFactory); } } - var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); for (var factory : typeScopePropagatorFactories) { - queryBuilder.queries(factory.getQueries()); + factory.configure(storeBuilder); } } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java index 98f6ed8b..cfb95829 100644 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java @@ -6,7 +6,9 @@ package tools.refinery.store.reasoning.scope.internal; import com.google.ortools.linearsolver.MPConstraint; +import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; import tools.refinery.store.query.dnf.AnyQuery; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.resultset.ResultSet; @@ -53,9 +55,13 @@ abstract class TypeScopePropagator { adapter.markAsChanged(); } - interface Factory { - TypeScopePropagator createPropagator(ScopePropagatorAdapterImpl adapter); + public abstract static class Factory { + public abstract TypeScopePropagator createPropagator(ScopePropagatorAdapterImpl adapter); - Collection getQueries(); + protected abstract Collection getQueries(); + + public void configure(ModelStoreBuilder storeBuilder) { + storeBuilder.getAdapter(ModelQueryBuilder.class).queries(getQueries()); + } } } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java index 9f09ed56..a0be0fb4 100644 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; import static tools.refinery.store.reasoning.literal.PartialLiterals.must; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; class UpperTypeScopePropagator extends TypeScopePropagator { private final int upperBound; @@ -29,19 +30,19 @@ class UpperTypeScopePropagator extends TypeScopePropagator { constraint.setUb(upperBound - getSingleCount()); } - public static class Factory implements TypeScopePropagator.Factory { + public static class Factory extends TypeScopePropagator.Factory { private final int upperBound; private final RelationalQuery allMust; private final RelationalQuery multiMust; - public Factory(RelationalQuery multi, PartialRelation type, int upperBound) { + public Factory(PartialRelation type, int upperBound) { this.upperBound = upperBound; allMust = Query.of(type.name() + "#must", (builder, instance) -> builder.clause( must(type.call(instance)) )); multiMust = Query.of(type.name() + "#multiMust", (builder, instance) -> builder.clause( must(type.call(instance)), - multi.call(instance) + MULTI_VIEW.call(instance) )); } @@ -51,7 +52,7 @@ class UpperTypeScopePropagator extends TypeScopePropagator { } @Override - public Collection getQueries() { + protected Collection getQueries() { return List.of(allMust, multiMust); } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java index 17aec09c..1dda7ac1 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java @@ -42,6 +42,9 @@ public interface ReasoningAdapter extends ModelAdapter { @Nullable Tuple1 split(int parentMultiObject); + @Nullable + Tuple1 focus(int parentObject); + boolean cleanup(int nodeToDelete); static ReasoningBuilder builder() { diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java index 3d4c672f..79bce33e 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java @@ -6,6 +6,7 @@ package tools.refinery.store.reasoning; import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.dse.transition.objectives.Objective; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.dnf.Dnf; import tools.refinery.store.query.dnf.FunctionalQuery; @@ -35,6 +36,17 @@ public interface ReasoningBuilder extends ModelAdapterBuilder { ReasoningBuilder initializer(PartialModelInitializer initializer); + ReasoningBuilder objective(Objective objective); + + default ReasoningBuilder objectives(Objective... objectives) { + return objectives(List.of(objectives)); + } + + default ReasoningBuilder objectives(Collection objectives) { + objectives.forEach(this::objective); + return this; + } + Query lift(Modality modality, Concreteness concreteness, Query query); RelationalQuery lift(Modality modality, Concreteness concreteness, RelationalQuery query); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/FocusActionLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/FocusActionLiteral.java new file mode 100644 index 00000000..5a6f22d2 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/FocusActionLiteral.java @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.actions; + +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.reasoning.ReasoningAdapter; + +import java.util.List; + +public class FocusActionLiteral extends AbstractActionLiteral { + private final NodeVariable parentNode; + private final NodeVariable childNode; + + public FocusActionLiteral(NodeVariable parentNode, NodeVariable childNode) { + this.parentNode = parentNode; + this.childNode = childNode; + } + + public NodeVariable getParentNode() { + return parentNode; + } + + public NodeVariable getChildNode() { + return childNode; + } + + @Override + public List getInputVariables() { + return List.of(parentNode); + } + + @Override + public List getOutputVariables() { + return List.of(childNode); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); + return tuple -> reasoningAdapter.focus(tuple.get(0)); + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/MergeActionLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/MergeActionLiteral.java new file mode 100644 index 00000000..8d0d7961 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/MergeActionLiteral.java @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.actions; + +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class MergeActionLiteral extends AbstractActionLiteral { + private final PartialSymbol partialSymbol; + private final List parameters; + private final A value; + + public MergeActionLiteral(PartialSymbol partialSymbol, A value, List parameters) { + if (partialSymbol.arity() != parameters.size()) { + throw new IllegalArgumentException("Expected %d parameters for partial symbol %s, got %d instead" + .formatted(partialSymbol.arity(), partialSymbol, parameters.size())); + } + this.partialSymbol = partialSymbol; + this.parameters = parameters; + this.value = value; + } + + public PartialSymbol getPartialSymbol() { + return partialSymbol; + } + + public List getParameters() { + return parameters; + } + + public A getValue() { + return value; + } + + @Override + public List getInputVariables() { + return getParameters(); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var refiner = model.getAdapter(ReasoningAdapter.class).getRefiner(partialSymbol); + return tuple -> refiner.merge(tuple, value) ? Tuple.of() : null; + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java new file mode 100644 index 00000000..990d11e5 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.actions; + +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.representation.TruthValue; + +import java.util.List; + +public final class PartialActionLiterals { + private PartialActionLiterals() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static MergeActionLiteral merge(PartialSymbol partialSymbol, A value, + NodeVariable... parameters) { + return new MergeActionLiteral<>(partialSymbol, value, List.of(parameters)); + } + + public static MergeActionLiteral add(PartialRelation partialRelation, + NodeVariable... parameters) { + return merge(partialRelation, TruthValue.TRUE, parameters); + } + + public static MergeActionLiteral remove(PartialRelation partialRelation, + NodeVariable... parameters) { + return merge(partialRelation, TruthValue.FALSE, parameters); + } + + public static FocusActionLiteral focus(NodeVariable parent, NodeVariable child) { + return new FocusActionLiteral(parent, child); + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java index 2fa744de..f91fdd07 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java @@ -17,7 +17,10 @@ import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.refinement.StorageRefiner; import tools.refinery.store.reasoning.representation.AnyPartialSymbol; import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.representation.cardinality.CardinalityInterval; +import tools.refinery.store.representation.cardinality.CardinalityIntervals; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; @@ -32,6 +35,7 @@ class ReasoningAdapterImpl implements ReasoningAdapter { private final Map refiners; private final StorageRefiner[] storageRefiners; private final Interpretation nodeCountInterpretation; + private final Interpretation countInterpretation; ReasoningAdapterImpl(Model model, ReasoningStoreAdapterImpl storeAdapter) { this.model = model; @@ -51,6 +55,11 @@ class ReasoningAdapterImpl implements ReasoningAdapter { storageRefiners = storeAdapter.createStorageRefiner(model); nodeCountInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); + if (model.getStore().getSymbols().contains(MultiObjectTranslator.COUNT_STORAGE)) { + countInterpretation = model.getInterpretation(MultiObjectTranslator.COUNT_STORAGE); + } else { + countInterpretation = null; + } } private void createPartialInterpretations() { @@ -160,6 +169,26 @@ class ReasoningAdapterImpl implements ReasoningAdapter { return Tuple.of(newNodeId); } + @Override + public @Nullable Tuple1 focus(int parentObject) { + if (countInterpretation == null) { + throw new IllegalStateException("Cannot focus without " + MultiObjectTranslator.class.getSimpleName()); + } + var tuple = Tuple.of(parentObject); + var count = countInterpretation.get(tuple); + if (CardinalityIntervals.ONE.equals(count)) { + return tuple; + } + if (CardinalityIntervals.LONE.equals(count)) { + countInterpretation.put(tuple, CardinalityIntervals.ONE); + return tuple; + } + if (CardinalityIntervals.NONE.equals(count)) { + return null; + } + return split(parentObject); + } + @Override public boolean cleanup(int nodeToDelete) { // Avoid creating an iterator object. diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java index b4971d2c..d2cd2eb0 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java @@ -6,6 +6,9 @@ package tools.refinery.store.reasoning.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.ModelQueryBuilder; @@ -36,11 +39,13 @@ public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder requiredInterpretations = Set.of(Concreteness.values()); private final Map translators = new LinkedHashMap<>(); - private final Map> symbolInterpreters = new LinkedHashMap<>(); + private final Map> symbolInterpreters = + new LinkedHashMap<>(); private final Map> symbolRefiners = new LinkedHashMap<>(); private final Map> registeredStorageRefiners = new LinkedHashMap<>(); private final List initializers = new ArrayList<>(); + private final List objectives = new ArrayList<>(); @Override public ReasoningBuilder requiredInterpretations(Collection requiredInterpretations) { @@ -74,6 +79,13 @@ public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder Query lift(Modality modality, Concreteness concreteness, Query query) { return lifter.lift(modality, concreteness, query); @@ -109,6 +121,10 @@ public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder dseBuilder.objective(Objectives.sum(objectives))); + } } private void doConfigure(ModelStoreBuilder storeBuilder, PartialRelationTranslator relationConfiguration) { diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialRelationTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialRelationTranslator.java index 4600d5a4..c2039afc 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialRelationTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialRelationTranslator.java @@ -5,14 +5,21 @@ */ package tools.refinery.store.reasoning.translator; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.dse.transition.objectives.Criteria; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.Constraint; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.QueryBuilder; import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.term.Variable; +import tools.refinery.store.query.literal.Literal; +import tools.refinery.store.query.term.NodeVariable; import tools.refinery.store.query.view.MayView; import tools.refinery.store.query.view.MustView; +import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.ReasoningBuilder; import tools.refinery.store.reasoning.interpretation.PartialInterpretation; import tools.refinery.store.reasoning.interpretation.PartialRelationRewriter; @@ -21,6 +28,7 @@ import tools.refinery.store.reasoning.interpretation.QueryBasedRelationRewriter; import tools.refinery.store.reasoning.lifting.DnfLifter; import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.literal.Modality; +import tools.refinery.store.reasoning.literal.PartialLiterals; import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.refinement.PartialModelInitializer; @@ -30,8 +38,11 @@ import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.representation.TruthValue; +import java.util.ArrayList; import java.util.function.BiConsumer; +import static tools.refinery.store.query.literal.Literals.not; + @SuppressWarnings("UnusedReturnValue") public final class PartialRelationTranslator extends PartialSymbolTranslator { private final PartialRelation partialRelation; @@ -94,6 +105,30 @@ public final class PartialRelationTranslator extends PartialSymbolTranslator callback) { + private RelationalQuery createQuery(String name, BiConsumer callback) { int arity = partialRelation.arity(); var queryBuilder = Query.builder(name); - var parameters = new Variable[arity]; + var parameters = new NodeVariable[arity]; for (int i = 0; i < arity; i++) { parameters[i] = queryBuilder.parameter("p" + 1); } @@ -293,6 +330,55 @@ public final class PartialRelationTranslator extends PartialSymbolTranslator { + var literals = new ArrayList(parameters.length + 2); + literals.add(PartialLiterals.must(partialRelation.call(parameters))); + literals.add(not(PartialLiterals.may(partialRelation.call(parameters)))); + for (var parameter : parameters) { + literals.add(PartialLiterals.must(ReasoningAdapter.EXISTS_SYMBOL.call(parameter))); + } + builder.clause(literals); + }); + exclude = Criteria.whenHasMatch(excludeQuery); + } + + private void createFallbackObjective() { + if (acceptWasSet && objectiveWasSet) { + return; + } + var invalidCandidate = createQuery("invalidCandidate", (builder, parameters) -> builder + .clause( + PartialLiterals.candidateMust(partialRelation.call(parameters)), + not(PartialLiterals.candidateMay(partialRelation.call(parameters))) + ) + .clause( + PartialLiterals.candidateMust(partialRelation.call(parameters)), + not(PartialLiterals.may(partialRelation.call(parameters))) + ) + .clause( + PartialLiterals.must(partialRelation.call(parameters)), + not(PartialLiterals.candidateMay(partialRelation.call(parameters))) + )); + var reject = createQuery("reject", (builder, parameters) -> { + var literals = new ArrayList(parameters.length + 1); + literals.add(invalidCandidate.call(parameters)); + for (var parameter : parameters) { + literals.add(PartialLiterals.candidateMust(ReasoningAdapter.EXISTS_SYMBOL.call(parameter))); + } + builder.clause(literals); + }); + if (!acceptWasSet) { + accept = Criteria.whenNoMatch(reject); + } + if (!objectiveWasSet) { + objective = Objectives.count(reject); + } + } + public PartialRelationRewriter getRewriter() { checkConfigured(); return rewriter; diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialSymbolTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialSymbolTranslator.java index e7e26cb2..6cdb287d 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialSymbolTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/PartialSymbolTranslator.java @@ -5,10 +5,14 @@ */ package tools.refinery.store.reasoning.translator; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.reasoning.ReasoningBuilder; import tools.refinery.store.reasoning.interpretation.PartialInterpretation; -import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.refinement.PartialModelInitializer; import tools.refinery.store.reasoning.refinement.StorageRefiner; @@ -17,7 +21,8 @@ import tools.refinery.store.reasoning.seed.SeedInitializer; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; @SuppressWarnings("UnusedReturnValue") public abstract sealed class PartialSymbolTranslator implements AnyPartialSymbolTranslator @@ -29,6 +34,13 @@ public abstract sealed class PartialSymbolTranslator implements AnyPartial protected StorageRefiner.Factory storageRefiner; protected PartialInterpretation.Factory interpretationFactory; protected PartialModelInitializer initializer; + protected List decisionRules = new ArrayList<>(); + protected boolean acceptWasSet; + protected @Nullable Criterion accept; + protected boolean excludeWasSet; + protected @Nullable Criterion exclude; + protected boolean objectiveWasSet; + protected @Nullable Objective objective; PartialSymbolTranslator(PartialSymbol partialSymbol) { this.partialSymbol = partialSymbol; @@ -102,6 +114,38 @@ public abstract sealed class PartialSymbolTranslator implements AnyPartial return this; } + public PartialSymbolTranslator decision(Rule decisionRule) { + decisionRules.add(decisionRule); + return this; + } + + public PartialSymbolTranslator accept(@Nullable Criterion acceptanceCriterion) { + if (acceptWasSet) { + throw new IllegalStateException("Accept was already set"); + } + this.accept = acceptanceCriterion; + acceptWasSet = true; + return this; + } + + public PartialSymbolTranslator exclude(@Nullable Criterion exclusionCriterion) { + if (excludeWasSet) { + throw new IllegalStateException("Exclude was already set"); + } + this.exclude = exclusionCriterion; + excludeWasSet = true; + return this; + } + + public PartialSymbolTranslator objective(Objective objective) { + if (objectiveWasSet) { + throw new IllegalStateException("Objective was already set"); + } + this.objective = objective; + objectiveWasSet = true; + return this; + } + @Override public void configure(ModelStoreBuilder storeBuilder) { checkNotConfigured(); @@ -124,6 +168,18 @@ public abstract sealed class PartialSymbolTranslator implements AnyPartial if (initializer != null) { reasoningBuilder.initializer(initializer); } + storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class).ifPresent(dseBuilder -> { + dseBuilder.transformations(decisionRules); + if (accept != null) { + dseBuilder.accept(accept); + } + if (exclude != null) { + dseBuilder.exclude(exclude); + } + }); + if (objective != null) { + reasoningBuilder.objective(objective); + } } private void registerStorageRefiner(ReasoningBuilder reasoningBuilder, StorageRefiner.Factory factory) { diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java index dda9f2c8..5c3298ac 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java @@ -5,6 +5,8 @@ */ package tools.refinery.store.reasoning.translator.containment; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; @@ -22,6 +24,7 @@ import tools.refinery.store.reasoning.refinement.RefinementBasedInitializer; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.translator.PartialRelationTranslator; import tools.refinery.store.reasoning.translator.RoundingMode; +import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; import tools.refinery.store.representation.Symbol; @@ -37,8 +40,9 @@ import static tools.refinery.store.query.literal.Literals.not; import static tools.refinery.store.query.term.int_.IntTerms.constant; import static tools.refinery.store.query.term.int_.IntTerms.less; import static tools.refinery.store.reasoning.ReasoningAdapter.EXISTS_SYMBOL; -import static tools.refinery.store.reasoning.literal.PartialLiterals.may; -import static tools.refinery.store.reasoning.literal.PartialLiterals.must; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.focus; +import static tools.refinery.store.reasoning.literal.PartialLiterals.*; public class ContainmentHierarchyTranslator implements ModelStoreConfiguration { public static final PartialRelation CONTAINED_SYMBOL = new PartialRelation("contained", 1); @@ -104,6 +108,7 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration { translateContainmentLinkType(storeBuilder, linkType, info); translateInvalidMultiplicity(storeBuilder, linkType, info); } + translateFocusNotContained(storeBuilder); } private void translateContainmentLinkType(ModelStoreBuilder storeBuilder, PartialRelation linkType, @@ -188,7 +193,17 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration { ))) .roundingMode(RoundingMode.PREFER_FALSE) .refiner(ContainmentLinkRefiner.of(linkType, containsStorage, info.sourceType(), info.targetType())) - .initializer(new RefinementBasedInitializer<>(linkType))); + .initializer(new RefinementBasedInitializer<>(linkType)) + .decision(Rule.of(linkType.name(), (builder, source, target) -> builder + .clause( + may(linkType.call(source, target)), + not(candidateMust(linkType.call(source, target))), + not(MultiObjectTranslator.MULTI_VIEW.call(source)) + ) + .action(focusedTarget -> List.of( + focus(target, focusedTarget), + add(linkType, source, focusedTarget) + ))))); } private void translateInvalidMultiplicity(ModelStoreBuilder storeBuilder, PartialRelation linkType, @@ -216,4 +231,25 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration { storeBuilder.with(new InvalidMultiplicityErrorTranslator(CONTAINED_SYMBOL, CONTAINS_SYMBOL, true, ConstrainedMultiplicity.of(CardinalityIntervals.ONE, INVALID_CONTAINER))); } + + private void translateFocusNotContained(ModelStoreBuilder storeBuilder) { + var dseBuilderOption = storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class); + if (dseBuilderOption.isEmpty()) { + return; + } + var dseBuilder = dseBuilderOption.get(); + dseBuilder.transformation(Rule.of("NOT_CONTAINED", (builder, multi) -> builder + .clause( + MultiObjectTranslator.MULTI_VIEW.call(multi), + not(may(CONTAINED_SYMBOL.call(multi))) + ) + .clause((container) -> List.of( + MultiObjectTranslator.MULTI_VIEW.call(multi), + must(CONTAINS_SYMBOL.call(container, multi)), + not(MultiObjectTranslator.MULTI_VIEW.call(container)) + )) + .action( + focus(multi, Variable.of()) + ))); + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java index f978aad4..9028337c 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.reasoning.translator.crossreference; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; @@ -22,8 +23,9 @@ import tools.refinery.store.representation.Symbol; import tools.refinery.store.representation.TruthValue; import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.reasoning.literal.PartialLiterals.may; -import static tools.refinery.store.reasoning.literal.PartialLiterals.must; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; +import static tools.refinery.store.reasoning.literal.PartialLiterals.*; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration { private final PartialRelation linkType; @@ -67,7 +69,17 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration } })) .refiner(DirectedCrossReferenceRefiner.of(symbol, sourceType, targetType)) - .initializer(new RefinementBasedInitializer<>(linkType))); + .initializer(new RefinementBasedInitializer<>(linkType)) + .decision(Rule.of(linkType.name(), (builder, source, target) -> builder + .clause( + may(linkType.call(source, target)), + not(candidateMust(linkType.call(source, target))), + not(MULTI_VIEW.call(source)), + not(MULTI_VIEW.call(target)) + ) + .action( + add(linkType, source, target) + )))); storeBuilder.with(new InvalidMultiplicityErrorTranslator(sourceType, linkType, false, info.sourceMultiplicity())); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java index e599992d..c554e2a4 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.reasoning.translator.crossreference; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; @@ -20,8 +21,9 @@ import tools.refinery.store.representation.Symbol; import tools.refinery.store.representation.TruthValue; import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.reasoning.literal.PartialLiterals.may; -import static tools.refinery.store.reasoning.literal.PartialLiterals.must; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; +import static tools.refinery.store.reasoning.literal.PartialLiterals.*; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; public class UndirectedCrossReferenceTranslator implements ModelStoreConfiguration { private final PartialRelation linkType; @@ -64,7 +66,17 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati } })) .refiner(UndirectedCrossReferenceRefiner.of(symbol, type)) - .initializer(new RefinementBasedInitializer<>(linkType))); + .initializer(new RefinementBasedInitializer<>(linkType)) + .decision(Rule.of(linkType.name(), (builder, source, target) -> builder + .clause( + may(linkType.call(source, target)), + not(candidateMust(linkType.call(source, target))), + not(MULTI_VIEW.call(source)), + not(MULTI_VIEW.call(target)) + ) + .action( + add(linkType, source, target) + )))); storeBuilder.with(new InvalidMultiplicityErrorTranslator(type, linkType, false, info.multiplicity())); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java index 735896fa..05704096 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java @@ -5,9 +5,14 @@ */ package tools.refinery.store.reasoning.translator.multiobject; +import tools.refinery.store.dse.transition.objectives.Criteria; +import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.term.Variable; +import tools.refinery.store.query.term.int_.IntTerms; +import tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.ReasoningBuilder; @@ -23,16 +28,14 @@ import tools.refinery.store.representation.cardinality.UpperCardinality; import java.util.List; import static tools.refinery.store.query.literal.Literals.check; -import static tools.refinery.store.query.term.int_.IntTerms.constant; -import static tools.refinery.store.query.term.int_.IntTerms.greaterEq; -import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.constant; -import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.greaterEq; +import static tools.refinery.store.query.term.int_.IntTerms.*; public class MultiObjectTranslator implements ModelStoreConfiguration { public static final Symbol COUNT_STORAGE = Symbol.of("COUNT", 1, CardinalityInterval.class, null); public static final AnySymbolView LOWER_CARDINALITY_VIEW = new LowerCardinalityView(COUNT_STORAGE); public static final AnySymbolView UPPER_CARDINALITY_VIEW = new UpperCardinalityView(COUNT_STORAGE); + public static final AnySymbolView MULTI_VIEW = new MultiView(COUNT_STORAGE); public static final PartialFunction COUNT_SYMBOL = new PartialFunction<>("COUNT", 1, CardinalityDomain.INSTANCE); @@ -40,11 +43,23 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { public void apply(ModelStoreBuilder storeBuilder) { storeBuilder.symbol(COUNT_STORAGE); + var aboveLowerBound = Query.of("count#aboveLowerBound", Integer.class, (builder, node, output) -> builder + .clause(Integer.class, lowerBound -> List.of( + LOWER_CARDINALITY_VIEW.call(node, lowerBound), + output.assign(sub(lowerBound, IntTerms.constant(1))), + check(greater(output, IntTerms.constant(0))) + ))); + var missingCardinality = Query.of("count#missing", Integer.class, (builder, output) -> builder + .clause( + output.assign(aboveLowerBound.aggregate(INT_SUM, Variable.of())) + )); + storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EXISTS_SYMBOL) .may(Query.of("exists#may", (builder, p1) -> builder .clause(UpperCardinality.class, upper -> List.of( UPPER_CARDINALITY_VIEW.call(p1, upper), - check(greaterEq(upper, constant(UpperCardinalities.ONE))) + check(UpperCardinalityTerms.greaterEq(upper, + UpperCardinalityTerms.constant(UpperCardinalities.ONE))) )))) .must(Query.of("exists#must", (builder, p1) -> builder .clause(Integer.class, lower -> List.of( @@ -52,11 +67,17 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { check(greaterEq(lower, constant(1))) )))) .roundingMode(RoundingMode.PREFER_FALSE) - .refiner(ExistsRefiner.of(COUNT_STORAGE))); + .refiner(ExistsRefiner.of(COUNT_STORAGE)) + .exclude(null) + .accept(Criteria.whenNoMatch(aboveLowerBound)) + .objective(Objectives.value(missingCardinality))); storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EQUALS_SYMBOL) .rewriter(EqualsRelationRewriter.of(UPPER_CARDINALITY_VIEW)) - .refiner(EqualsRefiner.of(COUNT_STORAGE))); + .refiner(EqualsRefiner.of(COUNT_STORAGE)) + .exclude(null) + .accept(null) + .objective(null)); var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE)); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java new file mode 100644 index 00000000..498bcd83 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.translator.multiobject; + +import tools.refinery.store.query.view.TuplePreservingView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.representation.cardinality.CardinalityInterval; +import tools.refinery.store.representation.cardinality.CardinalityIntervals; +import tools.refinery.store.tuple.Tuple; + +class MultiView extends TuplePreservingView { + protected MultiView(Symbol symbol) { + super(symbol, "multi"); + } + + @Override + protected boolean doFilter(Tuple key, CardinalityInterval value) { + return !CardinalityIntervals.ONE.equals(value); + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java index c5e5e83e..ee982f4f 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.reasoning.translator.multiplicity; +import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; @@ -22,7 +23,10 @@ import tools.refinery.store.representation.cardinality.UpperCardinality; import java.util.List; import static tools.refinery.store.query.literal.Literals.check; +import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM; +import static tools.refinery.store.query.term.int_.IntTerms.constant; import static tools.refinery.store.query.term.int_.IntTerms.greater; +import static tools.refinery.store.query.term.int_.IntTerms.sub; import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.constant; import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.less; import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMust; @@ -67,6 +71,8 @@ public class InvalidMultiplicityErrorTranslator implements ModelStoreConfigurati .parameter(node); var candidateMustBuilder = Query.builder(DnfLifter.decorateName(name, Modality.MUST, Concreteness.PARTIAL)) .parameter(node); + var missingOutput = Variable.of("missing", Integer.class); + var missingBuilder = Query.builder(name + "#missingMultiplicity").parameter(node).output(missingOutput); int lowerBound = cardinalityInterval.lowerBound(); if (lowerBound > 0) { @@ -79,12 +85,18 @@ public class InvalidMultiplicityErrorTranslator implements ModelStoreConfigurati candidateMayBuilder.clause(Integer.class, existingContents -> List.of( candidateMust(nodeType.call(node)), new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments), - check(IntTerms.less(existingContents, IntTerms.constant(lowerBound))) + check(IntTerms.less(existingContents, constant(lowerBound))) )); candidateMustBuilder.clause(Integer.class, existingContents -> List.of( candidateMust(nodeType.call(node)), new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments), - check(IntTerms.less(existingContents, IntTerms.constant(lowerBound))) + check(IntTerms.less(existingContents, constant(lowerBound))) + )); + missingBuilder.clause(Integer.class, existingContents -> List.of( + candidateMust(nodeType.call(node)), + new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments), + missingOutput.assign(sub(constant(lowerBound), existingContents)), + check(greater(missingOutput, constant(0))) )); } @@ -93,24 +105,35 @@ public class InvalidMultiplicityErrorTranslator implements ModelStoreConfigurati mustBuilder.clause(Integer.class, existingContents -> List.of( must(nodeType.call(node)), new CountLowerBoundLiteral(existingContents, linkType, arguments), - check(greater(existingContents, IntTerms.constant(upperBound))) + check(greater(existingContents, constant(upperBound))) )); candidateMayBuilder.clause(Integer.class, existingContents -> List.of( candidateMust(nodeType.call(node)), new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments), - check(greater(existingContents, IntTerms.constant(upperBound))) + check(greater(existingContents, constant(upperBound))) )); candidateMustBuilder.clause(Integer.class, existingContents -> List.of( candidateMust(nodeType.call(node)), new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments), - check(greater(existingContents, IntTerms.constant(upperBound))) + check(greater(existingContents, constant(upperBound))) + )); + missingBuilder.clause(Integer.class, existingContents -> List.of( + candidateMust(nodeType.call(node)), + new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments), + missingOutput.assign(sub(existingContents, constant(upperBound))), + check(greater(missingOutput, constant(0))) )); } + var objective = Query.of(name + "#objective", Integer.class, (builder, output) -> builder.clause( + output.assign(missingBuilder.build().aggregate(INT_SUM, Variable.of())) + )); + storeBuilder.with(PartialRelationTranslator.of(constrainedMultiplicity.errorSymbol()) .mayNever() .must(mustBuilder.build()) .candidateMay(candidateMayBuilder.build()) - .candidateMust(candidateMustBuilder.build())); + .candidateMust(candidateMustBuilder.build()) + .objective(Objectives.value(objective))); } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java index 16745da1..b401118e 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java @@ -78,7 +78,14 @@ public class PredicateTranslator implements ModelStoreConfiguration { .clause(mayLiterals) .build(); translator.may(may); - } else if (!defaultValue.may()) { + } else if (defaultValue.may()) { + // If all values are permitted, we don't need to check for any forbidden values in the model. + // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) + // will be {@code ERROR} as well. + translator.exclude(null); + translator.accept(null); + translator.objective(null); + } else { translator.mayNever(); } storeBuilder.with(translator); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java index 67e8035f..dc8a1d36 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java @@ -5,16 +5,26 @@ */ package tools.refinery.store.reasoning.translator.typehierarchy; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.dse.transition.actions.ActionLiteral; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.reasoning.ReasoningBuilder; +import tools.refinery.store.reasoning.actions.PartialActionLiterals; import tools.refinery.store.reasoning.literal.PartialLiterals; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.translator.PartialRelationTranslator; +import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; import tools.refinery.store.reasoning.translator.proxy.PartialRelationTranslatorProxy; import tools.refinery.store.representation.Symbol; +import java.util.ArrayList; + +import static tools.refinery.store.query.literal.Literals.not; +import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMust; +import static tools.refinery.store.reasoning.literal.PartialLiterals.may; + public class TypeHierarchyTranslator implements ModelStoreConfiguration { private final Symbol typeSymbol = Symbol.of("TYPE", 1, InferredType.class, InferredType.UNTYPED); private final TypeHierarchy typeHierarchy; @@ -49,7 +59,7 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { builder.clause(new MayTypeView(typeSymbol, type).call(p1)); } for (var subtype : result.getDirectSubtypes()) { - builder.clause(PartialLiterals.may(subtype.call(p1))); + builder.clause(may(subtype.call(p1))); } }); @@ -66,11 +76,31 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { } }); - return PartialRelationTranslator.of(type) + var translator = PartialRelationTranslator.of(type) .may(may) .must(must) .candidate(candidate) .refiner(InferredTypeRefiner.of(typeSymbol, result)); + + if (!result.isAbstractType()) { + var decision = Rule.of(type.name(), (builder, instance) -> builder + .clause( + may(type.call(instance)), + not(candidateMust(type.call(instance))), + not(MultiObjectTranslator.MULTI_VIEW.call(instance)) + ) + .action(() -> { + var actionLiterals = new ArrayList(); + actionLiterals.add(PartialActionLiterals.add(type, instance)); + for (var subtype : result.getDirectSubtypes()) { + actionLiterals.add(PartialActionLiterals.remove(subtype, instance)); + } + return actionLiterals; + })); + translator.decision(decision); + } + + return translator; } private ModelStoreConfiguration createEliminatedTypeTranslator( -- cgit v1.2.3-54-g00ecf From 59dff3ea8673e402c576d94a28ec26d3ab181a92 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 7 Sep 2023 23:08:09 +0200 Subject: fix(dse): best-first strategy The query engine must be flushed after firing a tranformation activation. --- .../visualization/ModelVisualizerBuilder.java | 2 +- .../internal/ModelVisualizerBuilderImpl.java | 6 +- .../store/dse/strategy/BestFirstExplorer.java | 136 +++------------------ .../store/dse/strategy/BestFirstStoreManager.java | 6 +- .../store/dse/strategy/BestFirstWorker.java | 29 +++-- .../internal/ActivationStoreBitVectorEntry.java | 19 +-- .../statespace/internal/ActivationStoreEntry.java | 5 + .../statespace/internal/ActivationStoreImpl.java | 35 +++--- .../tools/refinery/store/dse/CRAExamplesTest.java | 35 ++++-- .../java/tools/refinery/store/dse/DebugTest.java | 4 +- 10 files changed, 98 insertions(+), 179 deletions(-) (limited to 'subprojects/store-dse') diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java index 592f5fcf..1ee41cc3 100644 --- a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -9,7 +9,7 @@ import tools.refinery.store.adapter.ModelAdapterBuilder; import tools.refinery.visualization.internal.FileFormat; public interface ModelVisualizerBuilder extends ModelAdapterBuilder { - ModelVisualizerBuilder withOutputpath(String outputpath); + ModelVisualizerBuilder withOutputPath(String outputPath); ModelVisualizerBuilder withFormat(FileFormat format); ModelVisualizerBuilder saveDesignSpace(); ModelVisualizerBuilder saveStates(); diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java index e4d801d8..9ba2abe8 100644 --- a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -18,7 +18,7 @@ public class ModelVisualizerBuilderImpl private String outputPath; private boolean saveDesignSpace = false; private boolean saveStates = false; - private Set formats = new LinkedHashSet<>(); + private final Set formats = new LinkedHashSet<>(); @Override protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { @@ -26,9 +26,9 @@ public class ModelVisualizerBuilderImpl } @Override - public ModelVisualizerBuilder withOutputpath(String outputpath) { + public ModelVisualizerBuilder withOutputPath(String outputPath) { checkNotConfigured(); - this.outputPath = outputpath; + this.outputPath = outputPath; return this; } 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 a2b6268f..8f7e3bdc 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 @@ -5,7 +5,6 @@ */ package tools.refinery.store.dse.strategy; -import tools.refinery.store.dse.transition.VersionWithObjectiveValue; import tools.refinery.store.model.Model; import java.util.Random; @@ -13,6 +12,7 @@ import java.util.Random; public class BestFirstExplorer extends BestFirstWorker { final int id; Random random; + public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) { super(storeManager, model); this.id = id; @@ -20,6 +20,7 @@ public class BestFirstExplorer extends BestFirstWorker { } private boolean interrupted = false; + public void interrupt() { this.interrupted = true; } @@ -29,138 +30,37 @@ public class BestFirstExplorer extends BestFirstWorker { } public void explore() { - VersionWithObjectiveValue lastVisited = submit().newVersion(); - + var lastBest = submit().newVersion(); while (shouldRun()) { - - if (lastVisited == null) { - lastVisited = this.restoreToBest(); - if(lastVisited == null) { + if (lastBest == null) { + lastBest = restoreToBest(); + if (lastBest == null) { return; } } - boolean tryActivation = true; - while(tryActivation && shouldRun()) { - RandomVisitResult randomVisitResult = this.visitRandomUnvisited(random); - + while (tryActivation && shouldRun()) { + var randomVisitResult = this.visitRandomUnvisited(random); tryActivation = randomVisitResult.shouldRetry(); var newSubmit = randomVisitResult.submitResult(); - if(newSubmit != null) { - if(!newSubmit.include()) { + if (newSubmit != null) { + if (!newSubmit.include()) { restoreToLast(); } else { var newVisit = newSubmit.newVersion(); - int compareResult = compare(lastVisited,newVisit); - if(compareResult >= 0) { - lastVisited = newVisit; - break; - } - } - } - else { - lastVisited = null; - break; - } - } - - //final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - /*boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - // Global constraint is not satisfied in the first state. Terminate. - return; - } - - final Fitness firstFitness = dseAdapter.getFitness(); - if (firstFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - // First state is a solution. Terminate. - if (backTrackIfSolution) { - return; - } - } - - if (maxDepth == 0) { - return; - }*/ - - /* - var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); - trajectoriesToExplore.add(firstTrajectoryWithFitness); - TrajectoryWithFitness currentTrajectoryWithFitness = null; - */ -/* - Collection activations = dseAdapter.getUntraversedActivations(); - Iterator iterator = activations.iterator(); - - while (iterator.hasNext()) { - final Activation nextActivation = iterator.next(); - if (!iterator.hasNext()) { - // Last untraversed activation of the state. - trajectoriesToExplore.remove(currentTrajectoryWithFitness); - } - - // Executing new activation - dseAdapter.fireActivation(nextActivation); - if (dseAdapter.isCurrentStateAlreadyTraversed()) { - // The new state is already visited. - dseAdapter.backtrack(); - } else if (!dseAdapter.checkGlobalConstraints()) { - // Global constraint is not satisfied. - dseAdapter.backtrack(); - } else { - final Fitness nextFitness = dseAdapter.getFitness(); - if (nextFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - var solutions = dseAdapter.getSolutions().size(); - if (solutions >= maxSolutions) { - return; - } - // Found a solution. - if (backTrackIfSolution) { - dseAdapter.backtrack(); - continue; - } - } - if (dseAdapter.getDepth() >= maxDepth) { - // Reached max depth. - dseAdapter.backtrack(); - continue; - } - - TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( - dseAdapter.getTrajectory(), nextFitness); - trajectoriesToExplore.add(nextTrajectoryWithFitness); - - int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, - nextTrajectoryWithFitness.fitness); - if (compare < 0) { - // Better fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } else if (compare == 0) { - if (onlyBetterFirst) { - // Equally good fitness, backtrack - dseAdapter.backtrack(); + int compareResult = compare(lastBest, newVisit); + if (compareResult >= 0) { + lastBest = newVisit; } else { - // Equally good fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; + lastBest = null; } - } else { - //"Worse fitness - currentTrajectoryWithFitness = null; - continue mainLoop; + break; } + } else { + lastBest = null; + break; } } - - // State is fully traversed. - currentTrajectoryWithFitness = null; -*/ } - // Interrupted. - } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java index 4ccba6f7..02634a02 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -17,9 +17,7 @@ import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriority import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl; import tools.refinery.store.map.Version; import tools.refinery.store.model.ModelStore; -import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.statecoding.StateCoderStoreAdapter; -import tools.refinery.visualization.ModelVisualizerStoreAdapter; import tools.refinery.visualization.statespace.VisualizationStore; import tools.refinery.visualization.statespace.internal.VisualizationStoreImpl; @@ -34,7 +32,7 @@ public class BestFirstStoreManager { EquivalenceClassStore equivalenceClassStore; VisualizationStore visualizationStore; - public BestFirstStoreManager(ModelStore modelStore) { + public BestFirstStoreManager(ModelStore modelStore, int maxNumberOfSolutions) { this.modelStore = modelStore; DesignSpaceExplorationStoreAdapter storeAdapter = modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class); @@ -42,7 +40,7 @@ public class BestFirstStoreManager { objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); Consumer whenAllActivationsVisited = x -> objectiveStore.remove(x); activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); - solutionStore = new SolutionStoreImpl(50); + solutionStore = new SolutionStoreImpl(maxNumberOfSolutions); equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { @Override protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index f1bec14f..5d738297 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -40,11 +40,13 @@ public class BestFirstWorker { isVisualizationEnabled = visualizationStore != null; } - private VersionWithObjectiveValue last = null; - - //public boolean isIncluded + protected VersionWithObjectiveValue last = null; public SubmitResult submit() { + checkSynchronized(); + if (queryAdapter.hasPendingChanges()) { + throw new AssertionError("Pending changes detected before model submission"); + } if (explorationAdapter.checkExclude()) { return new SubmitResult(false, false, null, null); } @@ -86,14 +88,11 @@ public class BestFirstWorker { public VersionWithObjectiveValue restoreToBest() { var bestVersion = storeManager.getObjectiveStore().getBest(); + last = bestVersion; if (bestVersion != null) { - var oldVersion = model.getState(); this.model.restore(bestVersion.version()); - if (isVisualizationEnabled) { - visualizationStore.addTransition(oldVersion, last.version(), ""); - } } - return bestVersion; + return last; } public VersionWithObjectiveValue restoreToRandom(Random random) { @@ -102,7 +101,7 @@ public class BestFirstWorker { if (randomVersion != null) { this.model.restore(randomVersion.version()); } - return randomVersion; + return last; } public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) { @@ -121,9 +120,10 @@ public class BestFirstWorker { } public RandomVisitResult visitRandomUnvisited(Random random) { + checkSynchronized(); if (!model.hasUncommittedChanges()) { - queryAdapter.flushChanges(); var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); + queryAdapter.flushChanges(); if (visitResult.successfulVisit()) { Version oldVersion = null; @@ -133,7 +133,8 @@ public class BestFirstWorker { var submitResult = submit(); if (isVisualizationEnabled && submitResult.newVersion() != null) { var newVersion = submitResult.newVersion().version(); - visualizationStore.addTransition(oldVersion, newVersion, ""); + visualizationStore.addTransition(oldVersion, newVersion, + "fire: " + visitResult.transformation() + ", " + visitResult.activation()); } return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); } else { @@ -147,4 +148,10 @@ public class BestFirstWorker { public boolean hasEnoughSolution() { return storeManager.solutionStore.hasEnoughSolution(); } + + private void checkSynchronized() { + if (last != null && !last.version().equals(model.getState())) { + throw new AssertionError("Worker is not synchronized with model state"); + } + } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java index 24145d03..c204ae35 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java @@ -16,32 +16,35 @@ public class ActivationStoreBitVectorEntry extends ActivationStoreEntry { @Override public int getNumberOfVisitedActivations() { int visited = 0; - for (int i : selected) { - visited += Integer.bitCount(i); + // Use indexed for loop to avoid allocating an iterator. + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < selected.length; i++) { + visited += Integer.bitCount(selected[i]); } return visited; } private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE - private static final int ELEMENT_BITMASK = (1<> ELEMENT_POSITION; - final int selectedBit = 1<<(position & ELEMENT_BITMASK); + final int selectedBit = 1 << (position & ELEMENT_BITMASK); - if((selected[selectedElement] & selectedBit) == 0) { + if ((selected[selectedElement] & selectedBit) == 0) { selected[selectedElement] |= selectedBit; return position; } else { - if(position < this.numberOfActivations-1) { + if (position < this.numberOfActivations - 1) { position++; } else { position = 0; } } - } while(position != index); - throw new IllegalArgumentException("There is are no unvisited activations!"); + } while (position != index); + throw new IllegalArgumentException("There is are no unvisited activations!"); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java index d7339805..7249751c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java @@ -17,6 +17,11 @@ public abstract class ActivationStoreEntry { public int getNumberOfUnvisitedActivations() { return numberOfActivations - getNumberOfVisitedActivations(); } + + public int getNumberOfActivations() { + return numberOfActivations; + } + public abstract int getAndAddActivationAfter(int index); // public abstract boolean contains(int activation) diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java index 4d775b5a..d9e29eca 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -28,7 +28,7 @@ public class ActivationStoreImpl implements ActivationStore { var entries = versionToActivations.computeIfAbsent(to, x -> { successful[0] = true; List result = new ArrayList<>(emptyEntrySizes.length); - for(int emptyEntrySize : emptyEntrySizes) { + for (int emptyEntrySize : emptyEntrySizes) { result.add(ActivationStoreEntry.create(emptyEntrySize)); } return result; @@ -40,13 +40,14 @@ public class ActivationStoreImpl implements ActivationStore { break; } } - if(!hasMore) { + if (!hasMore) { actionWhenAllActivationVisited.accept(to); } return new VisitResult(successful[0], hasMore, -1, -1); } - public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, int activationIndex) { + public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, + int activationIndex) { var entries = versionToActivations.get(from); var entry = entries.get(transformationIndex); final int unvisited = entry.getNumberOfUnvisitedActivations(); @@ -66,7 +67,7 @@ public class ActivationStoreImpl implements ActivationStore { activation = -1; } - if(!hasMoreInActivation) { + if (!hasMoreInActivation) { boolean hasMoreInOtherTransformation = false; for (var e : entries) { if (e != entry && e.getNumberOfUnvisitedActivations() > 0) { @@ -79,7 +80,7 @@ public class ActivationStoreImpl implements ActivationStore { hasMore = true; } - if(!hasMore) { + if (!hasMore) { actionWhenAllActivationVisited.accept(from); } @@ -103,34 +104,28 @@ public class ActivationStoreImpl implements ActivationStore { public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) { var entries = versionToActivations.get(version); - int sum1 = 0; + int numberOfAllUnvisitedActivations = 0; for (var entry : entries) { - sum1 += entry.getNumberOfUnvisitedActivations(); + numberOfAllUnvisitedActivations += entry.getNumberOfUnvisitedActivations(); } - if(sum1 == 0) { + if (numberOfAllUnvisitedActivations == 0) { this.actionWhenAllActivationVisited.accept(version); return new VisitResult(false, false, -1, -1); } - int selected = random.nextInt(sum1); - int sum2 = 0; + int offset = random.nextInt(numberOfAllUnvisitedActivations); int transformation = 0; - int activation = -1; for (; transformation < entries.size(); transformation++) { var entry = entries.get(transformation); int unvisited = entry.getNumberOfUnvisitedActivations(); - if (selected < sum2 + unvisited) { - activation = sum2 + unvisited - selected - 1; - break; - } else { - sum2 += unvisited; + if (unvisited > 0 && offset < unvisited) { + int activation = random.nextInt(entry.getNumberOfActivations()); + return this.visitActivation(version, transformation, activation); } - } - if (activation == -1) { - throw new IllegalArgumentException("no unvisited"); + offset -= unvisited; } - return this.visitActivation(version, transformation, activation); + throw new AssertionError("Unvisited activation %d not found".formatted(offset)); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 685b88bd..63da6cc3 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -11,10 +11,11 @@ import tools.refinery.store.dse.modification.DanglingEdges; import tools.refinery.store.dse.modification.ModificationAdapter; import tools.refinery.store.dse.strategy.BestFirstStoreManager; import tools.refinery.store.dse.tests.DummyCriterion; -import tools.refinery.store.dse.tests.DummyRandomCriterion; import tools.refinery.store.dse.tests.DummyRandomObjective; import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.dse.transition.objectives.Criteria; +import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; @@ -59,6 +60,12 @@ class CRAExamplesTest { methodView.call(f) )); + private static final RelationalQuery unEncapsulatedFeature = Query.of("unEncapsulatedFeature", + (builder, f) -> builder.clause( + feature.call(f), + not(encapsulatesView.call(Variable.of(), f)) + )); + private static final Rule assignFeatureRule = Rule.of("AssignFeature", (builder, f, c1) -> builder .clause( feature.call(f), @@ -66,24 +73,24 @@ class CRAExamplesTest { not(encapsulatesView.call(Variable.of(), f)) ) .action( - add(encapsulates, f, c1) + add(encapsulates, c1, f) )); private static final Rule deleteEmptyClassRule = Rule.of("DeleteEmptyClass", (builder, c) -> builder - .clause((f) -> List.of( + .clause( classElementView.call(c), - not(encapsulatesView.call(c, f)) - )) + not(encapsulatesView.call(c, Variable.of())) + ) .action( remove(classElement, c), delete(c, DanglingEdges.IGNORE) )); private static final Rule createClassRule = Rule.of("CreateClass", (builder, f) -> builder - .clause((c) -> List.of( + .clause( feature.call(f), - not(encapsulatesView.call(f, c)) - )) + not(encapsulatesView.call(Variable.of(), f)) + ) .action((newClass) -> List.of( create(newClass), add(classElement, newClass), @@ -110,7 +117,7 @@ class CRAExamplesTest { .symbols(classElement, encapsulates, attribute, method, dataDependency, functionalDependency, name) .with(ViatraModelQueryAdapter.builder()) .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") + .withOutputPath("test_output") .withFormat(FileFormat.DOT) .withFormat(FileFormat.SVG) .saveStates() @@ -119,8 +126,11 @@ class CRAExamplesTest { .with(ModificationAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) - .objectives(new DummyRandomObjective()) - .accept(new DummyRandomCriterion()) + .objectives(Objectives.sum( + new DummyRandomObjective(), + Objectives.count(unEncapsulatedFeature) + )) + .accept(Criteria.whenNoMatch(unEncapsulatedFeature)) .exclude(new DummyCriterion(false))) .build(); @@ -192,9 +202,10 @@ class CRAExamplesTest { var initialVersion = model.commit(); queryEngine.flushChanges(); - var bestFirst = new BestFirstStoreManager(store); + var bestFirst = new BestFirstStoreManager(store, 50); bestFirst.startExploration(initialVersion); var resultStore = bestFirst.getSolutionStore(); System.out.println("states size: " + resultStore.getSolutions().size()); + model.getAdapter(ModelVisualizerAdapter.class).visualize(bestFirst.getVisualizationStore()); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index baa7c8a4..b912eba3 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -64,7 +64,7 @@ class DebugTest { .symbols(classModel, classElement, feature, classes, features) .with(ViatraModelQueryAdapter.builder()) .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") + .withOutputPath("test_output") .withFormat(FileFormat.DOT) .withFormat(FileFormat.SVG) .saveStates() @@ -91,7 +91,7 @@ class DebugTest { var initialVersion = model.commit(); queryEngine.flushChanges(); - var bestFirst = new BestFirstStoreManager(store); + var bestFirst = new BestFirstStoreManager(store, 50); bestFirst.startExploration(initialVersion); var resultStore = bestFirst.getSolutionStore(); System.out.println("states size: " + resultStore.getSolutions().size()); -- cgit v1.2.3-54-g00ecf From 0bdb400deef88cb2a7c0b8c90afebf84b29c04d5 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 9 Sep 2023 12:57:49 +0200 Subject: feat: integrate DSE with partial interpretation --- subprojects/language-semantics/build.gradle.kts | 3 + .../language/semantics/model/ModelInitializer.java | 23 +- .../semantics/model/CountPropagationTest.java | 82 +++++++ .../semantics/model/ModelGenerationTest.java | 272 +++++++++++++++++++++ .../store/dse/propagation/BoundPropagator.java | 11 + .../store/dse/propagation/PropagationAdapter.java | 20 ++ .../store/dse/propagation/PropagationBuilder.java | 32 +++ .../store/dse/propagation/PropagationResult.java | 28 +++ .../dse/propagation/PropagationStoreAdapter.java | 14 ++ .../refinery/store/dse/propagation/Propagator.java | 17 ++ .../propagation/impl/PropagationAdapterImpl.java | 72 ++++++ .../propagation/impl/PropagationBuilderImpl.java | 53 ++++ .../impl/PropagationStoreAdapterImpl.java | 38 +++ .../impl/rule/BoundPropagationRule.java | 37 +++ .../impl/rule/BoundRuleBasedPropagator.java | 43 ++++ .../propagation/impl/rule/RuleBasedPropagator.java | 36 +++ .../store/dse/strategy/BestFirstExplorer.java | 8 +- .../store/dse/strategy/BestFirstWorker.java | 63 ++--- .../store/dse/transition/actions/BoundAction.java | 23 +- .../dse/transition/objectives/QueryObjective.java | 4 +- .../statespace/internal/ActivationStoreImpl.java | 23 +- .../statespace/internal/ActivationStoreWorker.java | 6 +- .../reasoning/scope/BoundScopePropagator.java | 229 +++++++++++++++++ .../reasoning/scope/LowerTypeScopePropagator.java | 86 +++++++ .../store/reasoning/scope/RoundingUtil.java | 26 ++ .../store/reasoning/scope/ScopePropagator.java | 102 ++++++++ .../reasoning/scope/ScopePropagatorAdapter.java | 21 -- .../reasoning/scope/ScopePropagatorBuilder.java | 21 -- .../scope/ScopePropagatorStoreAdapter.java | 16 -- .../store/reasoning/scope/TypeScopePropagator.java | 66 +++++ .../reasoning/scope/UpperTypeScopePropagator.java | 59 +++++ .../scope/internal/LowerTypeScopePropagator.java | 86 ------- .../reasoning/scope/internal/RoundingUtil.java | 26 -- .../scope/internal/ScopePropagatorAdapterImpl.java | 253 ------------------- .../scope/internal/ScopePropagatorBuilderImpl.java | 86 ------- .../internal/ScopePropagatorStoreAdapterImpl.java | 58 ----- .../scope/internal/TypeScopePropagator.java | 67 ----- .../scope/internal/UpperTypeScopePropagator.java | 59 ----- .../store/reasoning/scope/MultiObjectTest.java | 38 +-- .../store/reasoning/scope/RoundingUtilTest.java | 69 ++++++ .../reasoning/scope/internal/RoundingUtilTest.java | 69 ------ .../reasoning/actions/CleanupActionLiteral.java | 43 ++++ .../reasoning/actions/PartialActionLiterals.java | 4 + .../reasoning/internal/ReasoningBuilderImpl.java | 9 +- .../internal/ReasoningStoreAdapterImpl.java | 6 + .../reasoning/refinement/RefinementResult.java | 24 -- .../ContainmentHierarchyTranslator.java | 2 +- .../containment/InferredContainment.java | 44 +++- .../multiobject/MultiObjectTranslator.java | 21 ++ .../translator/typehierarchy/InferredType.java | 45 +++- .../typehierarchy/TypeHierarchyTranslator.java | 15 +- .../cardinality/FiniteUpperCardinality.java | 13 + .../cardinality/NonEmptyCardinalityInterval.java | 14 ++ .../cardinality/UnboundedUpperCardinality.java | 10 + 54 files changed, 1719 insertions(+), 876 deletions(-) create mode 100644 subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java create mode 100644 subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/BoundPropagator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationResult.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/Propagator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundRuleBasedPropagator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/RuleBasedPropagator.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/BoundScopePropagator.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/LowerTypeScopePropagator.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/RoundingUtil.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagator.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorAdapter.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorBuilder.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorStoreAdapter.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/TypeScopePropagator.java create mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/UpperTypeScopePropagator.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/RoundingUtil.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorStoreAdapterImpl.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java delete mode 100644 subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java create mode 100644 subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/RoundingUtilTest.java delete mode 100644 subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/internal/RoundingUtilTest.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/CleanupActionLiteral.java delete mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/RefinementResult.java (limited to 'subprojects/store-dse') diff --git a/subprojects/language-semantics/build.gradle.kts b/subprojects/language-semantics/build.gradle.kts index 4374f78c..338ae345 100644 --- a/subprojects/language-semantics/build.gradle.kts +++ b/subprojects/language-semantics/build.gradle.kts @@ -16,4 +16,7 @@ dependencies { api(project(":refinery-store-reasoning")) implementation(project(":refinery-store-reasoning-scope")) runtimeOnly(libs.eclipseCollections) + testImplementation(project(":refinery-store-dse-visualization")) + testImplementation(project(":refinery-store-query-viatra")) + testImplementation(testFixtures(project(":refinery-language"))) } diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java index 89c41a8e..a14b40d0 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java @@ -13,6 +13,7 @@ import tools.refinery.language.semantics.model.internal.MutableSeed; import tools.refinery.language.utils.BuiltinSymbols; import tools.refinery.language.utils.ProblemDesugarer; import tools.refinery.language.utils.ProblemUtil; +import tools.refinery.store.dse.propagation.PropagationBuilder; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.Constraint; import tools.refinery.store.query.dnf.InvalidClauseException; @@ -24,7 +25,7 @@ import tools.refinery.store.query.term.Variable; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.representation.AnyPartialSymbol; import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.reasoning.scope.ScopePropagatorBuilder; +import tools.refinery.store.reasoning.scope.ScopePropagator; import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.reasoning.seed.Seed; import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator; @@ -73,7 +74,9 @@ public class ModelInitializer { private Metamodel metamodel; - private Map countSeed = new LinkedHashMap<>(); + private final Map countSeed = new LinkedHashMap<>(); + + private ScopePropagator scopePropagator; private ModelSeed modelSeed; @@ -139,6 +142,12 @@ public class ModelInitializer { modelSeedBuilder.seed(partialRelation, info.toSeed(nodeCount)); } collectScopes(); + if (scopePropagator != null) { + if (storeBuilder.tryGetAdapter(PropagationBuilder.class).isEmpty()) { + throw new TracedException(problem, "Type scopes require a PropagationBuilder"); + } + storeBuilder.with(scopePropagator); + } modelSeedBuilder.seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder .reducedValue(CardinalityIntervals.SET) .putAll(countSeed)); @@ -534,8 +543,7 @@ public class ModelInitializer { } } - private void toLiterals(Expr expr, Map localScope, + private void toLiterals(Expr expr, Map localScope, List literals) { if (expr instanceof LogicConstant logicConstant) { switch (logicConstant.getLogicValue()) { @@ -645,14 +653,15 @@ public class ModelInitializer { } private void collectTypeScope(TypeScope typeScope) { - var scopePropagatorBuilder = storeBuilder.tryGetAdapter(ScopePropagatorBuilder.class).orElseThrow( - () -> new TracedException(typeScope, "Type scopes require a ScopePropagatorBuilder")); var type = relationTrace.get(typeScope.getTargetType()); if (type == null) { throw new TracedException(typeScope, "Unknown target type"); } var interval = getCardinalityInterval(typeScope.getMultiplicity()); - scopePropagatorBuilder.scope(type, interval); + if (scopePropagator == null) { + scopePropagator = new ScopePropagator(); + } + scopePropagator.scope(type, interval); } private record RelationInfo(PartialRelation partialRelation, MutableSeed assertions, 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 new file mode 100644 index 00000000..a383d043 --- /dev/null +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/CountPropagationTest.java @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.semantics.model; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.propagation.PropagationAdapter; +import tools.refinery.store.dse.propagation.PropagationResult; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.ReasoningStoreAdapter; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.scope.ScopePropagator; +import tools.refinery.store.reasoning.seed.ModelSeed; +import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; +import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchy; +import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; +import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.representation.cardinality.CardinalityIntervals; +import tools.refinery.store.tuple.Tuple; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class CountPropagationTest { + @Test + void countPropagationTest() { + var a1 = new PartialRelation("A1", 1); + var c1 = new PartialRelation("C1", 1); + var c2 = new PartialRelation("C2", 1); + + var typeHierarchy = TypeHierarchy.builder() + .type(a1, true) + .type(c1, a1) + .type(c2, a1) + .build(); + + var store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(PropagationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .with(ReasoningAdapter.builder()) + .with(new MultiObjectTranslator()) + .with(new TypeHierarchyTranslator(typeHierarchy)) + .with(new ScopePropagator() + .scope(a1, CardinalityIntervals.between(1000, 1100)) + .scope(c1, CardinalityIntervals.between(100, 150))) + .build(); + + var modelSeed = ModelSeed.builder(4) + .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder + .reducedValue(CardinalityIntervals.ONE) + .put(Tuple.of(0), CardinalityIntervals.SET) + .put(Tuple.of(1), CardinalityIntervals.SET)) + .seed(a1, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(c1, builder -> builder + .reducedValue(TruthValue.FALSE) + .put(Tuple.of(0), TruthValue.TRUE) + .put(Tuple.of(2), TruthValue.TRUE)) + .seed(c2, builder -> builder + .reducedValue(TruthValue.FALSE) + .put(Tuple.of(1), TruthValue.TRUE) + .put(Tuple.of(3), TruthValue.TRUE)) + .build(); + + var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); + var propagationAdapter = model.getAdapter(PropagationAdapter.class); + model.commit(); + + reasoningAdapter.split(0); + assertThat(propagationAdapter.propagate(), is(PropagationResult.UNCHANGED)); + model.commit(); + + reasoningAdapter.split(0); + assertThat(propagationAdapter.propagate(), is(PropagationResult.UNCHANGED)); + } +} 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 new file mode 100644 index 00000000..779e18ab --- /dev/null +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/ModelGenerationTest.java @@ -0,0 +1,272 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.semantics.model; + +import com.google.inject.Inject; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import tools.refinery.language.ProblemStandaloneSetup; +import tools.refinery.language.model.tests.utils.ProblemParseHelper; +import tools.refinery.language.tests.ProblemInjectorProvider; +import tools.refinery.store.dse.propagation.PropagationAdapter; +import tools.refinery.store.dse.strategy.BestFirstStoreManager; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.ReasoningStoreAdapter; +import tools.refinery.store.reasoning.literal.Concreteness; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; +import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; + +import java.util.LinkedHashMap; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; + +@ExtendWith(InjectionExtension.class) +@InjectWith(ProblemInjectorProvider.class) +@Disabled("For debugging purposes only") +class ModelGenerationTest { + @Inject + private ProblemParseHelper parseHelper; + + @Inject + private ModelInitializer modelInitializer; + + @Test + void socialNetworkTest() { + var parsedProblem = parseHelper.parse(""" + % Metamodel + class Person { + contains Post posts opposite author + Person friend opposite friend + } + + class Post { + container Person[0..1] author opposite posts + Post replyTo + } + + % Constraints + error replyToNotFriend(Post x, Post y) <-> + replyTo(x, y), + author(x, xAuthor), + author(y, yAuthor), + xAuthor != yAuthor, + !friend(xAuthor, yAuthor). + + error replyToCycle(Post x) <-> replyTo+(x, x). + + % Instance model + !friend(a, b). + author(p1, a). + author(p2, b). + + !author(Post::new, a). + + % Scope + scope Post = 5, Person = 5. + """); + assertThat(parsedProblem.errors(), empty()); + var problem = parsedProblem.problem(); + + var storeBuilder = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(ModelVisualizerAdapter.builder() + .withOutputPath("test_output") + .withFormat(FileFormat.DOT) + .withFormat(FileFormat.SVG) +// .saveStates() + .saveDesignSpace()) + .with(PropagationAdapter.builder()) + .with(StateCoderAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .with(ReasoningAdapter.builder()); + + var modelSeed = modelInitializer.createModel(problem, storeBuilder); + + var store = storeBuilder.build(); + + var initialModel = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + + var initialVersion = initialModel.commit(); + + var bestFirst = new BestFirstStoreManager(store, 1); + bestFirst.startExploration(initialVersion); + var resultStore = bestFirst.getSolutionStore(); + System.out.println("states size: " + resultStore.getSolutions().size()); +// initialModel.getAdapter(ModelVisualizerAdapter.class).visualize(bestFirst.getVisualizationStore()); + } + + @Test + void statechartTest() { + var parsedProblem = parseHelper.parse(""" + // Metamodel + abstract class CompositeElement { + contains Region[] regions + } + + class Region { + contains Vertex[] vertices opposite region + } + + abstract class Vertex { + container Region[0..1] region opposite vertices + Transition[] outgoingTransition opposite source + Transition[] incomingTransition opposite target + } + + class Transition { + Vertex source opposite outgoingTransition + Vertex target opposite incomingTransition + } + + abstract class Pseudostate extends Vertex {} + + abstract class RegularState extends Vertex {} + + class Entry extends Pseudostate {} + + class Exit extends Pseudostate {} + + class Choice extends Pseudostate {} + + class FinalState extends RegularState {} + + class State extends RegularState, CompositeElement {} + + class Statechart extends CompositeElement {} + + // Constraints + + ///////// + // Entry + ///////// + + pred entryInRegion(Region r, Entry e) <-> + vertices(r, e). + + error noEntryInRegion(Region r) <-> + !entryInRegion(r, _). + + error multipleEntryInRegion(Region r) <-> + entryInRegion(r, e1), + entryInRegion(r, e2), + e1 != e2. + + error incomingToEntry(Transition t, Entry e) <-> + target(t, e). + + error noOutgoingTransitionFromEntry(Entry e) <-> + !source(_, e). + + error multipleTransitionFromEntry(Entry e, Transition t1, Transition t2) <-> + outgoingTransition(e, t1), + outgoingTransition(e, t2), + t1 != t2. + + ///////// + // Exit + ///////// + + error outgoingFromExit(Transition t, Exit e) <-> + source(t, e). + + ///////// + // Final + ///////// + + error outgoingFromFinal(Transition t, FinalState e) <-> + source(t, e). + + ///////// + // State vs Region + ///////// + + pred stateInRegion(Region r, State s) <-> + vertices(r, s). + + error noStateInRegion(Region r) <-> + !stateInRegion(r, _). + + ///////// + // Choice + ///////// + + error choiceHasNoOutgoing(Choice c) <-> + !source(_, c). + + error choiceHasNoIncoming(Choice c) <-> + !target(_, c). + + scope node = 50..60, Statechart = 1. + """); + assertThat(parsedProblem.errors(), empty()); + var problem = parsedProblem.problem(); + + var storeBuilder = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) +// .with(ModelVisualizerAdapter.builder() +// .withOutputPath("test_output") +// .withFormat(FileFormat.DOT) +// .withFormat(FileFormat.SVG) +// .saveStates() +// .saveDesignSpace()) + .with(PropagationAdapter.builder()) + .with(StateCoderAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .with(ReasoningAdapter.builder()); + + var modelSeed = modelInitializer.createModel(problem, storeBuilder); + + var store = storeBuilder.build(); + + var initialModel = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + + var initialVersion = initialModel.commit(); + + var bestFirst = new BestFirstStoreManager(store, 1); + bestFirst.startExploration(initialVersion); + var resultStore = bestFirst.getSolutionStore(); + System.out.println("states size: " + resultStore.getSolutions().size()); + + var model = store.createModelForState(resultStore.getSolutions().get(0).version()); + var interpretation = model.getAdapter(ReasoningAdapter.class) + .getPartialInterpretation(Concreteness.CANDIDATE, ReasoningAdapter.EXISTS_SYMBOL); + var cursor = interpretation.getAll(); + int max = -1; + var types = new LinkedHashMap(); + var typeInterpretation = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL); + while (cursor.move()) { + max = Math.max(max, cursor.getKey().get(0)); + var type = typeInterpretation.get(cursor.getKey()); + if (type != null) { + types.compute(type.candidateType(), (ignoredKey, oldValue) -> oldValue == null ? 1 : oldValue + 1); + } + } + System.out.println("Model size: " + (max + 1)); + System.out.println(types); +// initialModel.getAdapter(ModelVisualizerAdapter.class).visualize(bestFirst.getVisualizationStore()); + } + + public static void main(String[] args) { + ProblemStandaloneSetup.doSetup(); + var injector = new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration(); + var test = injector.getInstance(ModelGenerationTest.class); + try { + test.statechartTest(); + } catch (AssertionError e) { + e.printStackTrace(); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/BoundPropagator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/BoundPropagator.java new file mode 100644 index 00000000..5ad61463 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/BoundPropagator.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +@FunctionalInterface +public interface BoundPropagator { + PropagationResult propagateOne(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationAdapter.java new file mode 100644 index 00000000..3ea5a75f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationAdapter.java @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.propagation.impl.PropagationBuilderImpl; + +public interface PropagationAdapter extends ModelAdapter { + @Override + PropagationStoreAdapter getStoreAdapter(); + + PropagationResult propagate(); + + static PropagationBuilder builder() { + return new PropagationBuilderImpl(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java new file mode 100644 index 00000000..f8a89b30 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.model.ModelStore; + +import java.util.Collection; +import java.util.List; + +@SuppressWarnings("UnusedReturnValue") +public interface PropagationBuilder extends ModelAdapterBuilder { + PropagationBuilder rule(Rule propagationRule); + + default PropagationBuilder rules(Rule... propagationRules) { + return rules(List.of(propagationRules)); + } + + default PropagationBuilder rules(Collection propagationRules) { + propagationRules.forEach(this::rule); + return this; + } + + PropagationBuilder propagator(Propagator propagator); + + @Override + PropagationStoreAdapter build(ModelStore store); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationResult.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationResult.java new file mode 100644 index 00000000..ea56653a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationResult.java @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +public enum PropagationResult { + UNCHANGED, + PROPAGATED, + REJECTED; + + public PropagationResult andThen(PropagationResult next) { + return switch (this) { + case UNCHANGED -> next; + case PROPAGATED -> next == REJECTED ? REJECTED : PROPAGATED; + case REJECTED -> REJECTED; + }; + } + + public boolean isRejected() { + return this == REJECTED; + } + + public boolean isChanged() { + return this == PROPAGATED; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationStoreAdapter.java new file mode 100644 index 00000000..82cba909 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationStoreAdapter.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.model.Model; + +public interface PropagationStoreAdapter extends ModelStoreAdapter { + @Override + PropagationAdapter createModelAdapter(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/Propagator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/Propagator.java new file mode 100644 index 00000000..c6b4e1c9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/Propagator.java @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; + +@FunctionalInterface +public interface Propagator { + default void configure(ModelStoreBuilder storeBuilder) { + } + + BoundPropagator bindToModel(Model 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 new file mode 100644 index 00000000..586a8d7a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl; + +import tools.refinery.store.dse.propagation.BoundPropagator; +import tools.refinery.store.dse.propagation.PropagationAdapter; +import tools.refinery.store.dse.propagation.PropagationResult; +import tools.refinery.store.dse.propagation.PropagationStoreAdapter; +import tools.refinery.store.model.Model; + +class PropagationAdapterImpl implements PropagationAdapter { + private final Model model; + private final PropagationStoreAdapterImpl storeAdapter; + private final BoundPropagator[] boundPropagators; + + public PropagationAdapterImpl(Model model, PropagationStoreAdapterImpl storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + var propagators = storeAdapter.getPropagators(); + boundPropagators = new BoundPropagator[propagators.size()]; + for (int i = 0; i < boundPropagators.length; i++) { + boundPropagators[i] = propagators.get(i).bindToModel(model); + } + } + + @Override + public PropagationResult propagate() { + PropagationResult result = PropagationResult.UNCHANGED; + PropagationResult lastResult; + do { + lastResult = propagateOne(); + result = result.andThen(lastResult); + } while (lastResult.isChanged()); + return result; + } + + private PropagationResult propagateOne() { + PropagationResult result = PropagationResult.UNCHANGED; + for (int i = 0; i < boundPropagators.length; i++) { + var lastResult = propagateUntilFixedPoint(i); + result = result.andThen(lastResult); + if (result.isRejected()) { + break; + } + } + return result; + } + + private PropagationResult propagateUntilFixedPoint(int propagatorIndex) { + var propagator = boundPropagators[propagatorIndex]; + PropagationResult result = PropagationResult.UNCHANGED; + PropagationResult lastResult; + do { + lastResult = propagator.propagateOne(); + result = result.andThen(lastResult); + } while (lastResult.isChanged()); + return result; + } + + @Override + public Model getModel() { + return model; + } + + @Override + public PropagationStoreAdapter getStoreAdapter() { + return storeAdapter; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java new file mode 100644 index 00000000..c844a89f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.propagation.PropagationBuilder; +import tools.refinery.store.dse.propagation.PropagationStoreAdapter; +import tools.refinery.store.dse.propagation.Propagator; +import tools.refinery.store.dse.propagation.impl.rule.RuleBasedPropagator; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; + +import java.util.*; + +public class PropagationBuilderImpl extends AbstractModelAdapterBuilder + implements PropagationBuilder { + private final Set propagationRules = new LinkedHashSet<>(); + private final Deque propagators = new ArrayDeque<>(); + + @Override + public PropagationBuilder rule(Rule propagationRule) { + checkNotConfigured(); + propagationRules.add(propagationRule); + return this; + } + + @Override + public PropagationBuilder propagator(Propagator propagator) { + checkNotConfigured(); + propagators.addFirst(propagator); + return this; + } + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + super.doConfigure(storeBuilder); + if (!propagationRules.isEmpty()) { + propagators.addFirst(new RuleBasedPropagator(List.copyOf(propagationRules))); + } + for (var propagator : propagators) { + propagator.configure(storeBuilder); + } + } + + @Override + protected PropagationStoreAdapter doBuild(ModelStore store) { + return new PropagationStoreAdapterImpl(store, List.copyOf(propagators)); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java new file mode 100644 index 00000000..a223caed --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl; + +import tools.refinery.store.dse.propagation.PropagationAdapter; +import tools.refinery.store.dse.propagation.PropagationStoreAdapter; +import tools.refinery.store.dse.propagation.Propagator; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; + +import java.util.List; + +class PropagationStoreAdapterImpl implements PropagationStoreAdapter { + private final ModelStore store; + private final List propagators; + + PropagationStoreAdapterImpl(ModelStore store, List propagators) { + this.store = store; + this.propagators = propagators; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public PropagationAdapter createModelAdapter(Model model) { + return new PropagationAdapterImpl(model, this); + } + + List getPropagators() { + return propagators; + } +} 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 new file mode 100644 index 00000000..6e6a78d2 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl.rule; + +import tools.refinery.store.dse.propagation.PropagationResult; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.dse.transition.actions.BoundAction; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.resultset.ResultSet; + +class BoundPropagationRule { + private final ResultSet resultSet; + private final BoundAction action; + + public BoundPropagationRule(Model model, Rule rule) { + resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(rule.getPrecondition()); + action = rule.createAction(model); + } + + public PropagationResult fireAll() { + if (resultSet.size() == 0) { + return PropagationResult.UNCHANGED; + } + var cursor = resultSet.getAll(); + while (cursor.move()) { + var result = action.fire(cursor.getKey()); + if (!result) { + return PropagationResult.REJECTED; + } + } + return PropagationResult.PROPAGATED; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundRuleBasedPropagator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundRuleBasedPropagator.java new file mode 100644 index 00000000..bd03f923 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundRuleBasedPropagator.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl.rule; + +import tools.refinery.store.dse.propagation.BoundPropagator; +import tools.refinery.store.dse.propagation.PropagationResult; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; + +import java.util.List; + +public class BoundRuleBasedPropagator implements BoundPropagator { + private final ModelQueryAdapter queryEngine; + private final BoundPropagationRule[] boundRules; + + public BoundRuleBasedPropagator(Model model, List propagationRules) { + queryEngine = model.getAdapter(ModelQueryAdapter.class); + boundRules = new BoundPropagationRule[propagationRules.size()]; + for (int i = 0; i < boundRules.length; i++) { + boundRules[i] = new BoundPropagationRule(model, propagationRules.get(i)); + } + } + + @Override + public PropagationResult propagateOne() { + queryEngine.flushChanges(); + PropagationResult result = PropagationResult.UNCHANGED; + // Use a classic for loop to avoid allocating an iterator. + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < boundRules.length; i++) { + var lastResult = boundRules[i].fireAll(); + result = result.andThen(lastResult); + if (result.isRejected()) { + break; + } + } + return result; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/RuleBasedPropagator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/RuleBasedPropagator.java new file mode 100644 index 00000000..709b2416 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/RuleBasedPropagator.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.propagation.impl.rule; + +import tools.refinery.store.dse.propagation.BoundPropagator; +import tools.refinery.store.dse.propagation.Propagator; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryBuilder; + +import java.util.List; + +public class RuleBasedPropagator implements Propagator { + private final List propagationRules; + + public RuleBasedPropagator(List propagationRules) { + this.propagationRules = propagationRules; + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); + for (var propagationRule : propagationRules) { + queryBuilder.query(propagationRule.getPrecondition()); + } + } + + @Override + public BoundPropagator bindToModel(Model model) { + return new BoundRuleBasedPropagator(model, propagationRules); + } +} 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 8f7e3bdc..4a75a3a6 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 @@ -33,7 +33,11 @@ public class BestFirstExplorer extends BestFirstWorker { var lastBest = submit().newVersion(); while (shouldRun()) { if (lastBest == null) { - lastBest = restoreToBest(); + if (random.nextInt(10) == 0) { + lastBest = restoreToRandom(random); + } else { + lastBest = restoreToBest(); + } if (lastBest == null) { return; } @@ -49,7 +53,7 @@ public class BestFirstExplorer extends BestFirstWorker { } else { var newVisit = newSubmit.newVersion(); int compareResult = compare(lastBest, newVisit); - if (compareResult >= 0) { + if (compareResult >= 0) { lastBest = newVisit; } else { lastBest = null; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java index 5d738297..aca800a3 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -5,6 +5,8 @@ */ package tools.refinery.store.dse.strategy; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.dse.propagation.PropagationAdapter; import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.dse.transition.ObjectiveValue; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; @@ -24,6 +26,7 @@ public class BestFirstWorker { final StateCoderAdapter stateCoderAdapter; final DesignSpaceExplorationAdapter explorationAdapter; final ModelQueryAdapter queryAdapter; + final @Nullable PropagationAdapter propagationAdapter; final VisualizationStore visualizationStore; final boolean isVisualizationEnabled; @@ -34,6 +37,7 @@ public class BestFirstWorker { explorationAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); queryAdapter = model.getAdapter(ModelQueryAdapter.class); + propagationAdapter = model.tryGetAdapter(PropagationAdapter.class).orElse(null); activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), explorationAdapter.getTransformations()); visualizationStore = storeManager.getVisualizationStore(); @@ -96,7 +100,11 @@ public class BestFirstWorker { } public VersionWithObjectiveValue restoreToRandom(Random random) { - var randomVersion = storeManager.getObjectiveStore().getRandom(random); + var objectiveStore = storeManager.getObjectiveStore(); + if (objectiveStore.getSize() == 0) { + return null; + } + var randomVersion = objectiveStore.getRandom(random); last = randomVersion; if (randomVersion != null) { this.model.restore(randomVersion.version()); @@ -108,41 +116,40 @@ public class BestFirstWorker { return storeManager.getObjectiveStore().getComparator().compare(s1, s2); } - public boolean stateHasUnvisited() { - if (!model.hasUncommittedChanges()) { - return storeManager.getActivationStore().hasUnmarkedActivation(last); - } else { - throw new IllegalStateException("The model has uncommitted changes!"); - } - } - public record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) { } public RandomVisitResult visitRandomUnvisited(Random random) { checkSynchronized(); - if (!model.hasUncommittedChanges()) { - var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); - queryAdapter.flushChanges(); - - if (visitResult.successfulVisit()) { - Version oldVersion = null; - if (isVisualizationEnabled) { - oldVersion = last.version(); - } - var submitResult = submit(); - if (isVisualizationEnabled && submitResult.newVersion() != null) { - var newVersion = submitResult.newVersion().version(); - visualizationStore.addTransition(oldVersion, newVersion, - "fire: " + visitResult.transformation() + ", " + visitResult.activation()); - } - return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); - } else { + if (model.hasUncommittedChanges()) { + throw new IllegalStateException("The model has uncommitted changes!"); + } + + var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); + + if (!visitResult.successfulVisit()) { + return new RandomVisitResult(null, visitResult.mayHaveMore()); + } + + if (propagationAdapter != null) { + var propagationResult = propagationAdapter.propagate(); + if (propagationResult.isRejected()) { return new RandomVisitResult(null, visitResult.mayHaveMore()); } - } else { - throw new IllegalStateException("The model has uncommitted changes!"); } + queryAdapter.flushChanges(); + + Version oldVersion = null; + if (isVisualizationEnabled) { + oldVersion = last.version(); + } + var submitResult = submit(); + if (isVisualizationEnabled && submitResult.newVersion() != null) { + var newVersion = submitResult.newVersion().version(); + visualizationStore.addTransition(oldVersion, newVersion, + "fire: " + visitResult.transformation() + ", " + visitResult.activation()); + } + return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); } public boolean hasEnoughSolution() { 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 55f43735..ed2ff33d 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 @@ -11,18 +11,14 @@ import tools.refinery.store.tuple.Tuple; public class BoundAction { private final Action action; - private final BoundActionLiteral[] boundLiterals; + private final Model model; + private BoundActionLiteral @Nullable [] boundLiterals; private Tuple activation; private final int[] localVariables; BoundAction(Action action, Model model) { this.action = action; - var actionLiterals = action.getActionLiterals(); - int size = actionLiterals.size(); - boundLiterals = new BoundActionLiteral[size]; - for (int i = 0; i < size; i++) { - boundLiterals[i] = actionLiterals.get(i).bindToModel(model); - } + this.model = model; localVariables = new int[action.getLocalVariables().size()]; } @@ -31,6 +27,9 @@ public class BoundAction { throw new IllegalStateException("Reentrant firing is not allowed"); } this.activation = activation; + if (boundLiterals == null) { + boundLiterals = bindLiterals(); + } try { int size = boundLiterals.length; for (int i = 0; i < size; i++) { @@ -50,6 +49,16 @@ public class BoundAction { return true; } + private BoundActionLiteral[] bindLiterals() { + var actionLiterals = action.getActionLiterals(); + int size = actionLiterals.size(); + var boundLiteralsArray = new BoundActionLiteral[size]; + for (int i = 0; i < size; i++) { + boundLiteralsArray[i] = actionLiterals.get(i).bindToModel(model); + } + return boundLiteralsArray; + } + private Tuple getInputTuple(int @Nullable [] inputAllocation) { if (inputAllocation == null) { // Identity allocation. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java index 9f4bb536..5a7ba8f4 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -28,9 +28,9 @@ public class QueryObjective implements Objective { return () -> { var cursor = resultSet.getAll(); if (!cursor.move()) { - throw new IllegalStateException("Query providing the objective function has no values!"); + return 0; } - return cursor.getValue().doubleValue(); + return Math.max(cursor.getValue().doubleValue(), 0); }; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java index d9e29eca..82f89db7 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -104,9 +104,16 @@ public class ActivationStoreImpl implements ActivationStore { public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) { var entries = versionToActivations.get(version); + var weights = new double[entries.size()]; + double totalWeight = 0; int numberOfAllUnvisitedActivations = 0; - for (var entry : entries) { - numberOfAllUnvisitedActivations += entry.getNumberOfUnvisitedActivations(); + for (int i = 0; i < weights.length; i++) { + var entry = entries.get(i); + int unvisited = entry.getNumberOfUnvisitedActivations(); + double weight = unvisited == 0 ? 0 : unvisited; //(Math.log(unvisited) + 1.0); + weights[i] = weight; + totalWeight += weight; + numberOfAllUnvisitedActivations += unvisited; } if (numberOfAllUnvisitedActivations == 0) { @@ -114,18 +121,18 @@ public class ActivationStoreImpl implements ActivationStore { return new VisitResult(false, false, -1, -1); } - int offset = random.nextInt(numberOfAllUnvisitedActivations); + double offset = random.nextDouble(totalWeight); int transformation = 0; for (; transformation < entries.size(); transformation++) { - var entry = entries.get(transformation); - int unvisited = entry.getNumberOfUnvisitedActivations(); - if (unvisited > 0 && offset < unvisited) { + double weight = weights[transformation]; + if (weight > 0 && offset < weight) { + var entry = entries.get(transformation); int activation = random.nextInt(entry.getNumberOfActivations()); return this.visitActivation(version, transformation, activation); } - offset -= unvisited; + offset -= weight; } - throw new AssertionError("Unvisited activation %d not found".formatted(offset)); + throw new AssertionError("Unvisited activation %f not found".formatted(offset)); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java index 881b133c..1d7c5ce5 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java @@ -32,7 +32,7 @@ public class ActivationStoreWorker { public ActivationStore.VisitResult fireRandomActivation(VersionWithObjectiveValue thisVersion, Random random) { var result = store.getRandomAndMarkAsVisited(thisVersion, random); - if(result.successfulVisit()) { + if (result.successfulVisit()) { int selectedTransformation = result.transformation(); int selectedActivation = result.activation(); @@ -40,13 +40,13 @@ public class ActivationStoreWorker { var tuple = transformation.getActivation(selectedActivation); boolean success = transformation.fireActivation(tuple); - if(success) { + if (success) { return result; } else { return new ActivationStore.VisitResult( false, result.mayHaveMore(), - selectedActivation, + selectedTransformation, selectedActivation); } } diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/BoundScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/BoundScopePropagator.java new file mode 100644 index 00000000..62aadb4a --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/BoundScopePropagator.java @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; +import org.eclipse.collections.api.factory.primitive.IntObjectMaps; +import org.eclipse.collections.api.factory.primitive.IntSets; +import org.eclipse.collections.api.map.primitive.MutableIntObjectMap; +import org.eclipse.collections.api.set.primitive.MutableIntSet; +import tools.refinery.store.dse.propagation.BoundPropagator; +import tools.refinery.store.dse.propagation.PropagationResult; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.representation.cardinality.*; +import tools.refinery.store.tuple.Tuple; + +class BoundScopePropagator implements BoundPropagator { + private final ModelQueryAdapter queryEngine; + private final Interpretation countInterpretation; + private final MPSolver solver; + private final MPObjective objective; + private final MutableIntObjectMap variables = IntObjectMaps.mutable.empty(); + private final MutableIntSet activeVariables = IntSets.mutable.empty(); + private final TypeScopePropagator[] propagators; + private boolean changed = true; + + public BoundScopePropagator(Model model, ScopePropagator storeAdapter) { + queryEngine = model.getAdapter(ModelQueryAdapter.class); + countInterpretation = model.getInterpretation(storeAdapter.getCountSymbol()); + solver = MPSolver.createSolver("GLOP"); + objective = solver.objective(); + initializeVariables(); + countInterpretation.addListener(this::countChanged, true); + var propagatorFactories = storeAdapter.getTypeScopePropagatorFactories(); + propagators = new TypeScopePropagator[propagatorFactories.size()]; + for (int i = 0; i < propagators.length; i++) { + propagators[i] = propagatorFactories.get(i).createPropagator(this); + } + } + + ModelQueryAdapter getQueryEngine() { + return queryEngine; + } + + private void initializeVariables() { + var cursor = countInterpretation.getAll(); + while (cursor.move()) { + var interval = cursor.getValue(); + if (!interval.equals(CardinalityIntervals.ONE)) { + int nodeId = cursor.getKey().get(0); + createVariable(nodeId, interval); + activeVariables.add(nodeId); + } + } + } + + private MPVariable createVariable(int nodeId, CardinalityInterval interval) { + double lowerBound = interval.lowerBound(); + double upperBound = getUpperBound(interval); + var variable = solver.makeNumVar(lowerBound, upperBound, "x" + nodeId); + variables.put(nodeId, variable); + return variable; + } + + private void countChanged(Tuple key, CardinalityInterval fromValue, CardinalityInterval toValue, + boolean ignoredRestoring) { + int nodeId = key.get(0); + if ((toValue == null || toValue.equals(CardinalityIntervals.ONE))) { + if (fromValue != null && !fromValue.equals(CardinalityIntervals.ONE)) { + removeActiveVariable(toValue, nodeId); + } + return; + } + if (fromValue == null || fromValue.equals(CardinalityIntervals.ONE)) { + activeVariables.add(nodeId); + } + var variable = variables.get(nodeId); + if (variable == null) { + createVariable(nodeId, toValue); + markAsChanged(); + return; + } + double lowerBound = toValue.lowerBound(); + double upperBound = getUpperBound(toValue); + if (variable.lb() != lowerBound) { + variable.setLb(lowerBound); + markAsChanged(); + } + if (variable.ub() != upperBound) { + variable.setUb(upperBound); + markAsChanged(); + } + } + + private void removeActiveVariable(CardinalityInterval toValue, int nodeId) { + var variable = variables.get(nodeId); + if (variable == null || !activeVariables.remove(nodeId)) { + throw new AssertionError("Variable not active: " + nodeId); + } + if (toValue == null) { + variable.setBounds(0, 0); + } else { + // Until queries are flushed and the constraints can be properly updated, + // the variable corresponding to the (previous) multi-object has to stand in for a single object. + variable.setBounds(1, 1); + } + markAsChanged(); + } + + MPConstraint makeConstraint() { + return solver.makeConstraint(); + } + + MPVariable getVariable(int nodeId) { + var variable = variables.get(nodeId); + if (variable != null) { + return variable; + } + var interval = countInterpretation.get(Tuple.of(nodeId)); + if (interval == null || interval.equals(CardinalityIntervals.ONE)) { + interval = CardinalityIntervals.NONE; + } else { + activeVariables.add(nodeId); + markAsChanged(); + } + return createVariable(nodeId, interval); + } + + void markAsChanged() { + changed = true; + } + + @Override + public PropagationResult propagateOne() { + queryEngine.flushChanges(); + if (!changed) { + return PropagationResult.UNCHANGED; + } + changed = false; + for (var propagator : propagators) { + propagator.updateBounds(); + } + var result = PropagationResult.UNCHANGED; + if (activeVariables.isEmpty()) { + return checkEmptiness(); + } + var iterator = activeVariables.intIterator(); + while (iterator.hasNext()) { + int nodeId = iterator.next(); + var variable = variables.get(nodeId); + if (variable == null) { + throw new AssertionError("Missing active variable: " + nodeId); + } + result = result.andThen(propagateNode(nodeId, variable)); + if (result.isRejected()) { + return result; + } + } + return result; + } + + private PropagationResult checkEmptiness() { + var emptinessCheckingResult = solver.solve(); + return switch (emptinessCheckingResult) { + case OPTIMAL, UNBOUNDED -> PropagationResult.UNCHANGED; + case INFEASIBLE -> PropagationResult.REJECTED; + default -> throw new IllegalStateException("Failed to check for consistency: " + emptinessCheckingResult); + }; + } + + private PropagationResult propagateNode(int nodeId, MPVariable variable) { + objective.setCoefficient(variable, 1); + try { + objective.setMinimization(); + var minimizationResult = solver.solve(); + int lowerBound; + switch (minimizationResult) { + case OPTIMAL -> lowerBound = RoundingUtil.roundUp(objective.value()); + case UNBOUNDED -> lowerBound = 0; + case INFEASIBLE -> { + return PropagationResult.REJECTED; + } + default -> throw new IllegalStateException("Failed to solve for minimum of %s: %s" + .formatted(variable, minimizationResult)); + } + + objective.setMaximization(); + var maximizationResult = solver.solve(); + UpperCardinality upperBound; + switch (maximizationResult) { + case OPTIMAL -> upperBound = UpperCardinalities.atMost(RoundingUtil.roundDown(objective.value())); + // Problem was feasible when minimizing, the only possible source of {@code UNBOUNDED_OR_INFEASIBLE} is + // an unbounded maximization problem. See https://github.com/google/or-tools/issues/3319 + case UNBOUNDED, INFEASIBLE -> upperBound = UpperCardinalities.UNBOUNDED; + default -> throw new IllegalStateException("Failed to solve for maximum of %s: %s" + .formatted(variable, minimizationResult)); + } + + var newInterval = CardinalityIntervals.between(lowerBound, upperBound); + var oldInterval = countInterpretation.put(Tuple.of(nodeId), newInterval); + if (newInterval.lowerBound() < oldInterval.lowerBound() || + newInterval.upperBound().compareTo(oldInterval.upperBound()) > 0) { + throw new IllegalArgumentException("Failed to refine multiplicity %s of node %d to %s" + .formatted(oldInterval, nodeId, newInterval)); + } + return newInterval.equals(oldInterval) ? PropagationResult.UNCHANGED : PropagationResult.PROPAGATED; + } finally { + objective.setCoefficient(variable, 0); + } + } + + private static double getUpperBound(CardinalityInterval interval) { + var upperBound = interval.upperBound(); + if (upperBound instanceof FiniteUpperCardinality finiteUpperCardinality) { + return finiteUpperCardinality.finiteUpperBound(); + } else if (upperBound instanceof UnboundedUpperCardinality) { + return Double.POSITIVE_INFINITY; + } else { + throw new IllegalArgumentException("Unknown upper bound: " + upperBound); + } + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/LowerTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/LowerTypeScopePropagator.java new file mode 100644 index 00000000..5d903f41 --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/LowerTypeScopePropagator.java @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.objectives.Criteria; +import tools.refinery.store.dse.transition.objectives.Objectives; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.term.Variable; +import tools.refinery.store.reasoning.ReasoningBuilder; +import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; +import tools.refinery.store.reasoning.representation.PartialRelation; + +import java.util.Collection; +import java.util.List; + +import static tools.refinery.store.query.literal.Literals.check; +import static tools.refinery.store.query.term.int_.IntTerms.*; +import static tools.refinery.store.reasoning.literal.PartialLiterals.may; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; + +class LowerTypeScopePropagator extends TypeScopePropagator { + private final int lowerBound; + + private LowerTypeScopePropagator(BoundScopePropagator adapter, int lowerBound, RelationalQuery allQuery, + RelationalQuery multiQuery) { + super(adapter, allQuery, multiQuery); + this.lowerBound = lowerBound; + } + + @Override + public void updateBounds() { + constraint.setLb((lowerBound - getSingleCount())); + } + + public static class Factory extends TypeScopePropagator.Factory { + private final PartialRelation type; + private final int lowerBound; + private final RelationalQuery allMay; + private final RelationalQuery multiMay; + + public Factory(PartialRelation type, int lowerBound) { + this.type = type; + this.lowerBound = lowerBound; + allMay = Query.of(type.name() + "#may", (builder, instance) -> builder.clause( + may(type.call(instance)) + )); + multiMay = Query.of(type.name() + "#multiMay", (builder, instance) -> builder.clause( + may(type.call(instance)), + MULTI_VIEW.call(instance) + )); + } + + @Override + public TypeScopePropagator createPropagator(BoundScopePropagator adapter) { + return new LowerTypeScopePropagator(adapter, lowerBound, allMay, multiMay); + } + + @Override + protected Collection getQueries() { + return List.of(allMay, multiMay); + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + super.configure(storeBuilder); + + var requiredObjects = Query.of(type.name() + "#required", Integer.class, (builder, output) -> builder + .clause(Integer.class, candidateLowerBound -> List.of( + new CountCandidateLowerBoundLiteral(candidateLowerBound, type, List.of(Variable.of())), + output.assign(sub(constant(lowerBound), candidateLowerBound)), + check(greater(output, constant(0))) + ))); + + storeBuilder.getAdapter(ReasoningBuilder.class).objective(Objectives.value(requiredObjects)); + storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class).ifPresent(dseBuilder -> + dseBuilder.accept(Criteria.whenNoMatch(requiredObjects))); + } + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/RoundingUtil.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/RoundingUtil.java new file mode 100644 index 00000000..986771a1 --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/RoundingUtil.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +final class RoundingUtil { + private static final double TOLERANCE = 0.01; + + private RoundingUtil() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static int roundUp(double value) { + double ceil = Math.ceil(value - TOLERANCE); + int intCeil = (int) ceil; + return Math.max(intCeil, 0); + } + + public static int roundDown(double value) { + double floor = Math.floor(value + TOLERANCE); + int intFloor = (int) floor; + return Math.max(intFloor, 0); + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagator.java new file mode 100644 index 00000000..25b1966c --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagator.java @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import com.google.ortools.Loader; +import tools.refinery.store.dse.propagation.PropagationBuilder; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.model.ModelStoreConfiguration; +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.representation.Symbol; +import tools.refinery.store.representation.cardinality.CardinalityInterval; +import tools.refinery.store.representation.cardinality.FiniteUpperCardinality; + +import java.util.*; + +public class ScopePropagator implements ModelStoreConfiguration { + private final Symbol countSymbol; + private final Map scopes = new LinkedHashMap<>(); + private final List typeScopePropagatorFactories = new ArrayList<>(); + + public ScopePropagator() { + this(MultiObjectTranslator.COUNT_STORAGE); + } + + public ScopePropagator(Symbol countSymbol) { + if (countSymbol.arity() != 1) { + throw new IllegalArgumentException("Count symbol must have arty 1, got %s with arity %d instead" + .formatted(countSymbol, countSymbol.arity())); + } + if (!countSymbol.valueType().equals(CardinalityInterval.class)) { + throw new IllegalArgumentException("Count symbol must have CardinalityInterval values"); + } + if (countSymbol.defaultValue() != null) { + throw new IllegalArgumentException("Count symbol must default value null"); + } + this.countSymbol = countSymbol; + } + + public ScopePropagator scope(PartialRelation type, CardinalityInterval interval) { + if (type.arity() != 1) { + throw new TranslationException(type, "Only types with arity 1 may have scopes, got %s with arity %d" + .formatted(type, type.arity())); + } + var newValue = scopes.compute(type, (ignoredKey, oldValue) -> + oldValue == null ? interval : oldValue.meet(interval)); + if (newValue.isEmpty()) { + throw new TranslationException(type, "Unsatisfiable scope for type %s".formatted(type)); + } + return this; + } + + public ScopePropagator scopes(Map scopes) { + return scopes(scopes.entrySet()); + } + + public ScopePropagator scopes(Collection> scopes) { + for (var entry : scopes) { + scope(entry.getKey(), entry.getValue()); + } + return this; + } + + @Override + public void apply(ModelStoreBuilder storeBuilder) { + createTypeScopePropagatorFactories(); + Loader.loadNativeLibraries(); + for (var factory : typeScopePropagatorFactories) { + factory.configure(storeBuilder); + } + storeBuilder.getAdapter(PropagationBuilder.class) + .propagator(model -> new BoundScopePropagator(model, this)); + } + + private void createTypeScopePropagatorFactories() { + for (var entry : scopes.entrySet()) { + var type = entry.getKey(); + var bounds = entry.getValue(); + if (bounds.lowerBound() > 0) { + var lowerFactory = new LowerTypeScopePropagator.Factory(type, bounds.lowerBound()); + typeScopePropagatorFactories.add(lowerFactory); + } + if (bounds.upperBound() instanceof FiniteUpperCardinality finiteUpperCardinality) { + var upperFactory = new UpperTypeScopePropagator.Factory(type, + finiteUpperCardinality.finiteUpperBound()); + typeScopePropagatorFactories.add(upperFactory); + } + } + } + + Symbol getCountSymbol() { + return countSymbol; + } + + List getTypeScopePropagatorFactories() { + return typeScopePropagatorFactories; + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorAdapter.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorAdapter.java deleted file mode 100644 index c2d3f59e..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorAdapter.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.reasoning.refinement.RefinementResult; -import tools.refinery.store.reasoning.scope.internal.ScopePropagatorBuilderImpl; - -public interface ScopePropagatorAdapter extends ModelAdapter { - @Override - ScopePropagatorStoreAdapter getStoreAdapter(); - - RefinementResult propagate(); - - static ScopePropagatorBuilder builder() { - return new ScopePropagatorBuilderImpl(); - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorBuilder.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorBuilder.java deleted file mode 100644 index a17e04a4..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorBuilder.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.representation.cardinality.CardinalityInterval; - -public interface ScopePropagatorBuilder extends ModelAdapterBuilder { - ScopePropagatorBuilder countSymbol(Symbol countSymbol); - - ScopePropagatorBuilder scope(PartialRelation type, CardinalityInterval interval); - - @Override - ScopePropagatorStoreAdapter build(ModelStore store); -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorStoreAdapter.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorStoreAdapter.java deleted file mode 100644 index 65d9c38d..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/ScopePropagatorStoreAdapter.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope; - -import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.representation.cardinality.CardinalityInterval; - -import java.util.Map; - -public interface ScopePropagatorStoreAdapter extends ModelStoreAdapter { - Map getScopes(); -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/TypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/TypeScopePropagator.java new file mode 100644 index 00000000..db80be7f --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/TypeScopePropagator.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import com.google.ortools.linearsolver.MPConstraint; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.Collection; + +abstract class TypeScopePropagator { + private final BoundScopePropagator adapter; + private final ResultSet allNodes; + private final ResultSet multiNodes; + protected final MPConstraint constraint; + + protected TypeScopePropagator(BoundScopePropagator adapter, RelationalQuery allQuery, + RelationalQuery multiQuery) { + this.adapter = adapter; + var queryEngine = adapter.getQueryEngine(); + allNodes = queryEngine.getResultSet(allQuery); + multiNodes = queryEngine.getResultSet(multiQuery); + constraint = adapter.makeConstraint(); + constraint.setBounds(0, Double.POSITIVE_INFINITY); + var cursor = multiNodes.getAll(); + while (cursor.move()) { + var variable = adapter.getVariable(cursor.getKey().get(0)); + constraint.setCoefficient(variable, 1); + } + allNodes.addListener(this::allChanged); + multiNodes.addListener(this::multiChanged); + } + + public abstract void updateBounds(); + + protected int getSingleCount() { + return allNodes.size() - multiNodes.size(); + } + + private void allChanged(Tuple ignoredKey, Boolean ignoredOldValue, Boolean ignoredNewValue) { + adapter.markAsChanged(); + } + + private void multiChanged(Tuple key, Boolean ignoredOldValue, Boolean newValue) { + var variable = adapter.getVariable(key.get(0)); + constraint.setCoefficient(variable, Boolean.TRUE.equals(newValue) ? 1 : 0); + adapter.markAsChanged(); + } + + public abstract static class Factory { + public abstract TypeScopePropagator createPropagator(BoundScopePropagator adapter); + + protected abstract Collection getQueries(); + + public void configure(ModelStoreBuilder storeBuilder) { + storeBuilder.getAdapter(ModelQueryBuilder.class).queries(getQueries()); + } + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/UpperTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/UpperTypeScopePropagator.java new file mode 100644 index 00000000..062f976c --- /dev/null +++ b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/UpperTypeScopePropagator.java @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.reasoning.representation.PartialRelation; + +import java.util.Collection; +import java.util.List; + +import static tools.refinery.store.reasoning.literal.PartialLiterals.must; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; + +class UpperTypeScopePropagator extends TypeScopePropagator { + private final int upperBound; + + private UpperTypeScopePropagator(BoundScopePropagator adapter, int upperBound, RelationalQuery allQuery, + RelationalQuery multiQuery) { + super(adapter, allQuery, multiQuery); + this.upperBound = upperBound; + } + + @Override + public void updateBounds() { + constraint.setUb(upperBound - getSingleCount()); + } + + public static class Factory extends TypeScopePropagator.Factory { + private final int upperBound; + private final RelationalQuery allMust; + private final RelationalQuery multiMust; + + public Factory(PartialRelation type, int upperBound) { + this.upperBound = upperBound; + allMust = Query.of(type.name() + "#must", (builder, instance) -> builder.clause( + must(type.call(instance)) + )); + multiMust = Query.of(type.name() + "#multiMust", (builder, instance) -> builder.clause( + must(type.call(instance)), + MULTI_VIEW.call(instance) + )); + } + + @Override + public TypeScopePropagator createPropagator(BoundScopePropagator adapter) { + return new UpperTypeScopePropagator(adapter, upperBound, allMust, multiMust); + } + + @Override + protected Collection getQueries() { + return List.of(allMust, multiMust); + } + } +} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java deleted file mode 100644 index 393c4b72..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/LowerTypeScopePropagator.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; -import tools.refinery.store.dse.transition.objectives.Criteria; -import tools.refinery.store.dse.transition.objectives.Objectives; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.dnf.AnyQuery; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.term.Variable; -import tools.refinery.store.reasoning.ReasoningBuilder; -import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; -import tools.refinery.store.reasoning.representation.PartialRelation; - -import java.util.Collection; -import java.util.List; - -import static tools.refinery.store.query.literal.Literals.check; -import static tools.refinery.store.query.term.int_.IntTerms.*; -import static tools.refinery.store.reasoning.literal.PartialLiterals.may; -import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; - -class LowerTypeScopePropagator extends TypeScopePropagator { - private final int lowerBound; - - private LowerTypeScopePropagator(ScopePropagatorAdapterImpl adapter, int lowerBound, RelationalQuery allQuery, - RelationalQuery multiQuery) { - super(adapter, allQuery, multiQuery); - this.lowerBound = lowerBound; - } - - @Override - public void updateBounds() { - constraint.setLb((lowerBound - getSingleCount())); - } - - public static class Factory extends TypeScopePropagator.Factory { - private final PartialRelation type; - private final int lowerBound; - private final RelationalQuery allMay; - private final RelationalQuery multiMay; - - public Factory(PartialRelation type, int lowerBound) { - this.type = type; - this.lowerBound = lowerBound; - allMay = Query.of(type.name() + "#may", (builder, instance) -> builder.clause( - may(type.call(instance)) - )); - multiMay = Query.of(type.name() + "#multiMay", (builder, instance) -> builder.clause( - may(type.call(instance)), - MULTI_VIEW.call(instance) - )); - } - - @Override - public TypeScopePropagator createPropagator(ScopePropagatorAdapterImpl adapter) { - return new LowerTypeScopePropagator(adapter, lowerBound, allMay, multiMay); - } - - @Override - protected Collection getQueries() { - return List.of(allMay, multiMay); - } - - @Override - public void configure(ModelStoreBuilder storeBuilder) { - super.configure(storeBuilder); - - var requiredObjects = Query.of(type.name() + "#required", Integer.class, (builder, output) -> builder - .clause(Integer.class, currentCount -> List.of( - new CountCandidateLowerBoundLiteral(currentCount, type, List.of(Variable.of())), - output.assign(sub(currentCount, constant(lowerBound))), - check(greater(currentCount, constant(0))) - ))); - - storeBuilder.getAdapter(ReasoningBuilder.class).objective(Objectives.value(requiredObjects)); - storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class).ifPresent(dseBuilder -> - dseBuilder.accept(Criteria.whenNoMatch(requiredObjects))); - } - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/RoundingUtil.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/RoundingUtil.java deleted file mode 100644 index a6d663ea..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/RoundingUtil.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -final class RoundingUtil { - private static final double TOLERANCE = 0.01; - - private RoundingUtil() { - throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); - } - - public static int roundUp(double value) { - double ceil = Math.ceil(value - TOLERANCE); - int intCeil = (int) ceil; - return Math.max(intCeil, 0); - } - - public static int roundDown(double value) { - double floor = Math.floor(value + TOLERANCE); - int intFloor = (int) floor; - return Math.max(intFloor, 0); - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java deleted file mode 100644 index 99c501ce..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorAdapterImpl.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import com.google.ortools.linearsolver.MPConstraint; -import com.google.ortools.linearsolver.MPObjective; -import com.google.ortools.linearsolver.MPSolver; -import com.google.ortools.linearsolver.MPVariable; -import org.eclipse.collections.api.factory.primitive.IntObjectMaps; -import org.eclipse.collections.api.factory.primitive.IntSets; -import org.eclipse.collections.api.map.primitive.MutableIntObjectMap; -import org.eclipse.collections.api.set.primitive.MutableIntSet; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.reasoning.refinement.RefinementResult; -import tools.refinery.store.reasoning.scope.ScopePropagatorAdapter; -import tools.refinery.store.reasoning.scope.ScopePropagatorStoreAdapter; -import tools.refinery.store.representation.cardinality.*; -import tools.refinery.store.tuple.Tuple; - -class ScopePropagatorAdapterImpl implements ScopePropagatorAdapter { - private final Model model; - private final ScopePropagatorStoreAdapterImpl storeAdapter; - private final ModelQueryAdapter queryEngine; - private final Interpretation countInterpretation; - private final MPSolver solver; - private final MPObjective objective; - private final MutableIntObjectMap variables = IntObjectMaps.mutable.empty(); - private final MutableIntSet activeVariables = IntSets.mutable.empty(); - private final TypeScopePropagator[] propagators; - private boolean changed = true; - - public ScopePropagatorAdapterImpl(Model model, ScopePropagatorStoreAdapterImpl storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - queryEngine = model.getAdapter(ModelQueryAdapter.class); - countInterpretation = model.getInterpretation(storeAdapter.getCountSymbol()); - solver = MPSolver.createSolver("GLOP"); - objective = solver.objective(); - initializeVariables(); - countInterpretation.addListener(this::countChanged, true); - var propagatorFactories = storeAdapter.getPropagatorFactories(); - propagators = new TypeScopePropagator[propagatorFactories.size()]; - for (int i = 0; i < propagators.length; i++) { - propagators[i] = propagatorFactories.get(i).createPropagator(this); - } - } - - @Override - public Model getModel() { - return model; - } - - @Override - public ScopePropagatorStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - private void initializeVariables() { - var cursor = countInterpretation.getAll(); - while (cursor.move()) { - var interval = cursor.getValue(); - if (!interval.equals(CardinalityIntervals.ONE)) { - int nodeId = cursor.getKey().get(0); - createVariable(nodeId, interval); - activeVariables.add(nodeId); - } - } - } - - private MPVariable createVariable(int nodeId, CardinalityInterval interval) { - double lowerBound = interval.lowerBound(); - double upperBound = getUpperBound(interval); - var variable = solver.makeNumVar(lowerBound, upperBound, "x" + nodeId); - variables.put(nodeId, variable); - return variable; - } - - private void countChanged(Tuple key, CardinalityInterval fromValue, CardinalityInterval toValue, - boolean ignoredRestoring) { - int nodeId = key.get(0); - if ((toValue == null || toValue.equals(CardinalityIntervals.ONE))) { - if (fromValue != null && !fromValue.equals(CardinalityIntervals.ONE)) { - removeActiveVariable(toValue, nodeId); - } - return; - } - if (fromValue == null || fromValue.equals(CardinalityIntervals.ONE)) { - activeVariables.add(nodeId); - } - var variable = variables.get(nodeId); - if (variable == null) { - createVariable(nodeId, toValue); - markAsChanged(); - return; - } - double lowerBound = toValue.lowerBound(); - double upperBound = getUpperBound(toValue); - if (variable.lb() != lowerBound) { - variable.setLb(lowerBound); - markAsChanged(); - } - if (variable.ub() != upperBound) { - variable.setUb(upperBound); - markAsChanged(); - } - } - - private void removeActiveVariable(CardinalityInterval toValue, int nodeId) { - var variable = variables.get(nodeId); - if (variable == null || !activeVariables.remove(nodeId)) { - throw new AssertionError("Variable not active: " + nodeId); - } - if (toValue == null) { - variable.setBounds(0, 0); - } else { - // Until queries are flushed and the constraints can be properly updated, - // the variable corresponding to the (previous) multi-object has to stand in for a single object. - variable.setBounds(1, 1); - } - markAsChanged(); - } - - MPConstraint makeConstraint() { - return solver.makeConstraint(); - } - - MPVariable getVariable(int nodeId) { - var variable = variables.get(nodeId); - if (variable != null) { - return variable; - } - var interval = countInterpretation.get(Tuple.of(nodeId)); - if (interval == null || interval.equals(CardinalityIntervals.ONE)) { - interval = CardinalityIntervals.NONE; - } else { - activeVariables.add(nodeId); - markAsChanged(); - } - return createVariable(nodeId, interval); - } - - void markAsChanged() { - changed = true; - } - - @Override - public RefinementResult propagate() { - var result = RefinementResult.UNCHANGED; - RefinementResult currentRoundResult; - do { - currentRoundResult = propagateOne(); - result = result.andThen(currentRoundResult); - if (result.isRejected()) { - return result; - } - } while (currentRoundResult != RefinementResult.UNCHANGED); - return result; - } - - private RefinementResult propagateOne() { - queryEngine.flushChanges(); - if (!changed) { - return RefinementResult.UNCHANGED; - } - changed = false; - for (var propagator : propagators) { - propagator.updateBounds(); - } - var result = RefinementResult.UNCHANGED; - if (activeVariables.isEmpty()) { - return checkEmptiness(); - } - var iterator = activeVariables.intIterator(); - while (iterator.hasNext()) { - int nodeId = iterator.next(); - var variable = variables.get(nodeId); - if (variable == null) { - throw new AssertionError("Missing active variable: " + nodeId); - } - result = result.andThen(propagateNode(nodeId, variable)); - if (result.isRejected()) { - return result; - } - } - return result; - } - - private RefinementResult checkEmptiness() { - var emptinessCheckingResult = solver.solve(); - return switch (emptinessCheckingResult) { - case OPTIMAL, UNBOUNDED -> RefinementResult.UNCHANGED; - case INFEASIBLE -> RefinementResult.REJECTED; - default -> throw new IllegalStateException("Failed to check for consistency: " + emptinessCheckingResult); - }; - } - - private RefinementResult propagateNode(int nodeId, MPVariable variable) { - objective.setCoefficient(variable, 1); - try { - objective.setMinimization(); - var minimizationResult = solver.solve(); - int lowerBound; - switch (minimizationResult) { - case OPTIMAL -> lowerBound = RoundingUtil.roundUp(objective.value()); - case UNBOUNDED -> lowerBound = 0; - case INFEASIBLE -> { - return RefinementResult.REJECTED; - } - default -> throw new IllegalStateException("Failed to solve for minimum of %s: %s" - .formatted(variable, minimizationResult)); - } - - objective.setMaximization(); - var maximizationResult = solver.solve(); - UpperCardinality upperBound; - switch (maximizationResult) { - case OPTIMAL -> upperBound = UpperCardinalities.atMost(RoundingUtil.roundDown(objective.value())); - // Problem was feasible when minimizing, the only possible source of {@code UNBOUNDED_OR_INFEASIBLE} is - // an unbounded maximization problem. See https://github.com/google/or-tools/issues/3319 - case UNBOUNDED, INFEASIBLE -> upperBound = UpperCardinalities.UNBOUNDED; - default -> throw new IllegalStateException("Failed to solve for maximum of %s: %s" - .formatted(variable, minimizationResult)); - } - - var newInterval = CardinalityIntervals.between(lowerBound, upperBound); - var oldInterval = countInterpretation.put(Tuple.of(nodeId), newInterval); - if (newInterval.lowerBound() < oldInterval.lowerBound() || - newInterval.upperBound().compareTo(oldInterval.upperBound()) > 0) { - throw new IllegalArgumentException("Failed to refine multiplicity %s of node %d to %s" - .formatted(oldInterval, nodeId, newInterval)); - } - return newInterval.equals(oldInterval) ? RefinementResult.UNCHANGED : RefinementResult.REFINED; - } finally { - objective.setCoefficient(variable, 0); - } - } - - private static double getUpperBound(CardinalityInterval interval) { - var upperBound = interval.upperBound(); - if (upperBound instanceof FiniteUpperCardinality finiteUpperCardinality) { - return finiteUpperCardinality.finiteUpperBound(); - } else if (upperBound instanceof UnboundedUpperCardinality) { - return Double.POSITIVE_INFINITY; - } else { - throw new IllegalArgumentException("Unknown upper bound: " + upperBound); - } - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java deleted file mode 100644 index 531a7440..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorBuilderImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import com.google.ortools.Loader; -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.reasoning.scope.ScopePropagatorBuilder; -import tools.refinery.store.reasoning.scope.ScopePropagatorStoreAdapter; -import tools.refinery.store.reasoning.translator.TranslationException; -import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.representation.cardinality.CardinalityInterval; -import tools.refinery.store.representation.cardinality.FiniteUpperCardinality; - -import java.util.*; - -public class ScopePropagatorBuilderImpl extends AbstractModelAdapterBuilder - implements ScopePropagatorBuilder { - private Symbol countSymbol = MultiObjectTranslator.COUNT_STORAGE; - private final Map scopes = new LinkedHashMap<>(); - private List typeScopePropagatorFactories; - - @Override - public ScopePropagatorBuilder countSymbol(Symbol countSymbol) { - if (countSymbol.arity() != 1) { - throw new IllegalArgumentException("Count symbol must have arty 1, got %s with arity %d instead" - .formatted(countSymbol, countSymbol.arity())); - } - if (!countSymbol.valueType().equals(CardinalityInterval.class)) { - throw new IllegalArgumentException("Count symbol must have CardinalityInterval values"); - } - if (countSymbol.defaultValue() != null) { - throw new IllegalArgumentException("Count symbol must default value null"); - } - this.countSymbol = countSymbol; - return this; - } - - @Override - public ScopePropagatorBuilder scope(PartialRelation type, CardinalityInterval interval) { - if (type.arity() != 1) { - throw new TranslationException(type, "Only types with arity 1 may have scopes, got %s with arity %d" - .formatted(type, type.arity())); - } - var newValue = scopes.compute(type, (ignoredKey, oldValue) -> - oldValue == null ? interval : oldValue.meet(interval)); - if (newValue.isEmpty()) { - throw new TranslationException(type, "Unsatisfiable scope for type %s".formatted(type)); - } - return this; - } - - @Override - protected void doConfigure(ModelStoreBuilder storeBuilder) { - typeScopePropagatorFactories = new ArrayList<>(scopes.size()); - for (var entry : scopes.entrySet()) { - var type = entry.getKey(); - var bounds = entry.getValue(); - if (bounds.lowerBound() > 0) { - var lowerFactory = new LowerTypeScopePropagator.Factory(type, bounds.lowerBound()); - typeScopePropagatorFactories.add(lowerFactory); - } - if (bounds.upperBound() instanceof FiniteUpperCardinality finiteUpperCardinality) { - var upperFactory = new UpperTypeScopePropagator.Factory(type, - finiteUpperCardinality.finiteUpperBound()); - typeScopePropagatorFactories.add(upperFactory); - } - } - for (var factory : typeScopePropagatorFactories) { - factory.configure(storeBuilder); - } - } - - @Override - protected ScopePropagatorStoreAdapter doBuild(ModelStore store) { - Loader.loadNativeLibraries(); - return new ScopePropagatorStoreAdapterImpl(store, countSymbol, Collections.unmodifiableMap(scopes), - Collections.unmodifiableList(typeScopePropagatorFactories)); - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorStoreAdapterImpl.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorStoreAdapterImpl.java deleted file mode 100644 index 282ffe6f..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/ScopePropagatorStoreAdapterImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.reasoning.scope.ScopePropagatorStoreAdapter; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.representation.cardinality.CardinalityInterval; - -import java.util.List; -import java.util.Map; - -// Not a record, because we want to control getter visibility. -@SuppressWarnings("ClassCanBeRecord") -class ScopePropagatorStoreAdapterImpl implements ScopePropagatorStoreAdapter { - private final ModelStore store; - private final Symbol countSymbol; - private final Map scopes; - private final List propagatorFactories; - - public ScopePropagatorStoreAdapterImpl( - ModelStore store, Symbol countSymbol, - Map scopes, List propagatorFactories) { - this.store = store; - this.countSymbol = countSymbol; - this.scopes = scopes; - this.propagatorFactories = propagatorFactories; - } - - @Override - public ModelStore getStore() { - return store; - } - - Symbol getCountSymbol() { - return countSymbol; - } - - @Override - public Map getScopes() { - return scopes; - } - - public List getPropagatorFactories() { - return propagatorFactories; - } - - @Override - public ModelAdapter createModelAdapter(Model model) { - return new ScopePropagatorAdapterImpl(model, this); - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java deleted file mode 100644 index cfb95829..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/TypeScopePropagator.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import com.google.ortools.linearsolver.MPConstraint; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.ModelQueryBuilder; -import tools.refinery.store.query.dnf.AnyQuery; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.Collection; - -abstract class TypeScopePropagator { - private final ScopePropagatorAdapterImpl adapter; - private final ResultSet allNodes; - private final ResultSet multiNodes; - protected final MPConstraint constraint; - - protected TypeScopePropagator(ScopePropagatorAdapterImpl adapter, RelationalQuery allQuery, - RelationalQuery multiQuery) { - this.adapter = adapter; - var queryEngine = adapter.getModel().getAdapter(ModelQueryAdapter.class); - allNodes = queryEngine.getResultSet(allQuery); - multiNodes = queryEngine.getResultSet(multiQuery); - constraint = adapter.makeConstraint(); - constraint.setBounds(0, Double.POSITIVE_INFINITY); - var cursor = multiNodes.getAll(); - while (cursor.move()) { - var variable = adapter.getVariable(cursor.getKey().get(0)); - constraint.setCoefficient(variable, 1); - } - allNodes.addListener(this::allChanged); - multiNodes.addListener(this::multiChanged); - } - - public abstract void updateBounds(); - - protected int getSingleCount() { - return allNodes.size() - multiNodes.size(); - } - - private void allChanged(Tuple ignoredKey, Boolean ignoredOldValue, Boolean ignoredNewValue) { - adapter.markAsChanged(); - } - - private void multiChanged(Tuple key, Boolean ignoredOldValue, Boolean newValue) { - var variable = adapter.getVariable(key.get(0)); - constraint.setCoefficient(variable, Boolean.TRUE.equals(newValue) ? 1 : 0); - adapter.markAsChanged(); - } - - public abstract static class Factory { - public abstract TypeScopePropagator createPropagator(ScopePropagatorAdapterImpl adapter); - - protected abstract Collection getQueries(); - - public void configure(ModelStoreBuilder storeBuilder) { - storeBuilder.getAdapter(ModelQueryBuilder.class).queries(getQueries()); - } - } -} diff --git a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java b/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java deleted file mode 100644 index a0be0fb4..00000000 --- a/subprojects/store-reasoning-scope/src/main/java/tools/refinery/store/reasoning/scope/internal/UpperTypeScopePropagator.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import tools.refinery.store.query.dnf.AnyQuery; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.reasoning.representation.PartialRelation; - -import java.util.Collection; -import java.util.List; - -import static tools.refinery.store.reasoning.literal.PartialLiterals.must; -import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; - -class UpperTypeScopePropagator extends TypeScopePropagator { - private final int upperBound; - - private UpperTypeScopePropagator(ScopePropagatorAdapterImpl adapter, int upperBound, RelationalQuery allQuery, - RelationalQuery multiQuery) { - super(adapter, allQuery, multiQuery); - this.upperBound = upperBound; - } - - @Override - public void updateBounds() { - constraint.setUb(upperBound - getSingleCount()); - } - - public static class Factory extends TypeScopePropagator.Factory { - private final int upperBound; - private final RelationalQuery allMust; - private final RelationalQuery multiMust; - - public Factory(PartialRelation type, int upperBound) { - this.upperBound = upperBound; - allMust = Query.of(type.name() + "#must", (builder, instance) -> builder.clause( - must(type.call(instance)) - )); - multiMust = Query.of(type.name() + "#multiMust", (builder, instance) -> builder.clause( - must(type.call(instance)), - MULTI_VIEW.call(instance) - )); - } - - @Override - public TypeScopePropagator createPropagator(ScopePropagatorAdapterImpl adapter) { - return new UpperTypeScopePropagator(adapter, upperBound, allMust, multiMust); - } - - @Override - protected Collection getQueries() { - return List.of(allMust, multiMust); - } - } -} diff --git a/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/MultiObjectTest.java b/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/MultiObjectTest.java index 42ce2f56..5fc70ae1 100644 --- a/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/MultiObjectTest.java +++ b/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/MultiObjectTest.java @@ -7,13 +7,14 @@ package tools.refinery.store.reasoning.scope; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.propagation.PropagationAdapter; +import tools.refinery.store.dse.propagation.PropagationResult; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.ReasoningStoreAdapter; -import tools.refinery.store.reasoning.refinement.RefinementResult; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.reasoning.translator.PartialRelationTranslator; @@ -38,11 +39,12 @@ class MultiObjectTest { void beforeEach() { store = ModelStore.builder() .with(ViatraModelQueryAdapter.builder()) + .with(PropagationAdapter.builder()) .with(ReasoningAdapter.builder()) .with(new MultiObjectTranslator()) .with(PartialRelationTranslator.of(person) .symbol(Symbol.of("Person", 1, TruthValue.class, TruthValue.FALSE))) - .with(ScopePropagatorAdapter.builder() + .with(new ScopePropagator() .scope(person, CardinalityIntervals.between(5, 15))) .build(); model = null; @@ -57,7 +59,7 @@ class MultiObjectTest { .put(Tuple.of(0), CardinalityIntervals.SET)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(0)), is(CardinalityIntervals.between(2, 12))); } @@ -69,7 +71,7 @@ class MultiObjectTest { .put(Tuple.of(0), CardinalityIntervals.between(5, 20))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(0)), is(CardinalityIntervals.between(5, 12))); } @@ -81,7 +83,7 @@ class MultiObjectTest { .put(Tuple.of(0), CardinalityIntervals.SET)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -90,7 +92,7 @@ class MultiObjectTest { .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder.reducedValue(CardinalityIntervals.ONE)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.UNCHANGED)); + assertThat(propagate(), is(PropagationResult.UNCHANGED)); } @Test @@ -99,7 +101,7 @@ class MultiObjectTest { .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder.reducedValue(CardinalityIntervals.ONE)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -110,7 +112,7 @@ class MultiObjectTest { .put(Tuple.of(0), CardinalityIntervals.atLeast(20))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -121,7 +123,7 @@ class MultiObjectTest { .put(Tuple.of(0), CardinalityIntervals.atMost(1))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -133,7 +135,7 @@ class MultiObjectTest { .put(Tuple.of(1), CardinalityIntervals.SET)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(0)), is(CardinalityIntervals.atMost(12))); assertThat(countStorage.get(Tuple.of(1)), is(CardinalityIntervals.atMost(12))); } @@ -147,7 +149,7 @@ class MultiObjectTest { .put(Tuple.of(1), CardinalityIntervals.atMost(11))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(0)), is(CardinalityIntervals.between(7, 12))); assertThat(countStorage.get(Tuple.of(1)), is(CardinalityIntervals.atMost(5))); } @@ -161,7 +163,7 @@ class MultiObjectTest { .put(Tuple.of(1), CardinalityIntervals.exactly(11))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -173,7 +175,7 @@ class MultiObjectTest { .put(Tuple.of(1), CardinalityIntervals.atMost(2))) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REJECTED)); + assertThat(propagate(), is(PropagationResult.REJECTED)); } @Test @@ -185,14 +187,14 @@ class MultiObjectTest { .put(Tuple.of(1), CardinalityIntervals.SET)) .seed(person, builder -> builder.reducedValue(TruthValue.TRUE)) .build()); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(0)), is(CardinalityIntervals.LONE)); assertThat(countStorage.get(Tuple.of(1)), is(CardinalityIntervals.between(1, 12))); countStorage.put(Tuple.of(0), CardinalityIntervals.ONE); - assertThat(propagate(), is(RefinementResult.REFINED)); + assertThat(propagate(), is(PropagationResult.PROPAGATED)); assertThat(countStorage.get(Tuple.of(1)), is(CardinalityIntervals.between(1, 11))); countStorage.put(Tuple.of(1), CardinalityIntervals.ONE); - assertThat(propagate(), is(RefinementResult.UNCHANGED)); + assertThat(propagate(), is(PropagationResult.UNCHANGED)); } private void createModel(ModelSeed modelSeed) { @@ -200,7 +202,7 @@ class MultiObjectTest { countStorage = model.getInterpretation(MultiObjectTranslator.COUNT_STORAGE); } - private RefinementResult propagate() { - return model.getAdapter(ScopePropagatorAdapter.class).propagate(); + private PropagationResult propagate() { + return model.getAdapter(PropagationAdapter.class).propagate(); } } diff --git a/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/RoundingUtilTest.java b/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/RoundingUtilTest.java new file mode 100644 index 00000000..e697298e --- /dev/null +++ b/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/RoundingUtilTest.java @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.scope; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class RoundingUtilTest { + @ParameterizedTest + @MethodSource + void roundUpTest(double value, int expected) { + int actual = RoundingUtil.roundUp(value); + assertThat(actual, is(expected)); + } + + static Stream roundUpTest() { + return Stream.of( + Arguments.of(0.0, 0), + Arguments.of(-0.0, 0), + Arguments.of(-0.9, 0), + Arguments.of(-2, 0), + Arguments.of(0.009, 0), + Arguments.of(0.011, 1), + Arguments.of(0.1, 1), + Arguments.of(0.991, 1), + Arguments.of(1, 1), + Arguments.of(1.009, 1), + Arguments.of(1.011, 2), + Arguments.of(1.5, 2), + Arguments.of(2, 2), + Arguments.of(100.5, 101) + ); + } + + @ParameterizedTest + @MethodSource + void roundDownTest(double value, int expected) { + int actual = RoundingUtil.roundDown(value); + assertThat(actual, is(expected)); + } + + static Stream roundDownTest() { + return Stream.of( + Arguments.of(0.0, 0), + Arguments.of(-0.0, 0), + Arguments.of(-0.9, 0), + Arguments.of(-2, 0), + Arguments.of(0.989, 0), + Arguments.of(0.991, 1), + Arguments.of(1, 1), + Arguments.of(1.5, 1), + Arguments.of(1.009, 1), + Arguments.of(1.989, 1), + Arguments.of(1.991, 2), + Arguments.of(2, 2), + Arguments.of(2.009, 2), + Arguments.of(100.5, 100) + ); + } +} diff --git a/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/internal/RoundingUtilTest.java b/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/internal/RoundingUtilTest.java deleted file mode 100644 index 9daed660..00000000 --- a/subprojects/store-reasoning-scope/src/test/java/tools/refinery/store/reasoning/scope/internal/RoundingUtilTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.scope.internal; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -class RoundingUtilTest { - @ParameterizedTest - @MethodSource - void roundUpTest(double value, int expected) { - int actual = RoundingUtil.roundUp(value); - assertThat(actual, is(expected)); - } - - static Stream roundUpTest() { - return Stream.of( - Arguments.of(0.0, 0), - Arguments.of(-0.0, 0), - Arguments.of(-0.9, 0), - Arguments.of(-2, 0), - Arguments.of(0.009, 0), - Arguments.of(0.011, 1), - Arguments.of(0.1, 1), - Arguments.of(0.991, 1), - Arguments.of(1, 1), - Arguments.of(1.009, 1), - Arguments.of(1.011, 2), - Arguments.of(1.5, 2), - Arguments.of(2, 2), - Arguments.of(100.5, 101) - ); - } - - @ParameterizedTest - @MethodSource - void roundDownTest(double value, int expected) { - int actual = RoundingUtil.roundDown(value); - assertThat(actual, is(expected)); - } - - static Stream roundDownTest() { - return Stream.of( - Arguments.of(0.0, 0), - Arguments.of(-0.0, 0), - Arguments.of(-0.9, 0), - Arguments.of(-2, 0), - Arguments.of(0.989, 0), - Arguments.of(0.991, 1), - Arguments.of(1, 1), - Arguments.of(1.5, 1), - Arguments.of(1.009, 1), - Arguments.of(1.989, 1), - Arguments.of(1.991, 2), - Arguments.of(2, 2), - Arguments.of(2.009, 2), - Arguments.of(100.5, 100) - ); - } -} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/CleanupActionLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/CleanupActionLiteral.java new file mode 100644 index 00000000..62c35cee --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/CleanupActionLiteral.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.actions; + +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class CleanupActionLiteral extends AbstractActionLiteral { + private final NodeVariable node; + + public CleanupActionLiteral(NodeVariable node) { + this.node = node; + } + + public NodeVariable getNode() { + return node; + } + + @Override + public List getInputVariables() { + return List.of(node); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); + return tuple -> reasoningAdapter.cleanup(tuple.get(0)) ? Tuple.of() : null; + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java index 990d11e5..f36fde44 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/actions/PartialActionLiterals.java @@ -35,4 +35,8 @@ public final class PartialActionLiterals { public static FocusActionLiteral focus(NodeVariable parent, NodeVariable child) { return new FocusActionLiteral(parent, child); } + + public static CleanupActionLiteral cleanup(NodeVariable node) { + return new CleanupActionLiteral(node); + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java index d2cd2eb0..722458c8 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java @@ -18,18 +18,19 @@ import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.reasoning.ReasoningBuilder; import tools.refinery.store.reasoning.interpretation.PartialInterpretation; -import tools.refinery.store.reasoning.refinement.DefaultStorageRefiner; -import tools.refinery.store.reasoning.translator.AnyPartialSymbolTranslator; -import tools.refinery.store.reasoning.translator.PartialRelationTranslator; import tools.refinery.store.reasoning.lifting.DnfLifter; import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.literal.Modality; +import tools.refinery.store.reasoning.refinement.DefaultStorageRefiner; import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.refinement.PartialModelInitializer; import tools.refinery.store.reasoning.refinement.StorageRefiner; import tools.refinery.store.reasoning.representation.AnyPartialSymbol; +import tools.refinery.store.reasoning.translator.AnyPartialSymbolTranslator; +import tools.refinery.store.reasoning.translator.PartialRelationTranslator; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderBuilder; import java.util.*; @@ -109,6 +110,8 @@ public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder stateCoderBuilder.exclude(ReasoningAdapterImpl.NODE_COUNT_SYMBOL)); for (var translator : translators.values()) { translator.configure(storeBuilder); if (translator instanceof PartialRelationTranslator relationConfiguration) { diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java index 8eb5a034..9ef6fb16 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.reasoning.internal; +import tools.refinery.store.dse.propagation.PropagationAdapter; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; @@ -103,6 +104,11 @@ class ReasoningStoreAdapterImpl implements ReasoningStoreAdapter { for (var initializer : initializers) { initializer.initialize(model, modelSeed); } + model.tryGetAdapter(PropagationAdapter.class).ifPresent(propagationAdapter -> { + if (propagationAdapter.propagate().isRejected()) { + throw new IllegalArgumentException("Inconsistent initial mode: propagation failed"); + } + }); model.getAdapter(ModelQueryAdapter.class).flushChanges(); return model; } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/RefinementResult.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/RefinementResult.java deleted file mode 100644 index 1bc537d1..00000000 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/RefinementResult.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.refinement; - -public enum RefinementResult { - UNCHANGED, - REFINED, - REJECTED; - - public RefinementResult andThen(RefinementResult next) { - return switch (this) { - case UNCHANGED -> next; - case REFINED -> next == REJECTED ? REJECTED : REFINED; - case REJECTED -> REJECTED; - }; - } - - public boolean isRejected() { - return this == REJECTED; - } -} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java index 5c3298ac..61037be3 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java @@ -243,7 +243,7 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration { MultiObjectTranslator.MULTI_VIEW.call(multi), not(may(CONTAINED_SYMBOL.call(multi))) ) - .clause((container) -> List.of( + .clause(container -> List.of( MultiObjectTranslator.MULTI_VIEW.call(multi), must(CONTAINS_SYMBOL.call(container, multi)), not(MultiObjectTranslator.MULTI_VIEW.call(container)) diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/InferredContainment.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/InferredContainment.java index 90802864..8df23d9a 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/InferredContainment.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/InferredContainment.java @@ -8,18 +8,23 @@ package tools.refinery.store.reasoning.translator.containment; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.representation.TruthValue; +import java.util.Objects; import java.util.Set; -record InferredContainment(TruthValue contains, Set mustLinks, - Set forbiddenLinks) { +final class InferredContainment { public static final InferredContainment UNKNOWN = new InferredContainment( TruthValue.UNKNOWN, Set.of(), Set.of()); + private final TruthValue contains; + private final Set mustLinks; + private final Set forbiddenLinks; + private final int hashCode; public InferredContainment(TruthValue contains, Set mustLinks, Set forbiddenLinks) { this.contains = adjustContains(contains, mustLinks, forbiddenLinks); this.mustLinks = mustLinks; this.forbiddenLinks = forbiddenLinks; + hashCode = Objects.hash(contains, mustLinks, forbiddenLinks); } private static TruthValue adjustContains(TruthValue contains, Set mustLinks, @@ -34,4 +39,39 @@ record InferredContainment(TruthValue contains, Set mustLinks, } return result; } + + public TruthValue contains() { + return contains; + } + + public Set mustLinks() { + return mustLinks; + } + + public Set forbiddenLinks() { + return forbiddenLinks; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (InferredContainment) obj; + return Objects.equals(this.contains, that.contains) && + Objects.equals(this.mustLinks, that.mustLinks) && + Objects.equals(this.forbiddenLinks, that.forbiddenLinks); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return "InferredContainment[" + + "contains=" + contains + ", " + + "mustLinks=" + mustLinks + ", " + + "forbiddenLinks=" + forbiddenLinks + ']'; + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java index 05704096..bad96a66 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java @@ -5,17 +5,21 @@ */ package tools.refinery.store.reasoning.translator.multiobject; +import tools.refinery.store.dse.propagation.PropagationBuilder; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.dse.transition.objectives.Criteria; import tools.refinery.store.dse.transition.objectives.Objectives; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.literal.Literals; import tools.refinery.store.query.term.Variable; import tools.refinery.store.query.term.int_.IntTerms; import tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.ReasoningBuilder; +import tools.refinery.store.reasoning.actions.PartialActionLiterals; import tools.refinery.store.reasoning.representation.PartialFunction; import tools.refinery.store.reasoning.translator.PartialRelationTranslator; import tools.refinery.store.reasoning.translator.RoundingMode; @@ -66,6 +70,11 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { LOWER_CARDINALITY_VIEW.call(p1, lower), check(greaterEq(lower, constant(1))) )))) + .candidate(Query.of("exists#candidate", (builder, p1) -> builder + .clause( + LOWER_CARDINALITY_VIEW.call(p1, Variable.of(Integer.class)), + Literals.not(MULTI_VIEW.call(p1)) + ))) .roundingMode(RoundingMode.PREFER_FALSE) .refiner(ExistsRefiner.of(COUNT_STORAGE)) .exclude(null) @@ -82,5 +91,17 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE)); reasoningBuilder.storageRefiner(COUNT_STORAGE, MultiObjectStorageRefiner::new); + + storeBuilder.tryGetAdapter(PropagationBuilder.class) + .ifPresent(propagationBuilder -> propagationBuilder.rule( + Rule.of("exists#cleanup", (builder, node) -> builder + .clause(UpperCardinality.class, upper -> List.of( + UPPER_CARDINALITY_VIEW.call(node, upper), + check(UpperCardinalityTerms.less(upper, + UpperCardinalityTerms.constant(UpperCardinalities.ONE))) + )) + .action( + PartialActionLiterals.cleanup(node) + )))); } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/InferredType.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/InferredType.java index 1ae94ae1..9a0c2b0f 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/InferredType.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/InferredType.java @@ -8,17 +8,22 @@ package tools.refinery.store.reasoning.translator.typehierarchy; import tools.refinery.store.reasoning.representation.PartialRelation; import java.util.Collections; +import java.util.Objects; import java.util.Set; -record InferredType(Set mustTypes, Set mayConcreteTypes, - PartialRelation candidateType) { +public final class InferredType { public static final InferredType UNTYPED = new InferredType(Set.of(), Set.of(), null); + private final Set mustTypes; + private final Set mayConcreteTypes; + private final PartialRelation candidateType; + private final int hashCode; public InferredType(Set mustTypes, Set mayConcreteTypes, PartialRelation candidateType) { this.mustTypes = Collections.unmodifiableSet(mustTypes); this.mayConcreteTypes = Collections.unmodifiableSet(mayConcreteTypes); this.candidateType = candidateType; + hashCode = Objects.hash(mustTypes, mayConcreteTypes, candidateType); } public boolean isConsistent() { @@ -32,4 +37,40 @@ record InferredType(Set mustTypes, Set mayConc public boolean isMayConcrete(PartialRelation partialRelation) { return mayConcreteTypes.contains(partialRelation); } + + + public Set mustTypes() { + return mustTypes; + } + + public Set mayConcreteTypes() { + return mayConcreteTypes; + } + + public PartialRelation candidateType() { + return candidateType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InferredType that = (InferredType) o; + return Objects.equals(mustTypes, that.mustTypes) && + Objects.equals(mayConcreteTypes, that.mayConcreteTypes) && + Objects.equals(candidateType, that.candidateType); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return "InferredType[" + + "mustTypes=" + mustTypes + ", " + + "mayConcreteTypes=" + mayConcreteTypes + ", " + + "candidateType=" + candidateType + ']'; + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java index dc8a1d36..37ea1448 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslator.java @@ -26,7 +26,8 @@ import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMu import static tools.refinery.store.reasoning.literal.PartialLiterals.may; public class TypeHierarchyTranslator implements ModelStoreConfiguration { - private final Symbol typeSymbol = Symbol.of("TYPE", 1, InferredType.class, InferredType.UNTYPED); + public static final Symbol TYPE_SYMBOL = Symbol.of("TYPE", 1, InferredType.class, + InferredType.UNTYPED); private final TypeHierarchy typeHierarchy; public TypeHierarchyTranslator(TypeHierarchy typeHierarchy) { @@ -39,7 +40,7 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { return; } - storeBuilder.symbol(typeSymbol); + storeBuilder.symbol(TYPE_SYMBOL); for (var entry : typeHierarchy.getPreservedTypes().entrySet()) { storeBuilder.with(createPreservedTypeTranslator(entry.getKey(), entry.getValue())); @@ -50,13 +51,13 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { } var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); - reasoningBuilder.initializer(new TypeHierarchyInitializer(typeHierarchy, typeSymbol)); + reasoningBuilder.initializer(new TypeHierarchyInitializer(typeHierarchy, TYPE_SYMBOL)); } private ModelStoreConfiguration createPreservedTypeTranslator(PartialRelation type, TypeAnalysisResult result) { var may = Query.of(type.name() + "#partial#may", (builder, p1) -> { if (!result.isAbstractType()) { - builder.clause(new MayTypeView(typeSymbol, type).call(p1)); + builder.clause(new MayTypeView(TYPE_SYMBOL, type).call(p1)); } for (var subtype : result.getDirectSubtypes()) { builder.clause(may(subtype.call(p1))); @@ -64,12 +65,12 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { }); var must = Query.of(type.name() + "#partial#must", (builder, p1) -> builder.clause( - new MustTypeView(typeSymbol, type).call(p1) + new MustTypeView(TYPE_SYMBOL, type).call(p1) )); var candidate = Query.of(type.name() + "#candidate", (builder, p1) -> { if (!result.isAbstractType()) { - builder.clause(new CandidateTypeView(typeSymbol, type).call(p1)); + builder.clause(new CandidateTypeView(TYPE_SYMBOL, type).call(p1)); } for (var subtype : result.getDirectSubtypes()) { builder.clause(PartialLiterals.candidateMust(subtype.call(p1))); @@ -80,7 +81,7 @@ public class TypeHierarchyTranslator implements ModelStoreConfiguration { .may(may) .must(must) .candidate(candidate) - .refiner(InferredTypeRefiner.of(typeSymbol, result)); + .refiner(InferredTypeRefiner.of(TYPE_SYMBOL, result)); if (!result.isAbstractType()) { var decision = Rule.of(type.name(), (builder, instance) -> builder diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java index 088e3925..b63a8637 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java @@ -67,4 +67,17 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi } throw new IllegalArgumentException("Unknown UpperCardinality: " + other); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FiniteUpperCardinality that = (FiniteUpperCardinality) o; + return finiteUpperBound == that.finiteUpperBound; + } + + @Override + public int hashCode() { + return finiteUpperBound; + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java index bfaeea25..6bd66df7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.representation.cardinality; +import java.util.Objects; import java.util.function.BinaryOperator; import java.util.function.IntBinaryOperator; @@ -89,4 +90,17 @@ public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upper } return "[%d..%s]".formatted(lowerBound, upperBound); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NonEmptyCardinalityInterval that = (NonEmptyCardinalityInterval) o; + return lowerBound == that.lowerBound && Objects.equals(upperBound, that.upperBound); + } + + @Override + public int hashCode() { + return lowerBound * 31 + upperBound.hashCode(); + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java index e3a334cd..03c701ae 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java @@ -53,4 +53,14 @@ public final class UnboundedUpperCardinality implements UpperCardinality { public String toString() { return "*"; } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj != null && getClass() == obj.getClass()); + } + + @Override + public int hashCode() { + return -1; + } } -- cgit v1.2.3-54-g00ecf 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 (limited to 'subprojects/store-dse') 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 From 13b464db253566290be6a1063ad8e296288d3339 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 14 Sep 2023 03:05:28 +0200 Subject: feat: specify random seed for generation --- subprojects/frontend/src/editor/AnimatedButton.tsx | 2 +- subprojects/frontend/src/editor/EditorStore.ts | 8 +++---- subprojects/frontend/src/editor/GenerateButton.tsx | 8 ++++++- .../frontend/src/editor/GeneratedModelStore.ts | 4 ++-- .../frontend/src/xtext/ModelGenerationService.ts | 26 +++++++++++++++++++--- subprojects/frontend/src/xtext/UpdateService.ts | 7 +++--- subprojects/frontend/src/xtext/XtextClient.ts | 5 +++-- .../frontend/src/xtext/XtextWebSocketClient.ts | 2 +- .../web/generator/ModelGenerationService.java | 4 ++-- .../web/generator/ModelGenerationWorker.java | 7 ++++-- .../server/ThreadPoolExecutorServiceProvider.java | 20 ++++++++++++++++- .../xtext/server/push/PushServiceDispatcher.java | 4 +++- .../store/dse/strategy/BestFirstStoreManager.java | 11 +++++++-- 13 files changed, 83 insertions(+), 25 deletions(-) (limited to 'subprojects/store-dse') diff --git a/subprojects/frontend/src/editor/AnimatedButton.tsx b/subprojects/frontend/src/editor/AnimatedButton.tsx index 24ec69be..606aabea 100644 --- a/subprojects/frontend/src/editor/AnimatedButton.tsx +++ b/subprojects/frontend/src/editor/AnimatedButton.tsx @@ -45,7 +45,7 @@ export default function AnimatedButton({ children, }: { 'aria-label'?: string; - onClick?: () => void; + onClick?: React.MouseEventHandler; color: 'error' | 'warning' | 'primary' | 'inherit'; disabled?: boolean; startIcon?: JSX.Element; diff --git a/subprojects/frontend/src/editor/EditorStore.ts b/subprojects/frontend/src/editor/EditorStore.ts index f9a9a7da..9508858d 100644 --- a/subprojects/frontend/src/editor/EditorStore.ts +++ b/subprojects/frontend/src/editor/EditorStore.ts @@ -313,14 +313,14 @@ export default class EditorStore { this.disposed = true; } - startModelGeneration(): void { + startModelGeneration(randomSeed?: number): void { this.client - ?.startModelGeneration() + ?.startModelGeneration(randomSeed) ?.catch((error) => log.error('Could not start model generation', error)); } - addGeneratedModel(uuid: string): void { - this.generatedModels.set(uuid, new GeneratedModelStore()); + addGeneratedModel(uuid: string, randomSeed: number): void { + this.generatedModels.set(uuid, new GeneratedModelStore(randomSeed)); this.selectGeneratedModel(uuid); } diff --git a/subprojects/frontend/src/editor/GenerateButton.tsx b/subprojects/frontend/src/editor/GenerateButton.tsx index b8dcd531..b6b1655a 100644 --- a/subprojects/frontend/src/editor/GenerateButton.tsx +++ b/subprojects/frontend/src/editor/GenerateButton.tsx @@ -98,7 +98,13 @@ const GenerateButton = observer(function GenerateButton({ disabled={!editorStore.opened} color={warningCount > 0 ? 'warning' : 'primary'} startIcon={} - onClick={() => editorStore.startModelGeneration()} + onClick={(event) => { + if (event.shiftKey) { + editorStore.startModelGeneration(1); + } else { + editorStore.startModelGeneration(); + } + }} > {summary === '' ? GENERATE_LABEL : `${GENERATE_LABEL} (${summary})`} diff --git a/subprojects/frontend/src/editor/GeneratedModelStore.ts b/subprojects/frontend/src/editor/GeneratedModelStore.ts index d0181eed..5088d603 100644 --- a/subprojects/frontend/src/editor/GeneratedModelStore.ts +++ b/subprojects/frontend/src/editor/GeneratedModelStore.ts @@ -18,9 +18,9 @@ export default class GeneratedModelStore { graph: GraphStore | undefined; - constructor() { + constructor(randomSeed: number) { const time = new Date().toLocaleTimeString(undefined, { hour12: false }); - this.title = `Generated at ${time}`; + this.title = `Generated at ${time} (${randomSeed})`; makeAutoObservable(this); } diff --git a/subprojects/frontend/src/xtext/ModelGenerationService.ts b/subprojects/frontend/src/xtext/ModelGenerationService.ts index 1e9f837a..29a70623 100644 --- a/subprojects/frontend/src/xtext/ModelGenerationService.ts +++ b/subprojects/frontend/src/xtext/ModelGenerationService.ts @@ -4,12 +4,18 @@ * SPDX-License-Identifier: EPL-2.0 */ +import type { Transaction } from '@codemirror/state'; + import type EditorStore from '../editor/EditorStore'; import type UpdateService from './UpdateService'; import { ModelGenerationResult } from './xtextServiceResults'; +const INITIAL_RANDOM_SEED = 1; + export default class ModelGenerationService { + private nextRandomSeed = INITIAL_RANDOM_SEED; + constructor( private readonly store: EditorStore, private readonly updateService: UpdateService, @@ -26,14 +32,24 @@ export default class ModelGenerationService { } } + onTransaction(transaction: Transaction): void { + if (transaction.docChanged) { + this.resetRandomSeed(); + } + } + onDisconnect(): void { this.store.modelGenerationCancelled(); + this.resetRandomSeed(); } - async start(): Promise { - const result = await this.updateService.startModelGeneration(); + async start(randomSeed?: number): Promise { + const randomSeedOrNext = randomSeed ?? this.nextRandomSeed; + this.nextRandomSeed = randomSeedOrNext + 1; + const result = + await this.updateService.startModelGeneration(randomSeedOrNext); if (!result.cancelled) { - this.store.addGeneratedModel(result.data.uuid); + this.store.addGeneratedModel(result.data.uuid, randomSeedOrNext); } } @@ -43,4 +59,8 @@ export default class ModelGenerationService { this.store.modelGenerationCancelled(); } } + + private resetRandomSeed() { + this.nextRandomSeed = INITIAL_RANDOM_SEED; + } } diff --git a/subprojects/frontend/src/xtext/UpdateService.ts b/subprojects/frontend/src/xtext/UpdateService.ts index d1246d5e..70e79764 100644 --- a/subprojects/frontend/src/xtext/UpdateService.ts +++ b/subprojects/frontend/src/xtext/UpdateService.ts @@ -343,9 +343,9 @@ export default class UpdateService { return { cancelled: false, data: parsedOccurrencesResult }; } - async startModelGeneration(): Promise< - CancellableResult - > { + async startModelGeneration( + randomSeed: number, + ): Promise> { try { await this.updateOrThrow(); } catch (error) { @@ -360,6 +360,7 @@ export default class UpdateService { serviceType: 'modelGeneration', requiredStateId: this.xtextStateId, start: true, + randomSeed, }); if (isConflictResult(data)) { return { cancelled: true }; diff --git a/subprojects/frontend/src/xtext/XtextClient.ts b/subprojects/frontend/src/xtext/XtextClient.ts index 4df4f57a..7486d737 100644 --- a/subprojects/frontend/src/xtext/XtextClient.ts +++ b/subprojects/frontend/src/xtext/XtextClient.ts @@ -99,6 +99,7 @@ export default class XtextClient { this.contentAssistService.onTransaction(transaction); this.updateService.onTransaction(transaction); this.occurrencesService.onTransaction(transaction); + this.modelGenerationService.onTransaction(transaction); } private onPush( @@ -150,8 +151,8 @@ export default class XtextClient { return this.contentAssistService.contentAssist(context); } - startModelGeneration(): Promise { - return this.modelGenerationService.start(); + startModelGeneration(randomSeed?: number): Promise { + return this.modelGenerationService.start(randomSeed); } cancelModelGeneration(): Promise { diff --git a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts index bb84223c..280ac875 100644 --- a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts +++ b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts @@ -204,7 +204,7 @@ export default class XtextWebSocketClient { get state() { this.stateAtom.reportObserved(); - return this.interpreter.state; + return this.interpreter.getSnapshot(); } get opening(): boolean { diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationService.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationService.java index 5a60007f..9f72e462 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationService.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationService.java @@ -34,13 +34,13 @@ public class ModelGenerationService { timeoutSec = SemanticsService.getTimeout("REFINERY_MODEL_GENERATION_TIMEOUT_SEC").orElse(600L); } - public ModelGenerationStartedResult generateModel(PushWebDocumentAccess document){ + public ModelGenerationStartedResult generateModel(PushWebDocumentAccess document, int randomSeed) { return document.modify(new CancelableUnitOfWork<>() { @Override public ModelGenerationStartedResult exec(IXtextWebDocument state, CancelIndicator cancelIndicator) { var pushState = (PushWebDocument) state; var worker = workerProvider.get(); - worker.setState(pushState, timeoutSec); + worker.setState(pushState, randomSeed, timeoutSec); var manager = pushState.getModelGenerationManager(); worker.start(); boolean canceled = manager.setActiveModelGenerationWorker(worker, cancelIndicator); 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 index 77fc7484..9ee74207 100644 --- 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 @@ -79,6 +79,8 @@ public class ModelGenerationWorker implements Runnable { private ScheduledExecutorService scheduledExecutorService; + private int randomSeed; + private long timeoutSec; private Future future; @@ -97,8 +99,9 @@ public class ModelGenerationWorker implements Runnable { scheduledExecutorService = provider.getScheduled(ModelGenerationService.MODEL_GENERATION_TIMEOUT_EXECUTOR); } - public void setState(PushWebDocument state, long timeoutSec) { + public void setState(PushWebDocument state, int randomSeed, long timeoutSec) { this.state = state; + this.randomSeed = randomSeed; this.timeoutSec = timeoutSec; text = state.getText(); } @@ -188,7 +191,7 @@ public class ModelGenerationWorker implements Runnable { cancellationToken.checkCancelled(); notifyResult(new ModelGenerationStatusResult(uuid, "Generating model")); var bestFirst = new BestFirstStoreManager(store, 1); - bestFirst.startExploration(initialVersion); + bestFirst.startExploration(initialVersion, randomSeed); cancellationToken.checkCancelled(); var solutionStore = bestFirst.getSolutionStore(); if (solutionStore.getSolutions().isEmpty()) { diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java index 625909b9..ff8f4943 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java @@ -9,6 +9,9 @@ import com.google.inject.Singleton; import org.eclipse.xtext.ide.ExecutorServiceProvider; import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.refinery.language.web.generator.ModelGenerationService; import tools.refinery.language.web.semantics.SemanticsService; import java.lang.invoke.MethodHandle; @@ -25,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; @Singleton public class ThreadPoolExecutorServiceProvider extends ExecutorServiceProvider { + private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolExecutorServiceProvider.class); private static final String DOCUMENT_LOCK_EXECUTOR; private static final AtomicInteger POOL_ID = new AtomicInteger(1); @@ -33,6 +37,7 @@ public class ThreadPoolExecutorServiceProvider extends ExecutorServiceProvider { private final int executorThreadCount; private final int lockExecutorThreadCount; private final int semanticsExecutorThreadCount; + private final int generatorExecutorThreadCount; static { var lookup = MethodHandles.lookup(); @@ -57,7 +62,18 @@ public class ThreadPoolExecutorServiceProvider extends ExecutorServiceProvider { public ThreadPoolExecutorServiceProvider() { executorThreadCount = getCount("REFINERY_XTEXT_THREAD_COUNT").orElse(0); lockExecutorThreadCount = getCount("REFINERY_XTEXT_LOCKING_THREAD_COUNT").orElse(executorThreadCount); - semanticsExecutorThreadCount = getCount("REFINERY_XTEXT_SEMANTICS_THREAD_COUNT").orElse(executorThreadCount); + int semanticsCount = getCount("REFINERY_XTEXT_SEMANTICS_THREAD_COUNT").orElse(0); + if (semanticsCount == 0 || executorThreadCount == 0) { + semanticsExecutorThreadCount = 0; + } else { + semanticsExecutorThreadCount = Math.max(semanticsCount, executorThreadCount); + } + if (semanticsExecutorThreadCount != semanticsCount) { + LOG.warn("Setting REFINERY_XTEXT_SEMANTICS_THREAD_COUNT to {} to avoid deadlock. This value must be " + + "either 0 or at least as large as REFINERY_XTEXT_THREAD_COUNT to avoid lock contention.", + semanticsExecutorThreadCount); + } + generatorExecutorThreadCount = getCount("REFINERY_MODEL_GENERATION_THREAD_COUNT").orElse(executorThreadCount); } private static Optional getCount(String name) { @@ -94,6 +110,8 @@ public class ThreadPoolExecutorServiceProvider extends ExecutorServiceProvider { private int getSize(String key) { if (SemanticsService.SEMANTICS_EXECUTOR.equals(key)) { return semanticsExecutorThreadCount; + } else if (ModelGenerationService.MODEL_GENERATION_EXECUTOR.equals(key)) { + return generatorExecutorThreadCount; } else if (DOCUMENT_LOCK_EXECUTOR.equals(key)) { return lockExecutorThreadCount; } else { diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java index a04ee226..e1d00d8f 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java @@ -62,11 +62,13 @@ public class PushServiceDispatcher extends XtextServiceDispatcher { if (!start && !cancel) { throw new InvalidRequestException("Either start of cancel must be specified"); } + @SuppressWarnings({"squid:S4738"}) + int randomSeed = start ? getInt(context, "randomSeed", Optional.absent()) : 0; var descriptor = new ServiceDescriptor(); descriptor.setService(() -> { try { if (start) { - return modelGenerationService.generateModel(document); + return modelGenerationService.generateModel(document, randomSeed); } else { return modelGenerationService.cancelModelGeneration(document); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java index 02634a02..3d32f84c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -44,11 +44,13 @@ public class BestFirstStoreManager { equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { @Override protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { - throw new UnsupportedOperationException("This equivalence storage is not prepared to resolve symmetries!"); + throw new UnsupportedOperationException("This equivalence storage is not prepared to resolve " + + "symmetries!"); } }; visualizationStore = new VisualizationStoreImpl(); } + public ModelStore getModelStore() { return modelStore; } @@ -74,7 +76,12 @@ public class BestFirstStoreManager { } public void startExploration(Version initial) { - BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial), 1); + startExploration(initial, 1); + } + + public void startExploration(Version initial, int randomSeed) { + BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial), + randomSeed); bestFirstExplorer.explore(); } } -- cgit v1.2.3-54-g00ecf