Skip to content

Commit

Permalink
fixed bug that caused the calculation of wrong start indices
Browse files Browse the repository at this point in the history
  • Loading branch information
rinde committed Aug 6, 2015
1 parent dc0e41d commit 077073c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 64 deletions.
14 changes: 8 additions & 6 deletions src/main/java/com/github/rinde/logistics/pdptw/solver/Opt2.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ public Opt2(long seed, Solver deleg, ObjectiveFunction objFunc, boolean dfs) {
@Override
public ImmutableList<ImmutableList<Parcel>> solve(GlobalStateObject state) {
final ImmutableList<ImmutableList<Parcel>> schedule = delegate
.solve(state);
.solve(state);
final ImmutableList.Builder<Integer> indexBuilder = ImmutableList.builder();
for (final VehicleStateObject vso : state.getVehicles()) {
indexBuilder.add(vso.getDestination() == null ? 0 : 1);
indexBuilder.add(vso.getDestination().isPresent() ? 1 : 0);
}
if (depthFirstSearch) {
return Swaps.dfsOpt2(schedule, indexBuilder.build(), state, evaluator,
Expand All @@ -90,7 +90,8 @@ public ImmutableList<ImmutableList<Parcel>> solve(GlobalStateObject state) {
* {@link Opt2}.
*/
public static StochasticSupplier<Solver> breadthFirstSupplier(
final StochasticSupplier<Solver> delegate, final ObjectiveFunction objFunc) {
final StochasticSupplier<Solver> delegate,
final ObjectiveFunction objFunc) {
return new Opt2Supplier(delegate, objFunc, false);
}

Expand All @@ -105,7 +106,8 @@ public static StochasticSupplier<Solver> breadthFirstSupplier(
* {@link Opt2}.
*/
public static StochasticSupplier<Solver> depthFirstSupplier(
final StochasticSupplier<Solver> delegate, final ObjectiveFunction objFunc) {
final StochasticSupplier<Solver> delegate,
final ObjectiveFunction objFunc) {
return new Opt2Supplier(delegate, objFunc, true);
}

Expand All @@ -116,7 +118,7 @@ private static class Opt2Supplier extends AbstractStochasticSupplier<Solver> {
private final boolean depthFirstSearch;

Opt2Supplier(StochasticSupplier<Solver> del, ObjectiveFunction objFunc,
boolean dfs) {
boolean dfs) {
delegate = del;
objectiveFunction = objFunc;
depthFirstSearch = dfs;
Expand All @@ -126,7 +128,7 @@ private static class Opt2Supplier extends AbstractStochasticSupplier<Solver> {
public Solver get(long seed) {
final RandomGenerator rand = new MersenneTwister(seed);
return new Opt2(rand.nextLong(), delegate.get(rand.nextLong()),
objectiveFunction, depthFirstSearch);
objectiveFunction, depthFirstSearch);
}
}
}
30 changes: 17 additions & 13 deletions src/main/java/com/github/rinde/opt/localsearch/Insertions.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ private Insertions() {}
public static <T> Iterator<ImmutableList<T>> insertionsIterator(
ImmutableList<T> list, T item, int startIndex, int numOfInsertions) {
checkArgument(startIndex >= 0 && startIndex <= list.size(),
"startIndex must be >= 0 and <= %s (list size), it is %s.",
list.size(), startIndex);
"startIndex must be >= 0 and <= %s (list size), it is %s.",
list.size(), startIndex);
checkArgument(numOfInsertions > 0, "numOfInsertions must be positive.");
return Iterators.transform(new InsertionIndexGenerator(numOfInsertions,
list.size(), startIndex), new IndexToInsertionTransform<T>(list, item));
list.size(), startIndex),
new IndexToInsertionTransform<T>(list, item));
}

/**
Expand All @@ -75,13 +76,13 @@ public static <T> Iterator<ImmutableList<T>> insertionsIterator(
public static <T> ImmutableList<ImmutableList<T>> insertions(
ImmutableList<T> list, T item, int startIndex, int numOfInsertions) {
return ImmutableList.copyOf(insertionsIterator(list, item, startIndex,
numOfInsertions));
numOfInsertions));
}

/**
* Calculates the number of <code>k</code> sized multisubsets that can be
* formed in a set of size <code>n</code>. See <a
* href="https://en.wikipedia.org/wiki/Combination#
* formed in a set of size <code>n</code>. See
* <a href="https://en.wikipedia.org/wiki/Combination#
* Number_of_combinations_with_repetition">Wikipedia</a> for a description.
*
* @param n The size of the set to create subsets from.
Expand All @@ -105,18 +106,18 @@ static long multichoose(int n, int k) {
public static <T> ImmutableList<T> insert(List<T> originalList,
List<Integer> insertionIndices, T item) {
checkArgument(!insertionIndices.isEmpty(),
"At least one insertion index must be defined.");
"At least one insertion index must be defined.");
int prev = 0;
final ImmutableList.Builder<T> builder = ImmutableList.<T> builder();
final ImmutableList.Builder<T> builder = ImmutableList.<T>builder();
for (int i = 0; i < insertionIndices.size(); i++) {
final int cur = insertionIndices.get(i);
checkArgument(
cur >= 0 && cur <= originalList.size(),
"The specified indices must be >= 0 and <= %s (list size), it is %s.",
originalList.size(), cur);
cur >= 0 && cur <= originalList.size(),
"The specified indices must be >= 0 and <= %s (list size), it is %s.",
originalList.size(), cur);
checkArgument(cur >= prev,
"The specified indices must be in ascending order. Received %s.",
insertionIndices);
"The specified indices must be in ascending order. Received %s.",
insertionIndices);
builder.addAll(originalList.subList(prev, cur));
builder.add(item);
prev = cur;
Expand Down Expand Up @@ -150,6 +151,9 @@ static class InsertionIndexGenerator implements
private int index = 0;

InsertionIndexGenerator(int numOfInsertions, int listSize, int startIndex) {
checkArgument(startIndex <= listSize,
"startIndex (%s) must be <= listSize (%s).",
startIndex, listSize);
insertionPositions = new int[numOfInsertions];
for (int i = 0; i < insertionPositions.length; i++) {
insertionPositions[i] = startIndex;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/github/rinde/opt/localsearch/Schedule.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ static <C, T> Schedule<C, T> create(C context,

static <C, T> Schedule<C, T> create(C context,
ImmutableList<ImmutableList<T>> routes,
ImmutableList<Integer> startIndices, RouteEvaluator<C, T> routeEvaluator) {
ImmutableList<Integer> startIndices,
RouteEvaluator<C, T> routeEvaluator) {
final ImmutableList.Builder<Double> costsBuilder = ImmutableList.builder();
double sumCost = 0;
for (int i = 0; i < routes.size(); i++) {
Expand Down
90 changes: 46 additions & 44 deletions src/main/java/com/github/rinde/opt/localsearch/Swaps.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static <C, T> ImmutableList<ImmutableList<T>> bfsOpt2(
ImmutableList<Integer> startIndices, C context,
RouteEvaluator<C, T> evaluator) {
return opt2(schedule, startIndices, context, evaluator, false,
Optional.<RandomGenerator> absent());
Optional.<RandomGenerator>absent());
}

/**
Expand Down Expand Up @@ -114,7 +114,7 @@ public static <C, T> ImmutableList<ImmutableList<T>> dfsOpt2(
ImmutableList<Integer> startIndices, C context,
RouteEvaluator<C, T> evaluator, RandomGenerator rng) {
return opt2(schedule, startIndices, context, evaluator, true,
Optional.of(rng));
Optional.of(rng));
}

static <C, T> ImmutableList<ImmutableList<T>> opt2(
Expand All @@ -126,12 +126,12 @@ static <C, T> ImmutableList<ImmutableList<T>> opt2(
checkArgument(schedule.size() == startIndices.size());

final Schedule<C, T> baseSchedule = Schedule.create(context, schedule,
startIndices, evaluator);
startIndices, evaluator);

final Map<ImmutableList<T>, Double> routeCostCache = newLinkedHashMap();
for (int i = 0; i < baseSchedule.routes.size(); i++) {
routeCostCache.put(baseSchedule.routes.get(i),
baseSchedule.objectiveValues.get(i));
baseSchedule.objectiveValues.get(i));
}

Schedule<C, T> bestSchedule = baseSchedule;
Expand All @@ -151,9 +151,9 @@ static <C, T> ImmutableList<ImmutableList<T>> opt2(
while (it.hasNext()) {
final Swap<T> swapOperation = it.next();
final Optional<Schedule<C, T>> newSchedule = Swaps.swap(curBest,
swapOperation,
bestSchedule.objectiveValue - curBest.objectiveValue,
routeCostCache);
swapOperation,
bestSchedule.objectiveValue - curBest.objectiveValue,
routeCostCache);

if (newSchedule.isPresent()) {
isImproving = true;
Expand All @@ -170,16 +170,17 @@ static <C, T> ImmutableList<ImmutableList<T>> opt2(
}

static <C, T> Iterator<Swap<T>> swapIterator(Schedule<C, T> schedule) {
final ImmutableList.Builder<Iterator<Swap<T>>> iteratorBuilder = ImmutableList
.builder();
final ImmutableList.Builder<Iterator<Swap<T>>> iteratorBuilder =
ImmutableList
.builder();
final Set<T> seen = newLinkedHashSet();
for (int i = 0; i < schedule.routes.size(); i++) {
final ImmutableList<T> row = schedule.routes.get(i);
for (int j = 0; j < row.size(); j++) {
final T t = row.get(j);
if (j >= schedule.startIndices.get(i) && !seen.contains(t)) {
iteratorBuilder.add(oneItemSwapIterator(schedule,
schedule.startIndices, t, i));
schedule.startIndices, t, i));
}
seen.add(t);
}
Expand All @@ -190,9 +191,10 @@ static <C, T> Iterator<Swap<T>> swapIterator(Schedule<C, T> schedule) {
static <C, T> Iterator<Swap<T>> oneItemSwapIterator(Schedule<C, T> schedule,
ImmutableList<Integer> startIndices, T item, int fromRow) {
final ImmutableList<Integer> indices = indices(
schedule.routes.get(fromRow), item);
final ImmutableList.Builder<Iterator<Swap<T>>> iteratorBuilder = ImmutableList
.builder();
schedule.routes.get(fromRow), item);
final ImmutableList.Builder<Iterator<Swap<T>>> iteratorBuilder =
ImmutableList
.builder();

Range<Integer> range;
if (indices.size() == 1) {
Expand Down Expand Up @@ -221,7 +223,7 @@ static <C, T> Iterator<Swap<T>> oneItemSwapIterator(Schedule<C, T> schedule,
static <C, T> Optional<Schedule<C, T>> swap(Schedule<C, T> s, Swap<T> swap,
double threshold) {
return swap(s, swap, threshold,
new LinkedHashMap<ImmutableList<T>, Double>());
new LinkedHashMap<ImmutableList<T>, Double>());
}

/**
Expand Down Expand Up @@ -249,33 +251,33 @@ static <C, T> Optional<Schedule<C, T>> swap(Schedule<C, T> s, Swap<T> swap,
double threshold, Map<ImmutableList<T>, Double> cache) {

checkArgument(swap.fromRow >= 0 && swap.fromRow < s.routes.size(),
"fromRow must be >= 0 and < %s, it is %s.", s.routes.size(),
swap.fromRow);
"fromRow must be >= 0 and < %s, it is %s.", s.routes.size(),
swap.fromRow);
checkArgument(swap.toRow >= 0 && swap.toRow < s.routes.size(),
"toRow must be >= 0 and < %s, it is %s.", s.routes.size(), swap.toRow);
"toRow must be >= 0 and < %s, it is %s.", s.routes.size(), swap.toRow);

if (swap.fromRow == swap.toRow) {
// 1. swap within same vehicle
// compute cost of original ordering
// compute cost of new ordering
final double originalCost = s.objectiveValues.get(swap.fromRow);
final ImmutableList<T> newRoute = inListSwap(s.routes.get(swap.fromRow),
swap.toIndices, swap.item);
swap.toIndices, swap.item);

final double newCost = computeCost(s, swap.fromRow, newRoute, cache);
final double diff = newCost - originalCost;

if (diff < threshold) {
// it improves
final ImmutableList<ImmutableList<T>> newRoutes = replace(s.routes,
ImmutableList.of(swap.fromRow), ImmutableList.of(newRoute));
ImmutableList.of(swap.fromRow), ImmutableList.of(newRoute));
final double newObjectiveValue = s.objectiveValue + diff;
final ImmutableList<Double> newObjectiveValues = replace(
s.objectiveValues, ImmutableList.of(swap.fromRow),
ImmutableList.of(newCost));
s.objectiveValues, ImmutableList.of(swap.fromRow),
ImmutableList.of(newCost));
return Optional
.of(Schedule.create(s.context, newRoutes, s.startIndices,
newObjectiveValues, newObjectiveValue, s.evaluator));
newObjectiveValues, newObjectiveValue, s.evaluator));
} else {
return Optional.absent();
}
Expand All @@ -285,42 +287,42 @@ static <C, T> Optional<Schedule<C, T>> swap(Schedule<C, T> s, Swap<T> swap,
// compute cost of removal from original vehicle
final double originalCostA = s.objectiveValues.get(swap.fromRow);
final ImmutableList<T> newRouteA = ImmutableList.copyOf(filter(
s.routes.get(swap.fromRow), not(equalTo(swap.item))));
s.routes.get(swap.fromRow), not(equalTo(swap.item))));
final int itemCount = s.routes.get(swap.fromRow).size()
- newRouteA.size();
checkArgument(
itemCount > 0,
"The item (%s) is not in row %s, hence it cannot be swapped to another row.",
swap.item, swap.fromRow);
itemCount > 0,
"The item (%s) is not in row %s, hence it cannot be swapped to another row.",
swap.item, swap.fromRow);
checkArgument(
itemCount == swap.toIndices.size(),
"The number of occurences in the fromRow (%s) should equal the number of insertion indices (%s).",
itemCount, swap.toIndices.size());
itemCount == swap.toIndices.size(),
"The number of occurences in the fromRow (%s) should equal the number of insertion indices (%s).",
itemCount, swap.toIndices.size());

final double newCostA = computeCost(s, swap.fromRow, newRouteA, cache);
final double diffA = newCostA - originalCostA;

// compute cost of insertion in new vehicle
final double originalCostB = s.objectiveValues.get(swap.toRow);
final ImmutableList<T> newRouteB = Insertions.insert(
s.routes.get(swap.toRow), swap.toIndices, swap.item);
s.routes.get(swap.toRow), swap.toIndices, swap.item);

final double newCostB = computeCost(s, swap.toRow, newRouteB, cache);
final double diffB = newCostB - originalCostB;

final double diff = diffA + diffB;
if (diff < threshold) {
final ImmutableList<Integer> rows = ImmutableList.of(swap.fromRow,
swap.toRow);
swap.toRow);
final ImmutableList<ImmutableList<T>> newRoutes = replace(s.routes,
rows, ImmutableList.of(newRouteA, newRouteB));
rows, ImmutableList.of(newRouteA, newRouteB));
final double newObjectiveValue = s.objectiveValue + diff;
final ImmutableList<Double> newObjectiveValues = replace(
s.objectiveValues, rows, ImmutableList.of(newCostA, newCostB));
s.objectiveValues, rows, ImmutableList.of(newCostA, newCostB));

return Optional
.of(Schedule.create(s.context, newRoutes, s.startIndices,
newObjectiveValues, newObjectiveValue, s.evaluator));
newObjectiveValues, newObjectiveValue, s.evaluator));
} else {
return Optional.absent();
}
Expand Down Expand Up @@ -357,14 +359,14 @@ static <T> ImmutableList<T> inListSwap(ImmutableList<T> originalList,
final List<T> newList = newArrayList(originalList);
final List<Integer> indices = removeAll(newList, item);
checkArgument(
newList.size() == originalList.size() - insertionIndices.size(),
"The number of occurrences (%s) of item should equal the number of insertionIndices (%s), original list: %s, item %s, insertionIndices %s.",
indices.size(), insertionIndices.size(), originalList, item,
insertionIndices);
newList.size() == originalList.size() - insertionIndices.size(),
"The number of occurrences (%s) of item should equal the number of insertionIndices (%s), original list: %s, item %s, insertionIndices %s.",
indices.size(), insertionIndices.size(), originalList, item,
insertionIndices);
checkArgument(
!indices.equals(insertionIndices),
"Attempt to move the item to exactly the same locations as the input. Indices in original list %s, insertion indices %s.",
indices, insertionIndices);
!indices.equals(insertionIndices),
"Attempt to move the item to exactly the same locations as the input. Indices in original list %s, insertion indices %s.",
indices, insertionIndices);
return Insertions.insert(newList, insertionIndices, item);
}

Expand Down Expand Up @@ -402,8 +404,8 @@ static <T> ImmutableList<Integer> indices(List<T> list, T item) {
static <T> ImmutableList<T> replace(ImmutableList<T> list,
ImmutableList<Integer> indices, ImmutableList<T> elements) {
checkArgument(indices.size() == elements.size(),
"Number of indices (%s) must equal number of elements (%s).",
indices.size(), elements.size());
"Number of indices (%s) must equal number of elements (%s).",
indices.size(), elements.size());
final List<T> newL = newArrayList(list);
for (int i = 0; i < indices.size(); i++) {
newL.set(indices.get(i), elements.get(i));
Expand Down Expand Up @@ -433,7 +435,7 @@ public String toString() {
}

static class IndexToSwapTransform<T> implements
Function<ImmutableList<Integer>, Swap<T>> {
Function<ImmutableList<Integer>, Swap<T>> {
private final T item;
private final int fromRow;
private final int toRow;
Expand Down

0 comments on commit 077073c

Please sign in to comment.