From 48dc694fca1d23c04b8b55c1ed604d57d9cf461f Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 6 Mar 2015 16:16:45 +0100 Subject: [PATCH] add and test new ruin strategy: cluster ruin based on dbscan clusterer --- .../core/algorithm/ruin/RuinClusters.java | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinClusters.java diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinClusters.java b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinClusters.java new file mode 100644 index 00000000..0492d1f7 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/RuinClusters.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * 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.algorithm.listener.IterationStartsListener; +import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.job.Job; +import jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import jsprit.core.problem.solution.route.VehicleRoute; +import jsprit.core.problem.solution.route.activity.TourActivity; +import jsprit.core.util.RandomNumberGeneration; +import jsprit.core.util.RandomUtils; +import org.apache.commons.math3.ml.clustering.Clusterable; +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 RuinClusters extends AbstractRuinStrategy implements IterationStartsListener { + + @Override + public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { + minPts = 1 + RandomNumberGeneration.getRandom().nextInt(2); + epsFactor = 0.5 + RandomNumberGeneration.getRandom().nextDouble(); + } + + public static class JobActivityWrapper implements Clusterable { + + private TourActivity.JobActivity jobActivity; + + public JobActivityWrapper(TourActivity.JobActivity jobActivity) { + this.jobActivity = jobActivity; + } + + @Override + public double[] getPoint() { + return new double[]{ jobActivity.getLocation().getCoordinate().getX(), jobActivity.getLocation().getCoordinate().getY() }; + } + + public TourActivity.JobActivity getActivity(){ + return jobActivity; + } + } + + private Logger logger = LogManager.getLogger(RuinClusters.class); + + private VehicleRoutingProblem vrp; + + + private JobNeighborhoods jobNeighborhoods; + + private int noClusters = 2; + + private int minPts = 1; + + private double epsFactor = 0.8; + + public RuinClusters(VehicleRoutingProblem vrp, final int initialNumberJobsToRemove, JobNeighborhoods jobNeighborhoods) { + super(); + this.vrp = vrp; + setRuinShareFactory(new RuinShareFactory() { + @Override + public int createNumberToBeRemoved() { + return initialNumberJobsToRemove; + } + }); + this.jobNeighborhoods = jobNeighborhoods; + logger.info("initialise " + this); + logger.info("done"); + } + + public void setNoClusters(int noClusters) { + this.noClusters = noClusters; + } + + /** + * 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 IllegalStateException("not supported"); + } + + private void ruin(Collection vehicleRoutes, int nOfJobs2BeRemoved, List unassignedJobs) { + Map mappedRoutes = map(vehicleRoutes); + int toRemove = nOfJobs2BeRemoved; + + Collection lastRemoved = new ArrayList(); + Set ruined = new HashSet(); + Set removed = new HashSet(); + Set cycleCandidates = new HashSet(); + while(toRemove > 0) { + Job target; + VehicleRoute targetRoute = null; + if(lastRemoved.isEmpty()){ + target = RandomUtils.nextJob(vrp.getJobs().values(), random); + targetRoute = mappedRoutes.get(target); + } + else{ + target = RandomUtils.nextJob(lastRemoved, random); + Iterator neighborIterator = jobNeighborhoods.getNearestNeighborsIterator(nOfJobs2BeRemoved,target); + while(neighborIterator.hasNext()){ + Job j = neighborIterator.next(); + if(!removed.contains(j) && !ruined.contains(mappedRoutes.get(j))){ + targetRoute = mappedRoutes.get(j); + break; + } + } + lastRemoved.clear(); + } + if(targetRoute == null) break; + if(cycleCandidates.contains(targetRoute)) break; + if(ruined.contains(targetRoute)) { + cycleCandidates.add(targetRoute); + break; + } + DBSCANClusterer dbscan = new DBSCANClusterer(vrp.getTransportCosts()); + dbscan.setMinPts(minPts); + dbscan.setEpsFactor(epsFactor); + List cluster = dbscan.getRandomCluster(targetRoute); + for(Job j : cluster){ + if(toRemove == 0) break; + removeJob(j, vehicleRoutes); + lastRemoved.add(j); + unassignedJobs.add(j); + toRemove--; + } + ruined.add(targetRoute); + } + } + + private List wrap(List activities) { + List wl = new ArrayList(); + for(TourActivity act : activities){ + wl.add(new JobActivityWrapper((TourActivity.JobActivity) act)); + } + return wl; + } + + private Map map(Collection vehicleRoutes) { + Map map = new HashMap(vrp.getJobs().size()); + for(VehicleRoute r : vehicleRoutes){ + for(Job j : r.getTourActivities().getJobs()){ + map.put(j,r); + } + } + return map; + } + + @Override + public String toString() { + return "[name=clusterRuin]"; + } + +}