/*******************************************************************************
* Copyright (C) 2014 Stefan Schroeder
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package jsprit.core.algorithm.io;
import jsprit.core.algorithm.*;
import jsprit.core.algorithm.acceptor.*;
import jsprit.core.algorithm.io.VehicleRoutingAlgorithms.TypedMap.*;
import jsprit.core.algorithm.listener.AlgorithmEndsListener;
import jsprit.core.algorithm.listener.AlgorithmStartsListener;
import jsprit.core.algorithm.listener.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
import jsprit.core.algorithm.listener.VehicleRoutingAlgorithmListeners.Priority;
import jsprit.core.algorithm.module.RuinAndRecreateModule;
import jsprit.core.algorithm.recreate.InsertionStrategy;
import jsprit.core.algorithm.recreate.VehicleSwitched;
import jsprit.core.algorithm.recreate.listener.InsertionListener;
import jsprit.core.algorithm.ruin.RadialRuinStrategyFactory;
import jsprit.core.algorithm.ruin.RandomRuinStrategyFactory;
import jsprit.core.algorithm.ruin.RuinStrategy;
import jsprit.core.algorithm.ruin.distance.AvgServiceAndShipmentDistance;
import jsprit.core.algorithm.ruin.distance.JobDistance;
import jsprit.core.algorithm.selector.SelectBest;
import jsprit.core.algorithm.selector.SelectRandomly;
import jsprit.core.algorithm.selector.SolutionSelector;
import jsprit.core.algorithm.state.*;
import jsprit.core.algorithm.termination.IterationWithoutImprovementTermination;
import jsprit.core.algorithm.termination.PrematureAlgorithmTermination;
import jsprit.core.algorithm.termination.TimeTermination;
import jsprit.core.algorithm.termination.VariationCoefficientTermination;
import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.VehicleRoutingProblem.FleetSize;
import jsprit.core.problem.constraint.ConstraintManager;
import jsprit.core.problem.solution.SolutionCostCalculator;
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.solution.route.activity.End;
import jsprit.core.problem.solution.route.activity.ReverseActivityVisitor;
import jsprit.core.problem.solution.route.activity.TourActivity;
import jsprit.core.problem.vehicle.FiniteFleetManagerFactory;
import jsprit.core.problem.vehicle.InfiniteFleetManagerFactory;
import jsprit.core.problem.vehicle.Vehicle;
import jsprit.core.problem.vehicle.VehicleFleetManager;
import jsprit.core.util.ActivityTimeTracker;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VehicleRoutingAlgorithms {
static class TypedMap {
static interface AbstractKey<K> {
Class<K> getType();
}
static class AcceptorKey implements AbstractKey<SolutionAcceptor>{
private ModKey modKey;
public AcceptorKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AcceptorKey))
return false;
AcceptorKey other = (AcceptorKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SolutionAcceptor> getType() {
return SolutionAcceptor.class;
}
}
static class SelectorKey implements AbstractKey<SolutionSelector>{
private ModKey modKey;
public SelectorKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SelectorKey other = (SelectorKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SolutionSelector> getType() {
return SolutionSelector.class;
}
}
static class StrategyModuleKey implements AbstractKey<SearchStrategyModule>{
private ModKey modKey;
public StrategyModuleKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StrategyModuleKey other = (StrategyModuleKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SearchStrategyModule> getType() {
return SearchStrategyModule.class;
}
}
static class RuinStrategyKey implements AbstractKey<RuinStrategy>{
private ModKey modKey;
public RuinStrategyKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RuinStrategyKey other = (RuinStrategyKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<RuinStrategy> getType() {
return RuinStrategy.class;
}
}
static class InsertionStrategyKey implements AbstractKey<InsertionStrategy>{
private ModKey modKey;
public InsertionStrategyKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InsertionStrategyKey other = (InsertionStrategyKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<InsertionStrategy> getType() {
return InsertionStrategy.class;
}
}
private Map<AbstractKey<?>, Object> map = new HashMap<AbstractKey<?>, Object>();
public <T> T get(AbstractKey<T> key) {
if(map.get(key) == null) return null;
return key.getType().cast(map.get(key));
}
public <T> T put(AbstractKey<T> key, T value) {
return key.getType().cast(map.put(key, key.getType().cast(value)));
}
public Set<AbstractKey<?>> keySet(){
return map.keySet();
}
}
static class ModKey {
private String name;
private String id;
public ModKey(String name, String id) {
super();
this.name = name;
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ModKey other = (ModKey) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
private static Logger log = LogManager.getLogger(VehicleRoutingAlgorithms.class.getName());
private VehicleRoutingAlgorithms(){}
/**
* Creates a {@link jsprit.core.algorithm.VehicleRoutingAlgorithm} from a AlgorithConfig based on the input vrp.
*
* @param vrp the routing problem
* @param algorithmConfig the algorithm config
* @return {@link jsprit.core.algorithm.VehicleRoutingAlgorithm}
*/
public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp, final AlgorithmConfig algorithmConfig){
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),0, null);
}
public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp, int nThreads, final AlgorithmConfig algorithmConfig){
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),nThreads, null);
}
/**
* Read and creates a {@link VehicleRoutingAlgorithm} from an url.
*
* @param vrp the routing problem
* @param configURL config url
* @return {@link jsprit.core.algorithm.VehicleRoutingAlgorithm}
*/
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final URL configURL){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configURL);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),0, null);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, int nThreads, final URL configURL){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configURL);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),nThreads, null);
}
/**
* Read and creates {@link jsprit.core.problem.VehicleRoutingProblem} from config-file.
*
* @param vrp the routing problem
* @param configFileName the config filename (and location)
* @return {@link jsprit.core.algorithm.VehicleRoutingAlgorithm}
*/
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final String configFileName){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configFileName);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),0, null);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final String configFileName, StateManager stateManager){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configFileName);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),0, stateManager);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, int nThreads, final String configFileName, StateManager stateManager){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configFileName);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(), nThreads, stateManager);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(VehicleRoutingProblem vrp, int nThreads, String configFileName) {
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configFileName);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration(),nThreads, null);
}
private static class OpenRouteStateVerifier implements StateUpdater, ReverseActivityVisitor{
private End end;
private boolean firstAct = true;
private Vehicle vehicle;
@Override
public void begin(VehicleRoute route) {
end = route.getEnd();
vehicle = route.getVehicle();
}
@Override
public void visit(TourActivity activity) {
if(firstAct){
firstAct=false;
if(!vehicle.isReturnToDepot()){
assert activity.getLocationId().equals(end.getLocationId()) : "route end and last activity are not equal even route is open. this should not be.";
}
}
}
@Override
public void finish() {
firstAct = true;
}
}
private static VehicleRoutingAlgorithm createAlgo(final VehicleRoutingProblem vrp, XMLConfiguration config, int nuOfThreads, StateManager stateMan){
//create state-manager
final StateManager stateManager;
if(stateMan!=null) {
stateManager = stateMan;
}
else{
stateManager = new StateManager(vrp);
}
stateManager.updateLoadStates();
stateManager.updateTimeWindowStates();
stateManager.updateSkillStates();
stateManager.addStateUpdater(new UpdateEndLocationIfRouteIsOpen());
stateManager.addStateUpdater(new OpenRouteStateVerifier());
stateManager.addStateUpdater(new UpdateActivityTimes(vrp.getTransportCosts()));
stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager));
/*
* define constraints
*/
//constraint manager
ConstraintManager constraintManager = new ConstraintManager(vrp,stateManager);
constraintManager.addTimeWindowConstraint();
constraintManager.addLoadConstraint();
constraintManager.addSkillsConstraint();
return readAndCreateAlgorithm(vrp, config, nuOfThreads, null, stateManager, constraintManager, true);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, AlgorithmConfig config,
int nuOfThreads, SolutionCostCalculator solutionCostCalculator, final StateManager stateManager, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
return readAndCreateAlgorithm(vrp, config.getXMLConfiguration(),nuOfThreads, solutionCostCalculator, stateManager, constraintManager, addDefaultCostCalculators);
}
private static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, XMLConfiguration config,
int nuOfThreads, SolutionCostCalculator solutionCostCalculator, final StateManager stateManager, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
// map to store constructed modules
TypedMap definedClasses = new TypedMap();
// algorithm listeners
Set<PrioritizedVRAListener> algorithmListeners = new HashSet<PrioritizedVRAListener>();
// insertion listeners
List<InsertionListener> insertionListeners = new ArrayList<InsertionListener>();
//threading
final ExecutorService executorService;
if(nuOfThreads > 0){
log.info("setup executor-service with " + nuOfThreads + " threads");
executorService = Executors.newFixedThreadPool(nuOfThreads);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, new AlgorithmEndsListener() {
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem,Collection<VehicleRoutingProblemSolution> solutions) {
log.info("shutdown executor-service");
executorService.shutdown();
}
}));
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread arg0, Throwable arg1) {
System.err.println(arg1.toString());
System.exit(0);
}
});
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run(){
if(!executorService.isShutdown()){
System.err.println("shutdowHook shuts down executorService");
executorService.shutdown();
}
}
});
}
else executorService = null;
//create fleetmanager
final VehicleFleetManager vehicleFleetManager = createFleetManager(vrp);
String switchString = config.getString("construction.insertion.allowVehicleSwitch");
final boolean switchAllowed;
if(switchString != null){
switchAllowed = Boolean.parseBoolean(switchString);
}
else switchAllowed = true;
ActivityTimeTracker.ActivityPolicy activityPolicy;
if(stateManager.timeWindowUpdateIsActivated()){
UpdateVehicleDependentPracticalTimeWindows timeWindowUpdater = new UpdateVehicleDependentPracticalTimeWindows(stateManager,vrp.getTransportCosts());
timeWindowUpdater.setVehiclesToUpdate(new UpdateVehicleDependentPracticalTimeWindows.VehiclesToUpdate() {
@Override
public Collection<Vehicle> get(VehicleRoute route) {
Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
vehicles.add(route.getVehicle());
if(switchAllowed) {
vehicles.addAll(vehicleFleetManager.getAvailableVehicles(route.getVehicle()));
}
return vehicles;
}
});
stateManager.addStateUpdater(timeWindowUpdater);
activityPolicy = ActivityTimeTracker.ActivityPolicy.AS_SOON_AS_TIME_WINDOW_OPENS;
}
else{
activityPolicy = ActivityTimeTracker.ActivityPolicy.AS_SOON_AS_ARRIVED;
}
stateManager.addStateUpdater(new UpdateActivityTimes(vrp.getTransportCosts(),activityPolicy));
stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager, activityPolicy));
SolutionCostCalculator costCalculator;
if(solutionCostCalculator==null) costCalculator = getDefaultCostCalculator(stateManager);
else costCalculator = solutionCostCalculator;
//construct initial solution creator
AlgorithmStartsListener createInitialSolution = createInitialSolution(config,vrp,vehicleFleetManager,stateManager,algorithmListeners,definedClasses,executorService,nuOfThreads,costCalculator, constraintManager, addDefaultCostCalculators);
if(createInitialSolution != null) algorithmListeners.add(new PrioritizedVRAListener(Priority.MEDIUM, createInitialSolution));
//construct algorithm, i.e. search-strategies and its modules
int solutionMemory = config.getInt("strategy.memory");
SearchStrategyManager searchStratManager = new SearchStrategyManager();
List<HierarchicalConfiguration> strategyConfigs = config.configurationsAt("strategy.searchStrategies.searchStrategy");
for(HierarchicalConfiguration strategyConfig : strategyConfigs){
String name = getName(strategyConfig);
SolutionAcceptor acceptor = getAcceptor(strategyConfig,vrp,algorithmListeners,definedClasses,solutionMemory);
SolutionSelector selector = getSelector(strategyConfig,vrp,algorithmListeners,definedClasses);
SearchStrategy strategy = new SearchStrategy(selector, acceptor, costCalculator);
strategy.setName(name);
List<HierarchicalConfiguration> modulesConfig = strategyConfig.configurationsAt("modules.module");
for(HierarchicalConfiguration moduleConfig : modulesConfig){
SearchStrategyModule module = buildModule(moduleConfig,vrp,vehicleFleetManager,stateManager,algorithmListeners,definedClasses,executorService,nuOfThreads, constraintManager, addDefaultCostCalculators);
strategy.addModule(module);
}
searchStratManager.addStrategy(strategy, strategyConfig.getDouble("probability"));
}
//construct algorithm
VehicleRoutingAlgorithm metaAlgorithm = new VehicleRoutingAlgorithm(vrp, searchStratManager);
String maxIterationsString = config.getString("iterations");
if(maxIterationsString == null) maxIterationsString = config.getString("maxIterations");
if(maxIterationsString != null) metaAlgorithm.setMaxIterations(Integer.parseInt(maxIterationsString));
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(stateManager);
metaAlgorithm.getAlgorithmListeners().addListener(stateManager);
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new RemoveEmptyVehicles(vehicleFleetManager));
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new ResetAndIniFleetManager(vehicleFleetManager));
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new VehicleSwitched(vehicleFleetManager));
//define prematureBreak
PrematureAlgorithmTermination prematureAlgorithmTermination = getPrematureTermination(config, algorithmListeners);
if(prematureAlgorithmTermination != null) metaAlgorithm.setPrematureAlgorithmTermination(prematureAlgorithmTermination);
else{
List<HierarchicalConfiguration> terminationCriteria = config.configurationsAt("terminationCriteria.termination");
for(HierarchicalConfiguration terminationConfig : terminationCriteria){
PrematureAlgorithmTermination termination = getTerminationCriterion(terminationConfig, algorithmListeners);
if(termination != null) metaAlgorithm.addTerminationCriterion(termination);
}
}
//misc
// algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, new SolutionVerifier()));
//register listeners
registerListeners(metaAlgorithm,algorithmListeners);
registerInsertionListeners(definedClasses,insertionListeners);
return metaAlgorithm;
}
private static SolutionCostCalculator getDefaultCostCalculator(final StateManager stateManager) {
return new VariablePlusFixedSolutionCostCalculatorFactory(stateManager).createCalculator();
}
private static VehicleFleetManager createFleetManager(final VehicleRoutingProblem vrp) {
if(vrp.getFleetSize().equals(FleetSize.INFINITE)){
return new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
}
else if(vrp.getFleetSize().equals(FleetSize.FINITE)){
return new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
}
throw new IllegalStateException("fleet size can only be infinite or finite. " +
"makes sure your config file contains one of these options");
}
private static PrematureAlgorithmTermination getTerminationCriterion(HierarchicalConfiguration config, Set<PrioritizedVRAListener> algorithmListeners) {
String basedOn = config.getString("[@basedOn]");
if(basedOn == null){
log.info("set default prematureBreak, i.e. no premature break at all.");
return null;
}
if(basedOn.equals("iterations")){
log.info("set prematureBreak based on iterations");
String iter = config.getString("iterations");
if(iter == null) throw new IllegalStateException("iterations is missing");
int iterations = Integer.valueOf(iter);
return new IterationWithoutImprovementTermination(iterations);
}
if(basedOn.equals("time")){
log.info("set prematureBreak based on time");
String timeString = config.getString("time");
if(timeString == null) throw new IllegalStateException("time is missing");
double time = Double.valueOf(timeString);
TimeTermination timeBreaker = new TimeTermination(time);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, timeBreaker));
return timeBreaker;
}
if(basedOn.equals("variationCoefficient")){
log.info("set prematureBreak based on variation coefficient");
String thresholdString = config.getString("threshold");
String iterationsString = config.getString("iterations");
if(thresholdString == null) throw new IllegalStateException("threshold is missing");
if(iterationsString == null) throw new IllegalStateException("iterations is missing");
double threshold = Double.valueOf(thresholdString);
int iterations = Integer.valueOf(iterationsString);
VariationCoefficientTermination variationCoefficientBreaker = new VariationCoefficientTermination(iterations, threshold);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, variationCoefficientBreaker));
return variationCoefficientBreaker;
}
throw new IllegalStateException("prematureBreak basedOn " + basedOn + " is not defined");
}
private static PrematureAlgorithmTermination getPrematureTermination(XMLConfiguration config, Set<PrioritizedVRAListener> algorithmListeners) {
String basedOn = config.getString("prematureBreak[@basedOn]");
if(basedOn == null){
log.info("set default prematureBreak, i.e. no premature break at all.");
return null;
}
if(basedOn.equals("iterations")){
log.info("set prematureBreak based on iterations");
String iter = config.getString("prematureBreak.iterations");
if(iter == null) throw new IllegalStateException("prematureBreak.iterations is missing");
int iterations = Integer.valueOf(iter);
return new IterationWithoutImprovementTermination(iterations);
}
if(basedOn.equals("time")){
log.info("set prematureBreak based on time");
String timeString = config.getString("prematureBreak.time");
if(timeString == null) throw new IllegalStateException("prematureBreak.time is missing");
double time = Double.valueOf(timeString);
TimeTermination timeBreaker = new TimeTermination(time);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, timeBreaker));
return timeBreaker;
}
if(basedOn.equals("variationCoefficient")){
log.info("set prematureBreak based on variation coefficient");
String thresholdString = config.getString("prematureBreak.threshold");
String iterationsString = config.getString("prematureBreak.iterations");
if(thresholdString == null) throw new IllegalStateException("prematureBreak.threshold is missing");
if(iterationsString == null) throw new IllegalStateException("prematureBreak.iterations is missing");
double threshold = Double.valueOf(thresholdString);
int iterations = Integer.valueOf(iterationsString);
VariationCoefficientTermination variationCoefficientBreaker = new VariationCoefficientTermination(iterations, threshold);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, variationCoefficientBreaker));
return variationCoefficientBreaker;
}
throw new IllegalStateException("prematureBreak basedOn " + basedOn + " is not defined");
}
private static void registerInsertionListeners(TypedMap definedClasses, List<InsertionListener> insertionListeners) {
for(AbstractKey<?> key : definedClasses.keySet()){
if(key instanceof InsertionStrategyKey){
InsertionStrategyKey insertionKey = (InsertionStrategyKey) key;
InsertionStrategy insertionStrategy = definedClasses.get(insertionKey);
for(InsertionListener l : insertionListeners){
insertionStrategy.addListener(l);
}
}
}
}
private static String getName(HierarchicalConfiguration strategyConfig) {
if(strategyConfig.containsKey("[@name]")){
return strategyConfig.getString("[@name]");
}
return "";
}
private static void registerListeners(VehicleRoutingAlgorithm metaAlgorithm, Set<PrioritizedVRAListener> algorithmListeners) {
metaAlgorithm.getAlgorithmListeners().addAll(algorithmListeners);
}
private static AlgorithmStartsListener createInitialSolution(XMLConfiguration config, final VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, final StateManager routeStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses, ExecutorService executorService, int nuOfThreads, final SolutionCostCalculator solutionCostCalculator, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
List<HierarchicalConfiguration> modConfigs = config.configurationsAt("construction.insertion");
if(modConfigs == null) return null;
if(modConfigs.isEmpty()) return null;
if(modConfigs.size() != 1) throw new IllegalStateException("#construction.modules != 1. 1 expected");
HierarchicalConfiguration modConfig = modConfigs.get(0);
String insertionName = modConfig.getString("[@name]");
if(insertionName == null) throw new IllegalStateException("insertion[@name] is missing.");
String insertionId = modConfig.getString("[@id]");
if(insertionId == null) insertionId = "noId";
ModKey modKey = makeKey(insertionName,insertionId);
InsertionStrategyKey insertionStrategyKey = new InsertionStrategyKey(modKey);
InsertionStrategy insertionStrategy = definedClasses.get(insertionStrategyKey);
if(insertionStrategy == null){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertionStrategy = createInsertionStrategy(modConfig, vrp, vehicleFleetManager, routeStates, prioListeners, executorService, nuOfThreads, constraintManager, addDefaultCostCalculators);
algorithmListeners.addAll(prioListeners);
definedClasses.put(insertionStrategyKey,insertionStrategy);
}
final InsertionStrategy finalInsertionStrategy = insertionStrategy;
return new AlgorithmStartsListener() {
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
InsertionInitialSolutionFactory insertionInitialSolutionFactory = new InsertionInitialSolutionFactory(finalInsertionStrategy, solutionCostCalculator);
VehicleRoutingProblemSolution vrpSol = insertionInitialSolutionFactory.createSolution(vrp);
solutions.add(vrpSol);
}
};
}
private static SolutionSelector getSelector(HierarchicalConfiguration strategyConfig, VehicleRoutingProblem vrp, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedSelectors) {
String selectorName = strategyConfig.getString("selector[@name]");
if(selectorName == null) throw new IllegalStateException("no solutionSelector defined. define either \"selectRandomly\" or \"selectBest\"");
String selectorId = strategyConfig.getString("selector[@id]");
if(selectorId == null) selectorId="noId";
ModKey modKey = makeKey(selectorName,selectorId);
SelectorKey selectorKey = new SelectorKey(modKey);
SolutionSelector definedSelector = definedSelectors.get(selectorKey);
if(definedSelector != null) {
return definedSelector;
}
if(selectorName.equals("selectRandomly")){
SelectRandomly selector = SelectRandomly.getInstance();
definedSelectors.put(selectorKey, selector);
return selector;
}
if(selectorName.equals("selectBest")){
SelectBest selector = SelectBest.getInstance();
definedSelectors.put(selectorKey, selector);
return selector;
}
throw new IllegalStateException("solutionSelector is not know. Currently, it only knows \"selectRandomly\" and \"selectBest\"");
}
private static ModKey makeKey(String name, String id){
return new ModKey(name, id);
}
private static SolutionAcceptor getAcceptor(HierarchicalConfiguration strategyConfig, VehicleRoutingProblem vrp, Set<PrioritizedVRAListener> algorithmListeners, TypedMap typedMap, int solutionMemory) {
String acceptorName = strategyConfig.getString("acceptor[@name]");
if(acceptorName == null) throw new IllegalStateException("no solution acceptor is defined");
String acceptorId = strategyConfig.getString("acceptor[@id]");
if(acceptorId == null) acceptorId = "noId";
AcceptorKey acceptorKey = new AcceptorKey(makeKey(acceptorName,acceptorId));
SolutionAcceptor definedAcceptor = typedMap.get(acceptorKey);
if(definedAcceptor != null) return definedAcceptor;
if(acceptorName.equals("acceptNewRemoveWorst")){
GreedyAcceptance acceptor = new GreedyAcceptance(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("acceptNewRemoveFirst")){
AcceptNewRemoveFirst acceptor = new AcceptNewRemoveFirst(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("greedyAcceptance")){
GreedyAcceptance acceptor = new GreedyAcceptance(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("greedyAcceptance_minVehFirst")){
GreedyAcceptance_minVehFirst acceptor = new GreedyAcceptance_minVehFirst(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("schrimpfAcceptance")){
String nuWarmupIterations = strategyConfig.getString("acceptor.warmup");
double alpha = strategyConfig.getDouble("acceptor.alpha");
SchrimpfAcceptance schrimpf = new SchrimpfAcceptance(solutionMemory, alpha);
if(nuWarmupIterations!=null){
SchrimpfInitialThresholdGenerator iniThresholdGenerator = new SchrimpfInitialThresholdGenerator(schrimpf, Integer.parseInt(nuWarmupIterations));
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, iniThresholdGenerator));
}
else{
double threshold = strategyConfig.getDouble("acceptor.initialThreshold");
schrimpf.setInitialThreshold(threshold);
}
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, schrimpf));
typedMap.put(acceptorKey, schrimpf);
return schrimpf;
}
if(acceptorName.equals("experimentalSchrimpfAcceptance")){
int iterOfSchrimpf = strategyConfig.getInt("acceptor.warmup");
double alpha = strategyConfig.getDouble("acceptor.alpha");
ExperimentalSchrimpfAcceptance schrimpf = new ExperimentalSchrimpfAcceptance(solutionMemory, alpha, iterOfSchrimpf);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, schrimpf));
typedMap.put(acceptorKey, schrimpf);
return schrimpf;
}
else{
throw new IllegalStateException("solution acceptor " + acceptorName + " is not known");
}
}
private static SearchStrategyModule buildModule(HierarchicalConfiguration moduleConfig, final VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager,
final StateManager routeStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses, ExecutorService executorService, int nuOfThreads, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
String moduleName = moduleConfig.getString("[@name]");
if(moduleName == null) throw new IllegalStateException("module(-name) is missing.");
String moduleId = moduleConfig.getString("[@id]");
if(moduleId == null) moduleId = "noId";
ModKey modKey = makeKey(moduleName,moduleId);
StrategyModuleKey strategyModuleKey = new StrategyModuleKey(modKey);
SearchStrategyModule definedModule = definedClasses.get(strategyModuleKey);
if(definedModule != null) return definedModule;
if(moduleName.equals("ruin_and_recreate")){
String ruin_name = moduleConfig.getString("ruin[@name]");
if(ruin_name == null) throw new IllegalStateException("module.ruin[@name] is missing.");
String ruin_id = moduleConfig.getString("ruin[@id]");
if(ruin_id == null) ruin_id = "noId";
String shareToRuinString = moduleConfig.getString("ruin.share");
if(shareToRuinString == null) throw new IllegalStateException("module.ruin.share is missing.");
double shareToRuin = Double.valueOf(shareToRuinString);
final RuinStrategy ruin;
ModKey ruinKey = makeKey(ruin_name,ruin_id);
if(ruin_name.equals("randomRuin")){
ruin = getRandomRuin(vrp, routeStates, definedClasses, ruinKey, shareToRuin);
}
else if(ruin_name.equals("radialRuin")){
JobDistance jobDistance = new AvgServiceAndShipmentDistance(vrp.getTransportCosts());
ruin = getRadialRuin(vrp, routeStates, definedClasses, ruinKey, shareToRuin, jobDistance);
}
else throw new IllegalStateException("ruin[@name] " + ruin_name + " is not known. Use either randomRuin or radialRuin.");
String insertionName = moduleConfig.getString("insertion[@name]");
if(insertionName == null) throw new IllegalStateException("module.insertion[@name] is missing. set it to \"regretInsertion\" or \"bestInsertion\"");
String insertionId = moduleConfig.getString("insertion[@id]");
if(insertionId == null) insertionId = "noId";
ModKey insertionKey = makeKey(insertionName,insertionId);
InsertionStrategyKey insertionStrategyKey = new InsertionStrategyKey(insertionKey);
InsertionStrategy insertion = definedClasses.get(insertionStrategyKey);
if(insertion == null){
List<HierarchicalConfiguration> insertionConfigs = moduleConfig.configurationsAt("insertion");
if(insertionConfigs.size() != 1) throw new IllegalStateException("this should be 1");
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, routeStates, prioListeners, executorService, nuOfThreads, constraintManager, addDefaultCostCalculators);
algorithmListeners.addAll(prioListeners);
}
final InsertionStrategy final_insertion = insertion;
RuinAndRecreateModule rrModule = new RuinAndRecreateModule("ruin_and_recreate", final_insertion, ruin);
return rrModule;
}
throw new NullPointerException("no module found with moduleName=" + moduleName +
"\n\tcheck config whether the correct names are used" +
"\n\tcurrently there are following modules available: " +
"\n\tbestInsertion" +
"\n\trandomRuin" +
"\n\tradialRuin");
}
private static RuinStrategy getRadialRuin(final VehicleRoutingProblem vrp, final StateManager routeStates, TypedMap definedClasses, ModKey modKey, double shareToRuin, JobDistance jobDistance) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = new RadialRuinStrategyFactory(shareToRuin, jobDistance).createStrategy(vrp);
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static RuinStrategy getRandomRuin(final VehicleRoutingProblem vrp, final StateManager routeStates, TypedMap definedClasses, ModKey modKey, double shareToRuin) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = new RandomRuinStrategyFactory(shareToRuin).createStrategy(vrp);
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static InsertionStrategy createInsertionStrategy(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp,VehicleFleetManager vehicleFleetManager, StateManager routeStates, List<PrioritizedVRAListener> algorithmListeners, ExecutorService executorService, int nuOfThreads, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
return InsertionFactory.createInsertion(vrp, moduleConfig, vehicleFleetManager, routeStates, algorithmListeners, executorService, nuOfThreads, constraintManager, addDefaultCostCalculators);
}
}