/*
* SPDX-FileCopyrightText: 2023 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.store.dse.transition.statespace.internal;
import org.eclipse.collections.api.block.procedure.Procedure;
import tools.refinery.store.dse.transition.VersionWithObjectiveValue;
import tools.refinery.store.dse.transition.statespace.ActivationStore;
import java.util.*;
public class ActivationStoreImpl implements ActivationStore {
final int numberOfTransformations;
final Procedure actionWhenAllActivationVisited;
final Map> versionToActivations;
public ActivationStoreImpl(final int numberOfTransformations,
Procedure actionWhenAllActivationVisited) {
this.numberOfTransformations = numberOfTransformations;
this.actionWhenAllActivationVisited = actionWhenAllActivationVisited;
versionToActivations = new HashMap<>();
}
public synchronized VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes) {
boolean[] successful = new boolean[]{false};
var entries = versionToActivations.computeIfAbsent(to, x -> {
successful[0] = true;
List result = new ArrayList<>(emptyEntrySizes.length);
for(int emptyEntrySize : emptyEntrySizes) {
result.add(ActivationStoreListEntry.create(emptyEntrySize));
}
return result;
});
boolean hasMore = false;
for (var entry : entries) {
if (entry.getNumberOfUnvisitedActivations() > 0) {
hasMore = true;
break;
}
}
if(!hasMore) {
actionWhenAllActivationVisited.accept(to);
}
return new VisitResult(successful[0], hasMore, -1, -1);
}
public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, int activationIndex) {
var entries = versionToActivations.get(from);
var entry = entries.get(transformationIndex);
final int unvisited = entry.getNumberOfUnvisitedActivations();
final boolean successfulVisit = unvisited > 0;
final boolean hasMoreInActivation = unvisited > 1;
final boolean hasMore;
final int transformation;
final int activation;
if (successfulVisit) {
transformation = transformationIndex;
activation = entry.getAndAddActivationAfter(activationIndex);
} else {
transformation = -1;
activation = -1;
}
if(!hasMoreInActivation) {
boolean hasMoreInOtherTransformation = false;
for (var e : entries) {
if (e != entry && e.getNumberOfVisitedActivations() > 0) {
hasMoreInOtherTransformation = true;
break;
}
}
hasMore = hasMoreInOtherTransformation;
} else {
hasMore = true;
}
if(!hasMore) {
actionWhenAllActivationVisited.accept(from);
}
return new VisitResult(successfulVisit, hasMore, transformation, activation);
}
@Override
public synchronized boolean hasUnmarkedActivation(VersionWithObjectiveValue version) {
var entries = versionToActivations.get(version);
boolean hasMore = false;
for (var entry : entries) {
if (entry.getNumberOfUnvisitedActivations() > 0) {
hasMore = true;
break;
}
}
return hasMore;
}
@Override
public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) {
var entries = versionToActivations.get(version);
int sum1 = 0;
for (var entry : entries) {
sum1 += entry.getNumberOfUnvisitedActivations();
}
int selected = random.nextInt(sum1);
int sum2 = 0;
int transformation = 0;
int activation = -1;
for (; transformation < entries.size(); transformation++) {
var entry = entries.get(transformation);
int unvisited = entry.getNumberOfUnvisitedActivations();
if (selected < sum2 + unvisited) {
activation = sum2 + unvisited - selected - 1;
break;
} else {
sum2 += unvisited;
}
}
if (activation == -1) {
throw new IllegalArgumentException("no unvisited");
}
return this.visitActivation(version, transformation, activation);
}
}