1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00
This commit is contained in:
Stefan Schroeder 2013-06-04 10:25:47 +02:00
commit 3581d6e097
435 changed files with 46952 additions and 0 deletions

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.log4j.Logger;
import basics.Job;
import basics.algo.InsertionEndsListener;
import basics.algo.InsertionListener;
import basics.algo.InsertionStartsListener;
import basics.algo.JobInsertedListener;
import basics.route.VehicleRoute;
abstract class AbstractInsertionStrategy implements InsertionStrategy{
private static Logger log = Logger.getLogger(AbstractInsertionStrategy.class);
private Collection<InsertionListener> listener = new ArrayList<InsertionListener>();
public abstract RouteAlgorithm getRouteAlgorithm();
public void informJobInserted(int nOfJobs2Recreate, Job insertedJob, VehicleRoute insertedIn){
for(InsertionListener l : listener){
if(l instanceof JobInsertedListener){
((JobInsertedListener)l).informJobInserted(nOfJobs2Recreate, insertedJob, insertedIn);
}
}
}
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route){
for(InsertionListener l : listener){
if(l instanceof BeforeJobInsertionListener){
((BeforeJobInsertionListener)l).informBeforeJobInsertion(job, data, route);
}
}
}
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate){
for(InsertionListener l : listener){
if(l instanceof InsertionStartsListener){
((InsertionStartsListener)l).informInsertionStarts(vehicleRoutes,nOfJobs2Recreate);
}
}
}
public void informInsertionEndsListeners(Collection<VehicleRoute> vehicleRoutes) {
for(InsertionListener l : listener){
if(l instanceof InsertionEndsListener){
((InsertionEndsListener)l).informInsertionEnds(vehicleRoutes);
}
}
}
public Collection<InsertionListener> getListener() {
return Collections.unmodifiableCollection(listener);
}
public void addListener(InsertionListener l){
log.info("add insertion-listener " + l);
listener.add(l);
}
public void addAllListener(List<InsertionListener> list) {
for(InsertionListener l : list) addListener(l);
}
}

View file

@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Iterator;
import java.util.List;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.TourActivity;
import basics.route.Vehicle;
final class AuxilliaryCostCalculator {
private final VehicleRoutingTransportCosts routingCosts;
private final VehicleRoutingActivityCosts activityCosts;
public AuxilliaryCostCalculator(final VehicleRoutingTransportCosts routingCosts, final VehicleRoutingActivityCosts costFunction) {
super();
this.routingCosts = routingCosts;
this.activityCosts = costFunction;
}
/**
*
* @param path
* @param depTime
* @param driver
* @param vehicle
* @return
*/
public double costOfPath(final List<TourActivity> path, final double depTime, final Driver driver, final Vehicle vehicle){
if(path.isEmpty()){
return 0.0;
}
double cost = 0.0;
Iterator<TourActivity> actIter = path.iterator();
TourActivity prevAct = actIter.next();
double startCost = 0.0;
cost += startCost;
double departureTimePrevAct = depTime;
while(actIter.hasNext()){
TourActivity act = actIter.next();
double transportCost = routingCosts.getTransportCost(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
double transportTime = routingCosts.getTransportTime(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
cost += transportCost;
double actStartTime = departureTimePrevAct + transportTime;
double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
double actEndTime = earliestOperationStartTime + act.getOperationTime();
departureTimePrevAct = actEndTime;
cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
prevAct = act;
}
return cost;
}
public double costOfPath(String startLocationId, final double startTime, final List<TourActivity> path, String endLocationId, final Driver driver, final Vehicle vehicle){
if(path.isEmpty()){
return 0.0;
}
double cost = 0.0;
// Iterator<TourActivity> actIter = path.iterator();
String prevActLocation = startLocationId;
// TourActivity prevAct = actIter.next();
double startCost = 0.0;
cost += startCost;
double departureTimePrevAct = startTime;
for(TourActivity act : path){
// TourActivity act = actIter.next();
double transportCost = routingCosts.getTransportCost(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
double transportTime = routingCosts.getTransportTime(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
cost += transportCost;
double actStartTime = departureTimePrevAct + transportTime;
double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
double actEndTime = earliestOperationStartTime + act.getOperationTime();
departureTimePrevAct = actEndTime;
cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
prevActLocation = act.getLocationId();
}
/*
*!!! ENDLOCATION
=> Start u. End können primitiv sein.
*/
return cost;
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.algo.InsertionListener;
import basics.route.VehicleRoute;
interface BeforeJobInsertionListener extends InsertionListener{
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route);
}

View file

@ -0,0 +1,160 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.VehicleRoute;
/**
* Simplest recreation strategy. All removed customers are inserted where
* insertion costs are minimal. I.e. each tour-agent is asked for minimal
* marginal insertion costs. The tour-agent offering the lowest marginal
* insertion costs gets the customer/shipment.
*
* @author stefan schroeder
*
*/
final class BestInsertion extends AbstractInsertionStrategy{
public static BestInsertion newInstance(RouteAlgorithm routeAlgorithm){
return new BestInsertion(routeAlgorithm);
}
private static Logger logger = Logger.getLogger(BestInsertion.class);
private Random random = RandomNumberGeneration.getRandom();
private RouteAlgorithm routeAlgorithm;
private Map<String,VehicleRoute> experimentalPreferredRoute = new HashMap<String, VehicleRoute>();
public void setExperimentalPreferredRoute(Map<String, VehicleRoute> experimentalPreferredRoute) {
this.experimentalPreferredRoute = experimentalPreferredRoute;
}
private boolean allowUnassignedJobs = false;
private boolean fixRouteSet = false;
private boolean minVehiclesFirst = false;
public void setFixRouteSet(boolean fixRouteSet) {
this.fixRouteSet = fixRouteSet;
}
public void setRandom(Random random) {
this.random = random;
}
public BestInsertion(RouteAlgorithm routeAlgorithm) {
super();
this.routeAlgorithm = routeAlgorithm;
logger.info("initialise " + this);
}
public RouteAlgorithm getRouteAlgorithm(){
return routeAlgorithm;
}
@Override
public String toString() {
return "[name=bestInsertion]";
}
@Override
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
informInsertionStarts(vehicleRoutes,unassignedJobs.size());
int inserted = 0;
List<String> reasons = new ArrayList<String>();
for(Job unassignedJob : unassignedJobList){
VehicleRoute insertIn = null;
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
for(VehicleRoute vehicleRoute : vehicleRoutes){
InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
if(iData instanceof NoInsertionFound) {
continue;
}
if(iData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(vehicleRoute,iData);
bestInsertionCost = iData.getInsertionCost();
}
}
if(!minVehiclesFirst){
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData newIData = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(newIData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(newRoute,newIData);
bestInsertionCost = newIData.getInsertionCost();
vehicleRoutes.add(newRoute);
}
}
if(bestInsertion != null){
informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
insertIn = bestInsertion.getRoute();
// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
}
else {
if(fixRouteSet){
if(allowUnassignedJobs) logger.warn("cannot insert job yet " + unassignedJob);
else throw new IllegalStateException("given the vehicles, could not insert job\n");
}
else{
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound){
if(allowUnassignedJobs){
logger.warn("cannot insert job yet " + unassignedJob);
}
else {
for(String s : reasons){
System.out.println("reason="+s);
}
throw new IllegalStateException("given the vehicles, could not insert job\n" +
"\t" + unassignedJob + "\n\t");
}
}
else{
insertIn = newRoute;
informBeforeJobInsertion(unassignedJob,bestI,newRoute);
routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
vehicleRoutes.add(newRoute);
}
}
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
}
informInsertionEndsListeners(vehicleRoutes);
}
}

View file

@ -0,0 +1,191 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.VehicleRoute;
/**
* Simplest recreation strategy. All removed customers are inserted where
* insertion costs are minimal. I.e. each tour-agent is asked for minimal
* marginal insertion costs. The tour-agent offering the lowest marginal
* insertion costs gets the customer/shipment.
*
* @author stefan schroeder
*
*/
final class BestInsertionConcurrent extends AbstractInsertionStrategy{
static class Batch {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
}
private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class);
private Random random = RandomNumberGeneration.getRandom();
private RouteAlgorithm routeAlgorithm;
// private ExecutorService executor;
private int nuOfBatches;
private ExecutorCompletionService<Insertion> completionService;
public void setRandom(Random random) {
this.random = random;
}
public BestInsertionConcurrent(RouteAlgorithm routeAlgorithm, ExecutorService executor, int nuOfThreads) {
super();
this.routeAlgorithm = routeAlgorithm;
// this.executor = executor;
logger.info("initialise " + this);
this.nuOfBatches = nuOfThreads;
completionService = new ExecutorCompletionService<Insertion>(executor);
}
@Override
public String toString() {
return "[name=concurrentBestInsertion]";
}
@Override
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
informInsertionStarts(vehicleRoutes,unassignedJobs.size());
int inserted = 0;
for(final Job unassignedJob : unassignedJobList){
VehicleRoute insertIn = null;
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
List<Batch> batches = distributeRoutes(vehicleRoutes,nuOfBatches);
for(final Batch batch : batches){
completionService.submit(new Callable<Insertion>() {
@Override
public Insertion call() throws Exception {
return getBestInsertion(batch,unassignedJob);
}
});
}
try{
for(int i=0;i<batches.size();i++){
Future<Insertion> futureIData = completionService.take();
Insertion insertion = futureIData.get();
if(insertion == null) continue;
if(insertion.getInsertionData().getInsertionCost() < bestInsertionCost){
bestInsertion = insertion;
bestInsertionCost = insertion.getInsertionData().getInsertionCost();
}
}
}
catch(InterruptedException e){
Thread.currentThread().interrupt();
}
catch (ExecutionException e) {
e.printStackTrace();
logger.error(e.getCause().toString());
System.exit(1);
}
if(bestInsertion != null){
informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
insertIn = bestInsertion.getRoute();
// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
}
else {
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound)
throw new IllegalStateException("given the vehicles, could not create a valid solution.\n\tthe reason might be" +
" inappropriate vehicle capacity.\n\tthe job that does not fit in any vehicle anymore is \n\t" + unassignedJob);
insertIn = newRoute;
informBeforeJobInsertion(unassignedJob,bestI,newRoute);
routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
vehicleRoutes.add(newRoute);
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
}
}
private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
for(VehicleRoute vehicleRoute : batch.routes){
InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
if(iData instanceof NoInsertionFound) continue;
if(iData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(vehicleRoute,iData);
bestInsertionCost = iData.getInsertionCost();
}
}
return bestInsertion;
}
private List<Batch> distributeRoutes(Collection<VehicleRoute> vehicleRoutes, int nuOfBatches) {
List<Batch> batches = new ArrayList<Batch>();
for(int i=0;i<nuOfBatches;i++) batches.add(new Batch());
if(vehicleRoutes.size()<nuOfBatches){
int nOfNewRoutes = nuOfBatches-vehicleRoutes.size();
for(int i=0;i<nOfNewRoutes;i++){
vehicleRoutes.add(VehicleRoute.emptyRoute());
}
}
int count = 0;
for(VehicleRoute route : vehicleRoutes){
if(count == nuOfBatches) count=0;
batches.get(count).routes.add(route);
count++;
}
return batches;
}
@Override
public RouteAlgorithm getRouteAlgorithm() {
return routeAlgorithm;
}
}

View file

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.Start;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
final class CalculatesActivityInsertionWithHardTimeWindows {
private static Logger logger = Logger.getLogger(CalculatesActivityInsertionWithHardTimeWindows.class);
private RouteStates routeStates;
private VehicleRoutingTransportCosts routingCosts;
private VehicleRoutingActivityCosts activityCosts;
public CalculatesActivityInsertionWithHardTimeWindows(RouteStates activityStates, VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts){
this.routeStates = activityStates;
this.routingCosts = routingCosts;
this.activityCosts = activityCosts;
logger.info("initialise " + this);
}
public double calculate(VehicleRoute vehicleRoute, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, Driver driver, Vehicle vehicle) {
boolean prevIsStart = false;
if(prevAct instanceof Start){
prevIsStart = true;
}
double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double newAct_arrTime = prevAct.getEndTime() + tp_time_prevAct_newAct;
double newAct_operationStartTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime());
double newAct_endTime = newAct_operationStartTime + newAct.getOperationTime();
double act_costs_newAct = activityCosts.getActivityCost(newAct, newAct_arrTime, driver, vehicle);
double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, driver, vehicle);
double activityInsertionCosts;
double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct + act_costs_newAct + act_costs_nextAct;
if(nextAct_arrTime > getLatestOperationStart(nextAct)){
activityInsertionCosts = Double.MAX_VALUE;
}
else if(vehicleRoute.isEmpty()){
activityInsertionCosts = totalCosts;
}
else{
double oldCostOfPrevAct;
if(prevIsStart) oldCostOfPrevAct = 0.0;
else oldCostOfPrevAct = state(prevAct).getCurrentCost();
double oldCostOfNextAct;
if(nextAct instanceof End) oldCostOfNextAct = routeStates.getRouteState(vehicleRoute).getCosts();
else oldCostOfNextAct = state(nextAct).getCurrentCost();
activityInsertionCosts = (totalCosts) - (oldCostOfNextAct-oldCostOfPrevAct);
}
return activityInsertionCosts;
}
private ActivityState state(TourActivity act) {
return routeStates.getState(act);
}
private double getLatestOperationStart(TourActivity act) {
if(state(act) != null){
return state(act).getLatestOperationStart();
}
return act.getTheoreticalLatestOperationStartTime();
}
@Override
public String toString() {
return "[name=calculatesHardTimeWindowActivityInsertion]";
}
}

View file

@ -0,0 +1,209 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import util.Neighborhood;
import algorithms.RouteStates.ActivityState;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.ServiceActivity;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class CalculatesServiceInsertion implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertion.class);
private RouteStates routeStates;
private VehicleRoutingTransportCosts routingCosts;
private VehicleRoutingActivityCosts activityCosts;
private Start start;
private End end;
private Neighborhood neighborhood = new Neighborhood() {
@Override
public boolean areNeighbors(String location1, String location2) {
return true;
}
};
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
logger.info("initialise neighborhood " + neighborhood);
}
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
public CalculatesServiceInsertion(VehicleRoutingTransportCosts vehicleRoutingTransportCosts, VehicleRoutingActivityCosts vehicleRoutingActivityCosts) {
super();
this.routingCosts = vehicleRoutingTransportCosts;
this.activityCosts = vehicleRoutingActivityCosts;
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertion]";
}
/**
* Calculates the marginal cost of inserting job i locally. This is based on the
* assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1.
*
*/
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) {
if(jobToInsert == null) throw new IllegalStateException("jobToInsert is missing.");
if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("newVehicle is missing.");
TourActivities tour = currentRoute.getTourActivities();
double bestCost = bestKnownCosts;
Service service = (Service)jobToInsert;
if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
return InsertionData.noInsertionFound();
}
int insertionIndex = InsertionData.NO_INDEX;
TourActivity deliveryAct2Insert = ServiceActivity.newInstance(service);
// TourActivity deliveryAct2Insert = actStates.getActivity(service, true);
initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
TourActivity prevAct = start;
double prevCostInOriginalTour = 0.0;
int actIndex = 0;
for(TourActivity nextAct : tour.getActivities()){
double nextCostInOriginalTour = state(nextAct).getCurrentCost();
if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, nextCostInOriginalTour - prevCostInOriginalTour);
if(mc < bestCost){
bestCost = mc;
insertionIndex = actIndex;
}
}
prevCostInOriginalTour = nextCostInOriginalTour;
prevAct = nextAct;
actIndex++;
}
End nextAct = end;
if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, routeStates.getRouteState(currentRoute).getCosts() - prevCostInOriginalTour);
if(mc < bestCost){
bestCost = mc;
insertionIndex = actIndex;
}
}
if(insertionIndex == InsertionData.NO_INDEX) {
return InsertionData.noInsertionFound();
}
InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
private void initialiseStartAndEnd(final Vehicle newVehicle,
double newVehicleDepartureTime) {
if(start == null){
start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
else{
start.setLocationId(newVehicle.getLocationId());
start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
if(end == null){
end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
}
else{
end.setLocationId(newVehicle.getLocationId());
end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
}
}
public double calculate(TourActivities tour, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, Driver driver, Vehicle vehicle, double bestKnownCosts, double costWithoutNewJob) {
double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double newAct_arrTime = prevAct.getEndTime() + tp_time_prevAct_newAct;
double newAct_operationStartTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime());
double newAct_endTime = newAct_operationStartTime + newAct.getOperationTime();
double act_costs_newAct = activityCosts.getActivityCost(newAct, newAct_arrTime, driver, vehicle);
if((tp_costs_prevAct_newAct + act_costs_newAct - costWithoutNewJob) > bestKnownCosts){
return Double.MAX_VALUE;
}
double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, driver, vehicle);
double activityInsertionCosts;
double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct + act_costs_newAct + act_costs_nextAct;
if(totalCosts - costWithoutNewJob > bestKnownCosts){
activityInsertionCosts = Double.MAX_VALUE;
}
if(nextAct_arrTime > getLatestOperationStart(nextAct)){
activityInsertionCosts = Double.MAX_VALUE;
}
else{
activityInsertionCosts = totalCosts - costWithoutNewJob;
}
return activityInsertionCosts;
}
private double getLatestOperationStart(TourActivity act) {
if(state(act) != null){
return state(act).getLatestOperationStart();
}
return act.getTheoreticalLatestOperationStartTime();
}
}

View file

@ -0,0 +1,116 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
import basics.route.VehicleRoute;
final class CalculatesServiceInsertionConsideringFixCost implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionConsideringFixCost.class);
private final JobInsertionCalculator standardServiceInsertion;
private double weight_deltaFixCost = 0.5;
private double solution_completeness_ratio = 0.5;
private RouteStates routeStates;
public CalculatesServiceInsertionConsideringFixCost(final JobInsertionCalculator standardInsertionCalculator, RouteStates routeStates) {
super();
this.standardServiceInsertion = standardInsertionCalculator;
this.routeStates = routeStates;
logger.info("inialise " + this);
}
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownPrice) {
double relFixCost = getDeltaRelativeFixCost(currentRoute, newVehicle, jobToInsert);
double absFixCost = getDeltaAbsoluteFixCost(currentRoute, newVehicle, jobToInsert);
double deltaFixCost = (1-solution_completeness_ratio)*relFixCost + solution_completeness_ratio*absFixCost;
double fixcost_contribution = weight_deltaFixCost*solution_completeness_ratio*deltaFixCost;
if(fixcost_contribution > bestKnownPrice){
return InsertionData.noInsertionFound();
}
InsertionData iData = standardServiceInsertion.calculate(currentRoute, jobToInsert, newVehicle, newVehicleDepartureTime, newDriver, bestKnownPrice);
if(iData instanceof NoInsertionFound){
return iData;
}
double totalInsertionCost = iData.getInsertionCost() + fixcost_contribution;
InsertionData insertionData = new InsertionData(totalInsertionCost, iData.getPickupInsertionIndex(), iData.getDeliveryInsertionIndex(), newVehicle, newDriver);
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
public void setWeightOfFixCost(double weight){
weight_deltaFixCost = weight;
logger.info("set weightOfFixCostSaving to " + weight);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionConsideringFixCost][weightOfFixedCostSavings="+weight_deltaFixCost+"]";
}
public void setSolutionCompletenessRatio(double ratio){
solution_completeness_ratio = ratio;
}
private double getDeltaAbsoluteFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
double load = routeStates.getRouteState(route).getLoad() + job.getCapacityDemand();
double currentFix = 0.0;
if(route.getVehicle() != null){
if(!(route.getVehicle() instanceof NoVehicle)){
currentFix += route.getVehicle().getType().vehicleCostParams.fix;
}
}
if(newVehicle.getCapacity() < load){
return Double.MAX_VALUE;
}
return newVehicle.getType().vehicleCostParams.fix - currentFix;
}
private double getDeltaRelativeFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
int currentLoad = routeStates.getRouteState(route).getLoad();
double load = currentLoad + job.getCapacityDemand();
double currentRelFix = 0.0;
if(route.getVehicle() != null){
if(!(route.getVehicle() instanceof NoVehicle)){
currentRelFix += route.getVehicle().getType().vehicleCostParams.fix*currentLoad/route.getVehicle().getCapacity();
}
}
if(newVehicle.getCapacity() < load){
return Double.MAX_VALUE;
}
double relativeFixCost = newVehicle.getType().vehicleCostParams.fix*(load/newVehicle.getCapacity()) - currentRelFix;
return relativeFixCost;
}
}

View file

@ -0,0 +1,356 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import org.apache.log4j.Logger;
import util.Neighborhood;
import algorithms.RouteStates.ActivityState;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class CalculatesServiceInsertionOnRouteLevel implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionOnRouteLevel.class);
private final VehicleRoutingTransportCosts transportCosts;
private final VehicleRoutingActivityCosts activityCosts;
private AuxilliaryCostCalculator auxilliaryPathCostCalculator;
private RouteStates routeStates;
private int nuOfActsForwardLooking = 0;
private int memorySize = 2;
private Start start;
private End end;
private Neighborhood neighborhood = new Neighborhood() {
@Override
public boolean areNeighbors(String location1, String location2) {
return true;
}
};
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
logger.info("initialise neighborhood " + neighborhood);
}
public void setMemorySize(int memorySize) {
this.memorySize = memorySize;
logger.info("set [solutionMemory="+memorySize+"]");
}
public CalculatesServiceInsertionOnRouteLevel(VehicleRoutingTransportCosts vehicleRoutingCosts, VehicleRoutingActivityCosts costFunc) {
super();
this.transportCosts = vehicleRoutingCosts;
this.activityCosts = costFunc;
auxilliaryPathCostCalculator = new AuxilliaryCostCalculator(transportCosts, activityCosts);
logger.info("initialise " + this);
}
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
void setNuOfActsForwardLooking(int nOfActsForwardLooking) {
this.nuOfActsForwardLooking = nOfActsForwardLooking;
logger.info("set [forwardLooking="+nOfActsForwardLooking+"]");
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionOnRouteLevel][solutionMemory="+memorySize+"][forwardLooking="+nuOfActsForwardLooking+"]";
}
/**
* Calculates the insertion costs of job i on route level (which is based on the assumption that inserting job i does not only
* have local effects but affects the entire route).
* Calculation is conducted by two steps. In the first step, promising insertion positions are identified by appromiximating their
* marginal insertion cost. In the second step, marginal cost of the best M positions are calculated exactly.
*
*
*/
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double best_known_insertion_costs) {
if(jobToInsert == null) throw new IllegalStateException("job is null. cannot calculate the insertion of a null-job.");
if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("no vehicle given. set para vehicle!");
/**
* map that memorizes the costs with newVehicle, which is a cost-snapshot at tour-activities.
*/
Map<TourActivity,Double> activity2costWithNewVehicle = new HashMap<TourActivity,Double>();
/**
* priority queue that stores insertion-data by insertion-costs in ascending order.
*/
PriorityQueue<InsertionData> bestInsertionsQueue = new PriorityQueue<InsertionData>(Math.max(2, currentRoute.getTourActivities().getActivities().size()), getComparator());
TourActivities tour = currentRoute.getTourActivities();
double best_insertion_costs = best_known_insertion_costs;
Service service = (Service)jobToInsert;
/**
* pre-check whether vehicle-capacity of new vehicle is sufficient to load service.
*/
if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
return InsertionData.noInsertionFound();
}
/**
* some inis
*/
TourActivity serviceAct2Insert = routeStates.getActivity(service, true);
int best_insertion_index = InsertionData.NO_INDEX;
initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
TourActivity prevAct = start;
int actIndex = 0;
double sumOf_prevCosts_newVehicle = 0.0;
double prevActDepTime_newVehicle = start.getEndTime();
/**
* inserting serviceAct2Insert in route r={0,1,...,i-1,i,j,j+1,...,n(r),n(r)+1}
* i=prevAct
* j=nextAct
* k=serviceAct2Insert
*/
for(TourActivity nextAct : tour.getActivities()){
if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
/**
* builds a path on this route forwardPath={i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking}
*/
List<TourActivity> path = new ArrayList<TourActivity>();
path.add(prevAct); path.add(serviceAct2Insert); path.add(nextAct);
if(nuOfActsForwardLooking > 0){ path.addAll(getForwardLookingPath(currentRoute,actIndex)); }
/**
* calculates the path costs with new vehicle, c(forwardPath,newVehicle).
*/
double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
/**
* insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
*/
double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
/**
* memorize it in insertion-queue
*/
if(insertion_cost_approximation < best_known_insertion_costs){
bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation, InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
}
}
/**
* calculate transport and activity costs with new vehicle (without inserting k)
*/
double transportCost_prevAct_nextAct_newVehicle = transportCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
double transportTime_prevAct_nextAct_newVehicle = transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
double arrTime_nextAct_newVehicle = prevActDepTime_newVehicle + transportTime_prevAct_nextAct_newVehicle;
double activityCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct_newVehicle, newDriver, newVehicle);
/**
* memorize transport and activity costs with new vehicle without inserting k
*/
sumOf_prevCosts_newVehicle += transportCost_prevAct_nextAct_newVehicle + activityCost_nextAct;
activity2costWithNewVehicle.put(nextAct, sumOf_prevCosts_newVehicle);
/**
* departure time at nextAct with new vehicle
*/
double depTime_nextAct_newVehicle = Math.max(arrTime_nextAct_newVehicle, nextAct.getTheoreticalEarliestOperationStartTime()) + nextAct.getOperationTime();
/**
* set previous to next
*/
prevAct = nextAct;
prevActDepTime_newVehicle = depTime_nextAct_newVehicle;
actIndex++;
}
End nextAct = end;
if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
/**
* calculates the path costs with new vehicle, c(forwardPath,newVehicle).
*/
List<TourActivity> path = Arrays.asList(prevAct,serviceAct2Insert,end);
double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
/**
* insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
*/
double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
/**
* memorize it in insertion-queue
*/
if(insertion_cost_approximation < best_known_insertion_costs){
bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation,InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
}
}
/**
* the above calculations approximate insertion costs. now calculate the exact insertion costs for the most promising (according to the approximation)
* insertion positions.
*
*/
for(int i=0;i<memorySize;i++){
InsertionData data = bestInsertionsQueue.poll();
if(data == null){
continue;
}
/**
* build tour with new activity.
*/
List<TourActivity> wholeTour = new ArrayList<TourActivity>();
wholeTour.add(start);
wholeTour.addAll(currentRoute.getTourActivities().getActivities());
wholeTour.add(end);
wholeTour.add(data.getDeliveryInsertionIndex()+1, serviceAct2Insert);
/**
* compute cost-diff of tour with and without new activity --> insertion_costs
*/
double insertion_costs = auxilliaryPathCostCalculator.costOfPath(wholeTour, start.getEndTime(), newDriver, newVehicle) - routeStates.getRouteState(currentRoute).getCosts();
/**
* if better than best known, make it the best known
*/
if(insertion_costs < best_insertion_costs){
best_insertion_index = data.getDeliveryInsertionIndex();
best_insertion_costs = insertion_costs;
}
}
if(best_insertion_index == InsertionData.NO_INDEX) return InsertionData.noInsertionFound();
return new InsertionData(best_insertion_costs, InsertionData.NO_INDEX, best_insertion_index, newVehicle, newDriver);
}
/**
* initialize start and end of tour.
*
* @param newVehicle
* @param newVehicleDepartureTime
*/
private void initialiseStartAndEnd(final Vehicle newVehicle, double newVehicleDepartureTime) {
if(start == null){
start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
else{
start.setLocationId(newVehicle.getLocationId());
start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
if(end == null){
end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
}
else{
end.setLocationId(newVehicle.getLocationId());
end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
}
}
private double pathCost_oldVehicle(VehicleRoute vehicleRoute, List<TourActivity> path) {
TourActivity act = path.get(path.size()-1);
if(act instanceof End){
return routeStates.getRouteState(vehicleRoute).getCosts();
}
return state(act).getCurrentCost();
}
/**
* returns the path or the partial route r_partial = {j+1,j+2,...,j+nuOfActsForwardLooking}
*
* @param route
* @param actIndex
* @return
*/
private List<TourActivity> getForwardLookingPath(VehicleRoute route, int actIndex) {
List<TourActivity> forwardLookingPath = new ArrayList<TourActivity>();
int nuOfActsInPath = 0;
int index = actIndex + 1;
while(index < route.getTourActivities().getActivities().size() && nuOfActsInPath < nuOfActsForwardLooking){
forwardLookingPath.add(route.getTourActivities().getActivities().get(index));
index++;
nuOfActsInPath++;
}
if(nuOfActsInPath < nuOfActsForwardLooking){
forwardLookingPath.add(route.getEnd());
}
return forwardLookingPath;
}
/**
* creates a comparator to sort insertion-data in insertionQueue in ascending order according insertion costs.
* @return
*/
private Comparator<InsertionData> getComparator() {
return new Comparator<InsertionData>() {
@Override
public int compare(InsertionData o1, InsertionData o2) {
if(o1.getInsertionCost() < o2.getInsertionCost()){
return -1;
}
else {
return 1;
}
}
};
}
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Logger;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCalculator{
private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class);
private JobInsertionCalculator jic;
private Random random = new Random();
private int nOfDepartureTimes = 3;
private double timeSlice = 900.0;
public CalculatesServiceInsertionWithTimeScheduling(JobInsertionCalculator jic, double timeSlice, int neighbors) {
super();
this.jic = jic;
this.timeSlice = timeSlice;
this.nOfDepartureTimes = neighbors;
log.info("initialise " + this);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionWithTimeScheduling][timeSlice="+timeSlice+"][#timeSlice="+nOfDepartureTimes+"]";
}
@Override
public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore) {
List<Double> vehicleDepartureTimes = new ArrayList<Double>();
double currentStart;
if(currentRoute.getStart() == null){
currentStart = newVehicleDepartureTime;
}
else currentStart = currentRoute.getStart().getEndTime();
vehicleDepartureTimes.add(currentStart);
double earliestDeparture = newVehicle.getEarliestDeparture();
double latestEnd = newVehicle.getLatestArrival();
for(int i=0;i<nOfDepartureTimes;i++){
double neighborStartTime_earlier = currentStart - (i+1)*timeSlice;
if(neighborStartTime_earlier > earliestDeparture) vehicleDepartureTimes.add(neighborStartTime_earlier);
double neighborStartTime_later = currentStart + (i+1)*timeSlice;
if(neighborStartTime_later < latestEnd) vehicleDepartureTimes.add(neighborStartTime_later);
}
InsertionData bestIData = null;
for(Double departureTime : vehicleDepartureTimes){
InsertionData iData = jic.calculate(currentRoute, jobToInsert, newVehicle, departureTime, newDriver, bestKnownScore);
if(bestIData == null) bestIData = iData;
else if(iData.getInsertionCost() < bestIData.getInsertionCost()){
iData.setVehicleDepartureTime(departureTime);
bestIData = iData;
}
}
// log.info(bestIData);
return bestIData;
}
}

View file

@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import algorithms.VehicleFleetManager.TypeKey;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
import basics.route.VehicleImpl.VehicleType;
import basics.route.VehicleRoute;
final class CalculatesVehTypeDepServiceInsertion implements JobInsertionCalculator{
private Logger logger = Logger.getLogger(CalculatesVehTypeDepServiceInsertion.class);
private final VehicleFleetManager fleetManager;
private final JobInsertionCalculator insertionCalculator;
public CalculatesVehTypeDepServiceInsertion(final VehicleFleetManager fleetManager, final JobInsertionCalculator jobInsertionCalc) {
this.fleetManager = fleetManager;
this.insertionCalculator = jobInsertionCalc;
logger.info("inialise " + this);
}
@Override
public String toString() {
return "[name=vehicleTypeDependentServiceInsertion]";
}
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle vehicle, double newVehicleDepartureTime, final Driver driver, final double bestKnownCost) {
Vehicle selectedVehicle = currentRoute.getVehicle();
Driver selectedDriver = currentRoute.getDriver();
InsertionData bestIData = InsertionData.noInsertionFound();
double bestKnownCost_ = bestKnownCost;
Collection<Vehicle> relevantVehicles = new ArrayList<Vehicle>();
if(!(selectedVehicle instanceof NoVehicle)) relevantVehicles.add(selectedVehicle);
for(TypeKey typeKey : fleetManager.getAvailableVehicleTypes()){
if(!(currentRoute.getVehicle() instanceof NoVehicle)){
TypeKey key = makeTypeKey(currentRoute.getVehicle().getType(),currentRoute.getVehicle().getLocationId());
if(typeKey.equals(key)){
continue;
}
}
relevantVehicles.add(fleetManager.getEmptyVehicle(typeKey));
}
for(Vehicle v : relevantVehicles){
double depTime = v.getEarliestDeparture();
InsertionData iData = insertionCalculator.calculate(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_);
if(iData instanceof NoInsertionFound) {
if(bestIData instanceof NoInsertionFound) bestIData = iData;
continue;
}
if(iData.getInsertionCost() < bestKnownCost_){
bestIData = iData;
bestKnownCost_ = iData.getInsertionCost();
}
}
return bestIData;
}
private TypeKey makeTypeKey(VehicleType type, String locationId) {
return new TypeKey(type,locationId);
}
}

View file

@ -0,0 +1,268 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.configuration.XMLConfiguration;
import util.NeighborhoodImpl;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.algo.InsertionListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
import basics.costs.VehicleRoutingActivityCosts;
class CalculatorBuilder {
private static class CalculatorPlusListeners {
private JobInsertionCalculator calculator;
public JobInsertionCalculator getCalculator() {
return calculator;
}
private List<PrioritizedVRAListener> algorithmListener = new ArrayList<PrioritizedVRAListener>();
private List<InsertionListener> insertionListener = new ArrayList<InsertionListener>();
public CalculatorPlusListeners(JobInsertionCalculator calculator) {
super();
this.calculator = calculator;
}
public List<PrioritizedVRAListener> getAlgorithmListener() {
return algorithmListener;
}
public List<InsertionListener> getInsertionListener() {
return insertionListener;
}
}
private List<InsertionListener> insertionListeners;
private List<PrioritizedVRAListener> algorithmListeners;
private VehicleRoutingProblem vrp;
private RouteStates activityStates;
private boolean local = true;
private int forwardLooking = 0;
private int memory = 1;
private boolean considerFixedCost = false;
private double weightOfFixedCost = 0;
private VehicleFleetManager fleetManager;
private boolean timeScheduling;
private double timeSlice;
private int neighbors;
/**
* Constructs the builder.
*
* <p>Some calculators require information from the overall algorithm or the higher-level insertion procedure. Thus listeners inform them.
* These listeners are cached in the according list and can thus be added when its time to add them.
*
* @param insertionListeners
* @param algorithmListeners
*/
public CalculatorBuilder(List<InsertionListener> insertionListeners, List<PrioritizedVRAListener> algorithmListeners) {
super();
this.insertionListeners = insertionListeners;
this.algorithmListeners = algorithmListeners;
}
/**
* Sets activityStates. MUST be set.
*
* @param activityStates
* @return
*/
public CalculatorBuilder setActivityStates(RouteStates activityStates){
this.activityStates = activityStates;
return this;
}
/**
* Sets routingProblem. MUST be set.
*
* @param vehicleRoutingProblem
* @return
*/
public CalculatorBuilder setVehicleRoutingProblem(VehicleRoutingProblem vehicleRoutingProblem){
this.vrp = vehicleRoutingProblem;
return this;
}
/**
* Sets fleetManager. MUST be set.
*
* @param fleetManager
* @return
*/
public CalculatorBuilder setVehicleFleetManager(VehicleFleetManager fleetManager){
this.fleetManager = fleetManager;
return this;
}
/**
* Sets a flag to build a calculator based on local calculations.
*
* <p>Insertion of a job and job-activity is evaluated based on the previous and next activity.
*/
public void setLocalLevel(){
local = true;
}
/**
* Sets a flag to build a calculator that evaluates job insertion on route-level.
*
* @param forwardLooking
* @param memory
*/
public void setRouteLevel(int forwardLooking, int memory){
local = false;
this.forwardLooking = forwardLooking;
this.memory = memory;
}
/**
* Sets a flag to consider also fixed-cost when evaluating the insertion of a job. The weight of the fixed-cost can be determined by setting
* weightofFixedCosts.
*
* @param weightOfFixedCosts
*/
public void considerFixedCosts(double weightOfFixedCosts){
considerFixedCost = true;
this.weightOfFixedCost = weightOfFixedCosts;
}
public void experimentalTimeScheduler(double timeSlice, int neighbors){
timeScheduling = true;
this.timeSlice = timeSlice;
this.neighbors = neighbors;
}
/**
* Builds the jobInsertionCalculator.
*
* @return jobInsertionCalculator.
* @throws IllegalStateException if vrp == null or activityStates == null or fleetManager == null.
*/
public JobInsertionCalculator build(){
if(vrp == null) throw new IllegalStateException("vehicle-routing-problem is null, but it must be set (this.setVehicleRoutingProblem(vrp))");
if(activityStates == null) throw new IllegalStateException("activity states is null, but is must be set (this.setActivityStates(states))");
if(fleetManager == null) throw new IllegalStateException("fleetManager is null, but it must be set (this.setVehicleFleetManager(fleetManager))");
JobInsertionCalculator baseCalculator = null;
CalculatorPlusListeners standardLocal = null;
if(local){
standardLocal = createStandardLocal(vrp, activityStates);
}
else{
standardLocal = createStandardRoute(vrp, activityStates,forwardLooking,memory);
}
baseCalculator = standardLocal.getCalculator();
addAlgorithmListeners(standardLocal.getAlgorithmListener());
addInsertionListeners(standardLocal.getInsertionListener());
if(considerFixedCost){
CalculatorPlusListeners withFixed = createCalculatorConsideringFixedCosts(vrp, baseCalculator, activityStates, weightOfFixedCost);
baseCalculator = withFixed.getCalculator();
addAlgorithmListeners(withFixed.getAlgorithmListener());
addInsertionListeners(withFixed.getInsertionListener());
}
if(timeScheduling){
baseCalculator = new CalculatesServiceInsertionWithTimeScheduling(baseCalculator,timeSlice,neighbors);
}
return createFinalInsertion(fleetManager, baseCalculator, activityStates);
}
private void addInsertionListeners(List<InsertionListener> list) {
for(InsertionListener iL : list){
insertionListeners.add(iL);
}
}
private void addAlgorithmListeners(List<PrioritizedVRAListener> list) {
for(PrioritizedVRAListener aL : list){
algorithmListeners.add(aL);
}
}
private CalculatorPlusListeners createStandardLocal(VehicleRoutingProblem vrp, RouteStates activityStates){
JobInsertionCalculator standardServiceInsertion = new CalculatesServiceInsertion(vrp.getTransportCosts(), vrp.getActivityCosts());
((CalculatesServiceInsertion) standardServiceInsertion).setActivityStates(activityStates);
((CalculatesServiceInsertion) standardServiceInsertion).setNeighborhood(vrp.getNeighborhood());
CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(standardServiceInsertion);
return calcPlusListeners;
}
private CalculatorPlusListeners createCalculatorConsideringFixedCosts(VehicleRoutingProblem vrp, JobInsertionCalculator baseCalculator, RouteStates activityStates, double weightOfFixedCosts){
final CalculatesServiceInsertionConsideringFixCost withFixCost = new CalculatesServiceInsertionConsideringFixCost(baseCalculator, activityStates);
withFixCost.setWeightOfFixCost(weightOfFixedCosts);
CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(withFixCost);
calcPlusListeners.getInsertionListener().add(new ConfigureFixCostCalculator(vrp, withFixCost));
return calcPlusListeners;
}
private CalculatorPlusListeners createStandardRoute(VehicleRoutingProblem vrp, RouteStates activityStates, int forwardLooking, int solutionMemory){
int after = forwardLooking;
JobInsertionCalculator jobInsertionCalculator = new CalculatesServiceInsertionOnRouteLevel(vrp.getTransportCosts(), vrp.getActivityCosts());
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNuOfActsForwardLooking(after);
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setMemorySize(solutionMemory);
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNeighborhood(vrp.getNeighborhood());
((CalculatesServiceInsertionOnRouteLevel) jobInsertionCalculator).setActivityStates(activityStates);
CalculatorPlusListeners calcPlusListener = new CalculatorPlusListeners(jobInsertionCalculator);
return calcPlusListener;
}
private JobInsertionCalculator createFinalInsertion(VehicleFleetManager fleetManager, JobInsertionCalculator baseCalc, RouteStates routeStates){
return new CalculatesVehTypeDepServiceInsertion(fleetManager, baseCalc);
}
}

View file

@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.algo.InsertionStartsListener;
import basics.algo.JobInsertedListener;
import basics.route.VehicleRoute;
final class ConfigureFixCostCalculator implements InsertionStartsListener, JobInsertedListener{
private static Logger log = Logger.getLogger(ConfigureFixCostCalculator.class);
VehicleRoutingProblem vrp;
CalculatesServiceInsertionConsideringFixCost calcConsideringFix;
public ConfigureFixCostCalculator(VehicleRoutingProblem vrp, CalculatesServiceInsertionConsideringFixCost calcConsideringFix) {
super();
this.vrp = vrp;
this.calcConsideringFix = calcConsideringFix;
}
@Override
public String toString() {
return "[name=configureFixCostCalculator]";
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> routes, int nOfJobs2Recreate) {
double completenessRatio = (1-((double)nOfJobs2Recreate/(double)vrp.getJobs().values().size()));
calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
// log.debug("initialise completenessRatio to " + completenessRatio);
}
@Override
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
double completenessRatio = (1-((double)nOfJobsStill2Recreate/(double)vrp.getJobs().values().size()));
calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
// log.debug("set completenessRatio to " + completenessRatio);
}
}

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
/* *********************************************************************** *
* project: org.matsim.*
* IniSolution.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2011 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */
package algorithms;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.route.DriverImpl;
import basics.route.TourActivities;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
final class CreateInitialSolution implements InitialSolutionFactory {
private static final Logger logger = Logger.getLogger(CreateInitialSolution.class);
private final AbstractInsertionStrategy insertion;
private boolean generateAsMuchAsRoutesAsVehiclesExist = false;
public void setGenerateAsMuchAsRoutesAsVehiclesExist(boolean generateAsMuchAsRoutesAsVehiclesExist) {
this.generateAsMuchAsRoutesAsVehiclesExist = generateAsMuchAsRoutesAsVehiclesExist;
}
public CreateInitialSolution(AbstractInsertionStrategy insertion) {
super();
this.insertion = insertion;
}
@Override
public VehicleRoutingProblemSolution createInitialSolution(final VehicleRoutingProblem vrp) {
logger.info("create initial solution.");
List<VehicleRoute> vehicleRoutes = new ArrayList<VehicleRoute>();
if(generateAsMuchAsRoutesAsVehiclesExist){
for(Vehicle vehicle : vrp.getVehicles()){
vehicleRoutes.add(VehicleRoute.newInstance(TourActivities.emptyTour(), DriverImpl.noDriver(), vehicle));
}
}
insertion.run(vehicleRoutes, getUnassignedJobs(vrp), Double.MAX_VALUE);
double totalCost = getTotalCost(vehicleRoutes);
logger.info("creation done");
return new VehicleRoutingProblemSolution(vehicleRoutes, totalCost);
}
private double getTotalCost(List<VehicleRoute> serviceProviders) {
double c = 0.0;
for(VehicleRoute a : serviceProviders){
c += a.getCost();
}
return c;
}
private List<Job> getUnassignedJobs(VehicleRoutingProblem vrp) {
List<Job> jobs = new ArrayList<Job>(vrp.getJobs().values());
return jobs;
}
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class FindCheaperVehicle implements InsertionStartsListener{
FindCheaperVehicleAlgo findCheaperVehicle;
public FindCheaperVehicle(FindCheaperVehicleAlgo findCheaperVehicle) {
super();
this.findCheaperVehicle = findCheaperVehicle;
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
List<VehicleRoute> newRoutes = new ArrayList<VehicleRoute>();
for(VehicleRoute route : vehicleRoutes){
if(route.isEmpty()) continue;
VehicleRoute cheaperRoute = findCheaperVehicle.runAndGetVehicleRoute(route);
newRoutes.add(cheaperRoute);
}
vehicleRoutes.clear();
vehicleRoutes.addAll(newRoutes);
}
@Override
public String toString() {
return "[name=findCheaperVehicle]";
}
}

View file

@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import algorithms.VehicleFleetManager.TypeKey;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class FindCheaperVehicleAlgo {
private static Logger log = Logger.getLogger(FindCheaperVehicleAlgo.class);
private VehicleFleetManager fleetManager;
private VehicleRouteUpdater tourStateCalculator;
private AuxilliaryCostCalculator auxilliaryCostCalculator;
private double weightFixCosts = 1.0;
private RouteStates states;
public void setWeightFixCosts(double weightFixCosts) {
this.weightFixCosts = weightFixCosts;
}
public void setStates(RouteStates states) {
this.states = states;
}
public FindCheaperVehicleAlgo(VehicleFleetManager fleetManager, VehicleRouteUpdater tourStateCalculator, AuxilliaryCostCalculator auxilliaryCostCalculator) {
super();
this.fleetManager = fleetManager;
this.tourStateCalculator = tourStateCalculator;
this.auxilliaryCostCalculator = auxilliaryCostCalculator;
}
public VehicleRoute runAndGetVehicleRoute(VehicleRoute vehicleRoute) {
if(vehicleRoute.getVehicle() instanceof NoVehicle){
return vehicleRoute;
}
if(vehicleRoute.getTourActivities() == null || vehicleRoute.getVehicle() == null){
return vehicleRoute;
}
Collection<TypeKey> availableVehicleTypes = fleetManager.getAvailableVehicleTypes(new TypeKey(vehicleRoute.getVehicle().getType(),vehicleRoute.getVehicle().getLocationId()));
double bestSaving = 0.0;
Vehicle bestVehicle = null;
List<TourActivity> path = new ArrayList<TourActivity>();
path.add(vehicleRoute.getStart());
path.addAll(vehicleRoute.getTourActivities().getActivities());
path.add(vehicleRoute.getEnd());
for(TypeKey vehicleType : availableVehicleTypes){
Vehicle vehicle = fleetManager.getEmptyVehicle(vehicleType);
if(vehicle.getType().typeId.equals(vehicleRoute.getVehicle().getType().typeId)){
continue;
}
if(states.getRouteState(vehicleRoute).getLoad() <= vehicle.getCapacity()){
double fixCostSaving = vehicleRoute.getVehicle().getType().vehicleCostParams.fix - vehicle.getType().vehicleCostParams.fix;
double departureTime = vehicleRoute.getStart().getEndTime();
double newCost = auxilliaryCostCalculator.costOfPath(path, departureTime, vehicleRoute.getDriver(), vehicle);
double varCostSaving = states.getRouteState(vehicleRoute).getCosts() - newCost;
double totalCostSaving = varCostSaving + weightFixCosts*fixCostSaving;
if(totalCostSaving > bestSaving){
bestSaving = totalCostSaving;
bestVehicle = vehicle;
}
}
}
if(bestVehicle != null){
try{
fleetManager.unlock(vehicleRoute.getVehicle());
fleetManager.lock(bestVehicle);
}
catch(IllegalStateException e){
throw new IllegalStateException(e);
}
TourActivities newTour = TourActivities.copyOf(vehicleRoute.getTourActivities());
tourStateCalculator.updateRoute(vehicleRoute);
return VehicleRoute.newInstance(newTour,vehicleRoute.getDriver(),bestVehicle);
}
return vehicleRoute;
}
}

View file

@ -0,0 +1,207 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.SearchStrategyModule;
import basics.algo.SearchStrategyModuleListener;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
import basics.route.TourActivity.JobActivity;
import util.RandomNumberGeneration;
final class GendreauPostOpt implements SearchStrategyModule{
private final static Logger log = Logger.getLogger(GendreauPostOpt.class);
private final static String NAME = "gendreauPostOpt";
private final RuinStrategy ruin;
private final VehicleRoutingProblem vrp;
private final AbstractInsertionStrategy insertionStrategy;
private final RouteAlgorithm routeAlgorithm;
private VehicleFleetManager fleetManager;
private Random random = RandomNumberGeneration.getRandom();
private int nOfIterations = 10;
private double shareOfJobsToRuin = 0.15;
public void setShareOfJobsToRuin(double shareOfJobsToRuin) {
this.shareOfJobsToRuin = shareOfJobsToRuin;
}
public GendreauPostOpt(VehicleRoutingProblem vrp, RuinStrategy ruin, AbstractInsertionStrategy insertionStrategy) {
super();
this.routeAlgorithm = insertionStrategy.getRouteAlgorithm();
this.ruin = ruin;
this.vrp = vrp;
this.insertionStrategy = insertionStrategy;
}
@Override
public String toString() {
return "[name=gendreauPostOpt][iterations="+nOfIterations+"][share2ruin="+shareOfJobsToRuin+"]";
}
public void setRandom(Random random) {
this.random = random;
}
public void setNuOfIterations(int nOfIterations) {
this.nOfIterations = nOfIterations;
}
public void setFleetManager(VehicleFleetManager vehicleFleetManager) {
this.fleetManager = vehicleFleetManager;
}
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
// log.info("run gendreau postopt");
VehicleRoutingProblemSolution bestSolution = vrpSolution;
int itersWithoutImprovement = 0;
for(int i=0;i<nOfIterations;i++){
List<VehicleRoute> copiedRoutes = copyRoutes(bestSolution.getRoutes());
iniFleet(copiedRoutes);
VehicleRoute route2split = pickRouteThatHasAtLeastTwoJobs(copiedRoutes);
if(route2split == null) continue;
List<Job> jobsInRoute = getJobs(route2split);
Set<Job> unassignedJobs = new HashSet<Job>();
unassignedJobs.addAll(jobsInRoute);
copiedRoutes.remove(route2split);
Collections.shuffle(jobsInRoute,random);
Job targetJob = jobsInRoute.get(0);
int nOfJobs2BeRemovedAdditionally = (int) (shareOfJobsToRuin*(double)vrp.getJobs().size());
Collection<Job> unassignedJobsList = ruin.ruin(copiedRoutes, targetJob, nOfJobs2BeRemovedAdditionally);
unassignedJobs.addAll(unassignedJobsList);
VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute();
copiedRoutes.add(emptyRoute1);
routeAlgorithm.insertJob(targetJob, routeAlgorithm.calculateBestInsertion(emptyRoute1, targetJob, Double.MAX_VALUE), emptyRoute1);
unassignedJobs.remove(targetJob);
VehicleRoute emptyRoute2 = VehicleRoute.emptyRoute();
copiedRoutes.add(emptyRoute2);
Job job2 = jobsInRoute.get(1);
routeAlgorithm.insertJob(job2, routeAlgorithm.calculateBestInsertion(emptyRoute2, job2, Double.MAX_VALUE), emptyRoute2);
unassignedJobs.remove(job2);
insertionStrategy.run(copiedRoutes, unassignedJobs, Double.MAX_VALUE);
double cost = getCost(copiedRoutes);
if(cost < bestSolution.getCost()){
// log.info("BING - new: " + cost + " old: " + bestSolution.getCost());
bestSolution = new VehicleRoutingProblemSolution(copiedRoutes, cost);
itersWithoutImprovement=0;
}
else{
itersWithoutImprovement++;
if(itersWithoutImprovement > 200){
// log.info("BREAK i="+i);
break;
}
}
}
return bestSolution;
}
private List<VehicleRoute> copyRoutes(Collection<VehicleRoute> routes) {
List<VehicleRoute> routeList = new ArrayList<VehicleRoute>();
for(VehicleRoute r : routes){
routeList.add(VehicleRoute.copyOf(r));
}
return routeList;
}
private void iniFleet(Collection<VehicleRoute> routes) {
fleetManager.unlockAll();
for(VehicleRoute route : routes){
if(!route.isEmpty()){
fleetManager.lock(route.getVehicle());
}
}
}
private double getCost(Collection<VehicleRoute> routes) {
double c = 0.0;
for(VehicleRoute r : routes){
c+=r.getCost();
}
return c;
}
private List<Job> getJobs(VehicleRoute route2split) {
Set<Job> jobs = new HashSet<Job>();
for(TourActivity act : route2split.getTourActivities().getActivities()){
if(act instanceof JobActivity){
jobs.add(((JobActivity) act).getJob());
}
}
return new ArrayList<Job>(jobs);
}
private VehicleRoute pickRouteThatHasAtLeastTwoJobs(Collection<VehicleRoute> routeList) {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
for(VehicleRoute r : routeList){
if(getJobs(r).size() > 1){
routes.add(r);
}
}
if(routes.isEmpty()) return null;
Collections.shuffle(routes,random);
return routes.get(0);
}
@Override
public String getName() {
return NAME;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.route.Vehicle;
import basics.route.VehicleImpl.VehicleType;
class InfiniteVehicles implements VehicleFleetManager{
static class TypeKeyComparator implements Comparator<TypeKey>{
@Override
public int compare(TypeKey k1, TypeKey k2) {
double k1_fix = k1.type.getVehicleCostParams().fix;
double k2_fix = k2.type.getVehicleCostParams().fix;
return (int)(k1_fix - k2_fix);
}
}
private static Logger logger = Logger.getLogger(InfiniteVehicles.class);
private Map<TypeKey,Vehicle> types = new HashMap<TypeKey, Vehicle>();
private List<TypeKey> sortedTypes = new ArrayList<VehicleFleetManager.TypeKey>();
public InfiniteVehicles(Collection<Vehicle> vehicles){
extractTypes(vehicles);
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=infiniteVehicle]";
}
private void extractTypes(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
types.put(typeKey,v);
sortedTypes.add(typeKey);
}
Collections.sort(sortedTypes, new TypeKeyComparator());
}
@Override
public Vehicle getEmptyVehicle(TypeKey typeId) {
return types.get(typeId);
}
@Override
public Collection<TypeKey> getAvailableVehicleTypes() {
return sortedTypes;
}
@Override
public void lock(Vehicle vehicle) {
}
@Override
public void unlock(Vehicle vehicle) {
}
@Override
public Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType) {
Set<TypeKey> typeSet = new HashSet<TypeKey>(types.keySet());
typeSet.remove(withoutThisType);
return typeSet;
}
@Override
public boolean isLocked(Vehicle vehicle) {
return false;
}
@Override
public void unlockAll() {
}
}

View file

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
interface InitialSolutionFactory {
public VehicleRoutingProblemSolution createInitialSolution(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.Driver;
import basics.route.Vehicle;
class InsertionData {
static class NoInsertionFound extends InsertionData{
public NoInsertionFound() {
super(Double.MAX_VALUE, NO_INDEX, NO_INDEX, null, null);
}
}
private static InsertionData noInsertion = new NoInsertionFound();
public static InsertionData noInsertionFound(){
return noInsertion;
}
static int NO_INDEX = -1;
private final double insertionCost;
private final int pickupInsertionIndex;
private final int deliveryInsertionIndex;
private final Vehicle selectedVehicle;
private final Driver selectedDriver;
private double departureTime;
public InsertionData(double insertionCost, int pickupInsertionIndex, int deliveryInsertionIndex, Vehicle vehicle, Driver driver){
this.insertionCost = insertionCost;
this.pickupInsertionIndex = pickupInsertionIndex;
this.deliveryInsertionIndex = deliveryInsertionIndex;
this.selectedVehicle = vehicle;
this.selectedDriver = driver;
}
@Override
public String toString() {
return "[iCost="+insertionCost+"][iIndex="+deliveryInsertionIndex+"][depTime="+departureTime+"][vehicle="+selectedVehicle+"][driver="+selectedDriver+"]";
}
public int getDeliveryInsertionIndex(){
return deliveryInsertionIndex;
}
public int getPickupInsertionIndex(){
return pickupInsertionIndex;
}
public double getInsertionCost() {
return insertionCost;
}
public Vehicle getSelectedVehicle() {
return selectedVehicle;
}
public Driver getSelectedDriver(){
return selectedDriver;
}
/**
* @return the departureTime
*/
public double getVehicleDepartureTime() {
return departureTime;
}
/**
* @param departureTime the departureTime to set
*/
public void setVehicleDepartureTime(double departureTime) {
this.departureTime = departureTime;
}
}

View file

@ -0,0 +1,132 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.algo.InsertionListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
class InsertionFactory {
private static Logger log = Logger.getLogger(InsertionFactory.class);
public static AbstractInsertionStrategy createInsertion(VehicleRoutingProblem vrp, HierarchicalConfiguration config,
VehicleFleetManager vehicleFleetManager, RouteStates activityStates, List<PrioritizedVRAListener> algorithmListeners){
if(config.containsKey("[@name]")){
String insertionName = config.getString("[@name]");
if(!insertionName.equals("bestInsertion") && !insertionName.equals("regretInsertion")){
new IllegalStateException(insertionName + " is not supported. use either \"bestInsertion\" or \"regretInsertion\"");
}
AbstractInsertionStrategy insertionStrategy = null;
List<InsertionListener> insertionListeners = new ArrayList<InsertionListener>();
List<PrioritizedVRAListener> algoListeners = new ArrayList<PrioritizedVRAListener>();
CalculatorBuilder calcBuilder = new CalculatorBuilder(insertionListeners, algorithmListeners);
calcBuilder.setActivityStates(activityStates);
calcBuilder.setVehicleRoutingProblem(vrp);
calcBuilder.setVehicleFleetManager(vehicleFleetManager);
if(config.containsKey("level")){
String level = config.getString("level");
if(level.equals("local")){
calcBuilder.setLocalLevel();
}
else if(level.equals("route")){
int forwardLooking = 0;
int memory = 1;
String forward = config.getString("level[@forwardLooking]");
String mem = config.getString("level[@memory]");
if(forward != null) forwardLooking = Integer.parseInt(forward);
else log.warn("parameter route[@forwardLooking] is missing. by default it is 0 which equals to local level");
if(mem != null) memory = Integer.parseInt(mem);
else log.warn("parameter route[@memory] is missing. by default it is 1");
calcBuilder.setRouteLevel(forwardLooking, memory);
}
else throw new IllegalStateException("level " + level + " is not known. currently it only knows \"local\" or \"route\"");
}
else calcBuilder.setLocalLevel();
if(config.containsKey("considerFixedCosts") || config.containsKey("considerFixedCost")){
String val = config.getString("considerFixedCosts");
if(val == null) val = config.getString("considerFixedCost");
if(val.equals("true")){
double fixedCostWeight = 0.5;
String weight = config.getString("considerFixedCosts[@weight]");
if(weight == null) weight = config.getString("considerFixedCost[@weight]");
if(weight != null) fixedCostWeight = Double.parseDouble(weight);
else log.warn("parameter considerFixedCosts[@weight] is missing. by default, it is 0.5.");
calcBuilder.considerFixedCosts(fixedCostWeight);
}
}
String timeSliceString = config.getString("experimental[@timeSlice]");
String neighbors = config.getString("experimental[@neighboringSlices]");
if(timeSliceString != null && neighbors != null){
calcBuilder.experimentalTimeScheduler(Double.parseDouble(timeSliceString),Integer.parseInt(neighbors));
}
JobInsertionCalculator jic = calcBuilder.build();
TourStateUpdater tourStateCalculator = new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts());
RouteAlgorithm routeAlgorithm = RouteAlgorithmImpl.newInstance(jic, tourStateCalculator);
routeAlgorithm.getListeners().add(new VehicleSwitched(vehicleFleetManager));
((RouteAlgorithmImpl) routeAlgorithm).setActivityStates(activityStates);
if(insertionName.equals("bestInsertion")){
insertionStrategy = BestInsertion.newInstance(routeAlgorithm);
}
else if(insertionName.equals("regretInsertion")){
insertionStrategy = RegretInsertion.newInstance(routeAlgorithm);
}
// else if(insertionName.equals("concurrentBestInsertion")){
// String processorsString = config.getString("[@processors]");
// int processors = 1;
// if(processorsString != null) processors = Integer.parseInt(processorsString);
//// BestInsertionConcurrent.newInstance(routeAlgorithm,)
//
//
// }
insertionStrategy.addListener(new RemoveEmptyVehicles());
insertionStrategy.addListener(new ResetAndIniFleetManager(vehicleFleetManager));
insertionStrategy.addAllListener(insertionListeners);
// insertionStrategy.addListener(new FindCheaperVehicle(
// new FindCheaperVehicleAlgoNew(vehicleFleetManager, tourStateCalculator, auxCalculator)));
algorithmListeners.addAll(algoListeners);
return insertionStrategy;
}
else throw new IllegalStateException("cannot create insertionStrategy, since it has no name.");
}
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.Job;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
interface InsertionStrategy {
class Insertion {
private final VehicleRoute route;
private final InsertionData insertionData;
public Insertion(VehicleRoute vehicleRoute, InsertionData insertionData) {
super();
this.route = vehicleRoute;
this.insertionData = insertionData;
}
public VehicleRoute getRoute() {
return route;
}
public InsertionData getInsertionData() {
return insertionData;
}
}
/**
* Assigns the unassigned jobs to service-providers
*
* @param vehicleRoutes
* @param unassignedJobs
* @param result2beat
*/
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat);
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
interface InsertionStrategyFactory {
public InsertionStrategy createStrategy(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
interface JobDistance {
public double calculateDistance(Job i, Job j);
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingTransportCosts;
class JobDistanceAvgCosts implements JobDistance {
private VehicleRoutingTransportCosts costs;
public JobDistanceAvgCosts(VehicleRoutingTransportCosts costs) {
super();
this.costs = costs;
}
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
}
} else {
throw new UnsupportedOperationException(
"currently, this class just works with shipments and services.");
}
return avgCost;
}
private double calcDist(String from, String to) {
return costs.getTransportCost(from, to, 0.0, null, null);
}
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import util.CrowFlyCosts;
import util.Locations;
import basics.Job;
import basics.Service;
class JobDistanceBeeline implements JobDistance {
private Locations locations;
public JobDistanceBeeline(Locations locations) {
super();
this.locations = locations;
}
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
}
} else {
throw new UnsupportedOperationException(
"currently, this class just works with shipments and services.");
}
return avgCost;
}
private double calcDist(String from, String to) {
return new CrowFlyCosts(locations).getTransportCost(from, to, 0.0,null, null);
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
interface JobInsertionCalculator {
public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore);
}

View file

@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import algorithms.RouteStates.ActivityState;
import basics.algo.AlgorithmEndsListener;
import basics.algo.JobInsertedListener;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
import basics.route.TourActivity.JobActivity;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
class JobObserver implements JobInsertedListener, BeforeJobInsertionListener, AlgorithmEndsListener{
private static class Info {
double depTime;
double tourSize;
double insertionIndex;
double error;
public Info(double depTime, double tourSize, double insertionIndex,
double error) {
super();
this.depTime = depTime;
this.tourSize = tourSize;
this.insertionIndex = insertionIndex;
this.error = error;
}
}
private String locationId = "70";
private double routeCostBefore;
private double estimatedMC;
private boolean beforeFirst = false;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
Collection<Info> infos = new ArrayList<Info>();
@Override
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
if(job2insert instanceof Service){
if(((Service) job2insert).getLocationId().equals(locationId)){
double actualMC = insertedIn.getCost()-routeCostBefore;
TourActivity act = getAct(job2insert,insertedIn);
double error = (estimatedMC-actualMC);
int tourSize = insertedIn.getTourActivities().getActivities().size();
int insertionIndex = getIndexOf(job2insert, insertedIn);
// infos.add(new Info())
double depTime = state(act).getEarliestOperationStart()+act.getOperationTime();
infos.add(new Info(depTime,tourSize,insertionIndex,error));
// System.out.println("[id=1][tourSize="+tourSize+"][index="+insertionIndex+
// "][earliestDeparture="+depTime+
// "][tourCostBefore="+routeCostBefore+"][routeCostAfter="+insertedIn.getCost()+"]" +
// "[estimated="+Math.round(estimatedMC)+"][actual="+Math.round(actualMC)+"][error(abs)="+error +
// "][errorPerNextCustomer="+ (error/(double)(tourSize-insertionIndex)) + "]");
routeCostBefore = 0.0;
estimatedMC = 0.0;
if(!beforeFirst) throw new IllegalStateException("ähhh");
beforeFirst = false;
}
}
}
private TourActivity getAct(Job job2insert, VehicleRoute insertedIn) {
for(TourActivity act : insertedIn.getTourActivities().getActivities()){
if(act instanceof JobActivity){
if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
return act;
}
}
}
return null;
}
private int getIndexOf(Job job2insert, VehicleRoute insertedIn) {
int index=0;
for(TourActivity act : insertedIn.getTourActivities().getActivities()){
if(act instanceof JobActivity){
if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
return index;
}
}
index++;
}
return -1;
}
@Override
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
if(job instanceof Service){
if(((Service) job).getLocationId().equals(locationId)){
// System.out.println("[id=1][tourSize="+route.getTour().getActivities().size()+"][tourCost="+route.getCost()+"]" +
// "[estimatedMarginalInsertionCost="+data.getInsertionCost()+"]");
routeCostBefore = route.getCost();
estimatedMC = data.getInsertionCost();
beforeFirst = true;
}
}
}
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("output/errorAna.txt"));
for(Info info : infos){
writer.write(new StringBuilder().append(info.depTime).append(";").append(info.tourSize).append(";").append(info.insertionIndex).append(";")
.append(info.error).append("\n").toString());
}
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
interface JobRemover {
/**
* Removes jobs from vehicRoute and return true if job has been successfully removed.
*
* @return true if job removed successfully, otherwise false
*/
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import basics.Job;
import basics.route.VehicleRoute;
class JobRemoverImpl implements JobRemover{
interface RemoverListener {
public void informRemovedJob(Job j, VehicleRoute r);
}
private List<RemoverListener> remListeners = new ArrayList<RemoverListener>();
@Override
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
boolean jobRemoved = vehicleRoute.getTourActivities().removeJob(job);
if(jobRemoved) informRemovedJob(job,vehicleRoute);
return jobRemoved;
}
private void informRemovedJob(Job job, VehicleRoute vehicleRoute) {
for(RemoverListener l : remListeners) l.informRemovedJob(job, vehicleRoute);
}
public List<RemoverListener> getRemListeners() {
return remListeners;
}
}

View file

@ -0,0 +1,134 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.log4j.Logger;
import util.CrowFlyCosts;
import util.EuclideanDistanceCalculator;
import util.Locations;
import util.NeighborhoodImpl;
import util.Solutions;
import algorithms.selectors.SelectBest;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.VehicleRoutingAlgorithmFactory;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
public class NeighborhoodThresholdInitialiser implements AlgorithmStartsListener{
private static Logger log = Logger.getLogger(NeighborhoodThresholdInitialiser.class);
private NeighborhoodImpl neighborhood;
private VehicleRoutingAlgorithmFactory routingAlgorithmFactory = new VehicleRoutingAlgorithmFactory() {
@Override
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp) {
VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "resources/config.xml");
return algorithm;
}
};
private int crowFlySpeed = 20;
public NeighborhoodThresholdInitialiser(NeighborhoodImpl neighborhood) {
this.neighborhood = neighborhood;
}
/**
* @param crowFlySpeed the crowFlySpeed to set
*/
public void setCrowFlySpeed(int crowFlySpeed) {
this.crowFlySpeed = crowFlySpeed;
}
/**
* @param routingAlgorithmFactory the routingAlgorithm to set
*/
public void setRoutingAlgorithmFactory(VehicleRoutingAlgorithmFactory routingAlgorithmFactory) {
this.routingAlgorithmFactory = routingAlgorithmFactory;
}
public void initialise(VehicleRoutingProblem problem){
informAlgorithmStarts(problem, null, null);
}
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
builder.addAllJobs(problem.getJobs().values());
builder.addAllVehicles(problem.getVehicles());
VehicleRoutingProblem pblm = builder.build();
CrowFlyCosts crowFly = new CrowFlyCosts(builder.getLocations());
crowFly.speed = crowFlySpeed;
pblm.setTransportCosts(crowFly);
VehicleRoutingAlgorithm algo = routingAlgorithmFactory.createAlgorithm(pblm);
Collection<VehicleRoutingProblemSolution> mySolutions = algo.searchSolutions();
double threshold = determineThreshold(pblm,builder.getLocations(), mySolutions);
neighborhood.setThreshold(threshold);
neighborhood.initialise();
}
private double determineThreshold(VehicleRoutingProblem pblm, Locations locations, Collection<VehicleRoutingProblemSolution> mySolutions) {
VehicleRoutingProblemSolution bestSolution = Solutions.getBest(mySolutions);
double[] distances = new double[bestSolution.getRoutes().size()+pblm.getJobs().size()];
getDistances(distances,bestSolution,locations);
Mean mean = new Mean();
double meanValue = mean.evaluate(distances);
StandardDeviation dev = new StandardDeviation();
double devValue = dev.evaluate(distances, meanValue);
log.info("mean="+meanValue+", dev="+devValue);
return meanValue + devValue;
// + 2*devValue;
// return Double.MAX_VALUE;
}
private void getDistances(double[] distances, VehicleRoutingProblemSolution bestSolution, Locations locations) {
int index = 0;
for(VehicleRoute route : bestSolution.getRoutes()){
TourActivity prev = null;
for(TourActivity act : route.getTourActivities().getActivities()){
if(prev == null){ prev = act; continue; }
double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(act.getLocationId()));
// log.info("dist="+dist);
distances[index] = dist;
index++;
prev = act;
}
// double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(route.getEnd().getLocationId()));
// distances[index] = dist;
// index++;
}
}
}

View file

@ -0,0 +1,229 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
///*******************************************************************************
// * Copyright (c) 2011 Stefan Schroeder.
// * eMail: stefan.schroeder@kit.edu
// *
// * All rights reserved. This program and the accompanying materials
// * are made available under the terms of the GNU Public License v2.0
// * which accompanies this distribution, and is available at
// * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
// *
// * Contributors:
// * Stefan Schroeder - initial API and implementation
// ******************************************************************************/
//package algorithms;
//
//import java.util.ArrayList;
//import java.util.Collection;
//import java.util.List;
//import java.util.concurrent.Callable;
//import java.util.concurrent.CompletionService;
//import java.util.concurrent.ExecutionException;
//import java.util.concurrent.ExecutorCompletionService;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Future;
//
//import org.apache.log4j.Logger;
//
//import basics.InsertionData;
//import basics.Job;
//import basics.Service;
//import basics.Shipment;
//import basics.VehicleRoute;
//import basics.InsertionData.NoInsertionFound;
//
//
//
//
//
//
//
///**
// * Simplest recreation strategy. All removed customers are inserted where insertion costs are minimal. I.e. each tour-agent is asked for
// * minimal marginal insertion costs. The tour-agent offering the lowest marginal insertion costs gets the customer/shipment.
// *
// * @author stefan schroeder
// *
// */
//
//final class ParRegretInsertion extends AbstractRecreationStrategy{
//
//
// private Logger logger = Logger.getLogger(ParRegretInsertion.class);
//
//
// public static double scoreParam_of_timeWindowLegth = 0.0;
//
// private ExecutorService executor;
//
// public static double scoreParam_of_distance = 0.5;
//
// private DepotDistance depotDistance;
//
// private RouteAlgorithm routeAlgorithm;
//
// private VehicleRouteFactory vehicleRouteFactory;
//
// public ParRegretInsertion(ExecutorService executor, RouteAlgorithm routeAlgorithm, VehicleRouteFactory routeFactory, DepotDistance depotDistance) {
// super();
// this.executor = executor;
// this.routeAlgorithm = routeAlgorithm;
// this.depotDistance = depotDistance;
// this.vehicleRouteFactory = routeFactory;
// }
//
//
// @Override
// public void recreate(final Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
// List<Job> jobs = new ArrayList<Job>(unassignedJobs);
// informRecreationStart(unassignedJobs.size());
//
// while(!jobs.isEmpty()){
// List<Job> unassignedJobList = new ArrayList<Job>(jobs);
// ScoredJob bestScoredJob = null;
// double bestScore = -1*Double.MAX_VALUE;
// CompletionService<ScoredJob> completionService = new ExecutorCompletionService<ScoredJob>(executor);
//
// for(final Job unassignedJob : unassignedJobList){
// completionService.submit(new Callable<ScoredJob>(){
//
// @Override
// public ScoredJob call() throws Exception {
// return getScoredJob(vehicleRoutes, unassignedJob);
// }
//
// });
//
// }
// try{
// for(int i=0;i<unassignedJobList.size();i++){
// Future<ScoredJob> fsj = completionService.take();
// ScoredJob scoredJob = fsj.get();
// if(scoredJob == null){
// continue;
// }
// if(scoredJob.getScore() > bestScore){
// bestScoredJob = scoredJob;
// bestScore = scoredJob.getScore();
// }
// }
// }
// catch(InterruptedException e){
// Thread.currentThread().interrupt();
// }
// catch (ExecutionException e) {
// e.printStackTrace();
// logger.error(e.getCause().toString());
// System.exit(1);
// }
// if(bestScoredJob == null){
// Job job = unassignedJobList.get(0);
// VehicleRoute newRoute = vehicleRouteFactory.createVehicleRoute();
// InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
// if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
// routeAlgorithm.insertJob(job,bestI,newRoute);
// vehicleRoutes.add(newRoute);
// jobs.remove(job);
//
// }
// else{
// routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(),bestScoredJob.getRoute());
// jobs.remove(bestScoredJob.getJob());
// }
// informJobInsertion(null, (unassignedJobList.size()-1), null);
// }
// }
//
// private ScoredJob getScoredJob(Collection<VehicleRoute> vehicleRoutes, Job job){
// InsertionData best = null;
// InsertionData secondBest = null;
// VehicleRoute bestRoute = null;
// double benchmark = Double.MAX_VALUE;
// for(VehicleRoute route : vehicleRoutes){
// if(secondBest != null){
// benchmark = secondBest.getInsertionCost();
// }
// InsertionData iData = routeAlgorithm.calculateBestInsertion(route, job, benchmark);
// if(iData instanceof NoInsertionFound) continue;
// if(best == null) {
// best = iData;
// bestRoute = route;
// }
// else if(iData.getInsertionCost() < best.getInsertionCost()){
// secondBest = best;
// best = iData;
// bestRoute = route;
// }
// else if(secondBest == null) secondBest = iData;
// else if(iData.getInsertionCost() < secondBest.getInsertionCost()) secondBest = iData;
// }
// if(best == null){
// return null;
// }
// double score = score(job,best,secondBest);
// return new ScoredJob(job, score, best, bestRoute);
// }
//
// private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
// /*
// * wieder so eine bescheuerte fallunterscheidung. hier will ich
// * doch einfach nur das maßgebende zeitfenster des jobs
// * job.getTimeWindow()
// * Problem: eine Shipment hat zwei TWs, sowohl ein PickupTW als auch
// * ein DeliveryTW
// */
// double twStart = 0.0;
// double twEnd = 0.0;
// if(unassignedJob instanceof Shipment){
// twStart = ((Shipment) unassignedJob).getDeliveryTW().getStart();
// twEnd = ((Shipment) unassignedJob).getDeliveryTW().getEnd();
// }
// else if(unassignedJob instanceof Service){
// twStart = ((Service) unassignedJob).getTimeWindow().getStart();
// twEnd = ((Service) unassignedJob).getTimeWindow().getEnd();
// }
// if(best == null){
// throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
// }
// if(secondBest == null){
// return Double.MAX_VALUE;
// }
//// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) + scoreParam_of_distance*getDistance(unassignedJob) - scoreParam_of_timeWindowLegth*(twEnd-twStart);
//// logger.info("priceDiff="+ (secondBest.getPrice()-best.getPrice()) + "; param*dist="
// double timeWindowInfluence = scoreParam_of_timeWindowLegth*(twEnd-twStart);
// double distanceInfluence = scoreParam_of_distance*getDistance(unassignedJob);
// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) - timeWindowInfluence
// + distanceInfluence;
// return score;
// }
//
// private double getDistance(Job unassignedJob) {
// return depotDistance.calcDistance(unassignedJob);
// }
//
//
// public double getTimeParam() {
// return scoreParam_of_timeWindowLegth;
// }
//
//
//}

View file

@ -0,0 +1,210 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.Service;
import basics.route.VehicleRoute;
/**
* Insertion based an regret approach.
*
* <p>Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference
* between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction.
* The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this
* customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later.
*
* @author stefan schroeder
*
*/
final class RegretInsertion extends AbstractInsertionStrategy{
/**
* Scorer to include other impacts on score such as time-window length or distance to depot.
*
* @author schroeder
*
*/
static interface ScoringFunction {
public double score(Job job);
}
/**
* Scorer that includes the length of the time-window when scoring a job. The wider the time-window, the lower the score.
*
* <p>This is the default scorer, i.e.: score = (secondBest - firstBest) + this.TimeWindowScorer.score(job)
*
* @author schroeder
*
*/
static class TimeWindowScorer implements ScoringFunction {
private double tw_scoringParam = - 0.1;
@Override
public double score(Job job) {
double twStart = 0.0;
double twEnd = 0.0;
// if(job instanceof Shipment){
// twStart = ((Shipment) job).getDeliveryTW().getStart();
// twEnd = ((Shipment) job).getDeliveryTW().getEnd();
// }
// else
if(job instanceof Service){
twStart = ((Service) job).getTimeWindow().getStart();
twEnd = ((Service) job).getTimeWindow().getEnd();
}
return (twEnd-twStart)*tw_scoringParam;
}
@Override
public String toString() {
return "[name=timeWindowScorer][scoringParam="+tw_scoringParam+"]";
}
}
public static RegretInsertion newInstance(RouteAlgorithm routeAlgorithm) {
return new RegretInsertion(routeAlgorithm);
}
private Logger logger = Logger.getLogger(RegretInsertion.class);
private RouteAlgorithm routeAlgorithm;
private ScoringFunction scoringFunction = new TimeWindowScorer();
/**
* Sets the scoring function.
*
* <p>By default, the this.TimeWindowScorer is used.
*
* @param scoringFunction
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
this.scoringFunction = scoringFunction;
}
public RegretInsertion(RouteAlgorithm routeAlgorithm) {
super();
this.routeAlgorithm = routeAlgorithm;
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=regretInsertion][additionalScorer="+scoringFunction+"]";
}
public RouteAlgorithm getRouteAlgorithm(){
return routeAlgorithm;
}
/**
* Runs insertion.
*
* <p>Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
*
*/
@Override
public void run(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs, double resultToBeat) {
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
informInsertionStarts(routes,unassignedJobs.size());
int inserted = 0;
while(!jobs.isEmpty()){
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
ScoredJob bestScoredJob = null;
double bestScore = -1*Double.MAX_VALUE;
VehicleRoute insertIn = null;
for(Job unassignedJob : unassignedJobList){
InsertionData best = null;
InsertionData secondBest = null;
VehicleRoute bestRoute = null;
double benchmark = Double.MAX_VALUE;
for(VehicleRoute route : routes){
if(secondBest != null){
benchmark = secondBest.getInsertionCost();
}
InsertionData iData = routeAlgorithm.calculateBestInsertion(route, unassignedJob, benchmark);
if(iData instanceof NoInsertionFound) continue;
if(best == null){
best = iData;
bestRoute = route;
}
else if(iData.getInsertionCost() < best.getInsertionCost()){
secondBest = best;
best = iData;
bestRoute = route;
}
else if(secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())){
secondBest = iData;
}
}
if(best == null){
break;
}
double score = score(unassignedJob,best,secondBest);
if(score > bestScore){
bestScoredJob = new ScoredJob(unassignedJob,score,best,bestRoute);
bestScore = score;
}
}
Job assignedJob;
if(bestScoredJob == null){
Job job = unassignedJobList.get(0);
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
insertIn=newRoute;
assignedJob=job;
routeAlgorithm.insertJob(job,bestI,newRoute);
routes.add(newRoute);
jobs.remove(job);
}
else{
routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
insertIn=bestScoredJob.getRoute();
assignedJob=bestScoredJob.getJob();
jobs.remove(bestScoredJob.getJob());
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), assignedJob, insertIn);
}
}
private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
if(best == null){
throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
}
if(secondBest == null){
return Double.MAX_VALUE;
}
return (secondBest.getInsertionCost()-best.getInsertionCost()) + scoringFunction.score(unassignedJob);
}
}

View file

@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import basics.algo.InsertionEndsListener;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class RemoveEmptyVehicles implements InsertionStartsListener, InsertionEndsListener{
private static Logger log = Logger.getLogger(RemoveEmptyVehicles.class);
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
// List<VehicleRoute> routes = new ArrayList<VehicleRoute>(vehicleRoutes);
// for(VehicleRoute route : routes){
// if(route.isEmpty()) { vehicleRoutes.remove(route); }
// }
}
@Override
public String toString() {
return "[name=removeEmptyVehicles]";
}
@Override
public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes) {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>(vehicleRoutes);
for(VehicleRoute route : routes){
if(route.isEmpty()) { vehicleRoutes.remove(route); }
}
}
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.log4j.Logger;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class ResetAndIniFleetManager implements InsertionStartsListener{
private static Logger log = Logger.getLogger(ResetAndIniFleetManager.class);
private VehicleFleetManager vehicleFleetManager;
ResetAndIniFleetManager(VehicleFleetManager vehicleFleetManager) {
super();
this.vehicleFleetManager = vehicleFleetManager;
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
vehicleFleetManager.unlockAll();
for(VehicleRoute route : vehicleRoutes){
if(!route.isEmpty()){
vehicleFleetManager.lock(route.getVehicle());
}
}
// log.info("#lockedVehicles=" + ((VehicleFleetManagerImpl)vehicleFleetManager).sizeOfLockedVehicles() + " #routes="+vehicleRoutes.size());
}
@Override
public String toString() {
return "[name=resetAndIniFleetManager]";
}
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.Job;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
interface RouteAlgorithm {
interface RouteAlgorithmListener {
}
interface JobRemovedListener extends RouteAlgorithmListener{
public void removed(VehicleRoute route, Job job);
}
interface JobInsertedListener extends RouteAlgorithmListener{
public void inserted(VehicleRoute route, Job job);
}
interface VehicleSwitchedListener extends RouteAlgorithmListener{
public void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle);
}
/**
* Calculates the best insertion position and the corresponding marginal costs of inserting the job (according to the insertionCostCalculator).
* This does not affect any input parameter, thus the vehicleRoute and its data will not be changed/affected.
*
* @param VehicleRoute, Job, double
* @return InsertionData
*/
public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownPrice);
/**
* Removes job from vehicleRoute and does not update the resulting tour. Thus the tour state might not be valid anymore.
* Note that this changes vehicleRoute!
*
* @return true if job removed successfully, otherwise false
*/
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
/**
* Removes job from input parameter vehicleRoute AND updates the state of the resulting tour with tourStateCalc.
* Note that this changes para vehicleRoute!
*/
public boolean removeJob(Job job, VehicleRoute vehicleRoute);
/**
* Inserts job into vehicleRoute.getTour().
* Please note, that this changes the parameter vehicleRoute!
*/
public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData);
/**
* Inserts job into vehicleRoute.getTour().
* Please note, that this changes the parameter vehicleRoute!
*/
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute);
/**
* Updates vehicleRoute, i.e. uses the tourStateCalculator to update for example timeWindows and loads on vehicleRoute.getTour()
* Note that this changes the parameter vehicleRoute!
*/
public void updateTour(VehicleRoute vehicleRoute);
public Collection<RouteAlgorithmListener> getListeners();
}

View file

@ -0,0 +1,181 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.Service;
import basics.route.ServiceActivity;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
final class RouteAlgorithmImpl implements RouteAlgorithm {
private static Logger logger = Logger.getLogger(RouteAlgorithmImpl.class);
static double NO_DEPARTURE_TIME = -12345.12345;
private String algoDescription = "algorithm to remove and insert jobs from vehicleRoutes. it also calculates marginal costs of the best insertion of a " +
"job into the given vehicle route";
public static RouteAlgorithmImpl newInstance(JobInsertionCalculator jobInsertionCalculator, VehicleRouteUpdater tourStateCalculator){
return new RouteAlgorithmImpl(jobInsertionCalculator, tourStateCalculator);
}
private Collection<RouteAlgorithmListener> listeners = new ArrayList<RouteAlgorithmListener>();
private VehicleRouteUpdater tourCalculator;
private JobInsertionCalculator insertionCostCalculator;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
private RouteAlgorithmImpl(JobInsertionCalculator insertionCostCalculator, VehicleRouteUpdater tourCalculator){
this.tourCalculator = tourCalculator;
this.insertionCostCalculator = insertionCostCalculator;
}
public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownCost) {
return insertionCostCalculator.calculate(vehicleRoute, job, null, NO_DEPARTURE_TIME, null, bestKnownCost);
}
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
boolean removed = vehicleRoute.getTourActivities().removeJob(job);
if(removed){
jobRemoved(vehicleRoute,job);
}
return removed;
}
private void jobRemoved(VehicleRoute vehicleRoute, Job job) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof JobRemovedListener){
((JobRemovedListener) l).removed(vehicleRoute, job);
}
}
}
public boolean removeJob(Job job, VehicleRoute vehicleRoute){
boolean removed = removeJobWithoutTourUpdate(job, vehicleRoute);
if(removed) updateTour(vehicleRoute);
return removed;
}
public void updateTour(VehicleRoute vehicleRoute){
boolean tourIsFeasible = tourCalculator.updateRoute(vehicleRoute);
if(!tourIsFeasible){
throw new IllegalStateException("At this point tour should be feasible. but it is not. \n currentTour=" + vehicleRoute.getTourActivities() +
"\n error sources: check jobInsertionCostCalculators and actInsertionCalculators. somehow an insertion is made, althought a hard constraint is broken. Here, hard constraints refer to \n" +
"hard time-window constraints. If you want to deal with such constraints, make sure a violation is penalized properly (such that it can never be the best insertion position). \n" +
"If you use CalculatesServiceInsertion and CalculatesActivityInsertion, the only hard constraint is the vehicle-capacity constraints. A violation of time-windows must be penalized in \n" +
"the vehicleRouteCostFunction. For example: in handleActivity(....) one can check whether the act start-time is higher than the latestOperationStartTime. If so penalize it with a very high value. \n" +
"For example: \n" +
"public void handleActivity(TourActivity tourAct, double startTime, double endTime) {\n" +
"\tif(startTime > tourAct.getLatestOperationStartTime()){\n" +
"\t\tcost += Double.MAX_VALUE;\n" +
"\t}\n" +
"});");
}
}
@Override
public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData) {
if(insertionData == null || (insertionData instanceof NoInsertionFound)) throw new IllegalStateException("insertionData null. cannot insert job.");
if(job == null) throw new IllegalStateException("cannot insert null-job");
if(!(vehicleRoute.getVehicle().getId().toString().equals(insertionData.getSelectedVehicle().getId().toString()))){
vehicleSwitched(vehicleRoute.getVehicle(),insertionData.getSelectedVehicle());
vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
}
if(job instanceof Service) {
vehicleRoute.getTourActivities().addActivity(insertionData.getDeliveryInsertionIndex(), ServiceActivity.newInstance((Service)job));
// if(vehicleRoute.getStart().getEndTime() != insertionData.getVehicleDepartureTime()){
// logger.info("depTime switched from " + vehicleRoute.getStart().getEndTime() + " to " + insertionData.getVehicleDepartureTime());
// }
vehicleRoute.setDepartureTime(insertionData.getVehicleDepartureTime());
}
else throw new IllegalStateException("neither service nor shipment. this is not supported.");
jobInserted(vehicleRoute,job);
}
private void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof VehicleSwitchedListener){
((VehicleSwitchedListener) l).vehicleSwitched(oldVehicle,newVehicle);
}
}
}
private void jobInserted(VehicleRoute vehicleRoute, Job job) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof JobInsertedListener){
((JobInsertedListener) l).inserted(vehicleRoute, job);
}
}
}
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){
insertJobWithoutTourUpdate(vehicleRoute, job, insertionData);
updateTour(vehicleRoute);
}
@Override
public String toString() {
return algoDescription;
}
private void insertJob(Service service, int serviceInsertionIndex, VehicleRoute vehicleRoute) {
// TourActivity del = actStates.getActivity(service,true);
vehicleRoute.getTourActivities().addActivity(serviceInsertionIndex, ServiceActivity.newInstance(service));
}
public Collection<RouteAlgorithmListener> getListeners() {
return listeners;
}
public void setAlgoDescription(String algoDescription) {
this.algoDescription = algoDescription;
}
}

View file

@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.route.ServiceActivity;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
class RouteStates implements IterationStartsListener{
Logger log = Logger.getLogger(RouteStates.class);
static class RouteState {
private double costs;
private int load;
private VehicleRoute route;
public RouteState(VehicleRoute route) {
super();
this.route = route;
}
/**
* @return the costs
*/
public double getCosts() {
return costs;
}
/**
* @param costs the costs to set
*/
public void setCosts(double costs) {
this.costs = costs;
}
/**
* @return the load
*/
public int getLoad() {
return load;
}
/**
* @param load the load to set
*/
public void setLoad(int load) {
this.load = load;
}
}
static class ActivityState {
private double earliestOperationStart;
private double latestOperationStart;
private double currentLoad;
private double currentCost;
private TourActivity act;
public ActivityState(TourActivity activity){
this.earliestOperationStart=activity.getTheoreticalEarliestOperationStartTime();
this.latestOperationStart=activity.getTheoreticalLatestOperationStartTime();
this.act = activity;
}
@Override
public String toString() {
return "[earliestStart="+earliestOperationStart+"][latestStart="+
latestOperationStart+"][currLoad="+currentLoad+"][currCost="+currentCost+"]";
}
public double getEarliestOperationStart() {
return earliestOperationStart;
}
void setEarliestOperationStart(double earliestOperationStart) {
this.earliestOperationStart = earliestOperationStart;
}
public double getLatestOperationStart() {
return latestOperationStart;
}
void setLatestOperationStart(double latestOperationStart) {
this.latestOperationStart = latestOperationStart;
}
public double getCurrentLoad() {
return currentLoad;
}
void setCurrentLoad(double currentLoad) {
this.currentLoad = currentLoad;
}
public double getCurrentCost() {
return currentCost;
}
void setCurrentCost(double currentCost) {
this.currentCost = currentCost;
}
public void reset() {
earliestOperationStart = act.getTheoreticalEarliestOperationStartTime();
latestOperationStart = act.getTheoreticalLatestOperationStartTime() ;
currentLoad = 0.0;
currentCost = 0.0;
}
}
private Map<TourActivity, ActivityState> activityStates;
private Map<Service, TourActivity> tourActivities;
private Map<VehicleRoute, RouteState> routeStates;
public RouteStates() {
activityStates = new HashMap<TourActivity, RouteStates.ActivityState>();
tourActivities = new HashMap<Service,TourActivity>();
routeStates = new HashMap<VehicleRoute, RouteStates.RouteState>();
}
ActivityState getState(TourActivity act){
if(!activityStates.containsKey(act)) return null;
return activityStates.get(act);
}
public void clearStates(){
activityStates.clear();
}
public Map<TourActivity, ActivityState> getActivityStates() {
return activityStates;
}
TourActivity getActivity(Service service, boolean resetState){
TourActivity tourActivity = tourActivities.get(service);
getState(tourActivity).reset();
return tourActivity;
}
public void resetRouteStates(){
routeStates.clear();
}
public RouteState getRouteState(VehicleRoute route){
RouteState routeState = routeStates.get(route);
if(routeState == null){
routeState = new RouteState(route);
putRouteState(route, routeState);
}
return routeState;
}
private void putRouteState(VehicleRoute route, RouteState routeState){
routeStates.put(route, routeState);
}
void initialiseStateOfJobs(Collection<Job> jobs){
for(Job job : jobs){
if(job instanceof Service){
ServiceActivity service = ServiceActivity.newInstance((Service)job);
ActivityState state = new ActivityState(service);
tourActivities.put((Service) job, service);
activityStates.put(service, state);
}
else{
throw new IllegalStateException();
}
}
}
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
resetRouteStates();
}
}

View file

@ -0,0 +1,207 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeSet;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import util.StopWatch;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.SearchStrategyModule;
import basics.route.VehicleRoute;
final class RuinRadial implements RuinStrategy {
private final static String NAME = "radialRuin";
/**
* returns a new creation of instance of ruinRadial
* @param vrp
* @param fraction TODO
* @param jobDistance
* @param jobRemover TODO
* @param routeUpdater TODO
* @return
*/
static RuinRadial newInstance(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
return new RuinRadial(vrp, fraction, jobDistance, jobRemover, routeUpdater);
}
static class ReferencedJob {
private Job job;
private double distance;
public ReferencedJob(Job job, double distance) {
super();
this.job = job;
this.distance = distance;
}
public Job getJob() {
return job;
}
public double getDistance() {
return distance;
}
}
private Logger logger = Logger.getLogger(RuinRadial.class);
private VehicleRoutingProblem vrp;
private double fractionOfAllNodes2beRuined;
private Map<String, TreeSet<ReferencedJob>> distanceNodeTree = new HashMap<String, TreeSet<ReferencedJob>>();
private Random random = RandomNumberGeneration.getRandom();
private JobDistance jobDistance;
private JobRemover jobRemover;
private VehicleRouteUpdater routeUpdater;
public void setRandom(Random random) {
this.random = random;
}
public RuinRadial(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater) {
super();
this.vrp = vrp;
this.jobDistance = jobDistance;
this.jobRemover = jobRemover;
this.routeUpdater = routeUpdater;
this.fractionOfAllNodes2beRuined = fraction;
calculateDistancesFromJob2Job();
logger.info("intialise " + this);
}
public void setRuinFraction(double fractionOfAllNodes) {
this.fractionOfAllNodes2beRuined = fractionOfAllNodes;
logger.info("fraction set " + this);
}
private void calculateDistancesFromJob2Job() {
logger.info("preprocess distances between locations ...");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int nuOfDistancesStored = 0;
for (Job i : vrp.getJobs().values()) {
TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
new Comparator<ReferencedJob>() {
@Override
public int compare(ReferencedJob o1, ReferencedJob o2) {
if (o1.getDistance() <= o2.getDistance()) {
return 1;
} else {
return -1;
}
}
});
distanceNodeTree.put(i.getId(), treeSet);
for (Job j : vrp.getJobs().values()) {
double distance = jobDistance.calculateDistance(i, j);
ReferencedJob refNode = new ReferencedJob(j, distance);
treeSet.add(refNode);
nuOfDistancesStored++;
}
}
stopWatch.stop();
logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
(distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
}
@Override
public String toString() {
return "[name=radialRuin][fraction="+fractionOfAllNodes2beRuined+"]";
}
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
if(vehicleRoutes.isEmpty()){
return Collections.EMPTY_LIST;
}
int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
if (nOfJobs2BeRemoved == 0) {
return Collections.EMPTY_LIST;
}
Job randomJob = pickRandomJob();
Collection<Job> unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
return unassignedJobs;
}
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
List<Job> unassignedJobs = new ArrayList<Job>();
TreeSet<ReferencedJob> tree = distanceNodeTree.get(targetJob.getId());
Iterator<ReferencedJob> descendingIterator = tree.descendingIterator();
int counter = 0;
while (descendingIterator.hasNext() && counter < nOfJobs2BeRemoved) {
ReferencedJob refJob = descendingIterator.next();
Job job = refJob.getJob();
unassignedJobs.add(job);
counter++;
boolean removed = false;
for (VehicleRoute route : vehicleRoutes) {
removed = jobRemover.removeJobWithoutTourUpdate(job, route);
if (removed) {
break;
}
}
}
for(VehicleRoute route : vehicleRoutes){
routeUpdater.updateRoute(route);
}
return unassignedJobs;
}
private Job pickRandomJob() {
int totNuOfJobs = vrp.getJobs().values().size();
int randomIndex = random.nextInt(totNuOfJobs);
Job job = new ArrayList<Job>(vrp.getJobs().values()).get(randomIndex);
return job;
}
private int getNuOfJobs2BeRemoved() {
return (int) Math.ceil(vrp.getJobs().values().size()
* fractionOfAllNodes2beRuined);
}
// @Override
// public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
// ruin(vrpSolution.getRoutes());
// return vrpSolution;
// }
//
// @Override
// public String getName() {
// return NAME;
// }
}

View file

@ -0,0 +1,151 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.route.VehicleRoute;
/**
* Ruin strategy that ruins current solution randomly. I.e.
* customer are removed randomly from current solution.
*
* @author stefan schroeder
*
*/
final class RuinRandom implements RuinStrategy {
public static RuinRandom newInstance(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
return new RuinRandom(vrp, fraction, jobRemover, routeUpdater);
}
private Logger logger = Logger.getLogger(RuinRandom.class);
private VehicleRoutingProblem vrp;
private double fractionOfAllNodes2beRuined;
private Random random = RandomNumberGeneration.getRandom();
private JobRemover jobRemover;
private VehicleRouteUpdater vehicleRouteUpdater;
public void setRandom(Random random) {
this.random = random;
}
/**
* Constructs ruinRandom.
*
* @param vrp
* @param fraction which is the fraction of total c
* @param jobRemover
* @param vehicleRouteUpdater
*/
public RuinRandom(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater vehicleRouteUpdater) {
super();
this.vrp = vrp;
this.jobRemover = jobRemover;
this.vehicleRouteUpdater = vehicleRouteUpdater;
this.fractionOfAllNodes2beRuined = fraction;
logger.info("initialise " + this);
logger.info("done");
}
/**
* Removes a fraction of jobs from vehicleRoutes.
*
* <p>The number of jobs is calculated as follows: Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined).
*/
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
List<Job> unassignedJobs = new ArrayList<Job>();
int nOfJobs2BeRemoved = selectNuOfJobs2BeRemoved();
ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
return unassignedJobs;
}
/**
* Removes nOfJobs2BeRemoved from vehicleRoutes, including targetJob.
*/
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved) {
List<Job> unassignedJobs = new ArrayList<Job>();
if(targetJob != null){
boolean removed = false;
for (VehicleRoute route : vehicleRoutes) {
removed = jobRemover.removeJobWithoutTourUpdate(targetJob, route);
if (removed) {
nOfJobs2BeRemoved--;
unassignedJobs.add(targetJob);
break;
}
}
}
ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
return unassignedJobs;
}
public void setRuinFraction(double fractionOfAllNodes2beRuined) {
this.fractionOfAllNodes2beRuined = fractionOfAllNodes2beRuined;
logger.info("fraction set " + this);
}
private void ruin(Collection<VehicleRoute> vehicleRoutes,int nOfJobs2BeRemoved, List<Job> unassignedJobs) {
LinkedList<Job> availableJobs = new LinkedList<Job>(vrp.getJobs().values());
for (int i = 0; i < nOfJobs2BeRemoved; i++) {
Job job = pickRandomJob(availableJobs);
unassignedJobs.add(job);
availableJobs.remove(job);
for (VehicleRoute route : vehicleRoutes) {
boolean removed = jobRemover.removeJobWithoutTourUpdate(job, route);
if (removed) break;
}
}
updateRoutes(vehicleRoutes);
}
private void updateRoutes(Collection<VehicleRoute> vehicleRoutes) {
for(VehicleRoute route : vehicleRoutes){
vehicleRouteUpdater.updateRoute(route);
}
}
@Override
public String toString() {
return "[name=randomRuin][fraction="+fractionOfAllNodes2beRuined+"]";
}
private Job pickRandomJob(LinkedList<Job> availableJobs) {
int randomIndex = random.nextInt(availableJobs.size());
return availableJobs.get(randomIndex);
}
private int selectNuOfJobs2BeRemoved() {
return (int) Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined);
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.List;
import basics.Job;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
interface RuinStrategy {
public static interface RuinListener {
}
/**
* Ruins a current solution, i.e. removes jobs from service providers and
* returns a collection of these removed, and thus unassigned, jobs.
*
* @param vehicleRoutes
* @return
*/
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes);
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved);
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
interface RuinStrategyFactory {
public RuinStrategy createStrategy(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.net.URL;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
/**
* Factory that creates the {@link VehicleRoutingAlgorithm} as proposed by Schrimpf et al., 2000 with the following parameters:
*
* <p>
* R&R_random (prob=0.5, F=0.5);
* R&R_radial (prob=0.5, F=0.3);
* threshold-accepting with exponentialDecayFunction (alpha=0.1, warmup-iterations=100);
* nuOfIterations=2000
*
* <p>Gerhard Schrimpf, Johannes Schneider, Hermann Stamm- Wilbrandt, and Gunter Dueck.
* Record breaking optimization results using the ruin and recreate principle.
* Journal of Computational Physics, 159(2):139 171, 2000. ISSN 0021-9991. doi: 10.1006/jcph.1999. 6413.
* URL http://www.sciencedirect.com/science/article/ pii/S0021999199964136
*
* <p>algorithm-xml-config is available at src/main/resources/schrimpf.xml.
*
* @author stefan schroeder
*
*/
public class SchrimpfFactory {
/**
* Creates the {@link VehicleRoutingAlgorithm}.
*
* @param vrp
* @return algorithm
*/
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
URL resource = this.getClass().getClassLoader().getResource("schrimpf.xml");
new AlgorithmConfigXmlReader(algorithmConfig).read(resource.getPath());
return VehicleRoutingAlgorithms.createAlgorithm(vrp, algorithmConfig);
}
}

View file

@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
class ScoredJob {
private final Job job;
private final double score;
private final InsertionData insertionData;
private final VehicleRoute route;
public ScoredJob(final Job job, final double score, final InsertionData insertionData, final VehicleRoute route) {
super();
this.job = job;
this.score = score;
this.insertionData = insertionData;
this.route = route;
}
public InsertionData getInsertionData() {
return insertionData;
}
public VehicleRoute getRoute() {
return route;
}
public Job getJob() {
return job;
}
public double getScore() {
return score;
}
}

View file

@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.route.VehicleRoute;
class SolutionVerifier implements AlgorithmEndsListener{
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(VehicleRoutingProblemSolution solution : solutions){
Set<Job> jobsInSolution = new HashSet<Job>();
for(VehicleRoute route : solution.getRoutes()){
jobsInSolution.addAll(route.getTourActivities().getJobs());
}
if(jobsInSolution.size() != problem.getJobs().size()){
throw new IllegalStateException("we are at the end of the algorithm and still have not found a valid solution." +
"This cannot be.");
}
}
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
class TourConstraintEngine {
// void ini(VehicleRoute route, Job job){
//
// }
//
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.VehicleRoute;
/**
* Updates tour state, i.e. the tour as well as each activity in that tour has a state such as currentLoad, currentCost, earliestOperationTime and
* latestOperationTime. Each time the tour is changed (for instance by removing or adding an activity), tour and activity states
* might change, thus this updater updates activity states.
* This includes:
* - update load and totalCost at tour-level
* - update currentLoad and currentCost at activity-level
* - update earliest- and latestOperationStart values at activity-level
*
* If ensureFeasibility is true, then it additionally checks whether the earliestOperationStartTime is higher than the latestOperationStartTime.
* If it is, it returns a false value to indicate that the tour is not feasible. This makes only sense for hard-timewindows.
*
* If softTimeWindow is set to true, latestOperationStartTimes are not updated and the tour is always feasible.
*
* @author stefan schroeder
*
*/
class TourStateUpdater implements VehicleRouteUpdater{
// public final static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger logger = Logger.getLogger(TourStateUpdater.class);
private boolean ensureFeasibility = true;
private UpdateTourStatesForwardInTime forwardUpdate;
private UpdateTourStatesBackwardInTime backwardUpdate;
private boolean updateTimeWindows = true;
private RouteStates actStates;
public TourStateUpdater(RouteStates activityStates, VehicleRoutingTransportCosts costs, VehicleRoutingActivityCosts costFunction) {
super();
forwardUpdate = new UpdateTourStatesForwardInTime(costs, costs, costFunction);
backwardUpdate = new UpdateTourStatesBackwardInTime(costs);
actStates=activityStates;
forwardUpdate.setActivityStates(actStates);
backwardUpdate.setActivityStates(actStates);
}
/*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
if(updateTimeWindows){
backwardUpdate.checkFeasibility = ensureFeasibility;
backwardUpdate.updateRoute(vehicleRoute);
}
forwardUpdate.updateRoute(vehicleRoute);
boolean tourIsFeasible = true;
return tourIsFeasible;
}
public void setTimeWindowUpdate(boolean updateTimeWindows) {
this.updateTimeWindows = updateTimeWindows;
logger.info("set timeWindowUpdate to " + updateTimeWindows);
}
public void setEnsureFeasibility(boolean ensureFeasibility) {
this.ensureFeasibility = ensureFeasibility;
}
}

View file

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Iterator;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.BackwardTransportTime;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
class UpdateTourStatesBackwardInTime implements VehicleRouteUpdater{
// public static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger log = Logger.getLogger(UpdateTourStatesBackwardInTime.class);
public boolean checkFeasibility = true;
private BackwardTransportTime transportTime;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
public UpdateTourStatesBackwardInTime(BackwardTransportTime transportTime) {
super();
this.transportTime = transportTime;
}
/*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
boolean ok = update(vehicleRoute);
return ok;
}
private boolean update(VehicleRoute vehicleRoute) {
TourActivities tour = vehicleRoute.getTourActivities();
int tourSize = tour.getActivities().size();
Iterator<TourActivity> reverseActIter = vehicleRoute.getTourActivities().reverseActivityIterator();
TourActivity prevAct;
boolean feasible = true;
prevAct = vehicleRoute.getEnd();
double startAtPrevAct = prevAct.getTheoreticalLatestOperationStartTime();
int count = 0;
while(reverseActIter.hasNext()){
TourActivity currAct = reverseActIter.next();
double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, currAct, startAtPrevAct);
ActivityState state = state(currAct);
state.setLatestOperationStart(latestOperationStartTime);
prevAct = currAct;
startAtPrevAct = latestOperationStartTime;
count++;
}
// Start start = vehicleRoute.getStart();
// double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, start, startAtPrevAct);
assert count == tourSize;
return feasible;
}
private double latestOperationStartTime(VehicleRoute vehicleRoute,
TourActivity prevAct, TourActivity currAct, double startAtPrevAct) {
double latestDepTimeAtCurrAct = startAtPrevAct - transportTime.getBackwardTransportTime(currAct.getLocationId(), prevAct.getLocationId(), startAtPrevAct, vehicleRoute.getDriver(),vehicleRoute.getVehicle());
double potentialLatestOperationStartTimeAtCurrAct = latestDepTimeAtCurrAct - currAct.getOperationTime();
double latestOperationStartTime = Math.min(currAct.getTheoreticalLatestOperationStartTime(), potentialLatestOperationStartTimeAtCurrAct);
return latestOperationStartTime;
}
}

View file

@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.ForwardTransportCost;
import basics.costs.ForwardTransportTime;
import basics.costs.VehicleRoutingActivityCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.ServiceActivity;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
/**
*
* @author sschroeder
*
*/
class UpdateTourStatesForwardInTime implements VehicleRouteUpdater{
// public static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger log = Logger.getLogger(UpdateTourStatesForwardInTime.class);
public boolean checkFeasibility = true;
private VehicleRoutingActivityCosts activityCost;
private ForwardTransportTime transportTime;
private ForwardTransportCost transportCost;
private RouteStates routeStates;
private boolean activityStatesSet = false;
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
activityStatesSet = true;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
public UpdateTourStatesForwardInTime(ForwardTransportTime transportTime, ForwardTransportCost transportCost, VehicleRoutingActivityCosts activityCost) {
super();
this.transportTime = transportTime;
this.transportCost = transportCost;
this.activityCost = activityCost;
}
/**
*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
vehicleRoute.getVehicleRouteCostCalculator().reset();
Vehicle vehicle = vehicleRoute.getVehicle();
Driver driver = vehicleRoute.getDriver();
TourActivity prevAct = vehicleRoute.getStart();
double startAtPrevAct = vehicleRoute.getStart().getEndTime();
double totalOperationCost = 0.0;
int totalLoadPicked = 0;
int currentLoadState = 0;
for(TourActivity currentAct : vehicleRoute.getTourActivities().getActivities()){
totalLoadPicked += getPickedLoad(currentAct);
currentLoadState += getCapDemand(currentAct);
double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
double operationStartTime = Math.max(currentAct.getTheoreticalEarliestOperationStartTime(), arrivalTimeAtCurrAct);
double operationEndTime = operationStartTime + currentAct.getOperationTime();
currentAct.setArrTime(arrivalTimeAtCurrAct);
currentAct.setEndTime(operationEndTime);
double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double actCost = activityCost.getActivityCost(currentAct, arrivalTimeAtCurrAct, driver, vehicle);
vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
vehicleRoute.getVehicleRouteCostCalculator().addActivityCost(actCost);
totalOperationCost += transportCost;
totalOperationCost += actCost;
if(activityStatesSet){
ActivityState currentState = state(currentAct);
currentState.setEarliestOperationStart(operationStartTime);
currentState.setCurrentLoad(currentLoadState);
currentState.setCurrentCost(totalOperationCost);
}
prevAct = currentAct;
startAtPrevAct = operationEndTime;
}
End currentAct = vehicleRoute.getEnd();
double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
currentAct.setArrTime(arrivalTimeAtCurrAct);
currentAct.setEndTime(arrivalTimeAtCurrAct);
totalOperationCost += transportCost;
routeStates.getRouteState(vehicleRoute).setCosts(totalOperationCost);
routeStates.getRouteState(vehicleRoute).setLoad(totalLoadPicked);
vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getDriver());
vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getVehicle());
vehicleRoute.getVehicleRouteCostCalculator().finish();
return true;
}
private int getCapDemand(TourActivity currentAct) {
return currentAct.getCapacityDemand();
}
private double getPickedLoad(TourActivity currentAct) {
if(currentAct instanceof ServiceActivity){
return currentAct.getCapacityDemand();
}
// else if(currentAct instanceof Pickup){
// return currentAct.getCapacityDemand();
// }
return 0.0;
}
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.VehicleType;
interface VehicleFleetManager {
public static class TypeKey {
public final VehicleType type;
public final String locationId;
public TypeKey(VehicleType type, String locationId) {
super();
this.type = type;
this.locationId = locationId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((locationId == null) ? 0 : locationId.hashCode());
result = prime * result + ((type == null) ? 0 : type.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;
TypeKey other = (TypeKey) obj;
if (locationId == null) {
if (other.locationId != null)
return false;
} else if (!locationId.equals(other.locationId))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}
public abstract Vehicle getEmptyVehicle(TypeKey typeId);
public abstract Collection<TypeKey> getAvailableVehicleTypes();
public abstract void lock(Vehicle vehicle);
public abstract void unlock(Vehicle vehicle);
public abstract Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType);
public abstract boolean isLocked(Vehicle vehicle);
public abstract void unlockAll();
}

View file

@ -0,0 +1,247 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
class VehicleFleetManagerImpl implements VehicleFleetManager {
public VehicleFleetManagerImpl newInstance(Collection<Vehicle> vehicles){
return new VehicleFleetManagerImpl(vehicles);
}
public static VehicleFleetManager createDefaultFleetManager() {
return new DefaultFleetManager();
}
public static class DefaultFleetManager extends VehicleFleetManagerImpl {
public DefaultFleetManager() {
super(Collections.EMPTY_LIST);
}
}
static class TypeContainer {
private TypeKey type;
private ArrayList<Vehicle> vehicleList;
public TypeContainer(TypeKey type) {
super();
this.type = type;
vehicleList = new ArrayList<Vehicle>();
}
void add(Vehicle vehicle){
if(vehicleList.contains(vehicle)){
throw new IllegalStateException("cannot add vehicle twice " + vehicle.getId());
}
vehicleList.add(vehicle);
}
void remove(Vehicle vehicle){
vehicleList.remove(vehicle);
}
public Vehicle getVehicle() {
return vehicleList.get(0);
// return vehicleList.getFirst();
}
public boolean isEmpty() {
return vehicleList.isEmpty();
}
}
private static Logger logger = Logger.getLogger(VehicleFleetManagerImpl.class);
private Collection<Vehicle> vehicles;
private Set<Vehicle> lockedVehicles;
private Map<TypeKey,TypeContainer> typeMapOfAvailableVehicles;
public VehicleFleetManagerImpl(Collection<Vehicle> vehicles) {
super();
this.vehicles = vehicles;
this.lockedVehicles = new HashSet<Vehicle>();
makeMap();
logger.info("initialise " + this);
}
public VehicleFleetManagerImpl(Collection<Vehicle> vehicles, Collection<Vehicle> lockedVehicles) {
this.vehicles = vehicles;
makeMap();
this.lockedVehicles = new HashSet<Vehicle>();
for(Vehicle v : lockedVehicles){
lock(v);
}
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=finiteVehicles]";
}
private void makeMap() {
typeMapOfAvailableVehicles = new HashMap<TypeKey, TypeContainer>();
for(Vehicle v : vehicles){
addVehicle(v);
}
}
private void addVehicle(Vehicle v) {
if(v.getType() == null){
throw new IllegalStateException("vehicle needs type");
}
// String typeId = v.getType().typeId;
TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
if(!typeMapOfAvailableVehicles.containsKey(typeKey)){
typeMapOfAvailableVehicles.put(typeKey, new TypeContainer(typeKey));
}
typeMapOfAvailableVehicles.get(typeKey).add(v);
}
private void removeVehicle(Vehicle v){
TypeKey key = new TypeKey(v.getType(),v.getLocationId());
if(typeMapOfAvailableVehicles.containsKey(key)){
typeMapOfAvailableVehicles.get(key).remove(v);
}
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getEmptyVehicle(java.lang.String)
*/
@Override
public Vehicle getEmptyVehicle(TypeKey typeId){
Vehicle v = null;
if(typeMapOfAvailableVehicles.containsKey(typeId)){
v = typeMapOfAvailableVehicles.get(typeId).getVehicle();
}
return v;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes()
*/
@Override
public Collection<TypeKey> getAvailableVehicleTypes(){
List<TypeKey> types = new ArrayList<TypeKey>();
for(TypeKey key : typeMapOfAvailableVehicles.keySet()){
if(!typeMapOfAvailableVehicles.get(key).isEmpty()){
types.add(key);
}
}
return types;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#lock(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public void lock(Vehicle vehicle){
if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
return;
}
boolean locked = lockedVehicles.add(vehicle);
removeVehicle(vehicle);
if(!locked){
throw new IllegalStateException("cannot lock vehicle twice " + vehicle.getId());
}
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlock(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public void unlock(Vehicle vehicle){
if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
return;
}
if(vehicle == null) return;
lockedVehicles.remove(vehicle);
addVehicle(vehicle);
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes(java.lang.String)
*/
@Override
public Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType) {
List<TypeKey> types = new ArrayList<TypeKey>();
for(TypeKey typeKey : typeMapOfAvailableVehicles.keySet()){
if(typeKey.equals(withoutThisType)){
continue;
}
if(!typeMapOfAvailableVehicles.get(typeKey).isEmpty()){
types.add(typeKey);
}
}
return types;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#isLocked(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public boolean isLocked(Vehicle vehicle) {
return lockedVehicles.contains(vehicle);
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlockAll()
*/
@Override
public void unlockAll() {
Collection<Vehicle> locked = new ArrayList<Vehicle>(lockedVehicles);
for(Vehicle v : locked){
unlock(v);
}
if(!lockedVehicles.isEmpty()){
throw new IllegalStateException("no vehicle must be locked");
}
}
public int sizeOfLockedVehicles(){
return lockedVehicles.size();
}
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.VehicleRoute;
/**
* Updater that updates a vehicleRoute, e.g. the total costs or the time-windows.
*
* @author stefan schroeder
*
*/
interface VehicleRouteUpdater {
public boolean updateRoute(VehicleRoute vehicleRoute);
}

View file

@ -0,0 +1,870 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import util.RouteUtils;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractInsertionKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AcceptorKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.RuinStrategyKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.SelectorKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.StrategyModuleKey;
import algorithms.acceptors.AcceptNewIfBetterThanWorst;
import algorithms.acceptors.AcceptNewRemoveFirst;
import algorithms.acceptors.SchrimpfAcceptance;
import algorithms.acceptors.SolutionAcceptor;
import algorithms.selectors.SelectBest;
import algorithms.selectors.SelectRandomly;
import algorithms.selectors.SolutionSelector;
import basics.Job;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetSize;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.InsertionListener;
import basics.algo.SearchStrategy;
import basics.algo.SearchStrategyManager;
import basics.algo.SearchStrategyModule;
import basics.algo.SearchStrategyModuleListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
import basics.route.VehicleRoute;
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 AbstractInsertionKey implements AbstractKey<AbstractInsertionStrategy>{
private ModKey modKey;
public AbstractInsertionKey(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;
AbstractInsertionKey other = (AbstractInsertionKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<AbstractInsertionStrategy> getType() {
return AbstractInsertionStrategy.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 = Logger.getLogger(VehicleRoutingAlgorithms.class);
private VehicleRoutingAlgorithms(){}
public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp, final AlgorithmConfig algorithmConfig){
return createAlgo(vrp,algorithmConfig.getXMLConfiguration());
}
@Deprecated
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final XMLConfiguration config){
return createAlgo(vrp,config);
}
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());
}
private static VehicleRoutingAlgorithm createAlgo(final VehicleRoutingProblem vrp, XMLConfiguration config){
//fleetmanager
final VehicleFleetManager vehicleFleetManager;
if(vrp.getFleetSize().equals(FleetSize.INFINITE)){
vehicleFleetManager = new InfiniteVehicles(vrp.getVehicles());
}
else if(vrp.getFleetSize().equals(FleetSize.FINITE)){
vehicleFleetManager = new VehicleFleetManagerImpl(vrp.getVehicles());
}
else{
throw new IllegalStateException("fleet size can only be infinite or finite. " +
"makes sure your config file contains one of these options");
}
Set<PrioritizedVRAListener> algorithmListeners = new HashSet<PrioritizedVRAListener>();
List<InsertionListener> insertionListeners = new ArrayList<InsertionListener>();
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, new SolutionVerifier()));
RouteStates routeStates = new RouteStates();
routeStates.initialiseStateOfJobs(vrp.getJobs().values());
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, routeStates));
TypedMap definedClasses = new TypedMap();
/*
* initial solution - construction
*/
AlgorithmStartsListener createInitialSolution = createInitialSolution(config,vrp,vehicleFleetManager,routeStates,algorithmListeners,definedClasses);
if(createInitialSolution != null) algorithmListeners.add(new PrioritizedVRAListener(Priority.MEDIUM, createInitialSolution));
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);
strategy.setName(name);
List<HierarchicalConfiguration> modulesConfig = strategyConfig.configurationsAt("modules.module");
for(HierarchicalConfiguration moduleConfig : modulesConfig){
SearchStrategyModule module = buildModule(moduleConfig,vrp,vehicleFleetManager,routeStates,algorithmListeners,definedClasses);
strategy.addModule(module);
}
searchStratManager.addStrategy(strategy, strategyConfig.getDouble("probability"));
}
VehicleRoutingAlgorithm metaAlgorithm = new VehicleRoutingAlgorithm(vrp, searchStratManager);
if(config.containsKey("iterations")){
metaAlgorithm.setNuOfIterations(config.getInt("iterations"));
}
registerListeners(metaAlgorithm,algorithmListeners);
registerInsertionListeners(definedClasses,insertionListeners);
return metaAlgorithm;
}
private static void registerInsertionListeners(TypedMap definedClasses, List<InsertionListener> insertionListeners) {
for(AbstractKey<?> key : definedClasses.keySet()){
if(key instanceof AbstractInsertionKey){
AbstractInsertionKey insertionKey = (AbstractInsertionKey) key;
AbstractInsertionStrategy insertionStrategy = definedClasses.get(insertionKey);
for(InsertionListener l : insertionListeners){
// log.info("add insertionListener " + l + " to " + insertionStrategy);
insertionStrategy.addListener(l);
}
}
}
// log.warn("cannot register insertion listeners yet");
}
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, RouteStates activityStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses) {
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);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(modKey);
AbstractInsertionStrategy insertionStrategy = definedClasses.get(insertionStrategyKey);
if(insertionStrategy == null){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertionStrategy = createInsertionStrategy(modConfig, vrp, vehicleFleetManager, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
definedClasses.put(insertionStrategyKey,insertionStrategy);
}
final AbstractInsertionStrategy finalInsertionStrategy = insertionStrategy;
return new AlgorithmStartsListener() {
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
CreateInitialSolution createInitialSolution = new CreateInitialSolution(finalInsertionStrategy);
createInitialSolution.setGenerateAsMuchAsRoutesAsVehiclesExist(false);
VehicleRoutingProblemSolution vrpSol = createInitialSolution.createInitialSolution(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 \"selectRandom\" 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("selectRandom")){
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 \"selectRandom\" 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")){
AcceptNewIfBetterThanWorst acceptor = new AcceptNewIfBetterThanWorst(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("schrimpfAcceptance")){
int iterOfSchrimpf = strategyConfig.getInt("acceptor.warmup");
double alpha = strategyConfig.getDouble("acceptor.alpha");
SchrimpfAcceptance schrimpf = new SchrimpfAcceptance(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, VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager,
RouteStates activityStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses) {
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, activityStates, definedClasses, ruinKey, shareToRuin);
}
else if(ruin_name.equals("radialRuin")){
ruin = getRadialRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin);
}
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);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(insertionKey);
AbstractInsertionStrategy 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, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
}
final AbstractInsertionStrategy final_insertion = insertion;
SearchStrategyModule module = new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
Collection<Job> ruinedJobs = ruin.ruin(vrpSolution.getRoutes());
final_insertion.run(vrpSolution.getRoutes(), ruinedJobs, Double.MAX_VALUE);
double totalCost = RouteUtils.getTotalCost(vrpSolution.getRoutes());
vrpSolution.setCost(totalCost);
return vrpSolution;
}
@Override
public String toString() {
return getName();
}
@Override
public String getName() {
return "[name=ruin_and_recreate][ruin="+ruin+"][recreate="+final_insertion+"]";
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
if(moduleListener instanceof InsertionListener){
InsertionListener iListener = (InsertionListener) moduleListener;
if(!final_insertion.getListener().contains(iListener)){
logger.info("register moduleListener " + moduleListener);
final_insertion.addListener(iListener);
}
}
}
};
return module;
}
if(moduleName.equals("bestInsertion") || moduleName.equals("regretInsertion")){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
AbstractInsertionStrategy insertion = getInsertionStrategy(moduleConfig, vrp, vehicleFleetManager, activityStates,
definedClasses, modKey, prioListeners);
SearchStrategyModule module = getModule(moduleName, insertion, vrp);
definedClasses.put(strategyModuleKey, module);
algorithmListeners.addAll(prioListeners);
return module;
}
// if(moduleName.equals("regretInsertion")){
// List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
// AbstractInsertionKey insertionKey = new AbstractInsertionKey(modKey);
// AbstractInsertionStrategy regretInsertion = definedClasses.get(insertionKey);
// if(regretInsertion == null){
// regretInsertion = createInsertionStrategy(moduleConfig,vrp,vehicleFleetManager,activityStates,prioListeners);
// }
// SearchStrategyModule module = getModule(moduleName, regretInsertion, vrp);
// definedClasses.put(strategyModuleKey, module);
// algorithmListeners.addAll(prioListeners);
// return module;
// }
if(moduleName.equals("randomRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRandomRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
if(moduleName.equals("radialRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRadialRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
if(moduleName.equals("gendreauPostOpt")){
int iterations = moduleConfig.getInt("iterations");
double share = moduleConfig.getDouble("share");
String ruinName = moduleConfig.getString("ruin[@name]");
if(ruinName == null) throw new IllegalStateException("gendreauPostOpt.ruin[@name] is missing. set it to \"radialRuin\" or \"randomRuin\"");
String ruinId = moduleConfig.getString("ruin[@id]");
if(ruinId == null) ruinId = "noId";
ModKey ruinKey = makeKey(ruinName,ruinId);
RuinStrategyKey stratKey = new RuinStrategyKey(ruinKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRadial.newInstance(vrp, 0.3, new JobDistanceAvgCosts(vrp.getTransportCosts()), new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
String insertionName = moduleConfig.getString("insertion[@name]");
if(insertionName == null) throw new IllegalStateException("gendreauPostOpt.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);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(insertionKey);
AbstractInsertionStrategy 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, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
}
GendreauPostOpt postOpt = new GendreauPostOpt(vrp, ruin, insertion);
postOpt.setShareOfJobsToRuin(share);
postOpt.setNuOfIterations(iterations);
postOpt.setFleetManager(vehicleFleetManager);
definedClasses.put(strategyModuleKey, postOpt);
return postOpt;
}
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" +
"\n\tgendreauPostOpt");
}
private static AbstractInsertionStrategy getInsertionStrategy(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp,VehicleFleetManager vehicleFleetManager,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, List<PrioritizedVRAListener> prioListeners) {
AbstractInsertionKey insertionKey = new AbstractInsertionKey(modKey);
AbstractInsertionStrategy bestInsertion = definedClasses.get(insertionKey);
if(bestInsertion == null){
bestInsertion = createInsertionStrategy(moduleConfig,vrp,vehicleFleetManager,activityStates,prioListeners);
}
return bestInsertion;
}
private static RuinStrategy getRadialRuin(VehicleRoutingProblem vrp,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, double shareToRuin) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRadial.newInstance(vrp, shareToRuin, new JobDistanceAvgCosts(vrp.getTransportCosts()), new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static RuinStrategy getRandomRuin(VehicleRoutingProblem vrp,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, double shareToRuin) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRandom.newInstance(vrp, shareToRuin, new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static AbstractInsertionStrategy createInsertionStrategy(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp,VehicleFleetManager vehicleFleetManager, RouteStates activityStates, List<PrioritizedVRAListener> algorithmListeners) {
AbstractInsertionStrategy insertion = InsertionFactory.createInsertion(vrp, moduleConfig, vehicleFleetManager, activityStates, algorithmListeners);
return insertion;
}
private static SearchStrategyModule getModule(final String moduleName, final AbstractInsertionStrategy insertion, final VehicleRoutingProblem vrp){
return new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
List<Job> unassignedJobs = getUnassignedJobs(vrpSolution,vrp);
// logger.info("unassigned: " + unassignedJobs.size());
insertion.run(vrpSolution.getRoutes(), unassignedJobs, Double.MAX_VALUE);
int jobsInSolution = countJobs(vrpSolution);
double penalty = 0.0;
if(jobsInSolution != vrp.getJobs().values().size()){
throw new IllegalStateException("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
//// throw new IllegalStateException("solution not valid\n" +
//// "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("a penalty of 1000 is added for each unassigned customer");
// penalty = (vrp.getJobs().values().size() - jobsInSolution)*1000.0;
// logger.warn("penalty = " + penalty);
}
double totalCost = RouteUtils.getTotalCost(vrpSolution.getRoutes());
vrpSolution.setCost(totalCost + penalty);
return vrpSolution;
}
@Override
public String toString() {
return "[name="+insertion+"]";
}
private int countJobs(VehicleRoutingProblemSolution vrpSolution) {
int counter = 0;
for(VehicleRoute route : vrpSolution.getRoutes()){
counter += route.getTourActivities().jobSize();
}
return counter;
}
private List<Job> getUnassignedJobs(VehicleRoutingProblemSolution vrpSolution,VehicleRoutingProblem vrp) {
List<Job> unassignedJobs = new ArrayList<Job>();
for(Job j : vrp.getJobs().values()){
boolean notAssigned = true;
for(VehicleRoute r : vrpSolution.getRoutes()){
if(r.getTourActivities().servesJob(j)){
notAssigned = false;
break;
}
}
if(notAssigned){
unassignedJobs.add(j);
}
}
return unassignedJobs;
}
@Override
public String getName() {
return moduleName;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
if(moduleListener instanceof InsertionListener){
InsertionListener iListener = (InsertionListener) moduleListener;
if(!insertion.getListener().contains(iListener)){
logger.info("register moduleListener " + moduleListener);
insertion.addListener(iListener);
}
}
}
};
}
private static SearchStrategyModule getModule(final String moduleName, final RuinStrategy ruin) {
return new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
ruin.ruin(vrpSolution.getRoutes());
return vrpSolution;
}
@Override
public String toString() {
return "[name="+ruin+"]";
}
@Override
public String getName() {
return moduleName;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// TODO Auto-generated method stub
}
};
}
}

View file

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.Vehicle;
import algorithms.RouteAlgorithm.VehicleSwitchedListener;
class VehicleSwitched implements VehicleSwitchedListener{
private VehicleFleetManager fleetManager;
VehicleSwitched(VehicleFleetManager fleetManager){
this.fleetManager = fleetManager;
}
@Override
public void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle) {
fleetManager.unlock(oldVehicle);
fleetManager.lock(newVehicle);
}
}

View file

@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class AcceptNewIfBetterThanWorst implements SolutionAcceptor{
private final int solutionMemory;
public AcceptNewIfBetterThanWorst(int solutionMemory){
this.solutionMemory = solutionMemory;
}
/**
* Accepts every solution if solution memory allows. If memory occupied, than accepts new solution only if better than the worst in memory.
* Consequently, the worst solution is removed from solutions, and the new solution added.
*
* <p>Note that this modifies Collection<VehicleRoutingProblemSolution> solutions.
*/
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
boolean solutionAccepted = false;
if (solutions.size() < solutionMemory) {
solutions.add(newSolution);
solutionAccepted = true;
} else {
VehicleRoutingProblemSolution worstSolution = null;
for (VehicleRoutingProblemSolution s : solutions) {
if (worstSolution == null) worstSolution = s;
else if (s.getCost() > worstSolution.getCost()) worstSolution = s;
}
if(newSolution.getCost() < worstSolution.getCost()){
solutions.remove(worstSolution);
solutions.add(newSolution);
solutionAccepted = true;
}
}
return solutionAccepted;
}
@Override
public String toString() {
return "[name=acceptNewRemoveWorst]";
}
}

View file

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class AcceptNewRemoveFirst implements SolutionAcceptor{
private final int solutionMemory;
public AcceptNewRemoveFirst(int solutionMemory){
this.solutionMemory = solutionMemory;
}
/**
* Accepts every solution if solution memory allows. If memory occupied, than accepts new solution only if better than the worst in memory.
* Consequently, the worst solution is removed from solutions, and the new solution added.
*
* <p>Note that this modifies Collection<VehicleRoutingProblemSolution> solutions.
*/
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
if (solutions.size() >= solutionMemory) {
solutions.remove(solutions.iterator().next());
}
solutions.add(newSolution);
return true;
}
@Override
public String toString() {
return "[name=acceptNewRemoveFirst]";
}
}

View file

@ -0,0 +1,155 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.net.URL;
import java.util.Collection;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.log4j.Logger;
import util.Solutions;
import algorithms.VehicleRoutingAlgorithms;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
public class SchrimpfAcceptance implements SolutionAcceptor, IterationStartsListener, AlgorithmStartsListener{
private static Logger logger = Logger.getLogger(SchrimpfAcceptance.class);
private final double alpha;
private int nOfTotalIterations = 1000;
private int currentIteration = 0;
private double initialThreshold = 0.0;
private final int nOfRandomWalks;
private final int solutionMemory;
public SchrimpfAcceptance(int solutionMemory, double alpha, int nOfWarmupIterations) {
super();
this.alpha = alpha;
this.nOfRandomWalks = nOfWarmupIterations;
this.solutionMemory = solutionMemory;
logger.info("initialise " + this);
}
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
boolean solutionAccepted = false;
if (solutions.size() < solutionMemory) {
solutions.add(newSolution);
solutionAccepted = true;
} else {
VehicleRoutingProblemSolution worst = null;
double threshold = getThreshold(currentIteration);
for(VehicleRoutingProblemSolution solutionInMemory : solutions){
if(worst == null) worst = solutionInMemory;
else if(solutionInMemory.getCost() > worst.getCost()) worst = solutionInMemory;
}
if(newSolution.getCost() < worst.getCost() + threshold){
solutions.remove(worst);
solutions.add(newSolution);
solutionAccepted = true;
}
}
return solutionAccepted;
}
@Override
public String toString() {
return "[name=schrimpfAcceptanceFunction][alpha="+alpha+"][warmup=" + nOfRandomWalks + "]";
}
private double getThreshold(int iteration) {
double scheduleVariable = (double) iteration / (double) nOfTotalIterations;
// logger.debug("iter="+iteration+" totalIter="+nOfTotalIterations+" scheduling="+scheduleVariable);
double currentThreshold = initialThreshold * Math.exp(-Math.log(2) * scheduleVariable / alpha);
return currentThreshold;
}
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
reset();
logger.info("---------------------------------------------------------------------");
logger.info("prepare schrimpfAcceptanceFunction, i.e. determine initial threshold");
logger.info("start random-walk (see algorith-config at scr/main/resources/randomWalk.xml)");
double now = System.currentTimeMillis();
this.nOfTotalIterations = algorithm.getNuOfIterations();
/*
* randomWalk to determine standardDev
*/
final double[] results = new double[nOfRandomWalks];
URL resource = this.getClass().getClassLoader().getResource("randomWalk.xml");
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
new AlgorithmConfigXmlReader(algorithmConfig).read(resource.getPath());
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.createAlgorithm(problem, algorithmConfig);
vra.setNuOfIterations(nOfRandomWalks);
vra.getAlgorithmListeners().addListener(new IterationEndsListener() {
@Override
public void informIterationEnds(int iteration, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
double result = Solutions.getBest(solutions).getCost();
// logger.info("result="+result);
results[iteration-1] = result;
}
});
vra.searchSolutions();
StandardDeviation dev = new StandardDeviation();
double standardDeviation = dev.evaluate(results);
initialThreshold = standardDeviation / 2;
logger.info("warmup done");
logger.info("total time: " + ((System.currentTimeMillis()-now)/1000.0) + "s");
logger.info("initial threshold: " + initialThreshold);
logger.info("---------------------------------------------------------------------");
}
private void reset() {
currentIteration = 0;
}
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
currentIteration = i;
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
/**
* Acceptor that decides whether the newSolution is accepted or not.
*
*
* @author stefan
*
*/
public interface SolutionAcceptor {
/**
* Accepts solution or not, and returns true if a new solution has been accepted.
*
* <p>If the solution is accepted, it is added to solutions, i.e. the solutions-collections is modified.
*
* @param solutions
* @param newSolution
* @return TODO
*/
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution);
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class SelectBest implements SolutionSelector{
private static SelectBest selector = null;
public static SelectBest getInstance(){
if(selector == null){
selector = new SelectBest();
return selector;
}
return selector;
}
@Override
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions) {
double minCost = Double.MAX_VALUE;
VehicleRoutingProblemSolution bestSolution = null;
for(VehicleRoutingProblemSolution sol : solutions){
if(bestSolution == null){
bestSolution = sol;
minCost = sol.getCost();
}
else if(sol.getCost() < minCost){
bestSolution = sol;
minCost = sol.getCost();
}
}
return bestSolution;
}
@Override
public String toString() {
return "[name=selectBest]";
}
}

View file

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import basics.VehicleRoutingProblemSolution;
import util.RandomNumberGeneration;
public class SelectRandomly implements SolutionSelector{
private static SelectRandomly selector = null;
public static SelectRandomly getInstance(){
if(selector == null){
selector = new SelectRandomly();
return selector;
}
return selector;
}
private Random random = RandomNumberGeneration.getRandom();
@Override
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions) {
if(solutions.isEmpty()) return null;
List<VehicleRoutingProblemSolution> solList = new ArrayList<VehicleRoutingProblemSolution>(solutions);
int randomIndex = random.nextInt(solutions.size());
return solList.get(randomIndex);
}
public void setRandom(Random random) {
this.random = random;
}
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public interface SolutionSelector {
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
public interface Job {
public String getId();
public int getCapacityDemand();
}

View file

@ -0,0 +1,183 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import basics.route.TimeWindow;
import util.Coordinate;
public class Service implements Job {
public static class Builder {
public static Builder newInstance(String id, int size){
return new Builder(id,size);
}
private String id;
private String locationId;
private String name = "service";
private Coordinate coord;
private double serviceTime;
private TimeWindow timeWindow = TimeWindow.newInstance(0.0, Double.MAX_VALUE);
private int demand;
private Builder(String id, int size) {
super();
this.id = id;
this.demand = size;
}
public Builder setName(String name){
this.name = name;
return this;
}
public Builder setLocationId(String locationId){
this.locationId = locationId;
return this;
}
public Builder setCoord(Coordinate coord){
this.coord = coord;
return this;
}
public Builder setServiceTime(double serviceTime){
this.serviceTime = serviceTime;
return this;
}
public Builder setTimeWindow(TimeWindow tw){
this.timeWindow = tw;
return this;
}
public Service build(){
if(locationId == null) {
if(coord == null) throw new IllegalStateException("either locationId or a coordinate must be given. But is not.");
locationId = coord.toString();
}
return new Service(this);
}
}
private final String id;
private final String locationId;
private final String name;
private final Coordinate coord;
private final double serviceTime;
private final TimeWindow timeWindow;
private final int demand;
private Service(Builder builder){
id = builder.id;
locationId = builder.locationId;
coord = builder.coord;
serviceTime = builder.serviceTime;
timeWindow = builder.timeWindow;
demand = builder.demand;
name = builder.name;
}
@Override
public String getId() {
return id;
}
public String getLocationId() {
return locationId;
}
public Coordinate getCoord(){
return coord;
}
public double getServiceDuration() {
return serviceTime;
}
public TimeWindow getTimeWindow(){
return timeWindow;
}
@Override
public int getCapacityDemand() {
return demand;
}
/**
* @return the name
*/
public String getType() {
return name;
}
@Override
public String toString() {
return "[id=" + id + "][locationId=" + locationId + "][coord="+coord+"][size=" + demand + "][serviceTime=" + serviceTime + "][timeWindow=" + timeWindow + "]";
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Service other = (Service) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View file

@ -0,0 +1,202 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import util.Counter;
import algorithms.acceptors.SolutionAcceptor;
import basics.algo.AlgorithmEndsListener;
import basics.algo.AlgorithmStartsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.algo.SearchStrategy;
import basics.algo.SearchStrategyManager;
import basics.algo.VehicleRoutingAlgorithmListener;
import basics.algo.VehicleRoutingAlgorithmListeners;
/**
* Algorithm that solves a {@link VehicleRoutingProblem}.
*
* @author stefan schroeder
*
*/
public class VehicleRoutingAlgorithm {
public static final int NOBREAK = Integer.MAX_VALUE;
private static Logger logger = Logger.getLogger(VehicleRoutingAlgorithm.class);
private VehicleRoutingProblem problem;
private int nOfIterations = 100;
private int prematureBreak = NOBREAK;
private Counter counter = new Counter("iterations ");
private SearchStrategyManager searchStrategyManager;
private VehicleRoutingAlgorithmListeners algoListeners = new VehicleRoutingAlgorithmListeners();
private Collection<VehicleRoutingProblemSolution> initialSolutions;
public VehicleRoutingAlgorithm(VehicleRoutingProblem problem, SearchStrategyManager searchStrategyManager) {
super();
this.problem = problem;
this.searchStrategyManager = searchStrategyManager;
initialSolutions = new ArrayList<VehicleRoutingProblemSolution>();
}
public VehicleRoutingAlgorithm(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> initialSolutions, SearchStrategyManager searchStrategyManager) {
super();
this.problem = problem;
this.searchStrategyManager = searchStrategyManager;
this.initialSolutions = initialSolutions;
}
/**
* Adds solution to the collection of initial solutions.
*
* @param solution
*/
public void addInitialSolution(VehicleRoutingProblemSolution solution){
initialSolutions.add(solution);
}
/**
* Sets premature break.
*
* <p>This breaks the algorithm prematurely after the assigned number of iterations without improvement (see input parameter).
* Improvement is what {@link SolutionAcceptor} understands about improvement. Or to put it in other words, the algo breaks prematurely after
* the assigned number of iterations without solution-acceptance.
*
*
* @param iterationsWithoutImprovement
*/
public void setPrematureBreak(int iterationsWithoutImprovement){
prematureBreak = iterationsWithoutImprovement;
}
/**
* Gets the {@link SearchStrategyManager}.
*
* @return SearchStrategyManager
*/
public SearchStrategyManager getSearchStrategyManager() {
return searchStrategyManager;
}
/**
* Runs the vehicle routing algorithm and returns a number of generated solutions.
*
* <p>The algorithm runs as long as it is specified in nuOfIterations and prematureBreak. In each iteration it selects a searchStrategy according
* to searchStrategyManager and runs the strategy to improve solutions.
* <p>Note that clients are allowed to observe/listen the algorithm. See {@link VehicleRoutingAlgorithmListener} and its according listeners.
*
* @return Collection<VehicleRoutingProblemSolution> the solutions
* @see {@link SearchStrategyManager}, {@link VehicleRoutingAlgorithmListener}, {@link AlgorithmStartsListener}, {@link AlgorithmEndsListener}, {@link IterationStartsListener}, {@link IterationEndsListener}
*/
public Collection<VehicleRoutingProblemSolution> searchSolutions(){
logger.info("------------------------------------------------");
logger.info("algorithm starts");
double now = System.currentTimeMillis();
verify();
counter.reset();
Collection<VehicleRoutingProblemSolution> solutions = new ArrayList<VehicleRoutingProblemSolution>(initialSolutions);
algorithmStarts(problem,solutions);
int iterWithoutImprovement = 0;
logger.info("iterations start");
for(int i=0;i<nOfIterations;i++){
iterationStarts(i+1,problem,solutions);
counter.incCounter();
SearchStrategy strat = searchStrategyManager.getRandomStrategy();
boolean newSolutionFoundAndAccepted = strat.run(problem, solutions);
selectedStrategy(strat.getName(),problem, solutions);
if(newSolutionFoundAndAccepted) iterWithoutImprovement = 0;
else iterWithoutImprovement++;
if(iterWithoutImprovement > prematureBreak){
logger.info("premature break at iteration "+ (i+1));
break;
}
iterationEnds(i+1,problem,solutions);
}
logger.info("iterations end at " + nOfIterations + " iterations");
algorithmEnds(problem,solutions);
logger.info("total time: " + ((System.currentTimeMillis()-now)/1000.0) + "s");
logger.info("done");
logger.info("------------------------------------------------");
return solutions;
}
private void selectedStrategy(String name, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.selectedStrategy(name,problem, solutions);
}
/**
* Returns the number of iterations.
*
* @return iterations
*/
public int getNuOfIterations(){
return nOfIterations;
}
/**
* Asserts that the sum of probabilities of the searchStrategies is equal to 1.0.
*/
private void verify() {
double sum = 0.0;
for(Double prob : searchStrategyManager.getProbabilities()){
sum += prob;
}
if(sum < 1.0*0.99 || sum > 1.0*1.01) throw new IllegalStateException("sum of probabilities is not 1.0, but is "+ sum + ". make sure that the sum of the probability of each searchStrategy is 1.0");
}
private void algorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.algorithmEnds(problem, solutions);
}
public VehicleRoutingAlgorithmListeners getAlgorithmListeners() {
return algoListeners;
}
private void iterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.iterationEnds(i,problem, solutions);
}
private void iterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.iterationStarts(i, problem, solutions);
}
private void algorithmStarts(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.algorithmStarts(problem, this, solutions);
}
public void setNuOfIterations(int nOfIterations) {
this.nOfIterations = nOfIterations;
}
}

View file

@ -0,0 +1,473 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import util.Coordinate;
import util.CrowFlyCosts;
import util.Locations;
import util.Neighborhood;
import util.NeighborhoodImpl;
import basics.costs.DefaultVehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.VehicleType;
/**
* Contains and describes the vehicle routing problem.
*
* <p>A routing problem is defined as jobs, vehicles and costs.
*
* <p> To construct the problem, use VehicleRoutingProblem.Builder (VehicleRoutingProblem.Builder.newInstance()).
*
* <p>By default, fleetSize is INFINITE and fleetComposition is HOMOGENEOUS, transport-costs are calculated as euclidean-distance (CrowFlyCosts),
* and activity-costs are set to DefaultVehicleRoutingActivityCosts which represent hard time-windows (missed time-windows are penalyzed with Double.MAX_VALUE).
*
*
*
* @author stefan schroeder
*
*/
public class VehicleRoutingProblem {
/**
* Builder to build the routing-problem.
*
* @author stefan schroeder
*
*/
public static class Builder {
/**
* Returns a new instance of this builder.
*
* @return builder
*/
public static Builder newInstance(){ return new Builder(); }
private VehicleRoutingTransportCosts transportCosts;
private VehicleRoutingActivityCosts activityCosts = new DefaultVehicleRoutingActivityCosts();
private Map<String,Job> jobs;
private Collection<Vehicle> vehicles;
private Map<String, Coordinate> coordinates;
private FleetSize fleetSize = FleetSize.INFINITE;
private FleetComposition fleetComposition = FleetComposition.HOMOGENEOUS;
private Collection<VehicleType> vehicleTypes;
public Builder() {
jobs = new HashMap<String, Job>();
vehicles = new ArrayList<Vehicle>();
coordinates = new HashMap<String, Coordinate>();
vehicleTypes = new ArrayList<VehicleImpl.VehicleType>();
}
/**
* Create a location (i.e. coordinate) and returns the key of the location which is Coordinate.toString().
*
* @param x
* @param y
* @return locationId
* @see Coordinate
*/
public String createLocation(double x, double y){
Coordinate coord = new Coordinate(x, y);
String id = coord.toString();
if(!coordinates.containsKey(id)){
coordinates.put(id, coord);
}
return id;
}
/**
* Returns the unmodifiable map of locations (mapped by their id).
*
* @return
*/
public Map<String,Coordinate> getLocationMap(){
return Collections.unmodifiableMap(coordinates);
}
/**
* Returns the locations collected by this builder.
*
* <p>Locations are cached when adding a shipment, service, depot, vehicle.
*
* @return locations
*
*/
public Locations getLocations(){
return new Locations() {
@Override
public Coordinate getCoord(String id) {
return coordinates.get(id);
}
};
}
/**
* Sets routing costs.
*
* @param costs
* @return builder
* @see VehicleRoutingTransportCosts
*/
public Builder setRoutingCost(VehicleRoutingTransportCosts costs){
this.transportCosts = costs;
return this;
}
/**
* Sets the type of fleetSize.
*
* <p>FleetSize is either FleetSize.INFINITE or FleetSize.FINITE
*
* @param fleetSize
* @return
*/
public Builder setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
// if(fleetSize.equals(FleetSize.INFINITE)){
// fleetSizeIsInfinite=true;
// }
return this;
}
/**
* Sets the fleetComposition.
*
* <p>FleetComposition is either FleetComposition.HETEROGENEOUS or FleetComposition.HOMOGENEOUS
*
* @param fleetComposition
* @return
*/
public Builder setFleetComposition(FleetComposition fleetComposition){
this.fleetComposition = fleetComposition;
return this;
}
/**
* Adds a service to jobList.
*
* <p>If jobList already contains service, a warning message is printed, and the existing job will be overwritten.
*
* @param service
* @return
*/
public Builder addService(Service service){
coordinates.put(service.getLocationId(), service.getCoord());
if(jobs.containsKey(service.getId())){ logger.warn("service " + service + " already in job list. overrides existing job."); }
jobs.put(service.getId(),service);
return this;
}
/**
* Adds a job which is either a service or a shipment.
*
* @param job
* @return
* @throws IllegalStateException if job is neither a shipment or a service.
*/
public Builder addJob(Job job) {
if(job instanceof Service) {
addService((Service) job);
}
// else if(job instanceof Shipment){
// addShipment((Shipment)job);
// }
else throw new IllegalStateException("job can only be a shipment or a service, but is instance of " + job.getClass());
return this;
}
// private void addShipment(Shipment job) {
// coordinates.put(job.getFromId(),job.getFromCoord());
// coordinates.put(job.getToId(), job.getToCoord());
// if(jobs.containsKey(job.getId())){ logger.warn("service " + job + " already in job list. overrides existing job."); }
// jobs.put(job.getId(),job);
//
// }
/**
* Adds a vehicle.
*
*
* @param vehicle
* @return
*/
public Builder addVehicle(Vehicle vehicle) {
// fleetSizeIsFinite = true;
vehicles.add(vehicle);
if(!vehicleTypes.contains(vehicle.getType())){
vehicleTypes.add(vehicle.getType());
}
coordinates.put(vehicle.getLocationId(), vehicle.getCoord());
return this;
}
/**
* Adds a vehicleType.
*
* @param type
* @return builder
*/
public Builder addVehicleType(VehicleType type){
vehicleTypes.add(type);
return this;
}
/**
* Sets the activityCostFunction that considers also activities on a vehicle-route.
*
* <p>Here you can consider missed time-windows for example. By default, this is set to a DefaultVehicleActivityCostFunction.
*
* @param activityCosts
* @return
* @see VehicleRoutingTransportCosts, DefaultVehicleRouteCostFunction
*/
public Builder setActivityCosts(VehicleRoutingActivityCosts activityCosts){
this.activityCosts = activityCosts;
return this;
}
public VehicleRoutingProblem build() {
log.info("build problem ...");
if(transportCosts == null){
logger.warn("set routing costs crowFlyDistance.");
transportCosts = new CrowFlyCosts(getLocations());
}
return new VehicleRoutingProblem(this);
}
public Builder addLocation(String id, Coordinate coord) {
coordinates.put(id, coord);
return this;
}
public Builder addAllJobs(Collection<Job> jobs) {
for(Job j : jobs){
addJob(j);
}
return this;
}
public Builder addAllVehicles(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
addVehicle(v);
}
return this;
}
}
/**
* Enum that characterizes the fleet-size.
*
* @author sschroeder
*
*/
public static enum FleetSize {
FINITE, INFINITE;
}
/**
* Enum that characterizes fleet-compostion.
*
* @author sschroeder
*
*/
public static enum FleetComposition {
HETEROGENEOUS, HOMOGENEOUS;
}
public static Builder newBuilderInstance(){
return new Builder();
}
public static Logger log = Logger.getLogger(VehicleRoutingProblem.class);
private static Logger logger = Logger.getLogger(VehicleRoutingProblem.class);
private VehicleRoutingTransportCosts transportCosts;
private VehicleRoutingActivityCosts activityCosts;
private Neighborhood neighborhood;
/**
* @return the neighborhood
*/
public Neighborhood getNeighborhood() {
return neighborhood;
}
/**
* @param neighborhood the neighborhood to set
*/
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
}
private final Map<String, Job> jobs;
/**
* Collection that contains available vehicles.
*/
private final Collection<Vehicle> vehicles;
/**
* Collection that contains all available types.
*/
private Collection<VehicleType> vehicleTypes;
/**
* An enum that indicates type of fleetSize. By default, it is INFINTE
*/
private FleetSize fleetSize = FleetSize.INFINITE;
/**
* An enum that indicates fleetSizeComposition. By default, it is HOMOGENOUS.
*/
private FleetComposition fleetComposition;
private VehicleRoutingProblem(Builder builder) {
this.jobs = builder.jobs;
this.fleetComposition = builder.fleetComposition;
this.fleetSize = builder.fleetSize;
this.vehicles=builder.vehicles;
this.vehicleTypes = builder.vehicleTypes;
this.transportCosts = builder.transportCosts;
this.activityCosts = builder.activityCosts;
this.neighborhood = new NeighborhoodImpl(this);
log.info("initialise " + this);
}
@Override
public String toString() {
return "[fleetSize="+fleetSize+"][fleetComposition="+fleetComposition+"][#jobs="+jobs.size()+"][#vehicles="+vehicles.size()+"][#vehicleTypes="+vehicleTypes.size()+"]["+
"transportCost="+transportCosts+"][activityCosts="+activityCosts+"]";
}
/**
* Returns fleet-composition.
*
* @return fleetComposition which is either FleetComposition.HETEROGENEOUS or FleetComposition.HOMOGENEOUS
*/
public FleetComposition getFleetComposition() {
return fleetComposition;
}
public void setFleetComposition(FleetComposition fleetComposition){
this.fleetComposition = fleetComposition;
}
/**
* Returns type of fleetSize, either INFINITE or FINITE.
*
* <p>By default, it is INFINITE.
*
* @return either FleetSize.INFINITE or FleetSize.FINITE
*/
public FleetSize getFleetSize() {
return fleetSize;
}
public void setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
}
/**
* Returns the unmodifiable job map.
*
* @return unmodifiable jobMap
*/
public Map<String, Job> getJobs() {
return Collections.unmodifiableMap(jobs);
}
/**
* Returns the entire, unmodifiable collection of types.
*
* @return unmodifiable collection of types
* @see VehicleType
*/
public Collection<VehicleType> getTypes(){
return Collections.unmodifiableCollection(vehicleTypes);
}
/**
* Returns the entire, unmodifiable collection of vehicles.
*
* @return unmodifiable collection of vehicles
* @see Vehicle
*/
public Collection<Vehicle> getVehicles() {
return Collections.unmodifiableCollection(vehicles);
}
/**
* Returns routing costs.
*
* @return routingCosts
* @see VehicleRoutingTransportCosts
*/
public VehicleRoutingTransportCosts getTransportCosts() {
return transportCosts;
}
/**
* Sets routing costs.
*
* @param costs
* @see VehicleRoutingTransportCosts
*/
public void setTransportCosts(VehicleRoutingTransportCosts costs) {
this.transportCosts = costs;
logger.info("transport costs set to " + costs.getClass());
}
/**
* Returns activityCosts.
*/
public VehicleRoutingActivityCosts getActivityCosts(){
return activityCosts;
}
/**
* Sets activityCosts.
*/
public void setActivityCosts(VehicleRoutingActivityCosts activityCosts){
this.activityCosts = activityCosts;
logger.info("activtiy costs set to " + activityCosts.getClass());
}
}

View file

@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import basics.route.VehicleRoute;
/**
* Contains the solution of a vehicle routing problem and its corresponding costs.
*
* @author stefan schröder
*
*/
public class VehicleRoutingProblemSolution {
public static double NO_COST_YET = -9999.0;
/**
* Makes a deep copy of the solution to be copied.
*
* @param solution2copy
* @return
*/
public static VehicleRoutingProblemSolution copyOf(VehicleRoutingProblemSolution solution2copy){
return new VehicleRoutingProblemSolution(solution2copy);
}
private final Collection<VehicleRoute> routes;
private double cost;
private VehicleRoutingProblemSolution(VehicleRoutingProblemSolution solution){
routes = new ArrayList<VehicleRoute>();
for(VehicleRoute r : solution.getRoutes()){
VehicleRoute route = VehicleRoute.copyOf(r);
routes.add(route);
}
this.cost = solution.getCost();
}
/**
* Constructs a solution with a number of {@link VehicleRoute}s and their corresponding aggregate cost value.
*
* @param routes
* @param cost
*/
public VehicleRoutingProblemSolution(Collection<VehicleRoute> routes, double cost) {
super();
this.routes = routes;
this.cost = cost;
}
public Collection<VehicleRoute> getRoutes() {
return routes;
}
public double getCost() {
return cost;
}
public void setCost(double cost){
this.cost = cost;
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface AlgorithmEndsListener extends VehicleRoutingAlgorithmListener{
void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface AlgorithmStartsListener extends VehicleRoutingAlgorithmListener{
void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.route.VehicleRoute;
public interface InsertionEndsListener extends InsertionListener {
public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public interface InsertionListener extends SearchStrategyModuleListener{
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.route.VehicleRoute;
public interface InsertionStartsListener extends InsertionListener {
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate);
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface IterationEndsListener extends VehicleRoutingAlgorithmListener{
public void informIterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface IterationStartsListener extends VehicleRoutingAlgorithmListener{
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import basics.Job;
import basics.route.VehicleRoute;
public interface JobInsertedListener extends InsertionListener{
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn);
}

View file

@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.apache.log4j.Logger;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import algorithms.acceptors.SolutionAcceptor;
import algorithms.selectors.SolutionSelector;
public class SearchStrategy {
private static Logger logger = Logger.getLogger(SearchStrategy.class);
private Collection<SearchStrategyModule> searchStrategyModules = new ArrayList<SearchStrategyModule>();
private SolutionSelector solutionSelector;
private SolutionAcceptor solutionAcceptor;
private String name;
public SearchStrategy(SolutionSelector solutionSelector, SolutionAcceptor solutionAcceptor) {
super();
this.solutionSelector = solutionSelector;
this.solutionAcceptor = solutionAcceptor;
logger.info("initialise " + this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<SearchStrategyModule> getSearchStrategyModules() {
return Collections.unmodifiableCollection(searchStrategyModules);
}
public SolutionSelector getSolutionSelector() {
return solutionSelector;
}
public SolutionAcceptor getSolutionAcceptor() {
return solutionAcceptor;
}
@Override
public String toString() {
return "searchStrategy [#modules="+searchStrategyModules.size()+"][selector="+solutionSelector+"][acceptor="+solutionAcceptor+"]";
}
/**
* Runs the search-strategy and its according modules, and returns true if a new solution has been accepted.
*
* <p>This involves three basic steps: 1) Selecting a solution from solutions (input parameter) according to {@link SolutionSelector}, 2) running the modules
* ({@link SearchStrategyModule}) on the selectedSolution and 3) accepting the new solution according to {@link SolutionAcceptor}.
* <p> Note that after 1) the selected solution is copied, thus the original solution is not modified.
* <p> Note also that 3) modifies the input parameter solutions by adding, removing, replacing existing solutions.
*
* @param vrp
* @param solutions which will be modified
* @return boolean true if solution has been accepted, false otherwise
* @see SolutionSelector, SearchStrategyModule, SolutionAcceptor
*/
public boolean run(VehicleRoutingProblem vrp, Collection<VehicleRoutingProblemSolution> solutions){
VehicleRoutingProblemSolution solution = solutionSelector.selectSolution(solutions);
if(solution == null) throw new IllegalStateException("solution is null. check solutionSelector to return an appropiate solution.");
VehicleRoutingProblemSolution lastSolution = VehicleRoutingProblemSolution.copyOf(solution);
for(SearchStrategyModule module : searchStrategyModules){
VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(lastSolution);
lastSolution = newSolution;
}
boolean solutionAccepted = solutionAcceptor.acceptSolution(solutions, lastSolution);
return solutionAccepted;
}
public void addModule(SearchStrategyModule module){
if(module == null) throw new IllegalStateException("module to be added is null.");
searchStrategyModules.add(module);
logger.info("module added [module="+module+"][#modules="+searchStrategyModules.size()+"]");
}
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
for(SearchStrategyModule module : searchStrategyModules){
module.addModuleListener(moduleListener);
}
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public class SearchStrategyListener {
}

View file

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import util.RandomNumberGeneration;
public class SearchStrategyManager {
private List<SearchStrategyListener> searchStrategyListeners = new ArrayList<SearchStrategyListener>();
private List<SearchStrategy> strategies = new ArrayList<SearchStrategy>();
private List<Double> probabilities = new ArrayList<Double>();
private Random random = RandomNumberGeneration.getRandom();
private double sumOfProbabilities = 0;
public void setRandom(Random random) {
this.random = random;
}
public List<SearchStrategy> getStrategies() {
return Collections.unmodifiableList(strategies);
}
public List<Double> getProbabilities() {
return Collections.unmodifiableList(probabilities);
}
/**
* adds a new search strategy. the probability must be within [0,1].
* @param strategy
* @param probability
*/
public void addStrategy(SearchStrategy strategy, double probability){
if(strategy == null){
throw new IllegalStateException("strategy is null. make sure adding a valid strategy.");
}
if(probability > 1.0){
throw new IllegalStateException("probability is higher than one, but it must be within [0,1].");
}
if(probability < 0.0){
throw new IllegalStateException("probability is lower than zero, but it must be within [0,1].");
}
strategies.add(strategy);
probabilities.add(probability);
sumOfProbabilities += probability;
if(sumOfProbabilities > 1.0){
throw new IllegalStateException("total probability of all strategies is higher than one, but it must be within [0,1].");
}
}
public SearchStrategy getRandomStrategy() {
if(random == null) throw new IllegalStateException("randomizer is null. make sure you set random object correctly");
double randomFig = random.nextDouble();
double sumWeight = 0.0;
for (int i = 0; i < probabilities.size(); i++) {
sumWeight += probabilities.get(i);
if (randomFig < sumWeight) {
return strategies.get(i);
}
}
throw new IllegalStateException("no seaarch-strategy found");
}
public void addSearchStrategyListener(SearchStrategyListener strategyListener){
searchStrategyListeners.add(strategyListener);
}
public void addSearchStrategyModuleListener(SearchStrategyModuleListener moduleListener){
for(SearchStrategy s : strategies){
s.addModuleListener(moduleListener);
}
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import basics.VehicleRoutingProblemSolution;
public interface SearchStrategyModule {
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution);
public String getName();
public void addModuleListener(SearchStrategyModuleListener moduleListener);
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public interface SearchStrategyModuleListener {
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface StrategySelectedListener extends VehicleRoutingAlgorithmListener{
void informSelectedStrategy(String strategyName, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
public interface VehicleRoutingAlgorithmFactory {
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public interface VehicleRoutingAlgorithmListener {
}

View file

@ -0,0 +1,190 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeSet;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public class VehicleRoutingAlgorithmListeners {
public static class PrioritizedVRAListener {
Priority priority;
VehicleRoutingAlgorithmListener l;
public PrioritizedVRAListener(Priority priority, VehicleRoutingAlgorithmListener l) {
super();
this.priority = priority;
this.l = l;
}
public Priority getPriority() {
return priority;
}
public VehicleRoutingAlgorithmListener getListener() {
return l;
}
// @Override
// public int hashCode() {
// final int prime = 31;
// int result = 1;
// result = prime * result + ((l == null) ? 0 : l.hashCode());
// result = prime * result
// + ((priority == null) ? 0 : priority.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;
// PrioritizedVRAListener other = (PrioritizedVRAListener) obj;
// if (l == null) {
// if (other.l != null)
// return false;
// } else if (!l.equals(other.l))
// return false;
// if (priority == null) {
// if (other.priority != null)
// return false;
// } else if (!priority.equals(other.priority))
// return false;
// return true;
// }
}
public enum Priority {
HIGH, MEDIUM, LOW
}
private TreeSet<PrioritizedVRAListener> algorithmListeners = new TreeSet<PrioritizedVRAListener>(new Comparator<PrioritizedVRAListener>() {
@Override
public int compare(PrioritizedVRAListener o1, PrioritizedVRAListener o2) {
if(o1 == o2) return 0;
if(o1.getPriority() == Priority.HIGH && o2.getPriority() != Priority.HIGH){
return -1;
}
else if(o2.getPriority() == Priority.HIGH && o1.getPriority() != Priority.HIGH){
return 1;
}
else if(o1.getPriority() == Priority.MEDIUM && o2.getPriority() != Priority.MEDIUM){
return -1;
}
else if(o2.getPriority() == Priority.MEDIUM && o1.getPriority() != Priority.MEDIUM){
return 1;
}
return 1;
}
});
public Collection<VehicleRoutingAlgorithmListener> getAlgorithmListeners() {
List<VehicleRoutingAlgorithmListener> list = new ArrayList<VehicleRoutingAlgorithmListener>();
for(PrioritizedVRAListener l : algorithmListeners){
list.add(l.getListener());
}
return Collections.unmodifiableCollection(list);
}
public void remove(PrioritizedVRAListener listener){
boolean removed = algorithmListeners.remove(listener);
if(!removed){ throw new IllegalStateException("cannot remove listener"); }
}
public void addListener(VehicleRoutingAlgorithmListener listener, Priority priority){
algorithmListeners.add(new PrioritizedVRAListener(priority, listener));
}
public void addListener(VehicleRoutingAlgorithmListener listener){
addListener(listener, Priority.LOW);
}
public void algorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(PrioritizedVRAListener l : algorithmListeners){
if(l.getListener() instanceof AlgorithmEndsListener){
((AlgorithmEndsListener)l.getListener()).informAlgorithmEnds(problem, solutions);
}
}
}
public void iterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(PrioritizedVRAListener l : algorithmListeners){
if(l.getListener() instanceof IterationEndsListener){
((IterationEndsListener)l.getListener()).informIterationEnds(i,problem, solutions);
}
}
}
public void iterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(PrioritizedVRAListener l : algorithmListeners){
if(l.getListener() instanceof IterationStartsListener){
((IterationStartsListener)l.getListener()).informIterationStarts(i,problem, solutions);
}
}
}
public void algorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
for(PrioritizedVRAListener l : algorithmListeners){
if(l.getListener() instanceof AlgorithmStartsListener){
((AlgorithmStartsListener)l.getListener()).informAlgorithmStarts(problem, algorithm, solutions);
}
}
}
public void add(PrioritizedVRAListener l){
algorithmListeners.add(l);
}
public void addAll(Collection<PrioritizedVRAListener> algorithmListeners) {
for(PrioritizedVRAListener l : algorithmListeners){
this.algorithmListeners.add(l);
}
}
public void selectedStrategy(String name, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(PrioritizedVRAListener l : algorithmListeners){
if(l.getListener() instanceof StrategySelectedListener){
((StrategySelectedListener)l.getListener()).informSelectedStrategy(name, problem, solutions);
}
}
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.Vehicle;
public interface BackwardTransportCost {
public double getBackwardTransportCost(String fromId, String toId,
double arrivalTime, Driver driver, Vehicle vehicle);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.Vehicle;
public interface BackwardTransportTime {
public double getBackwardTransportTime(String fromId, String toId,
double arrivalTime, Driver driver, Vehicle vehicle);
}

View file

@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.TourActivity;
import basics.route.Vehicle;
/**
* Function that basically does not allow soft time-windows. Actually, it is allowed but it is penalized with Double.MaxValue().
*
* @author schroeder
*
*/
public class DefaultVehicleRoutingActivityCosts implements VehicleRoutingActivityCosts{
@Override
public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle) {
if(arrivalTime > tourAct.getTheoreticalLatestOperationStartTime()){
return Double.MAX_VALUE;
}
return 0;
}
@Override
public String toString() {
return "[name=hardTimeWindowActCosts]";
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.Vehicle;
public interface ForwardTransportCost {
public double getTransportCost(String fromId, String toId,
double departureTime, Driver driver, Vehicle vehicle);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.Vehicle;
public interface ForwardTransportTime {
public double getTransportTime(String fromId, String toId,
double departureTime, Driver driver, Vehicle vehicle);
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
public interface TransportCost extends ForwardTransportCost, BackwardTransportCost{
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
public interface TransportTime extends ForwardTransportTime, BackwardTransportTime{
}

View file

@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
import basics.route.Driver;
import basics.route.TourActivity;
import basics.route.Vehicle;
/**
* Interface for overall routing and operation costs.
*
* <p>This calculates activity and leg-based costs. If you want to consider for example costs incurred by missed time-windows, you can do it here.
*
* @author schroeder
*
*/
public interface VehicleRoutingActivityCosts {
public static class Time {
public static double TOUREND = -2.0;
public static double TOURSTART = -1.0;
public static double UNDEFINED = -3.0;
}
public static interface Parameter {
public double getPenaltyForMissedTimeWindow();
}
/**
* Calculates and returns the activity cost at tourAct.
*
* <p>Here waiting-times, service-times and missed time-windows can be considered.
*
* @param tourAct
* @param arrivalTime is actually the arrival time at this tourActivity, which must not nessecarrily be the operation start time. If the theoretical earliest
* operation start time at this activity is later than actualStartTime, the driver must wait at this activity.
* @param driver TODO
* @param vehicle TODO
* @param earliestStartTime, this is the practical earliest operation start time which considers also previous activities.
* @param latestStartTime, this is the practical latest operation start time which consider also future activities in the tour.
* if earliestStartTime > latestStartTime activity operations cannot be conducted within the given time-window.
* @return
*/
public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle);
// public Parameter getParameter(TourActivity tourAct, Vehicle vehicle, Driver driver);
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.costs;
/**
* Interface for transportCost and transportTime.
*
* <p>Transport here is what happens between two activities within the transport system, i.e. in the physical transport network. And
* must give the answer of how long does it take from A to B, and how much does this cost.
*
* @author schroeder
*
*/
public interface VehicleRoutingTransportCosts extends TransportTime, TransportCost {
}

View file

@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.io;
import org.apache.commons.configuration.XMLConfiguration;
public class AlgorithmConfig {
private XMLConfiguration xmlConfig;
public AlgorithmConfig(){
xmlConfig = new XMLConfiguration();
}
public XMLConfiguration getXMLConfiguration(){
return xmlConfig;
}
}

View file

@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.io;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class AlgorithmConfigXmlReader {
private static Logger log = Logger.getLogger(AlgorithmConfigXmlReader.class);
private AlgorithmConfig algorithmConfig;
private boolean schemaValidation = true;
/**
* @param schemaValidation the schemaValidation to set
*/
public void setSchemaValidation(boolean schemaValidation) {
this.schemaValidation = schemaValidation;
}
public AlgorithmConfigXmlReader(AlgorithmConfig algorithmConfig){
this.algorithmConfig = algorithmConfig;
}
public void read(String filename){
log.info("read algorithm-config from file " + filename);
algorithmConfig.getXMLConfiguration().setFileName(filename);
algorithmConfig.getXMLConfiguration().setAttributeSplittingDisabled(true);
algorithmConfig.getXMLConfiguration().setDelimiterParsingDisabled(true);
if(schemaValidation){
final URL resource = this.getClass().getClassLoader().getResource("algorithm_schema.xsd");
if(resource != null) {
EntityResolver resolver = new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
{
InputSource is = new InputSource(resource.getFile());
return is;
}
}
};
algorithmConfig.getXMLConfiguration().setEntityResolver(resolver);
algorithmConfig.getXMLConfiguration().setSchemaValidation(true);
log.info("validating " + filename + " with xsd-schema");
}
else{
log.warn("cannot find schema-xsd file (algorithm_xml_schema.xsd). try to read xml without xml-file-validation.");
}
}
try {
algorithmConfig.getXMLConfiguration().load();
} catch (ConfigurationException e) {
log.error(e);
e.printStackTrace();
System.exit(1);
}
}
}

View file

@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.io;
final class Schema {
public static final String PROBLEM = "problem";
public static final String VEHICLE = "vehicle";
public static final String TYPES = "vehicleTypes";
public static final String VEHICLES = "vehicles";
public static final String SHIPMENTS = "shipments";
public static final String SHIPMENT = "shipment";
public static final String SERVICETIME = "serviceTime";
public static final String PICKUP = "pickup";
public static final String TYPE = "type";
public void dot(){
}
public static class PathBuilder {
StringBuilder stringBuilder = new StringBuilder();
boolean justCreated = true;
public PathBuilder dot(String string){
stringBuilder.append(".").append(string);
return this;
}
public PathBuilder append(String string){
stringBuilder.append(string);
return this;
}
public String build(){ return stringBuilder.toString(); }
}
public static PathBuilder builder(){
return new PathBuilder();
}
private Schema(){
}
}

View file

@ -0,0 +1,320 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.io;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import util.Coordinate;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.VehicleRoutingProblem.FleetSize;
import basics.VehicleRoutingProblemSolution;
import basics.route.Driver;
import basics.route.DriverImpl;
import basics.route.End;
import basics.route.ServiceActivity;
import basics.route.Start;
import basics.route.TimeWindow;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.VehicleBuilder;
import basics.route.VehicleImpl.VehicleType;
import basics.route.VehicleRoute;
public class VrpXMLReader{
private static Logger logger = Logger.getLogger(VrpXMLReader.class);
private VehicleRoutingProblem.Builder vrpBuilder;
private Map<String,Vehicle> vehicleMap;
private Map<String, Service> serviceMap;
private boolean schemaValidation = true;
private Collection<VehicleRoutingProblemSolution> solutions;
/**
* @param schemaValidation the schemaValidation to set
*/
public void setSchemaValidation(boolean schemaValidation) {
this.schemaValidation = schemaValidation;
}
public VrpXMLReader(VehicleRoutingProblem.Builder vrpBuilder, Collection<VehicleRoutingProblemSolution> solutions){
this.vrpBuilder = vrpBuilder;
this.vehicleMap = new HashMap<String, Vehicle>();
this.serviceMap = new HashMap<String, Service>();
this.solutions = solutions;
}
public VrpXMLReader(VehicleRoutingProblem.Builder vrpBuilder){
this.vrpBuilder = vrpBuilder;
this.vehicleMap = new HashMap<String, Vehicle>();
this.serviceMap = new HashMap<String, Service>();
this.solutions = null;
}
public void read(String filename) {
logger.info("read vrp from file " + filename);
XMLConfiguration xmlConfig = new XMLConfiguration();
xmlConfig.setFileName(filename);
xmlConfig.setAttributeSplittingDisabled(true);
xmlConfig.setDelimiterParsingDisabled(true);
if(schemaValidation){
final URL resource = this.getClass().getClassLoader().getResource("vrp_xml_schema.xsd");
if(resource != null) {
EntityResolver resolver = new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
{
InputSource is = new InputSource(resource.getFile());
return is;
}
}
};
xmlConfig.setEntityResolver(resolver);
xmlConfig.setSchemaValidation(true);
logger.info("validating " + filename + " with xsd-schema");
}
else{
logger.warn("cannot find schema-xsd file (vrp_xml_schema.xsd). try to read xml without xml-file-validation.");
}
}
try {
xmlConfig.load();
} catch (ConfigurationException e) {
logger.error(e);
e.printStackTrace();
System.exit(1);
}
readProblemType(xmlConfig);
readVehiclesAndTheirTypes(xmlConfig);
readServices(xmlConfig);
readSolutions(xmlConfig);
}
private void readSolutions(XMLConfiguration vrpProblem) {
if(solutions == null) return;
List<HierarchicalConfiguration> solutionConfigs = vrpProblem.configurationsAt("solutions.solution");
for(HierarchicalConfiguration solutionConfig : solutionConfigs){
String totalCost = solutionConfig.getString("cost");
double cost = -1;
if(totalCost != null) cost = Double.parseDouble(totalCost);
List<HierarchicalConfiguration> routeConfigs = solutionConfig.configurationsAt("routes.route");
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
for(HierarchicalConfiguration routeConfig : routeConfigs){
String driverId = routeConfig.getString("driverId");
//! here, driverId is set to noDriver, no matter whats in driverId.
Driver driver = DriverImpl.noDriver();
String vehicleId = routeConfig.getString("vehicleId");
Vehicle vehicle = getVehicle(vehicleId);
if(vehicle == null) throw new IllegalStateException("vehicle is missing.");
String start = routeConfig.getString("start");
if(start == null) throw new IllegalStateException("route start-time is missing.");
String end = routeConfig.getString("end");
if(end == null) throw new IllegalStateException("route end-time is missing.");
Start startAct = Start.newInstance(vehicle.getLocationId(), vehicle.getEarliestDeparture(), vehicle.getLatestArrival());
startAct.setEndTime(Double.parseDouble(start));
End endAct = End.newInstance(vehicle.getLocationId(), vehicle.getEarliestDeparture(), vehicle.getLatestArrival());
endAct.setArrTime(Double.parseDouble(end));
VehicleRoute.Builder routeBuilder = VehicleRoute.Builder.newInstance(startAct, endAct);
routeBuilder.setDriver(driver);
routeBuilder.setVehicle(vehicle);
List<HierarchicalConfiguration> actConfigs = routeConfig.configurationsAt("act");
for(HierarchicalConfiguration actConfig : actConfigs){
String type = actConfig.getString("[@type]");
if(type == null) throw new IllegalStateException("act[@type] is missing.");
String serviceId = actConfig.getString("serviceId");
if(serviceId == null) throw new IllegalStateException("act.serviceId is missing.");
Service service = getService(serviceId);
String arrTime = actConfig.getString("arrTime");
if(arrTime == null) throw new IllegalStateException("act.arrTime is missing.");
String endTime = actConfig.getString("endTime");
if(endTime == null) throw new IllegalStateException("act.endTime is missing.");
ServiceActivity serviceActivity = ServiceActivity.newInstance(service);
serviceActivity.setArrTime(Double.parseDouble(arrTime));
serviceActivity.setEndTime(Double.parseDouble(endTime));
routeBuilder.addActivity(serviceActivity);
}
routes.add(routeBuilder.build());
}
VehicleRoutingProblemSolution solution = new VehicleRoutingProblemSolution(routes, cost);
solutions.add(solution);
}
}
private Service getService(String serviceId) {
return serviceMap.get(serviceId);
}
private Vehicle getVehicle(String vehicleId) {
return vehicleMap.get(vehicleId);
}
private void readProblemType(XMLConfiguration vrpProblem) {
String fleetSize = vrpProblem.getString("problemType.fleetSize");
if(fleetSize == null) vrpBuilder.setFleetSize(FleetSize.INFINITE);
else if(fleetSize.toUpperCase().equals(FleetSize.INFINITE.toString())) vrpBuilder.setFleetSize(FleetSize.INFINITE);
else vrpBuilder.setFleetSize(FleetSize.FINITE);
String fleetComposition = vrpProblem.getString("problemType.fleetComposition");
if(fleetComposition == null) vrpBuilder.setFleetComposition(FleetComposition.HOMOGENEOUS);
else if(fleetComposition.toUpperCase().equals(FleetComposition.HETEROGENEOUS.toString())){
vrpBuilder.setFleetComposition(FleetComposition.HETEROGENEOUS);
}
else vrpBuilder.setFleetComposition(FleetComposition.HOMOGENEOUS);
}
private void readServices(XMLConfiguration vrpProblem) {
List<HierarchicalConfiguration> serviceConfigs = vrpProblem.configurationsAt("services.service");
for(HierarchicalConfiguration serviceConfig : serviceConfigs){
String id = serviceConfig.getString("[@id]");
if(id == null) throw new IllegalStateException("service[@id] is missing.");
String name = serviceConfig.getString("[@type]");
if(name == null) name = "service";
String capacityDemand = serviceConfig.getString("capacity-demand");
int cap = 0;
if(capacityDemand != null) cap = Integer.parseInt(capacityDemand);
Service.Builder builder = Service.Builder.newInstance(id, cap);
builder.setName(name);
String serviceLocationId = serviceConfig.getString("locationId");
builder.setLocationId(serviceLocationId);
Coordinate serviceCoord = null;
if(serviceConfig.getString("coord[@x]") != null && serviceConfig.getString("coord[@y]") != null){
double x = Double.parseDouble(serviceConfig.getString("coord[@x]"));
double y = Double.parseDouble(serviceConfig.getString("coord[@y]"));
serviceCoord = Coordinate.newInstance(x,y);
}
builder.setCoord(serviceCoord);
if(serviceCoord != null){
if(serviceLocationId != null){
vrpBuilder.addLocation(serviceLocationId,serviceCoord);
}
else{
vrpBuilder.addLocation(serviceCoord.toString(),serviceCoord);
builder.setLocationId(serviceCoord.toString());
}
}
if(serviceConfig.containsKey("duration")){
builder.setServiceTime(serviceConfig.getDouble("duration"));
}
List<HierarchicalConfiguration> deliveryTWConfigs = serviceConfig.configurationsAt("timeWindows.timeWindow");
if(!deliveryTWConfigs.isEmpty()){
for(HierarchicalConfiguration twConfig : deliveryTWConfigs){
builder.setTimeWindow(TimeWindow.newInstance(twConfig.getDouble("start"), twConfig.getDouble("end")));
}
}
Service service = builder.build();
serviceMap.put(service.getId(),service);
vrpBuilder.addJob(service);
}
}
private void readVehiclesAndTheirTypes(XMLConfiguration vrpProblem) {
//read vehicle-types
Map<String, VehicleType> types = new HashMap<String, VehicleType>();
List<HierarchicalConfiguration> typeConfigs = vrpProblem.configurationsAt("vehicleTypes.type");
for(HierarchicalConfiguration typeConfig : typeConfigs){
String typeId = typeConfig.getString("id");
Integer capacity = typeConfig.getInt("capacity");
Double fix = typeConfig.getDouble("costs.fixed");
Double timeC = typeConfig.getDouble("costs.time");
Double distC = typeConfig.getDouble("costs.distance");
// Double start = typeConfig.getDouble("timeSchedule.start");
// Double end = typeConfig.getDouble("timeSchedule.end");
if(typeId == null) throw new IllegalStateException("typeId is missing.");
if(capacity == null) throw new IllegalStateException("capacity is missing.");
VehicleType.Builder typeBuilder = VehicleType.Builder.newInstance(typeId, capacity);
if(fix != null) typeBuilder.setFixedCost(fix);
if(timeC != null) typeBuilder.setCostPerTime(timeC);
if(distC != null) typeBuilder.setCostPerDistance(distC);
// if(start != null && end != null) typeBuilder.setTimeSchedule(new TimeSchedule(start, end));
VehicleType type = typeBuilder.build();
types.put(type.typeId, type);
vrpBuilder.addVehicleType(type);
}
//read vehicles
List<HierarchicalConfiguration> vehicleConfigs = vrpProblem.configurationsAt("vehicles.vehicle");
boolean doNotWarnAgain = false;
for(HierarchicalConfiguration vehicleConfig : vehicleConfigs){
String vehicleId = vehicleConfig.getString("id");
if(vehicleId == null) throw new IllegalStateException("vehicleId is missing.");
VehicleBuilder builder = VehicleImpl.VehicleBuilder.newInstance(vehicleId);
String typeId = vehicleConfig.getString("typeId");
if(typeId == null) throw new IllegalStateException("typeId is missing.");
VehicleType type = types.get(typeId);
if(type == null) throw new IllegalStateException("vehicleType with typeId " + typeId + " is missing.");
builder.setType(type);
String locationId = vehicleConfig.getString("location.id");
if(locationId == null) throw new IllegalStateException("location.id is missing.");
builder.setLocationId(locationId);
String coordX = vehicleConfig.getString("location.coord[@x]");
String coordY = vehicleConfig.getString("location.coord[@y]");
if(coordX == null || coordY == null) {
if(!doNotWarnAgain) {
logger.warn("location.coord is missing. do not warn you again.");
doNotWarnAgain = true;
}
}
else{
Coordinate coordinate = Coordinate.newInstance(Double.parseDouble(coordX), Double.parseDouble(coordY));
builder.setLocationCoord(coordinate);
}
String start = vehicleConfig.getString("timeSchedule.start");
String end = vehicleConfig.getString("timeSchedule.end");
if(start != null) builder.setEarliestStart(Double.parseDouble(start));
if(end != null) builder.setLatestArrival(Double.parseDouble(end));
VehicleImpl vehicle = builder.build();
// vehicleMap.put(vehicle.getId(), vehicle);
vrpBuilder.addVehicle(vehicle);
vehicleMap.put(vehicleId, vehicle);
}
}
}

View file

@ -0,0 +1,295 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.io;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.route.ServiceActivity;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleImpl.VehicleType;
import basics.route.VehicleRoute;
public class VrpXMLWriter {
static class XMLConf extends XMLConfiguration {
public Document createDoc() throws ConfigurationException{
Document doc = createDocument();
return doc;
}
}
private Logger log = Logger.getLogger(VrpXMLWriter.class);
private VehicleRoutingProblem vrp;
private Collection<VehicleRoutingProblemSolution> solutions;
public VrpXMLWriter(VehicleRoutingProblem vrp, Collection<VehicleRoutingProblemSolution> solutions) {
this.vrp = vrp;
this.solutions = solutions;
}
public VrpXMLWriter(VehicleRoutingProblem vrp) {
this.vrp = vrp;
this.solutions = null;
}
private static Logger logger = Logger.getLogger(VrpXMLWriter.class);
public void write(String filename){
log.info("write vrp to " + filename);
XMLConf xmlConfig = new XMLConf();
xmlConfig.setFileName(filename);
xmlConfig.setRootElementName("problem");
xmlConfig.setAttributeSplittingDisabled(true);
xmlConfig.setDelimiterParsingDisabled(true);
writeProblemType(xmlConfig);
writeVehiclesAndTheirTypes(xmlConfig);
writerServices(xmlConfig);
// writeShipments(xmlConfig);
writeSolutions(xmlConfig);
OutputFormat format = new OutputFormat();
format.setIndenting(true);
format.setIndent(5);
try {
Document document = xmlConfig.createDoc();
Element element = document.getDocumentElement();
element.setAttribute("xmlns", "http://www.w3schools.com");
element.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
element.setAttribute("xsi:schemaLocation","http://www.w3schools.com vrp_xml_schema.xsd");
} catch (ConfigurationException e) {
logger.error(e);
e.printStackTrace();
System.exit(1);
}
try {
Writer out = new FileWriter(filename);
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(xmlConfig.getDocument());
} catch (IOException e) {
logger.error(e);
e.printStackTrace();
System.exit(1);
}
}
private void writeSolutions(XMLConf xmlConfig) {
if(solutions == null) return;
String solutionPath = "solutions.solution";
int counter = 0;
for(VehicleRoutingProblemSolution solution : solutions){
xmlConfig.setProperty(solutionPath + "(" + counter + ").cost", solution.getCost());
int routeCounter = 0;
for(VehicleRoute route : solution.getRoutes()){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").cost", route.getCost());
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").driverId", route.getDriver().getId());
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").vehicleId", route.getVehicle().getId());
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").start", route.getStart().getEndTime());
int actCounter = 0;
for(TourActivity act : route.getTourActivities().getActivities()){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+")[@type]", act.getName());
if(act instanceof ServiceActivity){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").serviceId", ((ServiceActivity) act).getJob().getId());
}
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").arrTime", act.getArrTime());
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").endTime", act.getEndTime());
actCounter++;
}
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").end", route.getEnd().getArrTime());
routeCounter++;
}
counter++;
}
}
private void writerServices(XMLConf xmlConfig) {
String shipmentPathString = "services.service";
int counter = 0;
for(Job j : vrp.getJobs().values()){
Service service = (Service) j;
xmlConfig.setProperty(shipmentPathString + "("+counter+")[@id]", service.getId());
xmlConfig.setProperty(shipmentPathString + "("+counter+")[@type]", service.getType());
if(service.getLocationId() != null) xmlConfig.setProperty(shipmentPathString + "("+counter+").locationId", service.getLocationId());
if(service.getCoord() != null) {
xmlConfig.setProperty(shipmentPathString + "("+counter+").coord[@x]", service.getCoord().getX());
xmlConfig.setProperty(shipmentPathString + "("+counter+").coord[@y]", service.getCoord().getY());
}
xmlConfig.setProperty(shipmentPathString + "("+counter+").capacity-demand", service.getCapacityDemand());
xmlConfig.setProperty(shipmentPathString + "("+counter+").duration", service.getServiceDuration());
xmlConfig.setProperty(shipmentPathString + "("+counter+").timeWindows.timeWindow(0).start", service.getTimeWindow().getStart());
xmlConfig.setProperty(shipmentPathString + "("+counter+").timeWindows.timeWindow(0).end", service.getTimeWindow().getEnd());
counter++;
}
}
// private void writeShipments(XMLConf xmlConfig) {
// String shipmentPathString = "shipments.shipment";
// int counter = 0;
// for(Job j : vrp.getJobs().values()){
// Shipment shipment = (Shipment) j;
// xmlConfig.setProperty(shipmentPathString + "("+counter+")[@id]", shipment.getId());
// if(shipment.getFromId() != null) xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.locationId", shipment.getFromId());
// if(shipment.getFromCoord() != null) {
// xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.coord[@x]", shipment.getFromCoord().getX());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.coord[@y]", shipment.getFromCoord().getY());
// }
// if(shipment.getToId() != null) xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.locationId", shipment.getToId());
// if(shipment.getFromCoord() != null) {
// xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.coord[@x]", shipment.getToCoord().getX());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.coord[@y]", shipment.getToCoord().getY());
// }
// xmlConfig.setProperty(shipmentPathString + "("+counter+").size", shipment.getSize());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.serviceTime", shipment.getPickupServiceTime());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.timeWindows.timeWindow(0).start", shipment.getPickupTW().getStart());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.timeWindows.timeWindow(0).end", shipment.getPickupTW().getEnd());
//
// xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.serviceTime", shipment.getDeliveryServiceTime());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.timeWindows.timeWindow(0).start", shipment.getDeliveryTW().getStart());
// xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.timeWindows.timeWindow(0).end", shipment.getDeliveryTW().getEnd());
//
// counter++;
// }
//
// }
private void writeProblemType(XMLConfiguration xmlConfig){
xmlConfig.setProperty("problemType.fleetSize", vrp.getFleetSize());
xmlConfig.setProperty("problemType.fleetComposition", vrp.getFleetComposition());
}
private void writeVehiclesAndTheirTypes(XMLConfiguration xmlConfig) {
// //depots
// Map<Depot,Integer> depot2id = new HashMap<Depot, Integer>();
// int depotCounter = 0;
// for(Depot depot : vrp.getDepots()){
// int depotId = depotCounter+1;
// depot2id.put(depot, depotId);
// xmlConfig.setProperty("depots.depot" + "("+depotCounter+")[@id]", (depotId));
// xmlConfig.setProperty("depots.depot" + "("+depotCounter+").locationId", depot.getId());
// xmlConfig.setProperty("depots.depot" + "("+depotCounter+").coord[@x]", depot.getCoord().getX());
// xmlConfig.setProperty("depots.depot" + "("+depotCounter+").coord[@y]", depot.getCoord().getY());
// depotCounter++;
// }
//vehicles
String vehiclePathString = new StringBuilder().append(Schema.VEHICLES).append(".").
append(Schema.VEHICLE).toString();
int counter = 0;
for(Vehicle vehicle : vrp.getVehicles()){
xmlConfig.setProperty(vehiclePathString + "("+counter+").id", vehicle.getId());
xmlConfig.setProperty(vehiclePathString + "("+counter+").typeId", vehicle.getType().typeId);
xmlConfig.setProperty(vehiclePathString + "("+counter+").location.id", vehicle.getLocationId());
if(vehicle.getCoord() != null){
xmlConfig.setProperty(vehiclePathString + "("+counter+").location.coord[@x]", vehicle.getCoord().getX());
xmlConfig.setProperty(vehiclePathString + "("+counter+").location.coord[@y]", vehicle.getCoord().getY());
}
xmlConfig.setProperty(vehiclePathString + "("+counter+").timeSchedule.start", vehicle.getEarliestDeparture());
xmlConfig.setProperty(vehiclePathString + "("+counter+").timeSchedule.end", vehicle.getLatestArrival());
// if(vehicle.getLocationId() != null) xmlConfig.setProperty(vehiclePathString + "("+counter+").locationId", vehicle.getLocationId());
// if(vehicle.getCoord() != null) {
// xmlConfig.setProperty(vehiclePathString + "("+counter+").coord[@x]", vehicle.getCoord().getX());
// xmlConfig.setProperty(vehiclePathString + "("+counter+").coord[@y]", vehicle.getCoord().getY());
// }
// xmlConfig.setProperty(vehiclePathString + "("+counter+").earliestStart", vehicle.getEarliestDeparture());
// xmlConfig.setProperty(vehiclePathString + "("+counter+").latestEnd", vehicle.getLatestArrival());
counter++;
}
//types
String typePathString = Schema.builder().append(Schema.TYPES).dot(Schema.TYPE).build();
int typeCounter = 0;
for(VehicleType type : vrp.getTypes()){
xmlConfig.setProperty(typePathString + "("+typeCounter+").id", type.typeId);
xmlConfig.setProperty(typePathString + "("+typeCounter+").capacity", type.capacity);
// xmlConfig.setProperty(typePathString + "("+typeCounter+").timeSchedule.start", type.getTimeSchedule().getEarliestStart());
// xmlConfig.setProperty(typePathString + "("+typeCounter+").timeSchedule.end", type.getTimeSchedule().getLatestEnd());
xmlConfig.setProperty(typePathString + "("+typeCounter+").costs.fixed", type.vehicleCostParams.fix);
xmlConfig.setProperty(typePathString + "("+typeCounter+").costs.distance", type.vehicleCostParams.perDistanceUnit);
xmlConfig.setProperty(typePathString + "("+typeCounter+").costs.time", type.vehicleCostParams.perTimeUnit);
typeCounter++;
}
// //type2depot assignments
// int assignmentCounter = 0;
// boolean first = true;
// for(Depot depot : vrp.getDepotToVehicleTypeAssignments().keySet()){
// if(first){
// xmlConfig.setProperty("assignments[@type]", "vehicleType2depot");
// }
// for(VehicleType type : vrp.getDepotToVehicleTypeAssignments().get(depot)){
// xmlConfig.setProperty("assignments.assignment" + "("+assignmentCounter+").depotId", depot2id.get(depot));
// xmlConfig.setProperty("assignments.assignment" + "("+assignmentCounter+").vehicleTypeId", type.getTypeId());
// assignmentCounter++;
// }
// }
//
// //vehicle2depot assignments
// int vehicleAssignmentCounter = 0;
// boolean first_ = true;
// for(Depot depot : vrp.getDepotToVehicleAssignments().keySet()){
// if(first_){
// xmlConfig.setProperty("assignments[@type]", "vehicle2depot");
// }
// for(Vehicle vehicle : vrp.getDepotToVehicleAssignments().get(depot)){
// xmlConfig.setProperty("assignments.assignment" + "("+vehicleAssignmentCounter+").depotId", depot2id.get(depot));
// xmlConfig.setProperty("assignments.assignment" + "("+vehicleAssignmentCounter+").vehicleId", vehicle.getId());
// vehicleAssignmentCounter++;
// }
// }
//
}
}

View file

@ -0,0 +1,133 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.route;
import basics.route.VehicleImpl.VehicleType;
public class DefaultVehicleRouteCostCalculator implements VehicleRouteCostCalculator {
private double tpCosts = 0.0;
private double actCosts = 0.0;
private double vehicleCosts = 0.0;
private double driverCosts = 0.0;
private double other = 0.0;
public DefaultVehicleRouteCostCalculator(){}
private DefaultVehicleRouteCostCalculator(DefaultVehicleRouteCostCalculator costCalc){
this.tpCosts=costCalc.getTpCosts();
this.actCosts = costCalc.getActCosts();
this.driverCosts = costCalc.getDriverCosts();
this.other = costCalc.getOther();
this.vehicleCosts = costCalc.getVehicleCosts();
}
public void addTransportCost(double tpCost) {
this.tpCosts+=tpCost;
}
public void addActivityCost(double actCost){
this.actCosts+=actCost;
}
public void price(Vehicle vehicle){
if(vehicle != null){
VehicleType type = vehicle.getType();
if(type != null){
this.vehicleCosts = type.getVehicleCostParams().fix;
}
}
}
public void price(Driver driver){
}
@Override
public void finish() {
// TODO Auto-generated method stub
}
@Override
public void reset() {
tpCosts = 0.0;
actCosts = 0.0;
vehicleCosts = 0.0;
driverCosts = 0.0;
other = 0.0;
}
@Override
public void addOtherCost(double cost) {
this.other = cost;
}
@Override
public double getCosts() {
return tpCosts + actCosts + vehicleCosts + driverCosts + other;
}
/**
* @return the tpCosts
*/
public double getTpCosts() {
return tpCosts;
}
/**
* @return the actCosts
*/
public double getActCosts() {
return actCosts;
}
/**
* @return the vehicleCosts
*/
public double getVehicleCosts() {
return vehicleCosts;
}
/**
* @return the driverCosts
*/
public double getDriverCosts() {
return driverCosts;
}
/**
* @return the other
*/
public double getOther() {
return other;
}
@Override
public VehicleRouteCostCalculator duplicate() {
return new DefaultVehicleRouteCostCalculator(this);
}
}

View file

@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.route;
public interface Driver {
public String getId();
}

View file

@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.route;
public class DriverImpl implements Driver {
public static NoDriver noDriver(){
return new NoDriver();
}
public static class NoDriver extends DriverImpl {
public NoDriver() {
super("noDriver");
}
}
private String id;
private double earliestStart = 0.0;
private double latestEnd = Double.MAX_VALUE;
private String home;
private DriverImpl(String id) {
super();
this.id = id;
}
public String getId() {
return id;
}
public double getEarliestStart() {
return earliestStart;
}
public void setEarliestStart(double earliestStart) {
this.earliestStart = earliestStart;
}
public double getLatestEnd() {
return latestEnd;
}
public void setLatestEnd(double latestEnd) {
this.latestEnd = latestEnd;
}
public void setHomeLocation(String locationId) {
this.home = locationId;
}
public String getHomeLocation() {
return this.home;
}
}

Some files were not shown because too many files have changed in this diff Show more