/*
* Copyright © 2010 by Ondrej Skalicka. All Rights Reserved
*/
package cz.cvut.felk.cig.jcop.solver;
import cz.cvut.felk.cig.jcop.algorithm.Algorithm;
import cz.cvut.felk.cig.jcop.problem.Configuration;
import cz.cvut.felk.cig.jcop.problem.ObjectiveProblem;
import cz.cvut.felk.cig.jcop.result.Result;
import cz.cvut.felk.cig.jcop.result.ResultEntry;
import cz.cvut.felk.cig.jcop.result.SimpleResult;
import cz.cvut.felk.cig.jcop.result.render.Render;
import cz.cvut.felk.cig.jcop.result.render.SimpleRender;
import cz.cvut.felk.cig.jcop.solver.condition.StopCondition;
import cz.cvut.felk.cig.jcop.solver.message.*;
import cz.cvut.felk.cig.jcop.util.PreciseTimestamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Implements some methods commonly used in solvers.
* <p/>
* BaseSolver implements method to notify all {@link cz.cvut.felk.cig.jcop.solver.condition.StopCondition
* stopConditions} in solver that something happened and to found if any condition is met.
* <p/>
* BaseSolver has default render {@link cz.cvut.felk.cig.jcop.result.render.SimpleRender} (however if you add more, this
* one is removed, so if you want both {@link cz.cvut.felk.cig.jcop.result.render.SimpleRender} and your own, add both
* using {@link #addRender(cz.cvut.felk.cig.jcop.result.render.Render)}.
* <p/>
* Default {@link cz.cvut.felk.cig.jcop.result.Result} is {@link cz.cvut.felk.cig.jcop.result.SimpleResult} which can be
* overridden by {@link #setResult(cz.cvut.felk.cig.jcop.result.Result)}.
*
* @author Ondrej Skalicka
*/
public abstract class BaseSolver implements Solver {
/**
* List of all stop conditions associated with this solver.
* <p/>
* At least one must be met to stop executing optimize on algorithm/problem.
*/
protected List<StopCondition> stopConditions = new ArrayList<StopCondition>();
/**
* Solver logger
*/
protected Logger logger;
/**
* List of all renders in this solver.
*/
protected List<Render> renders = new ArrayList<Render>();
/**
* Result for this solver.
*/
protected Result result;
/**
* List of renders used if no other renders are specified.
* <p/>
* By default, this list contains only {@link cz.cvut.felk.cig.jcop.result.render.SimpleRender}, but subclasses
* could modify this behaviour. For example Compare solvers could have both SimpleRender and some kind of
* CompareRender.
*/
protected List<Render> defaultRenders = new ArrayList<Render>();
/**
* List of all message listeners registered to this solver.
*/
protected List<MessageListener> messageListeners = new ArrayList<MessageListener>();
/**
* Creates new BaseSolver, gets log {@link LoggerFactory#getLogger(Class)} and set result to {@link
* cz.cvut.felk.cig.jcop.result.SimpleResult}.
*/
protected BaseSolver() {
logger = LoggerFactory.getLogger(this.getClass());
this.result = new SimpleResult();
this.defaultRenders.add(new SimpleRender());
}
public void addRender(Render render) {
this.renders.add(render);
}
/**
* {@inheritDoc}
* <p/>
* BaseSolver has a pitfall if no renders are specified so that it returns list of default renders instead.
*
* @return list of all renders in this solver
*/
public List<Render> getRenders() {
if (this.renders.size() == 0) return this.defaultRenders;
return this.renders;
}
public void setResult(Result result) {
this.result = result;
}
public Result getResult() {
return this.result;
}
public void render() {
for (Render render : this.getRenders()) {
try {
render.render(this.getResult());
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendMessage(Message message) {
for (MessageListener messageListener : messageListeners) {
messageListener.onMessage(message);
}
}
/**
* Applies one algorithm on one problem until any condition is met or exception is raised.
* <p/>
* This method just removes code duplicities since most solvers has identical this part of solving. It is not
* obligatory to use this method however, change it if necessary (for example algorithm switching on simple
* problem).
*
* @param objectiveProblem problem to be solved
* @param algorithm algorithm to be used during solving
* @return result entry for this optimization
*/
protected ResultEntry optimize(ObjectiveProblem objectiveProblem, Algorithm algorithm) {
PreciseTimestamp startPreciseTimestamp = null;
Exception algorithmException = null;
Configuration bestConfiguration = null;
int optimizeCounter = 0;
try {
startPreciseTimestamp = new PreciseTimestamp();
algorithm.init(objectiveProblem);
this.sendMessage(new MessageSolverStart(algorithm, objectiveProblem));
logger.info("Started optimize, {} on {}.", algorithm, objectiveProblem);
do {
algorithm.optimize();
this.sendMessage(new MessageOptimize());
optimizeCounter++;
if (algorithm.getBestConfiguration() != null
&& (bestConfiguration == null || !bestConfiguration.equals(algorithm.getBestConfiguration()))) {
logger.debug("Better solution {}, {}, {}",
algorithm.getBestFitness(), optimizeCounter, algorithm.getBestConfiguration());
bestConfiguration = algorithm.getBestConfiguration();
this.sendMessage(new MessageBetterConfigurationFound(bestConfiguration, algorithm.getBestFitness()));
if (objectiveProblem.isSolution(bestConfiguration))
this.sendMessage(new MessageSolutionFound(bestConfiguration, algorithm.getBestFitness()));
}
if (this.isConditionMet()) break;
} while (true);
} catch (Exception e) {
logger.warn("Got exception {}.", e.getClass().getSimpleName());
algorithmException = e;
}
this.sendMessage(new MessageSolverStop(algorithm, objectiveProblem));
logger.info("Stopped optimize.");
algorithm.cleanUp();
return new ResultEntry(algorithm, objectiveProblem, bestConfiguration, algorithm.getBestFitness(),
optimizeCounter, algorithmException, startPreciseTimestamp);
}
public void addListener(MessageListener messageListener) {
this.messageListeners.add(messageListener);
}
public boolean isConditionMet() {
for (StopCondition stopCondition : this.stopConditions)
if (stopCondition.isConditionMet()) return true;
return false;
}
public void addStopCondition(StopCondition stopCondition) {
this.stopConditions.add(stopCondition);
this.addListener(stopCondition);
}
}