mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
add shipment based activities, modify inserter and implement
insertionAlgo for shipments
This commit is contained in:
parent
c31e61a797
commit
2835d15dd3
12 changed files with 570 additions and 51 deletions
|
|
@ -19,19 +19,91 @@ package algorithms;
|
||||||
import algorithms.InsertionData.NoInsertionFound;
|
import algorithms.InsertionData.NoInsertionFound;
|
||||||
import basics.Job;
|
import basics.Job;
|
||||||
import basics.Service;
|
import basics.Service;
|
||||||
|
import basics.Shipment;
|
||||||
|
import basics.route.DefaultShipmentActivityFactory;
|
||||||
import basics.route.DefaultTourActivityFactory;
|
import basics.route.DefaultTourActivityFactory;
|
||||||
|
import basics.route.TourActivity;
|
||||||
import basics.route.TourActivityFactory;
|
import basics.route.TourActivityFactory;
|
||||||
|
import basics.route.TourShipmentActivityFactory;
|
||||||
import basics.route.VehicleRoute;
|
import basics.route.VehicleRoute;
|
||||||
|
|
||||||
class Inserter {
|
class Inserter {
|
||||||
|
|
||||||
|
interface JobInsertionHandler {
|
||||||
|
void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route);
|
||||||
|
|
||||||
|
void setNextHandler(JobInsertionHandler handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
class JobExceptionHandler implements JobInsertionHandler{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleJobInsertion(Job job, InsertionData iData,VehicleRoute route) {
|
||||||
|
throw new IllegalStateException("job insertion is not supported. Do not know job type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNextHandler(JobInsertionHandler handler) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServiceInsertionHandler implements JobInsertionHandler{
|
||||||
|
|
||||||
|
private TourActivityFactory activityFactory = new DefaultTourActivityFactory();
|
||||||
|
|
||||||
|
private JobInsertionHandler delegator = new JobExceptionHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) {
|
||||||
|
if(job instanceof Service){
|
||||||
|
route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), this.activityFactory.createActivity((Service)job));
|
||||||
|
route.setDepartureTime(iData.getVehicleDepartureTime());
|
||||||
|
}
|
||||||
|
else delegator.handleJobInsertion(job, iData, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextHandler(JobInsertionHandler jobInsertionHandler){
|
||||||
|
this.delegator = jobInsertionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShipmentInsertionHandler implements JobInsertionHandler {
|
||||||
|
|
||||||
|
private TourShipmentActivityFactory activityFactory = new DefaultShipmentActivityFactory();
|
||||||
|
|
||||||
|
private JobInsertionHandler delegator = new JobExceptionHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) {
|
||||||
|
if(job instanceof Shipment){
|
||||||
|
TourActivity pickupShipment = this.activityFactory.createPickup((Shipment)job);
|
||||||
|
TourActivity deliverShipment = this.activityFactory.createDelivery((Shipment)job);
|
||||||
|
route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), deliverShipment);
|
||||||
|
route.getTourActivities().addActivity(iData.getPickupInsertionIndex(), pickupShipment);
|
||||||
|
route.setDepartureTime(iData.getVehicleDepartureTime());
|
||||||
|
}
|
||||||
|
else delegator.handleJobInsertion(job, iData, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextHandler(JobInsertionHandler jobInsertionHandler){
|
||||||
|
this.delegator = jobInsertionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private InsertionListeners insertionListeners;
|
private InsertionListeners insertionListeners;
|
||||||
|
|
||||||
private TourActivityFactory activityFactory;
|
private JobInsertionHandler jobInsertionHandler;
|
||||||
|
|
||||||
public Inserter(InsertionListeners insertionListeners) {
|
public Inserter(InsertionListeners insertionListeners) {
|
||||||
this.insertionListeners = insertionListeners;
|
this.insertionListeners = insertionListeners;
|
||||||
activityFactory = new DefaultTourActivityFactory();
|
new DefaultTourActivityFactory();
|
||||||
|
jobInsertionHandler = new ServiceInsertionHandler();
|
||||||
|
jobInsertionHandler.setNextHandler(new ShipmentInsertionHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){
|
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){
|
||||||
|
|
@ -43,14 +115,7 @@ class Inserter {
|
||||||
insertionListeners.informVehicleSwitched(vehicleRoute, vehicleRoute.getVehicle(), insertionData.getSelectedVehicle());
|
insertionListeners.informVehicleSwitched(vehicleRoute, vehicleRoute.getVehicle(), insertionData.getSelectedVehicle());
|
||||||
vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
|
vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
|
||||||
}
|
}
|
||||||
// if(vehicleRoute.getDepartureTime() != vehicleRoute.g)
|
jobInsertionHandler.handleJobInsertion(job, insertionData, vehicleRoute);
|
||||||
if(job instanceof Service) {
|
|
||||||
vehicleRoute.getTourActivities().addActivity(insertionData.getDeliveryInsertionIndex(), activityFactory.createActivity((Service)job));
|
|
||||||
vehicleRoute.setDepartureTime(insertionData.getVehicleDepartureTime());
|
|
||||||
}
|
|
||||||
else throw new IllegalStateException("neither service nor shipment. this is not supported.");
|
|
||||||
|
|
||||||
insertionListeners.informJobInserted(job, vehicleRoute, insertionData.getInsertionCost(), insertionData.getAdditionalTime());
|
insertionListeners.informJobInserted(job, vehicleRoute, insertionData.getInsertionCost(), insertionData.getAdditionalTime());
|
||||||
// updateTour(vehicleRoute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCal
|
||||||
private HardActivityLevelConstraint hardConstraint;
|
private HardActivityLevelConstraint hardConstraint;
|
||||||
|
|
||||||
private VehicleRoutingTransportCosts routingCosts;
|
private VehicleRoutingTransportCosts routingCosts;
|
||||||
|
|
||||||
private VehicleRoutingActivityCosts activityCosts;
|
private VehicleRoutingActivityCosts activityCosts;
|
||||||
|
|
||||||
public LocalActivityInsertionCostsCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts actCosts, HardActivityLevelConstraint hardActivityLevelConstraint) {
|
public LocalActivityInsertionCostsCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts actCosts, HardActivityLevelConstraint hardActivityLevelConstraint) {
|
||||||
|
|
@ -66,12 +67,16 @@ class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCal
|
||||||
double oldCosts;
|
double oldCosts;
|
||||||
double oldTime;
|
double oldTime;
|
||||||
if(iFacts.getRoute().isEmpty()){
|
if(iFacts.getRoute().isEmpty()){
|
||||||
oldCosts = 0.0;
|
double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
|
||||||
oldTime = 0.0;
|
double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
|
||||||
|
|
||||||
|
double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
|
||||||
|
oldCosts = tp_costs_prevAct_nextAct + actCost_nextAct;
|
||||||
|
oldTime = (nextAct.getArrTime() - depTimeAtPrevAct);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
|
double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
|
||||||
double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getNewDriver(), iFacts.getNewVehicle());
|
double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
|
||||||
|
|
||||||
double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
|
double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
|
||||||
oldCosts = tp_costs_prevAct_nextAct + actCost_nextAct;
|
oldCosts = tp_costs_prevAct_nextAct + actCost_nextAct;
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{
|
||||||
if(totalActivityInsertionCosts < bestCost){
|
if(totalActivityInsertionCosts < bestCost){
|
||||||
bestCost = totalActivityInsertionCosts;
|
bestCost = totalActivityInsertionCosts;
|
||||||
pickupInsertionIndex = i;
|
pickupInsertionIndex = i;
|
||||||
deliveryInsertionIndex = activities.size() - 1;
|
deliveryInsertionIndex = activities.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//update prevAct and endTime
|
//update prevAct and endTime
|
||||||
|
|
@ -166,43 +166,11 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{
|
||||||
double totalActivityInsertionCosts = pickupAIC.getAdditionalCosts() + deliveryAIC.getAdditionalCosts();
|
double totalActivityInsertionCosts = pickupAIC.getAdditionalCosts() + deliveryAIC.getAdditionalCosts();
|
||||||
if(totalActivityInsertionCosts < bestCost){
|
if(totalActivityInsertionCosts < bestCost){
|
||||||
bestCost = totalActivityInsertionCosts;
|
bestCost = totalActivityInsertionCosts;
|
||||||
pickupInsertionIndex = activities.size() - 1;
|
pickupInsertionIndex = activities.size();
|
||||||
deliveryInsertionIndex = activities.size() - 1;
|
deliveryInsertionIndex = activities.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for(TourActivity nextAct : activities){
|
|
||||||
// if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
|
|
||||||
// ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActEndTime);
|
|
||||||
// if(mc != null){
|
|
||||||
// if(mc.getAdditionalCosts() < bestCost){
|
|
||||||
// bestCost = mc.getAdditionalCosts();
|
|
||||||
// bestMarginals = mc;
|
|
||||||
// insertionIndex = actIndex;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// double nextActArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActEndTime, newDriver, newVehicle);
|
|
||||||
// double nextActEndTime = CalcUtils.getActivityEndTime(nextActArrTime, nextAct);
|
|
||||||
//
|
|
||||||
// prevActEndTime = nextActEndTime;
|
|
||||||
//
|
|
||||||
// prevAct = nextAct;
|
|
||||||
// actIndex++;
|
|
||||||
// }
|
|
||||||
// End nextAct = end;
|
|
||||||
// if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
|
|
||||||
// ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActEndTime);
|
|
||||||
// if(mc != null) {
|
|
||||||
// if(mc.getAdditionalCosts() < bestCost){
|
|
||||||
// bestCost = mc.getAdditionalCosts();
|
|
||||||
// bestMarginals = mc;
|
|
||||||
// insertionIndex = actIndex;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(pickupInsertionIndex == InsertionData.NO_INDEX) {
|
if(pickupInsertionIndex == InsertionData.NO_INDEX) {
|
||||||
return InsertionData.noInsertionFound();
|
return InsertionData.noInsertionFound();
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +179,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{
|
||||||
return insertionData;
|
return insertionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivityInsertionCosts calculate(InsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) {
|
private ActivityInsertionCosts calculate(InsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) {
|
||||||
return activityInsertionCostsCalculator.calculate(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct);
|
return activityInsertionCostsCalculator.calculate(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,14 +236,25 @@ public class VehicleRoutingProblem {
|
||||||
if(job instanceof Service) {
|
if(job instanceof Service) {
|
||||||
addService((Service) job);
|
addService((Service) job);
|
||||||
}
|
}
|
||||||
|
else if(job instanceof Shipment){
|
||||||
|
addShipment((Shipment)job);
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); }
|
// if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); }
|
||||||
jobs.put(job.getId(),job);
|
// coordinates.put(job.getLocationId(), job.getCoord());
|
||||||
|
// jobs.put(job.getId(),job);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void addShipment(Shipment job) {
|
||||||
|
if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); }
|
||||||
|
coordinates.put(job.getPickupLocation(), job.getPickupCoord());
|
||||||
|
coordinates.put(job.getDeliveryLocation(), job.getDeliveryCoord());
|
||||||
|
jobs.put(job.getId(),job);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a vehicle.
|
* Adds a vehicle.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -80,4 +80,8 @@ public final class DeliverShipment implements DeliveryActivity{
|
||||||
return new DeliverShipment(this);
|
return new DeliverShipment(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[act="+getName()+"][loc="+getLocationId()+"]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,4 +80,9 @@ public final class PickupShipment implements PickupActivity{
|
||||||
return new PickupShipment(this);
|
return new PickupShipment(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[act="+getName()+"][loc="+getLocationId()+"]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ public class TourActivities {
|
||||||
|
|
||||||
public void addActivity(int insertionIndex, TourActivity act) {
|
public void addActivity(int insertionIndex, TourActivity act) {
|
||||||
assert insertionIndex >= 0 : "insertionIndex == 0, this cannot be";
|
assert insertionIndex >= 0 : "insertionIndex == 0, this cannot be";
|
||||||
|
if(tourActivities.contains(act)) throw new IllegalStateException("act " + act + " already in tour. cannot add act twice.");
|
||||||
/*
|
/*
|
||||||
* if 1 --> between start and act(0) --> act(0)
|
* if 1 --> between start and act(0) --> act(0)
|
||||||
* if 2 && 2 <= acts.size --> between act(0) and act(1) --> act(1)
|
* if 2 && 2 <= acts.size --> between act(0) and act(1) --> act(1)
|
||||||
|
|
@ -164,6 +165,12 @@ public class TourActivities {
|
||||||
addJob(act);
|
addJob(act);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds activity.
|
||||||
|
*
|
||||||
|
* @throw {@link IllegalStateException} if same activity is added twice.
|
||||||
|
* @param act
|
||||||
|
*/
|
||||||
public void addActivity(TourActivity act){
|
public void addActivity(TourActivity act){
|
||||||
if(tourActivities.contains(act)) throw new IllegalStateException("act " + act + " already in tour. cannot add act twice.");
|
if(tourActivities.contains(act)) throw new IllegalStateException("act " + act + " already in tour. cannot add act twice.");
|
||||||
tourActivities.add(act);
|
tourActivities.add(act);
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,11 @@ public class VehicleRoute {
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[start="+start+"][end=" + end + "][departureTime=" + start.getEndTime() + "][vehicle=" + vehicle + "][driver=" + driver + "][nuOfActs="+tourActivities.getActivities().size()+"]";
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setVehicleRouteCostCalculator(VehicleRouteCostCalculator costAccumulator){
|
public void setVehicleRouteCostCalculator(VehicleRouteCostCalculator costAccumulator){
|
||||||
this.costCalculator = costAccumulator;
|
this.costCalculator = costAccumulator;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package algorithms;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.awt.image.CropImageFilter;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import util.CrowFlyCosts;
|
||||||
|
import util.Locations;
|
||||||
|
import util.ManhattanCosts;
|
||||||
|
import basics.Service;
|
||||||
|
import basics.Shipment;
|
||||||
|
|
||||||
|
public class AverageJobDistanceTest {
|
||||||
|
|
||||||
|
|
||||||
|
private CrowFlyCosts routingCosts;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void doBefore(){
|
||||||
|
Locations locations = new Locations(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Coordinate getCoord(String id) {
|
||||||
|
//assume: locationId="x,y"
|
||||||
|
String[] splitted = id.split(",");
|
||||||
|
return Coordinate.newInstance(Double.parseDouble(splitted[0]),
|
||||||
|
Double.parseDouble(splitted[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
routingCosts = new CrowFlyCosts(locations);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceOfTwoEqualShipmentsShouldBeSmallerThanAnyOtherDistance(){
|
||||||
|
Shipment s1 = Shipment.Builder.newInstance("s1", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build();
|
||||||
|
Shipment s2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build();
|
||||||
|
|
||||||
|
double dist = new AvgJobDistance(routingCosts).calculateDistance(s1, s2);
|
||||||
|
|
||||||
|
for(int i=0;i<10;i++){
|
||||||
|
for(int j=0;j<10;j++){
|
||||||
|
Shipment other1 = Shipment.Builder.newInstance("s1", 1).setPickupLocation("0,0").setDeliveryLocation(i+","+j).build();
|
||||||
|
Shipment other2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build();
|
||||||
|
double dist2 = new AvgJobDistance(routingCosts).calculateDistance(other1, other2);
|
||||||
|
System.out.println("("+i+","+j+"), dist=" + dist + ", dist2=" + dist2);
|
||||||
|
assertTrue(dist<=dist2+dist2*0.001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenServicesHaveSameLocation_distanceShouldBeZero(){
|
||||||
|
Service s1 = Service.Builder.newInstance("s1", 1).setLocationId("10,0").build();
|
||||||
|
Service s2 = Service.Builder.newInstance("s2", 1).setLocationId("10,0").build();
|
||||||
|
|
||||||
|
double dist = new AvgJobDistance(routingCosts).calculateDistance(s1, s2);
|
||||||
|
assertEquals(0.0,dist,0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (C) 2013 Stefan Schroeder
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 3.0 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
******************************************************************************/
|
||||||
|
package algorithms;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import util.Solutions;
|
||||||
|
import algorithms.HardConstraints.HardActivityLevelConstraintManager;
|
||||||
|
import algorithms.StateManager.StateImpl;
|
||||||
|
import algorithms.StateUpdates.UpdateActivityTimes;
|
||||||
|
import algorithms.StateUpdates.UpdateCostsAtAllLevels;
|
||||||
|
import algorithms.StateUpdates.UpdateEarliestStartTimeWindowAtActLocations;
|
||||||
|
import algorithms.StateUpdates.UpdateLatestOperationStartTimeAtActLocations;
|
||||||
|
import algorithms.acceptors.AcceptNewIfBetterThanWorst;
|
||||||
|
import algorithms.selectors.SelectBest;
|
||||||
|
import basics.Delivery;
|
||||||
|
import basics.Job;
|
||||||
|
import basics.Pickup;
|
||||||
|
import basics.Shipment;
|
||||||
|
import basics.VehicleRoutingAlgorithm;
|
||||||
|
import basics.VehicleRoutingProblem;
|
||||||
|
import basics.VehicleRoutingProblemSolution;
|
||||||
|
import basics.algo.InsertionStartsListener;
|
||||||
|
import basics.algo.JobInsertedListener;
|
||||||
|
import basics.algo.SearchStrategy;
|
||||||
|
import basics.algo.SearchStrategyManager;
|
||||||
|
import basics.algo.SolutionCostCalculator;
|
||||||
|
import basics.route.TourActivity;
|
||||||
|
import basics.route.Vehicle;
|
||||||
|
import basics.route.VehicleImpl;
|
||||||
|
import basics.route.VehicleRoute;
|
||||||
|
import basics.route.VehicleType;
|
||||||
|
import basics.route.VehicleTypeImpl;
|
||||||
|
|
||||||
|
public class BuildPDVRPWithShipmentsAlgoFromScratchTest {
|
||||||
|
|
||||||
|
VehicleRoutingProblem vrp;
|
||||||
|
|
||||||
|
VehicleRoutingAlgorithm vra;
|
||||||
|
|
||||||
|
static Logger log = Logger.getLogger(BuildPDVRPWithShipmentsAlgoFromScratchTest.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup(){
|
||||||
|
|
||||||
|
VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
|
||||||
|
|
||||||
|
VehicleType type = VehicleTypeImpl.Builder.newInstance("t", 2).setCostPerDistance(1.0).build();
|
||||||
|
Vehicle v = VehicleImpl.Builder.newInstance("v").setLocationCoord(Coordinate.newInstance(-1, -1)).setType(type).build();
|
||||||
|
|
||||||
|
Shipment s1 = Shipment.Builder.newInstance("s1", 1).setPickupCoord(Coordinate.newInstance(0, 0)).setDeliveryCoord(Coordinate.newInstance(10, 10)).build();
|
||||||
|
Shipment s2 = Shipment.Builder.newInstance("s2", 1).setPickupCoord(Coordinate.newInstance(1, 1)).setDeliveryCoord(Coordinate.newInstance(10, 10)).build();
|
||||||
|
builder.addJob(s1).addJob(s2);
|
||||||
|
builder.addVehicle(v);
|
||||||
|
|
||||||
|
vrp = builder.build();
|
||||||
|
|
||||||
|
final StateManagerImpl stateManager = new StateManagerImpl();
|
||||||
|
|
||||||
|
HardActivityLevelConstraintManager actLevelConstraintAccumulator = new HardActivityLevelConstraintManager();
|
||||||
|
actLevelConstraintAccumulator.addConstraint(new HardConstraints.HardPickupAndDeliveryActivityLevelConstraint(stateManager));
|
||||||
|
actLevelConstraintAccumulator.addConstraint(new HardConstraints.HardTimeWindowActivityLevelConstraint(stateManager, vrp.getTransportCosts()));
|
||||||
|
|
||||||
|
ActivityInsertionCostsCalculator marginalCalculus = new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actLevelConstraintAccumulator);
|
||||||
|
|
||||||
|
ShipmentInsertionCalculator serviceInsertion = new ShipmentInsertionCalculator(vrp.getTransportCosts(), marginalCalculus, new HardConstraints.HardPickupAndDeliveryLoadConstraint(stateManager));
|
||||||
|
// CalculatesServiceInsertion serviceInsertion = new CalculatesServiceInsertion(vrp.getTransportCosts(), marginalCalculus, new HardConstraints.HardLoadConstraint(stateManager));
|
||||||
|
|
||||||
|
VehicleFleetManager fleetManager = new InfiniteVehicles(vrp.getVehicles());
|
||||||
|
JobInsertionCalculator finalServiceInsertion = new CalculatesVehTypeDepServiceInsertion(fleetManager, serviceInsertion);
|
||||||
|
|
||||||
|
BestInsertion bestInsertion = new BestInsertion(finalServiceInsertion);
|
||||||
|
|
||||||
|
RuinRadial radial = new RuinRadial(vrp, 0.15, new AvgJobDistance(vrp.getTransportCosts()));
|
||||||
|
RuinRandom random = new RuinRandom(vrp, 0.25);
|
||||||
|
|
||||||
|
SolutionCostCalculator solutionCostCalculator = new SolutionCostCalculator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void calculateCosts(VehicleRoutingProblemSolution solution) {
|
||||||
|
double costs = 0.0;
|
||||||
|
for(VehicleRoute route : solution.getRoutes()){
|
||||||
|
costs += stateManager.getRouteState(route, StateTypes.COSTS).toDouble();
|
||||||
|
}
|
||||||
|
solution.setCost(costs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchStrategy randomStrategy = new SearchStrategy(new SelectBest(), new AcceptNewIfBetterThanWorst(1), solutionCostCalculator);
|
||||||
|
RuinAndRecreateModule randomModule = new RuinAndRecreateModule("randomRuin_bestInsertion", bestInsertion, random);
|
||||||
|
randomStrategy.addModule(randomModule);
|
||||||
|
|
||||||
|
SearchStrategy radialStrategy = new SearchStrategy(new SelectBest(), new AcceptNewIfBetterThanWorst(1), solutionCostCalculator);
|
||||||
|
RuinAndRecreateModule radialModule = new RuinAndRecreateModule("radialRuin_bestInsertion", bestInsertion, radial);
|
||||||
|
radialStrategy.addModule(radialModule);
|
||||||
|
|
||||||
|
SearchStrategyManager strategyManager = new SearchStrategyManager();
|
||||||
|
strategyManager.addStrategy(radialStrategy, 0.5);
|
||||||
|
strategyManager.addStrategy(randomStrategy, 0.5);
|
||||||
|
|
||||||
|
vra = new VehicleRoutingAlgorithm(vrp, strategyManager);
|
||||||
|
|
||||||
|
|
||||||
|
vra.getAlgorithmListeners().addListener(new StateUpdates.ResetStateManager(stateManager));
|
||||||
|
|
||||||
|
final RouteActivityVisitor iterateForward = new RouteActivityVisitor();
|
||||||
|
|
||||||
|
iterateForward.addActivityVisitor(new UpdateActivityTimes(vrp.getTransportCosts()));
|
||||||
|
iterateForward.addActivityVisitor(new UpdateEarliestStartTimeWindowAtActLocations(stateManager, vrp.getTransportCosts()));
|
||||||
|
iterateForward.addActivityVisitor(new UpdateCostsAtAllLevels(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager));
|
||||||
|
|
||||||
|
iterateForward.addActivityVisitor(new StateUpdates.UpdateOccuredDeliveriesAtActivityLevel(stateManager));
|
||||||
|
iterateForward.addActivityVisitor(new StateUpdates.UpdateLoadAtActivityLevel(stateManager));
|
||||||
|
|
||||||
|
final ReverseRouteActivityVisitor iterateBackward = new ReverseRouteActivityVisitor();
|
||||||
|
iterateBackward.addActivityVisitor(new UpdateLatestOperationStartTimeAtActLocations(stateManager, vrp.getTransportCosts()));
|
||||||
|
iterateBackward.addActivityVisitor(new StateUpdates.UpdateFuturePickupsAtActivityLevel(stateManager));
|
||||||
|
|
||||||
|
|
||||||
|
InsertionStartsListener loadVehicleInDepot = new InsertionStartsListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs) {
|
||||||
|
for(VehicleRoute route : vehicleRoutes){
|
||||||
|
int loadAtDepot = 0;
|
||||||
|
int loadAtEnd = 0;
|
||||||
|
for(Job j : route.getTourActivities().getJobs()){
|
||||||
|
if(j instanceof Delivery){
|
||||||
|
loadAtDepot += j.getCapacityDemand();
|
||||||
|
}
|
||||||
|
if(j instanceof Pickup){
|
||||||
|
loadAtEnd += j.getCapacityDemand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stateManager.putRouteState(route, StateTypes.LOAD_AT_DEPOT, new StateImpl(loadAtDepot));
|
||||||
|
stateManager.putRouteState(route, StateTypes.LOAD, new StateImpl(loadAtEnd));
|
||||||
|
iterateForward.visit(route);
|
||||||
|
iterateBackward.visit(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
vra.getSearchStrategyManager().addSearchStrategyModuleListener(new RemoveEmptyVehicles(fleetManager));
|
||||||
|
|
||||||
|
JobInsertedListener updateLoadAfterJobHasBeenInserted = new JobInsertedListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) {
|
||||||
|
// log.info("insert job " + job2insert.getClass().toString() + " job " + job2insert + "" + job2insert.getCapacityDemand() + " in route " + inRoute.getTourActivities());
|
||||||
|
|
||||||
|
if(job2insert instanceof Delivery){
|
||||||
|
int loadAtDepot = (int) stateManager.getRouteState(inRoute, StateTypes.LOAD_AT_DEPOT).toDouble();
|
||||||
|
// log.info("loadAtDepot="+loadAtDepot);
|
||||||
|
stateManager.putRouteState(inRoute, StateTypes.LOAD_AT_DEPOT, new StateImpl(loadAtDepot + job2insert.getCapacityDemand()));
|
||||||
|
}
|
||||||
|
if(job2insert instanceof Pickup){
|
||||||
|
int loadAtEnd = (int) stateManager.getRouteState(inRoute, StateTypes.LOAD).toDouble();
|
||||||
|
// log.info("loadAtEnd="+loadAtEnd);
|
||||||
|
stateManager.putRouteState(inRoute, StateTypes.LOAD, new StateImpl(loadAtEnd + job2insert.getCapacityDemand()));
|
||||||
|
}
|
||||||
|
iterateForward.visit(inRoute);
|
||||||
|
iterateBackward.visit(inRoute);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bestInsertion.addListener(loadVehicleInDepot);
|
||||||
|
bestInsertion.addListener(updateLoadAfterJobHasBeenInserted);
|
||||||
|
|
||||||
|
VehicleRoutingProblemSolution iniSolution = new CreateInitialSolution(bestInsertion, solutionCostCalculator).createInitialSolution(vrp);
|
||||||
|
// System.out.println("ini: costs="+iniSolution.getCost()+";#routes="+iniSolution.getRoutes().size());
|
||||||
|
vra.addInitialSolution(iniSolution);
|
||||||
|
|
||||||
|
vra.setNuOfIterations(10000);
|
||||||
|
vra.setPrematureBreak(1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test(){
|
||||||
|
Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
|
||||||
|
VehicleRoutingProblemSolution best = Solutions.getBest(solutions);
|
||||||
|
System.out.println(best.getCost());
|
||||||
|
for(VehicleRoute r : best.getRoutes()){
|
||||||
|
System.out.println(r);
|
||||||
|
System.out.println("#jobs="+r.getTourActivities().jobSize());
|
||||||
|
System.out.println(r.getStart());
|
||||||
|
for(TourActivity act : r.getTourActivities().getActivities()){
|
||||||
|
System.out.println(act);
|
||||||
|
}
|
||||||
|
System.out.println(r.getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
// for()
|
||||||
|
|
||||||
|
// new VrpXMLWriter(vrp, solutions).write("output/pd_solomon_r101.xml");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package algorithms;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import util.Locations;
|
||||||
|
import util.ManhattanCosts;
|
||||||
|
import algorithms.HardConstraints.HardActivityLevelConstraint;
|
||||||
|
import algorithms.HardConstraints.HardRouteLevelConstraint;
|
||||||
|
import basics.Shipment;
|
||||||
|
import basics.costs.VehicleRoutingActivityCosts;
|
||||||
|
import basics.costs.VehicleRoutingTransportCosts;
|
||||||
|
import basics.route.Driver;
|
||||||
|
import basics.route.TourActivity;
|
||||||
|
import basics.route.Vehicle;
|
||||||
|
import basics.route.VehicleImpl;
|
||||||
|
import basics.route.VehicleRoute;
|
||||||
|
import basics.route.VehicleType;
|
||||||
|
import basics.route.VehicleTypeImpl;
|
||||||
|
|
||||||
|
public class ShipmentInsertionCalculatorTest {
|
||||||
|
|
||||||
|
VehicleRoutingTransportCosts routingCosts;
|
||||||
|
|
||||||
|
VehicleRoutingActivityCosts activityCosts = new VehicleRoutingActivityCosts(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getActivityCost(TourActivity tourAct, double arrivalTime,Driver driver, Vehicle vehicle) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
HardActivityLevelConstraint hardActivityLevelConstraint = new HardActivityLevelConstraint() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fulfilled(InsertionContext iFacts, TourActivity prevAct,TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HardRouteLevelConstraint hardRouteLevelConstraint = new HardRouteLevelConstraint(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fulfilled(InsertionContext insertionContext) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityInsertionCostsCalculator activityInsertionCostsCalculator;
|
||||||
|
|
||||||
|
ShipmentInsertionCalculator insertionCalculator;
|
||||||
|
|
||||||
|
Vehicle vehicle;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void doBefore(){
|
||||||
|
Locations locations = new Locations(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Coordinate getCoord(String id) {
|
||||||
|
//assume: locationId="x,y"
|
||||||
|
String[] splitted = id.split(",");
|
||||||
|
return Coordinate.newInstance(Double.parseDouble(splitted[0]),
|
||||||
|
Double.parseDouble(splitted[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
routingCosts = new ManhattanCosts(locations);
|
||||||
|
VehicleType type = VehicleTypeImpl.Builder.newInstance("t", 1).setCostPerDistance(1).build();
|
||||||
|
vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("0,0").setType(type).build();
|
||||||
|
activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, hardActivityLevelConstraint);
|
||||||
|
createInsertionCalculator(hardRouteLevelConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createInsertionCalculator(HardRouteLevelConstraint hardRouteLevelConstraint) {
|
||||||
|
insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue(){
|
||||||
|
Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build();
|
||||||
|
VehicleRoute route = VehicleRoute.emptyRoute();
|
||||||
|
|
||||||
|
InsertionData iData = insertionCalculator.calculate(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE);
|
||||||
|
assertEquals(40.0,iData.getInsertionCost(),0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCalculatingInsertionIntoExistingRoute_itShouldReturnCorrectCosts(){
|
||||||
|
Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build();
|
||||||
|
Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build();
|
||||||
|
VehicleRoute route = VehicleRoute.emptyRoute();
|
||||||
|
new Inserter(new InsertionListeners()).insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route);
|
||||||
|
|
||||||
|
InsertionData iData = insertionCalculator.calculate(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE);
|
||||||
|
assertEquals(0.0,iData.getInsertionCost(),0.05);
|
||||||
|
assertEquals(1,iData.getPickupInsertionIndex());
|
||||||
|
assertEquals(2,iData.getDeliveryInsertionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenInsertingShipmentInRouteWithNotEnoughCapacity_itShouldReturnNoInsertion(){
|
||||||
|
Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build();
|
||||||
|
Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build();
|
||||||
|
VehicleRoute route = VehicleRoute.emptyRoute();
|
||||||
|
new Inserter(new InsertionListeners()).insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route);
|
||||||
|
createInsertionCalculator(new HardRouteLevelConstraint() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fulfilled(InsertionContext insertionContext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
InsertionData iData = insertionCalculator.calculate(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE);
|
||||||
|
assertEquals(InsertionData.noInsertionFound(),iData);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenInsertingThirdShipment_itShouldCalcCorrectVal(){
|
||||||
|
Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build();
|
||||||
|
Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build();
|
||||||
|
Shipment shipment3 = Shipment.Builder.newInstance("s3", 1).setPickupLocation("0,0").setDeliveryLocation("9,10").build();
|
||||||
|
|
||||||
|
VehicleRoute route = VehicleRoute.emptyRoute();
|
||||||
|
Inserter inserter = new Inserter(new InsertionListeners());
|
||||||
|
inserter.insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route);
|
||||||
|
inserter.insertJob(shipment2, new InsertionData(0,1,2,vehicle,null),route);
|
||||||
|
|
||||||
|
InsertionData iData = insertionCalculator.calculate(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE);
|
||||||
|
assertEquals(0.0,iData.getInsertionCost(),0.05);
|
||||||
|
assertEquals(0,iData.getPickupInsertionIndex());
|
||||||
|
assertEquals(1,iData.getDeliveryInsertionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenInsertingThirdShipment_itShouldCalcCorrectVal2(){
|
||||||
|
Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build();
|
||||||
|
Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build();
|
||||||
|
Shipment shipment3 = Shipment.Builder.newInstance("s3", 1).setPickupLocation("0,0").setDeliveryLocation("9,9").build();
|
||||||
|
|
||||||
|
VehicleRoute route = VehicleRoute.emptyRoute();
|
||||||
|
Inserter inserter = new Inserter(new InsertionListeners());
|
||||||
|
inserter.insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route);
|
||||||
|
inserter.insertJob(shipment2, new InsertionData(0,1,2,vehicle,null),route);
|
||||||
|
|
||||||
|
InsertionData iData = insertionCalculator.calculate(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE);
|
||||||
|
assertEquals(2.0,iData.getInsertionCost(),0.05);
|
||||||
|
assertEquals(0,iData.getPickupInsertionIndex());
|
||||||
|
assertEquals(1,iData.getDeliveryInsertionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -87,6 +87,8 @@ public class TestTour {
|
||||||
assertEquals(2,tour.getActivities().size());
|
assertEquals(2,tour.getActivities().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenRemovingShipment_tourShouldNotServiceItAnymore(){
|
public void whenRemovingShipment_tourShouldNotServiceItAnymore(){
|
||||||
Shipment s = Shipment.Builder.newInstance("s", 1).setDeliveryLocation("delLoc").setPickupLocation("pickLoc").build();
|
Shipment s = Shipment.Builder.newInstance("s", 1).setDeliveryLocation("delLoc").setPickupLocation("pickLoc").build();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue