aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
authorLibravatar Oszkár Semeráth <semerath@mit.bme.hu>2023-08-04 13:52:59 +0200
committerLibravatar GitHub <noreply@github.com>2023-08-04 13:52:59 +0200
commit7f552ebe52ac543a9a79c26d6ae91b4a2e63751b (patch)
tree31630f9a9cf093ee22ee73d25e51afcce9b9cdbb /subprojects
parentIntermediate commit with Lazy NeighbourhoodCalculator and StateEquivalenceChe... (diff)
parentMerge pull request #32 from nagilooh/design-space-exploration (diff)
downloadrefinery-7f552ebe52ac543a9a79c26d6ae91b4a2e63751b.tar.gz
refinery-7f552ebe52ac543a9a79c26d6ae91b4a2e63751b.tar.zst
refinery-7f552ebe52ac543a9a79c26d6ae91b4a2e63751b.zip
Merge branch 'graphs4value:main' into datastructure
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/store-dse/build.gradle.kts16
-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.java66
-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.java11
-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.java292
-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.java62
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java89
-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/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.java30
-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.java59
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java199
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java117
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java279
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java122
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java476
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java408
-rw-r--r--subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java57
-rw-r--r--subprojects/store-query-viatra/build.gradle.kts2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Model.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java4
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java6
-rw-r--r--subprojects/visualization/build.gradle.kts13
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java44
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java11
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java12
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java26
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java29
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java342
-rw-r--r--subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java19
36 files changed, 3257 insertions, 3 deletions
diff --git a/subprojects/store-dse/build.gradle.kts b/subprojects/store-dse/build.gradle.kts
new file mode 100644
index 00000000..bb1dee7d
--- /dev/null
+++ b/subprojects/store-dse/build.gradle.kts
@@ -0,0 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7plugins {
8 id("tools.refinery.gradle.java-library")
9}
10
11dependencies {
12 api(project(":refinery-store-query"))
13 api(project(":refinery-store-query-viatra"))
14 api(project(":refinery-store-reasoning"))
15 api(project(":refinery-visualization"))
16}
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
new file mode 100644
index 00000000..524c2f55
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.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;
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
new file mode 100644
index 00000000..8963a496
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java
@@ -0,0 +1,66 @@
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.map.Version;
10import tools.refinery.store.dse.internal.Activation;
11import tools.refinery.store.dse.internal.DesignSpaceExplorationBuilderImpl;
12import tools.refinery.store.dse.objectives.Fitness;
13import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper;
14import tools.refinery.store.tuple.Tuple;
15import tools.refinery.store.tuple.Tuple1;
16
17import java.util.Collection;
18import java.util.List;
19import java.util.Random;
20
21public interface DesignSpaceExplorationAdapter extends ModelAdapter {
22 @Override
23 DesignSpaceExplorationStoreAdapter getStoreAdapter();
24
25 static DesignSpaceExplorationBuilder builder() {
26 return new DesignSpaceExplorationBuilderImpl();
27 }
28
29 Collection<Version> explore();
30
31 public int getModelSize();
32
33 public Tuple1 createObject();
34
35 public Tuple deleteObject(Tuple tuple);
36
37 public boolean checkGlobalConstraints();
38
39 public boolean backtrack();
40
41 public Fitness calculateFitness();
42
43 public void newSolution();
44
45 public int getDepth();
46
47 public Collection<Activation> getUntraversedActivations();
48
49 public boolean fireActivation(Activation activation);
50
51 public void fireRandomActivation();
52
53 public boolean isCurrentInTrajectory();
54
55 public List<Version> getTrajectory();
56
57 public boolean isCurrentStateAlreadyTraversed();
58
59 public ObjectiveComparatorHelper getObjectiveComparatorHelper();
60
61 public void restoreTrajectory(List<Version> trajectory);
62
63 public void setRandom(Random random);
64
65 public void setRandom(long seed);
66}
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
new file mode 100644
index 00000000..4def5cb2
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java
@@ -0,0 +1,48 @@
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
new file mode 100644
index 00000000..186bfebb
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java
@@ -0,0 +1,11 @@
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;
9
10public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter {
11}
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
new file mode 100644
index 00000000..e240f478
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.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;
7
8public interface Strategy {
9
10 public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter);
11
12 public 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
new file mode 100644
index 00000000..1893ce2e
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.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.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
new file mode 100644
index 00000000..b32e9696
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java
@@ -0,0 +1,292 @@
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.RelationalQuery;
17import tools.refinery.store.dse.DesignSpaceExplorationAdapter;
18import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter;
19import tools.refinery.store.dse.Strategy;
20import tools.refinery.store.dse.objectives.Fitness;
21import tools.refinery.store.dse.objectives.Objective;
22import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper;
23import tools.refinery.store.query.resultset.ResultSet;
24import tools.refinery.store.representation.Symbol;
25import tools.refinery.store.tuple.Tuple;
26import tools.refinery.store.tuple.Tuple1;
27import tools.refinery.visualization.ModelVisualizerAdapter;
28
29import java.util.*;
30
31public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter {
32 static final Symbol<Integer> NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0);
33 private final Model model;
34 private final ModelQueryAdapter queryEngine;
35 private final DesignSpaceExplorationStoreAdapterImpl storeAdapter;
36 private final LinkedHashSet<TransformationRule> transformationRules;
37 private final LinkedHashSet<RelationalQuery> globalConstraints;
38 private final List<Objective> objectives;
39 private final LinkedHashSet<ResultSet<Boolean>> globalConstraintResultSets = new LinkedHashSet<>();
40 private final Interpretation<Integer> sizeInterpretation;
41 private final Strategy strategy;
42
43 private ObjectiveComparatorHelper objectiveComparatorHelper;
44 private List<Version> trajectory = new LinkedList<>();
45 private Fitness lastFitness;
46 private final LinkedHashSet<Version> solutions = new LinkedHashSet<>();
47 private Map<Version, LinkedHashSet<Activation>> statesAndUntraversedActivations;
48 private Map<Version, LinkedHashSet<Activation>> statesAndTraversedActivations;
49 private Random random = new Random();
50 private boolean isNewState = false;
51 private final boolean isVisualizationEnabled;
52 private final ModelVisualizerAdapter modelVisualizerAdapter;
53
54 public List<Version> getTrajectory() {
55 return new LinkedList<>(trajectory);
56 }
57
58 public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) {
59 this.model = model;
60 this.storeAdapter = storeAdapter;
61 this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL);
62 queryEngine = model.getAdapter(ModelQueryAdapter.class);
63
64 globalConstraints = storeAdapter.getGlobalConstraints();
65 for (var constraint : globalConstraints) {
66 globalConstraintResultSets.add(queryEngine.getResultSet(constraint));
67 }
68
69 transformationRules = storeAdapter.getTransformationSpecifications();
70 for (var rule : transformationRules) {
71 rule.prepare(model, queryEngine);
72 }
73
74 objectives = storeAdapter.getObjectives();
75 statesAndUntraversedActivations = new HashMap<>();
76 statesAndTraversedActivations = new HashMap<>();
77 strategy = storeAdapter.getStrategy();
78 modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null);
79 isVisualizationEnabled = modelVisualizerAdapter != null;
80
81 }
82
83 @Override
84 public Model getModel() {
85 return model;
86 }
87
88 @Override
89 public DesignSpaceExplorationStoreAdapter getStoreAdapter() {
90 return storeAdapter;
91 }
92
93 @Override
94 public LinkedHashSet<Version> explore() {
95 var state = model.commit();
96 trajectory.add(state);
97 statesAndUntraversedActivations.put(state, getAllActivations());
98 statesAndTraversedActivations.put(state, new LinkedHashSet<>());
99 strategy.initStrategy(this);
100 strategy.explore();
101 return solutions;
102 }
103
104 @Override
105 public int getModelSize() {
106 return sizeInterpretation.get(Tuple.of());
107 }
108
109 @Override
110 public Tuple1 createObject() {
111 var newNodeId = getModelSize();
112 sizeInterpretation.put(Tuple.of(), newNodeId + 1);
113 return Tuple.of(newNodeId);
114 }
115
116 @Override
117 public Tuple deleteObject(Tuple tuple) {
118 if (tuple.getSize() != 1) {
119 throw new IllegalArgumentException("Tuple size must be 1");
120 }
121// TODO: implement more efficient deletion
122// if (tuple.get(0) == getModelSize() - 1) {
123// sizeInterpretation.put(Tuple.of(), getModelSize() - 1);
124// }
125 return tuple;
126 }
127
128 @Override
129 public boolean checkGlobalConstraints() {
130 for (var resultSet : globalConstraintResultSets) {
131 if (resultSet.size() > 0) {
132 return false;
133 }
134 }
135 return true;
136 }
137
138 @Override
139 public boolean backtrack() {
140 if (trajectory.size() < 2) {
141 return false;
142 }
143 if (isVisualizationEnabled) {
144 modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1),
145 trajectory.get(trajectory.size() - 2), "backtrack");
146 }
147 model.restore(trajectory.get(trajectory.size() - 2));
148 trajectory.remove(trajectory.size() - 1);
149 return true;
150 }
151
152 @Override
153 public void restoreTrajectory(List<Version> trajectory) {
154 model.restore(trajectory.get(trajectory.size() - 1));
155// if (isVisualizationEnabled) {
156// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1),
157// trajectory.get(trajectory.size() - 1), "restore");
158// }
159 this.trajectory = trajectory;
160
161 }
162
163 @Override
164 public void setRandom(Random random) {
165 this.random = random;
166 }
167
168 @Override
169 public void setRandom(long seed) {
170 this.random = new Random(seed);
171 }
172
173 @Override
174 public Fitness calculateFitness() {
175 Fitness result = new Fitness();
176 boolean satisfiesHardObjectives = true;
177 for (Objective objective : objectives) {
178 var fitness = objective.getFitness(this);
179 result.put(objective.getName(), fitness);
180 if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) {
181 satisfiesHardObjectives = false;
182 }
183 }
184 result.setSatisfiesHardObjectives(satisfiesHardObjectives);
185
186 lastFitness = result;
187
188 return result;
189 }
190
191 @Override
192 public void newSolution() {
193 var state = model.getState();
194 solutions.add(state);
195 if (isVisualizationEnabled) {
196 modelVisualizerAdapter.addSolution(state);
197 }
198 }
199
200 @Override
201 public int getDepth() {
202 return trajectory.size() - 1;
203 }
204
205 public LinkedHashSet<Activation> getUntraversedActivations() {
206// return statesAndUntraversedActivations.get(model.getState());
207 LinkedHashSet<Activation> untraversedActivations = new LinkedHashSet<>();
208 for (Activation activation : getAllActivations()) {
209 if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) {
210 untraversedActivations.add(activation);
211 }
212 }
213
214 return untraversedActivations;
215 }
216
217 @Override
218 public boolean fireActivation(Activation activation) {
219 if (activation == null) {
220 return false;
221 }
222 var previousState = model.getState();
223 if (!statesAndUntraversedActivations.get(previousState).contains(activation)) {
224// TODO: throw exception?
225 return false;
226 }
227 if (!activation.fire()) {
228 return false;
229 }
230 statesAndUntraversedActivations.get(previousState).remove(activation);
231 statesAndTraversedActivations.get(previousState).add(activation);
232 var newState = model.commit();
233 trajectory.add(newState);
234 isNewState = !statesAndUntraversedActivations.containsKey(newState);
235 statesAndUntraversedActivations.put(newState, getAllActivations());
236 statesAndTraversedActivations.put(newState, new LinkedHashSet<>());
237 if (isVisualizationEnabled) {
238 if (isNewState) {
239 modelVisualizerAdapter.addState(newState);
240 }
241 modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2),
242 trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(),
243 activation.activation());
244 }
245 return true;
246 }
247
248 @Override
249 public void fireRandomActivation() {
250 var activations = getUntraversedActivations();
251 if (activations.isEmpty()) {
252// TODO: throw exception
253 return;
254 }
255 int index = random.nextInt(activations.size());
256 var iterator = activations.iterator();
257 while (index-- > 0) {
258 iterator.next();
259 }
260 var activationId = iterator.next();
261 fireActivation(activationId);
262 }
263
264 @Override
265 public boolean isCurrentInTrajectory() {
266 return trajectory.contains(model.getState());
267 }
268
269 public LinkedHashSet<Activation> getAllActivations() {
270 LinkedHashSet<Activation> result = new LinkedHashSet<>();
271 for (var rule : transformationRules) {
272 result.addAll(rule.getAllActivations());
273 }
274 return result;
275 }
276
277 public boolean isCurrentStateAlreadyTraversed() {
278// TODO: check isomorphism?
279 return !isNewState;
280 }
281
282 public Fitness getLastFitness() {
283 return lastFitness;
284 }
285
286 public ObjectiveComparatorHelper getObjectiveComparatorHelper() {
287 if (objectiveComparatorHelper == null) {
288 objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives);
289 }
290 return objectiveComparatorHelper;
291 }
292}
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
new file mode 100644
index 00000000..8f7056f2
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java
@@ -0,0 +1,67 @@
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
new file mode 100644
index 00000000..09925ae7
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java
@@ -0,0 +1,62 @@
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.ModelAdapter;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.dnf.RelationalQuery;
12import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter;
13import tools.refinery.store.dse.Strategy;
14import tools.refinery.store.dse.objectives.Objective;
15
16import java.util.LinkedHashSet;
17import java.util.List;
18
19public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter {
20 private final ModelStore store;
21 private final LinkedHashSet<TransformationRule> transformationSpecifications;
22 private final LinkedHashSet<RelationalQuery> globalConstraints;
23 private final List<Objective> objectives;
24 private final Strategy strategy;
25
26 public DesignSpaceExplorationStoreAdapterImpl(ModelStore store,
27 LinkedHashSet<TransformationRule> transformationSpecifications,
28 LinkedHashSet<RelationalQuery> globalConstraints,
29 List<Objective> objectives, Strategy strategy) {
30 this.store = store;
31 this.transformationSpecifications = transformationSpecifications;
32 this.globalConstraints = globalConstraints;
33 this.objectives = objectives;
34 this.strategy = strategy;
35 }
36
37 @Override
38 public ModelStore getStore() {
39 return store;
40 }
41
42 @Override
43 public ModelAdapter createModelAdapter(Model model) {
44 return new DesignSpaceExplorationAdapterImpl(model, this);
45 }
46
47 public LinkedHashSet<TransformationRule> getTransformationSpecifications() {
48 return transformationSpecifications;
49 }
50
51 public LinkedHashSet<RelationalQuery> getGlobalConstraints() {
52 return globalConstraints;
53 }
54
55 public List<Objective> getObjectives() {
56 return objectives;
57 }
58
59 public Strategy getStrategy() {
60 return strategy;
61 }
62}
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
new file mode 100644
index 00000000..015d4815
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java
@@ -0,0 +1,89 @@
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.LinkedHashSet;
18import java.util.Random;
19
20public class TransformationRule {
21
22 private final String name;
23 private final RelationalQuery precondition;
24 private final ActionFactory actionFactory;
25 private Procedure<Tuple> action;
26 private OrderedResultSet<Boolean> activations;
27 private Random random;
28 private ModelQueryAdapter queryEngine;
29
30 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) {
31 this(name, precondition, actionFactory, new Random());
32 }
33
34 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) {
35 this(name, precondition, actionFactory, new Random(seed));
36 }
37
38 public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) {
39 this.name = name;
40 this.precondition = precondition;
41 this.actionFactory = actionFactory;
42 this.random = random;
43 }
44 public boolean prepare(Model model, ModelQueryAdapter queryEngine) {
45 action = actionFactory.prepare(model);
46 this.queryEngine = queryEngine;
47 activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition));
48 return true;
49 }
50
51 public boolean fireActivation(Tuple activation) {
52 action.accept(activation);
53 queryEngine.flushChanges();
54 return true;
55 }
56
57 public boolean fireRandomActivation() {
58 return getRandomActivation().fire();
59 }
60
61 public String getName() {
62 return name;
63 }
64
65 public RelationalQuery getPrecondition() {
66 return precondition;
67 }
68
69 public ResultSet<Boolean> getAllActivationsAsSets() {
70 return activations;
71 }
72
73 public LinkedHashSet<Activation> getAllActivations() {
74 var result = new LinkedHashSet<Activation>();
75 var cursor = activations.getAll();
76 while (cursor.move()) {
77 result.add(new Activation(this, cursor.getKey()));
78 }
79 return result;
80 }
81
82 public Activation getRandomActivation() {
83 return new Activation(this, activations.getKey(random.nextInt(activations.size())));
84 }
85
86 public Activation getActivation(int index) {
87 return new Activation(this, activations.getKey(index));
88 }
89}
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
new file mode 100644
index 00000000..afed75fd
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java
@@ -0,0 +1,52 @@
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/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java
new file mode 100644
index 00000000..7df33efe
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java
@@ -0,0 +1,132 @@
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 public 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
new file mode 100644
index 00000000..476504b0
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java
@@ -0,0 +1,26 @@
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 = (o1, o2) -> o1.compareTo(o2);
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
new file mode 100644
index 00000000..92709d3e
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java
@@ -0,0 +1,30 @@
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}
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
new file mode 100644
index 00000000..c7313622
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java
@@ -0,0 +1,101 @@
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
new file mode 100644
index 00000000..1d676562
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java
@@ -0,0 +1,59 @@
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 } else if (o1HasBetterFitness) {
53 return 1;
54 }
55
56 return 0;
57
58 }
59}
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
new file mode 100644
index 00000000..8648864c
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java
@@ -0,0 +1,199 @@
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.Collection;
20import java.util.Iterator;
21import java.util.List;
22import java.util.PriorityQueue;
23
24public class BestFirstStrategy implements Strategy {
25
26 private DesignSpaceExplorationAdapter dseAdapter;
27
28 private int maxDepth;
29 private boolean backTrackIfSolution = true;
30 private boolean onlyBetterFirst = false;
31
32 private PriorityQueue<TrajectoryWithFitness> trajectoriesToExplore;
33
34 private static class TrajectoryWithFitness {
35
36 public List<Version> trajectory;
37 public Fitness fitness;
38
39 public TrajectoryWithFitness(List<Version> trajectory, Fitness fitness) {
40 super();
41 this.trajectory = trajectory;
42 this.fitness = fitness;
43 }
44
45 @Override
46 public String toString() {
47 return trajectory.toString() + fitness.toString();
48 }
49
50 }
51
52 public BestFirstStrategy() {
53 this(-1);
54 }
55
56 public BestFirstStrategy(int maxDepth) {
57 if (maxDepth < 0) {
58 this.maxDepth = Integer.MAX_VALUE;
59 } else {
60 this.maxDepth = maxDepth;
61 }
62 }
63
64 public BestFirstStrategy continueIfHardObjectivesFulfilled() {
65 backTrackIfSolution = false;
66 return this;
67 }
68
69 public BestFirstStrategy goOnOnlyIfFitnessIsBetter() {
70 onlyBetterFirst = true;
71 return this;
72 }
73
74 @Override
75 public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) {
76 this.dseAdapter = designSpaceExplorationAdapter;
77 final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper();
78
79 trajectoriesToExplore = new PriorityQueue<TrajectoryWithFitness>(11,
80 (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness));
81 }
82
83 @Override
84 public void explore() {
85 final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper();
86
87 boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints();
88 if (!globalConstraintsAreSatisfied) {
89 // "Global constraint is not satisfied in the first state. Terminate.");
90 return;
91 }
92
93 final Fitness firstFitness = dseAdapter.calculateFitness();
94 if (firstFitness.isSatisfiesHardObjectives()) {
95 dseAdapter.newSolution();
96 // "First state is a solution. Terminate.");
97 if (backTrackIfSolution) {
98 return;
99 }
100 }
101
102 if (maxDepth == 0) {
103 return;
104 }
105
106 final List<Version> firstTrajectory = dseAdapter.getTrajectory();
107 TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(firstTrajectory, firstFitness);
108 trajectoriesToExplore.add(currentTrajectoryWithFitness);
109
110 mainLoop: while (true) {
111
112 if (currentTrajectoryWithFitness == null) {
113 if (trajectoriesToExplore.isEmpty()) {
114 // "State space is fully traversed.");
115 return;
116 } else {
117 currentTrajectoryWithFitness = trajectoriesToExplore.element();
118// if (logger.isDebugEnabled()) {
119// "New trajectory is chosen: " + currentTrajectoryWithFitness);
120// }
121 dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory);
122 }
123 }
124
125 Collection<Activation> activations = dseAdapter.getUntraversedActivations();
126 Iterator<Activation> iterator = activations.iterator();
127
128
129
130 while (iterator.hasNext()) {
131 final Activation nextActivation = iterator.next();
132 if (!iterator.hasNext()) {
133 // "Last untraversed activation of the state.");
134 trajectoriesToExplore.remove(currentTrajectoryWithFitness);
135 }
136
137// if (logger.isDebugEnabled()) {
138// "Executing new activation: " + nextActivation);
139// }
140 dseAdapter.fireActivation(nextActivation);
141 if (dseAdapter.isCurrentStateAlreadyTraversed()) {
142 // "The new state is already visited.");
143 dseAdapter.backtrack();
144 } else if (!dseAdapter.checkGlobalConstraints()) {
145 // "Global constraint is not satisfied.");
146 dseAdapter.backtrack();
147 } else {
148 final Fitness nextFitness = dseAdapter.calculateFitness();
149 if (nextFitness.isSatisfiesHardObjectives()) {
150 dseAdapter.newSolution();
151 // "Found a solution.");
152 if (backTrackIfSolution) {
153 dseAdapter.backtrack();
154 continue;
155 }
156 }
157 if (dseAdapter.getDepth() >= maxDepth) {
158 // "Reached max depth.");
159 dseAdapter.backtrack();
160 continue;
161 }
162
163 TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness(
164 dseAdapter.getTrajectory(), nextFitness);
165 trajectoriesToExplore.add(nextTrajectoryWithFitness);
166
167 int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness,
168 nextTrajectoryWithFitness.fitness);
169 if (compare < 0) {
170 // "Better fitness, moving on: " + nextFitness);
171 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
172 continue mainLoop;
173 } else if (compare == 0) {
174 if (onlyBetterFirst) {
175 // "Equally good fitness, backtrack: " + nextFitness);
176 dseAdapter.backtrack();
177 continue;
178 } else {
179 // "Equally good fitness, moving on: " + nextFitness);
180 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
181 continue mainLoop;
182 }
183 } else {
184 // "Worse fitness.");
185 currentTrajectoryWithFitness = null;
186 continue mainLoop;
187 }
188 }
189 }
190
191 // "State is fully traversed.");
192 trajectoriesToExplore.remove(currentTrajectoryWithFitness);
193 currentTrajectoryWithFitness = null;
194
195 }
196 // "Interrupted.");
197
198 }
199}
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
new file mode 100644
index 00000000..1405789b
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java
@@ -0,0 +1,117 @@
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.dse.DesignSpaceExplorationAdapter;
13import tools.refinery.store.dse.Strategy;
14import tools.refinery.store.dse.internal.Activation;
15import tools.refinery.store.dse.objectives.Fitness;
16
17import java.util.Collection;
18
19public class DepthFirstStrategy implements Strategy {
20
21 private DesignSpaceExplorationAdapter dseAdapter;
22
23 private int maxDepth;
24 private boolean backTrackIfSolution = true;
25
26 public DepthFirstStrategy() {
27 this(-1);
28 }
29
30 public DepthFirstStrategy(int maxDepth) {
31 if (maxDepth < 0) {
32 this.maxDepth = Integer.MAX_VALUE;
33 } else {
34 this.maxDepth = maxDepth;
35 }
36 }
37
38 public DepthFirstStrategy continueIfHardObjectivesFulfilled() {
39 backTrackIfSolution = false;
40 return this;
41 }
42
43 @Override
44 public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) {
45 this.dseAdapter = designSpaceExplorationAdapter;
46 }
47
48 @Override
49 public void explore() {
50 mainloop: while (true) {
51 var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints();
52 if (!globalConstraintsAreSatisfied) {
53 var isSuccessfulUndo = dseAdapter.backtrack();
54 if (!isSuccessfulUndo) {
55// "Global constraint is not satisfied and cannot backtrack."
56 break;
57 }
58 else {
59// "Global constraint is not satisfied, backtrack."
60 continue;
61 }
62 }
63
64 Fitness fitness = dseAdapter.calculateFitness();
65 if (fitness.isSatisfiesHardObjectives()) {
66 dseAdapter.newSolution();
67 if (backTrackIfSolution) {
68 var isSuccessfulUndo = dseAdapter.backtrack();
69 if (!isSuccessfulUndo) {
70// "Found a solution but cannot backtrack."
71 break;
72 } else {
73// "Found a solution, backtrack."
74 continue;
75 }
76 }
77 }
78
79 var depth = dseAdapter.getDepth();
80 if (dseAdapter.getDepth() >= maxDepth) {
81 var isSuccessfulUndo = dseAdapter.backtrack();
82 if (!isSuccessfulUndo) {
83// "Reached max depth but cannot backtrack."
84 break;
85 }
86 }
87
88 Collection<Activation> activations;
89 do {
90 activations = dseAdapter.getUntraversedActivations();
91 if (activations.isEmpty()) {
92 if (!dseAdapter.backtrack()) {
93 // "No more transitions from current state and cannot backtrack."
94 break mainloop;
95 }
96 else {
97 // "No more transitions from current state, backtrack."
98 continue;
99 }
100 }
101 } while (activations.isEmpty());
102
103 dseAdapter.fireRandomActivation();
104// if (dseAdapter.isCurrentInTrajectory()) {
105// if (!dseAdapter.backtrack()) {
106//// TODO: throw exception
107//// "The new state is present in the trajectory but cannot backtrack. Should never happen!"
108// break;
109// }
110// else {
111//// "The new state is already visited in the trajectory, backtrack."
112// continue;
113// }
114// }
115 }
116 }
117}
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
new file mode 100644
index 00000000..4bdb05ec
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java
@@ -0,0 +1,279 @@
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.query.dnf.RelationalQuery;
13import tools.refinery.store.dse.internal.TransformationRule;
14import tools.refinery.store.dse.strategy.BestFirstStrategy;
15import tools.refinery.store.dse.strategy.DepthFirstStrategy;
16import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
17import tools.refinery.store.query.view.AnySymbolView;
18import tools.refinery.store.query.view.KeyOnlyView;
19import tools.refinery.store.representation.Symbol;
20import tools.refinery.store.tuple.Tuple;
21import tools.refinery.visualization.ModelVisualizerAdapter;
22import tools.refinery.visualization.internal.FileFormat;
23
24import java.util.List;
25
26import static tools.refinery.store.query.literal.Literals.not;
27
28public class CRAExamplesTest {
29 private static final Symbol<String> name = Symbol.of("Name", 1, String.class);
30
31// private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
32 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
33// private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
34 private static final Symbol<Boolean> attribute = Symbol.of("Attribute", 1);
35 private static final Symbol<Boolean> method = Symbol.of("Method", 1);
36
37// private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
38 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
39 private static final Symbol<Boolean> dataDependency = Symbol.of("DataDependency", 2);
40 private static final Symbol<Boolean> functionalDependency = Symbol.of("FunctionalDependency", 2);
41
42 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
43 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
44
45// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
46 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
47// private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
48 private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute);
49 private static final AnySymbolView methodView = new KeyOnlyView<>(method);
50// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
51 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
52 private static final AnySymbolView dataDependencyView = new KeyOnlyView<>(dataDependency);
53 private static final AnySymbolView functionalDependencyView = new KeyOnlyView<>(functionalDependency);
54 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
55 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
56
57 /*Example Transformation rules*/
58 private static final RelationalQuery feature = Query.of("Feature",
59 (builder, f) -> builder.clause(
60 attributeView.call(f))
61 .clause(
62 methodView.call(f))
63 );
64
65 private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper",
66 (builder, c, f) -> builder.clause(
67 classElementView.call(c),
68// classesView.call(model, c),
69 encapsulatesView.call(c, f)
70 ));
71
72 private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition",
73 (builder, f, c1) -> builder.clause((c2) -> List.of(
74// classModelView.call(model),
75 feature.call(f),
76 classElementView.call(c1),
77// featuresView.call(model, f),
78 not(assignFeaturePreconditionHelper.call(c2, f)),
79 not(encapsulatesView.call(c1, f))
80 )));
81
82 private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
83 (builder, c) -> builder.clause((f) -> List.of(
84// classModelView.call(model),
85 classElementView.call(c),
86// featuresView.call(model, f),
87 not(encapsulatesView.call(c, f))
88 )));
89
90 private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper",
91 (builder, f, c) -> builder.clause(
92 classElementView.call(c),
93// classesView.call(model, c),
94 encapsulatesView.call(c, f)
95 ));
96
97 private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition",
98 (builder, f) -> builder.clause((c) -> List.of(
99// classModelView.call(model),
100 feature.call(f),
101 not(createClassPreconditionHelper.call(f, c))
102 )));
103
104 private static final RelationalQuery moveFeaturePrecondition = Query.of("MoveFeature",
105 (builder, c1, c2, f) -> builder.clause(
106// classModelView.call(model),
107 classElementView.call(c1),
108 classElementView.call(c2),
109 c1.notEquivalent(c2),
110 feature.call(f),
111// classesView.call(model, c1),
112// classesView.call(model, c2),
113// featuresView.call(model, f),
114 encapsulatesView.call(c1, f)
115 ));
116
117 private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature",
118 assignFeaturePrecondition,
119 (model) -> {
120// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy);
121 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
122 return ((Tuple activation) -> {
123 var feature = activation.get(0);
124 var classElement = activation.get(1);
125
126// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true);
127 encapsulatesInterpretation.put(Tuple.of(classElement, feature), true);
128 });
129 });
130
131 private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
132 deleteEmptyClassPrecondition,
133 (model) -> {
134// var classesInterpretation = model.getInterpretation(classes);
135 var classElementInterpretation = model.getInterpretation(classElement);
136 return ((Tuple activation) -> {
137 // TODO: can we move dseAdapter outside?
138 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
139// var modelElement = activation.get(0);
140 var classElement = activation.get(0);
141
142// classesInterpretation.put(Tuple.of(modelElement, classElement), false);
143 classElementInterpretation.put(Tuple.of(classElement), false);
144 dseAdapter.deleteObject(Tuple.of(classElement));
145 });
146 });
147
148 private static final TransformationRule createClassRule = new TransformationRule("CreateClass",
149 createClassPrecondition,
150 (model) -> {
151 var classElementInterpretation = model.getInterpretation(classElement);
152// var classesInterpretation = model.getInterpretation(classes);
153 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
154 return ((Tuple activation) -> {
155 // TODO: can we move dseAdapter outside?
156 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
157// var modelElement = activation.get(0);
158 var feature = activation.get(0);
159
160 var newClassElement = dseAdapter.createObject();
161 var newClassElementId = newClassElement.get(0);
162 classElementInterpretation.put(newClassElement, true);
163// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
164 encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true);
165 });
166 });
167
168 private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature",
169 moveFeaturePrecondition,
170 (model) -> {
171 var encapsulatesInterpretation = model.getInterpretation(encapsulates);
172 return ((Tuple activation) -> {
173 var classElement1 = activation.get(0);
174 var classElement2 = activation.get(1);
175 var feature = activation.get(2);
176
177 encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false);
178 encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true);
179 });
180 });
181
182 @Test
183 void craTest() {
184 var store = ModelStore.builder()
185 .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency,
186 functionalDependency, name)
187 .with(ViatraModelQueryAdapter.builder()
188 .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition,
189 deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition,
190 moveFeaturePrecondition))
191 .with(ModelVisualizerAdapter.builder())
192 .with(DesignSpaceExplorationAdapter.builder()
193 .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule)
194// .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled()
195 .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled()
196// .goOnOnlyIfFitnessIsBetter()
197 ))
198 .build();
199
200 var model = store.createEmptyModel();
201 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
202// dseAdapter.setRandom(1);
203 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
204
205// var modelInterpretation = model.getInterpretation(classModel);
206 var nameInterpretation = model.getInterpretation(name);
207 var methodInterpretation = model.getInterpretation(method);
208 var attributeInterpretation = model.getInterpretation(attribute);
209 var dataDependencyInterpretation = model.getInterpretation(dataDependency);
210 var functionalDependencyInterpretation = model.getInterpretation(functionalDependency);
211
212// var modelElement = dseAdapter.createObject();
213 var method1 = dseAdapter.createObject();
214 var method1Id = method1.get(0);
215 var method2 = dseAdapter.createObject();
216 var method2Id = method2.get(0);
217 var method3 = dseAdapter.createObject();
218 var method3Id = method3.get(0);
219 var method4 = dseAdapter.createObject();
220 var method4Id = method4.get(0);
221 var attribute1 = dseAdapter.createObject();
222 var attribute1Id = attribute1.get(0);
223 var attribute2 = dseAdapter.createObject();
224 var attribute2Id = attribute2.get(0);
225 var attribute3 = dseAdapter.createObject();
226 var attribute3Id = attribute3.get(0);
227 var attribute4 = dseAdapter.createObject();
228 var attribute4Id = attribute4.get(0);
229 var attribute5 = dseAdapter.createObject();
230 var attribute5Id = attribute5.get(0);
231
232 nameInterpretation.put(method1, "M1");
233 nameInterpretation.put(method2, "M2");
234 nameInterpretation.put(method3, "M3");
235 nameInterpretation.put(method4, "M4");
236 nameInterpretation.put(attribute1, "A1");
237 nameInterpretation.put(attribute2, "A2");
238 nameInterpretation.put(attribute3, "A3");
239 nameInterpretation.put(attribute4, "A4");
240 nameInterpretation.put(attribute5, "A5");
241
242
243
244// modelInterpretation.put(modelElement, true);
245 methodInterpretation.put(method1, true);
246 methodInterpretation.put(method2, true);
247 methodInterpretation.put(method3, true);
248 methodInterpretation.put(method4, true);
249 attributeInterpretation.put(attribute1, true);
250 attributeInterpretation.put(attribute2, true);
251 attributeInterpretation.put(attribute3, true);
252 attributeInterpretation.put(attribute4, true);
253 attributeInterpretation.put(attribute5, true);
254
255 dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true);
256 dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true);
257 dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true);
258 dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true);
259 dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true);
260 dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true);
261 dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true);
262
263 functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true);
264 functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true);
265 functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true);
266 functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true);
267 functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true);
268 functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true);
269
270 queryEngine.flushChanges();
271
272 var states = dseAdapter.explore();
273 System.out.println("states size: " + states.size());
274 System.out.println("states: " + states);
275 var visualizer = model.getAdapter(ModelVisualizerAdapter.class);
276 visualizer.renderDesignSpace("test_output", FileFormat.SVG);
277 }
278
279}
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
new file mode 100644
index 00000000..101a5362
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java
@@ -0,0 +1,122 @@
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;
20import tools.refinery.visualization.ModelVisualizerAdapter;
21import tools.refinery.visualization.internal.FileFormat;
22
23public class DebugTest {
24 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
25 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
26 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
27
28 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
29 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
30
31 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
32 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
33
34 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
35 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
36 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
37 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
38 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
39 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
40 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
41
42
43 @Test
44 void BFSTest() {
45 var createClassPrecondition = Query.of("CreateClassPrecondition",
46 (builder, model) -> builder.clause(
47 classModelView.call(model)
48 ));
49
50 var createClassRule = new TransformationRule("CreateClass",
51 createClassPrecondition,
52 (model) -> {
53 var classesInterpretation = model.getInterpretation(classes);
54 var classElementInterpretation = model.getInterpretation(classElement);
55 return ((Tuple activation) -> {
56 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
57 var modelElement = activation.get(0);
58
59 var newClassElement = dseAdapter.createObject();
60 var newClassElementId = newClassElement.get(0);
61
62 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
63 classElementInterpretation.put(Tuple.of(newClassElementId), true);
64 });
65 });
66
67 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
68 (builder, model) -> builder.clause(
69 classModelView.call(model)
70 ));
71
72 var createFeatureRule = new TransformationRule("CreateFeature",
73 createFeaturePrecondition,
74 (model) -> {
75 var featuresInterpretation = model.getInterpretation(features);
76 var featureInterpretation = model.getInterpretation(feature);
77 return ((Tuple activation) -> {
78 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
79 var modelElement = activation.get(0);
80
81 var newClassElement = dseAdapter.createObject();
82 var newClassElementId = newClassElement.get(0);
83
84 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
85 featureInterpretation.put(Tuple.of(newClassElementId), true);
86 });
87 });
88
89 var store = ModelStore.builder()
90 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
91 .with(ViatraModelQueryAdapter.builder()
92 .queries(createClassPrecondition, createFeaturePrecondition))
93 .with(ModelVisualizerAdapter.builder())
94 .with(DesignSpaceExplorationAdapter.builder()
95 .transformations(createClassRule, createFeatureRule)
96 .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled()
97// .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled()
98// .goOnOnlyIfFitnessIsBetter()
99 ))
100 .build();
101
102 var model = store.createEmptyModel();
103 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
104// dseAdapter.setRandom(1);
105 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
106
107 var modelElementInterpretation = model.getInterpretation(classModel);
108 var classElementInterpretation = model.getInterpretation(classElement);
109 var modelElement = dseAdapter.createObject();
110 modelElementInterpretation.put(modelElement, true);
111 classElementInterpretation.put(modelElement, true);
112 queryEngine.flushChanges();
113
114
115 var states = dseAdapter.explore();
116 var visualizer = model.getAdapter(ModelVisualizerAdapter.class);
117 visualizer.renderDesignSpace("test_output", FileFormat.SVG);
118 System.out.println("states size: " + states.size());
119 System.out.println("states: " + states);
120
121 }
122}
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
new file mode 100644
index 00000000..f4644407
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java
@@ -0,0 +1,476 @@
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;
20import tools.refinery.visualization.ModelVisualizerAdapter;
21
22import static org.junit.jupiter.api.Assertions.assertEquals;
23
24public class DesignSpaceExplorationTest {
25// private static final Symbol<Boolean> namedElement = Symbol.of("NamedElement", 1);
26// private static final Symbol<Boolean> attribute = Symbol.of("Attribute", 1);
27// private static final Symbol<Boolean> method = Symbol.of("Method", 1);
28// private static final Symbol<Boolean> dataDependency = Symbol.of("DataDependency", 2);
29// private static final Symbol<Boolean> functionalDependency = Symbol.of("FunctionalDependency", 2);
30
31 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
32 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
33 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
34
35 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
36 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
37
38 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
39 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
40
41 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
42 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
43 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
44 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
45 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
46 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
47 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
48
49 @Test
50 void createObjectTest() {
51 var store = ModelStore.builder()
52 .with(ViatraModelQueryAdapter.builder())
53 .with(DesignSpaceExplorationAdapter.builder())
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 .build();
83
84 var model = store.createEmptyModel();
85 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
86
87 assertEquals(0, dseAdapter.getModelSize());
88
89 var newObject0 = dseAdapter.createObject();
90 var newObject0Id = newObject0.get(0);
91 var newObject1 = dseAdapter.createObject();
92 var newObject1Id = newObject1.get(0);
93 var newObject2 = dseAdapter.createObject();
94 var newObject2Id = newObject2.get(0);
95 var newObject3 = dseAdapter.createObject();
96 var newObject3Id = newObject3.get(0);
97
98 assertEquals(0, newObject0Id);
99 assertEquals(1, newObject1Id);
100 assertEquals(2, newObject2Id);
101 assertEquals(3, newObject3Id);
102 assertEquals(4, dseAdapter.getModelSize());
103
104 dseAdapter.deleteObject(newObject1);
105 assertEquals(4, dseAdapter.getModelSize());
106
107 var newObject4 = dseAdapter.createObject();
108 var newObject4Id = newObject4.get(0);
109 assertEquals(4, newObject4Id);
110 assertEquals(5, dseAdapter.getModelSize());
111
112 dseAdapter.deleteObject(newObject4);
113 assertEquals(5, dseAdapter.getModelSize());
114 }
115
116 @Test
117 void DFSTrivialTest() {
118 var store = ModelStore.builder()
119 .symbols(classModel)
120 .with(ViatraModelQueryAdapter.builder())
121 .with(ModelVisualizerAdapter.builder())
122 .with(DesignSpaceExplorationAdapter.builder()
123 .strategy(new DepthFirstStrategy(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(ModelVisualizerAdapter.builder())
162 .with(DesignSpaceExplorationAdapter.builder()
163 .transformations(createClassRule)
164 .strategy(new DepthFirstStrategy(4)
165 ))
166 .build();
167
168 var model = store.createEmptyModel();
169 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
170 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
171
172 var modelElementInterpretation = model.getInterpretation(classModel);
173 modelElementInterpretation.put(dseAdapter.createObject(), true);
174 queryEngine.flushChanges();
175
176 var states = dseAdapter.explore();
177 assertEquals(1, states.size());
178 }
179
180 @Test
181 void DFSContinueTest() {
182 var createClassPrecondition = Query.of("CreateClassPrecondition",
183 (builder, model) -> builder.clause(
184 classModelView.call(model)
185 ));
186
187 var createClassRule = new TransformationRule("CreateClass",
188 createClassPrecondition,
189 (model) -> {
190 var classesInterpretation = model.getInterpretation(classes);
191 var classElementInterpretation = model.getInterpretation(classElement);
192 return ((Tuple activation) -> {
193 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
194 var modelElement = activation.get(0);
195
196 var newClassElement = dseAdapter.createObject();
197 var newClassElementId = newClassElement.get(0);
198
199 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
200 classElementInterpretation.put(Tuple.of(newClassElementId), true);
201 });
202 });
203
204 var store = ModelStore.builder()
205 .symbols(classModel, classElement, classes)
206 .with(ViatraModelQueryAdapter.builder()
207 .queries(createClassPrecondition))
208 .with(ModelVisualizerAdapter.builder())
209 .with(DesignSpaceExplorationAdapter.builder()
210 .transformations(createClassRule)
211 .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled()
212 ))
213 .build();
214
215 var model = store.createEmptyModel();
216 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
217 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
218
219 var modelElementInterpretation = model.getInterpretation(classModel);
220 modelElementInterpretation.put(dseAdapter.createObject(), true);
221 queryEngine.flushChanges();
222
223 var states = dseAdapter.explore();
224 assertEquals(5, states.size());
225 }
226
227 @Test
228 void DFSCompletenessTest() {
229 var createClassPrecondition = Query.of("CreateClassPrecondition",
230 (builder, model) -> builder.clause(
231 classModelView.call(model)
232 ));
233
234 var createClassRule = new TransformationRule("CreateClass",
235 createClassPrecondition,
236 (model) -> {
237 var classesInterpretation = model.getInterpretation(classes);
238 var classElementInterpretation = model.getInterpretation(classElement);
239 return ((Tuple activation) -> {
240 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
241 var modelElement = activation.get(0);
242
243 var newClassElement = dseAdapter.createObject();
244 var newClassElementId = newClassElement.get(0);
245
246 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
247 classElementInterpretation.put(Tuple.of(newClassElementId), true);
248 });
249 });
250
251 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
252 (builder, model) -> builder.clause(
253 classModelView.call(model)
254 ));
255
256 var createFeatureRule = new TransformationRule("CreateFeature",
257 createFeaturePrecondition,
258 (model) -> {
259 var featuresInterpretation = model.getInterpretation(features);
260 var featureInterpretation = model.getInterpretation(feature);
261 return ((Tuple activation) -> {
262 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
263 var modelElement = activation.get(0);
264
265 var newClassElement = dseAdapter.createObject();
266 var newClassElementId = newClassElement.get(0);
267
268 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
269 featureInterpretation.put(Tuple.of(newClassElementId), true);
270 });
271 });
272
273 var store = ModelStore.builder()
274 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
275 .with(ViatraModelQueryAdapter.builder()
276 .queries(createClassPrecondition, createFeaturePrecondition))
277 .with(ModelVisualizerAdapter.builder())
278 .with(DesignSpaceExplorationAdapter.builder()
279 .transformations(createClassRule, createFeatureRule)
280 .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled()
281 ))
282 .build();
283
284 var model = store.createEmptyModel();
285 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
286 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
287
288 var modelElementInterpretation = model.getInterpretation(classModel);
289 modelElementInterpretation.put(dseAdapter.createObject(), true);
290 queryEngine.flushChanges();
291
292 var states = dseAdapter.explore();
293 assertEquals(2047, states.size());
294 }
295
296 @Test
297 void BFSTrivialTest() {
298 var store = ModelStore.builder()
299 .symbols(classModel)
300 .with(ViatraModelQueryAdapter.builder())
301 .with(ModelVisualizerAdapter.builder())
302 .with(DesignSpaceExplorationAdapter.builder()
303 .strategy(new BestFirstStrategy(0)))
304 .build();
305
306 var model = store.createEmptyModel();
307 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
308
309 var states = dseAdapter.explore();
310 assertEquals(1, states.size());
311 }
312
313 @Test
314 void BFSOneRuleTest() {
315 var createClassPrecondition = Query.of("CreateClassPrecondition",
316 (builder, model) -> builder.clause(
317 classModelView.call(model)
318 ));
319
320 var createClassRule = new TransformationRule("CreateClass",
321 createClassPrecondition,
322 (model) -> {
323 var classesInterpretation = model.getInterpretation(classes);
324 var classElementInterpretation = model.getInterpretation(classElement);
325 return ((Tuple activation) -> {
326 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
327 var modelElement = activation.get(0);
328
329 var newClassElement = dseAdapter.createObject();
330 var newClassElementId = newClassElement.get(0);
331
332 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
333 classElementInterpretation.put(Tuple.of(newClassElementId), true);
334 });
335 });
336
337 var store = ModelStore.builder()
338 .symbols(classModel, classElement, classes)
339 .with(ViatraModelQueryAdapter.builder()
340 .queries(createClassPrecondition))
341 .with(ModelVisualizerAdapter.builder())
342 .with(DesignSpaceExplorationAdapter.builder()
343 .transformations(createClassRule)
344 .strategy(new BestFirstStrategy(4)
345 ))
346 .build();
347
348 var model = store.createEmptyModel();
349 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
350 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
351
352 var modelElementInterpretation = model.getInterpretation(classModel);
353 modelElementInterpretation.put(dseAdapter.createObject(), true);
354 queryEngine.flushChanges();
355
356 var states = dseAdapter.explore();
357 assertEquals(1, states.size());
358 }
359
360 @Test
361 void BFSContinueTest() {
362 var createClassPrecondition = Query.of("CreateClassPrecondition",
363 (builder, model) -> builder.clause(
364 classModelView.call(model)
365 ));
366
367 var createClassRule = new TransformationRule("CreateClass",
368 createClassPrecondition,
369 (model) -> {
370 var classesInterpretation = model.getInterpretation(classes);
371 var classElementInterpretation = model.getInterpretation(classElement);
372 return ((Tuple activation) -> {
373 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
374 var modelElement = activation.get(0);
375
376 var newClassElement = dseAdapter.createObject();
377 var newClassElementId = newClassElement.get(0);
378
379 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
380 classElementInterpretation.put(Tuple.of(newClassElementId), true);
381 });
382 });
383
384 var store = ModelStore.builder()
385 .symbols(classModel, classElement, classes)
386 .with(ViatraModelQueryAdapter.builder()
387 .queries(createClassPrecondition))
388 .with(ModelVisualizerAdapter.builder())
389 .with(DesignSpaceExplorationAdapter.builder()
390 .transformations(createClassRule)
391 .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled()
392 ))
393 .build();
394
395 var model = store.createEmptyModel();
396 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
397 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
398
399 var modelElementInterpretation = model.getInterpretation(classModel);
400 modelElementInterpretation.put(dseAdapter.createObject(), true);
401 queryEngine.flushChanges();
402
403 var states = dseAdapter.explore();
404 assertEquals(5, states.size());
405 }
406
407 @Test
408 void BFSCompletenessTest() {
409 var createClassPrecondition = Query.of("CreateClassPrecondition",
410 (builder, model) -> builder.clause(
411 classModelView.call(model)
412 ));
413
414 var createClassRule = new TransformationRule("CreateClass",
415 createClassPrecondition,
416 (model) -> {
417 var classesInterpretation = model.getInterpretation(classes);
418 var classElementInterpretation = model.getInterpretation(classElement);
419 return ((Tuple activation) -> {
420 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
421 var modelElement = activation.get(0);
422
423 var newClassElement = dseAdapter.createObject();
424 var newClassElementId = newClassElement.get(0);
425
426 classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
427 classElementInterpretation.put(Tuple.of(newClassElementId), true);
428 });
429 });
430
431 var createFeaturePrecondition = Query.of("CreateFeaturePrecondition",
432 (builder, model) -> builder.clause(
433 classModelView.call(model)
434 ));
435
436 var createFeatureRule = new TransformationRule("CreateFeature",
437 createFeaturePrecondition,
438 (model) -> {
439 var featuresInterpretation = model.getInterpretation(features);
440 var featureInterpretation = model.getInterpretation(feature);
441 return ((Tuple activation) -> {
442 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
443 var modelElement = activation.get(0);
444
445 var newClassElement = dseAdapter.createObject();
446 var newClassElementId = newClassElement.get(0);
447
448 featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true);
449 featureInterpretation.put(Tuple.of(newClassElementId), true);
450 });
451 });
452
453 var store = ModelStore.builder()
454 .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates)
455 .with(ViatraModelQueryAdapter.builder()
456 .queries(createClassPrecondition, createFeaturePrecondition))
457 .with(ModelVisualizerAdapter.builder())
458 .with(DesignSpaceExplorationAdapter.builder()
459 .transformations(createClassRule, createFeatureRule)
460 .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled()
461 ))
462 .build();
463
464 var model = store.createEmptyModel();
465 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
466 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
467
468 var modelElementInterpretation = model.getInterpretation(classModel);
469 modelElementInterpretation.put(dseAdapter.createObject(), true);
470 queryEngine.flushChanges();
471
472 var states = dseAdapter.explore();
473 assertEquals(2047, states.size());
474 }
475
476}
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
new file mode 100644
index 00000000..312bcebd
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java
@@ -0,0 +1,408 @@
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.query.viatra.ViatraModelQueryAdapter;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.KeyOnlyView;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import java.util.List;
20import java.util.Map;
21
22import static org.junit.jupiter.api.Assertions.assertEquals;
23import static tools.refinery.store.query.literal.Literals.not;
24import static tools.refinery.store.dse.tests.QueryAssertions.assertResults;
25
26public class TransformationRuleTest {
27
28 private static final Symbol<Boolean> classModel = Symbol.of("ClassModel", 1);
29 private static final Symbol<Boolean> classElement = Symbol.of("ClassElement", 1);
30 private static final Symbol<Boolean> feature = Symbol.of("Feature", 1);
31
32 private static final Symbol<Boolean> isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2);
33 private static final Symbol<Boolean> encapsulates = Symbol.of("Encapsulates", 2);
34
35 private static final Symbol<Boolean> features = Symbol.of("Features", 2);
36 private static final Symbol<Boolean> classes = Symbol.of("Classes", 2);
37
38 private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel);
39 private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement);
40 private static final AnySymbolView featureView = new KeyOnlyView<>(feature);
41 private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy);
42 private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates);
43 private static final AnySymbolView featuresView = new KeyOnlyView<>(features);
44 private static final AnySymbolView classesView = new KeyOnlyView<>(classes);
45
46 @Test
47 void activationsTest() {
48 var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper",
49 (builder, model, c, f) -> builder.clause(
50 classElementView.call(c),
51 classesView.call(model, c),
52 encapsulatesView.call(c, f)
53 ));
54
55 var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f)
56 -> builder.clause((model, c1) -> List.of(
57 classModelView.call(model),
58 featureView.call(f),
59 classElementView.call(c2),
60 featuresView.call(model, f),
61 classesView.call(model, c1),
62 not(assignFeaturePreconditionHelper.call(model, c2, f)),
63 not(encapsulatesView.call(c2, f))
64 )));
65
66 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
67 (builder, model, c) -> builder.clause((f) -> List.of(
68 classModelView.call(model),
69 classElementView.call(c),
70 featuresView.call(model, f),
71 not(encapsulatesView.call(c, f))
72 )));
73
74 TransformationRule assignFeatureRule = new TransformationRule("AssignFeature",
75 assignFeaturePrecondition,
76 (model) -> {
77 var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy);
78 return ((Tuple activation) -> {
79 var feature = activation.get(0);
80 var classElement = activation.get(1);
81
82 isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true);
83 });
84 });
85
86 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
87 deleteEmptyClassPrecondition,
88 (model) -> {
89 var classesInterpretation = model.getInterpretation(classes);
90 var classElementInterpretation = model.getInterpretation(classElement);
91 return ((Tuple activation) -> {
92 var modelElement = activation.get(0);
93 var classElement = activation.get(1);
94
95 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
96 classElementInterpretation.put(Tuple.of(classElement), false);
97 });
98 });
99
100
101 var store = ModelStore.builder()
102 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
103 .with(ViatraModelQueryAdapter.builder()
104 .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper,
105 deleteEmptyClassPrecondition))
106 .with(DesignSpaceExplorationAdapter.builder())
107 .build();
108
109 var model = store.createEmptyModel();
110 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
111 assignFeatureRule.prepare(model, queryEngine);
112 deleteEmptyClassRule.prepare(model, queryEngine);
113
114 var classModelInterpretation = model.getInterpretation(classModel);
115 var classElementInterpretation = model.getInterpretation(classElement);
116 var featureInterpretation = model.getInterpretation(feature);
117 var featuresInterpretation = model.getInterpretation(features);
118 var classesInterpretation = model.getInterpretation(classes);
119
120 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
121 var newModel = dseAdapter.createObject();
122 var newModelId = newModel.get(0);
123 var newClass1 = dseAdapter.createObject();
124 var newClass1Id = newClass1.get(0);
125 var newClass2 = dseAdapter.createObject();
126 var newClass2Id = newClass2.get(0);
127 var newField = dseAdapter.createObject();
128 var newFieldId = newField.get(0);
129
130 classModelInterpretation.put(newModel, true);
131 classElementInterpretation.put(newClass1, true);
132 classElementInterpretation.put(newClass2, true);
133 featureInterpretation.put(newField, true);
134 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
135 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
136 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
137
138 queryEngine.flushChanges();
139
140 var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsSets();
141 var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsSets();
142
143 assertResults(Map.of(
144 Tuple.of(newClass1Id, newFieldId), true,
145 Tuple.of(newClass2Id, newFieldId), true
146 ), assignFeatureRuleActivations);
147
148 assertResults(Map.of(
149 Tuple.of(newModelId, newClass1Id), true,
150 Tuple.of(newModelId, newClass2Id), true
151 ), deleteEmptyClassRuleActivations);
152 }
153
154 @Test
155 void randomActivationTest() {
156 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
157 (builder, model, c) -> builder.clause((f) -> List.of(
158 classModelView.call(model),
159 classElementView.call(c),
160 featuresView.call(model, f),
161 not(encapsulatesView.call(c, f))
162 )));
163
164 TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0",
165 deleteEmptyClassPrecondition,
166 (model) -> {
167 var classesInterpretation = model.getInterpretation(classes);
168 var classElementInterpretation = model.getInterpretation(classElement);
169 return ((Tuple activation) -> {
170 var modelElement = activation.get(0);
171 var classElement = activation.get(1);
172
173 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
174 classElementInterpretation.put(Tuple.of(classElement), false);
175 });
176 },
177 0L);
178
179 TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1",
180 deleteEmptyClassPrecondition,
181 (model) -> {
182 var classesInterpretation = model.getInterpretation(classes);
183 var classElementInterpretation = model.getInterpretation(classElement);
184 return ((Tuple activation) -> {
185 var modelElement = activation.get(0);
186 var classElement = activation.get(1);
187
188 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
189 classElementInterpretation.put(Tuple.of(classElement), false);
190 });
191 },
192 78634L);
193
194 var store = ModelStore.builder()
195 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
196 .with(ViatraModelQueryAdapter.builder()
197 .queries(deleteEmptyClassPrecondition))
198 .with(DesignSpaceExplorationAdapter.builder())
199 .build();
200
201 var model = store.createEmptyModel();
202 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
203 deleteEmptyClassRule0.prepare(model, queryEngine);
204 deleteEmptyClassRule1.prepare(model, queryEngine);
205
206 var classModelInterpretation = model.getInterpretation(classModel);
207 var classElementInterpretation = model.getInterpretation(classElement);
208 var featureInterpretation = model.getInterpretation(feature);
209 var featuresInterpretation = model.getInterpretation(features);
210 var classesInterpretation = model.getInterpretation(classes);
211
212 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
213 var newModel = dseAdapter.createObject();
214 var newModelId = newModel.get(0);
215 var newClass1 = dseAdapter.createObject();
216 var newClass1Id = newClass1.get(0);
217 var newClass2 = dseAdapter.createObject();
218 var newClass2Id = newClass2.get(0);
219 var newField = dseAdapter.createObject();
220 var newFieldId = newField.get(0);
221
222 classModelInterpretation.put(newModel, true);
223 classElementInterpretation.put(newClass1, true);
224 classElementInterpretation.put(newClass2, true);
225 featureInterpretation.put(newField, true);
226 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
227 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
228 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
229
230 queryEngine.flushChanges();
231
232
233 var activation0 = deleteEmptyClassRule0.getRandomActivation().activation();
234 var activation1 = deleteEmptyClassRule1.getRandomActivation().activation();
235
236 assertResults(Map.of(
237 Tuple.of(newModelId, newClass1Id), true,
238 Tuple.of(newModelId, newClass2Id), true
239 ), deleteEmptyClassRule0.getAllActivationsAsSets());
240
241 assertResults(Map.of(
242 Tuple.of(newModelId, newClass1Id), true,
243 Tuple.of(newModelId, newClass2Id), true
244 ), deleteEmptyClassRule1.getAllActivationsAsSets());
245
246 assertEquals(Tuple.of(newModelId, newClass2Id), activation0);
247 assertEquals(Tuple.of(newModelId, newClass1Id), activation1);
248
249 }
250
251 @Test
252 void fireTest() {
253 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
254 (builder, model, c) -> builder.clause((f) -> List.of(
255 classModelView.call(model),
256 classElementView.call(c),
257 featuresView.call(model, f),
258 not(encapsulatesView.call(c, f))
259 )));
260
261 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass",
262 deleteEmptyClassPrecondition,
263 (model) -> {
264 var classesInterpretation = model.getInterpretation(classes);
265 var classElementInterpretation = model.getInterpretation(classElement);
266 return ((Tuple activation) -> {
267 var modelElement = activation.get(0);
268 var classElement = activation.get(1);
269
270 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
271 classElementInterpretation.put(Tuple.of(classElement), false);
272 });
273 });
274
275 var store = ModelStore.builder()
276 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
277 .with(ViatraModelQueryAdapter.builder()
278 .queries(deleteEmptyClassPrecondition))
279 .with(DesignSpaceExplorationAdapter.builder())
280 .build();
281
282 var model = store.createEmptyModel();
283 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
284 deleteEmptyClassRule.prepare(model, queryEngine);
285
286 var classModelInterpretation = model.getInterpretation(classModel);
287 var classElementInterpretation = model.getInterpretation(classElement);
288 var featureInterpretation = model.getInterpretation(feature);
289 var featuresInterpretation = model.getInterpretation(features);
290 var classesInterpretation = model.getInterpretation(classes);
291
292 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
293 var newModel = dseAdapter.createObject();
294 var newModelId = newModel.get(0);
295 var newClass1 = dseAdapter.createObject();
296 var newClass1Id = newClass1.get(0);
297 var newClass2 = dseAdapter.createObject();
298 var newClass2Id = newClass2.get(0);
299 var newField = dseAdapter.createObject();
300 var newFieldId = newField.get(0);
301
302 classModelInterpretation.put(newModel, true);
303 classElementInterpretation.put(newClass1, true);
304 classElementInterpretation.put(newClass2, true);
305 featureInterpretation.put(newField, true);
306 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
307 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
308 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
309
310 queryEngine.flushChanges();
311
312 assertResults(Map.of(
313 Tuple.of(newModelId, newClass1Id), true,
314 Tuple.of(newModelId, newClass2Id), true
315 ), deleteEmptyClassRule.getAllActivationsAsSets());
316
317
318 deleteEmptyClassRule.fireActivation(Tuple.of(0, 1));
319
320 assertResults(Map.of(
321 Tuple.of(newModelId, newClass1Id), false,
322 Tuple.of(newModelId, newClass2Id), true
323 ), deleteEmptyClassRule.getAllActivationsAsSets());
324 }
325
326 @Test
327 void randomFireTest() {
328 var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition",
329 (builder, model, c) -> builder.clause((f) -> List.of(
330 classModelView.call(model),
331 classElementView.call(c),
332 featuresView.call(model, f),
333 not(encapsulatesView.call(c, f))
334 )));
335
336 TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0",
337 deleteEmptyClassPrecondition,
338 (model) -> {
339 var classesInterpretation = model.getInterpretation(classes);
340 var classElementInterpretation = model.getInterpretation(classElement);
341 return ((Tuple activation) -> {
342 var modelElement = activation.get(0);
343 var classElement = activation.get(1);
344
345 classesInterpretation.put(Tuple.of(modelElement, classElement), false);
346 classElementInterpretation.put(Tuple.of(classElement), false);
347 });
348 },
349 0L);
350
351 var store = ModelStore.builder()
352 .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features)
353 .with(ViatraModelQueryAdapter.builder()
354 .queries(deleteEmptyClassPrecondition))
355 .with(DesignSpaceExplorationAdapter.builder())
356 .build();
357
358 var model = store.createEmptyModel();
359 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
360 deleteEmptyClassRule.prepare(model, queryEngine);
361
362 var classModelInterpretation = model.getInterpretation(classModel);
363 var classElementInterpretation = model.getInterpretation(classElement);
364 var featureInterpretation = model.getInterpretation(feature);
365 var featuresInterpretation = model.getInterpretation(features);
366 var classesInterpretation = model.getInterpretation(classes);
367
368 var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
369 var newModel = dseAdapter.createObject();
370 var newModelId = newModel.get(0);
371 var newClass1 = dseAdapter.createObject();
372 var newClass1Id = newClass1.get(0);
373 var newClass2 = dseAdapter.createObject();
374 var newClass2Id = newClass2.get(0);
375 var newField = dseAdapter.createObject();
376 var newFieldId = newField.get(0);
377
378 classModelInterpretation.put(newModel, true);
379 classElementInterpretation.put(newClass1, true);
380 classElementInterpretation.put(newClass2, true);
381 featureInterpretation.put(newField, true);
382 classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true);
383 classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true);
384 featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true);
385
386 queryEngine.flushChanges();
387
388 assertResults(Map.of(
389 Tuple.of(newModelId, newClass1Id), true,
390 Tuple.of(newModelId, newClass2Id), true
391 ), deleteEmptyClassRule.getAllActivationsAsSets());
392
393 deleteEmptyClassRule.fireRandomActivation();
394
395 assertResults(Map.of(
396 Tuple.of(newModelId, newClass1Id), true,
397 Tuple.of(newModelId, newClass2Id), false
398 ), deleteEmptyClassRule.getAllActivationsAsSets());
399
400 deleteEmptyClassRule.fireRandomActivation();
401
402 assertResults(Map.of(
403 Tuple.of(newModelId, newClass1Id), false,
404 Tuple.of(newModelId, newClass2Id), false
405 ), deleteEmptyClassRule.getAllActivationsAsSets());
406
407 }
408}
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
new file mode 100644
index 00000000..be514eaf
--- /dev/null
+++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java
@@ -0,0 +1,57 @@
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 org.junit.jupiter.api.function.Executable;
9import tools.refinery.store.query.resultset.ResultSet;
10import tools.refinery.store.tuple.Tuple;
11
12import java.util.*;
13
14import static org.hamcrest.MatcherAssert.assertThat;
15import static org.hamcrest.Matchers.is;
16import static org.hamcrest.Matchers.nullValue;
17import static org.junit.jupiter.api.Assertions.assertAll;
18
19public final class QueryAssertions {
20 private QueryAssertions() {
21 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
22 }
23
24 public static <T> void assertNullableResults(Map<Tuple, Optional<T>> expected, ResultSet<T> resultSet) {
25 var nullableValuesMap = new LinkedHashMap<Tuple, T>(expected.size());
26 for (var entry : expected.entrySet()) {
27 nullableValuesMap.put(entry.getKey(), entry.getValue().orElse(null));
28 }
29 assertResults(nullableValuesMap, resultSet);
30 }
31
32 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) {
33 var defaultValue = resultSet.getQuery().defaultValue();
34 var filteredExpected = new LinkedHashMap<Tuple, T>();
35 var executables = new ArrayList<Executable>();
36 for (var entry : expected.entrySet()) {
37 var key = entry.getKey();
38 var value = entry.getValue();
39 if (!Objects.equals(value, defaultValue)) {
40 filteredExpected.put(key, value);
41 }
42 executables.add(() -> assertThat("value for key " + key,resultSet.get(key), is(value)));
43 }
44 executables.add(() -> assertThat("results size", resultSet.size(), is(filteredExpected.size())));
45
46 var actual = new LinkedHashMap<Tuple, T>();
47 var cursor = resultSet.getAll();
48 while (cursor.move()) {
49 var key = cursor.getKey();
50 var previous = actual.put(key, cursor.getValue());
51 assertThat("duplicate value for key " + key, previous, nullValue());
52 }
53 executables.add(() -> assertThat("results cursor", actual, is(filteredExpected)));
54
55 assertAll(executables);
56 }
57}
diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts
index e3a22145..ef73e10a 100644
--- a/subprojects/store-query-viatra/build.gradle.kts
+++ b/subprojects/store-query-viatra/build.gradle.kts
@@ -12,4 +12,6 @@ dependencies {
12 implementation(libs.ecore) 12 implementation(libs.ecore)
13 api(libs.viatra) 13 api(libs.viatra)
14 api(project(":refinery-store-query")) 14 api(project(":refinery-store-query"))
15 api(project(":refinery-store-reasoning"))
16 api(project(":refinery-visualization"))
15} 17}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
index a028b81b..e2ab72e7 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
@@ -8,9 +8,11 @@ package tools.refinery.store.model;
8import tools.refinery.store.adapter.ModelAdapter; 8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.map.Version; 9import tools.refinery.store.map.Version;
10import tools.refinery.store.map.Versioned; 10import tools.refinery.store.map.Versioned;
11import tools.refinery.store.model.internal.VersionedInterpretation;
11import tools.refinery.store.representation.AnySymbol; 12import tools.refinery.store.representation.AnySymbol;
12import tools.refinery.store.representation.Symbol; 13import tools.refinery.store.representation.Symbol;
13 14
15import java.util.Map;
14import java.util.Optional; 16import java.util.Optional;
15 17
16public interface Model extends Versioned { 18public interface Model extends Versioned {
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
index c2ad9257..92694af4 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
@@ -187,4 +187,8 @@ public class ModelImpl implements Model {
187 public void removeListener(ModelListener listener) { 187 public void removeListener(ModelListener listener) {
188 listeners.remove(listener); 188 listeners.remove(listener);
189 } 189 }
190
191 public Map<? extends AnySymbol, ? extends Interpretation<?>> getInterpretations() {
192 return interpretations;
193 }
190} 194}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
index 65fa8d24..2bd187a8 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
@@ -59,13 +59,13 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
59 59
60 @Override 60 @Override
61 public ModelStore build() { 61 public ModelStore build() {
62 for (int i = adapters.size() - 1; i >= 0; i--) {
63 adapters.get(i).configure(this);
64 }
62 var stores = new LinkedHashMap<AnySymbol, VersionedMapStore<Tuple, ?>>(allSymbols.size()); 65 var stores = new LinkedHashMap<AnySymbol, VersionedMapStore<Tuple, ?>>(allSymbols.size());
63 for (var entry : equivalenceClasses.entrySet()) { 66 for (var entry : equivalenceClasses.entrySet()) {
64 createStores(stores, entry.getKey(), entry.getValue()); 67 createStores(stores, entry.getKey(), entry.getValue());
65 } 68 }
66 for (int i = adapters.size() - 1; i >= 0; i--) {
67 adapters.get(i).configure(this);
68 }
69 var modelStore = new ModelStoreImpl(stores, adapters.size()); 69 var modelStore = new ModelStoreImpl(stores, adapters.size());
70 for (var adapterBuilder : adapters) { 70 for (var adapterBuilder : adapters) {
71 var storeAdapter = adapterBuilder.build(modelStore); 71 var storeAdapter = adapterBuilder.build(modelStore);
diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts
new file mode 100644
index 00000000..abad0491
--- /dev/null
+++ b/subprojects/visualization/build.gradle.kts
@@ -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 */
6
7plugins {
8 id("tools.refinery.gradle.java-library")
9}
10
11dependencies {
12 api(project(":refinery-store-query"))
13}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java
new file mode 100644
index 00000000..bc32323d
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java
@@ -0,0 +1,44 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.map.Version;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.visualization.internal.FileFormat;
12import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl;
13
14public interface ModelVisualizerAdapter extends ModelAdapter {
15
16 ModelVisualizerStoreAdapter getStoreAdapter();
17 static ModelVisualizerBuilder builder() {
18 return new ModelVisualizerBuilderImpl();
19 }
20
21 public String createDotForCurrentModelState();
22
23 public String createDotForModelState(Version version);
24
25 public boolean saveDot(String dot, String filePath);
26
27 public boolean renderDot(String dot, String filePath);
28
29 public boolean renderDot(String dot, FileFormat format, String filePath);
30
31 public void addTransition(Version from, Version to, String action);
32
33
34 public void addTransition(Version from, Version to, String action, Tuple activation);
35 public void addState(Version state);
36 public void addSolution(Version state);
37
38 public boolean saveDesignSpace(String path);
39
40 public boolean renderDesignSpace(String path);
41
42 public boolean renderDesignSpace(String path, FileFormat format);
43
44}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java
new file mode 100644
index 00000000..56cc425e
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java
@@ -0,0 +1,11 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization;
7
8import tools.refinery.store.adapter.ModelAdapterBuilder;
9
10public interface ModelVisualizerBuilder extends ModelAdapterBuilder {
11}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java
new file mode 100644
index 00000000..6599d4c3
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java
@@ -0,0 +1,12 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization;
7
8import tools.refinery.store.adapter.ModelStoreAdapter;
9import tools.refinery.store.query.ModelQueryStoreAdapter;
10
11public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter {
12}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java
new file mode 100644
index 00000000..c5dffeb2
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java
@@ -0,0 +1,26 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization.internal;
7
8public enum FileFormat {
9 BMP("bmp"),
10 DOT("dot"),
11 JPEG("jpg"),
12 PDF("pdf"),
13 PLAIN("plain"),
14 PNG("png"),
15 SVG("svg");
16
17 private final String format;
18
19 FileFormat(String format) {
20 this.format = format;
21 }
22
23 public String getFormat() {
24 return format;
25 }
26}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java
new file mode 100644
index 00000000..b4db2682
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java
@@ -0,0 +1,29 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization.internal;
7
8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.visualization.ModelVisualizerStoreAdapter;
12
13public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter {
14 private final ModelStore store;
15
16 public ModelVisualizeStoreAdapterImpl(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 ModelVisualizerAdapterImpl(model, this);
28 }
29}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java
new file mode 100644
index 00000000..06cc8113
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java
@@ -0,0 +1,342 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization.internal;
7
8import tools.refinery.store.map.Version;
9import tools.refinery.store.model.Interpretation;
10import tools.refinery.store.model.Model;
11import tools.refinery.store.representation.AnySymbol;
12import tools.refinery.store.representation.TruthValue;
13import tools.refinery.store.tuple.Tuple;
14import tools.refinery.visualization.ModelVisualizerAdapter;
15import tools.refinery.visualization.ModelVisualizerStoreAdapter;
16
17import java.io.*;
18import java.util.*;
19import java.util.stream.Collectors;
20
21public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter {
22 private final Model model;
23 private final ModelVisualizerStoreAdapter storeAdapter;
24 private final Map<AnySymbol, Interpretation<?>> interpretations;
25 private final StringBuilder designSpaceBuilder = new StringBuilder();
26 private final Map<Version, Integer> states = new HashMap<>();
27 private int transitionCounter = 0;
28 private Integer numberOfStates = 0;
29 private static final Map<Object, String> truthValueToDot = new HashMap<>()
30 {{
31 put(TruthValue.TRUE, "1");
32 put(TruthValue.FALSE, "0");
33 put(TruthValue.UNKNOWN, "½");
34 put(TruthValue.ERROR, "E");
35 put(true, "1");
36 put(false, "0");
37 }};
38
39 public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) {
40 this.model = model;
41 this.storeAdapter = storeAdapter;
42 this.interpretations = new HashMap<>();
43 for (var symbol : storeAdapter.getStore().getSymbols()) {
44 var arity = symbol.arity();
45 if (arity < 1 || arity > 2) {
46 continue;
47 }
48 var interpretation = (Interpretation<?>) model.getInterpretation(symbol);
49 interpretations.put(symbol, interpretation);
50 }
51 designSpaceBuilder.append("digraph designSpace {\n");
52 designSpaceBuilder.append("""
53 node[
54 style=filled
55 fillcolor=white
56 ]
57 """);
58 }
59
60 @Override
61 public Model getModel() {
62 return model;
63 }
64
65 @Override
66 public ModelVisualizerStoreAdapter getStoreAdapter() {
67 return storeAdapter;
68 }
69
70 @Override
71 public String createDotForCurrentModelState() {
72
73 var unaryTupleToInterpretationsMap = new HashMap<Tuple, LinkedHashSet<Interpretation<?>>>();
74
75 var sb = new StringBuilder();
76
77 sb.append("digraph model {\n");
78 sb.append("""
79 node [
80 \tstyle="filled, rounded"
81 \tshape=plain
82 \tpencolor="#00000088"
83 \tfontname="Helvetica"
84 ]
85 """);
86 sb.append("""
87 edge [
88 \tlabeldistance=3
89 \tfontname="Helvetica"
90 ]
91 """);
92
93 for (var entry : interpretations.entrySet()) {
94 var key = entry.getKey();
95 var arity = key.arity();
96 var cursor = entry.getValue().getAll();
97 if (arity == 1) {
98 while (cursor.move()) {
99 unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>())
100 .add(entry.getValue());
101 }
102 } else if (arity == 2) {
103 while (cursor.move()) {
104 var tuple = cursor.getKey();
105 for (var i = 0; i < tuple.getSize(); i++) {
106 var id = tuple.get(i);
107 unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>());
108 }
109 sb.append(drawEdge(cursor.getKey(), key, entry.getValue()));
110 }
111 }
112 }
113 for (var entry : unaryTupleToInterpretationsMap.entrySet()) {
114 sb.append(drawElement(entry));
115 }
116 sb.append("}");
117 return sb.toString();
118 }
119
120 private StringBuilder drawElement(Map.Entry<Tuple, LinkedHashSet<Interpretation<?>>> entry) {
121 var sb = new StringBuilder();
122
123 var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\"";
124
125 var key = entry.getKey();
126 var id = key.get(0);
127 var mainLabel = String.valueOf(id);
128 var interpretations = entry.getValue();
129 var backgroundColor = toBackgroundColorString(averageColor(interpretations));
130
131 sb.append(id);
132 sb.append(" [\n");
133 sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n");
134 sb.append("\tlabel=");
135 if (interpretations.isEmpty()) {
136 sb.append("<<TABLE").append(tableStyle).append(">\n\t<TR><TD>").append(mainLabel).append("</TD></TR>");
137 }
138 else {
139 sb.append("<<TABLE").append(tableStyle).append(">\n\t\t<TR><TD COLSPAN=\"3\" BORDER=\"2\" SIDES=\"B\">")
140 .append(mainLabel).append("</TD></TR>\n");
141 for (var interpretation : interpretations) {
142 var rawValue = interpretation.get(key);
143
144 if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) {
145 continue;
146 }
147 var color = "black";
148 if (rawValue.equals(TruthValue.ERROR)) {
149 color = "red";
150 }
151 var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString());
152 var symbol = interpretation.getSymbol();
153
154 if (symbol.valueType() == String.class) {
155 value = "\"" + value + "\"";
156 }
157 sb.append("\t\t<TR><TD><FONT COLOR=\"").append(color).append("\">")
158 .append(interpretation.getSymbol().name())
159 .append("</FONT></TD><TD><FONT COLOR=\"").append(color).append("\">")
160 .append("=</FONT></TD><TD><FONT COLOR=\"").append(color).append("\">").append(value)
161 .append("</FONT></TD></TR>\n");
162 }
163 }
164 sb.append("\t\t</TABLE>>\n");
165 sb.append("]\n");
166
167 return sb;
168 }
169
170 private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation<?> interpretation) {
171 var value = interpretation.get(edge);
172
173 if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) {
174 return "";
175 }
176
177 var sb = new StringBuilder();
178 var style = "solid";
179 var color = "black";
180 if (value.equals(TruthValue.UNKNOWN)) {
181 style = "dotted";
182 }
183 else if (value.equals(TruthValue.ERROR)) {
184 style = "dashed";
185 color = "red";
186 }
187
188 var from = edge.get(0);
189 var to = edge.get(1);
190 var name = symbol.name();
191 sb.append(from).append(" -> ").append(to)
192 .append(" [\n\tstyle=").append(style)
193 .append("\n\tcolor=").append(color)
194 .append("\n\tfontcolor=").append(color)
195 .append("\n\tlabel=\"").append(name)
196 .append("\"]\n");
197 return sb.toString();
198 }
199
200 private String toBackgroundColorString(Integer[] backgroundColor) {
201 if (backgroundColor.length == 3)
202 return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]);
203 else if (backgroundColor.length == 4)
204 return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2],
205 backgroundColor[3]);
206 return null;
207 }
208
209 private Integer[] typeColor(String name) {
210 var random = new Random(name.hashCode());
211 return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 };
212 }
213
214 private Integer[] averageColor(Set<Interpretation<?>> interpretations) {
215 if(interpretations.isEmpty()) {
216 return new Integer[]{256, 256, 256};
217 }
218 // TODO: Only use interpretations where the value is not false (or unknown)
219 var symbols = interpretations.stream()
220 .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new);
221
222
223
224 return new Integer[] {
225 Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(),
226 Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(),
227 Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue()
228 };
229 }
230
231 @Override
232 public String createDotForModelState(Version version) {
233 var currentVersion = model.getState();
234 model.restore(version);
235 var graph = createDotForCurrentModelState();
236 model.restore(currentVersion);
237 return graph;
238 }
239
240 @Override
241 public boolean saveDot(String dot, String filePath) {
242 File file = new File(filePath);
243 file.getParentFile().mkdirs();
244
245 try (FileWriter writer = new FileWriter(file)) {
246 writer.write(dot);
247 } catch (Exception e) {
248 e.printStackTrace();
249 return false;
250 }
251 return true;
252 }
253
254 @Override
255 public boolean renderDot(String dot, String filePath) {
256 return renderDot(dot, FileFormat.SVG, filePath);
257 }
258
259 @Override
260 public boolean renderDot(String dot, FileFormat format, String filePath) {
261 try {
262 Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start();
263
264 OutputStream osToProcess = process.getOutputStream();
265 PrintWriter pwToProcess = new PrintWriter(osToProcess);
266 pwToProcess.write(dot);
267 pwToProcess.close();
268 } catch (IOException e) {
269 e.printStackTrace();
270 return false;
271 }
272 return true;
273 }
274
275 @Override
276 public void addTransition(Version from, Version to, String action) {
277 designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to))
278 .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n");
279 }
280
281 @Override
282 public void addTransition(Version from, Version to, String action, Tuple activation) {
283 designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to))
284 .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / ");
285
286
287 for (int i = 0; i < activation.getSize(); i++) {
288 designSpaceBuilder.append(activation.get(i));
289 if (i < activation.getSize() - 1) {
290 designSpaceBuilder.append(", ");
291 }
292 }
293 designSpaceBuilder.append("\"]\n");
294 }
295
296 @Override
297 public void addState(Version state) {
298 if (states.containsKey(state)) {
299 return;
300 }
301 states.put(state, numberOfStates++);
302 designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n");
303 }
304
305 @Override
306 public void addSolution(Version state) {
307 addState(state);
308 designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n");
309 }
310
311 private String buildDesignSpaceDot() {
312 designSpaceBuilder.append("}");
313 return designSpaceBuilder.toString();
314 }
315
316 @Override
317 public boolean saveDesignSpace(String path) {
318 saveDot(buildDesignSpaceDot(), path + "/designSpace.dot");
319 for (var entry : states.entrySet()) {
320 saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot");
321 }
322 return true;
323 }
324
325 @Override
326 public boolean renderDesignSpace(String path) {
327 return renderDesignSpace(path, FileFormat.SVG);
328 }
329
330 @Override
331 public boolean renderDesignSpace(String path, FileFormat format) {
332 for (var entry : states.entrySet()) {
333 var stateId = entry.getValue();
334 var stateDot = createDotForModelState(entry.getKey());
335 saveDot(stateDot, path + "/" + stateId + ".dot");
336 renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat());
337 }
338 var designSpaceDot = buildDesignSpaceDot();
339 saveDot(designSpaceDot, path + "/designSpace.dot");
340 return renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat());
341 }
342}
diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java
new file mode 100644
index 00000000..d19cf2cf
--- /dev/null
+++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java
@@ -0,0 +1,19 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.visualization.internal;
7
8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.visualization.ModelVisualizerBuilder;
11
12public class ModelVisualizerBuilderImpl
13 extends AbstractModelAdapterBuilder<ModelVisualizeStoreAdapterImpl>
14 implements ModelVisualizerBuilder {
15 @Override
16 protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) {
17 return new ModelVisualizeStoreAdapterImpl(store);
18 }
19}