From 1488502f6546dcfcad84dc28120fcab02035f45a Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 30 Jan 2015 14:23:41 +0100 Subject: [PATCH] add and test new ruin strategy: RuinWorst --- .../jsprit/core/algorithm/ruin/RuinWorst.java | 165 +++++++++++++++ .../java/jsprit/core/util/NoiseMaker.java | 10 + .../core/algorithm/ruin/RuinWorstTest.java | 200 ++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinWorst.java create mode 100644 jsprit-core/src/main/java/jsprit/core/util/NoiseMaker.java create mode 100644 jsprit-core/src/test/java/jsprit/core/algorithm/ruin/RuinWorstTest.java diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinWorst.java b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinWorst.java new file mode 100644 index 00000000..d32d55a5 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinWorst.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * 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 . + ******************************************************************************/ +package jsprit.core.algorithm.ruin; + +import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.driver.DriverImpl; +import jsprit.core.problem.job.Job; +import jsprit.core.problem.solution.route.VehicleRoute; +import jsprit.core.problem.solution.route.activity.TourActivity; +import jsprit.core.problem.vehicle.Vehicle; +import jsprit.core.util.NoiseMaker; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.*; + + +/** + * Ruin strategy that ruins current solution randomly. I.e. + * customer are removed randomly from current solution. + * + * @author stefan schroeder + * + */ + +public final class RuinWorst extends AbstractRuinStrategy { + + private Logger logger = LogManager.getLogger(RuinWorst.class); + + private VehicleRoutingProblem vrp; + + private NoiseMaker noiseMaker = new NoiseMaker(){ + + @Override + public double makeNoise() { + return 0; + } + }; + + public void setNoiseMaker(NoiseMaker noiseMaker) { + this.noiseMaker = noiseMaker; + } + + public RuinWorst(VehicleRoutingProblem vrp, final int initialNumberJobsToRemove) { + super(); + this.vrp = vrp; + setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return initialNumberJobsToRemove; + } + }); + logger.info("initialise " + this); + logger.info("done"); + } + + /** + * Removes a fraction of jobs from vehicleRoutes. + * + *

The number of jobs is calculated as follows: Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined). + */ + @Override + public Collection ruinRoutes(Collection vehicleRoutes) { + List unassignedJobs = new ArrayList(); + int nOfJobs2BeRemoved = getRuinShareFactory().createNumberToBeRemoved(); + ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs); + return unassignedJobs; + } + + /** + * Removes nOfJobs2BeRemoved from vehicleRoutes, including targetJob. + */ + @Override + public Collection ruinRoutes(Collection vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved) { + throw new UnsupportedOperationException("ruinRoutes not supported"); + } + + private void ruin(Collection vehicleRoutes, int nOfJobs2BeRemoved, List unassignedJobs) { + LinkedList availableJobs = new LinkedList(vrp.getJobs().values()); + int toRemove = nOfJobs2BeRemoved; + while(toRemove > 0){ + Job worst = getWorst(vehicleRoutes); + if(worst == null) break; + removeJob(worst,vehicleRoutes); + availableJobs.remove(worst); + unassignedJobs.add(worst); + toRemove--; + } + } + + private Job getWorst(Collection copied) { + Job worst = null; + double bestSavings = Double.MIN_VALUE; + + for(VehicleRoute route : copied) { + if(route.isEmpty()) continue; + Map savingsMap = new HashMap(); + TourActivity actBefore = route.getStart(); + TourActivity actToEval = null; + for (TourActivity act : route.getActivities()) { + if (actToEval == null) { + actToEval = act; + continue; + } + double savings = savings(route, actBefore, actToEval, act); + Job job = ((TourActivity.JobActivity) actToEval).getJob(); + if(!savingsMap.containsKey(job)){ + savingsMap.put(job,savings); + } + else { + double s = savingsMap.get(job); + savingsMap.put(job,s+savings); + } + actBefore = actToEval; + actToEval = act; + } + double savings = savings(route, actBefore, actToEval, route.getEnd()); + Job job = ((TourActivity.JobActivity) actToEval).getJob(); + if(!savingsMap.containsKey(job)){ + savingsMap.put(job,savings); + } + else { + double s = savingsMap.get(job); + savingsMap.put(job,s+savings); + } + //getCounts best + for(Job j : savingsMap.keySet()){ + if(savingsMap.get(j) > bestSavings){ + bestSavings = savingsMap.get(j); + worst = j; + } + } + } + return worst; + } + + private double savings(VehicleRoute route, TourActivity actBefore, TourActivity actToEval, TourActivity act) { + double savings = c(actBefore, actToEval, route.getVehicle()) + c(actToEval, act, route.getVehicle()) - c(actBefore, act, route.getVehicle()); + return Math.max(0,savings + noiseMaker.makeNoise()); + } + + private double c(TourActivity from, TourActivity to, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportCost(from.getLocation(),to.getLocation(),from.getEndTime(), DriverImpl.noDriver(), vehicle); + } + + @Override + public String toString() { + return "[name=worstRuin]"; + } + +} diff --git a/jsprit-core/src/main/java/jsprit/core/util/NoiseMaker.java b/jsprit-core/src/main/java/jsprit/core/util/NoiseMaker.java new file mode 100644 index 00000000..ffcb1f7a --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/util/NoiseMaker.java @@ -0,0 +1,10 @@ +package jsprit.core.util; + +/** + * Created by schroeder on 16/01/15. + */ +public interface NoiseMaker { + + public double makeNoise(); + +} diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/ruin/RuinWorstTest.java b/jsprit-core/src/test/java/jsprit/core/algorithm/ruin/RuinWorstTest.java new file mode 100644 index 00000000..4e65494a --- /dev/null +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/ruin/RuinWorstTest.java @@ -0,0 +1,200 @@ +package jsprit.core.algorithm.ruin; + +import jsprit.core.problem.Location; +import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.job.Job; +import jsprit.core.problem.job.Service; +import jsprit.core.problem.job.Shipment; +import jsprit.core.problem.solution.route.VehicleRoute; +import jsprit.core.problem.vehicle.VehicleImpl; +import jsprit.core.util.Coordinate; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Created by schroeder on 30/01/15. + */ +public class RuinWorstTest { + + @Test + public void itShouldRemoveCorrectNumber(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s1).addJob(s2).addJob(s3).addVehicle(v).build(); + RuinWorst worst = new RuinWorst(vrp,1); + + VehicleRoute route = VehicleRoute.Builder.newInstance(v).addService(s1).addService(s2).addService(s3).setJobActivityFactory(vrp.getJobActivityFactory()).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route)); + assertEquals(1,unassigned.size()); + + } + + @Test + public void itShouldRemoveWorst(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s1).addJob(s2).addJob(s3).addVehicle(v).build(); + RuinWorst worst = new RuinWorst(vrp,1); + + VehicleRoute route = VehicleRoute.Builder.newInstance(v).addService(s1).addService(s2).addService(s3).setJobActivityFactory(vrp.getJobActivityFactory()).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route)); + assertEquals(s3,unassigned.iterator().next()); + + } + + @Test + public void itShouldRemoveWorstTwo(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s1).addJob(s2).addJob(s3).addVehicle(v).build(); + RuinWorst worst = new RuinWorst(vrp,1); + worst.setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return 2; + } + }); + + VehicleRoute route = VehicleRoute.Builder.newInstance(v).addService(s1).addService(s2).addService(s3).setJobActivityFactory(vrp.getJobActivityFactory()).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route)); + + assertTrue(unassigned.size() == 2); + assertTrue(unassigned.contains(s2)); + assertTrue(unassigned.contains(s3)); + + } + + @Test + public void itShouldRemoveShipment(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + Shipment shipment = Shipment.Builder.newInstance("ship1") + .setPickupLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(2, 2)).build()) + .setDeliveryLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(9, 9)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() + .addJob(shipment).addJob(s1).addJob(s2).addJob(s3).addVehicle(v).build(); + RuinWorst worst = new RuinWorst(vrp,1); + worst.setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return 1; + } + }); + + VehicleRoute route = VehicleRoute.Builder.newInstance(v) + .addPickup(shipment).addService(s1).addService(s2).addService(s3).addDelivery(shipment) + .setJobActivityFactory(vrp.getJobActivityFactory()).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route)); + + assertTrue(unassigned.size() == 1); + assertTrue(unassigned.contains(shipment)); + + } + + @Test + public void itShouldRemoveShipmentFromSecondRoute(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + Shipment shipment = Shipment.Builder.newInstance("ship1") + .setPickupLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()) + .setDeliveryLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10.1)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() + .addJob(shipment).addJob(s1).addJob(s2).addJob(s3).addVehicle(v).addVehicle(v2).build(); + RuinWorst worst = new RuinWorst(vrp,1); + worst.setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return 1; + } + }); + + VehicleRoute route1 = VehicleRoute.Builder.newInstance(v) + .addService(s1).addService(s2).addService(s3) + .setJobActivityFactory(vrp.getJobActivityFactory()).build(); + VehicleRoute route2 = VehicleRoute.Builder.newInstance(v2) + .addPickup(shipment).addDelivery(shipment).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route1,route2)); + + assertTrue(unassigned.size() == 1); + assertTrue(unassigned.contains(shipment)); + + } + + @Test + public void itShouldRemoveServiceAndShipmentFromSecondRoute(){ + Service s1 = Service.Builder.newInstance("s1") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(1, 1)).build()).build(); + Service s2 = Service.Builder.newInstance("s2") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()).build(); + Service s3 = Service.Builder.newInstance("s3") + .setLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10)).build()).build(); + Shipment shipment = Shipment.Builder.newInstance("ship1") + .setPickupLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(3, 1)).build()) + .setDeliveryLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 10.1)).build()).build(); + VehicleImpl v = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2") + .setStartLocation(Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(0, 0)).build()).build(); + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() + .addJob(shipment).addJob(s1).addJob(s2).addJob(s3).addVehicle(v).addVehicle(v2).build(); + RuinWorst worst = new RuinWorst(vrp,1); + worst.setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return 2; + } + }); + + VehicleRoute route1 = VehicleRoute.Builder.newInstance(v) + .addService(s1).addService(s2).addService(s3) + .setJobActivityFactory(vrp.getJobActivityFactory()).build(); + VehicleRoute route2 = VehicleRoute.Builder.newInstance(v2) + .addPickup(shipment).addDelivery(shipment).build(); + Collection unassigned = worst.ruinRoutes(Arrays.asList(route1,route2)); + + assertTrue(unassigned.size() == 2); + assertTrue(unassigned.contains(shipment)); + assertTrue(unassigned.contains(s3)); + + } + + + +}