From 8da37cf4b382efd6041a825d6bdd0640a5543398 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:30:16 +0100 Subject: [PATCH] memorize failed constraint names - related to #180 --- .../recreate/AbstractInsertionCalculator.java | 95 +++++++++++++++++++ .../recreate/AbstractInsertionStrategy.java | 5 + .../algorithm/recreate/BestInsertion.java | 11 ++- .../recreate/BestInsertionConcurrent.java | 14 ++- .../algorithm/recreate/InsertionData.java | 11 +++ .../recreate/InsertionDataUpdater.java | 14 ++- .../algorithm/recreate/RegretInsertion.java | 30 +++--- .../recreate/RegretInsertionConcurrent.java | 18 ++-- .../RegretInsertionConcurrentFast.java | 14 +-- .../recreate/RegretInsertionFast.java | 16 ++-- .../core/algorithm/recreate/ScoredJob.java | 12 ++- .../recreate/ServiceInsertionCalculator.java | 32 ++++--- .../recreate/ShipmentInsertionCalculator.java | 30 +++--- ...leTypeDependentJobInsertionCalculator.java | 3 +- 14 files changed, 237 insertions(+), 68 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java new file mode 100644 index 00000000..6f3d54a4 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java @@ -0,0 +1,95 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint; +import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus; +import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Created by schroeder on 06/02/17. + */ +abstract class AbstractInsertionCalculator implements JobInsertionCostsCalculator { + + InsertionData checkRouteContraints(JobInsertionContext insertionContext, ConstraintManager constraintManager) { + for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) { + if (!hardRouteConstraint.fulfilled(insertionContext)) { + InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); + emptyInsertionData.addFailedConstrainName(hardRouteConstraint.getClass().getSimpleName()); + return emptyInsertionData; + } + } + return null; + } + + ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) { + ConstraintsStatus notFulfilled = null; + List failed = new ArrayList<>(); + for (HardActivityConstraint c : constraintManager.getCriticalHardActivityConstraints()) { + ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime); + if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) { + failedActivityConstraints.add(c.getClass().getSimpleName()); + return status; + } else { + if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { + failed.add(c.getClass().getSimpleName()); + notFulfilled = status; + } + } + } + if (notFulfilled != null) { + failedActivityConstraints.addAll(failed); + return notFulfilled; + } + + for (HardActivityConstraint c : constraintManager.getHighPrioHardActivityConstraints()) { + ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime); + if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) { + failedActivityConstraints.add(c.getClass().getSimpleName()); + return status; + } else { + if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { + failed.add(c.getClass().getSimpleName()); + notFulfilled = status; + } + } + } + if (notFulfilled != null) { + failedActivityConstraints.addAll(failed); + return notFulfilled; + } + + for (HardActivityConstraint constraint : constraintManager.getLowPrioHardActivityConstraints()) { + ConstraintsStatus status = constraint.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime); + if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK) || status.equals(ConstraintsStatus.NOT_FULFILLED)) { + failedActivityConstraints.add(constraint.getClass().getSimpleName()); + return status; + } + } + return ConstraintsStatus.FULFILLED; + } + +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java index 7d794a6d..5f5973f5 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Random; public abstract class AbstractInsertionStrategy implements InsertionStrategy { @@ -92,6 +93,10 @@ public abstract class AbstractInsertionStrategy implements InsertionStrategy { return badJobs; } + public void markUnassigned(Job unassigned, List reasons) { + insertionsListeners.informJobUnassignedListeners(unassigned, reasons); + } + public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs); @Override diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java index a78205ae..329a05ff 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java @@ -66,10 +66,12 @@ public final class BestInsertion extends AbstractInsertionStrategy { sometimesSortPriorities(unassignedJobList); for (Job unassignedJob : unassignedJobList) { Insertion bestInsertion = null; + InsertionData empty = new InsertionData.NoInsertionFound(); double bestInsertionCost = Double.MAX_VALUE; for (VehicleRoute vehicleRoute : vehicleRoutes) { InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); if (iData instanceof InsertionData.NoInsertionFound) { + empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); continue; } if (iData.getInsertionCost() < bestInsertionCost + noiseMaker.makeNoise()) { @@ -84,14 +86,19 @@ public final class BestInsertion extends AbstractInsertionStrategy { bestInsertion = new Insertion(newRoute, newIData); vehicleRoutes.add(newRoute); } + } else { + empty.getFailedConstraintNames().addAll(newIData.getFailedConstraintNames()); + } + if (bestInsertion == null) { + badJobs.add(unassignedJob); + markUnassigned(unassignedJob, empty.getFailedConstraintNames()); } - if (bestInsertion == null) badJobs.add(unassignedJob); else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); -// nextInsertion(); } return badJobs; } + private void sometimesSortPriorities(List unassignedJobList) { if(random.nextDouble() < 0.5){ Collections.sort(unassignedJobList, new Comparator() { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java index 52d913df..2b014df0 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java @@ -101,6 +101,7 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy { Collections.shuffle(unassignedJobList, random); sometimesSortPriorities(unassignedJobList); List batches = distributeRoutes(vehicleRoutes, nuOfBatches); + List failedConstraintNames = new ArrayList<>(); for (final Job unassignedJob : unassignedJobList) { Insertion bestInsertion = null; double bestInsertionCost = Double.MAX_VALUE; @@ -118,7 +119,10 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy { for (int i = 0; i < batches.size(); i++) { Future futureIData = completionService.take(); Insertion insertion = futureIData.get(); - if (insertion == null) continue; + if (insertion.insertionData instanceof NoInsertionFound) { + failedConstraintNames.addAll(insertion.getInsertionData().getFailedConstraintNames()); + continue; + } if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) { bestInsertion = insertion; bestInsertionCost = insertion.getInsertionData().getInsertionCost(); @@ -136,7 +140,10 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy { vehicleRoutes.add(newRoute); batches.get(random.nextInt(batches.size())).routes.add(newRoute); } - if (bestInsertion == null) badJobs.add(unassignedJob); + if (bestInsertion == null) { + badJobs.add(unassignedJob); + markUnassigned(unassignedJob, failedConstraintNames); + } else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } return badJobs; @@ -155,10 +162,12 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy { private Insertion getBestInsertion(Batch batch, Job unassignedJob) { Insertion bestInsertion = null; + InsertionData empty = new InsertionData.NoInsertionFound(); double bestInsertionCost = Double.MAX_VALUE; for (VehicleRoute vehicleRoute : batch.routes) { InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); if (iData instanceof NoInsertionFound) { + empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); continue; } if (iData.getInsertionCost() < bestInsertionCost) { @@ -166,6 +175,7 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy { bestInsertionCost = iData.getInsertionCost(); } } + if (bestInsertion == null) return new Insertion(null, empty); return bestInsertion; } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java index d5b5ef8d..fd78b3e9 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java @@ -31,6 +31,7 @@ import java.util.List; */ public class InsertionData { + public static class NoInsertionFound extends InsertionData { public NoInsertionFound() { @@ -75,6 +76,8 @@ public class InsertionData { return events; } + private List reasons = new ArrayList<>(); + /** * @return the additionalTime */ @@ -82,6 +85,14 @@ public class InsertionData { return additionalTime; } + public void addFailedConstrainName(String name) { + reasons.add(name); + } + + public List getFailedConstraintNames() { + return reasons; + } + /** * @param additionalTime the additionalTime to set */ diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java index ced87182..8d019e2c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java @@ -33,7 +33,7 @@ class InsertionDataUpdater { static boolean update(boolean addAllAvailable, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, TreeSet insertionDataSet, int updateRound, Job unassignedJob, Collection routes) { for(VehicleRoute route : routes) { - Collection relevantVehicles = new ArrayList(); + Collection relevantVehicles = new ArrayList<>(); if (!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) { relevantVehicles.add(route.getVehicle()); if(addAllAvailable && !initialVehicleIds.contains(route.getVehicle().getId())){ @@ -71,7 +71,7 @@ class InsertionDataUpdater { }; } - static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) { + static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) { ScoredJob bestScoredJob = null; for(Job j : unassignedJobList){ VehicleRoute bestRoute = null; @@ -79,6 +79,7 @@ class InsertionDataUpdater { InsertionData secondBest = null; TreeSet priorityQueue = priorityQueues[j.getIndex()]; Iterator iterator = priorityQueue.iterator(); + List failedConstraintNames = new ArrayList<>(); while(iterator.hasNext()){ VersionedInsertionData versionedIData = iterator.next(); if(bestRoute != null){ @@ -86,7 +87,10 @@ class InsertionDataUpdater { continue; } } - if(versionedIData.getiData() instanceof InsertionData.NoInsertionFound) continue; + if (versionedIData.getiData() instanceof InsertionData.NoInsertionFound) { + failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraintNames()); + continue; + } if(!(versionedIData.getRoute().getVehicle() instanceof VehicleImpl.NoVehicle)) { if (versionedIData.getiData().getSelectedVehicle() != versionedIData.getRoute().getVehicle()) { if (!switchAllowed) continue; @@ -136,9 +140,9 @@ class InsertionDataUpdater { } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) { secondBest = iData; } - } + } else failedConstraintNames.addAll(iData.getFailedConstraintNames()); if (best == null) { - badJobs.add(j); + badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames)); continue; } double score = score(j, best, secondBest, scoringFunction); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java index 6e405dec..30a7a98a 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java @@ -105,10 +105,10 @@ public class RegretInsertion extends AbstractInsertionStrategy { } } - List jobs = new ArrayList(unassignedJobs); + List jobs = new ArrayList<>(unassignedJobs); while (!jobs.isEmpty()) { - List unassignedJobList = new ArrayList(jobs); - List badJobList = new ArrayList(); + List unassignedJobList = new ArrayList<>(jobs); + List badJobList = new ArrayList<>(); ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList); if (bestScoredJob != null) { if (bestScoredJob.isNewRoute()) { @@ -117,9 +117,11 @@ public class RegretInsertion extends AbstractInsertionStrategy { insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute()); jobs.remove(bestScoredJob.getJob()); } - for (Job bad : badJobList) { - jobs.remove(bad); - badJobs.add(bad); + for (ScoredJob bad : badJobList) { + Job unassigned = bad.getJob(); + jobs.remove(unassigned); + badJobs.add(unassigned); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); } } return badJobs; @@ -132,12 +134,12 @@ public class RegretInsertion extends AbstractInsertionStrategy { return null; } - private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) { + private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) { ScoredJob bestScoredJob = null; for (Job unassignedJob : unassignedJobList) { ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction); if (scoredJob instanceof ScoredJob.BadJob) { - badJobs.add(unassignedJob); + badJobs.add(scoredJob); continue; } if (bestScoredJob == null) bestScoredJob = scoredJob; @@ -158,14 +160,17 @@ public class RegretInsertion extends AbstractInsertionStrategy { InsertionData best = null; InsertionData secondBest = null; VehicleRoute bestRoute = null; - + List failedConstraintNames = new ArrayList<>(); 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 (iData instanceof InsertionData.NoInsertionFound) { + failedConstraintNames.addAll(iData.getFailedConstraintNames()); + continue; + } if (best == null) { best = iData; bestRoute = route; @@ -191,9 +196,10 @@ public class RegretInsertion extends AbstractInsertionStrategy { } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) { secondBest = iData; } - } + } else failedConstraintNames.addAll(iData.getFailedConstraintNames()); if (best == null) { - return new ScoredJob.BadJob(unassignedJob); + ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraintNames); + return badJob; } double score = score(unassignedJob, best, secondBest, scoringFunction); ScoredJob scoredJob; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java index fe2146ba..b71ac5ab 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java @@ -109,10 +109,10 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy { } } - List jobs = new ArrayList(unassignedJobs); + List jobs = new ArrayList<>(unassignedJobs); while (!jobs.isEmpty()) { - List unassignedJobList = new ArrayList(jobs); - List badJobList = new ArrayList(); + List unassignedJobList = new ArrayList<>(jobs); + List badJobList = new ArrayList<>(); ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList); if (bestScoredJob != null) { if (bestScoredJob.isNewRoute()) { @@ -121,15 +121,17 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy { insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute()); jobs.remove(bestScoredJob.getJob()); } - for (Job j : badJobList) { - jobs.remove(j); - badJobs.add(j); + for (ScoredJob bad : badJobList) { + Job unassigned = bad.getJob(); + jobs.remove(unassigned); + badJobs.add(unassigned); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); } } return badJobs; } - private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) { + private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) { ScoredJob bestScoredJob = null; for (final Job unassignedJob : unassignedJobList) { @@ -148,7 +150,7 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy { Future fsj = completionService.take(); ScoredJob sJob = fsj.get(); if (sJob instanceof ScoredJob.BadJob) { - badJobList.add(sJob.getJob()); + badJobList.add(sJob); continue; } if (bestScoredJob == null) { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java index 187145f6..be4f5d11 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java @@ -143,8 +143,8 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy { int updateRound = 0; Map updates = new HashMap(); while (!jobs.isEmpty()) { - List unassignedJobList = new ArrayList(jobs); - List badJobList = new ArrayList(); + List unassignedJobList = new ArrayList<>(jobs); + List badJobList = new ArrayList<>(); if(!firstRun && lastModified == null) throw new IllegalStateException("ho. this must not be."); updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound,firstRun,lastModified,updates); if(firstRun) firstRun = false; @@ -159,9 +159,11 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy { lastModified = bestScoredJob.getRoute(); } else lastModified = null; - for (Job bad : badJobList) { - jobs.remove(bad); - badJobs.add(bad); + for (ScoredJob bad : badJobList) { + Job unassigned = bad.getJob(); + jobs.remove(unassigned); + badJobs.add(unassigned); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); } } return badJobs; @@ -172,7 +174,7 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy { boolean updatedAllRoutes = false; for (final Job unassignedJob : unassignedJobList) { if(priorityQueues[unassignedJob.getIndex()] == null){ - priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator()); + priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator()); } if(firstRun) { updatedAllRoutes = true; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java index 4369dd01..d805e55d 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java @@ -131,10 +131,10 @@ public class RegretInsertionFast extends AbstractInsertionStrategy { VehicleRoute lastModified = null; boolean firstRun = true; int updateRound = 0; - Map updates = new HashMap(); + Map updates = new HashMap<>(); while (!jobs.isEmpty()) { - List unassignedJobList = new ArrayList(jobs); - List badJobList = new ArrayList(); + List unassignedJobList = new ArrayList<>(jobs); + List badJobList = new ArrayList<>(); if(!firstRun && lastModified == null) throw new IllegalStateException("last modified route is null. this should not be."); if(firstRun){ updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound, firstRun, lastModified, updates); @@ -156,9 +156,11 @@ public class RegretInsertionFast extends AbstractInsertionStrategy { lastModified = bestScoredJob.getRoute(); } else lastModified = null; - for (Job bad : badJobList) { - jobs.remove(bad); - badJobs.add(bad); + for (ScoredJob bad : badJobList) { + Job unassigned = bad.getJob(); + jobs.remove(unassigned); + badJobs.add(unassigned); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); } } return badJobs; @@ -167,7 +169,7 @@ public class RegretInsertionFast extends AbstractInsertionStrategy { private void updateInsertionData(TreeSet[] priorityQueues, Collection routes, List unassignedJobList, int updateRound, boolean firstRun, VehicleRoute lastModified, Map updates) { for (Job unassignedJob : unassignedJobList) { if(priorityQueues[unassignedJob.getIndex()] == null){ - priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator()); + priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator()); } if(firstRun) { InsertionDataUpdater.update(switchAllowed, initialVehicleIds, fleetManager, insertionCostsCalculator, priorityQueues[unassignedJob.getIndex()], updateRound, unassignedJob, routes); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java index 48582800..f0f8950f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java @@ -21,6 +21,8 @@ package com.graphhopper.jsprit.core.algorithm.recreate; import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import java.util.List; + /** * Created by schroeder on 15/10/15. */ @@ -28,8 +30,14 @@ class ScoredJob { static class BadJob extends ScoredJob { - BadJob(Job job) { - super(job, 0., null, null, false); + BadJob(Job job, List failedConstraintNames) { + super(job, 0., getEmptyInsertion(failedConstraintNames), null, false); + } + + private static InsertionData getEmptyInsertion(List failedConstraintNames) { + InsertionData empty = new InsertionData.NoInsertionFound(); + empty.getFailedConstraintNames().addAll(failedConstraintNames); + return empty; } } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java index 977ba6ab..9f91ff9b 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java @@ -18,8 +18,10 @@ package com.graphhopper.jsprit.core.algorithm.recreate; import com.graphhopper.jsprit.core.problem.JobActivityFactory; -import com.graphhopper.jsprit.core.problem.constraint.*; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus; +import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint; +import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint; import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts; import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; import com.graphhopper.jsprit.core.problem.driver.Driver; @@ -36,6 +38,8 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; /** @@ -43,13 +47,13 @@ import java.util.Iterator; * * @author schroeder */ -final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { +final class ServiceInsertionCalculator extends AbstractInsertionCalculator { private static final Logger logger = LoggerFactory.getLogger(ServiceInsertionCalculator.class); - private HardRouteConstraint hardRouteLevelConstraint; +// private HardRouteConstraint hardRouteLevelConstraint; - private HardActivityConstraint hardActivityLevelConstraint; +// private HardActivityConstraint hardActivityLevelConstraint; private SoftRouteConstraint softRouteConstraint; @@ -65,12 +69,13 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private ConstraintManager constraintManager; + public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) { super(); this.transportCosts = routingCosts; this.activityCosts = activityCosts; - hardRouteLevelConstraint = constraintManager; - hardActivityLevelConstraint = constraintManager; + this.constraintManager = constraintManager; softActivityConstraint = constraintManager; softRouteConstraint = constraintManager; this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; @@ -103,9 +108,10 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { /* check hard constraints at route level */ - if (!hardRouteLevelConstraint.fulfilled(insertionContext)) { - return InsertionData.createEmptyInsertionData(); - } + InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager); + if (noInsertion != null) return noInsertion; + + Collection failedActivityConstraints = new ArrayList<>(); /* check soft constraints at route level @@ -142,7 +148,7 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { ActivityContext activityContext = new ActivityContext(); activityContext.setInsertionIndex(actIndex); insertionContext.setActivityContext(activityContext); - ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); + ConstraintsStatus status = fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime, failedActivityConstraints, constraintManager); if (status.equals(ConstraintsStatus.FULFILLED)) { double additionalICostsAtActLevel = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); @@ -163,7 +169,9 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { actIndex++; } if(insertionIndex == InsertionData.NO_INDEX) { - return InsertionData.createEmptyInsertionData(); + InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); + emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + return emptyInsertionData; } InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver); deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(bestTimeWindow.getStart()); @@ -173,4 +181,6 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator { insertionData.setVehicleDepartureTime(newVehicleDepartureTime); return insertionData; } + + } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java index 87c29c0e..e8698483 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java @@ -18,8 +18,10 @@ package com.graphhopper.jsprit.core.algorithm.recreate; import com.graphhopper.jsprit.core.problem.JobActivityFactory; -import com.graphhopper.jsprit.core.problem.constraint.*; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus; +import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint; +import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint; import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts; import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; import com.graphhopper.jsprit.core.problem.driver.Driver; @@ -36,16 +38,19 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; -final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { +final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { private static final Logger logger = LoggerFactory.getLogger(ShipmentInsertionCalculator.class); - private HardRouteConstraint hardRouteLevelConstraint; + private final ConstraintManager constraintManager; - private HardActivityConstraint hardActivityLevelConstraint; +// private HardRouteConstraint hardRouteLevelConstraint; +// +// private HardActivityConstraint hardActivityLevelConstraint; private SoftRouteConstraint softRouteConstraint; @@ -64,8 +69,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) { super(); this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; - this.hardRouteLevelConstraint = constraintManager; - this.hardActivityLevelConstraint = constraintManager; + this.constraintManager = constraintManager; this.softActivityConstraint = constraintManager; this.softRouteConstraint = constraintManager; this.transportCosts = routingCosts; @@ -99,9 +103,8 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { /* check hard route constraints */ - if (!hardRouteLevelConstraint.fulfilled(insertionContext)) { - return InsertionData.createEmptyInsertionData(); - } + InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager); + if (noInsertion != null) return noInsertion; /* check soft route constraints */ @@ -132,6 +135,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { //pickupShipmentLoop List activities = currentRoute.getTourActivities().getActivities(); + List failedActivityConstraints = new ArrayList<>(); while (!tourEnd) { TourActivity nextAct; if (i < activities.size()) { @@ -148,7 +152,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { ActivityContext activityContext = new ActivityContext(); activityContext.setInsertionIndex(i); insertionContext.setActivityContext(activityContext); - ConstraintsStatus pickupShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime); + ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager); if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) { pickupInsertionNotFulfilledBreak = false; continue; @@ -194,7 +198,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { ActivityContext activityContext_ = new ActivityContext(); activityContext_.setInsertionIndex(j); insertionContext.setActivityContext(activityContext_); - ConstraintsStatus deliverShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop); + ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop, failedActivityConstraints, constraintManager); if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop); double deliveryAIC = calculate(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop); @@ -230,7 +234,9 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator { i++; } if (pickupInsertionIndex == InsertionData.NO_INDEX) { - return InsertionData.createEmptyInsertionData(); + InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); + emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + return emptyInsertionData; } InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver); pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart()); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java index 9190a2f2..ad522012 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java @@ -98,7 +98,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo } Vehicle selectedVehicle = currentRoute.getVehicle(); Driver selectedDriver = currentRoute.getDriver(); - InsertionData bestIData = InsertionData.createEmptyInsertionData(); + InsertionData bestIData = new InsertionData.NoInsertionFound(); double bestKnownCost_ = bestKnownCost; Collection relevantVehicles = new ArrayList(); if (!(selectedVehicle instanceof VehicleImpl.NoVehicle)) { @@ -115,6 +115,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo else depTime = v.getEarliestDeparture(); InsertionData iData = insertionCalculator.getInsertionData(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_); if (iData instanceof InsertionData.NoInsertionFound) { + bestIData.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); continue; } if (iData.getInsertionCost() < bestKnownCost_) {