/******************************************************************************* * 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.examples; import jsprit.analysis.toolbox.AlgorithmEventsRecorder; import jsprit.analysis.toolbox.AlgorithmEventsViewer; import jsprit.core.algorithm.PrettyAlgorithmBuilder; import jsprit.core.algorithm.SearchStrategy; import jsprit.core.algorithm.VehicleRoutingAlgorithm; import jsprit.core.algorithm.acceptor.GreedyAcceptance; import jsprit.core.algorithm.listener.IterationStartsListener; import jsprit.core.algorithm.module.RuinAndRecreateModule; import jsprit.core.algorithm.recreate.*; import jsprit.core.algorithm.ruin.RadialRuinStrategyFactory; import jsprit.core.algorithm.ruin.RandomRuinStrategyFactory; import jsprit.core.algorithm.ruin.RuinStrategy; import jsprit.core.algorithm.ruin.distance.AvgServiceAndShipmentDistance; import jsprit.core.algorithm.selector.SelectBest; import jsprit.core.algorithm.state.StateManager; import jsprit.core.analysis.SolutionAnalyser; import jsprit.core.problem.Location; import jsprit.core.problem.VehicleRoutingProblem; import jsprit.core.problem.constraint.ConstraintManager; import jsprit.core.problem.job.Job; import jsprit.core.problem.solution.SolutionCostCalculator; import jsprit.core.problem.solution.VehicleRoutingProblemSolution; import jsprit.core.problem.solution.route.VehicleRoute; import jsprit.core.problem.vehicle.FiniteFleetManagerFactory; import jsprit.core.problem.vehicle.VehicleFleetManager; import jsprit.core.reporting.SolutionPrinter; import jsprit.core.util.Solutions; import jsprit.instance.reader.CordeauReader; import jsprit.util.Examples; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; public class BuildAlgorithmFromScratch { public static class MyBestStrategy extends AbstractInsertionStrategy { private JobInsertionCostsCalculatorLight insertionCalculator; public MyBestStrategy(VehicleRoutingProblem vrp, VehicleFleetManager fleetManager, StateManager stateManager, ConstraintManager constraintManager) { super(vrp); insertionCalculator = JobInsertionCostsCalculatorLightFactory.createStandardCalculator(vrp, fleetManager, stateManager, constraintManager); } @Override public Collection<Job> insertUnassignedJobs(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs) { List<Job> badJobs = new ArrayList<Job>(); List<Job> unassigned = new ArrayList<Job>(unassignedJobs); Collections.shuffle(unassigned, random); for (Job j : unassigned) { InsertionData bestInsertionData = InsertionData.createEmptyInsertionData(); VehicleRoute bestRoute = null; //look for inserting unassigned job into existing route for (VehicleRoute r : vehicleRoutes) { InsertionData insertionData = insertionCalculator.getInsertionData(j, r, bestInsertionData.getInsertionCost()); if (insertionData instanceof InsertionData.NoInsertionFound) continue; if (insertionData.getInsertionCost() < bestInsertionData.getInsertionCost()) { bestInsertionData = insertionData; bestRoute = r; } } //try whole new route VehicleRoute empty = VehicleRoute.emptyRoute(); InsertionData insertionData = insertionCalculator.getInsertionData(j, empty, bestInsertionData.getInsertionCost()); if (!(insertionData instanceof InsertionData.NoInsertionFound)) { if (insertionData.getInsertionCost() < bestInsertionData.getInsertionCost()) { vehicleRoutes.add(empty); insertJob(j, insertionData, empty); } } else { if (bestRoute != null) insertJob(j, bestInsertionData, bestRoute); else badJobs.add(j); } } return badJobs; } } public static void main(String[] args) { Examples.createOutputFolder(); VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); new CordeauReader(vrpBuilder).read("input/p08"); final VehicleRoutingProblem vrp = vrpBuilder.build(); VehicleRoutingAlgorithm vra = createAlgorithm(vrp); vra.setMaxIterations(100); AlgorithmEventsRecorder eventsRecorder = new AlgorithmEventsRecorder(vrp, "output/events.dgs.gz"); eventsRecorder.setRecordingRange(90, 100); vra.addListener(eventsRecorder); VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); SolutionPrinter.print(vrp, solution, SolutionPrinter.Print.VERBOSE); AlgorithmEventsViewer viewer = new AlgorithmEventsViewer(); viewer.setRuinDelay(3); viewer.setRecreationDelay(1); viewer.display("output/events.dgs.gz"); } public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp) { VehicleFleetManager fleetManager = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager(); StateManager stateManager = new StateManager(vrp); ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); /* * insertion strategies */ //my custom best insertion MyBestStrategy best = new MyBestStrategy(vrp, fleetManager, stateManager, constraintManager); //regret insertion InsertionBuilder iBuilder = new InsertionBuilder(vrp, fleetManager, stateManager, constraintManager); iBuilder.setInsertionStrategy(InsertionBuilder.Strategy.REGRET); RegretInsertionFast regret = (RegretInsertionFast) iBuilder.build(); DefaultScorer scoringFunction = new DefaultScorer(vrp); scoringFunction.setDepotDistanceParam(0.2); scoringFunction.setTimeWindowParam(-.2); regret.setScoringFunction(scoringFunction); /* * ruin strategies */ RuinStrategy randomRuin = new RandomRuinStrategyFactory(0.5).createStrategy(vrp); RuinStrategy radialRuin = new RadialRuinStrategyFactory(0.3, new AvgServiceAndShipmentDistance(vrp.getTransportCosts())).createStrategy(vrp); /* * objective function */ SolutionCostCalculator objectiveFunction = getObjectiveFunction(vrp); SearchStrategy firstStrategy = new SearchStrategy("firstStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); firstStrategy.addModule(new RuinAndRecreateModule("randRuinRegretIns", regret, randomRuin)); SearchStrategy secondStrategy = new SearchStrategy("secondStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); secondStrategy.addModule(new RuinAndRecreateModule("radRuinRegretIns", regret, radialRuin)); SearchStrategy thirdStrategy = new SearchStrategy("thirdStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); secondStrategy.addModule(new RuinAndRecreateModule("radRuinBestIns", regret, radialRuin)); PrettyAlgorithmBuilder prettyAlgorithmBuilder = PrettyAlgorithmBuilder.newInstance(vrp, fleetManager, stateManager, constraintManager); final VehicleRoutingAlgorithm vra = prettyAlgorithmBuilder .withStrategy(firstStrategy, 0.5).withStrategy(secondStrategy, 0.5).withStrategy(thirdStrategy, 0.2) .addCoreStateAndConstraintStuff() .constructInitialSolutionWith(regret, objectiveFunction) .build(); //if you want to switch on/off strategies or adapt their weight within the search, you can do the following //e.g. from iteration 50 on, switch off first strategy //switch on again at iteration 90 with slightly higher weight IterationStartsListener strategyAdaptor = new IterationStartsListener() { @Override public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) { if (i == 50) { vra.getSearchStrategyManager().informStrategyWeightChanged("firstStrategy", 0.0); System.out.println("switched off firstStrategy"); } if (i == 90) { vra.getSearchStrategyManager().informStrategyWeightChanged("firstStrategy", 0.7); System.out.println("switched on firstStrategy again with higher weight"); } } }; vra.addListener(strategyAdaptor); return vra; } private static SolutionCostCalculator getObjectiveFunction(final VehicleRoutingProblem vrp) { return new SolutionCostCalculator() { @Override public double getCosts(VehicleRoutingProblemSolution solution) { SolutionAnalyser analyser = new SolutionAnalyser(vrp, solution, new SolutionAnalyser.DistanceCalculator() { @Override public double getDistance(Location from, Location to) { return vrp.getTransportCosts().getTransportCost(from, to, 0., null, null); } }); return analyser.getVariableTransportCosts() + solution.getUnassignedJobs().size() * 500.; } }; } }