diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java index 2f8281e9..3278e04a 100644 --- a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java +++ b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java @@ -2,6 +2,7 @@ package algorithms; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; import basics.VehicleRoutingProblem; import basics.algo.InsertionListener; @@ -29,16 +30,21 @@ public class BestInsertionBuilder { private int forwaredLooking; private int memory; + + private ExecutorService executor; + + private int nuOfThreads; - public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager) { + public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) { super(); this.vrp = vrp; this.stateManager = stateManager; - this.constraintManager = new ConstraintManager(vrp,stateManager); + this.constraintManager = constraintManager; this.fleetManager = vehicleFleetManager; } public BestInsertionBuilder setRouteLevel(int forwardLooking, int memory){ + local = false; this.forwaredLooking = forwardLooking; this.memory = memory; @@ -56,11 +62,17 @@ public class BestInsertionBuilder { return this; } - public void setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator){ + public BestInsertionBuilder setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator){ this.actInsertionCostsCalculator = activityInsertionCostsCalculator; + return this; }; - + public BestInsertionBuilder setConcurrentMode(ExecutorService executor, int nuOfThreads){ + this.executor = executor; + this.nuOfThreads = nuOfThreads; + return this; + } + public InsertionStrategy build() { List iListeners = new ArrayList(); List algorithmListeners = new ArrayList(); @@ -80,13 +92,16 @@ public class BestInsertionBuilder { calcBuilder.considerFixedCosts(weightOfFixedCosts); } JobInsertionCostsCalculator jobInsertions = calcBuilder.build(); - BestInsertion bestInsertion = new BestInsertion(jobInsertions); - for(InsertionListener l : iListeners) bestInsertion.addListener(l); + InsertionStrategy bestInsertion; + if(executor == null){ + bestInsertion = new BestInsertion(jobInsertions); + + } + else{ + bestInsertion = new BestInsertionConcurrent(jobInsertions,executor,nuOfThreads); + } + for(InsertionListener l : iListeners) bestInsertion.addListener(l); return bestInsertion; } - public void setConstraintManager(ConstraintManager constraintManager) { - this.constraintManager = constraintManager; - } - } diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java new file mode 100644 index 00000000..afb37573 --- /dev/null +++ b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (C) 2013 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 . + ******************************************************************************/ +package algorithms; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import org.apache.log4j.Logger; + +import util.RandomNumberGeneration; +import algorithms.InsertionData.NoInsertionFound; +import basics.Job; +import basics.algo.InsertionListener; +import basics.route.Driver; +import basics.route.Vehicle; +import basics.route.VehicleRoute; + + + +/** + * + * @author stefan schroeder + * + */ + +final class BestInsertionConcurrent implements InsertionStrategy{ + + static class Batch { + List routes = new ArrayList(); + + } + + class Insertion { + + private final VehicleRoute route; + + private final InsertionData insertionData; + + public Insertion(VehicleRoute vehicleRoute, InsertionData insertionData) { + super(); + this.route = vehicleRoute; + this.insertionData = insertionData; + } + + public VehicleRoute getRoute() { + return route; + } + + public InsertionData getInsertionData() { + return insertionData; + } + + } + + private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class); + + private Random random = RandomNumberGeneration.getRandom(); + + private final static double NO_NEW_DEPARTURE_TIME_YET = -12345.12345; + + private final static Vehicle NO_NEW_VEHICLE_YET = null; + + private final static Driver NO_NEW_DRIVER_YET = null; + + private InsertionListeners insertionsListeners; + + private Inserter inserter; + + private JobInsertionCostsCalculator bestInsertionCostCalculator; + + private boolean minVehiclesFirst = false; + + private int nuOfBatches; + + private ExecutorService executor; + + private ExecutorCompletionService completionService; + + public void setRandom(Random random) { + this.random = random; + } + + public BestInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches) { + super(); + this.insertionsListeners = new InsertionListeners(); + this.executor = executorService; + this.nuOfBatches = nuOfBatches; + inserter = new Inserter(insertionsListeners); + bestInsertionCostCalculator = jobInsertionCalculator; + completionService = new ExecutorCompletionService(executor); + logger.info("initialise " + this); + } + + @Override + public String toString() { + return "[name=bestInsertion]"; + } + + @Override + public void insertJobs(Collection vehicleRoutes, Collection unassignedJobs) { + insertionsListeners.informInsertionStarts(vehicleRoutes,unassignedJobs); + List unassignedJobList = new ArrayList(unassignedJobs); + Collections.shuffle(unassignedJobList, random); + + List batches = distributeRoutes(vehicleRoutes,nuOfBatches); + + for(final Job unassignedJob : unassignedJobList){ + + Insertion bestInsertion = null; + double bestInsertionCost = Double.MAX_VALUE; + + for(final Batch batch : batches){ + completionService.submit(new Callable() { + + @Override + public Insertion call() throws Exception { + return getBestInsertion(batch,unassignedJob); + } + + }); + + } + + try{ + for(int i=0;i futureIData = completionService.take(); + Insertion insertion = futureIData.get(); + if(insertion == null) continue; + if(insertion.getInsertionData().getInsertionCost() < bestInsertionCost){ + bestInsertion = insertion; + bestInsertionCost = insertion.getInsertionData().getInsertionCost(); + } + } + } + catch(InterruptedException e){ + Thread.currentThread().interrupt(); + } + catch (ExecutionException e) { + e.printStackTrace(); + logger.error(e.getCause().toString()); + System.exit(1); + } + + if(!minVehiclesFirst){ + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData newIData = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); + if(newIData.getInsertionCost() < bestInsertionCost){ + bestInsertion = new Insertion(newRoute,newIData); + bestInsertionCost = newIData.getInsertionCost(); + vehicleRoutes.add(newRoute); + batches.get(0).routes.add(newRoute); + } + } + + if(bestInsertion == null){ + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData bestI = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE); + if(bestI instanceof InsertionData.NoInsertionFound){ + throw new IllegalStateException(getErrorMsg(unassignedJob)); + } + else{ + bestInsertion = new Insertion(newRoute,bestI); + vehicleRoutes.add(newRoute); + } + } +// logger.info("insert " + unassignedJob + " pickup@" + bestInsertion.getInsertionData().getPickupInsertionIndex() + " delivery@" + bestInsertion.getInsertionData().getDeliveryInsertionIndex()); + inserter.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); + } + insertionsListeners.informInsertionEndsListeners(vehicleRoutes); + } + + private String getErrorMsg(Job unassignedJob) { + return "given the vehicles, could not insert job\n" + + "\t" + unassignedJob + + "\n\tthis might have the following reasons:\n" + + "\t- no vehicle has the capacity to transport the job [check whether there is at least one vehicle that is capable to transport the job]\n" + + "\t- the time-window cannot be met, even in a commuter tour the time-window is missed [check whether it is possible to reach the time-window on the shortest path or make hard time-windows soft]\n" + + "\t- if you deal with finite vehicles, and the available vehicles are already fully employed, no vehicle can be found anymore to transport the job [add penalty-vehicles]"; + } + + @Override + public void removeListener(InsertionListener insertionListener) { + insertionsListeners.removeListener(insertionListener); + } + + @Override + public Collection getListeners() { + return Collections.unmodifiableCollection(insertionsListeners.getListeners()); + } + + @Override + public void addListener(InsertionListener insertionListener) { + insertionsListeners.addListener(insertionListener); + + } + + private Insertion getBestInsertion(Batch batch, Job unassignedJob) { + Insertion bestInsertion = null; + 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) { + continue; + } + if(iData.getInsertionCost() < bestInsertionCost){ + bestInsertion = new Insertion(vehicleRoute,iData); + bestInsertionCost = iData.getInsertionCost(); + } + } + return bestInsertion; + } + + private List distributeRoutes(Collection vehicleRoutes, int nuOfBatches) { + List batches = new ArrayList(); + for(int i=0;i