Package jsprit.core.algorithm.ruin

Source Code of jsprit.core.algorithm.ruin.RuinRadial$ReferencedJob

/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
package jsprit.core.algorithm.ruin;

import jsprit.core.algorithm.ruin.distance.JobDistance;
import jsprit.core.algorithm.ruin.listener.RuinListener;
import jsprit.core.algorithm.ruin.listener.RuinListeners;
import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.util.RandomNumberGeneration;
import jsprit.core.util.StopWatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;


/**
*
* RuinStrategy that ruins the neighborhood of a randomly selected job. The size and the structure of the neighborhood is defined by
* the share of jobs to be removed and the distance between jobs (where distance not necessarily mean Euclidean distance but an arbitrary
* measure).
*
* @author stefan
*
*/
final class RuinRadial implements RuinStrategy {
 
  static interface JobNeighborhoods {
   
    public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo);
   
  }
 
  static class NeighborhoodIterator implements Iterator<Job>{

    private static Logger log = LogManager.getLogger(NeighborhoodIterator.class);
   
    private Iterator<ReferencedJob> jobIter;
   
    private int nJobs;
   
    private int jobCount = 0;
   
    public NeighborhoodIterator(Iterator<ReferencedJob> jobIter, int nJobs) {
      super();
      this.jobIter = jobIter;
      this.nJobs = nJobs;
    }

    @Override
    public boolean hasNext() {
      if(jobCount < nJobs){
        boolean hasNext = jobIter.hasNext();
        if(!hasNext) log.warn("more jobs are requested then iterator can iterate over. probably the number of neighbors memorized in JobNeighborhoods is too small");
        return hasNext;
      }
      return false;
    }

    @Override
    public Job next() {
      ReferencedJob next = jobIter.next();
      jobCount++;
      return next.getJob();
    }

    @Override
    public void remove() {
      jobIter.remove();
    }
   
  }
 
  static class JobNeighborhoodsImpl implements JobNeighborhoods {

    private static Logger logger = LogManager.getLogger(JobNeighborhoodsImpl.class);
   
    private VehicleRoutingProblem vrp;
   
    private Map<String, TreeSet<ReferencedJob>> distanceNodeTree = new HashMap<String, TreeSet<ReferencedJob>>();
   
    private JobDistance jobDistance;
   
    public JobNeighborhoodsImpl(VehicleRoutingProblem vrp, JobDistance jobDistance) {
      super();
      this.vrp = vrp;
      this.jobDistance = jobDistance;
      logger.info("intialise " + this);
    }
   
    public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo){
      TreeSet<ReferencedJob> tree = distanceNodeTree.get(neighborTo.getId());
      Iterator<ReferencedJob> descendingIterator = tree.iterator();
      return new NeighborhoodIterator(descendingIterator, nNeighbors);
    }
   
    public void initialise(){
      logger.info("calculates and memorizes distances from EACH job to EACH job --> n^2 calculations");
      calculateDistancesFromJob2Job();
    }
   
    private void calculateDistancesFromJob2Job() {
      logger.info("preprocess distances between locations ...");
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();
      int nuOfDistancesStored = 0;
      for (Job i : vrp.getJobs().values()) {
        TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
            new Comparator<ReferencedJob>() {
              @Override
              public int compare(ReferencedJob o1, ReferencedJob o2) {
                if (o1.getDistance() <= o2.getDistance()) {
                  return -1;
                } else {
                  return 1;
                }
              }
            });
        distanceNodeTree.put(i.getId(), treeSet);
        for (Job j : vrp.getJobs().values()) {
          if(i==j) continue;
          double distance = jobDistance.getDistance(i, j);
          ReferencedJob refNode = new ReferencedJob(j, distance);
          treeSet.add(refNode);
          nuOfDistancesStored++;
        }

      }
      stopWatch.stop();
      logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
          (distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
    }
   
  }

  static class JobNeighborhoodsImplWithCapRestriction implements JobNeighborhoods {

    private static Logger logger = LogManager.getLogger(JobNeighborhoodsImpl.class);
   
    private VehicleRoutingProblem vrp;
   
    private Map<String, TreeSet<ReferencedJob>> distanceNodeTree = new HashMap<String, TreeSet<ReferencedJob>>();
   
    private JobDistance jobDistance;
   
    private int capacity;
   
    public JobNeighborhoodsImplWithCapRestriction(VehicleRoutingProblem vrp, JobDistance jobDistance, int capacity) {
      super();
      this.vrp = vrp;
      this.jobDistance = jobDistance;
      this.capacity = capacity;
      logger.info("intialise " + this);
    }
   
    public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo){
      TreeSet<ReferencedJob> tree = distanceNodeTree.get(neighborTo.getId());
      Iterator<ReferencedJob> descendingIterator = tree.iterator();
      return new NeighborhoodIterator(descendingIterator, nNeighbors);
    }
   
    public void initialise(){
      logger.info("calculates distances from EACH job to EACH job --> n^2="+Math.pow(vrp.getJobs().values().size(), 2) + " calculations, but 'only' "+(vrp.getJobs().values().size()*capacity)+ " are cached.");
      if(capacity==0) return;
      calculateDistancesFromJob2Job();
    }
   
    private void calculateDistancesFromJob2Job() {
      logger.info("preprocess distances between locations ...");
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();
      int nuOfDistancesStored = 0;
      for (Job i : vrp.getJobs().values()) {
        TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
            new Comparator<ReferencedJob>() {
              @Override
              public int compare(ReferencedJob o1, ReferencedJob o2) {
                if (o1.getDistance() <= o2.getDistance()) {
                  return -1;
                } else {
                  return 1;
                }
              }
            });
        distanceNodeTree.put(i.getId(), treeSet);
        for (Job j : vrp.getJobs().values()) {
          if(i==j) continue;
          double distance = jobDistance.getDistance(i, j);
          ReferencedJob refNode = new ReferencedJob(j, distance);
          if(treeSet.size() < capacity){
            treeSet.add(refNode);
            nuOfDistancesStored++;
          }
          else{
            if(treeSet.last().getDistance() > distance){
              treeSet.pollLast();
              treeSet.add(refNode);
            }
          }
        }
        assert treeSet.size() <= capacity : "treeSet.size() is bigger than specified capacity";

      }
      stopWatch.stop();
      logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
          (distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
    }
   
    @Override
    public String toString() {
      return "[name=neighborhoodWithCapRestriction][capacity="+capacity+"]";
    }
   
  }
 
 
  static class ReferencedJob {
    private Job job;
    private double distance;

    public ReferencedJob(Job job, double distance) {
      super();
      this.job = job;
      this.distance = distance;
    }

    public Job getJob() {
      return job;
    }

    public double getDistance() {
      return distance;
    }
  }

  private Logger logger = LogManager.getLogger(RuinRadial.class);

  private VehicleRoutingProblem vrp;

  private double fractionOfAllNodes2beRuined;

  private Random random = RandomNumberGeneration.getRandom();

  private RuinListeners ruinListeners;
 
  private JobNeighborhoods jobNeighborhoods;
 
  public void setRandom(Random random) {
    this.random = random;
  }

  /**
   * Constructs RuinRadial.
   *
   * @param vrp
   * @param fraction2beRemoved i.e. the share of jobs to be removed (relative to the total number of jobs in vrp)
   * @param jobDistance i.e. a measure to define the distance between two jobs and whether they are located close or distant to eachother
   */
  public RuinRadial(VehicleRoutingProblem vrp, double fraction2beRemoved, JobDistance jobDistance) {
    super();
    this.vrp = vrp;
    this.fractionOfAllNodes2beRuined = fraction2beRemoved;
    ruinListeners = new RuinListeners();
    int nJobsToMemorize = (int) Math.ceil(vrp.getJobs().values().size()*fraction2beRemoved);
    JobNeighborhoodsImplWithCapRestriction jobNeighborhoodsImpl = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, nJobsToMemorize);
    jobNeighborhoodsImpl.initialise();
    jobNeighborhoods = jobNeighborhoodsImpl;
    logger.info("intialise " + this);
  }
 
  @Override
  public String toString() {
    return "[name=radialRuin][fraction="+fractionOfAllNodes2beRuined+"]";
  }

  /**
   * Ruins the collection of vehicleRoutes, i.e. removes a share of jobs. First, it selects a job randomly. Second, it identifies its neighborhood. And finally, it removes
   * the neighborhood plus the randomly selected job from the number of vehicleRoutes. All removed jobs are then returned as a collection.
   */
  @Override
  public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
    if(vehicleRoutes.isEmpty()){
      return Collections.emptyList();
    }
    int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
    if (nOfJobs2BeRemoved == 0) {
      return Collections.emptyList();
    }
    Job randomJob = pickRandomJob();
    Collection<Job> unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
    return unassignedJobs;
  }
 
  /**
   * Removes targetJob and its neighborhood and returns the removed jobs.
   */
  public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
    ruinListeners.ruinStarts(vehicleRoutes);
    List<Job> unassignedJobs = new ArrayList<Job>();
    int nNeighbors = nOfJobs2BeRemoved - 1;
    removeJob(targetJob,vehicleRoutes);
    unassignedJobs.add(targetJob);
    Iterator<Job> neighborhoodIterator =  jobNeighborhoods.getNearestNeighborsIterator(nNeighbors, targetJob);
    while(neighborhoodIterator.hasNext()){
      Job job = neighborhoodIterator.next();
      removeJob(job,vehicleRoutes);
      unassignedJobs.add(job);
    }
    ruinListeners.ruinEnds(vehicleRoutes, unassignedJobs);
    return unassignedJobs;
  }
 
  private void removeJob(Job job, Collection<VehicleRoute> vehicleRoutes) {
    boolean removed = false;
    for (VehicleRoute route : vehicleRoutes) {
      removed = route.getTourActivities().removeJob(job);;
      if (removed) {
        ruinListeners.removed(job,route);
        break;
      }
    }
  }

  private Job pickRandomJob() {
    int totNuOfJobs = vrp.getJobs().values().size();
    int randomIndex = random.nextInt(totNuOfJobs);
    Job job = new ArrayList<Job>(vrp.getJobs().values()).get(randomIndex);
    return job;
  }

  private int getNuOfJobs2BeRemoved() {
    return (int) Math.ceil(vrp.getJobs().values().size()
        * fractionOfAllNodes2beRuined);
  }

  @Override
  public void addListener(RuinListener ruinListener) {
    ruinListeners.addListener(ruinListener);
  }

  @Override
  public void removeListener(RuinListener ruinListener) {
    ruinListeners.removeListener(ruinListener);
  }

  @Override
  public Collection<RuinListener> getListeners() {
    return ruinListeners.getListeners();
  }


}
TOP

Related Classes of jsprit.core.algorithm.ruin.RuinRadial$ReferencedJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.