mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
optimize neighboorhood calculation
This commit is contained in:
parent
cb08d19397
commit
a807024232
5 changed files with 320 additions and 55 deletions
|
|
@ -13,7 +13,8 @@ public class JobNeighborhoodsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public JobNeighborhoods createNeighborhoods(VehicleRoutingProblem vrp, JobDistance jobDistance, int capacity) {
|
public JobNeighborhoods createNeighborhoods(VehicleRoutingProblem vrp, JobDistance jobDistance, int capacity) {
|
||||||
return new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, capacity);
|
// return new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, capacity);
|
||||||
|
return new JobNeighborhoodsOptimized(vrp, jobDistance, capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ class JobNeighborhoodsImplWithCapRestriction implements JobNeighborhoods {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo) {
|
public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo) {
|
||||||
|
|
||||||
TreeSet<ReferencedJob> tree = distanceNodeTree.get(neighborTo.getId());
|
TreeSet<ReferencedJob> tree = distanceNodeTree.get(neighborTo.getId());
|
||||||
if (tree == null) return new Iterator<Job>() {
|
if (tree == null) return new Iterator<Job>() {
|
||||||
|
|
||||||
|
|
@ -73,10 +74,18 @@ class JobNeighborhoodsImplWithCapRestriction implements JobNeighborhoods {
|
||||||
|
|
||||||
private void calculateDistancesFromJob2Job() {
|
private void calculateDistancesFromJob2Job() {
|
||||||
logger.debug("preprocess distances between locations ...");
|
logger.debug("preprocess distances between locations ...");
|
||||||
|
//ToDo
|
||||||
|
/*
|
||||||
|
1 -> 2,3,4,5,6
|
||||||
|
2 -> 1,3,4,5,6
|
||||||
|
3
|
||||||
|
|
||||||
|
*/
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
int nuOfDistancesStored = 0;
|
int nuOfDistancesStored = 0;
|
||||||
for (Job i : vrp.getJobs().values()) {
|
for (Job i : vrp.getJobs().values()) {
|
||||||
|
// Collections.sort(list, );
|
||||||
TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
|
TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
|
||||||
new Comparator<ReferencedJob>() {
|
new Comparator<ReferencedJob>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.graphhopper.jsprit.core.algorithm.ruin;
|
||||||
|
|
||||||
|
import com.graphhopper.jsprit.core.algorithm.ruin.distance.JobDistance;
|
||||||
|
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
|
||||||
|
import com.graphhopper.jsprit.core.problem.job.Job;
|
||||||
|
import com.graphhopper.jsprit.core.util.StopWatch;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by schroeder on 07/01/15.
|
||||||
|
*/
|
||||||
|
class JobNeighborhoodsOptimized implements JobNeighborhoods {
|
||||||
|
|
||||||
|
static class ArrayIterator implements Iterator<Job> {
|
||||||
|
|
||||||
|
private final int noItems;
|
||||||
|
|
||||||
|
private final int[] itemArray;
|
||||||
|
|
||||||
|
private final Job[] jobs;
|
||||||
|
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
public ArrayIterator(int noItems, int[] itemArray, Job[] jobs) {
|
||||||
|
this.noItems = noItems;
|
||||||
|
this.itemArray = itemArray;
|
||||||
|
this.jobs = jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if(index < noItems && index < itemArray.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Job next() {
|
||||||
|
Job job = jobs[itemArray[index]];
|
||||||
|
index++;
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(JobNeighborhoodsImpl.class);
|
||||||
|
|
||||||
|
private VehicleRoutingProblem vrp;
|
||||||
|
|
||||||
|
private int[][] neighbors;
|
||||||
|
|
||||||
|
private Job[] jobs;
|
||||||
|
|
||||||
|
private JobDistance jobDistance;
|
||||||
|
|
||||||
|
private int capacity;
|
||||||
|
|
||||||
|
private double maxDistance = 0.;
|
||||||
|
|
||||||
|
public JobNeighborhoodsOptimized(VehicleRoutingProblem vrp, JobDistance jobDistance, int capacity) {
|
||||||
|
super();
|
||||||
|
this.vrp = vrp;
|
||||||
|
this.jobDistance = jobDistance;
|
||||||
|
this.capacity = capacity;
|
||||||
|
neighbors = new int[vrp.getJobs().size()][capacity];
|
||||||
|
jobs = new Job[vrp.getJobs().size()+1];
|
||||||
|
logger.debug("initialize {}", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Job> getNearestNeighborsIterator(int nNeighbors, Job neighborTo) {
|
||||||
|
int[] neighbors = this.neighbors[neighborTo.getIndex()-1];
|
||||||
|
return new ArrayIterator(nNeighbors,neighbors,jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialise() {
|
||||||
|
logger.debug("calculates distances from EACH job to EACH job --> n^2={} calculations, but 'only' {} are cached.", Math.pow(vrp.getJobs().values().size(), 2), (vrp.getJobs().values().size() * capacity));
|
||||||
|
if (capacity == 0) return;
|
||||||
|
calculateDistancesFromJob2Job();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getMaxDistance() {
|
||||||
|
return maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateDistancesFromJob2Job() {
|
||||||
|
logger.debug("pre-process distances between locations ...");
|
||||||
|
StopWatch stopWatch = new StopWatch();
|
||||||
|
stopWatch.start();
|
||||||
|
for (Job job_i : vrp.getJobs().values()) {
|
||||||
|
jobs[job_i.getIndex()] = job_i;
|
||||||
|
List<ReferencedJob> jobList = new ArrayList<ReferencedJob>(vrp.getJobs().size());
|
||||||
|
for (Job job_j : vrp.getJobs().values()) {
|
||||||
|
if (job_i == job_j) continue;
|
||||||
|
double distance = jobDistance.getDistance(job_i, job_j);
|
||||||
|
if (distance > maxDistance) maxDistance = distance;
|
||||||
|
ReferencedJob referencedJob = new ReferencedJob(job_j, distance);
|
||||||
|
jobList.add(referencedJob);
|
||||||
|
}
|
||||||
|
Collections.sort(jobList,getComparator());
|
||||||
|
int[] jobIndices = new int[capacity];
|
||||||
|
for(int index=0;index<capacity;index++){
|
||||||
|
jobIndices[index] = jobList.get(index).getJob().getIndex();
|
||||||
|
}
|
||||||
|
neighbors[job_i.getIndex()-1] = jobIndices;
|
||||||
|
}
|
||||||
|
stopWatch.stop();
|
||||||
|
logger.debug("pre-processing comp-time: {}", stopWatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Comparator<ReferencedJob> getComparator(){
|
||||||
|
return new Comparator<ReferencedJob>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ReferencedJob o1, ReferencedJob o2) {
|
||||||
|
if (o1.getDistance() < o2.getDistance()) {
|
||||||
|
return -1;
|
||||||
|
} else if (o1.getDistance() > o2.getDistance()){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[name=neighborhoodWithCapRestriction][capacity=" + capacity + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Stefan Schroeder - initial API and implementation
|
||||||
|
******************************************************************************/
|
||||||
|
package com.graphhopper.jsprit.core.algorithm.ruin;
|
||||||
|
|
||||||
|
import com.graphhopper.jsprit.core.algorithm.ruin.distance.EuclideanServiceDistance;
|
||||||
|
import com.graphhopper.jsprit.core.algorithm.ruin.distance.JobDistance;
|
||||||
|
import com.graphhopper.jsprit.core.problem.Location;
|
||||||
|
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
|
||||||
|
import com.graphhopper.jsprit.core.problem.job.Job;
|
||||||
|
import com.graphhopper.jsprit.core.problem.job.Service;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
public class JobNeighborhoodsOptimizedTest {
|
||||||
|
|
||||||
|
VehicleRoutingProblem vrp;
|
||||||
|
|
||||||
|
JobDistance jobDistance;
|
||||||
|
|
||||||
|
Service target;
|
||||||
|
Service s2;
|
||||||
|
Service s3;
|
||||||
|
Service s4;
|
||||||
|
Service s5;
|
||||||
|
Service s6;
|
||||||
|
Service s7;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void doBefore() {
|
||||||
|
VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
|
||||||
|
target = Service.Builder.newInstance("s1").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 5)).build();
|
||||||
|
s2 = Service.Builder.newInstance("s2").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 4)).build();
|
||||||
|
s3 = Service.Builder.newInstance("s3").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 3)).build();
|
||||||
|
s4 = Service.Builder.newInstance("s4").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 2)).build();
|
||||||
|
|
||||||
|
s5 = Service.Builder.newInstance("s5").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 6)).build();
|
||||||
|
s6 = Service.Builder.newInstance("s6").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 7)).build();
|
||||||
|
s7 = Service.Builder.newInstance("s7").addSizeDimension(0, 1).setLocation(Location.newInstance(0, 8)).build();
|
||||||
|
|
||||||
|
vrp = builder.addJob(target).addJob(s2).addJob(s3).addJob(s4).addJob(s5).addJob(s6).addJob(s7).build();
|
||||||
|
|
||||||
|
jobDistance = new EuclideanServiceDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingNeighborhoodOfTargetJob_nNeighborsShouldBeTwo() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,2);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(2, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertEquals(2, services.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingNeighborhoodOfTargetJob_s2ShouldBeNeighbor() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,2);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(2, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertTrue(services.contains(s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingNeighborhoodOfTargetJob_s4ShouldBeNeighbor() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,2);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(2, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertTrue(services.contains(s5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingNeighborhoodOfTargetJob_sizeShouldBe4() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,4);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(4, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertEquals(4, services.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingNeighborhoodOfTargetJob_neighborsShouldBeCorrect() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,4);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(4, s7);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
Assert.assertEquals(s6,services.get(0));
|
||||||
|
Assert.assertEquals(s5,services.get(1));
|
||||||
|
Assert.assertEquals(target,services.get(2));
|
||||||
|
Assert.assertEquals(s2,services.get(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestingMoreNeighborsThanExisting_itShouldReturnMaxNeighbors() {
|
||||||
|
JobNeighborhoodsOptimized jn = new JobNeighborhoodsOptimized(vrp,jobDistance,2);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(100, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertEquals(2, services.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<problem xmlns="http://www.w3schools.com"
|
<problem xmlns="http://www.w3schools.com"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com vrp_xml_schema.xsd">
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com vrp_xml_schema.xsd">
|
||||||
<problemType>
|
<problemType>
|
||||||
<fleetSize>INFINITE</fleetSize>
|
<fleetSize>FINITE</fleetSize>
|
||||||
</problemType>
|
</problemType>
|
||||||
<vehicles>
|
<vehicles>
|
||||||
<vehicle>
|
<vehicle>
|
||||||
|
|
@ -20,6 +20,21 @@
|
||||||
</timeSchedule>
|
</timeSchedule>
|
||||||
<returnToDepot>true</returnToDepot>
|
<returnToDepot>true</returnToDepot>
|
||||||
</vehicle>
|
</vehicle>
|
||||||
|
<vehicle>
|
||||||
|
<id>v2</id>
|
||||||
|
<typeId>vehType2</typeId>
|
||||||
|
<startLocation>
|
||||||
|
<id>loc</id>
|
||||||
|
</startLocation>
|
||||||
|
<endLocation>
|
||||||
|
<id>loc</id>
|
||||||
|
</endLocation>
|
||||||
|
<timeSchedule>
|
||||||
|
<start>0.0</start>
|
||||||
|
<end>1.7976931348623157E308</end>
|
||||||
|
</timeSchedule>
|
||||||
|
<returnToDepot>true</returnToDepot>
|
||||||
|
</vehicle>
|
||||||
</vehicles>
|
</vehicles>
|
||||||
<vehicleTypes>
|
<vehicleTypes>
|
||||||
<type>
|
<type>
|
||||||
|
|
@ -35,58 +50,18 @@
|
||||||
<wait>0.0</wait>
|
<wait>0.0</wait>
|
||||||
</costs>
|
</costs>
|
||||||
</type>
|
</type>
|
||||||
|
<type>
|
||||||
|
<id>vehType2</id>
|
||||||
|
<capacity-dimensions>
|
||||||
|
<dimension index="0">200</dimension>
|
||||||
|
</capacity-dimensions>
|
||||||
|
<costs>
|
||||||
|
<fixed>0.0</fixed>
|
||||||
|
<distance>1.0</distance>
|
||||||
|
<time>0.0</time>
|
||||||
|
<service>0.0</service>
|
||||||
|
<wait>0.0</wait>
|
||||||
|
</costs>
|
||||||
|
</type>
|
||||||
</vehicleTypes>
|
</vehicleTypes>
|
||||||
<services>
|
|
||||||
<service id="1" type="service">
|
|
||||||
<location>
|
|
||||||
<id>loc</id>
|
|
||||||
</location>
|
|
||||||
<capacity-dimensions>
|
|
||||||
<dimension index="0">1</dimension>
|
|
||||||
</capacity-dimensions>
|
|
||||||
<duration>2.0</duration>
|
|
||||||
<timeWindows>
|
|
||||||
<timeWindow>
|
|
||||||
<start>0.0</start>
|
|
||||||
<end>1.7976931348623157E308</end>
|
|
||||||
</timeWindow>
|
|
||||||
</timeWindows>
|
|
||||||
</service>
|
|
||||||
<service id="2" type="service">
|
|
||||||
<location>
|
|
||||||
<id>loc2</id>
|
|
||||||
</location>
|
|
||||||
<capacity-dimensions>
|
|
||||||
<dimension index="0">1</dimension>
|
|
||||||
</capacity-dimensions>
|
|
||||||
<duration>4.0</duration>
|
|
||||||
<timeWindows>
|
|
||||||
<timeWindow>
|
|
||||||
<start>0.0</start>
|
|
||||||
<end>1.7976931348623157E308</end>
|
|
||||||
</timeWindow>
|
|
||||||
</timeWindows>
|
|
||||||
</service>
|
|
||||||
</services>
|
|
||||||
<solutions>
|
|
||||||
<solution>
|
|
||||||
<cost>10.0</cost>
|
|
||||||
<routes>
|
|
||||||
<route>
|
|
||||||
<driverId>noDriver</driverId>
|
|
||||||
<vehicleId>v1</vehicleId>
|
|
||||||
<start>0.0</start>
|
|
||||||
<act type="service">
|
|
||||||
<serviceId>1</serviceId>
|
|
||||||
<arrTime>0.0</arrTime>
|
|
||||||
<endTime>0.0</endTime>
|
|
||||||
</act>
|
|
||||||
<end>0.0</end>
|
|
||||||
</route>
|
|
||||||
</routes>
|
|
||||||
<unassignedJobs>
|
|
||||||
<job id="2"/>
|
|
||||||
</unassignedJobs>
|
|
||||||
</solution>
|
|
||||||
</solutions>
|
|
||||||
</problem>
|
</problem>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue