
org.btrplace.scheduler.choco.runner.single.InstanceSolverRunner Maven / Gradle / Ivy
/*
* Copyright 2022 The BtrPlace Authors. All rights reserved.
* Use of this source code is governed by a LGPL-style
* license that can be found in the LICENSE.txt file.
*/
package org.btrplace.scheduler.choco.runner.single;
import org.btrplace.model.Instance;
import org.btrplace.model.Model;
import org.btrplace.model.Node;
import org.btrplace.model.VM;
import org.btrplace.model.constraint.*;
import org.btrplace.plan.ReconfigurationPlan;
import org.btrplace.scheduler.SchedulerException;
import org.btrplace.scheduler.SchedulerModelingException;
import org.btrplace.scheduler.choco.DefaultReconfigurationProblemBuilder;
import org.btrplace.scheduler.choco.LifeCycleViolationException;
import org.btrplace.scheduler.choco.Parameters;
import org.btrplace.scheduler.choco.ReconfigurationProblem;
import org.btrplace.scheduler.choco.constraint.CObjective;
import org.btrplace.scheduler.choco.constraint.ChocoConstraint;
import org.btrplace.scheduler.choco.constraint.ChocoMapper;
import org.btrplace.scheduler.choco.runner.Metrics;
import org.btrplace.scheduler.choco.runner.SolutionStatistics;
import org.btrplace.scheduler.choco.runner.SolvingStatistics;
import org.btrplace.scheduler.choco.view.ChocoView;
import org.chocosolver.solver.Cause;
import org.chocosolver.solver.Solution;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.search.SearchState;
import org.chocosolver.solver.search.loop.monitors.IMonitorSolution;
import org.chocosolver.solver.search.measure.Measures;
import org.chocosolver.solver.search.measure.MeasuresRecorder;
import org.chocosolver.solver.variables.IntVar;
import java.util.*;
import java.util.concurrent.Callable;
/**
* A basic solver that solve a whole instance.
* Use {@link #call()} to compute a solution
*
* @author Fabien Hermenier
*/
public class InstanceSolverRunner implements Callable {
private final Parameters params;
private ReconfigurationProblem rp;
private final Collection cstrs;
private final OptConstraint obj;
private final Model origin;
private final Instance instance;
private SingleRunnerStatistics stats;
/**
* Choco version of the constraints.
*/
private List cConstraints;
private List views;
/**
* Make a new runner.
*
* @param ps the parameters for the solving process
* @param i the instance to solve
*/
public InstanceSolverRunner(Parameters ps, Instance i) {
instance = i;
cstrs = i.getSatConstraints();
obj = i.getOptConstraint();
origin = i.getModel();
params = ps;
}
@Override
@SuppressWarnings("squid:S1166") //for the LifeCycleViolationException
public SolvingStatistics call() throws SchedulerException {
stats = new SingleRunnerStatistics(params, instance, System.currentTimeMillis());
rp = null;
//Build the core problem
long d = -System.currentTimeMillis();
try {
rp = buildRP();
} catch (@SuppressWarnings("unused") LifeCycleViolationException ex) {
//If there is a violation of the cycle it is not a bug that should be propagated
//it it just indicating there is no solution
stats.setCompleted(true);
stats.setMetrics(new Metrics());
return stats;
} finally {
d += System.currentTimeMillis();
stats.setCoreBuildDuration(d);
}
stats.setNbManagedVMs(rp.getManageableVMs().size());
//Customize the core problem
d = -System.currentTimeMillis();
if (!specialise()) {
d += System.currentTimeMillis();
stats.setSpecialisationDuration(d);
stats.setCompleted(true);
return getStatistics();
}
d += System.currentTimeMillis();
stats.setSpecialisationDuration(d);
//statistics
stats.setMetrics(new Metrics(rp.getSolver().getMeasures()));
rp.getLogger().debug(stats.toString());
//The solution monitor to store the measures at each solution
rp.getSolver().plugMonitor((IMonitorSolution) () -> {
Solution solution = new Solution(rp.getModel());
solution.record();
ReconfigurationPlan plan = rp.buildReconfigurationPlan(solution, origin);
views.forEach(v -> v.insertActions(rp, solution, plan));
MeasuresRecorder m = rp.getSolver().getMeasures();
SolutionStatistics st = new SolutionStatistics(new Metrics(m), plan);
IntVar o = rp.getObjective();
if (o != null) {
st.setObjective(solution.getIntVal(o));
}
stats.addSolution(st);
params.solutionListeners().forEach(c -> c.accept(rp, plan));
});
setVerbosity();
//The actual solving process
rp.solve(params.getTimeLimit(), params.doOptimize());
return getStatistics();
}
private void setVerbosity() {
if (params.getVerbosity() >= 2) {
//every second
rp.getSolver().showStatisticsDuringResolution(1000);
}
if (params.getVerbosity() >= 3) {
rp.getSolver().showDecisions();
}
if (params.getVerbosity() >= 4) {
rp.getSolver().showContradiction();
}
}
private boolean specialise() {
//Resolve the view dependencies, add them and inject them
views = resolveDependencies();
views.forEach(rp::addView);
//Inject the sat constraints, 2nd pass on the view. Then the objective for a late optimisation
Optional o = cConstraints.stream().filter(c -> c instanceof CObjective).findFirst();
return views.stream().allMatch(v -> v.inject(params, rp)) &&
cConstraints.stream().filter(c -> !(c instanceof CObjective))
.allMatch(c -> c.inject(params, rp)) &&
views.stream().allMatch(v -> v.beforeSolve(rp)) &&
(!o.isPresent() || o.get().inject(params, rp));
}
private ReconfigurationProblem buildRP() throws SchedulerException {
//Build the RP. As VM state management is not possible
//We extract VM-state related constraints first.
//For other constraint, we just create the right choco constraint
Set toRun = new HashSet<>();
Set toForge = new HashSet<>();
Set toKill = new HashSet<>();
Set toSleep = new HashSet<>();
cConstraints = new ArrayList<>();
for (SatConstraint cstr : cstrs) {
if (params.getVerbosity() >= 1) {
checkNodesExistence(origin, cstr.getInvolvedNodes());
}
//We cannot check for VMs that are going to the ready state
//as they are not forced to be a part of the initial model
//(when they will be forged)
if (!(cstrs instanceof Ready)) {
if (params.getVerbosity() >= 1) {
checkUnknownVMsInMapping(origin, cstr.getInvolvedVMs());
}
}
if (cstr instanceof Running) {
toRun.addAll(cstr.getInvolvedVMs());
} else if (cstr instanceof Sleeping) {
toSleep.addAll(cstr.getInvolvedVMs());
} else if (cstr instanceof Ready) {
if (params.getVerbosity() >= 1) {
checkUnknownVMsInMapping(origin, cstr.getInvolvedVMs());
}
toForge.addAll(cstr.getInvolvedVMs());
} else if (cstr instanceof Killed) {
if (params.getVerbosity() >= 1) {
checkUnknownVMsInMapping(origin, cstr.getInvolvedVMs());
}
toKill.addAll(cstr.getInvolvedVMs());
}
cConstraints.add(build(cstr));
}
cConstraints.add(build(obj));
views = makeViews();
DefaultReconfigurationProblemBuilder rpb = new DefaultReconfigurationProblemBuilder(origin)
.setNextVMsStates(toForge, toRun, toSleep, toKill)
.setParams(params);
if (params.doRepair()) {
Set toManage = new HashSet<>();
cConstraints.forEach(c -> toManage.addAll(c.getMisPlacedVMs(instance)));
views.forEach(v -> toManage.addAll(v.getMisPlacedVMs(instance)));
rpb.setManageableVMs(toManage);
}
//The core views have been instantiated and available through rp.getViews()
//Set the maximum duration
ReconfigurationProblem p = rpb.build();
try {
p.getEnd().updateUpperBound(params.getMaxEnd(), Cause.Null);
} catch (ContradictionException e) {
p.getLogger().debug("Unable to restrict the maximum plan duration to " + params.getMaxEnd(), e);
return null;
}
return p;
}
private List makeViews() throws SchedulerException {
List l = new ArrayList<>();
ChocoMapper mapper = params.getMapper();
origin.getViews().stream().filter(v -> mapper.viewHasMapping(v.getClass())).forEach(v -> l.add(mapper.get(v)));
return l;
}
/**
* Build a sat constraint
*
* @param cstr the model-side constraint
* @return the solver-side constraint
* @throws SchedulerException if the process failed
*/
private ChocoConstraint build(Constraint cstr) throws SchedulerException {
ChocoMapper mapper = params.getMapper();
ChocoConstraint cc = mapper.get(cstr);
if (cc == null) {
throw new SchedulerModelingException(origin, "No implementation mapped to '" + cstr.getClass().getSimpleName() + "'");
}
return cc;
}
/**
* Flatten the views while considering their dependencies.
* Operations over the views that respect the iteration order, satisfies the dependencies.
*
* @throws SchedulerException if there is a cyclic dependency
*/
public List resolveDependencies() throws SchedulerException {
Set done = new HashSet<>(rp.getViews());
List remaining = new ArrayList<>(views);
List solved = new ArrayList<>();
while (!remaining.isEmpty()) {
ListIterator ite = remaining.listIterator();
boolean blocked = true;
while (ite.hasNext()) {
ChocoView s = ite.next();
if (done.containsAll(s.getDependencies(rp))) {
ite.remove();
done.add(s.getIdentifier());
solved.add(s);
blocked = false;
}
}
if (blocked) {
throw new SchedulerModelingException(rp.getSourceModel(),
"Missing dependencies or cyclic dependencies prevent from using: " + remaining);
}
}
return solved;
}
private static boolean checkUnknownVMsInMapping(Model m, Collection vms) throws SchedulerException {
for (VM v : vms) {
//This loop prevents from a useless allocation of memory when there is no issue
if (!m.getMapping().contains(v)) {
Set unknown = new HashSet<>(vms);
unknown.removeAll(m.getMapping().getAllVMs());
throw new SchedulerModelingException(m, "Unknown VMs: " + unknown);
}
}
return true;
}
/**
* Check for the existence of nodes in a model
*
* @param mo the model to check
* @param ns the nodes to check
* @throws SchedulerModelingException if at least one of the given nodes is not in the RP.
*/
private static boolean checkNodesExistence(Model mo, Collection ns) throws SchedulerModelingException {
for (Node node : ns) {
if (!mo.getMapping().contains(node)) {
throw new SchedulerModelingException(mo, "Unknown node '" + node + "'");
}
}
return true;
}
/**
* Get the statistics about the solving process.
*
* @return the statistics
*/
public SingleRunnerStatistics getStatistics() {
if (rp != null) {
Measures m = rp.getSolver().getMeasures();
stats.setMetrics(new Metrics(m));
stats.setCompleted(m.getSearchState().equals(SearchState.TERMINATED)
|| m.getSearchState().equals(SearchState.NEW)
);
}
return stats;
}
/**
* Stop the solver.
*/
public void stop() {
if (rp == null) {
return;
}
rp.stop();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy