mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
speed up regret insertion with breaks
This commit is contained in:
parent
10defbe1ec
commit
de8689060a
2 changed files with 155 additions and 5 deletions
|
|
@ -27,10 +27,7 @@ import jsprit.core.problem.solution.route.VehicleRoute;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insertion based on regret approach.
|
* Insertion based on regret approach.
|
||||||
|
|
@ -185,6 +182,33 @@ public class RegretInsertion extends AbstractInsertionStrategy {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class VersionedInsertionData {
|
||||||
|
|
||||||
|
private InsertionData iData;
|
||||||
|
|
||||||
|
private VehicleRoute route;
|
||||||
|
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
public VersionedInsertionData(InsertionData iData, int version, VehicleRoute route) {
|
||||||
|
this.iData = iData;
|
||||||
|
this.version = version;
|
||||||
|
this.route = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertionData getiData() {
|
||||||
|
return iData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VehicleRoute getRoute() {
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Logger logger = LogManager.getLogger(RegretInsertion.class);
|
private static Logger logger = LogManager.getLogger(RegretInsertion.class);
|
||||||
|
|
||||||
private ScoringFunction scoringFunction;
|
private ScoringFunction scoringFunction;
|
||||||
|
|
@ -192,6 +216,7 @@ public class RegretInsertion extends AbstractInsertionStrategy {
|
||||||
private JobInsertionCostsCalculator insertionCostsCalculator;
|
private JobInsertionCostsCalculator insertionCostsCalculator;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scoring function.
|
* Sets the scoring function.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|
@ -247,17 +272,35 @@ public class RegretInsertion extends AbstractInsertionStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
|
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
|
||||||
|
PriorityQueue<VersionedInsertionData>[] priorityQueues = new PriorityQueue[vrp.getJobs().values().size() + 2];
|
||||||
|
VehicleRoute lastModified = null;
|
||||||
|
boolean firstRun = true;
|
||||||
|
int updateRound = 0;
|
||||||
|
Map<VehicleRoute,Integer> updates = new HashMap<VehicleRoute, Integer>();
|
||||||
while (!jobs.isEmpty()) {
|
while (!jobs.isEmpty()) {
|
||||||
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
|
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
|
||||||
List<Job> badJobList = new ArrayList<Job>();
|
List<Job> badJobList = new ArrayList<Job>();
|
||||||
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
|
if(firstRun){
|
||||||
|
firstRun = false;
|
||||||
|
updateInsertionData(priorityQueues, routes, unassignedJobList, badJobList, updateRound, updates);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
updateInsertionData(priorityQueues, Arrays.asList(lastModified), unassignedJobList, badJobList, updateRound, updates);
|
||||||
|
}
|
||||||
|
updateRound++;
|
||||||
|
ScoredJob bestScoredJob = getBest(priorityQueues,updates,unassignedJobList,badJobList);
|
||||||
|
// InsertionData d = insertionCostsCalculator.getInsertionData(bestScoredJob.getRoute(), bestScoredJob.getJob(), NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
|
||||||
|
//
|
||||||
|
// ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
|
||||||
if (bestScoredJob != null) {
|
if (bestScoredJob != null) {
|
||||||
if (bestScoredJob.isNewRoute()) {
|
if (bestScoredJob.isNewRoute()) {
|
||||||
routes.add(bestScoredJob.getRoute());
|
routes.add(bestScoredJob.getRoute());
|
||||||
}
|
}
|
||||||
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
|
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
|
||||||
jobs.remove(bestScoredJob.getJob());
|
jobs.remove(bestScoredJob.getJob());
|
||||||
|
lastModified = bestScoredJob.getRoute();
|
||||||
}
|
}
|
||||||
|
else throw new IllegalStateException("fooo");
|
||||||
for (Job bad : badJobList) {
|
for (Job bad : badJobList) {
|
||||||
jobs.remove(bad);
|
jobs.remove(bad);
|
||||||
badJobs.add(bad);
|
badJobs.add(bad);
|
||||||
|
|
@ -266,6 +309,106 @@ public class RegretInsertion extends AbstractInsertionStrategy {
|
||||||
return badJobs;
|
return badJobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ScoredJob getBest(PriorityQueue<VersionedInsertionData>[] priorityQueues, Map<VehicleRoute, Integer> updates, List<Job> unassignedJobList, List<Job> badJobs) {
|
||||||
|
ScoredJob bestScoredJob = null;
|
||||||
|
for(Job j : unassignedJobList){
|
||||||
|
VehicleRoute bestRoute = null;
|
||||||
|
InsertionData best = null;
|
||||||
|
InsertionData secondBest = null;
|
||||||
|
PriorityQueue<VersionedInsertionData> priorityQueue = priorityQueues[j.getIndex()];
|
||||||
|
Iterator<VersionedInsertionData> iterator = priorityQueue.iterator();
|
||||||
|
while(iterator.hasNext()){
|
||||||
|
VersionedInsertionData versionedIData = iterator.next();
|
||||||
|
if(versionedIData.getiData() instanceof InsertionData.NoInsertionFound) continue;
|
||||||
|
int currentDataVersion = updates.get(versionedIData.getRoute());
|
||||||
|
if(versionedIData.getVersion() == currentDataVersion){
|
||||||
|
if(best == null) {
|
||||||
|
best = versionedIData.getiData();
|
||||||
|
bestRoute = versionedIData.getRoute();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
secondBest = versionedIData.getiData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VehicleRoute emptyRoute = VehicleRoute.emptyRoute();
|
||||||
|
InsertionData iData = insertionCostsCalculator.getInsertionData(emptyRoute, j, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
|
||||||
|
if(!(iData instanceof InsertionData.NoInsertionFound)){
|
||||||
|
if (best == null) {
|
||||||
|
best = iData;
|
||||||
|
bestRoute = emptyRoute;
|
||||||
|
} else if (iData.getInsertionCost() < best.getInsertionCost()) {
|
||||||
|
secondBest = best;
|
||||||
|
best = iData;
|
||||||
|
bestRoute = emptyRoute;
|
||||||
|
} else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
|
||||||
|
secondBest = iData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best == null) {
|
||||||
|
badJobs.add(j);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double score = score(j, best, secondBest, scoringFunction);
|
||||||
|
ScoredJob scoredJob;
|
||||||
|
if (bestRoute == emptyRoute) {
|
||||||
|
scoredJob = new ScoredJob(j, score, best, bestRoute, true);
|
||||||
|
} else scoredJob = new ScoredJob(j, score, best, bestRoute, false);
|
||||||
|
|
||||||
|
if(bestScoredJob == null){
|
||||||
|
bestScoredJob = scoredJob;
|
||||||
|
}
|
||||||
|
else if(scoredJob.getScore() > bestScoredJob.getScore()){
|
||||||
|
bestScoredJob = scoredJob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestScoredJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Comparator<VersionedInsertionData> getComparator(){
|
||||||
|
return new Comparator<VersionedInsertionData>() {
|
||||||
|
@Override
|
||||||
|
public int compare(VersionedInsertionData o1, VersionedInsertionData o2) {
|
||||||
|
if(o1.getiData().getInsertionCost() < o2.getiData().getInsertionCost()) return -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInsertionData(PriorityQueue<VersionedInsertionData>[] priorityQueues, Collection<VehicleRoute> routes, List<Job> unassignedJobList, List<Job> badJobList, int updateRound, Map<VehicleRoute, Integer> updates) {
|
||||||
|
for (Job unassignedJob : unassignedJobList) {
|
||||||
|
if(priorityQueues[unassignedJob.getIndex()] == null){
|
||||||
|
priorityQueues[unassignedJob.getIndex()] = new PriorityQueue<VersionedInsertionData>(unassignedJobList.size(), getComparator());
|
||||||
|
}
|
||||||
|
for(VehicleRoute route : routes) {
|
||||||
|
|
||||||
|
InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
|
||||||
|
priorityQueues[unassignedJob.getIndex()].add(new VersionedInsertionData(iData,updateRound,route));
|
||||||
|
updates.put(route,updateRound);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
|
||||||
|
// if (scoredJob instanceof BadJob) {
|
||||||
|
// badJobs.add(unassignedJob);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// if (bestScoredJob == null) bestScoredJob = scoredJob;
|
||||||
|
// else {
|
||||||
|
// if (scoredJob.getScore() > bestScoredJob.getScore()) {
|
||||||
|
// bestScoredJob = scoredJob;
|
||||||
|
// } else if (scoredJob.getScore() == bestScoredJob.getScore()) {
|
||||||
|
// if (scoredJob.getJob().getId().compareTo(bestScoredJob.getJob().getId()) <= 0) {
|
||||||
|
// bestScoredJob = scoredJob;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
|
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
|
||||||
for(VehicleRoute r : routes){
|
for(VehicleRoute r : routes){
|
||||||
if(r.getVehicle().getBreak() == job) return r;
|
if(r.getVehicle().getBreak() == job) return r;
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,9 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
|
||||||
}
|
}
|
||||||
|
|
||||||
public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle vehicle, double newVehicleDepartureTime, final Driver driver, final double bestKnownCost) {
|
public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle vehicle, double newVehicleDepartureTime, final Driver driver, final double bestKnownCost) {
|
||||||
|
if(vehicle != null){
|
||||||
|
return insertionCalculator.getInsertionData(currentRoute, jobToInsert, vehicle, newVehicleDepartureTime, driver, bestKnownCost);
|
||||||
|
}
|
||||||
Vehicle selectedVehicle = currentRoute.getVehicle();
|
Vehicle selectedVehicle = currentRoute.getVehicle();
|
||||||
Driver selectedDriver = currentRoute.getDriver();
|
Driver selectedDriver = currentRoute.getDriver();
|
||||||
InsertionData bestIData = InsertionData.createEmptyInsertionData();
|
InsertionData bestIData = InsertionData.createEmptyInsertionData();
|
||||||
|
|
@ -122,6 +125,10 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
|
||||||
return bestIData;
|
return bestIData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VehicleFleetManager getFleetManager(){
|
||||||
|
return fleetManager;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isVehicleWithInitialRoute(Vehicle selectedVehicle) {
|
private boolean isVehicleWithInitialRoute(Vehicle selectedVehicle) {
|
||||||
return initialVehicleIds.contains(selectedVehicle.getId());
|
return initialVehicleIds.contains(selectedVehicle.getId());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue