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 00000000..7357d45b --- /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 00000000..dac80b10 --- /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); + } + +}