1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

renaming of regret

This commit is contained in:
oblonski 2015-10-20 19:25:54 +02:00
parent bce6f1a9f5
commit 8af268b1dc
10 changed files with 447 additions and 474 deletions

View file

@ -407,7 +407,7 @@ public class Jsprit {
final DefaultScorer scorer;
if (es != null) {
RegretInsertionConcurrentSlow regretInsertion = (RegretInsertionConcurrentSlow) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setConcurrentMode(es, noThreads)
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
@ -418,7 +418,7 @@ public class Jsprit {
regretInsertion.setScoringFunction(scorer);
regret = regretInsertion;
} else {
RegretInsertionSlow regretInsertion = (RegretInsertionSlow) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
RegretInsertion regretInsertion = (RegretInsertion) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))

View file

@ -166,12 +166,12 @@ public class InsertionBuilder {
}
} else if (strategy.equals(Strategy.REGRET)) {
if (executor == null) {
RegretInsertionSlow regret = new RegretInsertionSlow(costCalculator, vrp);
RegretInsertion regret = new RegretInsertion(costCalculator, vrp);
// regret.setSwitchAllowed(allowVehicleSwitch);
insertion = regret;
} else {
RegretInsertionConcurrentSlow regret = new RegretInsertionConcurrentSlow(costCalculator, vrp, executor);
RegretInsertionConcurrent regret = new RegretInsertionConcurrent(costCalculator, vrp, executor);
// regret.setSwitchAllowed(allowVehicleSwitch);
insertion = regret;
}

View file

@ -21,11 +21,13 @@ import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Break;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.vehicle.VehicleFleetManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Insertion based on regret approach.
@ -39,29 +41,14 @@ import java.util.*;
*/
public class RegretInsertion extends AbstractInsertionStrategy {
private static Logger logger = LogManager.getLogger(RegretInsertion.class);
private static Logger logger = LogManager.getLogger(RegretInsertionFast.class);
private ScoringFunction scoringFunction;
private JobInsertionCostsCalculator insertionCostsCalculator;
private VehicleFleetManager fleetManager;
private Set<String> initialVehicleIds;
private boolean switchAllowed = true;
public RegretInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, VehicleFleetManager fleetManager) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.fleetManager = fleetManager;
this.vrp = vehicleRoutingProblem;
this.initialVehicleIds = getInitialVehicleIds(vehicleRoutingProblem);
logger.debug("initialise {}", this);
}
/**
* Sets the scoring function.
@ -74,16 +61,12 @@ public class RegretInsertion extends AbstractInsertionStrategy {
this.scoringFunction = scoringFunction;
}
public void setSwitchAllowed(boolean switchAllowed) {
this.switchAllowed = switchAllowed;
}
private Set<String> getInitialVehicleIds(VehicleRoutingProblem vehicleRoutingProblem) {
Set<String> ids = new HashSet<String>();
for(VehicleRoute r : vehicleRoutingProblem.getInitialVehicleRoutes()){
ids.add(r.getVehicle().getId());
}
return ids;
public RegretInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
logger.debug("initialise {}", this);
}
@Override
@ -105,7 +88,7 @@ public class RegretInsertion extends AbstractInsertionStrategy {
while (jobIterator.hasNext()){
Job job = jobIterator.next();
if(job instanceof Break){
VehicleRoute route = InsertionDataUpdater.findRoute(routes, job);
VehicleRoute route = findRoute(routes,job);
if(route == null){
badJobs.add(job);
}
@ -122,35 +105,17 @@ public class RegretInsertion extends AbstractInsertionStrategy {
}
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
TreeSet<VersionedInsertionData>[] priorityQueues = new TreeSet[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()) {
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
List<Job> badJobList = new ArrayList<Job>();
if(!firstRun && lastModified == null) throw new IllegalStateException("fooo");
if(firstRun){
firstRun = false;
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound);
for(VehicleRoute r : routes) updates.put(r,updateRound);
}
else{
updateInsertionData(priorityQueues, Arrays.asList(lastModified), unassignedJobList, updateRound);
updates.put(lastModified,updateRound);
}
updateRound++;
ScoredJob bestScoredJob = InsertionDataUpdater.getBest(switchAllowed,initialVehicleIds,fleetManager,insertionCostsCalculator,scoringFunction,priorityQueues,updates,unassignedJobList,badJobList);
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
}
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
lastModified = bestScoredJob.getRoute();
}
else lastModified = null;
for (Job bad : badJobList) {
jobs.remove(bad);
badJobs.add(bad);
@ -159,15 +124,99 @@ public class RegretInsertion extends AbstractInsertionStrategy {
return badJobs;
}
private void updateInsertionData(TreeSet<VersionedInsertionData>[] priorityQueues, Collection<VehicleRoute> routes, List<Job> unassignedJobList, int updateRound) {
for (Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
priorityQueues[unassignedJob.getIndex()] = new TreeSet<VersionedInsertionData>(InsertionDataUpdater.getComparator());
}
InsertionDataUpdater.update(switchAllowed, initialVehicleIds,fleetManager,insertionCostsCalculator, priorityQueues[unassignedJob.getIndex()], updateRound, unassignedJob, routes);
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
for(VehicleRoute r : routes){
if(r.getVehicle().getBreak() == job) return r;
}
return null;
}
private ScoredJob nextJob(Collection<VehicleRoute> routes, Collection<Job> unassignedJobList, List<Job> badJobs) {
ScoredJob bestScoredJob = null;
for (Job unassignedJob : unassignedJobList) {
ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
if (scoredJob instanceof ScoredJob.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;
}
}
}
}
return bestScoredJob;
}
static ScoredJob getScoredJob(Collection<VehicleRoute> routes, Job unassignedJob, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction) {
InsertionData best = null;
InsertionData secondBest = null;
VehicleRoute bestRoute = null;
double benchmark = Double.MAX_VALUE;
for (VehicleRoute route : routes) {
if (secondBest != null) {
benchmark = secondBest.getInsertionCost();
}
InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
if (iData instanceof InsertionData.NoInsertionFound) continue;
if (best == null) {
best = iData;
bestRoute = route;
} else if (iData.getInsertionCost() < best.getInsertionCost()) {
secondBest = best;
best = iData;
bestRoute = route;
} else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
secondBest = iData;
}
}
VehicleRoute emptyRoute = VehicleRoute.emptyRoute();
InsertionData iData = insertionCostsCalculator.getInsertionData(emptyRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
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) {
return new ScoredJob.BadJob(unassignedJob);
}
double score = score(unassignedJob, best, secondBest, scoringFunction);
ScoredJob scoredJob;
if (bestRoute == emptyRoute) {
scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, true);
} else scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, false);
return scoredJob;
}
static double score(Job unassignedJob, InsertionData best, InsertionData secondBest, ScoringFunction scoringFunction) {
if (best == null) {
throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
}
double score;
if (secondBest == null) { //either there is only one vehicle or there are more vehicles, but they cannot load unassignedJob
//if only one vehicle, I want the job to be inserted with min iCosts
//if there are more vehicles, I want this job to be prioritized since there are no alternatives
score = Integer.MAX_VALUE - best.getInsertionCost() + scoringFunction.score(best, unassignedJob);
} else {
score = (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
}
return score;
}
}

View file

@ -21,14 +21,14 @@ import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Break;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.vehicle.VehicleFleetManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
/**
* Insertion based on regret approach.
@ -43,20 +43,13 @@ import java.util.concurrent.Future;
public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
private static Logger logger = LogManager.getLogger(RegretInsertionConcurrent.class);
private static Logger logger = LogManager.getLogger(RegretInsertionConcurrentFast.class);
private ScoringFunction scoringFunction;
private final JobInsertionCostsCalculator insertionCostsCalculator;
private final ExecutorService executor;
private VehicleFleetManager fleetManager;
private Set<String> initialVehicleIds;
private boolean switchAllowed = true;
private final ExecutorCompletionService<ScoredJob> completionService;
/**
* Sets the scoring function.
@ -69,14 +62,12 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
this.scoringFunction = scoringFunction;
}
public RegretInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService, VehicleFleetManager fleetManager) {
public RegretInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
this.executor = executorService;
this.fleetManager = fleetManager;
this.initialVehicleIds = getInitialVehicleIds(vehicleRoutingProblem);
completionService = new ExecutorCompletionService<ScoredJob>(executorService);
logger.debug("initialise " + this);
}
@ -85,18 +76,6 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
}
public void setSwitchAllowed(boolean switchAllowed) {
this.switchAllowed = switchAllowed;
}
private Set<String> getInitialVehicleIds(VehicleRoutingProblem vehicleRoutingProblem) {
Set<String> ids = new HashSet<String>();
for(VehicleRoute r : vehicleRoutingProblem.getInitialVehicleRoutes()){
ids.add(r.getVehicle().getId());
}
return ids;
}
/**
* Runs insertion.
@ -113,7 +92,7 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
while (jobIterator.hasNext()){
Job job = jobIterator.next();
if(job instanceof Break){
VehicleRoute route = InsertionDataUpdater.findRoute(routes, job);
VehicleRoute route = findRoute(routes,job);
if(route == null){
badJobs.add(job);
}
@ -130,67 +109,72 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
}
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
TreeSet<VersionedInsertionData>[] priorityQueues = new TreeSet[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()) {
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
List<Job> badJobList = new ArrayList<Job>();
if(!firstRun && lastModified == null) throw new IllegalStateException("ho. this must not be.");
if(firstRun){
firstRun = false;
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound);
for(VehicleRoute r : routes) updates.put(r,updateRound);
}
else{
updateInsertionData(priorityQueues, Arrays.asList(lastModified), unassignedJobList, updateRound);
updates.put(lastModified,updateRound);
}
updateRound++;
ScoredJob bestScoredJob = InsertionDataUpdater.getBest(switchAllowed,initialVehicleIds,fleetManager, insertionCostsCalculator, scoringFunction, priorityQueues, updates, unassignedJobList, badJobList);
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
}
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
lastModified = bestScoredJob.getRoute();
}
else lastModified = null;
for (Job bad : badJobList) {
jobs.remove(bad);
badJobs.add(bad);
for (Job j : badJobList) {
jobs.remove(j);
badJobs.add(j);
}
}
return badJobs;
}
private void updateInsertionData(final TreeSet<VersionedInsertionData>[] priorityQueues, final Collection<VehicleRoute> routes, List<Job> unassignedJobList, final int updateRound) {
List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
private ScoredJob nextJob(final Collection<VehicleRoute> routes, List<Job> unassignedJobList, List<Job> badJobList) {
ScoredJob bestScoredJob = null;
for (final Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
priorityQueues[unassignedJob.getIndex()] = new TreeSet<VersionedInsertionData>(InsertionDataUpdater.getComparator());
}
final TreeSet<VersionedInsertionData> priorityQueue = priorityQueues[unassignedJob.getIndex()];
tasks.add(new Callable<Boolean>() {
completionService.submit(new Callable<ScoredJob>() {
@Override
public Boolean call() throws Exception {
return InsertionDataUpdater.update(switchAllowed,initialVehicleIds,fleetManager, insertionCostsCalculator, priorityQueue, updateRound, unassignedJob, routes);
public ScoredJob call() throws Exception {
return RegretInsertion.getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
}
});
}
try {
List<Future<Boolean>> futures = executor.invokeAll(tasks);
for (int i = 0; i < unassignedJobList.size(); i++) {
Future<ScoredJob> fsj = completionService.take();
ScoredJob sJob = fsj.get();
if (sJob instanceof ScoredJob.BadJob) {
badJobList.add(sJob.getJob());
continue;
}
if (bestScoredJob == null) {
bestScoredJob = sJob;
} else if (sJob.getScore() > bestScoredJob.getScore()) {
bestScoredJob = sJob;
} else if (sJob.getScore() == bestScoredJob.getScore()) {
if (sJob.getJob().getId().compareTo(bestScoredJob.getJob().getId()) <= 0) {
bestScoredJob = sJob;
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
return bestScoredJob;
}
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
for(VehicleRoute r : routes){
if(r.getVehicle().getBreak() == job) return r;
}
return null;
}
}

View file

@ -0,0 +1,196 @@
/*******************************************************************************
* Copyright (C) 2014 Stefan Schroeder
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package jsprit.core.algorithm.recreate;
import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Break;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.vehicle.VehicleFleetManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
/**
* Insertion based on regret approach.
* <p/>
* <p>Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference
* between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction.
* The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this
* customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later.
*
* @author stefan schroeder
*/
public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
private static Logger logger = LogManager.getLogger(RegretInsertionConcurrentFast.class);
private ScoringFunction scoringFunction;
private final JobInsertionCostsCalculator insertionCostsCalculator;
private final ExecutorService executor;
private VehicleFleetManager fleetManager;
private Set<String> initialVehicleIds;
private boolean switchAllowed = true;
/**
* Sets the scoring function.
* <p/>
* <p>By default, the this.TimeWindowScorer is used.
*
* @param scoringFunction to score
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
this.scoringFunction = scoringFunction;
}
public RegretInsertionConcurrentFast(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService, VehicleFleetManager fleetManager) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
this.executor = executorService;
this.fleetManager = fleetManager;
this.initialVehicleIds = getInitialVehicleIds(vehicleRoutingProblem);
logger.debug("initialise " + this);
}
@Override
public String toString() {
return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
}
public void setSwitchAllowed(boolean switchAllowed) {
this.switchAllowed = switchAllowed;
}
private Set<String> getInitialVehicleIds(VehicleRoutingProblem vehicleRoutingProblem) {
Set<String> ids = new HashSet<String>();
for(VehicleRoute r : vehicleRoutingProblem.getInitialVehicleRoutes()){
ids.add(r.getVehicle().getId());
}
return ids;
}
/**
* Runs insertion.
* <p/>
* <p>Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
*
* @throws java.lang.RuntimeException if smth went wrong with thread execution
*/
@Override
public Collection<Job> insertUnassignedJobs(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
List<Job> badJobs = new ArrayList<Job>(unassignedJobs.size());
Iterator<Job> jobIterator = unassignedJobs.iterator();
while (jobIterator.hasNext()){
Job job = jobIterator.next();
if(job instanceof Break){
VehicleRoute route = InsertionDataUpdater.findRoute(routes, job);
if(route == null){
badJobs.add(job);
}
else {
InsertionData iData = insertionCostsCalculator.getInsertionData(route, job, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
if (iData instanceof InsertionData.NoInsertionFound) {
badJobs.add(job);
} else {
insertJob(job, iData, route);
}
}
jobIterator.remove();
}
}
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
TreeSet<VersionedInsertionData>[] priorityQueues = new TreeSet[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()) {
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
List<Job> badJobList = new ArrayList<Job>();
if(!firstRun && lastModified == null) throw new IllegalStateException("ho. this must not be.");
if(firstRun){
firstRun = false;
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound);
for(VehicleRoute r : routes) updates.put(r,updateRound);
}
else{
updateInsertionData(priorityQueues, Arrays.asList(lastModified), unassignedJobList, updateRound);
updates.put(lastModified,updateRound);
}
updateRound++;
ScoredJob bestScoredJob = InsertionDataUpdater.getBest(switchAllowed,initialVehicleIds,fleetManager, insertionCostsCalculator, scoringFunction, priorityQueues, updates, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
}
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
lastModified = bestScoredJob.getRoute();
}
else lastModified = null;
for (Job bad : badJobList) {
jobs.remove(bad);
badJobs.add(bad);
}
}
return badJobs;
}
private void updateInsertionData(final TreeSet<VersionedInsertionData>[] priorityQueues, final Collection<VehicleRoute> routes, List<Job> unassignedJobList, final int updateRound) {
List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
for (final Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
priorityQueues[unassignedJob.getIndex()] = new TreeSet<VersionedInsertionData>(InsertionDataUpdater.getComparator());
}
final TreeSet<VersionedInsertionData> priorityQueue = priorityQueues[unassignedJob.getIndex()];
tasks.add(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return InsertionDataUpdater.update(switchAllowed,initialVehicleIds,fleetManager, insertionCostsCalculator, priorityQueue, updateRound, unassignedJob, routes);
}
});
}
try {
List<Future<Boolean>> futures = executor.invokeAll(tasks);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}

View file

@ -21,14 +21,11 @@ import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Break;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.vehicle.VehicleFleetManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
import java.util.*;
/**
* Insertion based on regret approach.
@ -40,16 +37,31 @@ import java.util.concurrent.*;
*
* @author stefan schroeder
*/
public class RegretInsertionConcurrentSlow extends AbstractInsertionStrategy {
public class RegretInsertionFast extends AbstractInsertionStrategy {
private static Logger logger = LogManager.getLogger(RegretInsertionConcurrent.class);
private static Logger logger = LogManager.getLogger(RegretInsertionFast.class);
private ScoringFunction scoringFunction;
private final JobInsertionCostsCalculator insertionCostsCalculator;
private JobInsertionCostsCalculator insertionCostsCalculator;
private final ExecutorCompletionService<ScoredJob> completionService;
private VehicleFleetManager fleetManager;
private Set<String> initialVehicleIds;
private boolean switchAllowed = true;
public RegretInsertionFast(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, VehicleFleetManager fleetManager) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.fleetManager = fleetManager;
this.vrp = vehicleRoutingProblem;
this.initialVehicleIds = getInitialVehicleIds(vehicleRoutingProblem);
logger.debug("initialise {}", this);
}
/**
* Sets the scoring function.
@ -62,13 +74,16 @@ public class RegretInsertionConcurrentSlow extends AbstractInsertionStrategy {
this.scoringFunction = scoringFunction;
}
public RegretInsertionConcurrentSlow(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
completionService = new ExecutorCompletionService<ScoredJob>(executorService);
logger.debug("initialise " + this);
public void setSwitchAllowed(boolean switchAllowed) {
this.switchAllowed = switchAllowed;
}
private Set<String> getInitialVehicleIds(VehicleRoutingProblem vehicleRoutingProblem) {
Set<String> ids = new HashSet<String>();
for(VehicleRoute r : vehicleRoutingProblem.getInitialVehicleRoutes()){
ids.add(r.getVehicle().getId());
}
return ids;
}
@Override
@ -81,8 +96,6 @@ public class RegretInsertionConcurrentSlow extends AbstractInsertionStrategy {
* Runs insertion.
* <p/>
* <p>Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
*
* @throws java.lang.RuntimeException if smth went wrong with thread execution
*/
@Override
public Collection<Job> insertUnassignedJobs(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
@ -92,7 +105,7 @@ public class RegretInsertionConcurrentSlow extends AbstractInsertionStrategy {
while (jobIterator.hasNext()){
Job job = jobIterator.next();
if(job instanceof Break){
VehicleRoute route = findRoute(routes,job);
VehicleRoute route = InsertionDataUpdater.findRoute(routes, job);
if(route == null){
badJobs.add(job);
}
@ -109,72 +122,52 @@ public class RegretInsertionConcurrentSlow extends AbstractInsertionStrategy {
}
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
TreeSet<VersionedInsertionData>[] priorityQueues = new TreeSet[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()) {
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
List<Job> badJobList = new ArrayList<Job>();
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if(!firstRun && lastModified == null) throw new IllegalStateException("fooo");
if(firstRun){
firstRun = false;
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound);
for(VehicleRoute r : routes) updates.put(r,updateRound);
}
else{
updateInsertionData(priorityQueues, Arrays.asList(lastModified), unassignedJobList, updateRound);
updates.put(lastModified,updateRound);
}
updateRound++;
ScoredJob bestScoredJob = InsertionDataUpdater.getBest(switchAllowed,initialVehicleIds,fleetManager,insertionCostsCalculator,scoringFunction,priorityQueues,updates,unassignedJobList,badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
}
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
lastModified = bestScoredJob.getRoute();
}
for (Job j : badJobList) {
jobs.remove(j);
badJobs.add(j);
else lastModified = null;
for (Job bad : badJobList) {
jobs.remove(bad);
badJobs.add(bad);
}
}
return badJobs;
}
private ScoredJob nextJob(final Collection<VehicleRoute> routes, List<Job> unassignedJobList, List<Job> badJobList) {
ScoredJob bestScoredJob = null;
for (final Job unassignedJob : unassignedJobList) {
completionService.submit(new Callable<ScoredJob>() {
@Override
public ScoredJob call() throws Exception {
return RegretInsertionSlow.getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
}
});
}
try {
for (int i = 0; i < unassignedJobList.size(); i++) {
Future<ScoredJob> fsj = completionService.take();
ScoredJob sJob = fsj.get();
if (sJob instanceof ScoredJob.BadJob) {
badJobList.add(sJob.getJob());
continue;
}
if (bestScoredJob == null) {
bestScoredJob = sJob;
} else if (sJob.getScore() > bestScoredJob.getScore()) {
bestScoredJob = sJob;
} else if (sJob.getScore() == bestScoredJob.getScore()) {
if (sJob.getJob().getId().compareTo(bestScoredJob.getJob().getId()) <= 0) {
bestScoredJob = sJob;
}
}
private void updateInsertionData(TreeSet<VersionedInsertionData>[] priorityQueues, Collection<VehicleRoute> routes, List<Job> unassignedJobList, int updateRound) {
for (Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
priorityQueues[unassignedJob.getIndex()] = new TreeSet<VersionedInsertionData>(InsertionDataUpdater.getComparator());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw new RuntimeException(e);
InsertionDataUpdater.update(switchAllowed, initialVehicleIds,fleetManager,insertionCostsCalculator, priorityQueues[unassignedJob.getIndex()], updateRound, unassignedJob, routes);
}
return bestScoredJob;
}
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
for(VehicleRoute r : routes){
if(r.getVehicle().getBreak() == job) return r;
}
return null;
}
}

View file

@ -1,222 +0,0 @@
/*******************************************************************************
* Copyright (C) 2014 Stefan Schroeder
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package jsprit.core.algorithm.recreate;
import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Break;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Insertion based on regret approach.
* <p/>
* <p>Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference
* between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction.
* The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this
* customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later.
*
* @author stefan schroeder
*/
public class RegretInsertionSlow extends AbstractInsertionStrategy {
private static Logger logger = LogManager.getLogger(RegretInsertion.class);
private ScoringFunction scoringFunction;
private JobInsertionCostsCalculator insertionCostsCalculator;
/**
* Sets the scoring function.
* <p/>
* <p>By default, the this.TimeWindowScorer is used.
*
* @param scoringFunction to score
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
this.scoringFunction = scoringFunction;
}
public RegretInsertionSlow(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) {
super(vehicleRoutingProblem);
this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
logger.debug("initialise {}", this);
}
@Override
public String toString() {
return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
}
/**
* Runs insertion.
* <p/>
* <p>Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
*/
@Override
public Collection<Job> insertUnassignedJobs(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
List<Job> badJobs = new ArrayList<Job>(unassignedJobs.size());
Iterator<Job> jobIterator = unassignedJobs.iterator();
while (jobIterator.hasNext()){
Job job = jobIterator.next();
if(job instanceof Break){
VehicleRoute route = findRoute(routes,job);
if(route == null){
badJobs.add(job);
}
else {
InsertionData iData = insertionCostsCalculator.getInsertionData(route, job, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
if (iData instanceof InsertionData.NoInsertionFound) {
badJobs.add(job);
} else {
insertJob(job, iData, route);
}
}
jobIterator.remove();
}
}
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
while (!jobs.isEmpty()) {
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
List<Job> badJobList = new ArrayList<Job>();
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
}
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
}
for (Job bad : badJobList) {
jobs.remove(bad);
badJobs.add(bad);
}
}
return badJobs;
}
private VehicleRoute findRoute(Collection<VehicleRoute> routes, Job job) {
for(VehicleRoute r : routes){
if(r.getVehicle().getBreak() == job) return r;
}
return null;
}
private ScoredJob nextJob(Collection<VehicleRoute> routes, Collection<Job> unassignedJobList, List<Job> badJobs) {
ScoredJob bestScoredJob = null;
for (Job unassignedJob : unassignedJobList) {
ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
if (scoredJob instanceof ScoredJob.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;
}
}
}
}
return bestScoredJob;
}
static ScoredJob getScoredJob(Collection<VehicleRoute> routes, Job unassignedJob, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction) {
InsertionData best = null;
InsertionData secondBest = null;
VehicleRoute bestRoute = null;
double benchmark = Double.MAX_VALUE;
for (VehicleRoute route : routes) {
if (secondBest != null) {
benchmark = secondBest.getInsertionCost();
}
InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
if (iData instanceof InsertionData.NoInsertionFound) continue;
if (best == null) {
best = iData;
bestRoute = route;
} else if (iData.getInsertionCost() < best.getInsertionCost()) {
secondBest = best;
best = iData;
bestRoute = route;
} else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
secondBest = iData;
}
}
VehicleRoute emptyRoute = VehicleRoute.emptyRoute();
InsertionData iData = insertionCostsCalculator.getInsertionData(emptyRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
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) {
return new ScoredJob.BadJob(unassignedJob);
}
double score = score(unassignedJob, best, secondBest, scoringFunction);
ScoredJob scoredJob;
if (bestRoute == emptyRoute) {
scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, true);
} else scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, false);
return scoredJob;
}
static double score(Job unassignedJob, InsertionData best, InsertionData secondBest, ScoringFunction scoringFunction) {
if (best == null) {
throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
}
double score;
if (secondBest == null) { //either there is only one vehicle or there are more vehicles, but they cannot load unassignedJob
//if only one vehicle, I want the job to be inserted with min iCosts
//if there are more vehicles, I want this job to be prioritized since there are no alternatives
score = Integer.MAX_VALUE - best.getInsertionCost() + scoringFunction.score(best, unassignedJob);
} else {
score = (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
}
return score;
}
}

View file

@ -49,7 +49,7 @@ public class RegretInsertionTest {
VehicleFleetManager fm = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
JobInsertionCostsCalculator calculator = getCalculator(vrp);
RegretInsertion regretInsertion = new RegretInsertion(calculator, vrp, fm);
RegretInsertionFast regretInsertion = new RegretInsertionFast(calculator, vrp, fm);
Collection<VehicleRoute> routes = new ArrayList<VehicleRoute>();
regretInsertion.insertJobs(routes, vrp.getJobs().values());
@ -66,7 +66,7 @@ public class RegretInsertionTest {
VehicleFleetManager fm = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
JobInsertionCostsCalculator calculator = getCalculator(vrp);
RegretInsertion regretInsertion = new RegretInsertion(calculator, vrp, fm);
RegretInsertionFast regretInsertion = new RegretInsertionFast(calculator, vrp, fm);
Collection<VehicleRoute> routes = new ArrayList<VehicleRoute>();
regretInsertion.insertJobs(routes, vrp.getJobs().values());
@ -83,7 +83,7 @@ public class RegretInsertionTest {
VehicleFleetManager fm = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
JobInsertionCostsCalculator calculator = getCalculator(vrp);
RegretInsertion regretInsertion = new RegretInsertion(calculator, vrp, fm);
RegretInsertionFast regretInsertion = new RegretInsertionFast(calculator, vrp, fm);
Collection<VehicleRoute> routes = new ArrayList<VehicleRoute>();
CkeckJobSequence position = new CkeckJobSequence(2, s1);
@ -109,7 +109,7 @@ public class RegretInsertionTest {
VehicleFleetManager fm = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
JobInsertionCostsCalculator calculator = getShipmentCalculator(vrp);
RegretInsertion regretInsertion = new RegretInsertion(calculator, vrp, fm);
RegretInsertionFast regretInsertion = new RegretInsertionFast(calculator, vrp, fm);
Collection<VehicleRoute> routes = new ArrayList<VehicleRoute>();
CkeckJobSequence position = new CkeckJobSequence(2, s2);

View file

@ -2,9 +2,24 @@
<problem xmlns="http://www.w3schools.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com vrp_xml_schema.xsd">
<problemType>
<fleetSize>INFINITE</fleetSize>
<fleetSize>FINITE</fleetSize>
</problemType>
<vehicles>
<vehicle>
<id>v2</id>
<typeId>vehType2</typeId>
<startLocation>
<id>loc</id>
</startLocation>
<endLocation>
<id>loc</id>
</endLocation>
<timeSchedule>
<start>0.0</start>
<end>1.7976931348623157E308</end>
</timeSchedule>
<returnToDepot>true</returnToDepot>
</vehicle>
<vehicle>
<id>v1</id>
<typeId>vehType</typeId>
@ -33,58 +48,16 @@
<time>0.0</time>
</costs>
</type>
<type>
<id>vehType2</id>
<capacity-dimensions>
<dimension index="0">200</dimension>
</capacity-dimensions>
<costs>
<fixed>0.0</fixed>
<distance>1.0</distance>
<time>0.0</time>
</costs>
</type>
</vehicleTypes>
<services>
<service id="1" type="service">
<location>
<id>loc</id>
</location>
<capacity-dimensions>
<dimension index="0">1</dimension>
</capacity-dimensions>
<duration>2.0</duration>
<timeWindows>
<timeWindow>
<start>0.0</start>
<end>1.7976931348623157E308</end>
</timeWindow>
</timeWindows>
</service>
<service id="2" type="service">
<location>
<id>loc2</id>
</location>
<capacity-dimensions>
<dimension index="0">1</dimension>
</capacity-dimensions>
<duration>4.0</duration>
<timeWindows>
<timeWindow>
<start>0.0</start>
<end>1.7976931348623157E308</end>
</timeWindow>
</timeWindows>
</service>
</services>
<solutions>
<solution>
<cost>10.0</cost>
<routes>
<route>
<driverId>noDriver</driverId>
<vehicleId>v1</vehicleId>
<start>0.0</start>
<act type="service">
<serviceId>1</serviceId>
<arrTime>0.0</arrTime>
<endTime>0.0</endTime>
</act>
<end>0.0</end>
</route>
</routes>
<unassignedJobs>
<job id="2"/>
</unassignedJobs>
</solution>
</solutions>
</problem>

View file

@ -143,7 +143,7 @@ public class BuildAlgorithmFromScratch {
//regret insertion
InsertionBuilder iBuilder = new InsertionBuilder(vrp, fleetManager, stateManager, constraintManager);
iBuilder.setInsertionStrategy(InsertionBuilder.Strategy.REGRET);
RegretInsertion regret = (RegretInsertion) iBuilder.build();
RegretInsertionFast regret = (RegretInsertionFast) iBuilder.build();
DefaultScorer scoringFunction = new DefaultScorer(vrp);
scoringFunction.setDepotDistanceParam(0.2);
scoringFunction.setTimeWindowParam(-.2);