rinde.sim.pdptw.central.arrays.ArraysSolverValidator Maven / Gradle / Ivy
The newest version!
package rinde.sim.pdptw.central.arrays;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
/**
* Provides methods for validating input to and output from
* {@link SingleVehicleArraysSolver}s and {@link MultiVehicleArraysSolver}. Also
* provides wrap(..)
methods which decorates any solver such that
* both inputs and outputs are validated every time solve(..)
is
* called.
* @author Rinde van Lon
*/
public final class ArraysSolverValidator {
private ArraysSolverValidator() {}
/**
* Decorates the original {@link SingleVehicleArraysSolver} such that both the
* inputs to the solver and the outputs from the solver are validated. When an
* invalid input or output is detected a {@link IllegalArgumentException is
* thrown}.
* @param delegate The {@link SingleVehicleArraysSolver} that will be used for
* the actual solving.
* @return The wrapped solver.
*/
public static SingleVehicleArraysSolver wrap(
SingleVehicleArraysSolver delegate) {
return new SingleValidator(delegate);
}
/**
* Decorates the original {@link MultiVehicleArraysSolver} such that both the
* inputs to the solver and the outputs from the solver are validated. When an
* invalid input or output is detected a {@link IllegalArgumentException is
* thrown}.
* @param delegate The {@link MultiVehicleArraysSolver} that will be used for
* the actual solving.
* @return The wrapped solver.
*/
public static MultiVehicleArraysSolver wrap(MultiVehicleArraysSolver delegate) {
return new MultiValidator(delegate);
}
/**
* Validates the inputs for the {@link SingleVehicleArraysSolver}. This method
* checks all properties as defined in
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* . If the inputs are not correct an {@link IllegalArgumentException} is
* thrown.
* @param travelTime Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param releaseDates Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param dueDates Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param servicePairs Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param serviceTimes Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
*/
public static void validateInputs(int[][] travelTime, int[] releaseDates,
int[] dueDates, int[][] servicePairs, int[] serviceTimes) {
final int n = travelTime.length;
checkArgument(n > 0, "Travel time matrix cannot be empty");
// check that matrix is n x n
for (int i = 0; i < n; i++) {
checkArgument(travelTime[i].length == n, "row %s has invalid length %s",
i, travelTime[i].length);
}
checkArgument(releaseDates.length == n,
"ReleaseDates array has incorrect length (%s) should be %s",
releaseDates.length, n);
checkArgument(dueDates.length == n,
"dueDates array has incorrect length (%s) should be %s",
dueDates.length, n);
checkArgument(serviceTimes.length == n,
"serviceTimes has incorrect length (%s) should be %s",
serviceTimes.length, n);
for (int i = 0; i < n; i++) {
if (i == 0 || i == n - 1) {
checkArgument(serviceTimes[i] == 0,
"first and last index in serviceTimes should be 0");
} else {
checkArgument(serviceTimes[i] >= 0, "serviceTimes should be >= 0");
}
}
// check time windows validity
for (int i = 0; i < n; i++) {
checkArgument(
releaseDates[i] <= dueDates[i],
"Index %s, release date (%s) should always be before the due date (%s)",
i, releaseDates[i], dueDates[i]);
}
checkArgument(releaseDates[0] == 0 && dueDates[0] == 0,
"Start location should have release date and due date 0");
// checkArgument(releaseDates[n - 1] == 0,
// "Depot should have release date 0");
// check that every pair consists of valid ids and that a location is in
// only one pair
final Set set = newHashSet();
for (int i = 0; i < servicePairs.length; i++) {
checkArgument(servicePairs[i].length == 2,
"Each pair entry should consist of exactly two locations.");
for (int j = 0; j < 2; j++) {
checkArgument(
servicePairs[i][j] > 0 && servicePairs[i][j] < n - 1,
"Pair consists of an invalid location (start location and depot are not allowed), index is %s, location is %s",
i, servicePairs[i][j]);
checkArgument(
!set.contains(servicePairs[i][j]),
"Location can be part of only one pair, duplicate location: %s (index %s,%s)",
servicePairs[i][j], i, j);
set.add(servicePairs[i][j]);
}
}
}
/**
* Validates the inputs for the {@link MultiVehicleArraysSolver}. This method
* checks all properties as defined in
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* . If the inputs are not correct an {@link IllegalArgumentException} is
* thrown.
* @param travelTime Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param releaseDates Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param dueDates Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param servicePairs Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param serviceTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param vehicleTravelTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param inventories Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param remainingServiceTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param currentDestinations Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param currentSolutions Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
*/
public static void validateInputs(int[][] travelTime, int[] releaseDates,
int[] dueDates, int[][] servicePairs, int[] serviceTimes,
int[][] vehicleTravelTimes, int[][] inventories,
int[] remainingServiceTimes, int[] currentDestinations,
@Nullable SolutionObject[] currentSolutions) {
validateInputs(travelTime, releaseDates, dueDates, servicePairs,
serviceTimes);
// number of vehicles v
final int v = vehicleTravelTimes.length;
final int n = travelTime.length;
checkArgument(v > 0, "At least one vehicle is required.");
checkArgument(
v == remainingServiceTimes.length,
"Expected a remainingServiceTimes array of size %s, but found one with size %s.",
v, remainingServiceTimes.length);
checkArgument(currentDestinations.length == v,
"The currentDestinations array should be of length v=%s, it is %s.", v,
currentDestinations.length);
for (int i = 0; i < v; i++) {
checkArgument(
n == vehicleTravelTimes[i].length,
"We expected vehicleTravelTimes matrix of size v x %s, but we found v x %s at index %s.",
n, vehicleTravelTimes[i].length, i);
for (int j = 0; j < n; j++) {
checkArgument(
vehicleTravelTimes[i][j] >= 0,
"Found an invalid vehicle travel time (%s) at position %s,%s. All times must be >= 0.",
vehicleTravelTimes[i][j], i, j);
if (j == 0) {
checkArgument(0 == vehicleTravelTimes[i][j],
"vehicleTravelTimes[%s][%s] == %s, but must be 0.", i, j,
vehicleTravelTimes[i][j]);
} else if (currentDestinations[i] != 0 && j != currentDestinations[i]) {
checkArgument(
vehicleTravelTimes[i][j] == Integer.MAX_VALUE,
"vehicleTravelTimes[%s][%s] == %s, but must be Integer.MAX_VALUE.",
i, j, vehicleTravelTimes[i][j]);
} else {
checkArgument(
vehicleTravelTimes[i][j] != Integer.MAX_VALUE,
"vehicleTravelTimes[%s][%s] == Integer.MAX_VALUE, but must be normal value.",
i, j);
}
}
}
final ImmutableSet.Builder b = ImmutableSet.builder();
for (int i = 0; i < servicePairs.length; i++) {
b.add(servicePairs[i][0]);
b.add(servicePairs[i][1]);
}
final Set availLocs = b.build();
final int m = n - 2 - (servicePairs.length * 2);
checkArgument(
inventories.length == m,
"Invalid number of inventory entries, must be equal to number of delivery locations: %s, found: %s.",
m, servicePairs.length);
final Multimap inventoriesMap = HashMultimap.create();
final Set parcelsInInventory = newHashSet();
for (int i = 0; i < m; i++) {
checkArgument(
2 == inventories[i].length,
"We expected inventories matrix of size m x 2, but we found m x %s at index %s.",
inventories[i].length, i);
checkArgument(
inventories[i][0] >= 0 && inventories[i][0] < v,
"Found a reference to a non-existing vehicle (%s) in inventories at row %s.",
inventories[i][0], i);
checkArgument(
inventories[i][1] >= 1 && inventories[i][1] < n - 1,
"Found a reference to a non-existing location (%s) in inventories at row %s.",
inventories[i][1], i);
checkArgument(
!availLocs.contains(inventories[i][1]),
"Found a reference to a location (%s) in inventories at row %s which is available, as such, it can not be in the inventory.",
inventories[i][1], i);
checkArgument(!parcelsInInventory.contains(inventories[i][1]),
"Found a duplicate inventory entry, first duplicate at row %s.", i);
parcelsInInventory.add(inventories[i][1]);
inventoriesMap.put(inventories[i][0], inventories[i][1]);
}
for (int i = 0; i < v; i++) {
checkArgument(remainingServiceTimes[i] >= 0,
"Remaining service time must be >= 0, found %s.",
remainingServiceTimes[i]);
}
final ImmutableBiMap.Builder servicePairsBuilder = ImmutableBiMap
.builder();
for (int i = 0; i < servicePairs.length; i++) {
servicePairsBuilder.put(servicePairs[i][0], servicePairs[i][1]);
}
final ImmutableBiMap servicePairsMap = servicePairsBuilder
.build();
for (int i = 0; i < v; i++) {
if (remainingServiceTimes[i] != 0) {
checkArgument(currentDestinations[i] != 0);
}
if (currentDestinations[i] != 0) {
final int dest = currentDestinations[i];
checkArgument(
dest >= 1 && dest < n - 1,
"The destination must be a valid location, it can not be the depot. It is %s.",
dest);
final boolean isAvailablePickupLoc = servicePairsMap.keySet().contains(
dest);
final boolean isInInventory = inventoriesMap.containsValue(dest);
checkArgument(
isAvailablePickupLoc != isInInventory,
"The destination location %s must be an available pickup location OR a delivery location which is in the inventory, available pickup loc: %s, in inventory: %s.",
dest, isAvailablePickupLoc, isInInventory);
if (parcelsInInventory.contains(dest)) {
checkArgument(
inventoriesMap.get(i).contains(dest),
"When a vehicle is moving towards a destination which is a delivery location, it must contain this parcel in its cargo. Vehicle %s, destination %s.",
i, dest);
}
}
}
if (currentSolutions != null) {
checkArgument(
currentSolutions.length == v,
"The number of currentSolutions (%s) should equal the number of vehicles (%s).",
currentSolutions.length, v);
for (int i = 0; i < currentSolutions.length; i++) {
final List route = ImmutableList.copyOf(Ints
.asList(currentSolutions[i].route));
checkArgument(route.get(0) == 0,
"First item in route should always be 0, it is %s.", route.get(0));
checkArgument(route.get(route.size() - 1) == n - 1,
"Last item in route should always be depot (%s), it is %s.", n - 1,
route.get(route.size() - 1));
if (currentDestinations[i] > 0) {
// there is a current destination
checkArgument(
currentDestinations[i] == route.get(1),
"The vehicle has a current destination (%s) but it is not the first item in its route: %s.",
currentDestinations[i], route);
}
final Collection inventory = inventoriesMap.get(i);
checkArgument(
ImmutableSet.copyOf(route).containsAll(inventory),
"The route should contain all locations in its inventory. Vehicle %s, route: %s, inventory: %s.",
i, route, inventory);
for (int j = 1; j < route.size() - 1; j++) {
final Integer item = route.get(j);
final int freq = Collections.frequency(route, item);
checkArgument(
freq == 1,
"Vehicle %s: each location should occur only once, found %s instances of location %s. Route: %s.",
i, freq, item, route);
if (!inventoriesMap.containsEntry(i, item)) {
// not in cargo, so the pair should appear in the route
if (servicePairsMap.containsKey(item)) {
checkArgument(route.contains(servicePairsMap.get(item)),
"Couldn't find %s in regular mapping.", item);
} else {
checkArgument(
route.contains(servicePairsMap.inverse().get(item)),
"Couldn't find %s in inverse mapping.", item);
}
}
}
}
}
}
/**
* Validates the {@link SolutionObject} that is produced by a
* {@link SingleVehicleArraysSolver} . If the {@link SolutionObject} is
* infeasible, an {@link IllegalArgumentException} is thrown.
* @param sol The {@link SolutionObject} that is validated.
* @param travelTime Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param releaseDates Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param dueDates Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param servicePairs Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param serviceTimes Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @param currentSolution Parameter as specified by
* {@link SingleVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], SolutionObject)}
* .
* @return The solution as is supplied, used for method chaining.
*/
public static SolutionObject validateOutputs(SolutionObject sol,
int[][] travelTime, int[] releaseDates, int[] dueDates,
int[][] servicePairs, int[] serviceTimes,
@Nullable SolutionObject currentSolution) {
// convert single vehicle version to multi vehicle version for checking
// of inputs
final int n = travelTime.length;
final int[][] vehicleTravelTimes = new int[1][n];
// copy first row
for (int i = 0; i < n; i++) {
vehicleTravelTimes[0][i] = travelTime[0][i];
}
final Set locationSet = newHashSet(ContiguousSet.create(
Range.closedOpen(1, n - 1), DiscreteDomain.integers()));
for (int i = 0; i < servicePairs.length; i++) {
locationSet.remove(servicePairs[i][0]);
locationSet.remove(servicePairs[i][1]);
}
final int[][] inventories = new int[locationSet.size()][2];
final Iterator locationSetIterator = locationSet.iterator();
for (int i = 0; i < locationSet.size(); i++) {
inventories[i][0] = 0;
inventories[i][1] = locationSetIterator.next();
}
final int[] remainingServiceTimes = new int[] { 0 };
final int[] currentDestinations = new int[] { 0 };
@Nullable
final SolutionObject[] currentSolutions = currentSolution == null ? null
: new SolutionObject[] { currentSolution };
// check inputs again since we just modified them
validateInputs(travelTime, releaseDates, dueDates, servicePairs,
serviceTimes, vehicleTravelTimes, inventories, remainingServiceTimes,
currentDestinations, currentSolutions);
final SolutionObject[] sols = new SolutionObject[] { sol };
validateOutputs(sols, travelTime, releaseDates, dueDates, servicePairs,
serviceTimes, vehicleTravelTimes, inventories, remainingServiceTimes,
currentDestinations);
return sol;
}
/**
* Validates the {@link SolutionObject}s that are produced by a
* {@link MultiVehicleArraysSolver}. If any of the {@link SolutionObject}s is
* infeasible, an {@link IllegalArgumentException} is thrown.
* @param sols The {@link SolutionObject}s that are validated.
* @param travelTime Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param releaseDates Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param dueDates Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param servicePairs Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param serviceTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param vehicleTravelTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param inventories Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param remainingServiceTimes Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @param currentDestinations Parameter as specified by
* {@link MultiVehicleArraysSolver#solve(int[][], int[], int[], int[][], int[], int[][], int[][], int[], int[], SolutionObject[])}
* .
* @return The solution as is supplied, used for method chaining.
*/
public static SolutionObject[] validateOutputs(SolutionObject[] sols,
int[][] travelTime, int[] releaseDates, int[] dueDates,
int[][] servicePairs, int[] serviceTimes, int[][] vehicleTravelTimes,
int[][] inventories, int[] remainingServiceTimes,
int[] currentDestinations) {
final int n = travelTime.length;
final ImmutableSet.Builder routeSetBuilder = ImmutableSet
.builder();
int visitedLocations = 0;
for (final SolutionObject sol : sols) {
routeSetBuilder.addAll(toSet(sol.route));
visitedLocations += sol.route.length - 2;
}
final Set routeSet = routeSetBuilder.build();
final Set locationSet = ContiguousSet.create(
Range.closedOpen(0, travelTime.length), DiscreteDomain.integers());
checkArgument(
visitedLocations == n - 2,
"The number of visits in routes should equal the number of locations, expected: %s, observed: %s.",
n - 2, visitedLocations);
// checks duplicates and missing locations
checkArgument(
routeSet.size() == n,
"Every location should appear exactly once in one route. Missing location: %s.",
Sets.difference(locationSet, routeSet));
// checks for completeness of tour
checkArgument(
routeSet.equals(locationSet),
"Not all locations are serviced, there is probably a non-existing location in the route. Set difference: %s.",
Sets.difference(routeSet, locationSet));
final ImmutableMultimap.Builder inventoryBuilder = ImmutableMultimap
.builder();
for (int i = 0; i < inventories.length; i++) {
inventoryBuilder.put(inventories[i][0], inventories[i][1]);
}
final Multimap inventoryMap = inventoryBuilder.build();
for (int v = 0; v < sols.length; v++) {
final SolutionObject sol = sols[v];
/*
* CHECK SERVICE SEQUENCE
*/
checkArgument(
sol.route[0] == 0,
"The route should always start with the vehicle start location: 0, actual:%s.",
sol.route[0]);
checkArgument(sol.route[sol.route.length - 1] == n - 1,
"The route should always finish with the depot.");
if (currentDestinations[v] != 0) {
checkArgument(
sol.route[1] == currentDestinations[v],
"Vehicle %s has a current destination %s, as such this must be the first point to visit (at index 1). The route: %s.",
v, currentDestinations[v], Arrays.toString(sol.route));
}
final Set locs = ImmutableSet.copyOf(Ints.asList(sol.route));
final Collection inventory = inventoryMap.get(v);
for (final Integer i : inventory) {
checkArgument(
locs.contains(i),
"Every location in the inventory of a vehicle should occur in its route, route for vehicle %s does not contain location %s.",
v, i);
}
// check service pairs ordering, pickups should be visited before
// their corresponding delivery location
final Map pairs = newHashMap();
for (int i = 0; i < servicePairs.length; i++) {
pairs.put(servicePairs[i][0], servicePairs[i][1]);
}
final Set seen = newHashSet();
final Set set = newHashSet(Ints.asList(sol.route));
for (int i = 1; i < sol.route.length - 1; i++) {
if (pairs.containsKey(sol.route[i])) {
checkArgument(
!seen.contains(pairs.get(sol.route[i])),
"Pickups should be visited before their corresponding deliveries. Location %s should be visited after location %s.",
pairs.get(sol.route[i]), sol.route[i]);
checkArgument(
set.contains(pairs.get(sol.route[i])),
"Vehicle %s: this route should contain both the pickup and delivery location, found %s, didn't find %s.",
v, sol.route[i], pairs.get(sol.route[i]));
}
seen.add(sol.route[i]);
}
/*
* CHECK ARRIVAL TIMES
*/
checkArgument(sol.arrivalTimes.length == sol.route.length,
"Number of arrival times should equal number of locations.");
checkArgument(sol.arrivalTimes[0] == 0,
"The first arrival time should be 0, was %s.", sol.arrivalTimes[0]);
// check feasibility
final int[] minArrivalTimes = ArraysSolvers.computeArrivalTimes(
sol.route, travelTime, remainingServiceTimes[v],
vehicleTravelTimes[v], serviceTimes, releaseDates);
for (int i = 1; i < sol.route.length; i++) {
checkArgument(
sol.arrivalTimes[i] >= minArrivalTimes[i],
"Vehicle %s, route index %s, arrivalTime (%s) needs to be greater or equal to minArrivalTime (%s).",
v, i, sol.arrivalTimes[i], minArrivalTimes[i]);
}
/*
* CHECK OBJECTIVE VALUE
*/
// sum travel time
final int totalTravelTime = ArraysSolvers.computeTotalTravelTime(
sol.route, travelTime, vehicleTravelTimes[v]);
// sum tardiness
final int tardiness = ArraysSolvers.computeRouteTardiness(sol.route,
sol.arrivalTimes, serviceTimes, dueDates, remainingServiceTimes[v]);
checkArgument(
sol.objectiveValue == totalTravelTime + tardiness,
"Vehicle %s: incorrect objective value (%s), it should be travel time + tardiness = %s + %s = %s.",
v, sol.objectiveValue, totalTravelTime, tardiness, totalTravelTime
+ tardiness);
}
return sols;
}
static Set toSet(int[] arr) {
final Set set = newHashSet();
for (int i = 0; i < arr.length; i++) {
set.add(arr[i]);
}
return set;
}
private static class SingleValidator implements SingleVehicleArraysSolver {
private final SingleVehicleArraysSolver delegateSolver;
SingleValidator(SingleVehicleArraysSolver delegate) {
delegateSolver = delegate;
}
@Override
public SolutionObject solve(int[][] travelTime, int[] releaseDates,
int[] dueDates, int[][] servicePairs, int[] serviceTimes,
@Nullable SolutionObject currentSolution) {
// first check inputs
validateInputs(travelTime, releaseDates, dueDates, servicePairs,
serviceTimes);
// execute solver
final SolutionObject output = delegateSolver.solve(travelTime,
releaseDates, dueDates, servicePairs, serviceTimes, currentSolution);
// check outputs
return validateOutputs(output, travelTime, releaseDates, dueDates,
servicePairs, serviceTimes, currentSolution);
}
}
private static class MultiValidator implements MultiVehicleArraysSolver {
private final MultiVehicleArraysSolver delegateSolver;
MultiValidator(MultiVehicleArraysSolver delegate) {
delegateSolver = delegate;
}
@Override
public SolutionObject[] solve(int[][] travelTime, int[] releaseDates,
int[] dueDates, int[][] servicePairs, int[] serviceTimes,
int[][] vehicleTravelTimes, int[][] inventories,
int[] remainingServiceTimes, int[] currentDestinations,
@Nullable SolutionObject[] currentSolutions) {
validateInputs(travelTime, releaseDates, dueDates, servicePairs,
serviceTimes, vehicleTravelTimes, inventories, remainingServiceTimes,
currentDestinations, currentSolutions);
final SolutionObject[] output = delegateSolver.solve(travelTime,
releaseDates, dueDates, servicePairs, serviceTimes,
vehicleTravelTimes, inventories, remainingServiceTimes,
currentDestinations, currentSolutions);
return validateOutputs(output, travelTime, releaseDates, dueDates,
servicePairs, serviceTimes, vehicleTravelTimes, inventories,
remainingServiceTimes, currentDestinations);
}
}
}