From 7a30aba0affc5efa5cabdb85c4335fbdf7595397 Mon Sep 17 00:00:00 2001 From: ShivKJ Date: Mon, 27 Feb 2017 11:20:10 +0530 Subject: [PATCH] Purpose of this MultiVehicleCostMatrix is to enable different costMatrix for different VehicleTypeId (Each VehicleType has a VehicleTypeId). In case, method "public double getTransportCost" is invoked with Vehicle as null then in that case distance corresponding to some default vehicleType is provided. This class is instantiated with the help of Builder class where default vehicleTypeId is supplied. --- .../core/util/MultiVehicleCostMatrix.java | 151 ++++++++++++++++++ .../core/util/MultiVehicleCostMatrixTest.java | 119 ++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrix.java create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrixTest.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrix.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrix.java new file mode 100644 index 000000000..7357d45ba --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrix.java @@ -0,0 +1,151 @@ +package com.graphhopper.jsprit.core.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.cost.AbstractForwardVehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; + +/** + * @author Shiv Krishna Jaiswal + */ + +public class MultiVehicleCostMatrix extends AbstractForwardVehicleRoutingTransportCosts { + + /* + * First index respresents index of from_location + * Second index represents index of to_location + * Third index represents distance and time: 0th entry is for Distance and 1st entry is for time. + * Fourth index represents vehicleType. Each vehicleTypeId is assigned an index. + */ + + private final double[][][][] costMatrix; + + /* + * This Map is used to map vehicleTypeID to an index. + * There is one to one relation between them. + */ + + private final Map vehicleTypeToIndex; + private final int defaultIndex; + + private MultiVehicleCostMatrix(double[][][][] costMatrix, Map vehicleTypeToIndex, int defaultIndex) { + this.costMatrix = costMatrix; + this.vehicleTypeToIndex = vehicleTypeToIndex; + this.defaultIndex = defaultIndex; + } + + /* + * This method give transportation cost between two location for a vehicle. + * Transportation cost is defined as : + * perDistanceCost * distanceBetweenLocation + perTransportTime * timeBetweenLocation + * Parameters, perDistanceCost or perTransportTime, are controlled by VehicleType. + * Cases where Vehicle is null, then distance between the two locations are returned for default vehicleType. + * eg.(DefaultScorer.java:94) + */ + @Override + public double getTransportCost(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { + if (from.getIndex() < 0 || to.getIndex() < 0) + throw new IllegalArgumentException("Index of location must not be less than zero"); + + if (vehicle == null) + return costMatrix[from.getIndex()][to.getIndex()][0][defaultIndex]; + + VehicleTypeImpl.VehicleCostParams costParams = vehicle.getType().getVehicleCostParams(); + + return costParams.perDistanceUnit * costMatrix[from.getIndex()][to.getIndex()][0][vehicleTypeToIndex.get(vehicle.getType().getTypeId()).intValue()] + + costParams.perTransportTimeUnit * getTransportTime(from, to, departureTime, driver, vehicle); + } + + /* + * This method gives time to move between the two location by Vehicle. + */ + + @Override + public double getTransportTime(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { + if (from.getIndex() < 0 || to.getIndex() < 0) + throw new IllegalArgumentException("Index of location must not be less than zero"); + + return costMatrix[from.getIndex()][to.getIndex()][1][vehicle == null ? defaultIndex : vehicleTypeToIndex.get(vehicle.getType().getTypeId()).intValue()]; + } + + /* + * For convenience of retriving the distance and time taken between location, + * two methods are introduced below. + */ + + public double getDistanceBetween(int from, int to, String vehicleType) { + + return costMatrix[from][to][0][vehicleTypeToIndex.get(vehicleType).intValue()]; + } + + public double getTransportTimeBetween(int from, int to, String vehicleType) { + + return costMatrix[from][to][1][vehicleTypeToIndex.get(vehicleType).intValue()]; + } + + public static class Builder { + + private final double[][][][] costMatrix; + private final Map vehicleTypeToIndex; + private final boolean isSymmetric; + private final int defaultIndex; + + private Builder(int location, boolean isSymmetric, Map vehicleTypeIDToIndex, int defaultIndex) { + this.costMatrix = new double[location][location][2][vehicleTypeIDToIndex.size()]; + this.isSymmetric = isSymmetric; + this.defaultIndex = defaultIndex; + this.vehicleTypeToIndex = vehicleTypeIDToIndex; + + } + + /* + * defaultVehicleTypeID is used to incorporate the case when Jsprit call + * method " public double getTransportCost(Location from, Location to, double departureTime, Driver driver, + * Vehicle vehicle)" with vehicle as null or any case similar. So in this case distance corresponding to default + * vehicleTypeId is used. + */ + + public static Builder newInstance(int location, boolean isSymmetric, Collection vehicleTypeIDs, String defaultVehicleID) { + + List vehicleTypeIDsList = new ArrayList<>(new TreeSet<>(vehicleTypeIDs)); + Map vehicleTypeIDToIndex = new HashMap<>(vehicleTypeIDsList.size()); + + for (int i = 0; i < vehicleTypeIDsList.size(); i++) + vehicleTypeIDToIndex.put(vehicleTypeIDsList.get(i), Integer.valueOf(i)); + + return new Builder(location, isSymmetric, vehicleTypeIDToIndex, vehicleTypeIDToIndex.get(defaultVehicleID).intValue()); + + } + + public Builder addTransportationDistance(int from, int to, String vehicleTypeID, double distance) { + costMatrix[from][to][0][vehicleTypeToIndex.get(vehicleTypeID).intValue()] = distance; + + if (isSymmetric) + costMatrix[to][from][0][vehicleTypeToIndex.get(vehicleTypeID).intValue()] = distance; + + return this; + } + + public Builder addTransportationTime(int from, int to, String vehicleTypeID, double duration) { + costMatrix[from][to][1][vehicleTypeToIndex.get(vehicleTypeID).intValue()] = duration; + + if (isSymmetric) + costMatrix[to][from][1][vehicleTypeToIndex.get(vehicleTypeID).intValue()] = duration; + + return this; + } + + public MultiVehicleCostMatrix build() { + return new MultiVehicleCostMatrix(costMatrix, vehicleTypeToIndex, defaultIndex); + } + } + +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrixTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrixTest.java new file mode 100644 index 000000000..dac80b10a --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/MultiVehicleCostMatrixTest.java @@ -0,0 +1,119 @@ +package com.graphhopper.jsprit.core.util; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +import java.util.Collection; + +import org.junit.Before; +import org.junit.Test; + +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; + +public class MultiVehicleCostMatrixTest { + + private MultiVehicleCostMatrix costMatrix; + private final double delta = 10e-8 , departureTime = 0; + private final Driver driver = null; + private final Location zerothLoc = Location.newInstance(0) , firstLoc = Location.newInstance(1); + private final Vehicle bike = VehicleImpl.Builder.newInstance("vehicle1") + .setType(VehicleTypeImpl.Builder.newInstance("bike") + .setCostPerTransportTime(1) + .build()) + .setStartLocation(zerothLoc) + .build(); + private final Vehicle van = VehicleImpl.Builder.newInstance("vehicle2") + .setType(VehicleTypeImpl.Builder.newInstance("van") + .setCostPerTransportTime(1) + .build()) + .setStartLocation(zerothLoc) + .build(); + private final Collection vehicleTypes = asList("bike", "van"); + private final String defaultVehicleType = "bike"; + + @Before + public void setUp() throws Exception { + + MultiVehicleCostMatrix.Builder costMatrixBuilder = MultiVehicleCostMatrix.Builder.newInstance(2, false, vehicleTypes, defaultVehicleType); + costMatrixBuilder.addTransportationDistance(0, 0, "bike", 0); + costMatrixBuilder.addTransportationDistance(0, 1, "bike", 4.2); + costMatrixBuilder.addTransportationDistance(1, 0, "bike", 3.6); + costMatrixBuilder.addTransportationDistance(1, 1, "bike", 0); + + costMatrixBuilder.addTransportationDistance(0, 0, "van", 0); + costMatrixBuilder.addTransportationDistance(0, 1, "van", 5.2); + costMatrixBuilder.addTransportationDistance(1, 0, "van", 4.5); + costMatrixBuilder.addTransportationDistance(1, 1, "van", 0); + + costMatrixBuilder.addTransportationTime(0, 0, "bike", 0); + costMatrixBuilder.addTransportationTime(0, 1, "bike", 12.); + costMatrixBuilder.addTransportationTime(1, 0, "bike", 22.6); + costMatrixBuilder.addTransportationTime(1, 1, "bike", 0); + + costMatrixBuilder.addTransportationTime(0, 0, "van", 0); + costMatrixBuilder.addTransportationTime(0, 1, "van", 15.266); + costMatrixBuilder.addTransportationTime(1, 0, "van", 25.123); + costMatrixBuilder.addTransportationTime(1, 1, "van", 0); + + this.costMatrix = costMatrixBuilder.build(); + } + + @Test + public void testGetTransportCost() { + assertEquals(costMatrix.getTransportCost(zerothLoc, zerothLoc, departureTime, driver, bike), 0, delta); + assertEquals(costMatrix.getTransportCost(zerothLoc, firstLoc, departureTime, driver, bike), 16.2, delta);// 16.2 = 4.2 + 12 + assertEquals(costMatrix.getTransportCost(firstLoc, zerothLoc, departureTime, driver, bike), 26.2, delta);//26.2 = 3.6 + 22.6 + assertEquals(costMatrix.getTransportCost(firstLoc, firstLoc, departureTime, driver, bike), 0, delta); + + assertEquals(costMatrix.getTransportCost(zerothLoc, zerothLoc, departureTime, driver, van), 0, delta); + assertEquals(costMatrix.getTransportCost(zerothLoc, firstLoc, departureTime, driver, van), 20.466, delta);//20.466 = 5.2 + 15.266 + assertEquals(costMatrix.getTransportCost(firstLoc, zerothLoc, departureTime, driver, van), 29.623, delta);//29.623 = 4.5 + 25.123 + assertEquals(costMatrix.getTransportCost(firstLoc, firstLoc, departureTime, driver, van), 0, delta); + } + + @Test + public void testGetTransportTime() { + + assertEquals(costMatrix.getTransportTime(zerothLoc, zerothLoc, departureTime, driver, bike), 0, delta); + assertEquals(costMatrix.getTransportTime(zerothLoc, firstLoc, departureTime, driver, bike), 12, delta); + assertEquals(costMatrix.getTransportTime(firstLoc, zerothLoc, departureTime, driver, bike), 22.6, delta); + assertEquals(costMatrix.getTransportTime(firstLoc, firstLoc, departureTime, driver, bike), 0, delta); + + assertEquals(costMatrix.getTransportTime(zerothLoc, zerothLoc, departureTime, driver, van), 0, delta); + assertEquals(costMatrix.getTransportTime(zerothLoc, firstLoc, departureTime, driver, van), 15.266, delta); + assertEquals(costMatrix.getTransportTime(firstLoc, zerothLoc, departureTime, driver, van), 25.123, delta); + assertEquals(costMatrix.getTransportTime(firstLoc, firstLoc, departureTime, driver, van), 0, delta); + + } + + @Test + public void testGetDistanceBetween() { + assertEquals(costMatrix.getDistanceBetween(0, 0, "bike"), 0, delta); + assertEquals(costMatrix.getDistanceBetween(0, 1, "bike"), 4.2, delta); + assertEquals(costMatrix.getDistanceBetween(1, 0, "bike"), 3.6, delta); + assertEquals(costMatrix.getDistanceBetween(1, 1, "bike"), 0, delta); + + assertEquals(costMatrix.getDistanceBetween(0, 0, "van"), 0, delta); + assertEquals(costMatrix.getDistanceBetween(0, 1, "van"), 5.2, delta); + assertEquals(costMatrix.getDistanceBetween(1, 0, "van"), 4.5, delta); + assertEquals(costMatrix.getDistanceBetween(1, 1, "van"), 0, delta); + } + + @Test + public void testGetTimeBetween() { + assertEquals(costMatrix.getTransportTimeBetween(0, 0, "bike"), 0, delta); + assertEquals(costMatrix.getTransportTimeBetween(0, 1, "bike"), 12, delta); + assertEquals(costMatrix.getTransportTimeBetween(1, 0, "bike"), 22.6, delta); + assertEquals(costMatrix.getTransportTimeBetween(1, 1, "bike"), 0, delta); + + assertEquals(costMatrix.getTransportTimeBetween(0, 0, "van"), 0, delta); + assertEquals(costMatrix.getTransportTimeBetween(0, 1, "van"), 15.266, delta); + assertEquals(costMatrix.getTransportTimeBetween(1, 0, "van"), 25.123, delta); + assertEquals(costMatrix.getTransportTimeBetween(1, 1, "van"), 0, delta); + } + +}