From 8950d49e5a7f2d7d2bba3d17eecea8980ab5fe6e Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Tue, 16 Jul 2013 21:03:07 +0200 Subject: [PATCH] add VehicleRoutingTransportCostMatrix to easily consider time and distance matrices, and add an example that illustrates the matrix --- .../VehicleRoutingTransportCostsMatrix.java | 192 ++++++++++++++++++ jsprit-examples/input/fastAlgo.xml | 74 +++++++ ...nces => refuseCollectionExample_Distances} | 0 ...ies => refuseCollectionExample_Quantities} | 0 .../main/java/examples/CostMatrixExample.java | 93 +++++++++ .../examples/RefuseCollectionExample.java | 20 +- 6 files changed, 370 insertions(+), 9 deletions(-) create mode 100644 jsprit-core/src/main/java/util/VehicleRoutingTransportCostsMatrix.java create mode 100755 jsprit-examples/input/fastAlgo.xml rename jsprit-examples/input/{RefuseCollectionExample_Distances => refuseCollectionExample_Distances} (100%) rename jsprit-examples/input/{RefuseCollectionExample_Quantities => refuseCollectionExample_Quantities} (100%) create mode 100644 jsprit-examples/src/main/java/examples/CostMatrixExample.java diff --git a/jsprit-core/src/main/java/util/VehicleRoutingTransportCostsMatrix.java b/jsprit-core/src/main/java/util/VehicleRoutingTransportCostsMatrix.java new file mode 100644 index 00000000..2832d66c --- /dev/null +++ b/jsprit-core/src/main/java/util/VehicleRoutingTransportCostsMatrix.java @@ -0,0 +1,192 @@ +package util; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import basics.costs.VehicleRoutingTransportCosts; +import basics.route.Driver; +import basics.route.Vehicle; +import basics.route.VehicleTypeImpl.VehicleCostParams; + + +public class VehicleRoutingTransportCostsMatrix implements VehicleRoutingTransportCosts { + + static class RelationKey { + + static RelationKey newKey(String from, String to){ + int fromInt = Integer.parseInt(from); + int toInt = Integer.parseInt(to); + if(fromInt < toInt){ + return new RelationKey(from, to); + } + else { + return new RelationKey(to, from); + } + } + + final String from; + final String to; + + public RelationKey(String from, String to) { + super(); + this.from = from; + this.to = to; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((from == null) ? 0 : from.hashCode()); + result = prime * result + ((to == null) ? 0 : to.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; + RelationKey other = (RelationKey) obj; + if (from == null) { + if (other.from != null) + return false; + } else if (!from.equals(other.from)) + return false; + if (to == null) { + if (other.to != null) + return false; + } else if (!to.equals(other.to)) + return false; + return true; + } + } + + + + public static class Builder { + private static Logger log = Logger.getLogger(Builder.class); + + private boolean isSymmetric; + + private Map distances = new HashMap(); + + private Map times = new HashMap(); + + public static Builder newInstance(boolean isSymmetric){ + return new Builder(isSymmetric); + } + + private Builder(boolean isSymmetric){ + this.isSymmetric = isSymmetric; + } + + public Builder addTransportDistance(String from, String to, double distance){ + RelationKey key = RelationKey.newKey(from, to); + if(distances.containsKey(key)){ + log.warn("distance from " + from + " to " + to + " already exists. This overrides distance."); + } + distances.put(key, distance); + return this; + } + + public Builder addTransportTime(String from, String to, double time){ + RelationKey key = RelationKey.newKey(from, to); + if(times.containsKey(key)){ + log.warn("transport-time from " + from + " to " + to + " already exists. This overrides distance."); + } + times.put(key, time); + return this; + } + + public VehicleRoutingTransportCostsMatrix build(){ + return new VehicleRoutingTransportCostsMatrix(this); + } + } + + private Map distances = new HashMap(); + + private Map times = new HashMap(); + + private boolean isSymmetric; + + private VehicleRoutingTransportCostsMatrix(Builder builder){ + this.isSymmetric = builder.isSymmetric; + distances.putAll(builder.distances); + times.putAll(builder.times); + } + + + @Override + public double getTransportTime(String fromId, String toId, double departureTime, Driver driver, Vehicle vehicle) { + return getTime(fromId, toId); + } + + + private double getTime(String fromId, String toId) { + if(fromId.equals(toId)) return 0.0; + RelationKey key = RelationKey.newKey(fromId, toId); + if(!isSymmetric){ + if(times.containsKey(key)) return times.get(key); + else throw new IllegalStateException("time value for relation from " + fromId + " to " + toId + " does not exist"); + } + else{ + Double time = times.get(key); + if(time == null){ + time = times.get(RelationKey.newKey(toId, fromId)); + } + if(time != null) return time; + else throw new IllegalStateException("time value for relation from " + fromId + " to " + toId + " does not exist"); + } + } + + private double getDistance(String fromId, String toId) { + if(fromId.equals(toId)) return 0.0; + RelationKey key = RelationKey.newKey(fromId, toId); + if(!isSymmetric){ + if(distances.containsKey(key)) return distances.get(key); + else throw new IllegalStateException("distance value for relation from " + fromId + " to " + toId + " does not exist"); + } + else{ + Double time = distances.get(key); + if(time == null){ + time = distances.get(RelationKey.newKey(toId, fromId)); + } + if(time != null) return time; + else throw new IllegalStateException("distance value for relation from " + fromId + " to " + toId + " does not exist"); + } + } + + @Override + public double getBackwardTransportTime(String fromId, String toId, double arrivalTime, Driver driver, Vehicle vehicle) { + return getTransportTime(fromId, toId, arrivalTime, driver, vehicle); + } + + + @Override + public double getTransportCost(String fromId, String toId, double departureTime, Driver driver, Vehicle vehicle) { + if(vehicle == null) return getDistance(fromId, toId); + VehicleCostParams costParams = vehicle.getType().getVehicleCostParams(); + return costParams.perDistanceUnit*getDistance(fromId, toId) + costParams.perTimeUnit*getTime(fromId, toId); + } + + + @Override + public double getBackwardTransportCost(String fromId, String toId, double arrivalTime, Driver driver, Vehicle vehicle) { + return getTransportCost(fromId, toId, arrivalTime, driver, vehicle); + } + + + +} diff --git a/jsprit-examples/input/fastAlgo.xml b/jsprit-examples/input/fastAlgo.xml new file mode 100755 index 00000000..f887f49f --- /dev/null +++ b/jsprit-examples/input/fastAlgo.xml @@ -0,0 +1,74 @@ + + + + + 2000 + + 0.01 + 50 + + + + + + + + 1 + + + + + + + + 0.3 + + + + + + 0.5 + + + + + + + + + + 0.15 + + + + + + 0.5 + + + + + + + + diff --git a/jsprit-examples/input/RefuseCollectionExample_Distances b/jsprit-examples/input/refuseCollectionExample_Distances similarity index 100% rename from jsprit-examples/input/RefuseCollectionExample_Distances rename to jsprit-examples/input/refuseCollectionExample_Distances diff --git a/jsprit-examples/input/RefuseCollectionExample_Quantities b/jsprit-examples/input/refuseCollectionExample_Quantities similarity index 100% rename from jsprit-examples/input/RefuseCollectionExample_Quantities rename to jsprit-examples/input/refuseCollectionExample_Quantities diff --git a/jsprit-examples/src/main/java/examples/CostMatrixExample.java b/jsprit-examples/src/main/java/examples/CostMatrixExample.java new file mode 100644 index 00000000..85959b98 --- /dev/null +++ b/jsprit-examples/src/main/java/examples/CostMatrixExample.java @@ -0,0 +1,93 @@ +package examples; + +import java.util.Collection; + +import util.Solutions; +import util.VehicleRoutingTransportCostsMatrix; +import algorithms.GreedySchrimpfFactory; +import algorithms.VehicleRoutingAlgorithms; +import analysis.SolutionPlotter; +import analysis.SolutionPrinter; +import analysis.SolutionPrinter.Print; +import basics.Service; +import basics.VehicleRoutingAlgorithm; +import basics.VehicleRoutingProblem; +import basics.VehicleRoutingProblem.FleetSize; +import basics.VehicleRoutingProblemSolution; +import basics.costs.VehicleRoutingTransportCosts; +import basics.route.Vehicle; +import basics.route.VehicleImpl; +import basics.route.VehicleType; +import basics.route.VehicleTypeImpl; + +/** + * Illustrates how you can use jsprit with an already compiled distance and time matrix. + * + * @author schroeder + * + */ +public class CostMatrixExample { + + /** + * @param args + */ + public static void main(String[] args) { + + VehicleType type = VehicleTypeImpl.Builder.newInstance("type", 2).setCostPerDistance(1).setCostPerTime(2).build(); + Vehicle vehicle = VehicleImpl.Builder.newInstance("vehicle").setLocationId("0").setType(type).build(); + + Service s1 = Service.Builder.newInstance("1", 1).setLocationId("1").build(); + Service s2 = Service.Builder.newInstance("2", 1).setLocationId("2").build(); + Service s3 = Service.Builder.newInstance("3", 1).setLocationId("3").build(); + + + /* + * Assume the following symmetric distance-matrix + * from,to,distance + * 0,1,10.0 + * 0,2,20.0 + * 0,3,5.0 + * 1,2,4.0 + * 1,3,1.0 + * 2,3,2.0 + * + * and this time-matrix + * 0,1,5.0 + * 0,2,10.0 + * 0,3,2.5 + * 1,2,2.0 + * 1,3,0.5 + * 2,3,1.0 + */ + //define a matrix-builder building a symmetric matrix + VehicleRoutingTransportCostsMatrix.Builder costMatrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true); + costMatrixBuilder.addTransportDistance("0", "1", 10.0); + costMatrixBuilder.addTransportDistance("0", "2", 20.0); + costMatrixBuilder.addTransportDistance("0", "3", 5.0); + costMatrixBuilder.addTransportDistance("1", "2", 4.0); + costMatrixBuilder.addTransportDistance("1", "3", 1.0); + costMatrixBuilder.addTransportDistance("2", "3", 2.0); + + costMatrixBuilder.addTransportTime("0", "1", 10.0); + costMatrixBuilder.addTransportTime("0", "2", 20.0); + costMatrixBuilder.addTransportTime("0", "3", 5.0); + costMatrixBuilder.addTransportTime("1", "2", 4.0); + costMatrixBuilder.addTransportTime("1", "3", 1.0); + costMatrixBuilder.addTransportTime("2", "3", 2.0); + + VehicleRoutingTransportCosts costMatrix = costMatrixBuilder.build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(FleetSize.INFINITE).setRoutingCost(costMatrix) + .addVehicle(vehicle).addService(s1).addService(s2).addService(s3).build(); + + VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "input/fastAlgo.xml"); + + Collection solutions = vra.searchSolutions(); + + SolutionPrinter.print(Solutions.getBest(solutions), Print.VERBOSE); + + SolutionPlotter.plotSolutionAsPNG(vrp, Solutions.getBest(solutions), "output/yo.png", "po"); + + } + +} diff --git a/jsprit-examples/src/main/java/examples/RefuseCollectionExample.java b/jsprit-examples/src/main/java/examples/RefuseCollectionExample.java index 4c54c474..1e9df400 100644 --- a/jsprit-examples/src/main/java/examples/RefuseCollectionExample.java +++ b/jsprit-examples/src/main/java/examples/RefuseCollectionExample.java @@ -30,6 +30,8 @@ import java.util.HashMap; import java.util.Map; import util.Solutions; +import util.VehicleRoutingTransportCostsMatrix; +import util.VehicleRoutingTransportCostsMatrix.Builder; import algorithms.GreedySchrimpfFactory; import analysis.SolutionPrinter; import analysis.SolutionPrinter.Print; @@ -179,13 +181,12 @@ public class RefuseCollectionExample { readDemandQuantities(vrpBuilder); /* - * read distances + * create cost-matrix */ - Map distances = new HashMap(); - readDistances(distances); - - VehicleRoutingTransportCosts routingCosts = new RoutingCosts(distances); - vrpBuilder.setRoutingCost(routingCosts); + VehicleRoutingTransportCostsMatrix.Builder matrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true); + readDistances(matrixBuilder); + + vrpBuilder.setRoutingCost(matrixBuilder.build()); VehicleRoutingProblem vrp = vrpBuilder.build(); @@ -199,6 +200,7 @@ public class RefuseCollectionExample { } + private static void readDemandQuantities(VehicleRoutingProblem.Builder vrpBuilder) throws FileNotFoundException, IOException { BufferedReader reader = new BufferedReader(new FileReader(new File("input/RefuseCollectionExample_Quantities"))); String line = null; @@ -221,7 +223,8 @@ public class RefuseCollectionExample { reader.close(); } - private static void readDistances(Map distances) throws IOException { + + private static void readDistances(Builder matrixBuilder) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(new File("input/RefuseCollectionExample_Distances"))); String line = null; boolean firstLine = true; @@ -231,8 +234,7 @@ public class RefuseCollectionExample { continue; } String[] lineTokens = line.split(","); - RelationKey key = RelationKey.newKey(lineTokens[0],lineTokens[1]); - distances.put(key, Integer.parseInt(lineTokens[2])); + matrixBuilder.addTransportDistance(lineTokens[0],lineTokens[1], Integer.parseInt(lineTokens[2])); } reader.close();