
es.urjc.etsii.grafo.executors.Executor Maven / Gradle / Ivy
package es.urjc.etsii.grafo.executors;
import es.urjc.etsii.grafo.algorithms.Algorithm;
import es.urjc.etsii.grafo.algorithms.EmptyAlgorithm;
import es.urjc.etsii.grafo.algorithms.FMode;
import es.urjc.etsii.grafo.annotations.InheritedComponent;
import es.urjc.etsii.grafo.config.SolverConfig;
import es.urjc.etsii.grafo.events.EventPublisher;
import es.urjc.etsii.grafo.events.types.ErrorEvent;
import es.urjc.etsii.grafo.events.types.SolutionGeneratedEvent;
import es.urjc.etsii.grafo.exception.ExceptionHandler;
import es.urjc.etsii.grafo.exceptions.DefaultExceptionHandler;
import es.urjc.etsii.grafo.experiment.Experiment;
import es.urjc.etsii.grafo.experiment.reference.ReferenceResultProvider;
import es.urjc.etsii.grafo.io.Instance;
import es.urjc.etsii.grafo.io.InstanceManager;
import es.urjc.etsii.grafo.io.serializers.SolutionExportFrequency;
import es.urjc.etsii.grafo.services.IOManager;
import es.urjc.etsii.grafo.services.SolutionValidator;
import es.urjc.etsii.grafo.services.TimeLimitCalculator;
import es.urjc.etsii.grafo.solution.Solution;
import es.urjc.etsii.grafo.metrics.Metrics;
import es.urjc.etsii.grafo.solver.Mork;
import es.urjc.etsii.grafo.util.DoubleComparator;
import es.urjc.etsii.grafo.util.TimeControl;
import es.urjc.etsii.grafo.util.TimeUtil;
import es.urjc.etsii.grafo.util.ValidationUtil;
import es.urjc.etsii.grafo.util.random.RandomManager;
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;
import me.tongfei.progressbar.ProgressBarStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static es.urjc.etsii.grafo.orchestrator.AbstractOrchestrator.decideImplementation;
import static es.urjc.etsii.grafo.util.TimeUtil.nanosToSecs;
/**
* Processes work units
*
* @param Solution class
* @param Instance class
*/
@InheritedComponent
public abstract class Executor, I extends Instance> {
private static final Logger log = LoggerFactory.getLogger(Executor.class);
public static final int EXTRA_SECS_BEFORE_WARNING = 10;
protected final Optional> validator;
protected final Optional> timeLimitCalculator;
protected final IOManager io;
protected final InstanceManager instanceManager;
protected final List referenceResultProviders;
protected final SolverConfig solverConfig;
protected final FMode ofmode = Mork.getFMode();
private final ExceptionHandler exceptionHandler;
/**
* If time control is enabled, remove it and check ellapsed time to see if too many time has been spent
*
* @param Solution class
* @param Instance class
* @param timeLimitCalculator time limit calculator if implemented
* @param workUnit current work unit
*/
public static , I extends Instance> void endTimeControl(Optional> timeLimitCalculator, WorkUnit workUnit) {
if (timeLimitCalculator.isPresent()) {
if (TimeControl.remaining() < -TimeUtil.secsToNanos(EXTRA_SECS_BEFORE_WARNING)) {
log.warn("Algorithm takes too long to stop after time is up. Instance {}, algorithm {}", workUnit.instancePath(), workUnit.algorithm());
}
TimeControl.remove();
}
}
/**
* Fill common values used by all executors
*
* @param validator solution validator if available
* @param timeLimitCalculator time limit calculator if exists
* @param io IO manager
* @param referenceResultProviders list of all reference value providers implementations
* @param exceptionHandlers list of exception handlers available
*/
protected Executor(
Optional> validator,
Optional> timeLimitCalculator,
IOManager io,
InstanceManager instanceManager,
List referenceResultProviders,
SolverConfig solverConfig,
List> exceptionHandlers) {
this.timeLimitCalculator = timeLimitCalculator;
this.referenceResultProviders = referenceResultProviders;
this.solverConfig = solverConfig;
this.exceptionHandler = decideImplementation(exceptionHandlers, DefaultExceptionHandler.class);
if (validator.isEmpty()) {
log.warn("No SolutionValidator implementation has been found, solution CORRECTNESS WILL NOT BE CHECKED");
} else {
log.info("SolutionValidator implementation found: {}", validator.get().getClass().getSimpleName());
}
this.validator = validator;
this.io = io;
this.instanceManager = instanceManager;
}
public abstract void executeExperiment(Experiment experiment, List instanceNames, long startTimestamp);
/**
* Finalize and destroy all resources, we have finished and are shutting down now.
*/
public abstract void shutdown();
/**
* Run both user specific validations and our own.
*
* @param solution Solution to check.
*/
public void validate(S solution) {
ValidationUtil.positiveTTB(solution);
var instanceName = solution.getInstance().getId();
var optimalValue = this.getOptionalReferenceValue(instanceName, true);
if (optimalValue.isPresent()) {
// Check that solution score is not better than optimal value
double solutionScore = solution.getScore();
if(ofmode.isBetter(solutionScore, optimalValue.get())){
throw new AssertionError("Solution score (%s) improves optimal value (%s) in ReferenceResultProvider".formatted(solutionScore, optimalValue.get()));
}
}
// Run user validations if used implemented them
this.validator.ifPresent(v -> v.validate(solution));
}
/**
* Execute a single iteration for the given (experiment, instance, algorithm, iterationId)
*
* @param workUnit Minimum unit of work, cannot be divided further.
*/
protected WorkUnitResult doWork(WorkUnit workUnit) {
S solution = null;
I instance = this.instanceManager.getInstance(workUnit.instancePath());
Algorithm algorithm = workUnit.algorithm();
try {
// Preparate current work unit
RandomManager.reset(workUnit.i());
if (this.timeLimitCalculator.isPresent()) {
long maxDuration = this.timeLimitCalculator.get().timeLimitInMillis(instance, algorithm);
TimeControl.setMaxExecutionTime(maxDuration, TimeUnit.MILLISECONDS);
TimeControl.start();
}
if (solverConfig.isMetrics()) {
Metrics.enableMetrics();
Metrics.resetMetrics();
}
// Do real work
long starTime = System.nanoTime();
solution = algorithm.algorithm(instance);
long endTime = System.nanoTime();
// Prepare work unit results and cleanup
endTimeControl(timeLimitCalculator, workUnit);
validate(solution);
long timeToTarget = solution.getLastModifiedTime() - starTime;
long executionTime = endTime - starTime;
var metrics = Metrics.areMetricsEnabled()? Metrics.getCurrentThreadMetrics() : null;
return new WorkUnitResult<>(workUnit, solution, executionTime, timeToTarget, metrics);
} catch (Exception e) {
exceptionHandler.handleException(workUnit.experimentName(), workUnit.i(), e, Optional.ofNullable(solution), instance, workUnit.algorithm());
EventPublisher.getInstance().publishEvent(new ErrorEvent(e));
return null;
}
}
protected void processWorkUnitResult(WorkUnitResult r, ProgressBar pb) {
pb.step();
io.exportSolution(r, SolutionExportFrequency.ALL);
var solutionGenerated = new SolutionGeneratedEvent<>(r.iteration(), r.solution(), r.experimentName(), r.algorithm(), r.executionTime(), r.timeToTarget(), r.metrics());
EventPublisher.getInstance().publishEvent(solutionGenerated);
if (log.isDebugEnabled()) {
log.debug(String.format("\t%s.\tT(s): %.3f \tTTB(s): %.3f \t%s", r.iteration(), nanosToSecs(r.executionTime()), nanosToSecs(r.timeToTarget()), r.solution()));
}
}
protected void exportAlgorithmInstanceSolution(WorkUnitResult r) {
// replace best iteration number with generic text to avoid overwriting the corresponding work unit result
var modifiedWorkUnit = new WorkUnitResult<>(r.experimentName(), r.algorithm(), "bestiter", r.solution(), r.executionTime(), r.timeToTarget(), r.metrics());
io.exportSolution(modifiedWorkUnit, SolutionExportFrequency.BEST_PER_ALG_INSTANCE);
}
protected void exportInstanceSolution(WorkUnitResult r) {
// replace best iteration number and algorithm name with generic text to avoid overwriting the corresponding work unit result
var modifiedWorkUnit = new WorkUnitResult<>(r.experimentName(), new EmptyAlgorithm<>("bestalg"), "bestiter", r.solution(), r.executionTime(), r.timeToTarget(), r.metrics());
io.exportSolution(modifiedWorkUnit, SolutionExportFrequency.BEST_PER_INSTANCE);
}
protected Optional getOptionalReferenceValue(String instanceName, boolean onlyOptimal) {
double best = Mork.isMaximizing() ? Integer.MIN_VALUE : Integer.MAX_VALUE;
for (var r : referenceResultProviders) {
double score = Double.NaN;
var ref = r.getValueFor(instanceName);
if (ref != null) {
score = ref.getScoreOrNan();
}
// Ignore if not valid value
if (Double.isFinite(score) && (!onlyOptimal || ref.isOptimalValue())) {
if (Mork.isMaximizing()) {
best = Math.max(best, score);
} else {
best = Math.min(best, score);
}
}
}
if (best == Integer.MAX_VALUE || best == Integer.MIN_VALUE) {
return Optional.empty();
} else {
return Optional.of(best);
}
}
/**
* Create workunits with solve order
*
* @param experiment experiment definition
* @param instancePaths instance name list
* @param repetitions how many times should we repeat the (instance, algorithm) pair
* @return Map of workunits per instance
*/
protected Map, List>>> getOrderedWorkUnits(Experiment experiment, List instancePaths, int repetitions) {
var workUnits = new LinkedHashMap, List>>>();
for (String instancePath : instancePaths) {
var algWorkUnits = new LinkedHashMap, List>>();
for (var alg : experiment.algorithms()) {
var list = new ArrayList>();
for (int i = 0; i < repetitions; i++) {
var workUnit = new WorkUnit<>(experiment.name(), instancePath, alg, i);
list.add(workUnit);
}
algWorkUnits.put(alg, list);
}
workUnits.put(instancePath, algWorkUnits);
}
return workUnits;
}
protected boolean improves(WorkUnitResult candidate, WorkUnitResult best) {
if (candidate == null) {
throw new IllegalArgumentException("Null candidate");
}
if (best == null) {
return true;
}
if (Mork.isMaximizing()) {
return DoubleComparator.isGreater(candidate.solution().getScore(), best.solution().getScore());
} else {
return DoubleComparator.isLess(candidate.solution().getScore(), best.solution().getScore());
}
}
public String instanceName(String instancePath) {
return this.instanceManager.getInstance(instancePath).getId();
}
public static ProgressBarBuilder getPBarBuilder(String taskname) {
return new ProgressBarBuilder()
.setUpdateIntervalMillis(50)
.continuousUpdate()
.setTaskName(taskname)
.setStyle(ProgressBarStyle.COLORFUL_UNICODE_BAR);
}
public ProgressBar getGlobalSolvingProgressBar(String expName, Map, List>>> workUnits) {
int totalUnits = 0;
for (var v1 : workUnits.values()) {
for (var v2 : v1.values()) {
totalUnits += v2.size();
}
}
return getPBarBuilder(expName)
.setInitialMax(totalUnits)
.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy