for (Map.Entry<Long, List<Integer>> entry : appointmentIndexVsAllowedUsersIndex.entrySet()) {
int[] allowedVehicles = entry.getValue().stream().mapToInt(Integer::intValue).toArray();
long nodeIndex = manager.nodeToIndex(entry.getKey().intValue());
routing.setAllowedVehiclesForIndex(allowedVehicles, nodeIndex);
}
for (int v = 0; v < userTimeWindow.length; ++v) {
long startIndex = routing.start(v);
long endIndex = routing.end(v);
int vs = (int) Math.round(this.userTimeWindow[v][0]);
int ve = (int) Math.round(this.userTimeWindow[v][1]);
timeDimension.cumulVar(startIndex).setRange(vs, ve);
timeDimension.cumulVar(endIndex).setRange(vs, ve);
}
for (int i = 0; i < numNodes; ++i) {
routing.addDisjunction(
new long[]{manager.nodeToIndex(i)},
1_000_000_000 // 1e12
);
}
routing.addDimensionWithVehicleCapacity(
demandCallbackIndex,
0, // no capacity slack
userMaxCapacities, // per-vehicle capacities
true, // start cumul to zero
"Capacity" // dimension name
);
RoutingSearchParameters searchParameters = main.defaultRoutingSearchParameters()
.toBuilder()
.setFirstSolutionStrategy(FirstSolutionStrategy.Value.PARALLEL_CHEAPEST_INSERTION)
.setLocalSearchMetaheuristic(LocalSearchMetaheuristic.Value.AUTOMATIC)
.setTimeLimit(Duration.newBuilder().setSeconds(180).build())
.build();
for (int i = 0 ; i < stopTimeWindow.length; ++i) {
long index = manager.nodeToIndex(i);
timeDimension.setCumulVarSoftUpperBound(
index,
Math.round(this.stopTimeWindow[i][1]),
1000 // penalty per minute violated
);
int hardStart = (int)Math.round(stopTimeWindow[i][0]);
int hardEnd = (int)Math.round(stopTimeWindow[i][1]);
IntVar var = timeDimension.cumulVar(index);
// Soft window penalty
timeDimension.setCumulVarSoftLowerBound(index, hardStart, 50000); // penalty before start
timeDimension.setCumulVarSoftUpperBound(index, hardEnd, 50000); // penalty after end
}