aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-dse
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2023-09-14 19:29:36 +0200
committerLibravatar GitHub <noreply@github.com>2023-09-14 19:29:36 +0200
commit98ed3b6db5f4e51961a161050cc31c66015116e8 (patch)
tree8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/store-dse
parentMerge pull request #38 from nagilooh/design-space-exploration (diff)
parentMerge remote-tracking branch 'upstream/main' into partial-interpretation (diff)
downloadrefinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
Diffstat (limited to 'subprojects/store-dse')
-rw-r--r--subprojects/store-dse/build.gradle.kts7
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java71
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java48
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java29
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java13
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java303
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java67
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java65
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java99
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActionVariable.java12
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActivationVariable.java54
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/AtomicAction.java18
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/DeleteAction.java40
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/InsertAction.java94
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/NewItemVariable.java45
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/TransformationAction.java129
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java12
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java24
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java11
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java11
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java43
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java51
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java23
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java107
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java29
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java29
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java52
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java57
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java132
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java26
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java46
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java101
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java60
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/BoundPropagator.java11
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationAdapter.java20
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java32
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationResult.java28
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationStoreAdapter.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/Propagator.java17
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java75
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java53
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java38
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java40
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundRuleBasedPropagator.java43
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/RuleBasedPropagator.java36
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java65
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java87
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java203
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java164
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java92
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java28
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java61
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java27
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java24
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java69
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java99
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java71
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java43
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java11
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java9
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java132
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java19
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java33
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java119
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java16
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java64
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java15
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java16
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java16
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java16
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java16
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java13
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java14
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java94
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java75
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java74
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/AndCriterion.java53
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeCriterion.java43
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CompositeObjective.java69
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CountObjective.java47
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criteria.java47
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java22
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java10
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java23
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java10
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objectives.java41
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/OrCriterion.java53
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriterion.java59
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java41
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java18
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java17
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java21
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java17
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java54
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java50
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java37
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java138
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java104
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java55
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java104
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java37
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java68
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java52
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/ActionEqualsTest.java629
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java237
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java118
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java607
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java413
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java22
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java18
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomCriterion.java25
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyRandomObjective.java23
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java2
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java149
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java131
121 files changed, 4081 insertions, 3767 deletions
diff --git a/subprojects/store-dse/build.gradle.kts b/subprojects/store-dse/build.gradle.kts
index bb1dee7d..5517e189 100644
--- a/subprojects/store-dse/build.gradle.kts
+++ b/subprojects/store-dse/build.gradle.kts
@@ -10,7 +10,8 @@ plugins {
10 10
11dependencies { 11dependencies {
12 api(project(":refinery-store-query")) 12 api(project(":refinery-store-query"))
13 api(project(":refinery-store-query-viatra")) 13 implementation(project(":refinery-store-dse-visualization"))
14 api(project(":refinery-store-reasoning")) 14 implementation(libs.eclipseCollections.api)
15 api(project(":refinery-visualization")) 15 runtimeOnly(libs.eclipseCollections)
16 testImplementation(project(":refinery-store-query-viatra"))
16} 17}
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 524c2f55..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java
+++ /dev/null
@@ -1,14 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import org.eclipse.collections.api.block.procedure.Procedure;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.tuple.Tuple;
11
12public interface ActionFactory {
13 Procedure<Tuple> prepare(Model model);
14}
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 ab87ddd5..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java
+++ /dev/null
@@ -1,71 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.dse.internal.TransformationRule;
10import tools.refinery.store.map.Version;
11import tools.refinery.store.dse.internal.Activation;
12import tools.refinery.store.dse.internal.DesignSpaceExplorationBuilderImpl;
13import tools.refinery.store.dse.objectives.Fitness;
14import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper;
15import tools.refinery.store.tuple.Tuple;
16import tools.refinery.store.tuple.Tuple1;
17
18import java.util.Collection;
19import java.util.List;
20import java.util.Random;
21
22public interface DesignSpaceExplorationAdapter extends ModelAdapter {
23 @Override
24 DesignSpaceExplorationStoreAdapter getStoreAdapter();
25
26 static DesignSpaceExplorationBuilder builder() {
27 return new DesignSpaceExplorationBuilderImpl();
28 }
29
30 Collection<Version> explore();
31
32 public int getModelSize();
33
34 public Tuple1 createObject();
35
36 public Tuple deleteObject(Tuple tuple);
37
38 public boolean checkGlobalConstraints();
39
40 public boolean backtrack();
41
42 public boolean backtrack(String reason);
43
44 public Fitness getFitness();
45
46 public void newSolution();
47
48 public int getDepth();
49
50 public Collection<Activation> getUntraversedActivations();
51
52 public boolean fireActivation(Activation activation);
53
54 public boolean fireRandomActivation();
55
56 public List<Version> getTrajectory();
57
58 public boolean isCurrentStateAlreadyTraversed();
59
60 public ObjectiveComparatorHelper getObjectiveComparatorHelper();
61
62 public void restoreTrajectory(List<Version> trajectory);
63
64 public void setRandom(Random random);
65
66 public void setRandom(long seed);
67
68 public List<Version> getSolutions();
69
70 void addTransformationRule(TransformationRule transformationRule);
71}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import tools.refinery.store.adapter.ModelAdapterBuilder;
9import tools.refinery.store.query.dnf.RelationalQuery;
10import tools.refinery.store.dse.internal.TransformationRule;
11import tools.refinery.store.dse.objectives.Objective;
12
13import java.util.Collection;
14import java.util.List;
15
16public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder {
17 default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) {
18 return transformations(List.of(transformationRules));
19 }
20
21 default DesignSpaceExplorationBuilder transformations(Collection<? extends TransformationRule> transformationRules) {
22 transformationRules.forEach(this::transformation);
23 return this;
24 }
25
26 default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) {
27 return globalConstraints(List.of(globalConstraints));
28 }
29
30 default DesignSpaceExplorationBuilder globalConstraints(Collection<RelationalQuery> globalConstraints) {
31 globalConstraints.forEach(this::globalConstraint);
32 return this;
33 }
34
35 default DesignSpaceExplorationBuilder objectives(Objective... objectives) {
36 return objectives(List.of(objectives));
37 }
38
39 default DesignSpaceExplorationBuilder objectives(Collection<? extends Objective> objectives) {
40 objectives.forEach(this::objective);
41 return this;
42 }
43
44 DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule);
45 DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint);
46 DesignSpaceExplorationBuilder objective(Objective objective);
47 DesignSpaceExplorationBuilder strategy(Strategy strategy);
48}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9import tools.refinery.store.dse.internal.TransformationRule;
10import tools.refinery.store.dse.objectives.Objective;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.dnf.RelationalQuery;
13
14import java.util.List;
15import java.util.Set;
16
17public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter {
18
19 @Override
20 DesignSpaceExplorationAdapter createModelAdapter(Model model);
21
22 Set<TransformationRule> getTransformationSpecifications();
23
24 Set<RelationalQuery> getGlobalConstraints();
25
26 List<Objective> getObjectives();
27
28 Strategy getStrategy();
29}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8public interface Strategy {
9
10 void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter);
11
12 void explore();
13}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal;
7
8import tools.refinery.store.tuple.Tuple;
9
10public record Activation(TransformationRule transformationRule, Tuple activation) {
11 public boolean fire() {
12 return transformationRule.fireActivation(activation);
13 }
14}
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 1ae09916..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java
+++ /dev/null
@@ -1,303 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.internal;
11
12import tools.refinery.store.map.Version;
13import tools.refinery.store.model.Interpretation;
14import tools.refinery.store.model.Model;
15import tools.refinery.store.query.ModelQueryAdapter;
16import tools.refinery.store.query.dnf.Query;
17import tools.refinery.store.query.dnf.RelationalQuery;
18import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
19import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter;
20import tools.refinery.store.dse.Strategy;
21import tools.refinery.store.dse.objectives.Fitness;
22import tools.refinery.store.dse.objectives.Objective;
23import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper;
24import tools.refinery.store.query.resultset.ResultSet;
25import tools.refinery.store.representation.Symbol;
26import tools.refinery.store.tuple.Tuple;
27import tools.refinery.store.tuple.Tuple1;
28import tools.refinery.visualization.ModelVisualizerAdapter;
29
30import java.util.*;
31
32public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter {
33 static final Symbol<Integer> NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0);
34 private final Model model;
35 private final ModelQueryAdapter queryEngine;
36 private final DesignSpaceExplorationStoreAdapterImpl storeAdapter;
37 private final Set<TransformationRule> transformationRules;
38 private final Set<RelationalQuery> globalConstraints;
39 private final List<Objective> objectives;
40 private final LinkedHashSet<ResultSet<Boolean>> globalConstraintResultSets = new LinkedHashSet<>();
41 private final Interpretation<Integer> sizeInterpretation;
42 private final Strategy strategy;
43
44 private ObjectiveComparatorHelper objectiveComparatorHelper;
45 private List<Version> trajectory = new ArrayList<>();
46 private Map<Version, Version> parents = new HashMap<>();
47 private final List<Version> solutions = new ArrayList<>();
48 private Map<Version, List<Activation>> statesAndTraversedActivations;
49 @SuppressWarnings("squid:S2245")
50 private Random random = new Random();
51 private boolean isNewState = false;
52 private final boolean isVisualizationEnabled;
53 private final ModelVisualizerAdapter modelVisualizerAdapter;
54
55 private final Map<Version, Fitness> fitnessCache = new HashMap<>();
56
57 public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) {
58 this.model = model;
59 this.storeAdapter = storeAdapter;
60 this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL);
61 queryEngine = model.getAdapter(ModelQueryAdapter.class);
62
63 globalConstraints = storeAdapter.getGlobalConstraints();
64 for (var constraint : globalConstraints) {
65 globalConstraintResultSets.add(queryEngine.getResultSet(constraint));
66 }
67
68 transformationRules = storeAdapter.getTransformationSpecifications();
69 for (var rule : transformationRules) {
70 rule.prepare(model, queryEngine);
71 }
72
73 objectives = storeAdapter.getObjectives();
74 statesAndTraversedActivations = new HashMap<>();
75 strategy = storeAdapter.getStrategy();
76 strategy.initialize(this);
77 modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null);
78 isVisualizationEnabled = modelVisualizerAdapter != null;
79
80 }
81
82 @Override
83 public void addTransformationRule(TransformationRule rule) {
84 transformationRules.add(rule);
85 rule.prepare(model, queryEngine);
86 }
87
88 public List<Version> getTrajectory() {
89 return new ArrayList<>(trajectory);
90 }
91
92 @Override
93 public Model getModel() {
94 return model;
95 }
96
97 @Override
98 public DesignSpaceExplorationStoreAdapter getStoreAdapter() {
99 return storeAdapter;
100 }
101
102 @Override
103 public List<Version> explore() {
104 var state = model.commit();
105 trajectory.add(state);
106 strategy.explore();
107 if (isVisualizationEnabled) {
108 modelVisualizerAdapter.visualize();
109 }
110 return solutions;
111 }
112
113 @Override
114 public int getModelSize() {
115 return sizeInterpretation.get(Tuple.of());
116 }
117
118 @Override
119 public Tuple1 createObject() {
120 var newNodeId = getModelSize();
121 sizeInterpretation.put(Tuple.of(), newNodeId + 1);
122 return Tuple.of(newNodeId);
123 }
124
125 @Override
126 public Tuple deleteObject(Tuple tuple) {
127 if (tuple.getSize() != 1) {
128 throw new IllegalArgumentException("Tuple size must be 1");
129 }
130// TODO: implement more efficient deletion
131// if (tuple.get(0) == getModelSize() - 1) {
132// sizeInterpretation.put(Tuple.of(), getModelSize() - 1);
133// }
134 return tuple;
135 }
136
137 @Override
138 public boolean checkGlobalConstraints() {
139 for (var resultSet : globalConstraintResultSets) {
140 if (resultSet.size() > 0) {
141 return false;
142 }
143 }
144 return true;
145 }
146
147 @Override
148 public boolean backtrack() {
149 return backtrack("");
150 }
151 @Override
152 public boolean backtrack(String reason) {
153 if (trajectory.size() < 2) {
154 return false;
155 }
156 var currentState = model.getState();
157 if (!parents.containsKey(currentState)) {
158 return false;
159 }
160 if (isVisualizationEnabled) {
161 modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1),
162 trajectory.get(trajectory.size() - 2), "backtrack(" + reason + ")");
163 }
164 model.restore(parents.get(model.getState()));
165 trajectory.remove(trajectory.size() - 1);
166 return true;
167 }
168
169 @Override
170 public void restoreTrajectory(List<Version> trajectory) {
171 model.restore(trajectory.get(trajectory.size() - 1));
172// if (isVisualizationEnabled) {
173// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1),
174// trajectory.get(trajectory.size() - 1), "restore");
175// }
176 this.trajectory = new ArrayList<>(trajectory);
177
178 }
179
180 @Override
181 public void setRandom(Random random) {
182 this.random = random;
183 }
184
185 @Override
186 @SuppressWarnings("squid:S2245")
187 public void setRandom(long seed) {
188 this.random = new Random(seed);
189 }
190
191 @Override
192 public List<Version> getSolutions() {
193 return solutions;
194 }
195
196 @Override
197 public Fitness getFitness() {
198 return fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness());
199 }
200
201 private Fitness calculateFitness() {
202 Fitness result = new Fitness();
203 boolean satisfiesHardObjectives = true;
204 for (Objective objective : objectives) {
205 var fitness = objective.getFitness(this);
206 result.put(objective.getName(), fitness);
207 if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) {
208 satisfiesHardObjectives = false;
209 }
210 }
211 result.setSatisfiesHardObjectives(satisfiesHardObjectives);
212
213 return result;
214 }
215
216 @Override
217 public void newSolution() {
218 var state = model.getState();
219 solutions.add(state);
220 if (isVisualizationEnabled) {
221 modelVisualizerAdapter.addSolution(state);
222 }
223 }
224
225 @Override
226 public int getDepth() {
227 return trajectory.size() - 1;
228 }
229
230 public LinkedHashSet<Activation> getUntraversedActivations() {
231 var traversedActivations = statesAndTraversedActivations.get(model.getState());
232 if (traversedActivations == null) {
233 return new LinkedHashSet<>(getAllActivations());
234 }
235 else {
236 LinkedHashSet<Activation> untraversedActivations = new LinkedHashSet<>();
237 for (Activation activation : getAllActivations()) {
238 if (!traversedActivations.contains(activation)) {
239 untraversedActivations.add(activation);
240 }
241 }
242 return untraversedActivations;
243 }
244 }
245
246 @Override
247 public boolean fireActivation(Activation activation) {
248 if (activation == null) {
249 return false;
250 }
251 var previousState = model.getState();
252 if (!activation.fire()) {
253 return false;
254 }
255 statesAndTraversedActivations.computeIfAbsent(previousState, s -> new ArrayList<>()).add(activation);
256 var newState = model.commit();
257 trajectory.add(newState);
258 parents.put(newState, previousState);
259 isNewState = !statesAndTraversedActivations.containsKey(newState);
260 if (isVisualizationEnabled) {
261 if (isNewState) {
262 modelVisualizerAdapter.addState(newState, getFitness().values());
263 }
264 modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(),
265 activation.activation());
266 }
267 return true;
268 }
269
270 @Override
271 public boolean fireRandomActivation() {
272 var activations = getUntraversedActivations();
273 if (activations.isEmpty()) {
274 return false;
275 }
276 int index = random.nextInt(activations.size());
277 var iterator = activations.iterator();
278 while (index-- > 0) {
279 iterator.next();
280 }
281 var activationId = iterator.next();
282 return fireActivation(activationId);
283 }
284
285 public List<Activation> getAllActivations() {
286 List<Activation> result = new LinkedList<>();
287 for (var rule : transformationRules) {
288 result.addAll(rule.getAllActivationsAsList());
289 }
290 return result;
291 }
292
293 public boolean isCurrentStateAlreadyTraversed() {
294 return !isNewState;
295 }
296
297 public ObjectiveComparatorHelper getObjectiveComparatorHelper() {
298 if (objectiveComparatorHelper == null) {
299 objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives);
300 }
301 return objectiveComparatorHelper;
302 }
303}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal;
7
8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11import tools.refinery.store.query.dnf.RelationalQuery;
12import tools.refinery.store.dse.DesignSpaceExplorationBuilder;
13import tools.refinery.store.dse.Strategy;
14import tools.refinery.store.dse.objectives.Objective;
15
16import java.util.LinkedHashSet;
17import java.util.LinkedList;
18import java.util.List;
19
20public class DesignSpaceExplorationBuilderImpl
21 extends AbstractModelAdapterBuilder<DesignSpaceExplorationStoreAdapterImpl>
22 implements DesignSpaceExplorationBuilder {
23 private final LinkedHashSet<TransformationRule> transformationSpecifications = new LinkedHashSet<>();
24 private final LinkedHashSet<RelationalQuery> globalConstraints = new LinkedHashSet<>();
25 private final List<Objective> objectives = new LinkedList<>();
26 private Strategy strategy;
27
28 @Override
29 protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) {
30 return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints,
31 objectives, strategy);
32 }
33
34 @Override
35 public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) {
36 checkNotConfigured();
37 transformationSpecifications.add(transformationRule);
38 return this;
39 }
40
41 @Override
42 public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) {
43 checkNotConfigured();
44 globalConstraints.add(globalConstraint);
45 return this;
46 }
47
48 @Override
49 public DesignSpaceExplorationBuilder objective(Objective objective) {
50 checkNotConfigured();
51 objectives.add(objective);
52 return this;
53 }
54
55 @Override
56 public DesignSpaceExplorationBuilder strategy(Strategy strategy) {
57 checkNotConfigured();
58 this.strategy = strategy;
59 return this;
60 }
61
62 @Override
63 protected void doConfigure(ModelStoreBuilder storeBuilder) {
64 storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL);
65 super.doConfigure(storeBuilder);
66 }
67}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal;
7
8import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter;
9import tools.refinery.store.dse.Strategy;
10import tools.refinery.store.dse.objectives.Objective;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.ModelStore;
13import tools.refinery.store.query.dnf.RelationalQuery;
14
15import java.util.List;
16import java.util.Set;
17
18public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter {
19 private final ModelStore store;
20 private final Set<TransformationRule> transformationSpecifications;
21 private final Set<RelationalQuery> globalConstraints;
22 private final List<Objective> objectives;
23 private final Strategy strategy;
24
25 public DesignSpaceExplorationStoreAdapterImpl(ModelStore store,
26 Set<TransformationRule> transformationSpecifications,
27 Set<RelationalQuery> globalConstraints,
28 List<Objective> objectives, Strategy strategy) {
29 this.store = store;
30 this.transformationSpecifications = transformationSpecifications;
31 this.globalConstraints = globalConstraints;
32 this.objectives = objectives;
33 this.strategy = strategy;
34 }
35
36 @Override
37 public ModelStore getStore() {
38 return store;
39 }
40
41 @Override
42 public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) {
43 return new DesignSpaceExplorationAdapterImpl(model, this);
44 }
45
46 @Override
47 public Set<TransformationRule> getTransformationSpecifications() {
48 return transformationSpecifications;
49 }
50
51 @Override
52 public Set<RelationalQuery> getGlobalConstraints() {
53 return globalConstraints;
54 }
55
56 @Override
57 public List<Objective> getObjectives() {
58 return objectives;
59 }
60
61 @Override
62 public Strategy getStrategy() {
63 return strategy;
64 }
65}
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 37117164..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java
+++ /dev/null
@@ -1,99 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal;
7
8import org.eclipse.collections.api.block.procedure.Procedure;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.RelationalQuery;
12import tools.refinery.store.dse.ActionFactory;
13import tools.refinery.store.query.resultset.OrderedResultSet;
14import tools.refinery.store.query.resultset.ResultSet;
15import tools.refinery.store.tuple.Tuple;
16
17import java.util.*;
18
19public class TransformationRule {
20
21 private final String name;
22 private final RelationalQuery precondition;
23 private final ActionFactory actionFactory;
24 private Procedure<Tuple> action;
25 private OrderedResultSet<Boolean> activations;
26 private Random random;
27 private ModelQueryAdapter queryEngine;
28
29 @SuppressWarnings("squid:S2245")
30 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) {
31 this(name, precondition, actionFactory, new Random());
32 }
33
34 @SuppressWarnings("squid:S2245")
35 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) {
36 this(name, precondition, actionFactory, new Random(seed));
37 }
38
39 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) {
40 this.name = name;
41 this.precondition = precondition;
42 this.actionFactory = actionFactory;
43 this.random = random;
44 }
45 public boolean prepare(Model model, ModelQueryAdapter queryEngine) {
46 action = actionFactory.prepare(model);
47 this.queryEngine = queryEngine;
48 activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition));
49 return true;
50 }
51
52 public boolean fireActivation(Tuple activation) {
53 action.accept(activation);
54 queryEngine.flushChanges();
55 return true;
56 }
57
58 public boolean fireRandomActivation() {
59 return getRandomActivation().fire();
60 }
61
62 public String getName() {
63 return name;
64 }
65
66 public RelationalQuery getPrecondition() {
67 return precondition;
68 }
69
70 public ResultSet<Boolean> getAllActivationsAsResultSet() {
71 return activations;
72 }
73
74 public Set<Activation> getAllActivations() {
75 var result = new LinkedHashSet<Activation>();
76 var cursor = activations.getAll();
77 while (cursor.move()) {
78 result.add(new Activation(this, cursor.getKey()));
79 }
80 return result;
81 }
82
83 public List<Activation> getAllActivationsAsList() {
84 var result = new ArrayList<Activation>();
85 var cursor = activations.getAll();
86 while (cursor.move()) {
87 result.add(new Activation(this, cursor.getKey()));
88 }
89 return result;
90 }
91
92 public Activation getRandomActivation() {
93 return new Activation(this, activations.getKey(random.nextInt(activations.size())));
94 }
95
96 public Activation getActivation(int index) {
97 return new Activation(this, activations.getKey(index));
98 }
99}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActionVariable.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActionVariable.java
deleted file mode 100644
index 92de565d..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActionVariable.java
+++ /dev/null
@@ -1,12 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.tuple.Tuple;
9
10public interface ActionVariable extends AtomicAction {
11 Tuple getValue();
12}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActivationVariable.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActivationVariable.java
deleted file mode 100644
index 6b4c6340..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/ActivationVariable.java
+++ /dev/null
@@ -1,54 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.tuple.Tuple;
10
11public class ActivationVariable implements ActionVariable {
12
13 private final int index;
14 private Tuple value;
15
16 public ActivationVariable() {
17 this(0);
18 }
19
20 public ActivationVariable(int index) {
21 this.index = index;
22 }
23
24 @Override
25 public void fire(Tuple activation) {
26 value = Tuple.of(activation.get(index));
27 }
28
29 @Override
30 public ActivationVariable prepare(Model model) {
31 return this;
32 }
33
34 @Override
35 public Tuple getValue() {
36 return value;
37 }
38
39 @Override
40 public boolean equalsWithSubstitution(AtomicAction other) {
41 if (other == null || getClass() != other.getClass()) {
42 return false;
43 }
44 var otherAction = (ActivationVariable) other;
45
46 if (index != otherAction.index) {
47 return false;
48 }
49 if (value == null) {
50 return otherAction.value == null;
51 }
52 return value.equals(otherAction.value);
53 }
54}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/AtomicAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/AtomicAction.java
deleted file mode 100644
index a8f10bca..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/AtomicAction.java
+++ /dev/null
@@ -1,18 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.tuple.Tuple;
10
11public interface AtomicAction {
12
13 void fire(Tuple activation);
14
15 AtomicAction prepare(Model model);
16
17 boolean equalsWithSubstitution(AtomicAction other);
18}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/DeleteAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/DeleteAction.java
deleted file mode 100644
index 9900390f..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/DeleteAction.java
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.tuple.Tuple;
11
12public class DeleteAction implements AtomicAction {
13
14 private final ActionVariable variable;
15 private DesignSpaceExplorationAdapter dseAdapter;
16
17 public DeleteAction(ActionVariable variable) {
18 this.variable = variable;
19 }
20
21 @Override
22 public void fire(Tuple activation) {
23 dseAdapter.deleteObject(variable.getValue());
24 }
25
26 @Override
27 public DeleteAction prepare(Model model) {
28 dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
29 return this;
30 }
31
32 @Override
33 public boolean equalsWithSubstitution(AtomicAction other) {
34 if (other == null || getClass() != other.getClass()) {
35 return false;
36 }
37 var otherAction = (DeleteAction) other;
38 return this.variable.getClass() == otherAction.variable.getClass();
39 }
40}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/InsertAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/InsertAction.java
deleted file mode 100644
index 90fcc5ac..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/InsertAction.java
+++ /dev/null
@@ -1,94 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.model.Interpretation;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.tuple.Tuple0;
12
13import java.util.Arrays;
14
15public class InsertAction<T> implements AtomicAction {
16
17 private final Interpretation<T> interpretation;
18 private final T value;
19 private final int arity;
20 private final ActionVariable[] variables;
21
22 public InsertAction(Interpretation<T> interpretation, T value, ActionVariable... variables) {
23 this.interpretation = interpretation;
24 this.value = value;
25 this.variables = variables;
26 this.arity = interpretation.getSymbol().arity();
27 if (variables.length != arity) {
28 throw new IllegalArgumentException("Expected " + arity + " variables, but got " + variables.length);
29 }
30 }
31
32 @Override
33 public void fire(Tuple activation) {
34 Tuple tuple;
35 if (arity == 0) {
36 tuple = Tuple0.INSTANCE;
37 }
38 else if (arity == 1) {
39 tuple = variables[0].getValue();
40 }
41 else if (arity == 2) {
42 tuple = Tuple.of(variables[0].getValue().get(0), variables[1].getValue().get(0));
43 }
44 else if (arity == 3) {
45 tuple = Tuple.of(variables[0].getValue().get(0), variables[1].getValue().get(0), variables[2].getValue().get(0));
46 }
47 else {
48 tuple = Tuple.of(Arrays.stream(variables).map(variable -> variable.getValue().get(0))
49 .mapToInt(Integer::intValue).toArray());
50 }
51 interpretation.put(tuple, value);
52 }
53
54 public void put(Tuple tuple) {
55 interpretation.put(tuple, value);
56 }
57
58 @Override
59 public InsertAction<T> prepare(Model model) {
60 return this;
61 }
62
63 public ActionVariable[] getVariables() {
64 return variables;
65 }
66
67 @Override
68 public boolean equalsWithSubstitution(AtomicAction other) {
69 if (other == null || getClass() != other.getClass()) {
70 return false;
71 }
72 var otherAction = (InsertAction<?>) other;
73 if (variables.length != otherAction.variables.length) {
74 return false;
75 }
76 if (!interpretation.equals(otherAction.interpretation)) {
77 return false;
78 }
79 if (value == null) {
80 if (otherAction.value != null) {
81 return false;
82 }
83 }
84 else if (!value.equals(otherAction.value)) {
85 return false;
86 }
87 for (var i = 0; i < variables.length; i++) {
88 if (!variables[i].equalsWithSubstitution(otherAction.variables[i])) {
89 return false;
90 }
91 }
92 return true;
93 }
94}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/NewItemVariable.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/NewItemVariable.java
deleted file mode 100644
index cbb9697e..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/NewItemVariable.java
+++ /dev/null
@@ -1,45 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.tuple.Tuple1;
12
13public class NewItemVariable implements ActionVariable {
14 private DesignSpaceExplorationAdapter dseAdapter;
15 private Tuple1 value;
16
17 @Override
18 public void fire(Tuple activation) {
19 value = dseAdapter.createObject();
20 }
21
22 @Override
23 public NewItemVariable prepare(Model model) {
24 dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
25 return this;
26 }
27
28 @Override
29 public Tuple1 getValue() {
30 return value;
31 }
32
33 @Override
34 public boolean equalsWithSubstitution(AtomicAction other) {
35 if (other == null || getClass() != other.getClass()) {
36 return false;
37 }
38 var otherAction = (NewItemVariable) other;
39 if (value == null) {
40 return otherAction.value == null;
41 }
42 return value.equals(otherAction.value);
43
44 }
45}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/TransformationAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/TransformationAction.java
deleted file mode 100644
index adc4df9e..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/action/TransformationAction.java
+++ /dev/null
@@ -1,129 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.internal.action;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.tuple.Tuple;
10import tools.refinery.store.tuple.Tuple2;
11
12import java.util.*;
13
14public class TransformationAction {
15 private final List<ActionVariable> actionVariables = new ArrayList<>();
16 private final List<InsertAction<?>> insertActions = new ArrayList<>();
17 private final List<DeleteAction> deleteActions = new ArrayList<>();
18 private boolean configured = false;
19 private final Map<Integer, List<Tuple2>> actionVariableUsageMap = new LinkedHashMap<>();
20
21 public TransformationAction add(ActionVariable action) {
22 checkConfigured();
23 actionVariables.add(action);
24 return this;
25 }
26
27 public TransformationAction add(InsertAction<?> action) {
28 checkConfigured();
29 insertActions.add(action);
30 return this;
31 }
32
33 public TransformationAction add(DeleteAction action) {
34 checkConfigured();
35 deleteActions.add(action);
36 return this;
37 }
38
39 private void checkConfigured() {
40 if (configured) {
41 throw new IllegalStateException("Action already configured.");
42 }
43 }
44
45 public TransformationAction prepare(Model model) {
46 for (ActionVariable action : actionVariables) {
47 action.prepare(model);
48 }
49 for (InsertAction<?> action : insertActions) {
50 action.prepare(model);
51 }
52 for (DeleteAction action : deleteActions) {
53 action.prepare(model);
54 }
55
56 for (var insertAction : insertActions) {
57 var actionIndex = insertActions.indexOf(insertAction);
58 var variables = insertAction.getVariables();
59 for (var i = 0; i < variables.length; i++) {
60 var variablelGlobalIndex = actionVariables.indexOf(variables[i]);
61 actionVariableUsageMap.computeIfAbsent(variablelGlobalIndex, k -> new ArrayList<>());
62 actionVariableUsageMap.get(variablelGlobalIndex).add(Tuple.of(actionIndex, i));
63 }
64 }
65
66 configured = true;
67 return this;
68 }
69
70 public boolean fire(Tuple activation) {
71 for (ActionVariable action : actionVariables) {
72 action.fire(activation);
73 }
74 for (InsertAction<?> action : insertActions) {
75 action.fire(activation);
76 }
77 for (DeleteAction action : deleteActions) {
78 action.fire(activation);
79 }
80 return true;
81 }
82
83 // Returns true if ActionVariables and InsertActions are inserted in same order, ActionVariables are equal (they
84 // have the same index for getting the value from the activation Tuple) and InsertActions are equal (they have
85 // the same arity and value to be set).
86 public boolean equalsWithSubstitution(TransformationAction other) {
87 if (other == this) {
88 return true;
89 }
90
91 if (actionVariables.size() != other.actionVariables.size()) {
92 return false;
93 }
94
95 if (insertActions.size() != other.insertActions.size()) {
96 return false;
97 }
98
99 if (deleteActions.size() != other.deleteActions.size()) {
100 return false;
101 }
102
103 for (var i = 0; i < actionVariables.size(); i++) {
104 var variable = actionVariables.get(i);
105 var otherVariable = other.actionVariables.get(i);
106 if (!variable.equalsWithSubstitution(otherVariable)) {
107 return false;
108 }
109 }
110
111 for (var i = 0; i < insertActions.size(); i++) {
112 var insertAction = insertActions.get(i);
113 var otherInsertAction = other.insertActions.get(i);
114 if (!insertAction.equalsWithSubstitution(otherInsertAction)) {
115 return false;
116 }
117 }
118
119 for (var i = 0; i < deleteActions.size(); i++) {
120 var deleteAction = deleteActions.get(i);
121 var otherDeleteAction = other.deleteActions.get(i);
122 if (!deleteAction.equalsWithSubstitution(otherDeleteAction)) {
123 return false;
124 }
125 }
126 return this.actionVariableUsageMap.equals(other.actionVariableUsageMap);
127
128 }
129}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification;
7
8public enum DanglingEdges {
9 IGNORE,
10 DELETE,
11 FAIL
12}
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..58b60499
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java
@@ -0,0 +1,24 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.dse.modification.internal.ModificationBuilderImpl;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.tuple.Tuple1;
12
13public interface ModificationAdapter extends ModelAdapter {
14
15 int getModelSize();
16
17 Tuple1 createObject();
18
19 boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges);
20
21 static ModificationBuilder builder() {
22 return new ModificationBuilderImpl();
23 }
24}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification;
7
8import tools.refinery.store.adapter.ModelAdapterBuilder;
9
10public interface ModificationBuilder extends ModelAdapterBuilder {
11}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9
10public interface ModificationStoreAdapter extends ModelStoreAdapter {
11}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.actions;
7
8import tools.refinery.store.dse.modification.ModificationAdapter;
9import tools.refinery.store.dse.transition.actions.AbstractActionLiteral;
10import tools.refinery.store.dse.transition.actions.BoundActionLiteral;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.term.NodeVariable;
13
14import java.util.List;
15
16public class CreateActionLiteral extends AbstractActionLiteral {
17 private final NodeVariable variable;
18
19 public CreateActionLiteral(NodeVariable variable) {
20
21 this.variable = variable;
22 }
23
24 public NodeVariable getVariable() {
25 return variable;
26 }
27
28 @Override
29 public List<NodeVariable> getInputVariables() {
30 return List.of();
31 }
32
33 @Override
34 public List<NodeVariable> getOutputVariables() {
35 return List.of(variable);
36 }
37
38 @Override
39 public BoundActionLiteral bindToModel(Model model) {
40 var adapter = model.getAdapter(ModificationAdapter.class);
41 return ignoredTuple -> adapter.createObject();
42 }
43}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.actions;
7
8import tools.refinery.store.dse.modification.DanglingEdges;
9import tools.refinery.store.dse.modification.ModificationAdapter;
10import tools.refinery.store.dse.transition.actions.AbstractActionLiteral;
11import tools.refinery.store.dse.transition.actions.BoundActionLiteral;
12import tools.refinery.store.model.Model;
13import tools.refinery.store.query.term.NodeVariable;
14import tools.refinery.store.tuple.Tuple;
15
16import java.util.List;
17
18public class DeleteActionLiteral extends AbstractActionLiteral {
19 private final NodeVariable variable;
20 private final DanglingEdges danglingEdges;
21
22 public DeleteActionLiteral(NodeVariable variable, DanglingEdges danglingEdges) {
23
24 this.variable = variable;
25 this.danglingEdges = danglingEdges;
26 }
27
28 public NodeVariable getVariable() {
29 return variable;
30 }
31
32 public DanglingEdges getDanglingEdges() {
33 return danglingEdges;
34 }
35
36 @Override
37 public List<NodeVariable> getInputVariables() {
38 return List.of(variable);
39 }
40
41 @Override
42 public List<NodeVariable> getOutputVariables() {
43 return List.of();
44 }
45
46 @Override
47 public BoundActionLiteral bindToModel(Model model) {
48 var adapter = model.getAdapter(ModificationAdapter.class);
49 return tuple -> adapter.deleteObject(tuple, danglingEdges) ? Tuple.of() : null;
50 }
51}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.actions;
7
8import tools.refinery.store.dse.modification.DanglingEdges;
9import tools.refinery.store.query.term.NodeVariable;
10
11public class ModificationActionLiterals {
12 private ModificationActionLiterals() {
13 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
14 }
15
16 public static CreateActionLiteral create(NodeVariable variable) {
17 return new CreateActionLiteral(variable);
18 }
19
20 public static DeleteActionLiteral delete(NodeVariable variable, DanglingEdges danglingEdges) {
21 return new DeleteActionLiteral(variable, danglingEdges);
22 }
23}
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..4e77c462
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java
@@ -0,0 +1,107 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.internal;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9import tools.refinery.store.dse.modification.DanglingEdges;
10import tools.refinery.store.dse.modification.ModificationAdapter;
11import tools.refinery.store.model.Interpretation;
12import tools.refinery.store.model.Model;
13import tools.refinery.store.representation.Symbol;
14import tools.refinery.store.tuple.Tuple;
15import tools.refinery.store.tuple.Tuple1;
16
17import java.util.HashSet;
18
19public class ModificationAdapterImpl implements ModificationAdapter {
20 static final Symbol<Integer> NEXT_ID = Symbol.of("NEXT_ID", 0, Integer.class, 0);
21
22 final ModelStoreAdapter storeAdapter;
23 final Model model;
24 Interpretation<Integer> nodeCountInterpretation;
25
26 ModificationAdapterImpl(ModelStoreAdapter storeAdapter, Model model) {
27 this.storeAdapter = storeAdapter;
28 this.model = model;
29 this.nodeCountInterpretation = model.getInterpretation(NEXT_ID);
30 }
31
32 @Override
33 public Model getModel() {
34 return model;
35 }
36
37 @Override
38 public ModelStoreAdapter getStoreAdapter() {
39 return storeAdapter;
40 }
41
42 @Override
43 public int getModelSize() {
44 return nodeCountInterpretation.get(Tuple.of());
45 }
46
47 @Override
48 public Tuple1 createObject() {
49 var newNodeId = getModelSize();
50 nodeCountInterpretation.put(Tuple.of(), newNodeId + 1);
51 return Tuple.of(newNodeId);
52 }
53
54 @Override
55 public boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges) {
56 if (tuple.getSize() != 1) {
57 throw new IllegalArgumentException("Tuple size must be 1");
58 }
59 int objectId = tuple.get(0);
60 if (danglingEdges == DanglingEdges.DELETE) {
61 deleteDanglingEdges(objectId);
62 } else if (danglingEdges == DanglingEdges.FAIL && hasDanglingEdges(objectId)) {
63 return false;
64
65 }
66 int modelSize = getModelSize();
67 if (objectId == modelSize - 1) {
68 nodeCountInterpretation.put(Tuple.of(), modelSize - 1);
69 }
70 return true;
71 }
72
73 private void deleteDanglingEdges(int objectId) {
74 for (var symbol : model.getStore().getSymbols()) {
75 deleteDanglingEdges(objectId, (Symbol<?>) symbol);
76 }
77 }
78
79 private <T> void deleteDanglingEdges(int objectId, Symbol<T> symbol) {
80 var interpretation = model.getInterpretation(symbol);
81 var toDelete = new HashSet<Tuple>();
82 int arity = symbol.arity();
83 for (int i = 0; i < arity; i++) {
84 var cursor = interpretation.getAdjacent(i, objectId);
85 while (cursor.move()) {
86 toDelete.add(cursor.getKey());
87 }
88 }
89 var defaultValue = symbol.defaultValue();
90 for (var tuple : toDelete) {
91 interpretation.put(tuple, defaultValue);
92 }
93 }
94
95 private boolean hasDanglingEdges(int objectId) {
96 for (var symbol : model.getStore().getSymbols()) {
97 var interpretation = model.getInterpretation(symbol);
98 int arity = symbol.arity();
99 for (int i = 0; i < arity; i++) {
100 if (interpretation.getAdjacentSize(i, objectId) > 0) {
101 return true;
102 }
103 }
104 }
105 return false;
106 }
107}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.internal;
7
8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.dse.modification.ModificationBuilder;
10import tools.refinery.store.dse.modification.ModificationStoreAdapter;
11import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.model.ModelStoreBuilder;
13import tools.refinery.store.statecoding.StateCoderBuilder;
14
15public class ModificationBuilderImpl extends AbstractModelAdapterBuilder<ModificationStoreAdapter> implements ModificationBuilder {
16
17 @Override
18 protected void doConfigure(ModelStoreBuilder storeBuilder) {
19 storeBuilder.symbols(ModificationAdapterImpl.NEXT_ID);
20 storeBuilder.tryGetAdapter(StateCoderBuilder.class).ifPresent(
21 coderBuilder -> coderBuilder.exclude(ModificationAdapterImpl.NEXT_ID));
22 super.doConfigure(storeBuilder);
23 }
24
25 @Override
26 protected ModificationStoreAdapter doBuild(ModelStore store) {
27 return new ModificationStoreAdapterImpl(store);
28 }
29}
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..62e4227b
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java
@@ -0,0 +1,29 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.modification.internal;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.dse.modification.ModificationStoreAdapter;
10import tools.refinery.store.model.Model;
11import tools.refinery.store.model.ModelStore;
12
13public class ModificationStoreAdapterImpl implements ModificationStoreAdapter {
14 ModelStore store;
15
16 ModificationStoreAdapterImpl(ModelStore store) {
17 this.store = store;
18 }
19
20 @Override
21 public ModelStore getStore() {
22 return store;
23 }
24
25 @Override
26 public ModelAdapter createModelAdapter(Model model) {
27 return new ModificationAdapterImpl(this, model);
28 }
29}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
13
14/**
15 * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution.
16 *
17 * @author Andras Szabolcs Nagy
18 *
19 */
20public class AlwaysSatisfiedDummyHardObjective extends BaseObjective {
21
22 private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective";
23
24 public AlwaysSatisfiedDummyHardObjective() {
25 super(DEFAULT_NAME);
26 }
27
28 public AlwaysSatisfiedDummyHardObjective(String name) {
29 super(name);
30 }
31
32 @Override
33 public Double getFitness(DesignSpaceExplorationAdapter context) {
34 return 0d;
35 }
36
37 @Override
38 public boolean isHardObjective() {
39 return true;
40 }
41
42 @Override
43 public boolean satisfiesHardObjective(Double fitness) {
44 return true;
45 }
46
47 @Override
48 public Objective createNew() {
49 return this;
50 }
51
52}
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 cdd1754f..00000000
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java
+++ /dev/null
@@ -1,57 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
13
14import java.util.Random;
15
16/**
17 * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution.
18 *
19 * @author Andras Szabolcs Nagy
20 *
21 */
22public class AlwaysSatisfiedRandomHardObjective extends BaseObjective {
23
24 private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective";
25 @SuppressWarnings("squid:S2245")
26 private static final Random random = new Random(0);
27
28 public AlwaysSatisfiedRandomHardObjective() {
29 super(DEFAULT_NAME);
30 }
31
32 public AlwaysSatisfiedRandomHardObjective(String name) {
33 super(name);
34 }
35
36 @Override
37 public Double getFitness(DesignSpaceExplorationAdapter context) {
38// return 0d;
39 return random.nextDouble();
40 }
41
42 @Override
43 public boolean isHardObjective() {
44 return true;
45 }
46
47 @Override
48 public boolean satisfiesHardObjective(Double fitness) {
49 return true;
50 }
51
52 @Override
53 public Objective createNew() {
54 return this;
55 }
56
57}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
13
14import java.util.Comparator;
15import java.util.Objects;
16
17/**
18 * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name,
19 * comparator, level and fitness hard constraint.
20 *
21 * @author Andras Szabolcs Nagy
22 *
23 */
24public abstract class BaseObjective implements Objective {
25
26 protected final String name;
27 protected Comparator<Double> comparator = Comparators.HIGHER_IS_BETTER;
28
29 protected double fitnessConstraint;
30 protected boolean isThereFitnessConstraint = false;
31 protected Comparator<Double> fitnessConstraintComparator;
32
33 protected BaseObjective(String name) {
34 Objects.requireNonNull(name, "Name of the objective cannot be null.");
35 this.name = name;
36 }
37
38 @Override
39 public String getName() {
40 return name;
41 }
42
43 @Override
44 public void setComparator(Comparator<Double> comparator) {
45 this.comparator = comparator;
46 }
47
48 @Override
49 public Comparator<Double> getComparator() {
50 return comparator;
51 }
52
53 public BaseObjective withComparator(Comparator<Double> comparator) {
54 setComparator(comparator);
55 return this;
56 }
57
58 /**
59 * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the
60 * current state as a solution.
61 *
62 * @param fitnessConstraint
63 * Solutions should be better than this value.
64 * @param fitnessConstraintComparator
65 * {@link Comparator} to determine if the current state is better than the given value.
66 * @return The actual instance to enable builder pattern like usage.
67 */
68 public BaseObjective withHardConstraintOnFitness(double fitnessConstraint,
69 Comparator<Double> fitnessConstraintComparator) {
70 this.fitnessConstraint = fitnessConstraint;
71 this.fitnessConstraintComparator = fitnessConstraintComparator;
72 this.isThereFitnessConstraint = true;
73 return this;
74 }
75
76 /**
77 * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the
78 * current state as a solution. The provided comparator will be used.
79 *
80 * @param fitnessConstraint
81 * Solutions should be better than this value.
82 * @return The actual instance to enable builder pattern like usage.
83 */
84 public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) {
85 return withHardConstraintOnFitness(fitnessConstraint, null);
86 }
87
88 @Override
89 public void init(DesignSpaceExplorationAdapter context) {
90 if (fitnessConstraintComparator == null) {
91 fitnessConstraintComparator = comparator;
92 }
93 }
94
95 @Override
96 public boolean isHardObjective() {
97 return isThereFitnessConstraint;
98 }
99
100 @Override
101 public boolean satisfiesHardObjective(Double fitness) {
102 if (isThereFitnessConstraint) {
103 int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint);
104 if (compare < 0) {
105 return false;
106 }
107 }
108 return true;
109 }
110
111 @Override
112 public int hashCode() {
113 return name.hashCode();
114 }
115
116 @Override
117 public boolean equals(Object obj) {
118 if (this == obj) {
119 return true;
120 }
121 if (obj instanceof BaseObjective baseObjective) {
122 return name.equals(baseObjective.getName());
123 }
124 return false;
125 }
126
127 @Override
128 public String toString() {
129 return name;
130 }
131
132}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import java.util.Comparator;
13
14public class Comparators {
15
16 private Comparators() { /*Utility class constructor*/ }
17
18 public static final Comparator<Double> HIGHER_IS_BETTER = Double::compareTo;
19
20 public static final Comparator<Double> LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1);
21
22 private static final Double ZERO = (double) 0;
23
24 public static final Comparator<Double> DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2));
25
26}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import java.util.HashMap;
13
14public class Fitness extends HashMap<String, Double> {
15
16 private boolean satisfiesHardObjectives;
17
18 public boolean isSatisfiesHardObjectives() {
19 return satisfiesHardObjectives;
20 }
21
22 public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) {
23 this.satisfiesHardObjectives = satisfiesHardObjectives;
24 }
25
26 @Override
27 public String toString() {
28 return super.toString() + " hardObjectives=" + satisfiesHardObjectives;
29 }
30
31 @Override
32 public boolean equals(Object other) {
33 if (other == null) return false;
34 if (getClass() != other.getClass()) return false;
35 if (!super.equals(other)) return false;
36 return satisfiesHardObjectives == ((Fitness) other).satisfiesHardObjectives;
37 }
38
39 @Override
40 public int hashCode() {
41 int h = super.hashCode();
42 h = h * 31 + (satisfiesHardObjectives ? 1 : 0);
43 return h;
44 }
45
46}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
13
14import java.util.Comparator;
15
16/**
17 *
18 * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution
19 * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated
20 * value.
21 * <p>
22 * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard
23 * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution.
24 * <p>
25 * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary
26 * for every new thread, and the {@code createNew} method should not return the same instance more than once.
27 *
28 * @author Andras Szabolcs Nagy
29 *
30 */
31public interface Objective {
32
33 /**
34 * Returns the name of the objective.
35 *
36 * @return The name of the objective.
37 */
38 String getName();
39
40 /**
41 * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to
42 * minimize or maximize (or minimize or maximize a delta from a given number).
43 *
44 * @param comparator The comparator.
45 */
46 void setComparator(Comparator<Double> comparator);
47
48 /**
49 * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is
50 * to minimize or maximize (or minimize or maximize a delta from a given number).
51 *
52 * @return The comparator.
53 */
54 Comparator<Double> getComparator();
55
56 /**
57 * Calculates the value of the objective on a given solution (trajectory).
58 *
59 * @param context
60 * The {@link DesignSpaceExplorationAdapter}
61 * @return The objective value in double.
62 */
63 Double getFitness(DesignSpaceExplorationAdapter context);
64
65 /**
66 * Initializes the objective. It is called exactly once for every thread starts.
67 *
68 * @param context
69 * The {@link DesignSpaceExplorationAdapter}.
70 */
71 void init(DesignSpaceExplorationAdapter context);
72
73 /**
74 * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread
75 * save as they are called concurrently.
76 *
77 * @return An instance of the objective.
78 */
79 Objective createNew();
80
81 /**
82 * Returns true if the objective is a hard objective. In such a case the method
83 * {@link Objective#satisfiesHardObjective(Double)} is called.
84 *
85 * @return True if the objective is a hard objective.
86 * @see Objective#satisfiesHardObjective(Double)
87 * @see Objective
88 */
89 boolean isHardObjective();
90
91 /**
92 * Determines if the given fitness value satisfies the hard objective.
93 *
94 * @param fitness
95 * The fitness value of a solution.
96 * @return True if it satisfies the hard objective or it is a soft constraint.
97 * @see Objective
98 */
99 boolean satisfiesHardObjective(Double fitness);
100
101}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.objectives;
11
12import java.util.List;
13
14/**
15 * This class is responsible to compare and sort fitness values.
16 *
17 * @author András Szabolcs Nagy
18 */
19public class ObjectiveComparatorHelper {
20
21 private final List<Objective> objectives;
22
23 public ObjectiveComparatorHelper(List<Objective> objectives) {
24 this.objectives = objectives;
25 }
26
27 /**
28 * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better
29 * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates
30 * {@code o2}) and returns 0 if they are non-dominating each other.
31 */
32 public int compare(Fitness o1, Fitness o2) {
33
34 boolean o1HasBetterFitness = false;
35 boolean o2HasBetterFitness = false;
36
37 for (Objective objective : objectives) {
38 String objectiveName = objective.getName();
39 int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName));
40
41 if (sgn < 0) {
42 o2HasBetterFitness = true;
43 }
44 if (sgn > 0) {
45 o1HasBetterFitness = true;
46 }
47 if (o1HasBetterFitness && o2HasBetterFitness) {
48 break;
49 }
50 }
51 if (o2HasBetterFitness) {
52 return -1;
53 } else if (o1HasBetterFitness) {
54 return 1;
55 }
56
57 return 0;
58
59 }
60}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8@FunctionalInterface
9public interface BoundPropagator {
10 PropagationResult propagateOne();
11}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.dse.propagation.impl.PropagationBuilderImpl;
10
11public interface PropagationAdapter extends ModelAdapter {
12 @Override
13 PropagationStoreAdapter getStoreAdapter();
14
15 PropagationResult propagate();
16
17 static PropagationBuilder builder() {
18 return new PropagationBuilderImpl();
19 }
20}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8import tools.refinery.store.adapter.ModelAdapterBuilder;
9import tools.refinery.store.dse.transition.Rule;
10import tools.refinery.store.model.ModelStore;
11
12import java.util.Collection;
13import java.util.List;
14
15@SuppressWarnings("UnusedReturnValue")
16public interface PropagationBuilder extends ModelAdapterBuilder {
17 PropagationBuilder rule(Rule propagationRule);
18
19 default PropagationBuilder rules(Rule... propagationRules) {
20 return rules(List.of(propagationRules));
21 }
22
23 default PropagationBuilder rules(Collection<Rule> propagationRules) {
24 propagationRules.forEach(this::rule);
25 return this;
26 }
27
28 PropagationBuilder propagator(Propagator propagator);
29
30 @Override
31 PropagationStoreAdapter build(ModelStore store);
32}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8public enum PropagationResult {
9 UNCHANGED,
10 PROPAGATED,
11 REJECTED;
12
13 public PropagationResult andThen(PropagationResult next) {
14 return switch (this) {
15 case UNCHANGED -> next;
16 case PROPAGATED -> next == REJECTED ? REJECTED : PROPAGATED;
17 case REJECTED -> REJECTED;
18 };
19 }
20
21 public boolean isRejected() {
22 return this == REJECTED;
23 }
24
25 public boolean isChanged() {
26 return this == PROPAGATED;
27 }
28}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9import tools.refinery.store.model.Model;
10
11public interface PropagationStoreAdapter extends ModelStoreAdapter {
12 @Override
13 PropagationAdapter createModelAdapter(Model model);
14}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStoreBuilder;
10
11@FunctionalInterface
12public interface Propagator {
13 default void configure(ModelStoreBuilder storeBuilder) {
14 }
15
16 BoundPropagator bindToModel(Model model);
17}
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..fdd19217
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java
@@ -0,0 +1,75 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl;
7
8import tools.refinery.store.dse.propagation.BoundPropagator;
9import tools.refinery.store.dse.propagation.PropagationAdapter;
10import tools.refinery.store.dse.propagation.PropagationResult;
11import tools.refinery.store.dse.propagation.PropagationStoreAdapter;
12import tools.refinery.store.model.Model;
13
14class PropagationAdapterImpl implements PropagationAdapter {
15 private final Model model;
16 private final PropagationStoreAdapterImpl storeAdapter;
17 private final BoundPropagator[] boundPropagators;
18
19 public PropagationAdapterImpl(Model model, PropagationStoreAdapterImpl storeAdapter) {
20 this.model = model;
21 this.storeAdapter = storeAdapter;
22 var propagators = storeAdapter.getPropagators();
23 boundPropagators = new BoundPropagator[propagators.size()];
24 for (int i = 0; i < boundPropagators.length; i++) {
25 boundPropagators[i] = propagators.get(i).bindToModel(model);
26 }
27 }
28
29 @Override
30 public PropagationResult propagate() {
31 PropagationResult result = PropagationResult.UNCHANGED;
32 PropagationResult lastResult;
33 do {
34 model.checkCancelled();
35 lastResult = propagateOne();
36 result = result.andThen(lastResult);
37 } while (lastResult.isChanged());
38 return result;
39 }
40
41 private PropagationResult propagateOne() {
42 PropagationResult result = PropagationResult.UNCHANGED;
43 for (int i = 0; i < boundPropagators.length; i++) {
44 model.checkCancelled();
45 var lastResult = propagateUntilFixedPoint(i);
46 result = result.andThen(lastResult);
47 if (result.isRejected()) {
48 break;
49 }
50 }
51 return result;
52 }
53
54 private PropagationResult propagateUntilFixedPoint(int propagatorIndex) {
55 var propagator = boundPropagators[propagatorIndex];
56 PropagationResult result = PropagationResult.UNCHANGED;
57 PropagationResult lastResult;
58 do {
59 model.checkCancelled();
60 lastResult = propagator.propagateOne();
61 result = result.andThen(lastResult);
62 } while (lastResult.isChanged());
63 return result;
64 }
65
66 @Override
67 public Model getModel() {
68 return model;
69 }
70
71 @Override
72 public PropagationStoreAdapter getStoreAdapter() {
73 return storeAdapter;
74 }
75}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl;
7
8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.dse.propagation.PropagationBuilder;
10import tools.refinery.store.dse.propagation.PropagationStoreAdapter;
11import tools.refinery.store.dse.propagation.Propagator;
12import tools.refinery.store.dse.propagation.impl.rule.RuleBasedPropagator;
13import tools.refinery.store.dse.transition.Rule;
14import tools.refinery.store.model.ModelStore;
15import tools.refinery.store.model.ModelStoreBuilder;
16
17import java.util.*;
18
19public class PropagationBuilderImpl extends AbstractModelAdapterBuilder<PropagationStoreAdapter>
20 implements PropagationBuilder {
21 private final Set<Rule> propagationRules = new LinkedHashSet<>();
22 private final Deque<Propagator> propagators = new ArrayDeque<>();
23
24 @Override
25 public PropagationBuilder rule(Rule propagationRule) {
26 checkNotConfigured();
27 propagationRules.add(propagationRule);
28 return this;
29 }
30
31 @Override
32 public PropagationBuilder propagator(Propagator propagator) {
33 checkNotConfigured();
34 propagators.addFirst(propagator);
35 return this;
36 }
37
38 @Override
39 protected void doConfigure(ModelStoreBuilder storeBuilder) {
40 super.doConfigure(storeBuilder);
41 if (!propagationRules.isEmpty()) {
42 propagators.addFirst(new RuleBasedPropagator(List.copyOf(propagationRules)));
43 }
44 for (var propagator : propagators) {
45 propagator.configure(storeBuilder);
46 }
47 }
48
49 @Override
50 protected PropagationStoreAdapter doBuild(ModelStore store) {
51 return new PropagationStoreAdapterImpl(store, List.copyOf(propagators));
52 }
53}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl;
7
8import tools.refinery.store.dse.propagation.PropagationAdapter;
9import tools.refinery.store.dse.propagation.PropagationStoreAdapter;
10import tools.refinery.store.dse.propagation.Propagator;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.ModelStore;
13
14import java.util.List;
15
16class PropagationStoreAdapterImpl implements PropagationStoreAdapter {
17 private final ModelStore store;
18 private final List<Propagator> propagators;
19
20 PropagationStoreAdapterImpl(ModelStore store, List<Propagator> propagators) {
21 this.store = store;
22 this.propagators = propagators;
23 }
24
25 @Override
26 public ModelStore getStore() {
27 return store;
28 }
29
30 @Override
31 public PropagationAdapter createModelAdapter(Model model) {
32 return new PropagationAdapterImpl(model, this);
33 }
34
35 List<Propagator> getPropagators() {
36 return propagators;
37 }
38}
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..a70292ad
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java
@@ -0,0 +1,40 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl.rule;
7
8import tools.refinery.store.dse.propagation.PropagationResult;
9import tools.refinery.store.dse.transition.Rule;
10import tools.refinery.store.dse.transition.actions.BoundAction;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.ModelQueryAdapter;
13import tools.refinery.store.query.resultset.ResultSet;
14
15class BoundPropagationRule {
16 private final Model model;
17 private final ResultSet<Boolean> resultSet;
18 private final BoundAction action;
19
20 public BoundPropagationRule(Model model, Rule rule) {
21 this.model = model;
22 resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(rule.getPrecondition());
23 action = rule.createAction(model);
24 }
25
26 public PropagationResult fireAll() {
27 if (resultSet.size() == 0) {
28 return PropagationResult.UNCHANGED;
29 }
30 var cursor = resultSet.getAll();
31 while (cursor.move()) {
32 model.checkCancelled();
33 var result = action.fire(cursor.getKey());
34 if (!result) {
35 return PropagationResult.REJECTED;
36 }
37 }
38 return PropagationResult.PROPAGATED;
39 }
40}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl.rule;
7
8import tools.refinery.store.dse.propagation.BoundPropagator;
9import tools.refinery.store.dse.propagation.PropagationResult;
10import tools.refinery.store.dse.transition.Rule;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.ModelQueryAdapter;
13
14import java.util.List;
15
16public class BoundRuleBasedPropagator implements BoundPropagator {
17 private final ModelQueryAdapter queryEngine;
18 private final BoundPropagationRule[] boundRules;
19
20 public BoundRuleBasedPropagator(Model model, List<Rule> propagationRules) {
21 queryEngine = model.getAdapter(ModelQueryAdapter.class);
22 boundRules = new BoundPropagationRule[propagationRules.size()];
23 for (int i = 0; i < boundRules.length; i++) {
24 boundRules[i] = new BoundPropagationRule(model, propagationRules.get(i));
25 }
26 }
27
28 @Override
29 public PropagationResult propagateOne() {
30 queryEngine.flushChanges();
31 PropagationResult result = PropagationResult.UNCHANGED;
32 // Use a classic for loop to avoid allocating an iterator.
33 //noinspection ForLoopReplaceableByForEach
34 for (int i = 0; i < boundRules.length; i++) {
35 var lastResult = boundRules[i].fireAll();
36 result = result.andThen(lastResult);
37 if (result.isRejected()) {
38 break;
39 }
40 }
41 return result;
42 }
43}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.propagation.impl.rule;
7
8import tools.refinery.store.dse.propagation.BoundPropagator;
9import tools.refinery.store.dse.propagation.Propagator;
10import tools.refinery.store.dse.transition.Rule;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.ModelStoreBuilder;
13import tools.refinery.store.query.ModelQueryBuilder;
14
15import java.util.List;
16
17public class RuleBasedPropagator implements Propagator {
18 private final List<Rule> propagationRules;
19
20 public RuleBasedPropagator(List<Rule> propagationRules) {
21 this.propagationRules = propagationRules;
22 }
23
24 @Override
25 public void configure(ModelStoreBuilder storeBuilder) {
26 var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class);
27 for (var propagationRule : propagationRules) {
28 queryBuilder.query(propagationRule.getPrecondition());
29 }
30 }
31
32 @Override
33 public BoundPropagator bindToModel(Model model) {
34 return new BoundRuleBasedPropagator(model, propagationRules);
35 }
36}
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..5e2f8fa9
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java
@@ -0,0 +1,65 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.strategy;
7
8import tools.refinery.store.model.Model;
9
10import java.util.Random;
11
12public class BestFirstExplorer extends BestFirstWorker {
13 final int id;
14 Random random;
15
16 public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) {
17 super(storeManager, model);
18 this.id = id;
19 this.random = new Random(id);
20 }
21
22 private boolean shouldRun() {
23 model.checkCancelled();
24 return !hasEnoughSolution();
25 }
26
27 public void explore() {
28 var lastBest = submit().newVersion();
29 while (shouldRun()) {
30 if (lastBest == null) {
31 if (random.nextInt(10) == 0) {
32 lastBest = restoreToRandom(random);
33 } else {
34 lastBest = restoreToBest();
35 }
36 if (lastBest == null) {
37 return;
38 }
39 }
40 boolean tryActivation = true;
41 while (tryActivation && shouldRun()) {
42 var randomVisitResult = this.visitRandomUnvisited(random);
43 tryActivation = randomVisitResult.shouldRetry();
44 var newSubmit = randomVisitResult.submitResult();
45 if (newSubmit != null) {
46 if (!newSubmit.include()) {
47 restoreToLast();
48 } else {
49 var newVisit = newSubmit.newVersion();
50 int compareResult = compare(lastBest, newVisit);
51 if (compareResult >= 0) {
52 lastBest = newVisit;
53 } else {
54 lastBest = null;
55 }
56 break;
57 }
58 } else {
59 lastBest = null;
60 break;
61 }
62 }
63 }
64 }
65}
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..3d32f84c
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java
@@ -0,0 +1,87 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.strategy;
7
8import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter;
9import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
10import tools.refinery.store.dse.transition.statespace.ActivationStore;
11import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore;
12import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue;
13import tools.refinery.store.dse.transition.statespace.SolutionStore;
14import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreImpl;
15import tools.refinery.store.dse.transition.statespace.internal.FastEquivalenceClassStore;
16import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriorityQueueImpl;
17import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl;
18import tools.refinery.store.map.Version;
19import tools.refinery.store.model.ModelStore;
20import tools.refinery.store.statecoding.StateCoderStoreAdapter;
21import tools.refinery.visualization.statespace.VisualizationStore;
22import tools.refinery.visualization.statespace.internal.VisualizationStoreImpl;
23
24import java.util.function.Consumer;
25
26public class BestFirstStoreManager {
27
28 ModelStore modelStore;
29 ObjectivePriorityQueue objectiveStore;
30 ActivationStore activationStore;
31 SolutionStore solutionStore;
32 EquivalenceClassStore equivalenceClassStore;
33 VisualizationStore visualizationStore;
34
35 public BestFirstStoreManager(ModelStore modelStore, int maxNumberOfSolutions) {
36 this.modelStore = modelStore;
37 DesignSpaceExplorationStoreAdapter storeAdapter =
38 modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class);
39
40 objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives());
41 Consumer<VersionWithObjectiveValue> whenAllActivationsVisited = x -> objectiveStore.remove(x);
42 activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited);
43 solutionStore = new SolutionStoreImpl(maxNumberOfSolutions);
44 equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) {
45 @Override
46 protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) {
47 throw new UnsupportedOperationException("This equivalence storage is not prepared to resolve " +
48 "symmetries!");
49 }
50 };
51 visualizationStore = new VisualizationStoreImpl();
52 }
53
54 public ModelStore getModelStore() {
55 return modelStore;
56 }
57
58 ObjectivePriorityQueue getObjectiveStore() {
59 return objectiveStore;
60 }
61
62 ActivationStore getActivationStore() {
63 return activationStore;
64 }
65
66 public SolutionStore getSolutionStore() {
67 return solutionStore;
68 }
69
70 EquivalenceClassStore getEquivalenceClassStore() {
71 return equivalenceClassStore;
72 }
73
74 public VisualizationStore getVisualizationStore() {
75 return visualizationStore;
76 }
77
78 public void startExploration(Version initial) {
79 startExploration(initial, 1);
80 }
81
82 public void startExploration(Version initial, int randomSeed) {
83 BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial),
84 randomSeed);
85 bestFirstExplorer.explore();
86 }
87}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.store.dse.strategy;
11
12import tools.refinery.store.map.Version;
13import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
14import tools.refinery.store.dse.Strategy;
15import tools.refinery.store.dse.internal.Activation;
16import tools.refinery.store.dse.objectives.Fitness;
17import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper;
18
19import java.util.*;
20
21public class BestFirstStrategy implements Strategy {
22
23 private DesignSpaceExplorationAdapter dseAdapter;
24
25 private int maxDepth = Integer.MAX_VALUE;
26 private int maxSolutions = Integer.MAX_VALUE;
27 private boolean backTrackIfSolution = true;
28 private boolean onlyBetterFirst = false;
29
30 private PriorityQueue<TrajectoryWithFitness> trajectoriesToExplore;
31
32 private record TrajectoryWithFitness(List<Version> trajectory, Fitness fitness) {
33 @Override
34 public String toString() {
35 return trajectory.toString() + fitness.toString();
36 }
37
38 @Override
39 public int hashCode() {
40 return trajectory.get(trajectory.size() - 1).hashCode();
41 }
42
43 @Override
44 public boolean equals(Object obj) {
45 if (obj instanceof TrajectoryWithFitness other) {
46 return Objects.equals(trajectory.get(trajectory.size() - 1), other.trajectory.get(other.trajectory.size() - 1));
47// return trajectory.equals(((TrajectoryWithFitness) obj).trajectory);
48 }
49 return false;
50 }
51 }
52
53 public BestFirstStrategy withDepthLimit(int maxDepth) {
54 if (maxDepth >= 0) {
55 this.maxDepth = maxDepth;
56 }
57 return this;
58 }
59
60 public BestFirstStrategy withSolutionLimit(int maxSolutions) {
61 if (maxSolutions >= 0) {
62 this.maxSolutions = maxSolutions;
63 }
64 return this;
65 }
66
67 public BestFirstStrategy continueIfHardObjectivesFulfilled() {
68 backTrackIfSolution = false;
69 return this;
70 }
71
72 public BestFirstStrategy goOnOnlyIfFitnessIsBetter() {
73 onlyBetterFirst = true;
74 return this;
75 }
76
77 @Override
78 public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) {
79 this.dseAdapter = designSpaceExplorationAdapter;
80 final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper();
81
82 trajectoriesToExplore = new PriorityQueue<>(11,
83 (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness));
84 }
85
86 @Override
87 public void explore() {
88 if (maxSolutions == 0) {
89 return;
90 }
91 final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper();
92
93 boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints();
94 if (!globalConstraintsAreSatisfied) {
95 // Global constraint is not satisfied in the first state. Terminate.
96 return;
97 }
98
99 final Fitness firstFitness = dseAdapter.getFitness();
100 if (firstFitness.isSatisfiesHardObjectives()) {
101 dseAdapter.newSolution();
102 // First state is a solution. Terminate.
103 if (backTrackIfSolution) {
104 return;
105 }
106 }
107
108 if (maxDepth == 0) {
109 return;
110 }
111
112
113 var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness);
114 trajectoriesToExplore.add(firstTrajectoryWithFitness);
115 TrajectoryWithFitness currentTrajectoryWithFitness = null;
116
117 mainLoop: while (true) {
118
119 if (currentTrajectoryWithFitness == null) {
120 if (trajectoriesToExplore.isEmpty()) {
121 // State space is fully traversed.
122 return;
123 } else {
124 currentTrajectoryWithFitness = trajectoriesToExplore.element();
125 // New trajectory is chosen: " + currentTrajectoryWithFitness
126 dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory);
127 }
128 }
129
130 Collection<Activation> activations = dseAdapter.getUntraversedActivations();
131 Iterator<Activation> iterator = activations.iterator();
132
133
134
135 while (iterator.hasNext()) {
136 final Activation nextActivation = iterator.next();
137 if (!iterator.hasNext()) {
138 // Last untraversed activation of the state.
139 trajectoriesToExplore.remove(currentTrajectoryWithFitness);
140 }
141
142 // Executing new activation
143 dseAdapter.fireActivation(nextActivation);
144 if (dseAdapter.isCurrentStateAlreadyTraversed()) {
145 // The new state is already visited.
146 dseAdapter.backtrack();
147 } else if (!dseAdapter.checkGlobalConstraints()) {
148 // Global constraint is not satisfied.
149 dseAdapter.backtrack();
150 } else {
151 final Fitness nextFitness = dseAdapter.getFitness();
152 if (nextFitness.isSatisfiesHardObjectives()) {
153 dseAdapter.newSolution();
154 var solutions = dseAdapter.getSolutions().size();
155 if (solutions >= maxSolutions) {
156 return;
157 }
158 // Found a solution.
159 if (backTrackIfSolution) {
160 dseAdapter.backtrack();
161 continue;
162 }
163 }
164 if (dseAdapter.getDepth() >= maxDepth) {
165 // Reached max depth.
166 dseAdapter.backtrack();
167 continue;
168 }
169
170 TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness(
171 dseAdapter.getTrajectory(), nextFitness);
172 trajectoriesToExplore.add(nextTrajectoryWithFitness);
173
174 int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness,
175 nextTrajectoryWithFitness.fitness);
176 if (compare < 0) {
177 // Better fitness, moving on
178 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
179 continue mainLoop;
180 } else if (compare == 0) {
181 if (onlyBetterFirst) {
182 // Equally good fitness, backtrack
183 dseAdapter.backtrack();
184 } else {
185 // Equally good fitness, moving on
186 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
187 continue mainLoop;
188 }
189 } else {
190 //"Worse fitness
191 currentTrajectoryWithFitness = null;
192 continue mainLoop;
193 }
194 }
195 }
196
197 // State is fully traversed.
198 currentTrajectoryWithFitness = null;
199
200 }
201 // Interrupted.
202 }
203}
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..aca800a3
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java
@@ -0,0 +1,164 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.strategy;
7
8import org.jetbrains.annotations.Nullable;
9import tools.refinery.store.dse.propagation.PropagationAdapter;
10import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter;
11import tools.refinery.store.dse.transition.ObjectiveValue;
12import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
13import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreWorker;
14import tools.refinery.store.map.Version;
15import tools.refinery.store.model.Model;
16import tools.refinery.store.query.ModelQueryAdapter;
17import tools.refinery.store.statecoding.StateCoderAdapter;
18import tools.refinery.visualization.statespace.VisualizationStore;
19
20import java.util.Random;
21
22public class BestFirstWorker {
23 final BestFirstStoreManager storeManager;
24 final Model model;
25 final ActivationStoreWorker activationStoreWorker;
26 final StateCoderAdapter stateCoderAdapter;
27 final DesignSpaceExplorationAdapter explorationAdapter;
28 final ModelQueryAdapter queryAdapter;
29 final @Nullable PropagationAdapter propagationAdapter;
30 final VisualizationStore visualizationStore;
31 final boolean isVisualizationEnabled;
32
33 public BestFirstWorker(BestFirstStoreManager storeManager, Model model) {
34 this.storeManager = storeManager;
35 this.model = model;
36
37 explorationAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
38 stateCoderAdapter = model.getAdapter(StateCoderAdapter.class);
39 queryAdapter = model.getAdapter(ModelQueryAdapter.class);
40 propagationAdapter = model.tryGetAdapter(PropagationAdapter.class).orElse(null);
41 activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(),
42 explorationAdapter.getTransformations());
43 visualizationStore = storeManager.getVisualizationStore();
44 isVisualizationEnabled = visualizationStore != null;
45 }
46
47 protected VersionWithObjectiveValue last = null;
48
49 public SubmitResult submit() {
50 checkSynchronized();
51 if (queryAdapter.hasPendingChanges()) {
52 throw new AssertionError("Pending changes detected before model submission");
53 }
54 if (explorationAdapter.checkExclude()) {
55 return new SubmitResult(false, false, null, null);
56 }
57
58 var code = stateCoderAdapter.calculateStateCode();
59
60 boolean isNew = storeManager.getEquivalenceClassStore().submit(code);
61 if (isNew) {
62 Version version = model.commit();
63 ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue();
64 var versionWithObjectiveValue = new VersionWithObjectiveValue(version, objectiveValue);
65 last = versionWithObjectiveValue;
66 var accepted = explorationAdapter.checkAccept();
67
68 storeManager.getObjectiveStore().submit(versionWithObjectiveValue);
69 storeManager.getActivationStore().markNewAsVisited(versionWithObjectiveValue, activationStoreWorker.calculateEmptyActivationSize());
70 if(accepted) {
71 storeManager.solutionStore.submit(versionWithObjectiveValue);
72 }
73
74 if (isVisualizationEnabled) {
75 visualizationStore.addState(last.version(), last.objectiveValue().toString());
76 if (accepted) {
77 visualizationStore.addSolution(last.version());
78 }
79 }
80
81 return new SubmitResult(true, accepted, objectiveValue, last);
82 }
83
84 return new SubmitResult(false, false, null, null);
85 }
86
87 public void restoreToLast() {
88 if (explorationAdapter.getModel().hasUncommittedChanges()) {
89 explorationAdapter.getModel().restore(last.version());
90 }
91 }
92
93 public VersionWithObjectiveValue restoreToBest() {
94 var bestVersion = storeManager.getObjectiveStore().getBest();
95 last = bestVersion;
96 if (bestVersion != null) {
97 this.model.restore(bestVersion.version());
98 }
99 return last;
100 }
101
102 public VersionWithObjectiveValue restoreToRandom(Random random) {
103 var objectiveStore = storeManager.getObjectiveStore();
104 if (objectiveStore.getSize() == 0) {
105 return null;
106 }
107 var randomVersion = objectiveStore.getRandom(random);
108 last = randomVersion;
109 if (randomVersion != null) {
110 this.model.restore(randomVersion.version());
111 }
112 return last;
113 }
114
115 public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) {
116 return storeManager.getObjectiveStore().getComparator().compare(s1, s2);
117 }
118
119 public record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) {
120 }
121
122 public RandomVisitResult visitRandomUnvisited(Random random) {
123 checkSynchronized();
124 if (model.hasUncommittedChanges()) {
125 throw new IllegalStateException("The model has uncommitted changes!");
126 }
127
128 var visitResult = activationStoreWorker.fireRandomActivation(this.last, random);
129
130 if (!visitResult.successfulVisit()) {
131 return new RandomVisitResult(null, visitResult.mayHaveMore());
132 }
133
134 if (propagationAdapter != null) {
135 var propagationResult = propagationAdapter.propagate();
136 if (propagationResult.isRejected()) {
137 return new RandomVisitResult(null, visitResult.mayHaveMore());
138 }
139 }
140 queryAdapter.flushChanges();
141
142 Version oldVersion = null;
143 if (isVisualizationEnabled) {
144 oldVersion = last.version();
145 }
146 var submitResult = submit();
147 if (isVisualizationEnabled && submitResult.newVersion() != null) {
148 var newVersion = submitResult.newVersion().version();
149 visualizationStore.addTransition(oldVersion, newVersion,
150 "fire: " + visitResult.transformation() + ", " + visitResult.activation());
151 }
152 return new RandomVisitResult(submitResult, visitResult.mayHaveMore());
153 }
154
155 public boolean hasEnoughSolution() {
156 return storeManager.solutionStore.hasEnoughSolution();
157 }
158
159 private void checkSynchronized() {
160 if (last != null && !last.version().equals(model.getState())) {
161 throw new AssertionError("Worker is not synchronized with model state");
162 }
163 }
164}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.strategy;
7
8import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
9import tools.refinery.store.dse.Strategy;
10import tools.refinery.store.dse.objectives.Fitness;
11
12public class DepthFirstStrategy implements Strategy {
13
14 private DesignSpaceExplorationAdapter dseAdapter;
15
16 private int maxDepth = Integer.MAX_VALUE;
17 private int maxSolutions = Integer.MAX_VALUE;
18 private boolean backtrackFromSolution = true;
19
20 public DepthFirstStrategy withDepthLimit(int maxDepth) {
21 if (maxDepth >= 0) {
22 this.maxDepth = maxDepth;
23 }
24 return this;
25 }
26
27 public DepthFirstStrategy withSolutionLimit(int maxSolutions) {
28 if (maxSolutions >= 0) {
29 this.maxSolutions = maxSolutions;
30 }
31 return this;
32 }
33
34 public DepthFirstStrategy continueIfHardObjectivesFulfilled() {
35 backtrackFromSolution = false;
36 return this;
37 }
38
39 @Override
40 public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) {
41 this.dseAdapter = designSpaceExplorationAdapter;
42 }
43
44 @Override
45 public void explore() {
46 if (maxSolutions == 0) {
47 return;
48 }
49 while (dseAdapter.getSolutions().size() < maxSolutions) {
50 if (!checkAndHandleGlobalConstraints()) {
51 return;
52 }
53
54 Fitness fitness = dseAdapter.getFitness();
55 if (fitness.isSatisfiesHardObjectives()) {
56 dseAdapter.newSolution();
57 if (backtrackFromSolution && !dseAdapter.backtrack()) {
58 return;
59 }
60 }
61
62 if (!checkAndHandleDepth()) {
63 return;
64 }
65
66 if (!backtrackToLastUntraversed()) {
67 return;
68 }
69
70 if (!dseAdapter.fireRandomActivation()) {
71 return;
72 }
73 }
74 }
75
76 private boolean checkAndHandleGlobalConstraints() {
77 return dseAdapter.checkGlobalConstraints() || dseAdapter.backtrack();
78 }
79
80 private boolean checkAndHandleDepth() {
81 return dseAdapter.getDepth() < maxDepth || dseAdapter.backtrack();
82 }
83
84 private boolean backtrackToLastUntraversed() {
85 while (dseAdapter.getUntraversedActivations().isEmpty()) {
86 if (!dseAdapter.backtrack()) {
87 return false;
88 }
89 }
90 return true;
91 }
92}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.strategy;
7
8import tools.refinery.store.dse.transition.ObjectiveValue;
9import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
10import tools.refinery.store.map.Version;
11
12public record SubmitResult(boolean include, boolean accepted, ObjectiveValue objective, VersionWithObjectiveValue newVersion) {
13
14 }
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..d326f1dd
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java
@@ -0,0 +1,28 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.dse.transition.internal.DesignSpaceExplorationBuilderImpl;
10
11import java.util.List;
12
13public interface DesignSpaceExplorationAdapter extends ModelAdapter {
14 @Override
15 DesignSpaceExplorationStoreAdapter getStoreAdapter();
16
17 static DesignSpaceExplorationBuilder builder() {
18 return new DesignSpaceExplorationBuilderImpl();
19 }
20
21 List<Transformation> getTransformations();
22
23 boolean checkAccept();
24
25 boolean checkExclude();
26
27 ObjectiveValue getObjectiveValue();
28}
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..800cf8f7
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java
@@ -0,0 +1,61 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.adapter.ModelAdapterBuilder;
9import tools.refinery.store.dse.transition.objectives.Criterion;
10import tools.refinery.store.dse.transition.objectives.Objective;
11
12import java.util.Collection;
13import java.util.List;
14
15// Builder pattern with methods returning {@code this} for convenience.
16@SuppressWarnings("UnusedReturnValue")
17public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder {
18 DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition);
19
20 default DesignSpaceExplorationBuilder transformations(Rule... transformationRuleDefinitions) {
21 return transformations(List.of(transformationRuleDefinitions));
22 }
23
24 default DesignSpaceExplorationBuilder transformations(Collection<? extends Rule> transformationRules) {
25 transformationRules.forEach(this::transformation);
26 return this;
27 }
28
29 DesignSpaceExplorationBuilder accept(Criterion criteria);
30
31 default DesignSpaceExplorationBuilder accept(Criterion... criteria) {
32 return accept(List.of(criteria));
33 }
34
35 default DesignSpaceExplorationBuilder accept(Collection<Criterion> criteria) {
36 criteria.forEach(this::accept);
37 return this;
38 }
39
40 DesignSpaceExplorationBuilder exclude(Criterion criteria);
41
42 default DesignSpaceExplorationBuilder exclude(Criterion... criteria) {
43 return exclude(List.of(criteria));
44 }
45
46 default DesignSpaceExplorationBuilder exclude(Collection<Criterion> criteria) {
47 criteria.forEach(this::exclude);
48 return this;
49 }
50
51 DesignSpaceExplorationBuilder objective(Objective objective);
52
53 default DesignSpaceExplorationBuilder objectives(Objective... objectives) {
54 return objectives(List.of(objectives));
55 }
56
57 default DesignSpaceExplorationBuilder objectives(Collection<? extends Objective> objectives) {
58 objectives.forEach(this::objective);
59 return this;
60 }
61}
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..fb082fae
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java
@@ -0,0 +1,27 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9import tools.refinery.store.dse.transition.objectives.Criterion;
10import tools.refinery.store.dse.transition.objectives.Objective;
11import tools.refinery.store.model.Model;
12
13import java.util.List;
14
15public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter
16{
17 @Override
18 DesignSpaceExplorationAdapter createModelAdapter(Model model);
19
20 List<Rule> getTransformations();
21
22 List<Criterion> getAccepts();
23
24 List<Criterion> getExcludes();
25
26 List<Objective> getObjectives();
27}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8public interface ObjectiveValue {
9 double get(int index);
10 int getSize();
11
12 static ObjectiveValue of(double v1) {
13 return new ObjectiveValues.ObjectiveValue1(v1);
14 }
15
16 static ObjectiveValue of(double v1, double v2) {
17 return new ObjectiveValues.ObjectiveValue2(v1,v2);
18 }
19
20 static ObjectiveValue of(double[] v) {
21 return new ObjectiveValues.ObjectiveValueN(v);
22 }
23
24}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import java.util.Arrays;
9
10public interface ObjectiveValues {
11 public record ObjectiveValue1(double value0) implements ObjectiveValue {
12 @Override
13 public double get(int index) {
14 if(index == 0) return value0;
15 else throw new IllegalArgumentException("No value at " + index);
16 }
17
18 @Override
19 public int getSize() {
20 return 1;
21 }
22 }
23 public record ObjectiveValue2(double value0, double value1) implements ObjectiveValue {
24 @Override
25 public double get(int index) {
26 if(index == 0) return value0;
27 else if(index == 1) return value1;
28 else throw new IllegalArgumentException("No value at " + index);
29 }
30
31 @Override
32 public int getSize() {
33 return 2;
34 }
35 }
36 public record ObjectiveValueN(double[] values) implements ObjectiveValue {
37 @Override
38 public double get(int index) {
39 return values[index];
40 }
41
42 @Override
43 public int getSize() {
44 return values().length;
45 }
46
47 @Override
48 public boolean equals(Object o) {
49 if (this == o) return true;
50 if (o == null || getClass() != o.getClass()) return false;
51
52 ObjectiveValueN that = (ObjectiveValueN) o;
53
54 return Arrays.equals(values, that.values);
55 }
56
57 @Override
58 public int hashCode() {
59 return Arrays.hashCode(values);
60 }
61
62 @Override
63 public String toString() {
64 return "ObjectiveValueN{" +
65 "values=" + Arrays.toString(values) +
66 '}';
67 }
68 }
69}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.dse.transition.actions.Action;
9import tools.refinery.store.dse.transition.actions.BoundAction;
10import tools.refinery.store.dse.transition.callback.*;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.dnf.RelationalQuery;
13
14public class Rule {
15 private final String name;
16 private final RelationalQuery precondition;
17 private final Action action;
18
19 public Rule(String name, RelationalQuery precondition, Action action) {
20 if (precondition.arity() != action.getArity()) {
21 throw new IllegalArgumentException("Expected an action clause with %d parameters, got %d instead"
22 .formatted(precondition.arity(), action.getArity()));
23 }
24 this.name = name;
25 this.precondition = precondition;
26 this.action = action;
27 }
28
29 public String getName() {
30 return name;
31 }
32
33 public RelationalQuery getPrecondition() {
34 return precondition;
35 }
36
37 public BoundAction createAction(Model model) {
38 return action.bindToModel(model);
39 }
40
41 public static RuleBuilder builder(String name) {
42 return new RuleBuilder(name);
43 }
44
45 public static RuleBuilder builder() {
46 return builder(null);
47 }
48
49 public static Rule of(String name, RuleCallback0 callback) {
50 var builder = builder(name);
51 callback.accept(builder);
52 return builder.build();
53 }
54
55 public static Rule of(RuleCallback0 callback) {
56 return of(null, callback);
57 }
58
59 public static Rule of(String name, RuleCallback1 callback) {
60 var builder = builder(name);
61 callback.accept(builder, builder.parameter("p1"));
62 return builder.build();
63 }
64
65 public static Rule of(RuleCallback1 callback) {
66 return of(null, callback);
67 }
68
69 public static Rule of(String name, RuleCallback2 callback) {
70 var builder = builder(name);
71 callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"));
72 return builder.build();
73 }
74
75 public static Rule of(RuleCallback2 callback) {
76 return of(null, callback);
77 }
78
79 public static Rule of(String name, RuleCallback3 callback) {
80 var builder = builder(name);
81 callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3"));
82 return builder.build();
83 }
84
85 public static Rule of(RuleCallback3 callback) {
86 return of(null, callback);
87 }
88
89 public static Rule of(String name, RuleCallback4 callback) {
90 var builder = builder(name);
91 callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3"),
92 builder.parameter("p4"));
93 return builder.build();
94 }
95
96 public static Rule of(RuleCallback4 callback) {
97 return of(null, callback);
98 }
99}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.dse.transition.actions.Action;
9import tools.refinery.store.dse.transition.actions.ActionLiteral;
10import tools.refinery.store.dse.transition.callback.*;
11import tools.refinery.store.query.dnf.AbstractQueryBuilder;
12import tools.refinery.store.query.dnf.Dnf;
13import tools.refinery.store.query.term.Variable;
14
15import java.util.List;
16
17public class RuleBuilder extends AbstractQueryBuilder<RuleBuilder> {
18 private final String name;
19 private List<ActionLiteral> action;
20
21 RuleBuilder(String name) {
22 super(Dnf.builder(name == null ? null : name + "#precondition"));
23 this.name = name;
24 }
25
26 @Override
27 protected RuleBuilder self() {
28 return this;
29 }
30
31 public RuleBuilder action(ActionLiteral... literals) {
32 return action(List.of(literals));
33 }
34
35 public RuleBuilder action(List<? extends ActionLiteral> literals) {
36 if (this.action != null) {
37 throw new IllegalStateException("Actions have already been set");
38 }
39 this.action = List.copyOf(literals);
40 return this;
41 }
42
43 public RuleBuilder action(Action action) {
44 return action(action.getActionLiterals());
45 }
46
47 public RuleBuilder action(ActionCallback0 callback) {
48 return action(callback.toLiterals());
49 }
50
51 public RuleBuilder action(ActionCallback1 callback) {
52 return action(callback.toLiterals(Variable.of("v1")));
53 }
54
55 public RuleBuilder action(ActionCallback2 callback) {
56 return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2")));
57 }
58
59 public RuleBuilder action(ActionCallback3 callback) {
60 return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3")));
61 }
62
63 public RuleBuilder action(ActionCallback4 callback) {
64 return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3"), Variable.of("v4")));
65 }
66
67 public Rule build() {
68 var precondition = dnfBuilder.build().asRelation();
69 return new Rule(name, precondition, Action.ofPrecondition(precondition, action));
70 }
71}
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..0eeccbdf
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java
@@ -0,0 +1,43 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.dse.transition.actions.BoundAction;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.resultset.OrderedResultSet;
12import tools.refinery.store.query.resultset.ResultSet;
13import tools.refinery.store.tuple.Tuple;
14
15public class Transformation {
16 private final Rule definition;
17 private final OrderedResultSet<Boolean> activations;
18 private final BoundAction action;
19
20 public Transformation(Model model, Rule definition) {
21 this.definition = definition;
22 var precondition = definition.getPrecondition();
23 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
24 activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition));
25 action = definition.createAction(model);
26 }
27
28 public Rule getDefinition() {
29 return definition;
30 }
31
32 public ResultSet<Boolean> getAllActivationsAsResultSet() {
33 return activations;
34 }
35
36 public Tuple getActivation(int index) {
37 return activations.getKey(index);
38 }
39
40 public boolean fireActivation(Tuple activation) {
41 return action.fire(activation);
42 }
43}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import tools.refinery.store.map.Version;
9
10public record VersionWithObjectiveValue(Version version, ObjectiveValue objectiveValue) {
11}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8public abstract class AbstractActionLiteral implements ActionLiteral {
9}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import org.eclipse.collections.api.factory.primitive.ObjectIntMaps;
9import org.eclipse.collections.api.map.primitive.MutableObjectIntMap;
10import org.jetbrains.annotations.Nullable;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.dnf.RelationalQuery;
13import tools.refinery.store.query.dnf.SymbolicParameter;
14import tools.refinery.store.query.term.NodeVariable;
15
16import java.util.*;
17
18public class Action {
19 private final List<NodeVariable> parameters;
20 private final Set<NodeVariable> localVariables;
21 private final List<ActionLiteral> actionLiterals;
22 private final int[] @Nullable [] inputAllocations;
23 private final int[] @Nullable [] outputAllocations;
24
25 public Action(List<NodeVariable> parameters, List<? extends ActionLiteral> actionLiterals) {
26 this.parameters = List.copyOf(parameters);
27 this.actionLiterals = List.copyOf(actionLiterals);
28 var allocation = ObjectIntMaps.mutable.<NodeVariable>empty();
29 int arity = parameters.size();
30 for (int i = 0; i < arity; i++) {
31 allocation.put(parameters.get(i), i);
32 }
33 var mutableLocalVariables = new LinkedHashSet<NodeVariable>();
34 int size = actionLiterals.size();
35 inputAllocations = new int[size][];
36 outputAllocations = new int[size][];
37 for (int i = 0; i < size; i++) {
38 computeInputAllocation(i, parameters, allocation);
39 computeOutputAllocation(i, mutableLocalVariables, allocation);
40 }
41 this.localVariables = Collections.unmodifiableSet(mutableLocalVariables);
42 }
43
44 private void computeInputAllocation(int actionIndex, List<NodeVariable> parameters,
45 MutableObjectIntMap<NodeVariable> allocation) {
46 var actionLiteral = actionLiterals.get(actionIndex);
47 var inputVariables = actionLiteral.getInputVariables();
48 if (inputVariables.equals(parameters)) {
49 // Identity mappings use a {@code null} allocation to pass the activation tuple unchanged.
50 return;
51 }
52 var inputs = new int[inputVariables.size()];
53 for (int i = 0; i < inputs.length; i++) {
54 var variable = inputVariables.get(i);
55 if (!allocation.containsKey(variable)) {
56 throw new IllegalArgumentException("Unbound input variable %s of action literal %s"
57 .formatted(variable, actionLiteral));
58 }
59 inputs[i] = allocation.get(variable);
60 }
61 inputAllocations[actionIndex] = inputs;
62 }
63
64 private void computeOutputAllocation(int actionIndex, Set<NodeVariable> mutableLocalVariable,
65 MutableObjectIntMap<NodeVariable> allocation) {
66 var actionLiteral = actionLiterals.get(actionIndex);
67 var outputVariables = actionLiteral.getOutputVariables();
68 int size = outputVariables.size();
69 if (size == 0) {
70 // Identity mappings use a {@code null} allocation to avoid iterating over the output tuple.
71 return;
72 }
73 if (size >= 2 && new HashSet<>(outputVariables).size() != size) {
74 throw new IllegalArgumentException("Action literal %s has duplicate output variables %s"
75 .formatted(actionLiteral, outputVariables));
76 }
77 int arity = parameters.size();
78 var outputs = new int[size];
79 for (int i = 0; i < size; i++) {
80 var variable = outputVariables.get(i);
81 if (allocation.containsKey(variable)) {
82 throw new IllegalArgumentException("Output variable %s of action literal %s was already assigned"
83 .formatted(variable, actionLiteral));
84 }
85 int variableId = mutableLocalVariable.size();
86 allocation.put(variable, arity + variableId);
87 outputs[i] = variableId;
88 mutableLocalVariable.add(variable);
89 }
90 outputAllocations[actionIndex] = outputs;
91 }
92
93 public List<NodeVariable> getParameters() {
94 return parameters;
95 }
96
97 public int getArity() {
98 return parameters.size();
99 }
100
101 public Set<NodeVariable> getLocalVariables() {
102 return localVariables;
103 }
104
105 public List<ActionLiteral> getActionLiterals() {
106 return actionLiterals;
107 }
108
109 int @Nullable [] getInputAllocation(int actionIndex) {
110 return inputAllocations[actionIndex];
111 }
112
113 int @Nullable [] getOutputAllocation(int actionIndex) {
114 return outputAllocations[actionIndex];
115 }
116
117 public BoundAction bindToModel(Model model) {
118 return new BoundAction(this, model);
119 }
120
121 public static Action ofSymbolicParameters(List<SymbolicParameter> symbolicParameters,
122 List<? extends ActionLiteral> actionLiterals) {
123 var nodeVariables = symbolicParameters.stream()
124 .map(symbolicParameter -> symbolicParameter.getVariable().asNodeVariable())
125 .toList();
126 return new Action(nodeVariables, actionLiterals);
127 }
128
129 public static Action ofPrecondition(RelationalQuery precondition, List<? extends ActionLiteral> actionLiterals) {
130 return ofSymbolicParameters(precondition.getDnf().getSymbolicParameters(), actionLiterals);
131 }
132}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.query.term.NodeVariable;
10
11import java.util.List;
12
13public interface ActionLiteral {
14 List<NodeVariable> getInputVariables();
15
16 List<NodeVariable> getOutputVariables();
17
18 BoundActionLiteral bindToModel(Model model);
19}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import tools.refinery.store.query.term.NodeVariable;
9import tools.refinery.store.representation.Symbol;
10
11import java.util.List;
12import java.util.Objects;
13
14public final class ActionLiterals {
15 private ActionLiterals() {
16 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
17 }
18
19 public static <T> PutActionLiteral<T> put(Symbol<T> symbol, T value, NodeVariable... parameters) {
20 return new PutActionLiteral<>(symbol, value, List.of(parameters));
21 }
22
23 public static PutActionLiteral<Boolean> add(Symbol<Boolean> symbol, NodeVariable... parameters) {
24 if (!Objects.equals(symbol.defaultValue(), false)) {
25 throw new IllegalArgumentException("Use put to add a value to symbols other than two-valued logic");
26 }
27 return put(symbol, true, parameters);
28 }
29
30 public static <T> PutActionLiteral<T> remove(Symbol<T> symbol, NodeVariable... parameters) {
31 return put(symbol, symbol.defaultValue(), parameters);
32 }
33}
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..4da609fa
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java
@@ -0,0 +1,119 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import org.jetbrains.annotations.Nullable;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.tuple.Tuple;
11
12public class BoundAction {
13 private final Action action;
14 private final Model model;
15 private BoundActionLiteral @Nullable [] boundLiterals;
16 private Tuple activation;
17 private final int[] localVariables;
18
19 BoundAction(Action action, Model model) {
20 this.action = action;
21 this.model = model;
22 localVariables = new int[action.getLocalVariables().size()];
23 }
24
25 public boolean fire(Tuple activation) {
26 model.checkCancelled();
27 if (this.activation != null) {
28 throw new IllegalStateException("Reentrant firing is not allowed");
29 }
30 this.activation = activation;
31 if (boundLiterals == null) {
32 boundLiterals = bindLiterals();
33 }
34 try {
35 int size = boundLiterals.length;
36 for (int i = 0; i < size; i++) {
37 var inputAllocation = action.getInputAllocation(i);
38 var boundLiteral = boundLiterals[i];
39 var input = getInputTuple(inputAllocation);
40 var output = boundLiteral.fire(input);
41 if (output == null) {
42 return false;
43 }
44 var outputAllocation = this.action.getOutputAllocation(i);
45 setOutputTuple(outputAllocation, output);
46 }
47 } finally {
48 this.activation = null;
49 }
50 return true;
51 }
52
53 private BoundActionLiteral[] bindLiterals() {
54 var actionLiterals = action.getActionLiterals();
55 int size = actionLiterals.size();
56 var boundLiteralsArray = new BoundActionLiteral[size];
57 for (int i = 0; i < size; i++) {
58 boundLiteralsArray[i] = actionLiterals.get(i).bindToModel(model);
59 }
60 return boundLiteralsArray;
61 }
62
63 private Tuple getInputTuple(int @Nullable [] inputAllocation) {
64 if (inputAllocation == null) {
65 // Identity allocation.
66 return activation;
67 }
68 return switch (inputAllocation.length) {
69 case 0 -> Tuple.of();
70 case 1 -> Tuple.of(getInput(inputAllocation[0]));
71 case 2 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]));
72 case 3 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]),
73 getInput(inputAllocation[2]));
74 case 4 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]),
75 getInput(inputAllocation[2]), getInput(inputAllocation[3]));
76 default -> {
77 var elements = new int[inputAllocation.length];
78 for (var i = 0; i < inputAllocation.length; i++) {
79 elements[i] = getInput(inputAllocation[i]);
80 }
81 yield Tuple.of(elements);
82 }
83 };
84 }
85
86 private int getInput(int index) {
87 int arity = action.getArity();
88 return index < arity ? activation.get(index) : localVariables[index - arity];
89 }
90
91 private void setOutputTuple(int @Nullable [] outputAllocation, Tuple output) {
92 if (outputAllocation == null || outputAllocation.length == 0) {
93 return;
94 }
95 switch (outputAllocation.length) {
96 case 1 -> localVariables[outputAllocation[0]] = output.get(0);
97 case 2 -> {
98 localVariables[outputAllocation[0]] = output.get(0);
99 localVariables[outputAllocation[1]] = output.get(1);
100 }
101 case 3 -> {
102 localVariables[outputAllocation[0]] = output.get(0);
103 localVariables[outputAllocation[1]] = output.get(1);
104 localVariables[outputAllocation[2]] = output.get(2);
105 }
106 case 4 -> {
107 localVariables[outputAllocation[0]] = output.get(0);
108 localVariables[outputAllocation[1]] = output.get(1);
109 localVariables[outputAllocation[2]] = output.get(2);
110 localVariables[outputAllocation[3]] = output.get(3);
111 }
112 default -> {
113 for (int i = 0; i < outputAllocation.length; i++) {
114 localVariables[outputAllocation[i]] = output.get(i);
115 }
116 }
117 }
118 }
119}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import org.jetbrains.annotations.NotNull;
9import org.jetbrains.annotations.Nullable;
10import tools.refinery.store.tuple.Tuple;
11
12@FunctionalInterface
13public interface BoundActionLiteral {
14 @Nullable
15 Tuple fire(@NotNull Tuple tuple);
16}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.query.term.NodeVariable;
10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.tuple.Tuple;
12
13import java.util.List;
14
15public class PutActionLiteral<T> extends AbstractActionLiteral {
16 private final Symbol<T> symbol;
17 private final List<NodeVariable> parameters;
18 private final T value;
19
20 public PutActionLiteral(Symbol<T> symbol, T value, List<NodeVariable> parameters) {
21 if (symbol.arity() != parameters.size()) {
22 throw new IllegalArgumentException("Expected %d parameters for symbol %s, got %d instead"
23 .formatted(symbol.arity(), symbol, parameters.size()));
24 }
25 if (value != null && !symbol.valueType().isInstance(value)) {
26 throw new IllegalArgumentException("Expected value of type %s for symbol %s, got %s of type %s instead"
27 .formatted(symbol.valueType().getName(), symbol, value, value.getClass().getName()));
28 }
29 this.symbol = symbol;
30 this.parameters = List.copyOf(parameters);
31 this.value = value;
32 }
33
34 public Symbol<T> getSymbol() {
35 return symbol;
36 }
37
38 public List<NodeVariable> getParameters() {
39 return parameters;
40 }
41
42 public T getValue() {
43 return value;
44 }
45
46 @Override
47 public List<NodeVariable> getInputVariables() {
48 return getParameters();
49 }
50
51 @Override
52 public List<NodeVariable> getOutputVariables() {
53 return List.of();
54 }
55
56 @Override
57 public BoundActionLiteral bindToModel(Model model) {
58 var interpretation = model.getInterpretation(symbol);
59 return tuple -> {
60 interpretation.put(tuple, value);
61 return Tuple.of();
62 };
63 }
64}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.actions.ActionLiteral;
9
10import java.util.List;
11
12@FunctionalInterface
13public interface ActionCallback0 {
14 List<ActionLiteral> toLiterals();
15}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.actions.ActionLiteral;
9import tools.refinery.store.query.term.NodeVariable;
10
11import java.util.List;
12
13@FunctionalInterface
14public interface ActionCallback1 {
15 List<ActionLiteral> toLiterals(NodeVariable v1);
16}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.actions.ActionLiteral;
9import tools.refinery.store.query.term.NodeVariable;
10
11import java.util.List;
12
13@FunctionalInterface
14public interface ActionCallback2 {
15 List<ActionLiteral> toLiterals(NodeVariable v1, NodeVariable v2);
16}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.actions.ActionLiteral;
9import tools.refinery.store.query.term.NodeVariable;
10
11import java.util.List;
12
13@FunctionalInterface
14public interface ActionCallback3 {
15 List<ActionLiteral> toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3);
16}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.actions.ActionLiteral;
9import tools.refinery.store.query.term.NodeVariable;
10
11import java.util.List;
12
13@FunctionalInterface
14public interface ActionCallback4 {
15 List<ActionLiteral> toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3, NodeVariable v4);
16}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.RuleBuilder;
9
10@FunctionalInterface
11public interface RuleCallback0 {
12 void accept(RuleBuilder builder);
13}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.RuleBuilder;
9import tools.refinery.store.query.term.NodeVariable;
10
11@FunctionalInterface
12public interface RuleCallback1 {
13 void accept(RuleBuilder builder, NodeVariable p1);
14}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.RuleBuilder;
9import tools.refinery.store.query.term.NodeVariable;
10
11@FunctionalInterface
12public interface RuleCallback2 {
13 void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2);
14}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.RuleBuilder;
9import tools.refinery.store.query.term.NodeVariable;
10
11@FunctionalInterface
12public interface RuleCallback3 {
13 void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3);
14}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.callback;
7
8import tools.refinery.store.dse.transition.RuleBuilder;
9import tools.refinery.store.query.term.NodeVariable;
10
11@FunctionalInterface
12public interface RuleCallback4 {
13 void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3, NodeVariable p4);
14}
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..23325a1f
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java
@@ -0,0 +1,94 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.internal;
7
8import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter;
9import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter;
10import tools.refinery.store.dse.transition.ObjectiveValue;
11import tools.refinery.store.dse.transition.Transformation;
12import tools.refinery.store.dse.transition.objectives.CriterionCalculator;
13import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator;
14import tools.refinery.store.model.Model;
15
16import java.util.List;
17
18public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter {
19 final Model model;
20 final DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter;
21
22 final List<Transformation> transformations;
23 final List<CriterionCalculator> accepts;
24 final List<CriterionCalculator> excludes;
25 final List<ObjectiveCalculator> objectives;
26
27 public DesignSpaceExplorationAdapterImpl(Model model,
28 DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter,
29 List<Transformation> transformations,
30 List<CriterionCalculator> accepts,
31 List<CriterionCalculator> excludes,
32 List<ObjectiveCalculator> objectives) {
33 this.model = model;
34 this.designSpaceExplorationStoreAdapter = designSpaceExplorationStoreAdapter;
35
36 this.transformations = transformations;
37 this.accepts = accepts;
38 this.excludes = excludes;
39 this.objectives = objectives;
40 }
41
42 @Override
43 public Model getModel() {
44 return model;
45 }
46
47 @Override
48 public DesignSpaceExplorationStoreAdapter getStoreAdapter() {
49 return designSpaceExplorationStoreAdapter;
50 }
51
52 public List<Transformation> getTransformations() {
53 return transformations;
54 }
55
56 @Override
57 public boolean checkAccept() {
58 for (var accept : this.accepts) {
59 model.checkCancelled();
60 if (!accept.isSatisfied()) {
61 return false;
62 }
63 }
64 return true;
65 }
66
67 @Override
68 public boolean checkExclude() {
69 for (var exclude : this.excludes) {
70 model.checkCancelled();
71 if (exclude.isSatisfied()) {
72 return true;
73 }
74 }
75 return false;
76 }
77
78 @Override
79 public ObjectiveValue getObjectiveValue() {
80 model.checkCancelled();
81 if (objectives.size() == 1) {
82 return ObjectiveValue.of(objectives.get(0).getValue());
83 } else if (objectives.size() == 2) {
84 return ObjectiveValue.of(objectives.get(0).getValue(), objectives.get(1).getValue());
85 } else {
86 double[] res = new double[objectives.size()];
87 for (int i = 0; i < objectives.size(); i++) {
88 model.checkCancelled();
89 res[i] = objectives.get(i).getValue();
90 }
91 return ObjectiveValue.of(res);
92 }
93 }
94}
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..a91f6870
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java
@@ -0,0 +1,75 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.internal;
7
8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder;
10import tools.refinery.store.dse.transition.Rule;
11import tools.refinery.store.dse.transition.objectives.Criterion;
12import tools.refinery.store.dse.transition.objectives.Objective;
13import tools.refinery.store.model.ModelStore;
14import tools.refinery.store.model.ModelStoreBuilder;
15import tools.refinery.store.query.ModelQueryBuilder;
16
17import java.util.LinkedHashSet;
18import java.util.List;
19
20public class DesignSpaceExplorationBuilderImpl
21 extends AbstractModelAdapterBuilder<DesignSpaceExplorationStoreAdapterImpl>
22 implements DesignSpaceExplorationBuilder {
23
24 LinkedHashSet<Rule> transformationRuleDefinitions = new LinkedHashSet<>();
25 LinkedHashSet<Criterion> accepts = new LinkedHashSet<>();
26 LinkedHashSet<Criterion> excludes = new LinkedHashSet<>();
27 LinkedHashSet<Objective> objectives = new LinkedHashSet<>();
28
29 @Override
30 public DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition) {
31 transformationRuleDefinitions.add(transformationRuleDefinition);
32 return this;
33 }
34
35 @Override
36 public DesignSpaceExplorationBuilder accept(Criterion criteria) {
37 accepts.add(criteria);
38 return this;
39 }
40
41 @Override
42 public DesignSpaceExplorationBuilder exclude(Criterion criteria) {
43 excludes.add(criteria);
44 return this;
45 }
46
47
48 @Override
49 public DesignSpaceExplorationBuilder objective(Objective objective) {
50 objectives.add(objective);
51 return this;
52 }
53
54 @Override
55 protected void doConfigure(ModelStoreBuilder storeBuilder) {
56 var queryEngine = storeBuilder.getAdapter(ModelQueryBuilder.class);
57 transformationRuleDefinitions.forEach(x -> queryEngine.query(x.getPrecondition()));
58 accepts.forEach(x -> x.configure(storeBuilder));
59 excludes.forEach(x -> x.configure(storeBuilder));
60 objectives.forEach(x -> x.configure(storeBuilder));
61
62 super.doConfigure(storeBuilder);
63 }
64
65 @Override
66 protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) {
67 List<Rule> transformationRuleDefinitionsList = List.copyOf(transformationRuleDefinitions);
68 List<Criterion> acceptsList = List.copyOf(accepts);
69 List<Criterion> excludesList = List.copyOf(excludes);
70 List<Objective> objectivesList = List.copyOf(objectives);
71
72 return new DesignSpaceExplorationStoreAdapterImpl(store, transformationRuleDefinitionsList, acceptsList,
73 excludesList, objectivesList);
74 }
75}
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..bd85e7a6
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java
@@ -0,0 +1,74 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.internal;
7
8import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter;
9import tools.refinery.store.dse.transition.Transformation;
10import tools.refinery.store.dse.transition.Rule;
11import tools.refinery.store.dse.transition.objectives.Criterion;
12import tools.refinery.store.dse.transition.objectives.CriterionCalculator;
13import tools.refinery.store.dse.transition.objectives.Objective;
14import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator;
15import tools.refinery.store.model.Model;
16import tools.refinery.store.model.ModelStore;
17
18import java.util.List;
19
20public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter {
21 protected final ModelStore store;
22
23 protected final List<Rule> ruleDefinitions;
24 protected final List<Criterion> accepts;
25 protected final List<Criterion> excludes;
26 protected final List<Objective> objectives;
27
28 public DesignSpaceExplorationStoreAdapterImpl(
29 ModelStore store, List<Rule> ruleDefinitions, List<Criterion> accepts, List<Criterion> excludes,
30 List<Objective> objectives) {
31 this.store = store;
32 this.ruleDefinitions = ruleDefinitions;
33 this.accepts = accepts;
34 this.excludes = excludes;
35 this.objectives = objectives;
36 }
37
38 @Override
39 public ModelStore getStore() {
40 return store;
41 }
42
43 @Override
44 public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) {
45 final List<Transformation> t = this.ruleDefinitions.stream()
46 .map(x -> new Transformation(model, x))
47 .toList();
48 final List<CriterionCalculator> a = this.accepts.stream().map(x -> x.createCalculator(model)).toList();
49 final List<CriterionCalculator> e = this.excludes.stream().map(x -> x.createCalculator(model)).toList();
50 final List<ObjectiveCalculator> o = this.objectives.stream().map(x -> x.createCalculator(model)).toList();
51
52 return new DesignSpaceExplorationAdapterImpl(model, this, t, a, e, o);
53 }
54
55 @Override
56 public List<Rule> getTransformations() {
57 return ruleDefinitions;
58 }
59
60 @Override
61 public List<Criterion> getAccepts() {
62 return accepts;
63 }
64
65 @Override
66 public List<Criterion> getExcludes() {
67 return excludes;
68 }
69
70 @Override
71 public List<Objective> getObjectives() {
72 return objectives;
73 }
74}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.literal.Reduction;
11
12import java.util.ArrayList;
13import java.util.Collection;
14
15public final class AndCriterion extends CompositeCriterion {
16 AndCriterion(Collection<? extends Criterion> criteria) {
17 super(criteria);
18 }
19
20 @Override
21 public Reduction getReduction(ModelStore store) {
22 for (var criterion : getCriteria()) {
23 var reduction = criterion.getReduction(store);
24 if (reduction == Reduction.ALWAYS_FALSE) {
25 return Reduction.ALWAYS_FALSE;
26 } else if (reduction == Reduction.NOT_REDUCIBLE) {
27 return Reduction.NOT_REDUCIBLE;
28 }
29 }
30 return Reduction.ALWAYS_TRUE;
31 }
32
33 @Override
34 public CriterionCalculator createCalculator(Model model) {
35 var calculators = new ArrayList<CriterionCalculator>();
36 for (var criterion : getCriteria()) {
37 var reduction = criterion.getReduction(model.getStore());
38 if (reduction == Reduction.ALWAYS_FALSE) {
39 return () -> false;
40 } else if (reduction == Reduction.NOT_REDUCIBLE) {
41 calculators.add(criterion.createCalculator(model));
42 }
43 }
44 return () -> {
45 for (var calculator : calculators) {
46 if (!calculator.isSatisfied()) {
47 return false;
48 }
49 }
50 return true;
51 };
52 }
53}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.ModelStore;
9import tools.refinery.store.model.ModelStoreBuilder;
10import tools.refinery.store.query.literal.Reduction;
11
12import java.util.*;
13
14public abstract sealed class CompositeCriterion implements Criterion permits AndCriterion, OrCriterion {
15 private final List<Criterion> criteria;
16
17 protected CompositeCriterion(Collection<? extends Criterion> criteria) {
18 var deDuplicatedCriteria = new LinkedHashSet<Criterion>();
19 for (var criterion : criteria) {
20 if (criterion.getClass() == this.getClass()) {
21 var childCriteria = ((CompositeCriterion) criterion).getCriteria();
22 deDuplicatedCriteria.addAll(childCriteria);
23 } else {
24 deDuplicatedCriteria.add(criterion);
25 }
26 }
27 this.criteria = List.copyOf(deDuplicatedCriteria);
28 }
29
30 public List<Criterion> getCriteria() {
31 return criteria;
32 }
33
34 @Override
35 public abstract Reduction getReduction(ModelStore store);
36
37 @Override
38 public void configure(ModelStoreBuilder storeBuilder) {
39 for (var criterion : criteria) {
40 criterion.configure(storeBuilder);
41 }
42 }
43}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16
17public class CompositeObjective implements Objective {
18 private final List<Objective> objectives;
19
20 CompositeObjective(Collection<? extends Objective> objectives) {
21 var unwrappedObjectives = new ArrayList<Objective>();
22 for (var objective : objectives) {
23 if (objective instanceof CompositeObjective compositeObjective) {
24 unwrappedObjectives.addAll(compositeObjective.getObjectives());
25 } else {
26 unwrappedObjectives.add(objective);
27 }
28 }
29 this.objectives = Collections.unmodifiableList(unwrappedObjectives);
30 }
31
32 public List<Objective> getObjectives() {
33 return objectives;
34 }
35
36 @Override
37 public boolean isAlwaysZero(ModelStore store) {
38 for (var objective : objectives) {
39 if (!objective.isAlwaysZero(store)) {
40 return false;
41 }
42 }
43 return true;
44 }
45
46 @Override
47 public ObjectiveCalculator createCalculator(Model model) {
48 var calculators = new ArrayList<ObjectiveCalculator>();
49 for (var objective : objectives) {
50 if (!objective.isAlwaysZero(model.getStore())) {
51 calculators.add(objective.createCalculator(model));
52 }
53 }
54 return () -> {
55 double value = 0;
56 for (var calculator : calculators) {
57 value += calculator.getValue();
58 }
59 return value;
60 };
61 }
62
63 @Override
64 public void configure(ModelStoreBuilder storeBuilder) {
65 for (var objective : objectives) {
66 objective.configure(storeBuilder);
67 }
68 }
69}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.ModelQueryBuilder;
13import tools.refinery.store.query.ModelQueryStoreAdapter;
14import tools.refinery.store.query.dnf.RelationalQuery;
15import tools.refinery.store.query.literal.Reduction;
16
17public class CountObjective implements Objective {
18 private final RelationalQuery query;
19 private final double weight;
20
21 public CountObjective(RelationalQuery query) {
22 this(query, 1);
23 }
24
25 public CountObjective(RelationalQuery query, double weight) {
26 this.query = query;
27 this.weight = weight;
28 }
29
30 @Override
31 public boolean isAlwaysZero(ModelStore store) {
32 var queryStore = store.getAdapter(ModelQueryStoreAdapter.class);
33 var canonicalQuery = queryStore.getCanonicalQuery(query);
34 return canonicalQuery.getDnf().getReduction() == Reduction.ALWAYS_FALSE;
35 }
36
37 @Override
38 public ObjectiveCalculator createCalculator(Model model) {
39 var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query);
40 return () -> resultSet.size() * weight;
41 }
42
43 @Override
44 public void configure(ModelStoreBuilder storeBuilder) {
45 storeBuilder.getAdapter(ModelQueryBuilder.class).query(query);
46 }
47}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.query.dnf.AnyQuery;
9
10import java.util.Collection;
11import java.util.List;
12
13public final class Criteria {
14 private Criteria() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17
18 public static QueryCriterion whenHasMatch(AnyQuery query) {
19 return new QueryCriterion(query, true);
20 }
21
22 public static QueryCriterion whenNoMatch(AnyQuery query) {
23 return new QueryCriterion(query, false);
24 }
25
26 public static Criterion and(Criterion... criteria) {
27 return and(List.of(criteria));
28 }
29
30 public static Criterion and(Collection<? extends Criterion> criteria) {
31 if (criteria.size() == 1) {
32 return criteria.iterator().next();
33 }
34 return new AndCriterion(criteria);
35 }
36
37 public static Criterion or(Criterion... criteria) {
38 return or(List.of(criteria));
39 }
40
41 public static Criterion or(Collection<? extends Criterion> criteria) {
42 if (criteria.size() == 1) {
43 return criteria.iterator().next();
44 }
45 return new OrCriterion(criteria);
46 }
47}
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..c827f20e
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java
@@ -0,0 +1,22 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11import tools.refinery.store.query.literal.Reduction;
12
13public interface Criterion {
14 default void configure(ModelStoreBuilder storeBuilder) {
15 }
16
17 default Reduction getReduction(ModelStore store) {
18 return Reduction.NOT_REDUCIBLE;
19 }
20
21 CriterionCalculator createCalculator(Model model);
22}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8public interface CriterionCalculator {
9 boolean isSatisfied();
10}
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..49c34d87
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java
@@ -0,0 +1,23 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11
12public interface Objective {
13 default void configure(ModelStoreBuilder storeBuilder) {
14 }
15
16 // The name {@code isAlwaysZero} is more straightforward than something like {@code canBeNonZero}.
17 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
18 default boolean isAlwaysZero(ModelStore store) {
19 return false;
20 }
21
22 ObjectiveCalculator createCalculator(Model model);
23}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8public interface ObjectiveCalculator {
9 double getValue();
10}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.query.dnf.FunctionalQuery;
9import tools.refinery.store.query.dnf.RelationalQuery;
10
11import java.util.Collection;
12import java.util.List;
13
14public final class Objectives {
15 private Objectives() {
16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
17 }
18
19 public static CountObjective count(RelationalQuery query, double weight) {
20 return new CountObjective(query, weight);
21 }
22
23 public static CountObjective count(RelationalQuery query) {
24 return new CountObjective(query);
25 }
26
27 public static QueryObjective value(FunctionalQuery<? extends Number> query) {
28 return new QueryObjective(query);
29 }
30
31 public static Objective sum(Objective... objectives) {
32 return sum(List.of(objectives));
33 }
34
35 public static Objective sum(Collection<? extends Objective> objectives) {
36 if (objectives.size() == 1) {
37 return objectives.iterator().next();
38 }
39 return new CompositeObjective(objectives);
40 }
41}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.literal.Reduction;
11
12import java.util.ArrayList;
13import java.util.Collection;
14
15public final class OrCriterion extends CompositeCriterion {
16 OrCriterion(Collection<? extends Criterion> criteria) {
17 super(criteria);
18 }
19
20 @Override
21 public Reduction getReduction(ModelStore store) {
22 for (var criterion : getCriteria()) {
23 var reduction = criterion.getReduction(store);
24 if (reduction == Reduction.ALWAYS_TRUE) {
25 return Reduction.ALWAYS_TRUE;
26 } else if (reduction == Reduction.NOT_REDUCIBLE) {
27 return Reduction.NOT_REDUCIBLE;
28 }
29 }
30 return Reduction.ALWAYS_FALSE;
31 }
32
33 @Override
34 public CriterionCalculator createCalculator(Model model) {
35 var calculators = new ArrayList<CriterionCalculator>();
36 for (var criterion : getCriteria()) {
37 var reduction = criterion.getReduction(model.getStore());
38 if (reduction == Reduction.ALWAYS_TRUE) {
39 return () -> true;
40 } else if (reduction == Reduction.NOT_REDUCIBLE) {
41 calculators.add(criterion.createCalculator(model));
42 }
43 }
44 return () -> {
45 for (var calculator : calculators) {
46 if (calculator.isSatisfied()) {
47 return true;
48 }
49 }
50 return false;
51 };
52 }
53}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.ModelQueryBuilder;
13import tools.refinery.store.query.ModelQueryStoreAdapter;
14import tools.refinery.store.query.dnf.AnyQuery;
15import tools.refinery.store.query.literal.Reduction;
16
17public class QueryCriterion implements Criterion {
18 protected final boolean satisfiedIfHasMatch;
19 protected final AnyQuery query;
20
21 /**
22 * Criteria based on the existence of matches evaluated on the model.
23 *
24 * @param query The query evaluated on the model.
25 * @param satisfiedIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise,
26 * the criteria satisfied if the query has no match on the model.
27 */
28 public QueryCriterion(AnyQuery query, boolean satisfiedIfHasMatch) {
29 this.query = query;
30 this.satisfiedIfHasMatch = satisfiedIfHasMatch;
31 }
32
33 @Override
34 public Reduction getReduction(ModelStore store) {
35 var queryStore = store.getAdapter(ModelQueryStoreAdapter.class);
36 var canonicalQuery = queryStore.getCanonicalQuery(query);
37 var reduction = canonicalQuery.getDnf().getReduction();
38 if (satisfiedIfHasMatch) {
39 return reduction;
40 }
41 return reduction.negate();
42 }
43
44 @Override
45 public CriterionCalculator createCalculator(Model model) {
46 var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query);
47 if (satisfiedIfHasMatch) {
48 return () -> resultSet.size() > 0;
49 } else {
50 return () -> resultSet.size() == 0;
51 }
52 }
53
54 @Override
55 public void configure(ModelStoreBuilder storeBuilder) {
56 Criterion.super.configure(storeBuilder);
57 storeBuilder.getAdapter(ModelQueryBuilder.class).query(query);
58 }
59}
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..5a7ba8f4
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java
@@ -0,0 +1,41 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.objectives;
7
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.ModelStoreBuilder;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.ModelQueryBuilder;
12import tools.refinery.store.query.dnf.FunctionalQuery;
13
14public class QueryObjective implements Objective {
15 protected final FunctionalQuery<? extends Number> objectiveFunction;
16
17 public QueryObjective(FunctionalQuery<? extends Number> objectiveFunction) {
18 if (objectiveFunction.arity() != 0) {
19 throw new IllegalArgumentException("Objective functions must have 0 parameters, got %d instead"
20 .formatted(objectiveFunction.arity()));
21 }
22 this.objectiveFunction = objectiveFunction;
23 }
24
25 @Override
26 public ObjectiveCalculator createCalculator(Model model) {
27 var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(objectiveFunction);
28 return () -> {
29 var cursor = resultSet.getAll();
30 if (!cursor.move()) {
31 return 0;
32 }
33 return Math.max(cursor.getValue().doubleValue(), 0);
34 };
35 }
36
37 @Override
38 public void configure(ModelStoreBuilder storeBuilder) {
39 storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction);
40 }
41}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.map.Version;
10
11import java.util.Random;
12
13public interface ActivationStore {
14 record VisitResult(boolean successfulVisit, boolean mayHaveMore, int transformation, int activation) { }
15 VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes);
16 boolean hasUnmarkedActivation(VersionWithObjectiveValue version);
17 VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random);
18}
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..28d1488b
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java
@@ -0,0 +1,17 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.statecoding.StateCoderResult;
10
11public interface EquivalenceClassStore {
12 boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, int[] emptyActivations, boolean accept);
13 boolean submit(StateCoderResult stateCoderResult);
14 boolean hasUnresolvedSymmetry();
15 void resolveOneSymmetry();
16 int getNumberOfUnresolvedSymmetries();
17}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.map.Version;
10
11import java.util.Comparator;
12import java.util.Random;
13
14public interface ObjectivePriorityQueue {
15 Comparator<VersionWithObjectiveValue> getComparator();
16 void submit(VersionWithObjectiveValue versionWithObjectiveValue);
17 void remove(VersionWithObjectiveValue versionWithObjectiveValue);
18 int getSize();
19 VersionWithObjectiveValue getBest();
20 VersionWithObjectiveValue getRandom(Random random);
21}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9
10import java.util.List;
11import java.util.concurrent.Future;
12
13public interface SolutionStore {
14 boolean submit(VersionWithObjectiveValue version);
15 List<VersionWithObjectiveValue> getSolutions();
16 boolean hasEnoughSolution();
17}
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..b5087e86
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java
@@ -0,0 +1,54 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore;
10import tools.refinery.store.statecoding.StateCoderResult;
11import tools.refinery.store.statecoding.StateCoderStoreAdapter;
12
13public abstract class AbstractEquivalenceClassStore implements EquivalenceClassStore {
14 protected final StateCoderStoreAdapter stateCoderStoreAdapter;
15 AbstractEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) {
16 this.stateCoderStoreAdapter = stateCoderStoreAdapter;
17 }
18
19 protected int numberOfUnresolvedSymmetries = 0;
20
21 protected abstract void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept);
22 protected abstract boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion,
23 int[] emptyActivations, boolean accept);
24
25 public abstract boolean tryToAdd(StateCoderResult stateCoderResult);
26
27 @Override
28 public boolean submit(StateCoderResult stateCoderResult) {
29 return tryToAdd(stateCoderResult);
30 }
31
32 @Override
33 public synchronized boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult,
34 int[] emptyActivations, boolean accept) {
35 boolean hasNewVersion = tryToAdd(stateCoderResult, version, emptyActivations, accept);
36 if (hasNewVersion) {
37 delegate(version, emptyActivations, accept);
38 return true;
39 } else {
40 numberOfUnresolvedSymmetries++;
41 return false;
42 }
43 }
44
45 @Override
46 public boolean hasUnresolvedSymmetry() {
47 return numberOfUnresolvedSymmetries > 0;
48 }
49
50 @Override
51 public int getNumberOfUnresolvedSymmetries() {
52 return numberOfUnresolvedSymmetries;
53 }
54}
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..c204ae35
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java
@@ -0,0 +1,50 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8public class ActivationStoreBitVectorEntry extends ActivationStoreEntry {
9 final int[] selected;
10
11 ActivationStoreBitVectorEntry(int numberOfActivations) {
12 super(numberOfActivations);
13 this.selected = new int[(numberOfActivations / Integer.SIZE) + 1];
14 }
15
16 @Override
17 public int getNumberOfVisitedActivations() {
18 int visited = 0;
19 // Use indexed for loop to avoid allocating an iterator.
20 //noinspection ForLoopReplaceableByForEach
21 for (int i = 0; i < selected.length; i++) {
22 visited += Integer.bitCount(selected[i]);
23 }
24 return visited;
25 }
26
27 private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE
28 private static final int ELEMENT_BITMASK = (1 << ELEMENT_POSITION) - 1;
29
30 @Override
31 public int getAndAddActivationAfter(int index) {
32 int position = index;
33 do {
34 final int selectedElement = position >> ELEMENT_POSITION;
35 final int selectedBit = 1 << (position & ELEMENT_BITMASK);
36
37 if ((selected[selectedElement] & selectedBit) == 0) {
38 selected[selectedElement] |= selectedBit;
39 return position;
40 } else {
41 if (position < this.numberOfActivations - 1) {
42 position++;
43 } else {
44 position = 0;
45 }
46 }
47 } while (position != index);
48 throw new IllegalArgumentException("There is are no unvisited activations!");
49 }
50}
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..7249751c
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java
@@ -0,0 +1,37 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8public abstract class ActivationStoreEntry {
9 protected final int numberOfActivations;
10
11 ActivationStoreEntry(int numberOfActivations) {
12 this.numberOfActivations = numberOfActivations;
13 }
14
15 public abstract int getNumberOfVisitedActivations();
16
17 public int getNumberOfUnvisitedActivations() {
18 return numberOfActivations - getNumberOfVisitedActivations();
19 }
20
21 public int getNumberOfActivations() {
22 return numberOfActivations;
23 }
24
25 public abstract int getAndAddActivationAfter(int index);
26
27 // public abstract boolean contains(int activation)
28 // public abstract boolean add(int activation)
29
30 public static ActivationStoreEntry create(int size) {
31 if(size <= Integer.SIZE*6) {
32 return new ActivationStoreBitVectorEntry(size);
33 } else {
34 return new ActivationStoreListEntry(size);
35 }
36 }
37}
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..82f89db7
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java
@@ -0,0 +1,138 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.dse.transition.statespace.ActivationStore;
10
11import java.util.*;
12import java.util.function.Consumer;
13
14public class ActivationStoreImpl implements ActivationStore {
15 final int numberOfTransformations;
16 final Consumer<VersionWithObjectiveValue> actionWhenAllActivationVisited;
17 final Map<VersionWithObjectiveValue, List<ActivationStoreEntry>> versionToActivations;
18
19 public ActivationStoreImpl(final int numberOfTransformations,
20 Consumer<VersionWithObjectiveValue> actionWhenAllActivationVisited) {
21 this.numberOfTransformations = numberOfTransformations;
22 this.actionWhenAllActivationVisited = actionWhenAllActivationVisited;
23 versionToActivations = new HashMap<>();
24 }
25
26 public synchronized VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes) {
27 boolean[] successful = new boolean[]{false};
28 var entries = versionToActivations.computeIfAbsent(to, x -> {
29 successful[0] = true;
30 List<ActivationStoreEntry> result = new ArrayList<>(emptyEntrySizes.length);
31 for (int emptyEntrySize : emptyEntrySizes) {
32 result.add(ActivationStoreEntry.create(emptyEntrySize));
33 }
34 return result;
35 });
36 boolean hasMore = false;
37 for (var entry : entries) {
38 if (entry.getNumberOfUnvisitedActivations() > 0) {
39 hasMore = true;
40 break;
41 }
42 }
43 if (!hasMore) {
44 actionWhenAllActivationVisited.accept(to);
45 }
46 return new VisitResult(successful[0], hasMore, -1, -1);
47 }
48
49 public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex,
50 int activationIndex) {
51 var entries = versionToActivations.get(from);
52 var entry = entries.get(transformationIndex);
53 final int unvisited = entry.getNumberOfUnvisitedActivations();
54
55 final boolean successfulVisit = unvisited > 0;
56 final boolean hasMoreInActivation = unvisited > 1;
57 final boolean hasMore;
58 final int transformation;
59 final int activation;
60
61 if (successfulVisit) {
62 transformation = transformationIndex;
63 activation = entry.getAndAddActivationAfter(activationIndex);
64
65 } else {
66 transformation = -1;
67 activation = -1;
68 }
69
70 if (!hasMoreInActivation) {
71 boolean hasMoreInOtherTransformation = false;
72 for (var e : entries) {
73 if (e != entry && e.getNumberOfUnvisitedActivations() > 0) {
74 hasMoreInOtherTransformation = true;
75 break;
76 }
77 }
78 hasMore = hasMoreInOtherTransformation;
79 } else {
80 hasMore = true;
81 }
82
83 if (!hasMore) {
84 actionWhenAllActivationVisited.accept(from);
85 }
86
87 return new VisitResult(successfulVisit, hasMore, transformation, activation);
88 }
89
90 @Override
91 public synchronized boolean hasUnmarkedActivation(VersionWithObjectiveValue version) {
92 var entries = versionToActivations.get(version);
93 boolean hasMore = false;
94 for (var entry : entries) {
95 if (entry.getNumberOfUnvisitedActivations() > 0) {
96 hasMore = true;
97 break;
98 }
99 }
100 return hasMore;
101 }
102
103 @Override
104 public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) {
105 var entries = versionToActivations.get(version);
106
107 var weights = new double[entries.size()];
108 double totalWeight = 0;
109 int numberOfAllUnvisitedActivations = 0;
110 for (int i = 0; i < weights.length; i++) {
111 var entry = entries.get(i);
112 int unvisited = entry.getNumberOfUnvisitedActivations();
113 double weight = unvisited == 0 ? 0 : unvisited; //(Math.log(unvisited) + 1.0);
114 weights[i] = weight;
115 totalWeight += weight;
116 numberOfAllUnvisitedActivations += unvisited;
117 }
118
119 if (numberOfAllUnvisitedActivations == 0) {
120 this.actionWhenAllActivationVisited.accept(version);
121 return new VisitResult(false, false, -1, -1);
122 }
123
124 double offset = random.nextDouble(totalWeight);
125 int transformation = 0;
126 for (; transformation < entries.size(); transformation++) {
127 double weight = weights[transformation];
128 if (weight > 0 && offset < weight) {
129 var entry = entries.get(transformation);
130 int activation = random.nextInt(entry.getNumberOfActivations());
131 return this.visitActivation(version, transformation, activation);
132 }
133 offset -= weight;
134 }
135
136 throw new AssertionError("Unvisited activation %f not found".formatted(offset));
137 }
138}
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..14298bee
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java
@@ -0,0 +1,104 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import org.eclipse.collections.api.factory.primitive.IntLists;
9import org.eclipse.collections.api.list.primitive.MutableIntList;
10
11public class ActivationStoreListEntry extends ActivationStoreEntry {
12 private final MutableIntList visitedActivations = IntLists.mutable.empty();
13
14 ActivationStoreListEntry(int numberOfActivations) {
15 super(numberOfActivations);
16 }
17
18 @Override
19 public int getNumberOfVisitedActivations() {
20 return visitedActivations.size();
21 }
22
23 @Override
24 public int getAndAddActivationAfter(int index) {
25 // If it is empty, just add it.
26 if(this.visitedActivations.isEmpty()) {
27 this.visitedActivations.add(index);
28 return index;
29 }
30 final int positionInSearch = getPosition(index);
31 int position = positionInSearch;
32
33 // if the position is after the last, we can insert it at the end of the list
34 if(position == this.visitedActivations.size()) {
35 this.visitedActivations.add(index);
36 return index;
37 } else if(this.visitedActivations.get(position) != index) {
38 // If the index is not in the position, one can insert it
39
40 this.visitedActivations.addAtIndex(position,index);
41 return index;
42 }
43
44 // Otherwise, get the next empty space between two elements
45 while(position + 1 < this.visitedActivations.size()) {
46 if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) {
47 int newElement = this.visitedActivations.get(position)+1;
48 this.visitedActivations.addAtIndex(position+1, newElement);
49 return newElement;
50 }
51 position++;
52 }
53
54 // Otherwise, try to add to the last space
55 int last = this.visitedActivations.get(this.visitedActivations.size()-1);
56 if(last<this.numberOfActivations-1) {
57 this.visitedActivations.add(last+1);
58 return last+1;
59 }
60
61 // Otherwise, try to put to the beginning
62 if(this.visitedActivations.get(0) > 0) {
63 this.visitedActivations.addAtIndex(0,0);
64 return 0;
65 }
66
67 // Otherwise, get the next empty space between two elements
68 position = 0;
69 while(position < positionInSearch) {
70 if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) {
71 int newElement = this.visitedActivations.get(position)+1;
72 this.visitedActivations.addAtIndex(position+1, newElement);
73 return newElement;
74 }
75 position++;
76 }
77
78 throw new IllegalArgumentException("There is are no unvisited activations!");
79 }
80
81 /**
82 * Returns the position of an index in the {@code visitedActivations}. If the collection contains the index, in
83 * returns its position, otherwise, it returns the position where the index need to be put.
84 *
85 * @param index Index of an activation.
86 * @return The position of the index.
87 */
88 private int getPosition(int index) {
89 int left = 0;
90 int right = this.visitedActivations.size() - 1;
91 while (left <= right) {
92 final int middle = (right - left) / 2 + left;
93 final int middleElement = visitedActivations.get(middle);
94 if(middleElement == index) {
95 return middle;
96 } else if(middleElement < index) {
97 left = middle +1;
98 } else{
99 right = middle-1;
100 }
101 }
102 return right+1;
103 }
104}
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..1d7c5ce5
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java
@@ -0,0 +1,55 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import tools.refinery.store.dse.transition.Transformation;
9import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
10import tools.refinery.store.dse.transition.statespace.ActivationStore;
11
12import java.util.List;
13import java.util.Random;
14
15public class ActivationStoreWorker {
16 final ActivationStore store;
17 final List<Transformation> transformations;
18
19 public ActivationStoreWorker(ActivationStore store, List<Transformation> transformations) {
20 this.store = store;
21 this.transformations = transformations;
22 }
23
24 public int[] calculateEmptyActivationSize() {
25 int[] result = new int[transformations.size()];
26 for (int i = 0; i < result.length; i++) {
27 result[i] = transformations.get(i).getAllActivationsAsResultSet().size();
28 }
29 return result;
30 }
31
32
33 public ActivationStore.VisitResult fireRandomActivation(VersionWithObjectiveValue thisVersion, Random random) {
34 var result = store.getRandomAndMarkAsVisited(thisVersion, random);
35 if (result.successfulVisit()) {
36 int selectedTransformation = result.transformation();
37 int selectedActivation = result.activation();
38
39 Transformation transformation = transformations.get(selectedTransformation);
40 var tuple = transformation.getActivation(selectedActivation);
41
42 boolean success = transformation.fireActivation(tuple);
43 if (success) {
44 return result;
45 } else {
46 return new ActivationStore.VisitResult(
47 false,
48 result.mayHaveMore(),
49 selectedTransformation,
50 selectedActivation);
51 }
52 }
53 return result;
54 }
55}
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..20a026b6
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java
@@ -0,0 +1,104 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import org.eclipse.collections.api.factory.primitive.IntObjectMaps;
9import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
10import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
11import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore;
12import tools.refinery.store.statecoding.StateCoderResult;
13import tools.refinery.store.statecoding.StateCoderStoreAdapter;
14import tools.refinery.store.statecoding.StateEquivalenceChecker;
15
16import java.util.ArrayList;
17
18public abstract class CompleteEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore {
19
20 static class SymmetryStoreArray extends ArrayList<VersionWithObjectiveValue> {
21 final int[] activationSizes;
22 final boolean accept;
23
24 SymmetryStoreArray(int[] activationSizes, boolean accept) {
25 super();
26 this.activationSizes = activationSizes;
27 this.accept = accept;
28 }
29 }
30
31 private final MutableIntObjectMap<Object> modelCode2Versions = IntObjectMaps.mutable.empty();
32
33 protected CompleteEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) {
34 super(stateCoderStoreAdapter);
35 }
36
37 @Override
38 protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion,
39 int[] emptyActivations, boolean accept) {
40 int modelCode = stateCoderResult.modelCode();
41 Object old = modelCode2Versions.updateValue(
42 modelCode,
43 () -> newVersion,
44 x -> {
45 if (x instanceof SymmetryStoreArray array) {
46 if(array.accept != accept || array.activationSizes != emptyActivations) {
47 this.delegate(newVersion,emptyActivations,accept);
48 return x;
49 }
50 array.add(newVersion);
51 return array;
52 } else {
53 SymmetryStoreArray result = new SymmetryStoreArray(emptyActivations, accept);
54 result.add((VersionWithObjectiveValue) x);
55 result.add(newVersion);
56 return result;
57 }
58 });
59 return old == null;
60 }
61
62 @Override
63 public void resolveOneSymmetry() {
64 var unresolvedSimilarity = getOneUnresolvedSymmetry();
65 if (unresolvedSimilarity == null) {
66 return;
67 }
68 var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0).version(),
69 unresolvedSimilarity.get(1).version());
70 if (outcome != StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC) {
71 delegate(unresolvedSimilarity.get(1), unresolvedSimilarity.activationSizes, unresolvedSimilarity.accept);
72 }
73 }
74
75 //record UnresolvedSymmetryResult
76
77 private synchronized SymmetryStoreArray getOneUnresolvedSymmetry() {
78 if (numberOfUnresolvedSymmetries <= 0) {
79 return null;
80 }
81
82 for (var entry : modelCode2Versions.keyValuesView()) {
83 int hash = entry.getOne();
84 var value = entry.getTwo();
85 if (value instanceof SymmetryStoreArray array) {
86 int size = array.size();
87 var representative = array.get(0);
88 var similar = array.get(size - 1);
89 array.remove(size - 1);
90
91 if (size <= 2) {
92 modelCode2Versions.put(hash, representative);
93 }
94
95 var result = new SymmetryStoreArray(array.activationSizes, array.accept);
96 result.add(representative);
97 result.add(similar);
98 return result;
99 }
100 }
101
102 return null;
103 }
104}
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..6d028124
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java
@@ -0,0 +1,37 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import org.eclipse.collections.api.factory.primitive.IntSets;
9import org.eclipse.collections.api.set.primitive.MutableIntSet;
10import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
11import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore;
12import tools.refinery.store.statecoding.StateCoderResult;
13import tools.refinery.store.statecoding.StateCoderStoreAdapter;
14
15public abstract class FastEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore {
16
17 private final MutableIntSet codes = IntSets.mutable.empty();
18
19 protected FastEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) {
20 super(stateCoderStoreAdapter);
21 }
22
23 @Override
24 protected synchronized boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion,
25 int[] emptyActivations, boolean accept) {
26 return this.codes.add(stateCoderResult.modelCode());
27 }
28
29 public synchronized boolean tryToAdd(StateCoderResult stateCoderResult) {
30 return this.codes.add(stateCoderResult.modelCode());
31 }
32
33 @Override
34 public void resolveOneSymmetry() {
35 throw new IllegalArgumentException("This equivalence storage is not prepared to resolve symmetries!");
36 }
37}
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..2f3e142a
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java
@@ -0,0 +1,68 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import tools.refinery.store.dse.transition.ObjectiveValues;
9import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
10import tools.refinery.store.dse.transition.objectives.Objective;
11import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue;
12
13import java.util.Comparator;
14import java.util.List;
15import java.util.PriorityQueue;
16import java.util.Random;
17
18public class ObjectivePriorityQueueImpl implements ObjectivePriorityQueue {
19 public static final Comparator<VersionWithObjectiveValue> c1 = (o1, o2) -> Double.compare(
20 ((ObjectiveValues.ObjectiveValue1) o1.objectiveValue()).value0(),
21 ((ObjectiveValues.ObjectiveValue1) o2.objectiveValue()).value0());
22 // TODO: support multi objective!
23 final PriorityQueue<VersionWithObjectiveValue> priorityQueue;
24
25 public ObjectivePriorityQueueImpl(List<Objective> objectives) {
26
27 if(objectives.size() == 1) {
28 this.priorityQueue = new PriorityQueue<>(c1);
29 } else {
30 throw new UnsupportedOperationException("Only single objective comparator is implemented currently!");
31 }
32 }
33 @Override
34 public Comparator<VersionWithObjectiveValue> getComparator() {
35 return c1;
36 }
37
38 @Override
39 public synchronized void submit(VersionWithObjectiveValue versionWithObjectiveValue) {
40 priorityQueue.add(versionWithObjectiveValue);
41 }
42
43 @Override
44 public synchronized void remove(VersionWithObjectiveValue versionWithObjectiveValue) {
45 priorityQueue.remove(versionWithObjectiveValue);
46 }
47
48 @Override
49 public synchronized int getSize() {
50 return priorityQueue.size();
51 }
52
53 @Override
54 public synchronized VersionWithObjectiveValue getBest() {
55 return priorityQueue.peek();
56 }
57
58 @Override
59 public synchronized VersionWithObjectiveValue getRandom(Random random) {
60 int randomPosition = random.nextInt(getSize());
61 for (VersionWithObjectiveValue entry : this.priorityQueue) {
62 if (randomPosition-- == 0) {
63 return entry;
64 }
65 }
66 throw new IllegalStateException("The priority queue is inconsistent!");
67 }
68}
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..43548eaa
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java
@@ -0,0 +1,52 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.dse.transition.statespace.SolutionStore;
10
11import java.util.ArrayList;
12import java.util.List;
13import java.util.PriorityQueue;
14
15
16public class SolutionStoreImpl implements SolutionStore {
17 final int maxNumberSolutions;
18 public static final int UNLIMITED = -1;
19 final PriorityQueue<VersionWithObjectiveValue> solutions;
20
21 public SolutionStoreImpl(int maxNumberSolutions) {
22 this.maxNumberSolutions = maxNumberSolutions;
23 solutions = new PriorityQueue<>(ObjectivePriorityQueueImpl.c1.reversed());
24 }
25
26
27 @Override
28 public synchronized boolean submit(VersionWithObjectiveValue version) {
29 boolean removeLast = hasEnoughSolution();
30 solutions.add(version);
31 if(removeLast) {
32 var last = solutions.poll();
33 return last != version;
34 } else {
35 return true;
36 }
37 }
38
39 @Override
40 public List<VersionWithObjectiveValue> getSolutions() {
41 return new ArrayList<>(solutions);
42 }
43
44 @Override
45 public boolean hasEnoughSolution() {
46 if (maxNumberSolutions == UNLIMITED) {
47 return false;
48 } else {
49 return solutions.size() >= maxNumberSolutions;
50 }
51 }
52}
diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/ActionEqualsTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/ActionEqualsTest.java
deleted file mode 100644
index d4a05d12..00000000
--- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/ActionEqualsTest.java
+++ /dev/null
@@ -1,629 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import org.junit.jupiter.api.BeforeAll;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.dse.internal.action.*;
11import tools.refinery.store.dse.strategy.DepthFirstStrategy;
12import tools.refinery.store.model.Model;
13import tools.refinery.store.model.ModelStore;
14import tools.refinery.store.query.dnf.Query;
15import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
16import tools.refinery.store.query.view.KeyOnlyView;
17import tools.refinery.store.representation.Symbol;
18
19import static org.junit.jupiter.api.Assertions.assertFalse;
20import static org.junit.jupiter.api.Assertions.assertTrue;
21
22public class ActionEqualsTest {
23
24 private static Model model;
25 private static Symbol<Boolean> type1;
26 private static Symbol<Boolean> relation1;
27 private static Symbol<Boolean> relation2;
28
29 @BeforeAll
30 public static void init() {
31 type1 = Symbol.of("type1", 1);
32 relation1 = Symbol.of("relation1", 2);
33 relation2 = Symbol.of("relation2", 2);
34 var type1View = new KeyOnlyView<>(type1);
35 var precondition1 = Query.of("CreateClassPrecondition",
36 (builder, model) -> builder.clause(
37 type1View.call(model)
38 ));
39
40 var precondition2 = Query.of("CreateFeaturePrecondition",
41 (builder, model) -> builder.clause(
42 type1View.call(model)
43 ));
44 var store = ModelStore.builder()
45 .symbols(type1, relation2, relation1)
46 .with(ViatraModelQueryAdapter.builder()
47 .queries(precondition1, precondition2))
48 .with(DesignSpaceExplorationAdapter.builder()
49 .strategy(new DepthFirstStrategy()))
50 .build();
51
52
53 model = store.createEmptyModel();
54 }
55
56 @Test
57 void emptyActionEqualsTest() {
58 var action1 = new TransformationAction();
59 var action2 = new TransformationAction();
60
61 assertTrue(action1.equalsWithSubstitution(action1));
62 assertTrue(action2.equalsWithSubstitution(action2));
63 assertTrue(action1.equalsWithSubstitution(action2));
64 }
65
66 @Test
67 void actionTrivialTest() {
68 var newItemSymbol1 = new NewItemVariable();
69 var activationSymbol = new ActivationVariable();
70 var insertAction1 = new InsertAction<>(model.getInterpretation(type1), true, newItemSymbol1);
71 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
72 activationSymbol);
73 var insertAction3 = new InsertAction<>(model.getInterpretation(type1), true, newItemSymbol1);
74 var insertAction4 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
75 activationSymbol);
76
77 var action1 = new TransformationAction();
78 action1.add(newItemSymbol1);
79 action1.add(activationSymbol);
80 action1.add(insertAction1);
81 action1.add(insertAction2);
82 action1.prepare(model);
83
84 var action2 = new TransformationAction();
85 action2.add(newItemSymbol1);
86 action2.add(activationSymbol);
87 action2.add(insertAction3);
88 action2.add(insertAction4);
89 action2.prepare(model);
90
91 assertTrue(action1.equalsWithSubstitution(action2));
92 }
93
94 @Test
95 void actionIdenticalTest() {
96 var newItemSymbol1 = new NewItemVariable();
97 var activationSymbol1 = new ActivationVariable();
98 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
99 activationSymbol1);
100
101 var action1 = new TransformationAction();
102 action1.add(newItemSymbol1);
103 action1.add(activationSymbol1);
104 action1.add(insertAction1);
105 action1.prepare(model);
106
107 var newItemSymbol2 = new NewItemVariable();
108 var activationSymbol2 = new ActivationVariable();
109 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
110 activationSymbol2);
111
112 var action2 = new TransformationAction();
113 action2.add(newItemSymbol2);
114 action2.add(activationSymbol2);
115 action2.add(insertAction2);
116 action2.prepare(model);
117
118 assertTrue(action1.equalsWithSubstitution(action2));
119 }
120
121 @Test
122 void actionSymbolGlobalOrderTest() {
123 var newItemSymbol1 = new NewItemVariable();
124 var activationSymbol1 = new ActivationVariable();
125 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
126 activationSymbol1);
127
128 var action1 = new TransformationAction();
129 action1.add(newItemSymbol1);
130 action1.add(activationSymbol1);
131 action1.add(insertAction1);
132 action1.prepare(model);
133
134 var newItemSymbol2 = new NewItemVariable();
135 var activationSymbol2 = new ActivationVariable();
136 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
137 activationSymbol2);
138
139 var action2 = new TransformationAction();
140 action2.add(activationSymbol2);
141 action2.add(newItemSymbol2);
142 action2.add(insertAction2);
143 action2.prepare(model);
144
145 assertFalse(action1.equalsWithSubstitution(action2));
146 }
147
148 @Test
149 void actionSymbolRepeatedInInsertActionTest() {
150 var newItemSymbol1 = new NewItemVariable();
151 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
152 newItemSymbol1);
153
154 var action1 = new TransformationAction();
155 action1.add(newItemSymbol1);
156 action1.add(insertAction1);
157 action1.prepare(model);
158
159 var newItemSymbol2 = new NewItemVariable();
160 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
161 newItemSymbol2);
162
163 var action2 = new TransformationAction();
164 action2.add(newItemSymbol2);
165 action2.add(insertAction2);
166 action2.prepare(model);
167
168 assertTrue(action1.equalsWithSubstitution(action2));
169 }
170
171 @Test
172 void identicalInsertActionInDifferentOrderTest() {
173 var newItemSymbol1 = new NewItemVariable();
174 var activationSymbol1 = new ActivationVariable();
175 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
176 activationSymbol1);
177 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
178 activationSymbol1);
179
180 var action1 = new TransformationAction();
181 action1.add(newItemSymbol1);
182 action1.add(activationSymbol1);
183 action1.add(insertAction1);
184 action1.add(insertAction2);
185 action1.prepare(model);
186
187 var action2 = new TransformationAction();
188 action2.add(newItemSymbol1);
189 action2.add(activationSymbol1);
190 action2.add(insertAction2);
191 action2.add(insertAction1);
192 action2.prepare(model);
193
194 assertTrue(action1.equalsWithSubstitution(action2));
195 }
196
197 @Test
198 void identicalActionAndSymbolDifferentOrderTest() {
199 var newItemSymbol1 = new NewItemVariable();
200 var newItemSymbol2 = new NewItemVariable();
201 var activationSymbol1 = new ActivationVariable();
202 var activationSymbol2 = new ActivationVariable();
203
204 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
205 activationSymbol1);
206
207 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
208 activationSymbol2);
209
210 var action1 = new TransformationAction();
211 action1.add(newItemSymbol1);
212 action1.add(newItemSymbol2);
213 action1.add(activationSymbol1);
214 action1.add(activationSymbol2);
215 action1.add(insertAction1);
216 action1.add(insertAction2);
217 action1.prepare(model);
218
219 var action2 = new TransformationAction();
220 action2.add(newItemSymbol2);
221 action2.add(newItemSymbol1);
222 action2.add(activationSymbol2);
223 action2.add(activationSymbol1);
224 action2.add(insertAction2);
225 action2.add(insertAction1);
226 action2.prepare(model);
227
228 assertTrue(action1.equalsWithSubstitution(action2));
229 }
230
231 @Test
232 void identicalActionAndSymbolMixedOrderTest() {
233 var newItemSymbol1 = new NewItemVariable();
234 var activationSymbol1 = new ActivationVariable();
235 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
236 activationSymbol1);
237
238 var newItemSymbol2 = new NewItemVariable();
239 var activationSymbol2 = new ActivationVariable();
240 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
241 activationSymbol2);
242
243 var action1 = new TransformationAction();
244 action1.add(newItemSymbol1);
245 action1.add(newItemSymbol2);
246 action1.add(activationSymbol1);
247 action1.add(activationSymbol2);
248 action1.add(insertAction1);
249 action1.add(insertAction2);
250 action1.prepare(model);
251
252 var action2 = new TransformationAction();
253 action2.add(insertAction1);
254 action2.add(newItemSymbol1);
255 action2.add(newItemSymbol2);
256 action2.add(activationSymbol1);
257 action2.add(insertAction2);
258 action2.add(activationSymbol2);
259 action2.prepare(model);
260
261 assertTrue(action1.equalsWithSubstitution(action2));
262 }
263
264 @Test
265 void insertActionInterpretationTest() {
266 var newItemSymbol1 = new NewItemVariable();
267 var activationSymbol1 = new ActivationVariable();
268 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
269 activationSymbol1);
270 var insertAction2 = new InsertAction<>(model.getInterpretation(relation2), true, newItemSymbol1,
271 activationSymbol1);
272
273 var action1 = new TransformationAction();
274 action1.add(newItemSymbol1);
275 action1.add(activationSymbol1);
276 action1.add(insertAction1);
277 action1.prepare(model);
278
279 var action2 = new TransformationAction();
280 action2.add(newItemSymbol1);
281 action2.add(activationSymbol1);
282 action2.add(insertAction2);
283 action2.prepare(model);
284
285 assertFalse(action1.equalsWithSubstitution(action2));
286 }
287
288 @Test
289 void insertActionValueTest() {
290 var newItemSymbol1 = new NewItemVariable();
291 var activationSymbol1 = new ActivationVariable();
292 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
293 activationSymbol1);
294 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol1,
295 activationSymbol1);
296
297 var action1 = new TransformationAction();
298 action1.add(newItemSymbol1);
299 action1.add(activationSymbol1);
300 action1.add(insertAction1);
301 action1.prepare(model);
302
303 var action2 = new TransformationAction();
304 action2.add(newItemSymbol1);
305 action2.add(activationSymbol1);
306 action2.add(insertAction2);
307 action2.prepare(model);
308
309 assertFalse(action1.equalsWithSubstitution(action2));
310 }
311
312 @Test
313 void newItemSymbolDuplicateTest() {
314 var newItemSymbol1 = new NewItemVariable();
315 var newItemSymbol2 = new NewItemVariable();
316 var activationSymbol1 = new ActivationVariable();
317 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
318 activationSymbol1);
319 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
320 activationSymbol1);
321
322 var action1 = new TransformationAction();
323 action1.add(newItemSymbol1);
324 action1.add(activationSymbol1);
325 action1.add(insertAction1);
326 action1.prepare(model);
327
328 var action2 = new TransformationAction();
329 action2.add(newItemSymbol2);
330 action2.add(activationSymbol1);
331 action2.add(insertAction2);
332 action2.prepare(model);
333
334 assertTrue(action1.equalsWithSubstitution(action2));
335 }
336
337 @Test
338 void activationSymbolDuplicateTest() {
339 var newItemSymbol1 = new NewItemVariable();
340 var activationSymbol1 = new ActivationVariable();
341 var activationSymbol2 = new ActivationVariable();
342 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
343 activationSymbol1);
344 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
345 activationSymbol2);
346
347 var action1 = new TransformationAction();
348 action1.add(newItemSymbol1);
349 action1.add(activationSymbol1);
350 action1.add(insertAction1);
351 action1.prepare(model);
352
353 var action2 = new TransformationAction();
354 action2.add(newItemSymbol1);
355 action2.add(activationSymbol2);
356 action2.add(insertAction2);
357 action2.prepare(model);
358
359 assertTrue(action1.equalsWithSubstitution(action2));
360 }
361
362 @Test
363 void activationSymbolIndexTest() {
364 var newItemSymbol1 = new NewItemVariable();
365 var activationSymbol1 = new ActivationVariable(0);
366 var activationSymbol2 = new ActivationVariable(1);
367 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
368 activationSymbol1);
369 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
370 activationSymbol2);
371
372 var action1 = new TransformationAction();
373 action1.add(newItemSymbol1);
374 action1.add(activationSymbol1);
375 action1.add(insertAction1);
376 action1.prepare(model);
377
378 var action2 = new TransformationAction();
379 action2.add(newItemSymbol1);
380 action2.add(activationSymbol2);
381 action2.add(insertAction2);
382 action2.prepare(model);
383
384 assertFalse(action1.equalsWithSubstitution(action2));
385 }
386
387 @Test
388 void deleteActionTest() {
389 var newItemSymbol = new NewItemVariable();
390 var activationSymbol = new ActivationVariable(0);
391 var insertAction = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol,
392 activationSymbol);
393 var deleteAction = new DeleteAction(activationSymbol);
394
395 var action1 = new TransformationAction();
396 action1.add(newItemSymbol);
397 action1.add(activationSymbol);
398 action1.add(insertAction);
399 action1.add(deleteAction);
400 action1.prepare(model);
401
402 var action2 = new TransformationAction();
403 action2.add(newItemSymbol);
404 action2.add(activationSymbol);
405 action2.add(insertAction);
406 action2.add(deleteAction);
407 action2.prepare(model);
408
409 assertTrue(action1.equalsWithSubstitution(action2));
410 }
411
412 @Test
413 void deleteActionMissingTest() {
414 var newItemSymbol = new NewItemVariable();
415 var activationSymbol = new ActivationVariable(0);
416 var insertAction = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol,
417 activationSymbol);
418 var deleteAction = new DeleteAction(activationSymbol);
419
420 var action1 = new TransformationAction();
421 action1.add(newItemSymbol);
422 action1.add(activationSymbol);
423 action1.add(insertAction);
424 action1.add(deleteAction);
425 action1.prepare(model);
426
427 var action2 = new TransformationAction();
428 action2.add(newItemSymbol);
429 action2.add(activationSymbol);
430 action2.add(insertAction);
431 action2.prepare(model);
432
433 assertFalse(action1.equalsWithSubstitution(action2));
434 }
435
436 @Test
437 void deleteActionIdenticalTest() {
438 var newItemSymbol = new NewItemVariable();
439 var activationSymbol = new ActivationVariable(0);
440 var insertAction = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol,
441 activationSymbol);
442 var deleteAction1 = new DeleteAction(activationSymbol);
443 var deleteAction2 = new DeleteAction(activationSymbol);
444
445 var action1 = new TransformationAction();
446 action1.add(newItemSymbol);
447 action1.add(activationSymbol);
448 action1.add(insertAction);
449 action1.add(deleteAction1);
450 action1.prepare(model);
451
452 var action2 = new TransformationAction();
453 action2.add(newItemSymbol);
454 action2.add(activationSymbol);
455 action2.add(insertAction);
456 action2.add(deleteAction2);
457 action2.prepare(model);
458
459 assertTrue(action1.equalsWithSubstitution(action2));
460 }
461
462 @Test
463 void deleteActionSymbolTypeTest() {
464 var newItemSymbol = new NewItemVariable();
465 var activationSymbol = new ActivationVariable(0);
466 var insertAction = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol,
467 activationSymbol);
468 var deleteAction1 = new DeleteAction(activationSymbol);
469 var deleteAction2 = new DeleteAction(newItemSymbol);
470
471 var action1 = new TransformationAction();
472 action1.add(newItemSymbol);
473 action1.add(activationSymbol);
474 action1.add(insertAction);
475 action1.add(deleteAction1);
476 action1.prepare(model);
477
478 var action2 = new TransformationAction();
479 action2.add(newItemSymbol);
480 action2.add(activationSymbol);
481 action2.add(insertAction);
482 action2.add(deleteAction2);
483 action2.prepare(model);
484
485 assertFalse(action1.equalsWithSubstitution(action2));
486 }
487
488 @Test
489 void deleteActionOrderTest() {
490 var newItemSymbol = new NewItemVariable();
491 var activationSymbol = new ActivationVariable(0);
492 var insertAction = new InsertAction<>(model.getInterpretation(relation1), false, newItemSymbol,
493 activationSymbol);
494 var deleteAction1 = new DeleteAction(activationSymbol);
495 var deleteAction2 = new DeleteAction(newItemSymbol);
496
497 var action1 = new TransformationAction();
498 action1.add(newItemSymbol);
499 action1.add(activationSymbol);
500 action1.add(insertAction);
501 action1.add(deleteAction1);
502 action1.add(deleteAction2);
503 action1.prepare(model);
504
505 var action2 = new TransformationAction();
506 action2.add(newItemSymbol);
507 action2.add(activationSymbol);
508 action2.add(insertAction);
509 action2.add(deleteAction2);
510 action2.add(deleteAction1);
511 action2.prepare(model);
512
513 assertFalse(action1.equalsWithSubstitution(action2));
514 }
515
516 @Test
517 void actionsMixedOrderTest() {
518 var newItemSymbol1 = new NewItemVariable();
519 var activationSymbol1 = new ActivationVariable();
520 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
521 activationSymbol1);
522 var deleteAction1 = new DeleteAction(newItemSymbol1);
523
524 var newItemSymbol2 = new NewItemVariable();
525 var activationSymbol2 = new ActivationVariable();
526 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
527 activationSymbol2);
528 var deleteAction2 = new DeleteAction(activationSymbol2);
529
530 var action1 = new TransformationAction();
531 action1.add(newItemSymbol1);
532 action1.add(newItemSymbol2);
533 action1.add(activationSymbol1);
534 action1.add(activationSymbol2);
535 action1.add(insertAction1);
536 action1.add(insertAction2);
537 action1.add(deleteAction1);
538 action1.add(deleteAction2);
539 action1.prepare(model);
540
541 var action2 = new TransformationAction();
542 action2.add(deleteAction1);
543 action2.add(newItemSymbol1);
544 action2.add(insertAction1);
545 action2.add(newItemSymbol2);
546 action2.add(deleteAction2);
547 action2.add(activationSymbol1);
548 action2.add(insertAction2);
549 action2.add(activationSymbol2);
550 action2.prepare(model);
551
552 assertTrue(action1.equalsWithSubstitution(action2));
553 }
554
555 @Test
556 void twoUnpreparedActionsTest() {
557 var newItemSymbol1 = new NewItemVariable();
558 var activationSymbol1 = new ActivationVariable();
559 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
560 activationSymbol1);
561 var deleteAction1 = new DeleteAction(newItemSymbol1);
562
563 var newItemSymbol2 = new NewItemVariable();
564 var activationSymbol2 = new ActivationVariable();
565 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
566 activationSymbol2);
567 var deleteAction2 = new DeleteAction(activationSymbol2);
568
569 var action1 = new TransformationAction();
570 action1.add(newItemSymbol1);
571 action1.add(newItemSymbol2);
572 action1.add(activationSymbol1);
573 action1.add(activationSymbol2);
574 action1.add(insertAction1);
575 action1.add(insertAction2);
576 action1.add(deleteAction1);
577 action1.add(deleteAction2);
578
579 var action2 = new TransformationAction();
580 action2.add(deleteAction1);
581 action2.add(newItemSymbol1);
582 action2.add(insertAction1);
583 action2.add(newItemSymbol2);
584 action2.add(deleteAction2);
585 action2.add(activationSymbol1);
586 action2.add(insertAction2);
587 action2.add(activationSymbol2);
588
589 assertTrue(action1.equalsWithSubstitution(action2));
590 }
591
592 @Test
593 void oneUnpreparedActionTest() {
594 var newItemSymbol1 = new NewItemVariable();
595 var activationSymbol1 = new ActivationVariable();
596 var insertAction1 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol1,
597 activationSymbol1);
598 var deleteAction1 = new DeleteAction(newItemSymbol1);
599
600 var newItemSymbol2 = new NewItemVariable();
601 var activationSymbol2 = new ActivationVariable();
602 var insertAction2 = new InsertAction<>(model.getInterpretation(relation1), true, newItemSymbol2,
603 activationSymbol2);
604 var deleteAction2 = new DeleteAction(activationSymbol2);
605
606 var action1 = new TransformationAction();
607 action1.add(newItemSymbol1);
608 action1.add(newItemSymbol2);
609 action1.add(activationSymbol1);
610 action1.add(activationSymbol2);
611 action1.add(insertAction1);
612 action1.add(insertAction2);
613 action1.add(deleteAction1);
614 action1.add(deleteAction2);
615 action1.prepare(model);
616
617 var action2 = new TransformationAction();
618 action2.add(deleteAction1);
619 action2.add(newItemSymbol1);
620 action2.add(insertAction1);
621 action2.add(newItemSymbol2);
622 action2.add(deleteAction2);
623 action2.add(activationSymbol1);
624 action2.add(insertAction2);
625 action2.add(activationSymbol2);
626
627 assertFalse(action1.equalsWithSubstitution(action2));
628 }
629}
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..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
@@ -7,236 +7,161 @@ package tools.refinery.store.dse;
7 7
8import org.junit.jupiter.api.Disabled; 8import org.junit.jupiter.api.Disabled;
9import org.junit.jupiter.api.Test; 9import org.junit.jupiter.api.Test;
10import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; 10import tools.refinery.store.dse.modification.DanglingEdges;
11import tools.refinery.store.dse.modification.ModificationAdapter;
12import tools.refinery.store.dse.strategy.BestFirstStoreManager;
13import tools.refinery.store.dse.tests.DummyCriterion;
14import tools.refinery.store.dse.tests.DummyRandomObjective;
15import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter;
16import tools.refinery.store.dse.transition.Rule;
17import tools.refinery.store.dse.transition.objectives.Criteria;
18import tools.refinery.store.dse.transition.objectives.Objectives;
11import tools.refinery.store.model.ModelStore; 19import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.query.ModelQueryAdapter; 20import tools.refinery.store.query.ModelQueryAdapter;
13import tools.refinery.store.query.dnf.Query; 21import tools.refinery.store.query.dnf.Query;
14import tools.refinery.store.query.dnf.RelationalQuery; 22import tools.refinery.store.query.dnf.RelationalQuery;
15import tools.refinery.store.dse.internal.TransformationRule; 23import tools.refinery.store.query.term.Variable;
16import tools.refinery.store.dse.strategy.BestFirstStrategy;
17import tools.refinery.store.dse.strategy.DepthFirstStrategy;
18import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 24import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
19import tools.refinery.store.query.view.AnySymbolView; 25import tools.refinery.store.query.view.AnySymbolView;
20import tools.refinery.store.query.view.KeyOnlyView; 26import tools.refinery.store.query.view.KeyOnlyView;
21import tools.refinery.store.representation.Symbol; 27import tools.refinery.store.representation.Symbol;
28import tools.refinery.store.statecoding.StateCoderAdapter;
22import tools.refinery.store.tuple.Tuple; 29import tools.refinery.store.tuple.Tuple;
23import tools.refinery.visualization.ModelVisualizerAdapter; 30import tools.refinery.visualization.ModelVisualizerAdapter;
24import tools.refinery.visualization.internal.FileFormat; 31import tools.refinery.visualization.internal.FileFormat;
25 32
26import java.util.List; 33import java.util.List;
27 34
35import static tools.refinery.store.dse.modification.actions.ModificationActionLiterals.create;
36import static tools.refinery.store.dse.modification.actions.ModificationActionLiterals.delete;
37import static tools.refinery.store.dse.transition.actions.ActionLiterals.add;
38import static tools.refinery.store.dse.transition.actions.ActionLiterals.remove;
28import static tools.refinery.store.query.literal.Literals.not; 39import static tools.refinery.store.query.literal.Literals.not;
29 40
30class CRAExamplesTest { 41class CRAExamplesTest {
31 private static final Symbol<String> name = Symbol.of("Name", 1, String.class); 42 private static final Symbol<String> name = Symbol.of("Name", 1, String.class);
32
33// private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
34 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1); 43 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
35// private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
36 private static final Symbol<Boolean> attribute = Symbol.of("Attribute", 1); 44 private static final Symbol<Boolean> attribute = Symbol.of("Attribute", 1);
37 private static final Symbol<Boolean> method = Symbol.of("Method", 1); 45 private static final Symbol<Boolean> method = Symbol.of("Method", 1);
38
39// private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
40 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2); 46 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
41 private static final Symbol<Boolean> dataDependency = Symbol.of("DataDependency", 2); 47 private static final Symbol<Boolean> dataDependency = Symbol.of("DataDependency", 2);
42 private static final Symbol<Boolean> functionalDependency = Symbol.of("FunctionalDependency", 2); 48 private static final Symbol<Boolean> functionalDependency = Symbol.of("FunctionalDependency", 2);
43 49
44 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
45 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
46
47// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
48 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); 50 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
49// private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
50 private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute); 51 private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute);
51 private static final AnySymbolView methodView = new KeyOnlyView<>(method); 52 private static final AnySymbolView methodView = new KeyOnlyView<>(method);
52// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
53 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); 53 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
54 private static final AnySymbolView dataDependencyView = new KeyOnlyView<>(dataDependency);
55 private static final AnySymbolView functionalDependencyView = new KeyOnlyView<>(functionalDependency);
56 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
57 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
58 54
59 /*Example Transformation rules*/ 55 private static final RelationalQuery feature = Query.of("Feature", (builder, f) -> builder
60 private static final RelationalQuery feature = Query.of("Feature",
61 (builder, f) -> builder.clause(
62 attributeView.call(f))
63 .clause( 56 .clause(
64 methodView.call(f)) 57 attributeView.call(f)
65 ); 58 )
59 .clause(
60 methodView.call(f)
61 ));
66 62
67 private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", 63 private static final RelationalQuery unEncapsulatedFeature = Query.of("unEncapsulatedFeature",
68 (builder, c, f) -> builder.clause( 64 (builder, f) -> builder.clause(
69 classElementView.call(c), 65 feature.call(f),
70// classesView.call(model, c), 66 not(encapsulatesView.call(Variable.of(), f))
71 encapsulatesView.call(c, f)
72 )); 67 ));
73 68
74 private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", 69 private static final Rule assignFeatureRule = Rule.of("AssignFeature", (builder, f, c1) -> builder
75 (builder, f, c1) -> builder.clause((c2) -> List.of( 70 .clause(
76// classModelView.call(model),
77 feature.call(f), 71 feature.call(f),
78 classElementView.call(c1), 72 classElementView.call(c1),
79// featuresView.call(model, f), 73 not(encapsulatesView.call(Variable.of(), f))
80 not(assignFeaturePreconditionHelper.call(c2, f)), 74 )
81 not(encapsulatesView.call(c1, f)) 75 .action(
82 ))); 76 add(encapsulates, c1, f)
83 77 ));
84 private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
85 (builder, c) -> builder.clause((f) -> List.of(
86// classModelView.call(model),
87 classElementView.call(c),
88// featuresView.call(model, f),
89 not(encapsulatesView.call(c, f))
90 )));
91 78
92 private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper", 79 private static final Rule deleteEmptyClassRule = Rule.of("DeleteEmptyClass", (builder, c) -> builder
93 (builder, f, c) -> builder.clause( 80 .clause(
94 classElementView.call(c), 81 classElementView.call(c),
95// classesView.call(model, c), 82 not(encapsulatesView.call(c, Variable.of()))
96 encapsulatesView.call(c, f) 83 )
84 .action(
85 remove(classElement, c),
86 delete(c, DanglingEdges.IGNORE)
97 )); 87 ));
98 88
99 private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition", 89 private static final Rule createClassRule = Rule.of("CreateClass", (builder, f) -> builder
100 (builder, f) -> builder.clause((c) -> List.of( 90 .clause(
101// classModelView.call(model),
102 feature.call(f), 91 feature.call(f),
103 not(createClassPreconditionHelper.call(f, c)) 92 not(encapsulatesView.call(Variable.of(), f))
93 )
94 .action((newClass) -> List.of(
95 create(newClass),
96 add(classElement, newClass),
97 add(encapsulates, newClass, f)
104 ))); 98 )));
105 99
106 private static final RelationalQuery moveFeaturePrecondition = Query.of("MoveFeature", 100 private static final Rule moveFeatureRule = Rule.of("MoveFeature", (builder, c1, c2, f) -> builder
107 (builder, c1, c2, f) -> builder.clause( 101 .clause(
108// classModelView.call(model),
109 classElementView.call(c1), 102 classElementView.call(c1),
110 classElementView.call(c2), 103 classElementView.call(c2),
111 c1.notEquivalent(c2), 104 c1.notEquivalent(c2),
112 feature.call(f), 105 feature.call(f),
113// classesView.call(model, c1),
114// classesView.call(model, c2),
115// featuresView.call(model, f),
116 encapsulatesView.call(c1, f) 106 encapsulatesView.call(c1, f)
107 )
108 .action(
109 remove(encapsulates, c1, f),
110 add(encapsulates, c2, f)
117 )); 111 ));
118 112
119 private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature",
120 assignFeaturePrecondition,
121 (model) -> {
122// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy);
123 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
124 return ((Tuple activation) -> {
125 var feature = activation.get(0);
126 var classElement = activation.get(1);
127
128// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true);
129 encapsulatesInterpretation.put(Tuple.of(classElement, feature), true);
130 });
131 });
132
133 private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
134 deleteEmptyClassPrecondition,
135 (model) -> {
136// var classesInterpretation = model.getInterpretation(classes);
137 var classElementInterpretation = model.getInterpretation(classElement);
138 return ((Tuple activation) -> {
139 // TODO: can we move dseAdapter outside?
140 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
141// var modelElement = activation.get(0);
142 var classElement = activation.get(0);
143
144// classesInterpretation.put(Tuple.of(modelElement, classElement), false);
145 classElementInterpretation.put(Tuple.of(classElement), false);
146 dseAdapter.deleteObject(Tuple.of(classElement));
147 });
148 });
149
150 private static final TransformationRule createClassRule = new TransformationRule("CreateClass",
151 createClassPrecondition,
152 (model) -> {
153 var classElementInterpretation = model.getInterpretation(classElement);
154// var classesInterpretation = model.getInterpretation(classes);
155 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
156 return ((Tuple activation) -> {
157 // TODO: can we move dseAdapter outside?
158 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
159// var modelElement = activation.get(0);
160 var feature = activation.get(0);
161
162 var newClassElement = dseAdapter.createObject();
163 var newClassElementId = newClassElement.get(0);
164 classElementInterpretation.put(newClassElement, true);
165// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
166 encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true);
167 });
168 });
169
170 private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature",
171 moveFeaturePrecondition,
172 (model) -> {
173 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
174 return ((Tuple activation) -> {
175 var classElement1 = activation.get(0);
176 var classElement2 = activation.get(1);
177 var feature = activation.get(2);
178
179 encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false);
180 encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true);
181 });
182 });
183
184 @Test 113 @Test
185 @Disabled("This test is only for debugging purposes") 114 @Disabled("This test is only for debugging purposes")
186 void craTest() { 115 void craTest() {
187 var store = ModelStore.builder() 116 var store = ModelStore.builder()
188 .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, 117 .symbols(classElement, encapsulates, attribute, method, dataDependency, functionalDependency, name)
189 functionalDependency, name) 118 .with(ViatraModelQueryAdapter.builder())
190 .with(ViatraModelQueryAdapter.builder()
191 .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition,
192 deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition,
193 moveFeaturePrecondition))
194 .with(ModelVisualizerAdapter.builder() 119 .with(ModelVisualizerAdapter.builder()
195 .withOutputpath("test_output") 120 .withOutputPath("test_output")
196 .withFormat(FileFormat.DOT) 121 .withFormat(FileFormat.DOT)
197 .withFormat(FileFormat.SVG) 122 .withFormat(FileFormat.SVG)
198 .saveStates() 123 .saveStates()
199 .saveDesignSpace() 124 .saveDesignSpace())
200 ) 125 .with(StateCoderAdapter.builder())
126 .with(ModificationAdapter.builder())
201 .with(DesignSpaceExplorationAdapter.builder() 127 .with(DesignSpaceExplorationAdapter.builder()
202 .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) 128 .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule)
203 .objectives(new AlwaysSatisfiedRandomHardObjective()) 129 .objectives(Objectives.sum(
204// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() 130 new DummyRandomObjective(),
205 .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() 131 Objectives.count(unEncapsulatedFeature)
206// .goOnOnlyIfFitnessIsBetter()
207 )) 132 ))
133 .accept(Criteria.whenNoMatch(unEncapsulatedFeature))
134 .exclude(new DummyCriterion(false)))
208 .build(); 135 .build();
209 136
210 var model = store.createEmptyModel(); 137 var model = store.createEmptyModel();
211 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
212// dseAdapter.setRandom(1);
213 var queryEngine = model.getAdapter(ModelQueryAdapter.class); 138 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
214 139
215// var modelInterpretation = model.getInterpretation(classModel);
216 var nameInterpretation = model.getInterpretation(name); 140 var nameInterpretation = model.getInterpretation(name);
217 var methodInterpretation = model.getInterpretation(method); 141 var methodInterpretation = model.getInterpretation(method);
218 var attributeInterpretation = model.getInterpretation(attribute); 142 var attributeInterpretation = model.getInterpretation(attribute);
219 var dataDependencyInterpretation = model.getInterpretation(dataDependency); 143 var dataDependencyInterpretation = model.getInterpretation(dataDependency);
220 var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); 144 var functionalDependencyInterpretation = model.getInterpretation(functionalDependency);
221 145
222// var modelElement = dseAdapter.createObject(); 146 var modificationAdapter = model.getAdapter(ModificationAdapter.class);
223 var method1 = dseAdapter.createObject(); 147
148 var method1 = modificationAdapter.createObject();
224 var method1Id = method1.get(0); 149 var method1Id = method1.get(0);
225 var method2 = dseAdapter.createObject(); 150 var method2 = modificationAdapter.createObject();
226 var method2Id = method2.get(0); 151 var method2Id = method2.get(0);
227 var method3 = dseAdapter.createObject(); 152 var method3 = modificationAdapter.createObject();
228 var method3Id = method3.get(0); 153 var method3Id = method3.get(0);
229 var method4 = dseAdapter.createObject(); 154 var method4 = modificationAdapter.createObject();
230 var method4Id = method4.get(0); 155 var method4Id = method4.get(0);
231 var attribute1 = dseAdapter.createObject(); 156 var attribute1 = modificationAdapter.createObject();
232 var attribute1Id = attribute1.get(0); 157 var attribute1Id = attribute1.get(0);
233 var attribute2 = dseAdapter.createObject(); 158 var attribute2 = modificationAdapter.createObject();
234 var attribute2Id = attribute2.get(0); 159 var attribute2Id = attribute2.get(0);
235 var attribute3 = dseAdapter.createObject(); 160 var attribute3 = modificationAdapter.createObject();
236 var attribute3Id = attribute3.get(0); 161 var attribute3Id = attribute3.get(0);
237 var attribute4 = dseAdapter.createObject(); 162 var attribute4 = modificationAdapter.createObject();
238 var attribute4Id = attribute4.get(0); 163 var attribute4Id = attribute4.get(0);
239 var attribute5 = dseAdapter.createObject(); 164 var attribute5 = modificationAdapter.createObject();
240 var attribute5Id = attribute5.get(0); 165 var attribute5Id = attribute5.get(0);
241 166
242 nameInterpretation.put(method1, "M1"); 167 nameInterpretation.put(method1, "M1");
@@ -249,9 +174,6 @@ class CRAExamplesTest {
249 nameInterpretation.put(attribute4, "A4"); 174 nameInterpretation.put(attribute4, "A4");
250 nameInterpretation.put(attribute5, "A5"); 175 nameInterpretation.put(attribute5, "A5");
251 176
252
253
254// modelInterpretation.put(modelElement, true);
255 methodInterpretation.put(method1, true); 177 methodInterpretation.put(method1, true);
256 methodInterpretation.put(method2, true); 178 methodInterpretation.put(method2, true);
257 methodInterpretation.put(method3, true); 179 methodInterpretation.put(method3, true);
@@ -277,10 +199,13 @@ class CRAExamplesTest {
277 functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); 199 functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true);
278 functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); 200 functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true);
279 201
202 var initialVersion = model.commit();
280 queryEngine.flushChanges(); 203 queryEngine.flushChanges();
281 204
282 var states = dseAdapter.explore(); 205 var bestFirst = new BestFirstStoreManager(store, 50);
283 System.out.println("states size: " + states.size()); 206 bestFirst.startExploration(initialVersion);
207 var resultStore = bestFirst.getSolutionStore();
208 System.out.println("states size: " + resultStore.getSolutions().size());
209 model.getAdapter(ModelVisualizerAdapter.class).visualize(bestFirst.getVisualizationStore());
284 } 210 }
285
286} 211}
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..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
@@ -7,110 +7,79 @@ package tools.refinery.store.dse;
7 7
8import org.junit.jupiter.api.Disabled; 8import org.junit.jupiter.api.Disabled;
9import org.junit.jupiter.api.Test; 9import org.junit.jupiter.api.Test;
10import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; 10import tools.refinery.store.dse.modification.ModificationAdapter;
11import tools.refinery.store.dse.strategy.BestFirstStoreManager;
12import tools.refinery.store.dse.tests.DummyRandomCriterion;
13import tools.refinery.store.dse.tests.DummyRandomObjective;
14import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter;
15import tools.refinery.store.dse.transition.Rule;
11import tools.refinery.store.model.ModelStore; 16import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.query.ModelQueryAdapter; 17import tools.refinery.store.query.ModelQueryAdapter;
13import tools.refinery.store.query.dnf.Query;
14import tools.refinery.store.dse.internal.TransformationRule;
15import tools.refinery.store.dse.strategy.BestFirstStrategy;
16import tools.refinery.store.dse.strategy.DepthFirstStrategy;
17import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 18import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
18import tools.refinery.store.query.view.AnySymbolView; 19import tools.refinery.store.query.view.AnySymbolView;
19import tools.refinery.store.query.view.KeyOnlyView; 20import tools.refinery.store.query.view.KeyOnlyView;
20import tools.refinery.store.representation.Symbol; 21import tools.refinery.store.representation.Symbol;
21import tools.refinery.store.tuple.Tuple; 22import tools.refinery.store.statecoding.StateCoderAdapter;
22import tools.refinery.visualization.ModelVisualizerAdapter; 23import tools.refinery.visualization.ModelVisualizerAdapter;
23import tools.refinery.visualization.internal.FileFormat; 24import tools.refinery.visualization.internal.FileFormat;
24 25
26import java.util.List;
27
28import static tools.refinery.store.dse.modification.actions.ModificationActionLiterals.create;
29import static tools.refinery.store.dse.transition.actions.ActionLiterals.add;
30
25class DebugTest { 31class DebugTest {
26 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1); 32 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
27 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1); 33 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
28 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1); 34 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
29
30 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
31 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
32
33 private static final Symbol<Boolean> features = Symbol.of("Features", 2); 35 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
34 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2); 36 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
35 37
36 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); 38 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
37 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
38 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
39 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
40 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
41 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
42 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
43
44 39
45 @Test 40 @Test
46 @Disabled("This test is only for debugging purposes") 41 @Disabled("This test is only for debugging purposes")
47 void BFSTest() { 42 void BFSTest() {
48 var createClassPrecondition = Query.of("CreateClassPrecondition", 43 var createClassRule = Rule.of("CreateClass", (builder, model) -> builder
49 (builder, model) -> builder.clause( 44 .clause(
50 classModelView.call(model) 45 classModelView.call(model)
51 )); 46 )
52 47 .action((newClassElement) -> List.of(
53 var createClassRule = new TransformationRule("CreateClass", 48 create(newClassElement),
54 createClassPrecondition, 49 add(classElement, newClassElement),
55 (model) -> { 50 add(classes, model, newClassElement)
56 var classesInterpretation = model.getInterpretation(classes); 51 )));
57 var classElementInterpretation = model.getInterpretation(classElement); 52
58 return ((Tuple activation) -> { 53 var createFeatureRule = Rule.of("CreateFeature", (builder, model) -> builder
59 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); 54 .clause(
60 var modelElement = activation.get(0);
61
62 var newClassElement = dseAdapter.createObject();
63 var newClassElementId = newClassElement.get(0);
64
65 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
66 classElementInterpretation.put(Tuple.of(newClassElementId), true);
67 });
68 });
69
70 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
71 (builder, model) -> builder.clause(
72 classModelView.call(model) 55 classModelView.call(model)
73 )); 56 )
74 57 .action((newFeature) -> List.of(
75 var createFeatureRule = new TransformationRule("CreateFeature", 58 create(newFeature),
76 createFeaturePrecondition, 59 add(feature, newFeature),
77 (model) -> { 60 add(features, model, newFeature)
78 var featuresInterpretation = model.getInterpretation(features); 61 )));
79 var featureInterpretation = model.getInterpretation(feature);
80 return ((Tuple activation) -> {
81 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
82 var modelElement = activation.get(0);
83
84 var newClassElement = dseAdapter.createObject();
85 var newClassElementId = newClassElement.get(0);
86
87 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
88 featureInterpretation.put(Tuple.of(newClassElementId), true);
89 });
90 });
91 62
92 var store = ModelStore.builder() 63 var store = ModelStore.builder()
93 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) 64 .symbols(classModel, classElement, feature, classes, features)
94 .with(ViatraModelQueryAdapter.builder() 65 .with(ViatraModelQueryAdapter.builder())
95 .queries(createClassPrecondition, createFeaturePrecondition))
96 .with(ModelVisualizerAdapter.builder() 66 .with(ModelVisualizerAdapter.builder()
97 .withOutputpath("test_output") 67 .withOutputPath("test_output")
98 .withFormat(FileFormat.DOT) 68 .withFormat(FileFormat.DOT)
99 .withFormat(FileFormat.SVG) 69 .withFormat(FileFormat.SVG)
100 .saveStates() 70 .saveStates()
101 .saveDesignSpace() 71 .saveDesignSpace())
102 ) 72 .with(StateCoderAdapter.builder())
73 .with(ModificationAdapter.builder())
103 .with(DesignSpaceExplorationAdapter.builder() 74 .with(DesignSpaceExplorationAdapter.builder()
104 .transformations(createClassRule, createFeatureRule) 75 .transformations(createClassRule, createFeatureRule)
105 .objectives(new AlwaysSatisfiedRandomHardObjective()) 76 .objectives(new DummyRandomObjective())
106 .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() 77 .accept(new DummyRandomCriterion())
107// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() 78 .exclude(new DummyRandomCriterion()))
108// .goOnOnlyIfFitnessIsBetter()
109 ))
110 .build(); 79 .build();
111 80
112 var model = store.createEmptyModel(); 81 var model = store.createEmptyModel();
113 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); 82 var dseAdapter = model.getAdapter(ModificationAdapter.class);
114// dseAdapter.setRandom(1); 83// dseAdapter.setRandom(1);
115 var queryEngine = model.getAdapter(ModelQueryAdapter.class); 84 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
116 85
@@ -119,11 +88,12 @@ class DebugTest {
119 var modelElement = dseAdapter.createObject(); 88 var modelElement = dseAdapter.createObject();
120 modelElementInterpretation.put(modelElement, true); 89 modelElementInterpretation.put(modelElement, true);
121 classElementInterpretation.put(modelElement, true); 90 classElementInterpretation.put(modelElement, true);
91 var initialVersion = model.commit();
122 queryEngine.flushChanges(); 92 queryEngine.flushChanges();
123 93
124 94 var bestFirst = new BestFirstStoreManager(store, 50);
125 var states = dseAdapter.explore(); 95 bestFirst.startExploration(initialVersion);
126 System.out.println("states size: " + states.size()); 96 var resultStore = bestFirst.getSolutionStore();
127 97 System.out.println("states size: " + resultStore.getSolutions().size());
128 } 98 }
129} 99}
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
deleted file mode 100644
index 91e33f4a..00000000
--- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java
+++ /dev/null
@@ -1,607 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.dse.internal.TransformationRule;
13import tools.refinery.store.dse.strategy.BestFirstStrategy;
14import tools.refinery.store.dse.strategy.DepthFirstStrategy;
15import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
16import tools.refinery.store.query.view.AnySymbolView;
17import tools.refinery.store.query.view.KeyOnlyView;
18import tools.refinery.store.representation.Symbol;
19import tools.refinery.store.tuple.Tuple;
20
21import static org.junit.jupiter.api.Assertions.assertEquals;
22
23class DesignSpaceExplorationTest {
24// private static final Symbol<Boolean> namedElement = Symbol.of("NamedElement", 1);
25// private static final Symbol<Boolean> attribute = Symbol.of("Attribute", 1);
26// private static final Symbol<Boolean> method = Symbol.of("Method", 1);
27// private static final Symbol<Boolean> dataDependency = Symbol.of("DataDependency", 2);
28// private static final Symbol<Boolean> functionalDependency = Symbol.of("FunctionalDependency", 2);
29
30 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
31 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
32 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
33
34 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
35 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
36
37 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
38 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
39
40 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
41 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
42 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
43 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
44 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
45 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
46 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
47
48 @Test
49 void createObjectTest() {
50 var store = ModelStore.builder()
51 .with(ViatraModelQueryAdapter.builder())
52 .with(DesignSpaceExplorationAdapter.builder()
53 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
54 .build();
55
56 var model = store.createEmptyModel();
57 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
58
59 assertEquals(0, dseAdapter.getModelSize());
60
61 var newModel = dseAdapter.createObject();
62 var newModelId = newModel.get(0);
63 var newClass1 = dseAdapter.createObject();
64 var newClass1Id = newClass1.get(0);
65 var newClass2 = dseAdapter.createObject();
66 var newClass2Id = newClass2.get(0);
67 var newField = dseAdapter.createObject();
68 var newFieldId = newField.get(0);
69
70 assertEquals(0, newModelId);
71 assertEquals(1, newClass1Id);
72 assertEquals(2, newClass2Id);
73 assertEquals(3, newFieldId);
74 assertEquals(4, dseAdapter.getModelSize());
75 }
76
77 @Test
78 void deleteMiddleObjectTest() {
79 var store = ModelStore.builder()
80 .with(ViatraModelQueryAdapter.builder())
81 .with(DesignSpaceExplorationAdapter.builder()
82 .strategy(new DepthFirstStrategy()))
83 .build();
84
85 var model = store.createEmptyModel();
86 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
87
88 assertEquals(0, dseAdapter.getModelSize());
89
90 var newObject0 = dseAdapter.createObject();
91 var newObject0Id = newObject0.get(0);
92 var newObject1 = dseAdapter.createObject();
93 var newObject1Id = newObject1.get(0);
94 var newObject2 = dseAdapter.createObject();
95 var newObject2Id = newObject2.get(0);
96 var newObject3 = dseAdapter.createObject();
97 var newObject3Id = newObject3.get(0);
98
99 assertEquals(0, newObject0Id);
100 assertEquals(1, newObject1Id);
101 assertEquals(2, newObject2Id);
102 assertEquals(3, newObject3Id);
103 assertEquals(4, dseAdapter.getModelSize());
104
105 dseAdapter.deleteObject(newObject1);
106 assertEquals(4, dseAdapter.getModelSize());
107
108 var newObject4 = dseAdapter.createObject();
109 var newObject4Id = newObject4.get(0);
110 assertEquals(4, newObject4Id);
111 assertEquals(5, dseAdapter.getModelSize());
112
113 dseAdapter.deleteObject(newObject4);
114 assertEquals(5, dseAdapter.getModelSize());
115 }
116
117 @Test
118 void DFSTrivialTest() {
119 var store = ModelStore.builder()
120 .symbols(classModel)
121 .with(ViatraModelQueryAdapter.builder())
122 .with(DesignSpaceExplorationAdapter.builder()
123 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
124 .build();
125
126 var model = store.createEmptyModel();
127 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
128
129 var states = dseAdapter.explore();
130 assertEquals(1, states.size());
131 }
132
133 @Test
134 void DFSOneRuleTest() {
135 var createClassPrecondition = Query.of("CreateClassPrecondition",
136 (builder, model) -> builder.clause(
137 classModelView.call(model)
138 ));
139
140 var createClassRule = new TransformationRule("CreateClass",
141 createClassPrecondition,
142 (model) -> {
143 var classesInterpretation = model.getInterpretation(classes);
144 var classElementInterpretation = model.getInterpretation(classElement);
145 return ((Tuple activation) -> {
146 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
147 var modelElement = activation.get(0);
148
149 var newClassElement = dseAdapter.createObject();
150 var newClassElementId = newClassElement.get(0);
151
152 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
153 classElementInterpretation.put(Tuple.of(newClassElementId), true);
154 });
155 });
156
157 var store = ModelStore.builder()
158 .symbols(classModel, classElement, classes)
159 .with(ViatraModelQueryAdapter.builder()
160 .queries(createClassPrecondition))
161 .with(DesignSpaceExplorationAdapter.builder()
162 .transformations(createClassRule)
163 .strategy(new DepthFirstStrategy().withDepthLimit(0)
164 ))
165 .build();
166
167 var model = store.createEmptyModel();
168 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
169 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
170
171 var modelElementInterpretation = model.getInterpretation(classModel);
172 modelElementInterpretation.put(dseAdapter.createObject(), true);
173 queryEngine.flushChanges();
174
175 var states = dseAdapter.explore();
176 assertEquals(1, states.size());
177 }
178
179 @Test
180 void DFSContinueTest() {
181 var createClassPrecondition = Query.of("CreateClassPrecondition",
182 (builder, model) -> builder.clause(
183 classModelView.call(model)
184 ));
185
186 var createClassRule = new TransformationRule("CreateClass",
187 createClassPrecondition,
188 (model) -> {
189 var classesInterpretation = model.getInterpretation(classes);
190 var classElementInterpretation = model.getInterpretation(classElement);
191 return ((Tuple activation) -> {
192 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
193 var modelElement = activation.get(0);
194
195 var newClassElement = dseAdapter.createObject();
196 var newClassElementId = newClassElement.get(0);
197
198 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
199 classElementInterpretation.put(Tuple.of(newClassElementId), true);
200 });
201 });
202
203 var store = ModelStore.builder()
204 .symbols(classModel, classElement, classes)
205 .with(ViatraModelQueryAdapter.builder()
206 .queries(createClassPrecondition))
207 .with(DesignSpaceExplorationAdapter.builder()
208 .transformations(createClassRule)
209 .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled()
210 ))
211 .build();
212
213 var model = store.createEmptyModel();
214 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
215 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
216
217 var modelElementInterpretation = model.getInterpretation(classModel);
218 modelElementInterpretation.put(dseAdapter.createObject(), true);
219 queryEngine.flushChanges();
220
221 var states = dseAdapter.explore();
222 assertEquals(5, states.size());
223 }
224
225 @Test
226 void DFSCompletenessTest() {
227 var createClassPrecondition = Query.of("CreateClassPrecondition",
228 (builder, model) -> builder.clause(
229 classModelView.call(model)
230 ));
231
232 var createClassRule = new TransformationRule("CreateClass",
233 createClassPrecondition,
234 (model) -> {
235 var classesInterpretation = model.getInterpretation(classes);
236 var classElementInterpretation = model.getInterpretation(classElement);
237 return ((Tuple activation) -> {
238 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
239 var modelElement = activation.get(0);
240
241 var newClassElement = dseAdapter.createObject();
242 var newClassElementId = newClassElement.get(0);
243
244 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
245 classElementInterpretation.put(Tuple.of(newClassElementId), true);
246 });
247 });
248
249 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
250 (builder, model) -> builder.clause(
251 classModelView.call(model)
252 ));
253
254 var createFeatureRule = new TransformationRule("CreateFeature",
255 createFeaturePrecondition,
256 (model) -> {
257 var featuresInterpretation = model.getInterpretation(features);
258 var featureInterpretation = model.getInterpretation(feature);
259 return ((Tuple activation) -> {
260 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
261 var modelElement = activation.get(0);
262
263 var newClassElement = dseAdapter.createObject();
264 var newClassElementId = newClassElement.get(0);
265
266 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
267 featureInterpretation.put(Tuple.of(newClassElementId), true);
268 });
269 });
270
271 var store = ModelStore.builder()
272 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
273 .with(ViatraModelQueryAdapter.builder()
274 .queries(createClassPrecondition, createFeaturePrecondition))
275 .with(DesignSpaceExplorationAdapter.builder()
276 .transformations(createClassRule, createFeatureRule)
277 .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled()
278 ))
279 .build();
280
281 var model = store.createEmptyModel();
282 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
283 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
284
285 var modelElementInterpretation = model.getInterpretation(classModel);
286 modelElementInterpretation.put(dseAdapter.createObject(), true);
287 queryEngine.flushChanges();
288
289 var states = dseAdapter.explore();
290 assertEquals(2047, states.size());
291 }
292
293 @Test
294 void DFSSolutionLimitTest() {
295 var createClassPrecondition = Query.of("CreateClassPrecondition",
296 (builder, model) -> builder.clause(
297 classModelView.call(model)
298 ));
299
300 var createClassRule = new TransformationRule("CreateClass",
301 createClassPrecondition,
302 (model) -> {
303 var classesInterpretation = model.getInterpretation(classes);
304 var classElementInterpretation = model.getInterpretation(classElement);
305 return ((Tuple activation) -> {
306 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
307 var modelElement = activation.get(0);
308
309 var newClassElement = dseAdapter.createObject();
310 var newClassElementId = newClassElement.get(0);
311
312 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
313 classElementInterpretation.put(Tuple.of(newClassElementId), true);
314 });
315 });
316
317 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
318 (builder, model) -> builder.clause(
319 classModelView.call(model)
320 ));
321
322 var createFeatureRule = new TransformationRule("CreateFeature",
323 createFeaturePrecondition,
324 (model) -> {
325 var featuresInterpretation = model.getInterpretation(features);
326 var featureInterpretation = model.getInterpretation(feature);
327 return ((Tuple activation) -> {
328 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
329 var modelElement = activation.get(0);
330
331 var newClassElement = dseAdapter.createObject();
332 var newClassElementId = newClassElement.get(0);
333
334 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
335 featureInterpretation.put(Tuple.of(newClassElementId), true);
336 });
337 });
338
339 var store = ModelStore.builder()
340 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
341 .with(ViatraModelQueryAdapter.builder()
342 .queries(createClassPrecondition, createFeaturePrecondition))
343 .with(DesignSpaceExplorationAdapter.builder()
344 .transformations(createClassRule, createFeatureRule)
345 .strategy(new DepthFirstStrategy().withSolutionLimit(222)
346 .continueIfHardObjectivesFulfilled()
347 ))
348 .build();
349
350 var model = store.createEmptyModel();
351 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
352 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
353
354 var modelElementInterpretation = model.getInterpretation(classModel);
355 modelElementInterpretation.put(dseAdapter.createObject(), true);
356 queryEngine.flushChanges();
357
358 var states = dseAdapter.explore();
359 assertEquals(222, states.size());
360 }
361
362 @Test
363 void BeFSTrivialTest() {
364 var store = ModelStore.builder()
365 .symbols(classModel)
366 .with(ViatraModelQueryAdapter.builder())
367 .with(DesignSpaceExplorationAdapter.builder()
368 .strategy(new BestFirstStrategy().withDepthLimit(0)))
369 .build();
370
371 var model = store.createEmptyModel();
372 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
373
374 var states = dseAdapter.explore();
375 assertEquals(1, states.size());
376 }
377
378 @Test
379 void BeFSOneRuleTest() {
380 var createClassPrecondition = Query.of("CreateClassPrecondition",
381 (builder, model) -> builder.clause(
382 classModelView.call(model)
383 ));
384
385 var createClassRule = new TransformationRule("CreateClass",
386 createClassPrecondition,
387 (model) -> {
388 var classesInterpretation = model.getInterpretation(classes);
389 var classElementInterpretation = model.getInterpretation(classElement);
390 return ((Tuple activation) -> {
391 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
392 var modelElement = activation.get(0);
393
394 var newClassElement = dseAdapter.createObject();
395 var newClassElementId = newClassElement.get(0);
396
397 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
398 classElementInterpretation.put(Tuple.of(newClassElementId), true);
399 });
400 });
401
402 var store = ModelStore.builder()
403 .symbols(classModel, classElement, classes)
404 .with(ViatraModelQueryAdapter.builder()
405 .queries(createClassPrecondition))
406 .with(DesignSpaceExplorationAdapter.builder()
407 .transformations(createClassRule)
408 .strategy(new BestFirstStrategy().withDepthLimit(4)
409 ))
410 .build();
411
412 var model = store.createEmptyModel();
413 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
414 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
415
416 var modelElementInterpretation = model.getInterpretation(classModel);
417 modelElementInterpretation.put(dseAdapter.createObject(), true);
418 queryEngine.flushChanges();
419
420 var states = dseAdapter.explore();
421 assertEquals(1, states.size());
422 }
423
424 @Test
425 void BeFSContinueTest() {
426 var createClassPrecondition = Query.of("CreateClassPrecondition",
427 (builder, model) -> builder.clause(
428 classModelView.call(model)
429 ));
430
431 var createClassRule = new TransformationRule("CreateClass",
432 createClassPrecondition,
433 (model) -> {
434 var classesInterpretation = model.getInterpretation(classes);
435 var classElementInterpretation = model.getInterpretation(classElement);
436 return ((Tuple activation) -> {
437 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
438 var modelElement = activation.get(0);
439
440 var newClassElement = dseAdapter.createObject();
441 var newClassElementId = newClassElement.get(0);
442
443 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
444 classElementInterpretation.put(Tuple.of(newClassElementId), true);
445 });
446 });
447
448 var store = ModelStore.builder()
449 .symbols(classModel, classElement, classes)
450 .with(ViatraModelQueryAdapter.builder()
451 .queries(createClassPrecondition))
452 .with(DesignSpaceExplorationAdapter.builder()
453 .transformations(createClassRule)
454 .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled()
455 ))
456 .build();
457
458 var model = store.createEmptyModel();
459 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
460 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
461
462 var modelElementInterpretation = model.getInterpretation(classModel);
463 modelElementInterpretation.put(dseAdapter.createObject(), true);
464 queryEngine.flushChanges();
465
466 var states = dseAdapter.explore();
467 assertEquals(5, states.size());
468 }
469
470 @Test
471 void BeFSCompletenessTest() {
472 var createClassPrecondition = Query.of("CreateClassPrecondition",
473 (builder, model) -> builder.clause(
474 classModelView.call(model)
475 ));
476
477 var createClassRule = new TransformationRule("CreateClass",
478 createClassPrecondition,
479 (model) -> {
480 var classesInterpretation = model.getInterpretation(classes);
481 var classElementInterpretation = model.getInterpretation(classElement);
482 return ((Tuple activation) -> {
483 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
484 var modelElement = activation.get(0);
485
486 var newClassElement = dseAdapter.createObject();
487 var newClassElementId = newClassElement.get(0);
488
489 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
490 classElementInterpretation.put(Tuple.of(newClassElementId), true);
491 });
492 });
493
494 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
495 (builder, model) -> builder.clause(
496 classModelView.call(model)
497 ));
498
499 var createFeatureRule = new TransformationRule("CreateFeature",
500 createFeaturePrecondition,
501 (model) -> {
502 var featuresInterpretation = model.getInterpretation(features);
503 var featureInterpretation = model.getInterpretation(feature);
504 return ((Tuple activation) -> {
505 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
506 var modelElement = activation.get(0);
507
508 var newClassElement = dseAdapter.createObject();
509 var newClassElementId = newClassElement.get(0);
510
511 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
512 featureInterpretation.put(Tuple.of(newClassElementId), true);
513 });
514 });
515
516 var store = ModelStore.builder()
517 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
518 .with(ViatraModelQueryAdapter.builder()
519 .queries(createClassPrecondition, createFeaturePrecondition))
520 .with(DesignSpaceExplorationAdapter.builder()
521 .transformations(createClassRule, createFeatureRule)
522 .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled()
523 ))
524 .build();
525
526 var model = store.createEmptyModel();
527 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
528 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
529
530 var modelElementInterpretation = model.getInterpretation(classModel);
531 modelElementInterpretation.put(dseAdapter.createObject(), true);
532 queryEngine.flushChanges();
533
534 var states = dseAdapter.explore();
535 assertEquals(2047, states.size());
536 }
537
538 @Test
539 void BeFSSolutionLimitTest() {
540 var createClassPrecondition = Query.of("CreateClassPrecondition",
541 (builder, model) -> builder.clause(
542 classModelView.call(model)
543 ));
544
545 var createClassRule = new TransformationRule("CreateClass",
546 createClassPrecondition,
547 (model) -> {
548 var classesInterpretation = model.getInterpretation(classes);
549 var classElementInterpretation = model.getInterpretation(classElement);
550 return ((Tuple activation) -> {
551 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
552 var modelElement = activation.get(0);
553
554 var newClassElement = dseAdapter.createObject();
555 var newClassElementId = newClassElement.get(0);
556
557 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
558 classElementInterpretation.put(Tuple.of(newClassElementId), true);
559 });
560 });
561
562 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
563 (builder, model) -> builder.clause(
564 classModelView.call(model)
565 ));
566
567 var createFeatureRule = new TransformationRule("CreateFeature",
568 createFeaturePrecondition,
569 (model) -> {
570 var featuresInterpretation = model.getInterpretation(features);
571 var featureInterpretation = model.getInterpretation(feature);
572 return ((Tuple activation) -> {
573 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
574 var modelElement = activation.get(0);
575
576 var newClassElement = dseAdapter.createObject();
577 var newClassElementId = newClassElement.get(0);
578
579 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
580 featureInterpretation.put(Tuple.of(newClassElementId), true);
581 });
582 });
583
584 var store = ModelStore.builder()
585 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
586 .with(ViatraModelQueryAdapter.builder()
587 .queries(createClassPrecondition, createFeaturePrecondition))
588 .with(DesignSpaceExplorationAdapter.builder()
589 .transformations(createClassRule, createFeatureRule)
590 .strategy(new BestFirstStrategy().withSolutionLimit(222)
591 .continueIfHardObjectivesFulfilled()
592 ))
593 .build();
594
595 var model = store.createEmptyModel();
596 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
597 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
598
599 var modelElementInterpretation = model.getInterpretation(classModel);
600 modelElementInterpretation.put(dseAdapter.createObject(), true);
601 queryEngine.flushChanges();
602
603 var states = dseAdapter.explore();
604 assertEquals(222, states.size());
605 }
606
607}
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
deleted file mode 100644
index 5d24d712..00000000
--- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java
+++ /dev/null
@@ -1,413 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.dse.strategy.DepthFirstStrategy;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.dse.internal.TransformationRule;
14import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
15import tools.refinery.store.query.view.AnySymbolView;
16import tools.refinery.store.query.view.KeyOnlyView;
17import tools.refinery.store.representation.Symbol;
18import tools.refinery.store.tuple.Tuple;
19
20import java.util.List;
21import java.util.Map;
22
23import static org.junit.jupiter.api.Assertions.assertEquals;
24import static tools.refinery.store.query.literal.Literals.not;
25import static tools.refinery.store.dse.tests.QueryAssertions.assertResults;
26
27class TransformationRuleTest {
28
29 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
30 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
31 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
32
33 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
34 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
35
36 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
37 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
38
39 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
40 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
41 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
42 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
43 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
44 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
45 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
46
47 @Test
48 void activationsTest() {
49 var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper",
50 (builder, model, c, f) -> builder.clause(
51 classElementView.call(c),
52 classesView.call(model, c),
53 encapsulatesView.call(c, f)
54 ));
55
56 var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f)
57 -> builder.clause((model, c1) -> List.of(
58 classModelView.call(model),
59 featureView.call(f),
60 classElementView.call(c2),
61 featuresView.call(model, f),
62 classesView.call(model, c1),
63 not(assignFeaturePreconditionHelper.call(model, c2, f)),
64 not(encapsulatesView.call(c2, f))
65 )));
66
67 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
68 (builder, model, c) -> builder.clause((f) -> List.of(
69 classModelView.call(model),
70 classElementView.call(c),
71 featuresView.call(model, f),
72 not(encapsulatesView.call(c, f))
73 )));
74
75 TransformationRule assignFeatureRule = new TransformationRule("AssignFeature",
76 assignFeaturePrecondition,
77 (model) -> {
78 var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy);
79 return ((Tuple activation) -> {
80 var feature = activation.get(0);
81 var classElement = activation.get(1);
82
83 isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true);
84 });
85 });
86
87 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
88 deleteEmptyClassPrecondition,
89 (model) -> {
90 var classesInterpretation = model.getInterpretation(classes);
91 var classElementInterpretation = model.getInterpretation(classElement);
92 return ((Tuple activation) -> {
93 var modelElement = activation.get(0);
94 var classElement = activation.get(1);
95
96 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
97 classElementInterpretation.put(Tuple.of(classElement), false);
98 });
99 });
100
101
102 var store = ModelStore.builder()
103 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
104 .with(ViatraModelQueryAdapter.builder()
105 .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper,
106 deleteEmptyClassPrecondition))
107 .with(DesignSpaceExplorationAdapter.builder()
108 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
109 .build();
110
111 var model = store.createEmptyModel();
112 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
113 assignFeatureRule.prepare(model, queryEngine);
114 deleteEmptyClassRule.prepare(model, queryEngine);
115
116 var classModelInterpretation = model.getInterpretation(classModel);
117 var classElementInterpretation = model.getInterpretation(classElement);
118 var featureInterpretation = model.getInterpretation(feature);
119 var featuresInterpretation = model.getInterpretation(features);
120 var classesInterpretation = model.getInterpretation(classes);
121
122 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
123 var newModel = dseAdapter.createObject();
124 var newModelId = newModel.get(0);
125 var newClass1 = dseAdapter.createObject();
126 var newClass1Id = newClass1.get(0);
127 var newClass2 = dseAdapter.createObject();
128 var newClass2Id = newClass2.get(0);
129 var newField = dseAdapter.createObject();
130 var newFieldId = newField.get(0);
131
132 classModelInterpretation.put(newModel, true);
133 classElementInterpretation.put(newClass1, true);
134 classElementInterpretation.put(newClass2, true);
135 featureInterpretation.put(newField, true);
136 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
137 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
138 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
139
140 queryEngine.flushChanges();
141
142 var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet();
143 var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet();
144
145 assertResults(Map.of(
146 Tuple.of(newClass1Id, newFieldId), true,
147 Tuple.of(newClass2Id, newFieldId), true
148 ), assignFeatureRuleActivations);
149
150 assertResults(Map.of(
151 Tuple.of(newModelId, newClass1Id), true,
152 Tuple.of(newModelId, newClass2Id), true
153 ), deleteEmptyClassRuleActivations);
154 }
155
156 @Test
157 void randomActivationTest() {
158 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
159 (builder, model, c) -> builder.clause((f) -> List.of(
160 classModelView.call(model),
161 classElementView.call(c),
162 featuresView.call(model, f),
163 not(encapsulatesView.call(c, f))
164 )));
165
166 TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0",
167 deleteEmptyClassPrecondition,
168 (model) -> {
169 var classesInterpretation = model.getInterpretation(classes);
170 var classElementInterpretation = model.getInterpretation(classElement);
171 return ((Tuple activation) -> {
172 var modelElement = activation.get(0);
173 var classElement = activation.get(1);
174
175 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
176 classElementInterpretation.put(Tuple.of(classElement), false);
177 });
178 },
179 0L);
180
181 TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1",
182 deleteEmptyClassPrecondition,
183 (model) -> {
184 var classesInterpretation = model.getInterpretation(classes);
185 var classElementInterpretation = model.getInterpretation(classElement);
186 return ((Tuple activation) -> {
187 var modelElement = activation.get(0);
188 var classElement = activation.get(1);
189
190 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
191 classElementInterpretation.put(Tuple.of(classElement), false);
192 });
193 },
194 78634L);
195
196 var store = ModelStore.builder()
197 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
198 .with(ViatraModelQueryAdapter.builder()
199 .queries(deleteEmptyClassPrecondition))
200 .with(DesignSpaceExplorationAdapter.builder()
201 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
202 .build();
203
204 var model = store.createEmptyModel();
205 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
206 deleteEmptyClassRule0.prepare(model, queryEngine);
207 deleteEmptyClassRule1.prepare(model, queryEngine);
208
209 var classModelInterpretation = model.getInterpretation(classModel);
210 var classElementInterpretation = model.getInterpretation(classElement);
211 var featureInterpretation = model.getInterpretation(feature);
212 var featuresInterpretation = model.getInterpretation(features);
213 var classesInterpretation = model.getInterpretation(classes);
214
215 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
216 var newModel = dseAdapter.createObject();
217 var newModelId = newModel.get(0);
218 var newClass1 = dseAdapter.createObject();
219 var newClass1Id = newClass1.get(0);
220 var newClass2 = dseAdapter.createObject();
221 var newClass2Id = newClass2.get(0);
222 var newField = dseAdapter.createObject();
223 var newFieldId = newField.get(0);
224
225 classModelInterpretation.put(newModel, true);
226 classElementInterpretation.put(newClass1, true);
227 classElementInterpretation.put(newClass2, true);
228 featureInterpretation.put(newField, true);
229 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
230 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
231 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
232
233 queryEngine.flushChanges();
234
235
236 var activation0 = deleteEmptyClassRule0.getRandomActivation().activation();
237 var activation1 = deleteEmptyClassRule1.getRandomActivation().activation();
238
239 assertResults(Map.of(
240 Tuple.of(newModelId, newClass1Id), true,
241 Tuple.of(newModelId, newClass2Id), true
242 ), deleteEmptyClassRule0.getAllActivationsAsResultSet());
243
244 assertResults(Map.of(
245 Tuple.of(newModelId, newClass1Id), true,
246 Tuple.of(newModelId, newClass2Id), true
247 ), deleteEmptyClassRule1.getAllActivationsAsResultSet());
248
249 assertEquals(Tuple.of(newModelId, newClass2Id), activation0);
250 assertEquals(Tuple.of(newModelId, newClass1Id), activation1);
251
252 }
253
254 @Test
255 void fireTest() {
256 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
257 (builder, model, c) -> builder.clause((f) -> List.of(
258 classModelView.call(model),
259 classElementView.call(c),
260 featuresView.call(model, f),
261 not(encapsulatesView.call(c, f))
262 )));
263
264 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
265 deleteEmptyClassPrecondition,
266 (model) -> {
267 var classesInterpretation = model.getInterpretation(classes);
268 var classElementInterpretation = model.getInterpretation(classElement);
269 return ((Tuple activation) -> {
270 var modelElement = activation.get(0);
271 var classElement = activation.get(1);
272
273 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
274 classElementInterpretation.put(Tuple.of(classElement), false);
275 });
276 });
277
278 var store = ModelStore.builder()
279 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
280 .with(ViatraModelQueryAdapter.builder()
281 .queries(deleteEmptyClassPrecondition))
282 .with(DesignSpaceExplorationAdapter.builder()
283 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
284 .build();
285
286 var model = store.createEmptyModel();
287 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
288 deleteEmptyClassRule.prepare(model, queryEngine);
289
290 var classModelInterpretation = model.getInterpretation(classModel);
291 var classElementInterpretation = model.getInterpretation(classElement);
292 var featureInterpretation = model.getInterpretation(feature);
293 var featuresInterpretation = model.getInterpretation(features);
294 var classesInterpretation = model.getInterpretation(classes);
295
296 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
297 var newModel = dseAdapter.createObject();
298 var newModelId = newModel.get(0);
299 var newClass1 = dseAdapter.createObject();
300 var newClass1Id = newClass1.get(0);
301 var newClass2 = dseAdapter.createObject();
302 var newClass2Id = newClass2.get(0);
303 var newField = dseAdapter.createObject();
304 var newFieldId = newField.get(0);
305
306 classModelInterpretation.put(newModel, true);
307 classElementInterpretation.put(newClass1, true);
308 classElementInterpretation.put(newClass2, true);
309 featureInterpretation.put(newField, true);
310 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
311 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
312 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
313
314 queryEngine.flushChanges();
315
316 assertResults(Map.of(
317 Tuple.of(newModelId, newClass1Id), true,
318 Tuple.of(newModelId, newClass2Id), true
319 ), deleteEmptyClassRule.getAllActivationsAsResultSet());
320
321
322 deleteEmptyClassRule.fireActivation(Tuple.of(0, 1));
323
324 assertResults(Map.of(
325 Tuple.of(newModelId, newClass1Id), false,
326 Tuple.of(newModelId, newClass2Id), true
327 ), deleteEmptyClassRule.getAllActivationsAsResultSet());
328 }
329
330 @Test
331 void randomFireTest() {
332 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
333 (builder, model, c) -> builder.clause((f) -> List.of(
334 classModelView.call(model),
335 classElementView.call(c),
336 featuresView.call(model, f),
337 not(encapsulatesView.call(c, f))
338 )));
339
340 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0",
341 deleteEmptyClassPrecondition,
342 (model) -> {
343 var classesInterpretation = model.getInterpretation(classes);
344 var classElementInterpretation = model.getInterpretation(classElement);
345 return ((Tuple activation) -> {
346 var modelElement = activation.get(0);
347 var classElement = activation.get(1);
348
349 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
350 classElementInterpretation.put(Tuple.of(classElement), false);
351 });
352 },
353 0L);
354
355 var store = ModelStore.builder()
356 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
357 .with(ViatraModelQueryAdapter.builder()
358 .queries(deleteEmptyClassPrecondition))
359 .with(DesignSpaceExplorationAdapter.builder()
360 .strategy(new DepthFirstStrategy().withDepthLimit(0)))
361 .build();
362
363 var model = store.createEmptyModel();
364 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
365 deleteEmptyClassRule.prepare(model, queryEngine);
366
367 var classModelInterpretation = model.getInterpretation(classModel);
368 var classElementInterpretation = model.getInterpretation(classElement);
369 var featureInterpretation = model.getInterpretation(feature);
370 var featuresInterpretation = model.getInterpretation(features);
371 var classesInterpretation = model.getInterpretation(classes);
372
373 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
374 var newModel = dseAdapter.createObject();
375 var newModelId = newModel.get(0);
376 var newClass1 = dseAdapter.createObject();
377 var newClass1Id = newClass1.get(0);
378 var newClass2 = dseAdapter.createObject();
379 var newClass2Id = newClass2.get(0);
380 var newField = dseAdapter.createObject();
381 var newFieldId = newField.get(0);
382
383 classModelInterpretation.put(newModel, true);
384 classElementInterpretation.put(newClass1, true);
385 classElementInterpretation.put(newClass2, true);
386 featureInterpretation.put(newField, true);
387 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
388 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
389 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
390
391 queryEngine.flushChanges();
392
393 assertResults(Map.of(
394 Tuple.of(newModelId, newClass1Id), true,
395 Tuple.of(newModelId, newClass2Id), true
396 ), deleteEmptyClassRule.getAllActivationsAsResultSet());
397
398 deleteEmptyClassRule.fireRandomActivation();
399
400 assertResults(Map.of(
401 Tuple.of(newModelId, newClass1Id), true,
402 Tuple.of(newModelId, newClass2Id), false
403 ), deleteEmptyClassRule.getAllActivationsAsResultSet());
404
405 deleteEmptyClassRule.fireRandomActivation();
406
407 assertResults(Map.of(
408 Tuple.of(newModelId, newClass1Id), false,
409 Tuple.of(newModelId, newClass2Id), false
410 ), deleteEmptyClassRule.getAllActivationsAsResultSet());
411
412 }
413}
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..2d5a0f36
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyCriterion.java
@@ -0,0 +1,22 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.tests;
7
8import tools.refinery.store.dse.transition.objectives.Criterion;
9import tools.refinery.store.dse.transition.objectives.CriterionCalculator;
10import tools.refinery.store.model.Model;
11
12public class DummyCriterion implements Criterion {
13 protected final boolean returnValue;
14 public DummyCriterion(boolean returnValue) {
15 this.returnValue = returnValue;
16 }
17
18 @Override
19 public CriterionCalculator createCalculator(Model model) {
20 return () -> returnValue;
21 }
22}
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..f5d281aa
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/DummyObjective.java
@@ -0,0 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.tests;
7
8import tools.refinery.store.dse.transition.objectives.Objective;
9import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator;
10import tools.refinery.store.model.Model;
11
12public class DummyObjective implements Objective {
13
14 @Override
15 public ObjectiveCalculator createCalculator(Model model) {
16 return () -> {return 0d;};
17 }
18}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.tests;
7
8import tools.refinery.store.dse.transition.objectives.Criterion;
9import tools.refinery.store.dse.transition.objectives.CriterionCalculator;
10import tools.refinery.store.model.Model;
11
12import java.util.Random;
13
14public class DummyRandomCriterion implements Criterion {
15
16 @SuppressWarnings("squid:S2245")
17 private static final Random random = new Random(9856654);
18 public DummyRandomCriterion() {
19 }
20
21 @Override
22 public CriterionCalculator createCalculator(Model model) {
23 return random::nextBoolean;
24 }
25}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.tests;
7
8import tools.refinery.store.dse.transition.objectives.Objective;
9import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator;
10import tools.refinery.store.model.Model;
11
12import java.util.Random;
13
14public class DummyRandomObjective implements Objective {
15
16 @SuppressWarnings("squid:S2245")
17 private static final Random random = new Random(9856654);
18
19 @Override
20 public ObjectiveCalculator createCalculator(Model model) {
21 return random::nextDouble;
22 }
23}
diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java
index be514eaf..f0a20720 100644
--- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java
@@ -30,7 +30,7 @@ public final class QueryAssertions {
30 } 30 }
31 31
32 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) { 32 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) {
33 var defaultValue = resultSet.getQuery().defaultValue(); 33 var defaultValue = resultSet.getCanonicalQuery().defaultValue();
34 var filteredExpected = new LinkedHashMap<Tuple, T>(); 34 var filteredExpected = new LinkedHashMap<Tuple, T>();
35 var executables = new ArrayList<Executable>(); 35 var executables = new ArrayList<Executable>();
36 for (var entry : expected.entrySet()) { 36 for (var entry : expected.entrySet()) {
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..42a0f89b
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/TransitionTests.java
@@ -0,0 +1,149 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.dse.modification.ModificationAdapter;
10import tools.refinery.store.dse.transition.objectives.Criteria;
11import tools.refinery.store.dse.transition.objectives.Objectives;
12import tools.refinery.store.model.Model;
13import tools.refinery.store.model.ModelStore;
14import tools.refinery.store.query.ModelQueryAdapter;
15import tools.refinery.store.query.dnf.FunctionalQuery;
16import tools.refinery.store.query.dnf.Query;
17import tools.refinery.store.query.dnf.RelationalQuery;
18import tools.refinery.store.query.term.Variable;
19import tools.refinery.store.query.term.int_.IntTerms;
20import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
21import tools.refinery.store.query.view.AnySymbolView;
22import tools.refinery.store.query.view.KeyOnlyView;
23import tools.refinery.store.representation.Symbol;
24import tools.refinery.store.statecoding.StateCoderAdapter;
25import tools.refinery.store.tuple.Tuple;
26
27import java.util.List;
28
29import static org.junit.jupiter.api.Assertions.*;
30import static tools.refinery.store.query.literal.Literals.check;
31import static tools.refinery.store.query.literal.Literals.not;
32
33class TransitionBuildTests {
34 Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false);
35 Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false);
36
37 AnySymbolView personView = new KeyOnlyView<>(person);
38 AnySymbolView friendView = new KeyOnlyView<>(friend);
39
40 RelationalQuery moreThan3Friends = Query.of("moreThan3Friends", (builder, tooMuchFriends) -> builder
41 .clause(Integer.class, (numberOfFriends) -> List.of(
42 numberOfFriends.assign(friendView.count(tooMuchFriends, Variable.of())),
43 check(IntTerms.less(IntTerms.constant(3), numberOfFriends)),
44 personView.call(tooMuchFriends)
45 )));
46
47 RelationalQuery somebodyHasNoFriend = Query.of("somebodyHasNoFriend", (builder, lonely) -> builder
48 .clause(
49 personView.call(lonely),
50 not(friendView.call(lonely, Variable.of()))
51 ));
52
53 FunctionalQuery<Integer> numberOfFriends = Query.of(Integer.class, (builder, output) -> builder
54 .clause(
55 output.assign(friendView.count(Variable.of(), Variable.of()))
56 ));
57
58 @Test
59 void acceptTest() {
60 Model model = getModel();
61
62 var dse = model.getAdapter(DesignSpaceExplorationAdapter.class);
63 var query = model.getAdapter(ModelQueryAdapter.class);
64 var personI = model.getInterpretation(person);
65 var friendI = model.getInterpretation(friend);
66
67 assertTrue(dse.checkAccept());
68 personI.put(Tuple.of(1), true);
69 personI.put(Tuple.of(2), true);
70
71 query.flushChanges();
72
73 assertFalse(dse.checkAccept());
74 friendI.put(Tuple.of(1, 2), true);
75 friendI.put(Tuple.of(2, 1), true);
76
77 query.flushChanges();
78
79 assertTrue(dse.checkAccept());
80 }
81
82 @Test
83 void includeTest() {
84 Model model = getModel();
85
86 var dse = model.getAdapter(DesignSpaceExplorationAdapter.class);
87 var query = model.getAdapter(ModelQueryAdapter.class);
88 var personI = model.getInterpretation(person);
89 var friendI = model.getInterpretation(friend);
90
91 assertFalse(dse.checkExclude());
92
93 personI.put(Tuple.of(1), true);
94 friendI.put(Tuple.of(1, 2), true);
95 friendI.put(Tuple.of(1, 3), true);
96 friendI.put(Tuple.of(1, 4), true);
97
98 query.flushChanges();
99 assertFalse(dse.checkExclude());
100
101 personI.put(Tuple.of(5), true);
102 friendI.put(Tuple.of(1, 5), true);
103
104 query.flushChanges();
105 assertTrue(dse.checkExclude());
106
107 friendI.put(Tuple.of(1, 2), false);
108
109 query.flushChanges();
110 assertFalse(dse.checkExclude());
111 }
112
113 @Test
114 void objectiveTest() {
115 Model model = getModel();
116
117 var dse = model.getAdapter(DesignSpaceExplorationAdapter.class);
118 var query = model.getAdapter(ModelQueryAdapter.class);
119 var friendI = model.getInterpretation(friend);
120
121 assertEquals(0.0, dse.getObjectiveValue().get(0), 0.01);
122
123 friendI.put(Tuple.of(1, 2), true);
124
125 query.flushChanges();
126 assertEquals(1.0, dse.getObjectiveValue().get(0), 0.01);
127
128 friendI.put(Tuple.of(1, 3), true);
129 friendI.put(Tuple.of(1, 4), true);
130
131 query.flushChanges();
132 assertEquals(3.0, dse.getObjectiveValue().get(0), 0.01);
133 }
134
135 private Model getModel() {
136 var store = ModelStore.builder()
137 .symbols(person, friend)
138 .with(ViatraModelQueryAdapter.builder())
139 .with(StateCoderAdapter.builder())
140 .with(ModificationAdapter.builder())
141 .with(DesignSpaceExplorationAdapter.builder()
142 .objective(Objectives.value(numberOfFriends))
143 .exclude(Criteria.whenHasMatch(moreThan3Friends))
144 .accept(Criteria.whenNoMatch(somebodyHasNoFriend)))
145 .build();
146
147 return store.createEmptyModel();
148 }
149}
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..3a672b18
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java
@@ -0,0 +1,131 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.statespace.internal;
7
8import org.junit.jupiter.api.Assertions;
9import org.junit.jupiter.params.ParameterizedTest;
10import org.junit.jupiter.params.provider.MethodSource;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.List;
15import java.util.Random;
16import java.util.function.Supplier;
17import java.util.stream.Stream;
18
19class ActivationUnitTest {
20 private final static int SMALL_SIZE = 5;
21
22 private static Stream<ActivationStoreEntry> entries() {
23 return Stream.of(
24 new ActivationStoreBitVectorEntry(SMALL_SIZE),
25 new ActivationStoreListEntry(SMALL_SIZE)
26 );
27 }
28
29 void addTest(ActivationStoreEntry entry, int elementsAdded) {
30 Assertions.assertEquals(elementsAdded, entry.getNumberOfVisitedActivations());
31 Assertions.assertEquals(SMALL_SIZE - elementsAdded, entry.getNumberOfUnvisitedActivations());
32 }
33
34 @ParameterizedTest
35 @MethodSource("entries")
36 void testDifferent(ActivationStoreEntry entry) {
37 int elementsAdded = 0;
38 addTest(entry, elementsAdded);
39 Assertions.assertEquals(2, entry.getAndAddActivationAfter(2));
40 addTest(entry, ++elementsAdded);
41 Assertions.assertEquals(3, entry.getAndAddActivationAfter(3));
42 addTest(entry, ++elementsAdded);
43 Assertions.assertEquals(1, entry.getAndAddActivationAfter(1));
44 addTest(entry, ++elementsAdded);
45 Assertions.assertEquals(4, entry.getAndAddActivationAfter(4));
46 addTest(entry, ++elementsAdded);
47 Assertions.assertEquals(0, entry.getAndAddActivationAfter(0));
48 addTest(entry, ++elementsAdded);
49 }
50
51
52 @ParameterizedTest
53 @MethodSource("entries")
54 void testSame(ActivationStoreEntry entry) {
55 int elementsAdded = 0;
56 addTest(entry, 0);
57 entry.getAndAddActivationAfter(2);
58 addTest(entry, ++elementsAdded);
59 entry.getAndAddActivationAfter(2);
60 addTest(entry, ++elementsAdded);
61 entry.getAndAddActivationAfter(2);
62 addTest(entry, ++elementsAdded);
63 entry.getAndAddActivationAfter(2);
64 addTest(entry, ++elementsAdded);
65 entry.getAndAddActivationAfter(2);
66 addTest(entry, ++elementsAdded);
67 }
68
69 @ParameterizedTest
70 @MethodSource("entries")
71 void testFilling(ActivationStoreEntry entry) {
72 int elementsAdded = 0;
73 while (elementsAdded < SMALL_SIZE) {
74 entry.getAndAddActivationAfter(2);
75 elementsAdded++;
76 }
77 Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2));
78 }
79
80 void randomDifferentTestCase(ActivationStoreEntry entry, int seed) {
81 List<Integer> elements = new ArrayList<>(SMALL_SIZE);
82 for (int i = 0; i < SMALL_SIZE; i++) {
83 elements.add(i);
84 }
85 @SuppressWarnings("squid:S2245")
86 var random = new Random(seed);
87 Collections.shuffle(elements, random);
88
89 for (int element : elements) {
90 entry.getAndAddActivationAfter(element);
91 }
92 Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2));
93 }
94
95 private static final int fuzzNumber = 20;
96
97 @ParameterizedTest
98 @MethodSource("entryFactories")
99 void randomDifferentTest(Supplier<ActivationStoreEntry> entry) {
100 for (int i = 0; i < fuzzNumber; i++) {
101 randomDifferentTestCase(entry.get(), i);
102 }
103 }
104
105 void randomSameTestCase(ActivationStoreEntry entry, int seed) {
106
107 @SuppressWarnings("squid:S2245")
108 var random = new Random(seed);
109
110 for (int i = 0; i < SMALL_SIZE; i++) {
111 entry.getAndAddActivationAfter(random.nextInt(SMALL_SIZE));
112 }
113
114 Assertions.assertThrows(IllegalArgumentException.class, () -> entry.getAndAddActivationAfter(2));
115 }
116
117 @ParameterizedTest
118 @MethodSource("entryFactories")
119 void randomSameTest(Supplier<ActivationStoreEntry> entry) {
120 for (int i = 0; i < fuzzNumber; i++) {
121 randomSameTestCase(entry.get(), i);
122 }
123 }
124
125 private static Stream<Supplier<ActivationStoreEntry>> entryFactories() {
126 return Stream.of(
127 () -> new ActivationStoreBitVectorEntry(SMALL_SIZE),
128 () -> new ActivationStoreListEntry(SMALL_SIZE)
129 );
130 }
131}