aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-dse/src/main/java/tools
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-07 23:08:09 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-07 23:08:47 +0200
commit59dff3ea8673e402c576d94a28ec26d3ab181a92 (patch)
treefcc769ea25605a8cd7289c76784db9bfab68defa /subprojects/store-dse/src/main/java/tools
parentMerge remote-tracking branch 'nagilooh/datastructure' into partial-interpreta... (diff)
downloadrefinery-59dff3ea8673e402c576d94a28ec26d3ab181a92.tar.gz
refinery-59dff3ea8673e402c576d94a28ec26d3ab181a92.tar.zst
refinery-59dff3ea8673e402c576d94a28ec26d3ab181a92.zip
fix(dse): best-first strategy
The query engine must be flushed after firing a tranformation activation.
Diffstat (limited to 'subprojects/store-dse/src/main/java/tools')
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java136
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java6
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java29
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java19
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java5
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java35
6 files changed, 69 insertions, 161 deletions
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java
index a2b6268f..8f7e3bdc 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java
@@ -5,7 +5,6 @@
5 */ 5 */
6package tools.refinery.store.dse.strategy; 6package tools.refinery.store.dse.strategy;
7 7
8import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
9import tools.refinery.store.model.Model; 8import tools.refinery.store.model.Model;
10 9
11import java.util.Random; 10import java.util.Random;
@@ -13,6 +12,7 @@ import java.util.Random;
13public class BestFirstExplorer extends BestFirstWorker { 12public class BestFirstExplorer extends BestFirstWorker {
14 final int id; 13 final int id;
15 Random random; 14 Random random;
15
16 public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) { 16 public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) {
17 super(storeManager, model); 17 super(storeManager, model);
18 this.id = id; 18 this.id = id;
@@ -20,6 +20,7 @@ public class BestFirstExplorer extends BestFirstWorker {
20 } 20 }
21 21
22 private boolean interrupted = false; 22 private boolean interrupted = false;
23
23 public void interrupt() { 24 public void interrupt() {
24 this.interrupted = true; 25 this.interrupted = true;
25 } 26 }
@@ -29,138 +30,37 @@ public class BestFirstExplorer extends BestFirstWorker {
29 } 30 }
30 31
31 public void explore() { 32 public void explore() {
32 VersionWithObjectiveValue lastVisited = submit().newVersion(); 33 var lastBest = submit().newVersion();
33
34 while (shouldRun()) { 34 while (shouldRun()) {
35 35 if (lastBest == null) {
36 if (lastVisited == null) { 36 lastBest = restoreToBest();
37 lastVisited = this.restoreToBest(); 37 if (lastBest == null) {
38 if(lastVisited == null) {
39 return; 38 return;
40 } 39 }
41 } 40 }
42
43 boolean tryActivation = true; 41 boolean tryActivation = true;
44 while(tryActivation && shouldRun()) { 42 while (tryActivation && shouldRun()) {
45 RandomVisitResult randomVisitResult = this.visitRandomUnvisited(random); 43 var randomVisitResult = this.visitRandomUnvisited(random);
46
47 tryActivation = randomVisitResult.shouldRetry(); 44 tryActivation = randomVisitResult.shouldRetry();
48 var newSubmit = randomVisitResult.submitResult(); 45 var newSubmit = randomVisitResult.submitResult();
49 if(newSubmit != null) { 46 if (newSubmit != null) {
50 if(!newSubmit.include()) { 47 if (!newSubmit.include()) {
51 restoreToLast(); 48 restoreToLast();
52 } else { 49 } else {
53 var newVisit = newSubmit.newVersion(); 50 var newVisit = newSubmit.newVersion();
54 int compareResult = compare(lastVisited,newVisit); 51 int compareResult = compare(lastBest, newVisit);
55 if(compareResult >= 0) { 52 if (compareResult >= 0) {
56 lastVisited = newVisit; 53 lastBest = newVisit;
57 break;
58 }
59 }
60 }
61 else {
62 lastVisited = null;
63 break;
64 }
65 }
66
67 //final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper();
68
69 /*boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints();
70 if (!globalConstraintsAreSatisfied) {
71 // Global constraint is not satisfied in the first state. Terminate.
72 return;
73 }
74
75 final Fitness firstFitness = dseAdapter.getFitness();
76 if (firstFitness.isSatisfiesHardObjectives()) {
77 dseAdapter.newSolution();
78 // First state is a solution. Terminate.
79 if (backTrackIfSolution) {
80 return;
81 }
82 }
83
84 if (maxDepth == 0) {
85 return;
86 }*/
87
88 /*
89 var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness);
90 trajectoriesToExplore.add(firstTrajectoryWithFitness);
91 TrajectoryWithFitness currentTrajectoryWithFitness = null;
92 */
93/*
94 Collection<Activation> activations = dseAdapter.getUntraversedActivations();
95 Iterator<Activation> iterator = activations.iterator();
96
97 while (iterator.hasNext()) {
98 final Activation nextActivation = iterator.next();
99 if (!iterator.hasNext()) {
100 // Last untraversed activation of the state.
101 trajectoriesToExplore.remove(currentTrajectoryWithFitness);
102 }
103
104 // Executing new activation
105 dseAdapter.fireActivation(nextActivation);
106 if (dseAdapter.isCurrentStateAlreadyTraversed()) {
107 // The new state is already visited.
108 dseAdapter.backtrack();
109 } else if (!dseAdapter.checkGlobalConstraints()) {
110 // Global constraint is not satisfied.
111 dseAdapter.backtrack();
112 } else {
113 final Fitness nextFitness = dseAdapter.getFitness();
114 if (nextFitness.isSatisfiesHardObjectives()) {
115 dseAdapter.newSolution();
116 var solutions = dseAdapter.getSolutions().size();
117 if (solutions >= maxSolutions) {
118 return;
119 }
120 // Found a solution.
121 if (backTrackIfSolution) {
122 dseAdapter.backtrack();
123 continue;
124 }
125 }
126 if (dseAdapter.getDepth() >= maxDepth) {
127 // Reached max depth.
128 dseAdapter.backtrack();
129 continue;
130 }
131
132 TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness(
133 dseAdapter.getTrajectory(), nextFitness);
134 trajectoriesToExplore.add(nextTrajectoryWithFitness);
135
136 int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness,
137 nextTrajectoryWithFitness.fitness);
138 if (compare < 0) {
139 // Better fitness, moving on
140 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
141 continue mainLoop;
142 } else if (compare == 0) {
143 if (onlyBetterFirst) {
144 // Equally good fitness, backtrack
145 dseAdapter.backtrack();
146 } else { 54 } else {
147 // Equally good fitness, moving on 55 lastBest = null;
148 currentTrajectoryWithFitness = nextTrajectoryWithFitness;
149 continue mainLoop;
150 } 56 }
151 } else { 57 break;
152 //"Worse fitness
153 currentTrajectoryWithFitness = null;
154 continue mainLoop;
155 } 58 }
59 } else {
60 lastBest = null;
61 break;
156 } 62 }
157 } 63 }
158
159 // State is fully traversed.
160 currentTrajectoryWithFitness = null;
161*/
162 } 64 }
163 // Interrupted.
164
165 } 65 }
166} 66}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java
index 4ccba6f7..02634a02 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java
@@ -17,9 +17,7 @@ import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriority
17import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl; 17import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl;
18import tools.refinery.store.map.Version; 18import tools.refinery.store.map.Version;
19import tools.refinery.store.model.ModelStore; 19import tools.refinery.store.model.ModelStore;
20import tools.refinery.store.statecoding.StateCoderResult;
21import tools.refinery.store.statecoding.StateCoderStoreAdapter; 20import tools.refinery.store.statecoding.StateCoderStoreAdapter;
22import tools.refinery.visualization.ModelVisualizerStoreAdapter;
23import tools.refinery.visualization.statespace.VisualizationStore; 21import tools.refinery.visualization.statespace.VisualizationStore;
24import tools.refinery.visualization.statespace.internal.VisualizationStoreImpl; 22import tools.refinery.visualization.statespace.internal.VisualizationStoreImpl;
25 23
@@ -34,7 +32,7 @@ public class BestFirstStoreManager {
34 EquivalenceClassStore equivalenceClassStore; 32 EquivalenceClassStore equivalenceClassStore;
35 VisualizationStore visualizationStore; 33 VisualizationStore visualizationStore;
36 34
37 public BestFirstStoreManager(ModelStore modelStore) { 35 public BestFirstStoreManager(ModelStore modelStore, int maxNumberOfSolutions) {
38 this.modelStore = modelStore; 36 this.modelStore = modelStore;
39 DesignSpaceExplorationStoreAdapter storeAdapter = 37 DesignSpaceExplorationStoreAdapter storeAdapter =
40 modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class); 38 modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class);
@@ -42,7 +40,7 @@ public class BestFirstStoreManager {
42 objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); 40 objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives());
43 Consumer<VersionWithObjectiveValue> whenAllActivationsVisited = x -> objectiveStore.remove(x); 41 Consumer<VersionWithObjectiveValue> whenAllActivationsVisited = x -> objectiveStore.remove(x);
44 activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); 42 activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited);
45 solutionStore = new SolutionStoreImpl(50); 43 solutionStore = new SolutionStoreImpl(maxNumberOfSolutions);
46 equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { 44 equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) {
47 @Override 45 @Override
48 protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { 46 protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) {
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java
index f1bec14f..5d738297 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java
@@ -40,11 +40,13 @@ public class BestFirstWorker {
40 isVisualizationEnabled = visualizationStore != null; 40 isVisualizationEnabled = visualizationStore != null;
41 } 41 }
42 42
43 private VersionWithObjectiveValue last = null; 43 protected VersionWithObjectiveValue last = null;
44
45 //public boolean isIncluded
46 44
47 public SubmitResult submit() { 45 public SubmitResult submit() {
46 checkSynchronized();
47 if (queryAdapter.hasPendingChanges()) {
48 throw new AssertionError("Pending changes detected before model submission");
49 }
48 if (explorationAdapter.checkExclude()) { 50 if (explorationAdapter.checkExclude()) {
49 return new SubmitResult(false, false, null, null); 51 return new SubmitResult(false, false, null, null);
50 } 52 }
@@ -86,14 +88,11 @@ public class BestFirstWorker {
86 88
87 public VersionWithObjectiveValue restoreToBest() { 89 public VersionWithObjectiveValue restoreToBest() {
88 var bestVersion = storeManager.getObjectiveStore().getBest(); 90 var bestVersion = storeManager.getObjectiveStore().getBest();
91 last = bestVersion;
89 if (bestVersion != null) { 92 if (bestVersion != null) {
90 var oldVersion = model.getState();
91 this.model.restore(bestVersion.version()); 93 this.model.restore(bestVersion.version());
92 if (isVisualizationEnabled) {
93 visualizationStore.addTransition(oldVersion, last.version(), "");
94 }
95 } 94 }
96 return bestVersion; 95 return last;
97 } 96 }
98 97
99 public VersionWithObjectiveValue restoreToRandom(Random random) { 98 public VersionWithObjectiveValue restoreToRandom(Random random) {
@@ -102,7 +101,7 @@ public class BestFirstWorker {
102 if (randomVersion != null) { 101 if (randomVersion != null) {
103 this.model.restore(randomVersion.version()); 102 this.model.restore(randomVersion.version());
104 } 103 }
105 return randomVersion; 104 return last;
106 } 105 }
107 106
108 public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) { 107 public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) {
@@ -121,9 +120,10 @@ public class BestFirstWorker {
121 } 120 }
122 121
123 public RandomVisitResult visitRandomUnvisited(Random random) { 122 public RandomVisitResult visitRandomUnvisited(Random random) {
123 checkSynchronized();
124 if (!model.hasUncommittedChanges()) { 124 if (!model.hasUncommittedChanges()) {
125 queryAdapter.flushChanges();
126 var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); 125 var visitResult = activationStoreWorker.fireRandomActivation(this.last, random);
126 queryAdapter.flushChanges();
127 127
128 if (visitResult.successfulVisit()) { 128 if (visitResult.successfulVisit()) {
129 Version oldVersion = null; 129 Version oldVersion = null;
@@ -133,7 +133,8 @@ public class BestFirstWorker {
133 var submitResult = submit(); 133 var submitResult = submit();
134 if (isVisualizationEnabled && submitResult.newVersion() != null) { 134 if (isVisualizationEnabled && submitResult.newVersion() != null) {
135 var newVersion = submitResult.newVersion().version(); 135 var newVersion = submitResult.newVersion().version();
136 visualizationStore.addTransition(oldVersion, newVersion, ""); 136 visualizationStore.addTransition(oldVersion, newVersion,
137 "fire: " + visitResult.transformation() + ", " + visitResult.activation());
137 } 138 }
138 return new RandomVisitResult(submitResult, visitResult.mayHaveMore()); 139 return new RandomVisitResult(submitResult, visitResult.mayHaveMore());
139 } else { 140 } else {
@@ -147,4 +148,10 @@ public class BestFirstWorker {
147 public boolean hasEnoughSolution() { 148 public boolean hasEnoughSolution() {
148 return storeManager.solutionStore.hasEnoughSolution(); 149 return storeManager.solutionStore.hasEnoughSolution();
149 } 150 }
151
152 private void checkSynchronized() {
153 if (last != null && !last.version().equals(model.getState())) {
154 throw new AssertionError("Worker is not synchronized with model state");
155 }
156 }
150} 157}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java
index 24145d03..c204ae35 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java
@@ -16,32 +16,35 @@ public class ActivationStoreBitVectorEntry extends ActivationStoreEntry {
16 @Override 16 @Override
17 public int getNumberOfVisitedActivations() { 17 public int getNumberOfVisitedActivations() {
18 int visited = 0; 18 int visited = 0;
19 for (int i : selected) { 19 // Use indexed for loop to avoid allocating an iterator.
20 visited += Integer.bitCount(i); 20 //noinspection ForLoopReplaceableByForEach
21 for (int i = 0; i < selected.length; i++) {
22 visited += Integer.bitCount(selected[i]);
21 } 23 }
22 return visited; 24 return visited;
23 } 25 }
24 26
25 private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE 27 private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE
26 private static final int ELEMENT_BITMASK = (1<<ELEMENT_POSITION)-1; 28 private static final int ELEMENT_BITMASK = (1 << ELEMENT_POSITION) - 1;
29
27 @Override 30 @Override
28 public int getAndAddActivationAfter(int index) { 31 public int getAndAddActivationAfter(int index) {
29 int position = index; 32 int position = index;
30 do { 33 do {
31 final int selectedElement = position >> ELEMENT_POSITION; 34 final int selectedElement = position >> ELEMENT_POSITION;
32 final int selectedBit = 1<<(position & ELEMENT_BITMASK); 35 final int selectedBit = 1 << (position & ELEMENT_BITMASK);
33 36
34 if((selected[selectedElement] & selectedBit) == 0) { 37 if ((selected[selectedElement] & selectedBit) == 0) {
35 selected[selectedElement] |= selectedBit; 38 selected[selectedElement] |= selectedBit;
36 return position; 39 return position;
37 } else { 40 } else {
38 if(position < this.numberOfActivations-1) { 41 if (position < this.numberOfActivations - 1) {
39 position++; 42 position++;
40 } else { 43 } else {
41 position = 0; 44 position = 0;
42 } 45 }
43 } 46 }
44 } while(position != index); 47 } while (position != index);
45 throw new IllegalArgumentException("There is are no unvisited activations!"); 48 throw new IllegalArgumentException("There is are no unvisited activations!");
46 } 49 }
47} 50}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java
index d7339805..7249751c 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java
@@ -17,6 +17,11 @@ public abstract class ActivationStoreEntry {
17 public int getNumberOfUnvisitedActivations() { 17 public int getNumberOfUnvisitedActivations() {
18 return numberOfActivations - getNumberOfVisitedActivations(); 18 return numberOfActivations - getNumberOfVisitedActivations();
19 } 19 }
20
21 public int getNumberOfActivations() {
22 return numberOfActivations;
23 }
24
20 public abstract int getAndAddActivationAfter(int index); 25 public abstract int getAndAddActivationAfter(int index);
21 26
22 // public abstract boolean contains(int activation) 27 // public abstract boolean contains(int activation)
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java
index 4d775b5a..d9e29eca 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java
@@ -28,7 +28,7 @@ public class ActivationStoreImpl implements ActivationStore {
28 var entries = versionToActivations.computeIfAbsent(to, x -> { 28 var entries = versionToActivations.computeIfAbsent(to, x -> {
29 successful[0] = true; 29 successful[0] = true;
30 List<ActivationStoreEntry> result = new ArrayList<>(emptyEntrySizes.length); 30 List<ActivationStoreEntry> result = new ArrayList<>(emptyEntrySizes.length);
31 for(int emptyEntrySize : emptyEntrySizes) { 31 for (int emptyEntrySize : emptyEntrySizes) {
32 result.add(ActivationStoreEntry.create(emptyEntrySize)); 32 result.add(ActivationStoreEntry.create(emptyEntrySize));
33 } 33 }
34 return result; 34 return result;
@@ -40,13 +40,14 @@ public class ActivationStoreImpl implements ActivationStore {
40 break; 40 break;
41 } 41 }
42 } 42 }
43 if(!hasMore) { 43 if (!hasMore) {
44 actionWhenAllActivationVisited.accept(to); 44 actionWhenAllActivationVisited.accept(to);
45 } 45 }
46 return new VisitResult(successful[0], hasMore, -1, -1); 46 return new VisitResult(successful[0], hasMore, -1, -1);
47 } 47 }
48 48
49 public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, int activationIndex) { 49 public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex,
50 int activationIndex) {
50 var entries = versionToActivations.get(from); 51 var entries = versionToActivations.get(from);
51 var entry = entries.get(transformationIndex); 52 var entry = entries.get(transformationIndex);
52 final int unvisited = entry.getNumberOfUnvisitedActivations(); 53 final int unvisited = entry.getNumberOfUnvisitedActivations();
@@ -66,7 +67,7 @@ public class ActivationStoreImpl implements ActivationStore {
66 activation = -1; 67 activation = -1;
67 } 68 }
68 69
69 if(!hasMoreInActivation) { 70 if (!hasMoreInActivation) {
70 boolean hasMoreInOtherTransformation = false; 71 boolean hasMoreInOtherTransformation = false;
71 for (var e : entries) { 72 for (var e : entries) {
72 if (e != entry && e.getNumberOfUnvisitedActivations() > 0) { 73 if (e != entry && e.getNumberOfUnvisitedActivations() > 0) {
@@ -79,7 +80,7 @@ public class ActivationStoreImpl implements ActivationStore {
79 hasMore = true; 80 hasMore = true;
80 } 81 }
81 82
82 if(!hasMore) { 83 if (!hasMore) {
83 actionWhenAllActivationVisited.accept(from); 84 actionWhenAllActivationVisited.accept(from);
84 } 85 }
85 86
@@ -103,34 +104,28 @@ public class ActivationStoreImpl implements ActivationStore {
103 public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) { 104 public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) {
104 var entries = versionToActivations.get(version); 105 var entries = versionToActivations.get(version);
105 106
106 int sum1 = 0; 107 int numberOfAllUnvisitedActivations = 0;
107 for (var entry : entries) { 108 for (var entry : entries) {
108 sum1 += entry.getNumberOfUnvisitedActivations(); 109 numberOfAllUnvisitedActivations += entry.getNumberOfUnvisitedActivations();
109 } 110 }
110 111
111 if(sum1 == 0) { 112 if (numberOfAllUnvisitedActivations == 0) {
112 this.actionWhenAllActivationVisited.accept(version); 113 this.actionWhenAllActivationVisited.accept(version);
113 return new VisitResult(false, false, -1, -1); 114 return new VisitResult(false, false, -1, -1);
114 } 115 }
115 116
116 int selected = random.nextInt(sum1); 117 int offset = random.nextInt(numberOfAllUnvisitedActivations);
117 int sum2 = 0;
118 int transformation = 0; 118 int transformation = 0;
119 int activation = -1;
120 for (; transformation < entries.size(); transformation++) { 119 for (; transformation < entries.size(); transformation++) {
121 var entry = entries.get(transformation); 120 var entry = entries.get(transformation);
122 int unvisited = entry.getNumberOfUnvisitedActivations(); 121 int unvisited = entry.getNumberOfUnvisitedActivations();
123 if (selected < sum2 + unvisited) { 122 if (unvisited > 0 && offset < unvisited) {
124 activation = sum2 + unvisited - selected - 1; 123 int activation = random.nextInt(entry.getNumberOfActivations());
125 break; 124 return this.visitActivation(version, transformation, activation);
126 } else {
127 sum2 += unvisited;
128 } 125 }
129 } 126 offset -= unvisited;
130 if (activation == -1) {
131 throw new IllegalArgumentException("no unvisited");
132 } 127 }
133 128
134 return this.visitActivation(version, transformation, activation); 129 throw new AssertionError("Unvisited activation %d not found".formatted(offset));
135 } 130 }
136} 131}