
es.urjc.etsii.grafo.io.InstanceManager Maven / Gradle / Ivy
package es.urjc.etsii.grafo.io;
import es.urjc.etsii.grafo.config.InstanceConfiguration;
import es.urjc.etsii.grafo.executors.Executor;
import es.urjc.etsii.grafo.util.IOUtil;
import es.urjc.etsii.grafo.util.StringUtil;
import me.tongfei.progressbar.ProgressBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.lang.ref.SoftReference;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static es.urjc.etsii.grafo.util.IOUtil.checkExists;
/**
* Class to manage instances during the solving lifecycle
*
* @param Instance class
*/
@Service
public class InstanceManager {
private static final Logger log = LoggerFactory.getLogger(InstanceManager.class);
private static final int MAX_LENGTH = 300;
protected final SoftReference EMPTY = new SoftReference<>(null);
protected final InstanceConfiguration instanceConfiguration;
protected final InstanceImporter instanceImporter;
protected final Map> cacheByPath;
protected final Map> solveOrderByExperiment;
/**
* Build instance manager
*
* @param instanceConfiguration instance configuration
* @param instanceImporter instance importer
*/
public InstanceManager(InstanceConfiguration instanceConfiguration, InstanceImporter instanceImporter) {
this.instanceConfiguration = instanceConfiguration;
this.instanceImporter = instanceImporter;
this.cacheByPath = new ConcurrentHashMap<>();
this.solveOrderByExperiment = new ConcurrentHashMap<>();
}
public synchronized List getInstanceSolveOrder(String expName) {
return getInstanceSolveOrder(expName, this.instanceConfiguration.isPreload());
}
/**
* Get which instances have to be solved for a given experiment
*
* @param expName experiment name as string
* @param preload if true load instances to use comparator to sort them, if false uses lexicograph sort by path name
* @return Ordered list of instance identifiers, that can be later used by the getInstance method. Instances should be solved in the returned order.
*/
public synchronized List getInstanceSolveOrder(String expName, boolean preload) {
return this.solveOrderByExperiment.computeIfAbsent(expName, s -> {
String instancePath = this.instanceConfiguration.getPath(expName);
checkExists(instancePath);
List instances = IOUtil.iterate(instancePath);
List sortedInstances;
if (preload) {
sortedInstances = validateAndSort(expName, instances);
} else {
sortedInstances = lexicSort(instances);
}
return sortedInstances;
});
}
protected List validateAndSort(String expName, List instancePaths) {
List sortedInstances;
log.info("Loading all instances to check correctness...");
List instances = new ArrayList<>();
var iterator = ProgressBar.wrap(instancePaths, Executor.getPBarBuilder("Instance validation"));
for (var path : iterator) {
log.debug("Loading instance: {}", path);
I instance = loadInstance(path);
instances.add(instance);
cacheByPath.put(instance.getId(), new SoftReference<>(instance));
}
Collections.sort(instances);
validate(instances, expName);
sortedInstances = instances.stream().map(Instance::getPath).collect(Collectors.toList());
logInstances(sortedInstances);
return sortedInstances;
}
private static void logInstances(List sortedInstances) {
if (!log.isInfoEnabled()) return;
var sortedInstancesArray = sortedInstances.toArray(new String[0]);
var prefix = StringUtil.longestCommonPrefix(sortedInstancesArray);
var suffix = StringUtil.longestCommonSuffix(sortedInstancesArray);
int bothLength = prefix.length() + suffix.length();
List list = new ArrayList<>();
for (String path : sortedInstances) {
if(bothLength > path.length()){
list.add(path);
} else {
String substring = path.substring(prefix.length(), path.length() - suffix.length());
list.add(substring);
}
}
var orderedList = list.toString();
if (orderedList.length() > MAX_LENGTH) {
orderedList = orderedList.substring(0, MAX_LENGTH - 4) + "...]";
}
log.info("Instance validation completed, solve order: {}", orderedList);
}
protected List lexicSort(List instancePaths) {
List sortedInstances = new ArrayList<>();
for (var i : instancePaths) {
sortedInstances.add(i.toAbsolutePath().toString());
}
Collections.sort(sortedInstances);
return sortedInstances;
}
protected void validate(List instances, String expName) {
if (instances.isEmpty()) {
throw new IllegalArgumentException("Could not load any instance for experiment: " + expName);
}
Set names = new HashSet<>();
for (var instance : instances) {
var name = instance.getId();
if (names.contains(name)) {
throw new IllegalArgumentException("Duplicated instance name in instance folder, check that there aren't multiple instances with name: " + name);
}
names.add(name);
}
}
/**
* Returns an instance given a path
*
* @param p Path of instance to load
* @return Loaded instance
*/
protected synchronized I getInstance(Path p) {
String absolutePath = p.toAbsolutePath().toString();
I instance = this.cacheByPath.getOrDefault(absolutePath, EMPTY).get();
if (instance == null) {
// Load and put in cache
instance = loadInstance(p);
}
return instance;
}
protected synchronized I loadInstance(Path p) {
long startLoad = System.nanoTime();
I instance = this.instanceImporter.importInstance(p.toFile());
long endLoad = System.nanoTime();
instance.setProperty(Instance.LOAD_TIME_NANOS, endLoad - startLoad);
for(var e: instance.customProperties().entrySet()){
instance.setProperty(e.getKey(), e.getValue());
}
String absPath = p.toAbsolutePath().toString();
instance.setPath(absPath);
this.cacheByPath.put(absPath, new SoftReference<>(instance));
return instance;
}
/**
* Get instance by ID. The ID format is not guaranteed, it should be treated as an opaque constant.
*
* @param instancePath instance path
* @return Instance
*/
public I getInstance(String instancePath) {
return getInstance(Path.of(instancePath));
}
/**
* Purge instance cache
*/
public void purgeCache() {
this.cacheByPath.clear();
}
public InstanceImporter getUserImporterImplementation() {
return this.instanceImporter;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy