From 11639fe08871333ffadcd87038161a5344a693df Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Tue, 14 Jan 2014 10:39:02 -0500 Subject: [PATCH] add helper to easily add penalty-vehicles --- .../core/problem/VehicleRoutingProblem.java | 109 +++++++++++++++ .../problem/VehicleRoutingProblemTest.java | 124 ++++++++++++++++++ 2 files changed, 233 insertions(+) diff --git a/jsprit-core/src/main/java/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/jsprit/core/problem/VehicleRoutingProblem.java index 4e98f7ca..95c6d62b 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/VehicleRoutingProblem.java @@ -20,7 +20,10 @@ 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 jsprit.core.problem.cost.VehicleRoutingActivityCosts; import jsprit.core.problem.cost.VehicleRoutingTransportCosts; @@ -29,7 +32,9 @@ import jsprit.core.problem.job.Job; import jsprit.core.problem.job.Service; import jsprit.core.problem.job.Shipment; import jsprit.core.problem.solution.route.activity.TourActivity; +import jsprit.core.problem.vehicle.PenaltyVehicleType; import jsprit.core.problem.vehicle.Vehicle; +import jsprit.core.problem.vehicle.VehicleImpl; import jsprit.core.problem.vehicle.VehicleType; import jsprit.core.problem.vehicle.VehicleTypeImpl; import jsprit.core.util.Coordinate; @@ -79,6 +84,57 @@ public class VehicleRoutingProblem { */ public static class Builder { + /** + * Two locTypeKeys are equal if they have the same locationId and typeId + * + * @author schroeder + * + */ + private static class LocTypeKey { + String locationId; + String typeId; + + public LocTypeKey(String locationId, String typeId) { + super(); + this.locationId = locationId; + this.typeId = typeId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((locationId == null) ? 0 : locationId.hashCode()); + result = prime * result + + ((typeId == null) ? 0 : typeId.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; + LocTypeKey other = (LocTypeKey) obj; + if (locationId == null) { + if (other.locationId != null) + return false; + } else if (!locationId.equals(other.locationId)) + return false; + if (typeId == null) { + if (other.typeId != null) + return false; + } else if (!typeId.equals(other.typeId)) + return false; + return true; + } + + } + /** * Returns a new instance of this builder. * @@ -138,6 +194,10 @@ public class VehicleRoutingProblem { } }; + private boolean addPenaltyVehicles = false; + + private double penaltyFactor = 1.0; + /** * @deprecated use static method .newInstance() instead */ @@ -296,9 +356,42 @@ public class VehicleRoutingProblem { logger.warn("set routing costs crowFlyDistance."); transportCosts = new CrowFlyCosts(getLocations()); } + if(addPenaltyVehicles){ + if(fleetSize.equals(FleetSize.INFINITE)){ + logger.warn("penaltyType and FleetSize.INFINITE does not make sense. thus no penalty-types are added."); + } + else{ + addPenaltyVehicles(); + } + } return new VehicleRoutingProblem(this); } + private void addPenaltyVehicles() { + Set locTypeKeys = new HashSet(); + List uniqueVehicles = new ArrayList(); + for(Vehicle v : vehicles){ + LocTypeKey key = new LocTypeKey(v.getLocationId(),v.getType().getTypeId()); + if(!locTypeKeys.contains(key)){ + uniqueVehicles.add(v); + locTypeKeys.add(key); + } + } + for(Vehicle v : uniqueVehicles){ + VehicleTypeImpl t = VehicleTypeImpl.Builder.newInstance(v.getType().getTypeId(), v.getCapacity()) + .setCostPerDistance(penaltyFactor*v.getType().getVehicleCostParams().perDistanceUnit) + .setCostPerTime(penaltyFactor*v.getType().getVehicleCostParams().perTimeUnit) + .setFixedCost(penaltyFactor*v.getType().getVehicleCostParams().fix) + .build(); + PenaltyVehicleType penType = new PenaltyVehicleType(t,penaltyFactor); + String vehicleId = "penaltyVehicle_" + v.getLocationId() + "_" + t.getTypeId(); + Vehicle penVehicle = VehicleImpl.Builder.newInstance(vehicleId).setEarliestStart(v.getEarliestDeparture()) + .setLatestArrival(v.getLatestArrival()).setLocationCoord(v.getCoord()).setLocationId(v.getLocationId()) + .setReturnToDepot(v.isReturnToDepot()).setType(penType).build(); + vehicles.add(penVehicle); + } + } + public Builder addLocation(String id, Coordinate coord) { coordinates.put(id, coord); return this; @@ -359,6 +452,22 @@ public class VehicleRoutingProblem { return this; } + /** + * Adds penaltyVehicles, i.e. for every unique vehicle-location and type combination a penalty-vehicle is constructed having penaltyFactor times higher fixed and variable costs. + * + *

This only makes sense for FleetSize.FINITE. Thus, penaltyVehicles are only added if is FleetSize.FINITE. + *

The id of penaltyVehicles is constructed as follows vehicleId = "penaltyVehicle" + "_" + {locationId} + "_" + {typeId}. + *

By default: no penalty-vehicles are added + * + * @param penaltyFactor + * @return this builder + */ + public Builder addPenaltyVehicles(double penaltyFactor){ + this.addPenaltyVehicles = true; + this.penaltyFactor = penaltyFactor; + return this; + } + /** * Returns an unmodifiable collection of already added jobs. * diff --git a/jsprit-core/src/test/java/jsprit/core/problem/VehicleRoutingProblemTest.java b/jsprit-core/src/test/java/jsprit/core/problem/VehicleRoutingProblemTest.java index f9030222..e5aff4cf 100644 --- a/jsprit-core/src/test/java/jsprit/core/problem/VehicleRoutingProblemTest.java +++ b/jsprit-core/src/test/java/jsprit/core/problem/VehicleRoutingProblemTest.java @@ -17,6 +17,8 @@ package jsprit.core.problem; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -321,4 +323,126 @@ public class VehicleRoutingProblemTest { assertEquals(2,builder.getAddedVehicleTypes().size()); } + + @Test + public void whenSettingAddPenaltyVehicleOptions_itShouldAddPenaltyVehicle(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 0).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("loc").setType(type).build(); + + builder.addVehicle(vehicle); + builder.setFleetSize(FleetSize.FINITE); + builder.addPenaltyVehicles(3.0); + + VehicleRoutingProblem vrp = builder.build(); + + assertEquals(2,vrp.getVehicles().size()); + + boolean penaltyVehicleInCollection = false; + for(Vehicle v : vrp.getVehicles()){ + if(v.getId().equals("penaltyVehicle_loc_type")) penaltyVehicleInCollection = true; + } + assertTrue(penaltyVehicleInCollection); + + } + + @Test + public void whenSettingAddPenaltyVehicleOptionsAndFleetSizeIsInfinite_noPenaltyVehicleIsAdded(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 0).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("loc").setType(type).build(); + + builder.addVehicle(vehicle); + builder.addPenaltyVehicles(3.0); + + VehicleRoutingProblem vrp = builder.build(); + + assertEquals(1,vrp.getVehicles().size()); + + boolean penaltyVehicleInCollection = false; + for(Vehicle v : vrp.getVehicles()){ + if(v.getId().equals("penaltyVehicle_loc_type")) penaltyVehicleInCollection = true; + } + assertFalse(penaltyVehicleInCollection); + + } + + @Test + public void whenSettingAddPenaltyVehicleOptionsAndTwoVehiclesWithSameLocationAndType_onlyOnePenaltyVehicleIsAdded(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 0).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("loc").setType(type).build(); + Vehicle vehicle2 = VehicleImpl.Builder.newInstance("v2").setLocationId("loc").setType(type).build(); + + builder.addVehicle(vehicle); + builder.addVehicle(vehicle2); + builder.setFleetSize(FleetSize.FINITE); + builder.addPenaltyVehicles(3.0); + + VehicleRoutingProblem vrp = builder.build(); + + assertEquals(3,vrp.getVehicles().size()); + + boolean penaltyVehicleInCollection = false; + for(Vehicle v : vrp.getVehicles()){ + if(v.getId().equals("penaltyVehicle_loc_type")) penaltyVehicleInCollection = true; + } + assertTrue(penaltyVehicleInCollection); + + } + + @Test + public void whenSettingAddPenaltyVehicleOptionsAndTwoVehiclesWithDiffLocationAndType_twoPenaltyVehicleIsAdded(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 0).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("loc").setType(type).build(); + Vehicle vehicle2 = VehicleImpl.Builder.newInstance("v2").setLocationId("loc2").setType(type).build(); + + builder.addVehicle(vehicle); + builder.addVehicle(vehicle2); + builder.setFleetSize(FleetSize.FINITE); + builder.addPenaltyVehicles(3.0); + + VehicleRoutingProblem vrp = builder.build(); + + assertEquals(4,vrp.getVehicles().size()); + + boolean penaltyVehicleInCollection = false; + boolean anotherPenVehInCollection = false; + for(Vehicle v : vrp.getVehicles()){ + if(v.getId().equals("penaltyVehicle_loc_type")) penaltyVehicleInCollection = true; + if(v.getId().equals("penaltyVehicle_loc2_type")) anotherPenVehInCollection = true; + } + assertTrue(penaltyVehicleInCollection); + assertTrue(anotherPenVehInCollection); + + } + + @Test + public void whenSettingAddPenaltyVehicleOptionsAndTwoVehiclesWithSameLocationButDiffType_twoPenaltyVehicleIsAdded(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 0).build(); + VehicleType type2 = VehicleTypeImpl.Builder.newInstance("type2", 0).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("loc").setType(type).build(); + Vehicle vehicle2 = VehicleImpl.Builder.newInstance("v2").setLocationId("loc").setType(type2).build(); + + builder.addVehicle(vehicle); + builder.addVehicle(vehicle2); + builder.setFleetSize(FleetSize.FINITE); + builder.addPenaltyVehicles(3.0); + + VehicleRoutingProblem vrp = builder.build(); + + assertEquals(4,vrp.getVehicles().size()); + + boolean penaltyVehicleInCollection = false; + boolean anotherPenVehInCollection = false; + for(Vehicle v : vrp.getVehicles()){ + if(v.getId().equals("penaltyVehicle_loc_type")) penaltyVehicleInCollection = true; + if(v.getId().equals("penaltyVehicle_loc_type2")) anotherPenVehInCollection = true; + } + assertTrue(penaltyVehicleInCollection); + assertTrue(anotherPenVehInCollection); + + } }