mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
Merge branch 'prepareV010' into PickupMergeRelaxAPI
Conflicts: jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java
This commit is contained in:
commit
d1dac2d622
21 changed files with 11614 additions and 10997 deletions
|
|
@ -37,6 +37,7 @@ import algorithms.VehicleRoutingAlgorithms;
|
||||||
import basics.VehicleRoutingAlgorithm;
|
import basics.VehicleRoutingAlgorithm;
|
||||||
import basics.VehicleRoutingProblem;
|
import basics.VehicleRoutingProblem;
|
||||||
import basics.VehicleRoutingProblemSolution;
|
import basics.VehicleRoutingProblemSolution;
|
||||||
|
import basics.algo.VehicleRoutingAlgorithmFactory;
|
||||||
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
|
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
|
||||||
|
|
||||||
public class ConcurrentBenchmarker {
|
public class ConcurrentBenchmarker {
|
||||||
|
|
@ -47,7 +48,7 @@ public class ConcurrentBenchmarker {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String algorithmConfig;
|
private String algorithmConfig = null;
|
||||||
|
|
||||||
private List<BenchmarkInstance> benchmarkInstances = new ArrayList<BenchmarkInstance>();
|
private List<BenchmarkInstance> benchmarkInstances = new ArrayList<BenchmarkInstance>();
|
||||||
|
|
||||||
|
|
@ -66,6 +67,8 @@ public class ConcurrentBenchmarker {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private VehicleRoutingAlgorithmFactory algorithmFactory;
|
||||||
|
|
||||||
public void setCost(Cost cost){ this.cost = cost; }
|
public void setCost(Cost cost){ this.cost = cost; }
|
||||||
|
|
||||||
public ConcurrentBenchmarker(String algorithmConfig) {
|
public ConcurrentBenchmarker(String algorithmConfig) {
|
||||||
|
|
@ -74,6 +77,10 @@ public class ConcurrentBenchmarker {
|
||||||
Logger.getRootLogger().setLevel(Level.ERROR);
|
Logger.getRootLogger().setLevel(Level.ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConcurrentBenchmarker(VehicleRoutingAlgorithmFactory algorithmFactory){
|
||||||
|
this.algorithmFactory = algorithmFactory;
|
||||||
|
}
|
||||||
|
|
||||||
public void addBenchmarkWriter(BenchmarkWriter writer){
|
public void addBenchmarkWriter(BenchmarkWriter writer){
|
||||||
writers.add(writer);
|
writers.add(writer);
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +101,12 @@ public class ConcurrentBenchmarker {
|
||||||
benchmarkInstances.add(new BenchmarkInstance(name,problem,bestKnownResult,bestKnownVehicles));
|
benchmarkInstances.add(new BenchmarkInstance(name,problem,bestKnownResult,bestKnownVehicles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets nuOfRuns with same algorithm on same instance.
|
||||||
|
* <p>Default is 1
|
||||||
|
*
|
||||||
|
* @param runs
|
||||||
|
*/
|
||||||
public void setNuOfRuns(int runs){
|
public void setNuOfRuns(int runs){
|
||||||
this.runs = runs;
|
this.runs = runs;
|
||||||
}
|
}
|
||||||
|
|
@ -142,11 +155,11 @@ public class ConcurrentBenchmarker {
|
||||||
double[] times = new double[runs];
|
double[] times = new double[runs];
|
||||||
|
|
||||||
for(int run=0;run<runs;run++){
|
for(int run=0;run<runs;run++){
|
||||||
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(p.vrp, algorithmConfig);
|
VehicleRoutingAlgorithm vra = createAlgorithm(p);
|
||||||
StopWatch stopwatch = new StopWatch();
|
StopWatch stopwatch = new StopWatch();
|
||||||
vra.getAlgorithmListeners().addListener(stopwatch,Priority.HIGH);
|
vra.getAlgorithmListeners().addListener(stopwatch,Priority.HIGH);
|
||||||
Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
|
Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
|
||||||
VehicleRoutingProblemSolution best = Solutions.getBest(solutions);
|
VehicleRoutingProblemSolution best = Solutions.bestOf(solutions);
|
||||||
vehicles[run] = best.getRoutes().size();
|
vehicles[run] = best.getRoutes().size();
|
||||||
results[run] = cost.getCost(best);
|
results[run] = cost.getCost(best);
|
||||||
times[run] = stopwatch.getCompTimeInSeconds();
|
times[run] = stopwatch.getCompTimeInSeconds();
|
||||||
|
|
@ -155,6 +168,16 @@ public class ConcurrentBenchmarker {
|
||||||
return new BenchmarkResult(p, runs, results, times, vehicles);
|
return new BenchmarkResult(p, runs, results, times, vehicles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VehicleRoutingAlgorithm createAlgorithm(BenchmarkInstance p) {
|
||||||
|
if(algorithmConfig != null){
|
||||||
|
return VehicleRoutingAlgorithms.readAndCreateAlgorithm(p.vrp, algorithmConfig);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return algorithmFactory.createAlgorithm(p.vrp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void print(Collection<BenchmarkResult> results) {
|
private void print(Collection<BenchmarkResult> results) {
|
||||||
double sumTime=0.0;
|
double sumTime=0.0;
|
||||||
double sumResult=0.0;
|
double sumResult=0.0;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import basics.algo.InsertionListener;
|
||||||
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
|
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
|
||||||
import basics.route.VehicleFleetManager;
|
import basics.route.VehicleFleetManager;
|
||||||
|
|
||||||
public class BestInsertionBuilder implements InsertionStrategyBuilder{
|
public class BestInsertionBuilder {
|
||||||
|
|
||||||
private VehicleRoutingProblem vrp;
|
private VehicleRoutingProblem vrp;
|
||||||
|
|
||||||
|
|
@ -35,11 +35,11 @@ public class BestInsertionBuilder implements InsertionStrategyBuilder{
|
||||||
|
|
||||||
private int nuOfThreads;
|
private int nuOfThreads;
|
||||||
|
|
||||||
public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager) {
|
public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) {
|
||||||
super();
|
super();
|
||||||
this.vrp = vrp;
|
this.vrp = vrp;
|
||||||
this.stateManager = stateManager;
|
this.stateManager = stateManager;
|
||||||
this.constraintManager = new ConstraintManager(vrp,stateManager);
|
this.constraintManager = constraintManager;
|
||||||
this.fleetManager = vehicleFleetManager;
|
this.fleetManager = vehicleFleetManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ public class BestInsertionBuilder implements InsertionStrategyBuilder{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public InsertionStrategy build() {
|
public InsertionStrategy build() {
|
||||||
List<InsertionListener> iListeners = new ArrayList<InsertionListener>();
|
List<InsertionListener> iListeners = new ArrayList<InsertionListener>();
|
||||||
List<PrioritizedVRAListener> algorithmListeners = new ArrayList<PrioritizedVRAListener>();
|
List<PrioritizedVRAListener> algorithmListeners = new ArrayList<PrioritizedVRAListener>();
|
||||||
|
|
@ -99,14 +99,12 @@ public class BestInsertionBuilder implements InsertionStrategyBuilder{
|
||||||
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
bestInsertion = new BestInsertionConc(jobInsertions,executor,nuOfThreads);
|
|
||||||
|
bestInsertion = new BestInsertionConcurrent(jobInsertions,executor,nuOfThreads);
|
||||||
|
|
||||||
}
|
}
|
||||||
for(InsertionListener l : iListeners) bestInsertion.addListener(l);
|
for(InsertionListener l : iListeners) bestInsertion.addListener(l);
|
||||||
return bestInsertion;
|
return bestInsertion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConstraintManager(ConstraintManager constraintManager) {
|
|
||||||
this.constraintManager = constraintManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ import basics.route.VehicleRoute;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
final class BestInsertionConc implements InsertionStrategy{
|
final class BestInsertionConcurrent implements InsertionStrategy{
|
||||||
|
|
||||||
static class Batch {
|
static class Batch {
|
||||||
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
|
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
|
||||||
|
|
@ -74,7 +74,7 @@ final class BestInsertionConc implements InsertionStrategy{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(BestInsertionConc.class);
|
private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class);
|
||||||
|
|
||||||
private Random random = RandomNumberGeneration.getRandom();
|
private Random random = RandomNumberGeneration.getRandom();
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ final class BestInsertionConc implements InsertionStrategy{
|
||||||
this.random = random;
|
this.random = random;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BestInsertionConc(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches) {
|
public BestInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches) {
|
||||||
super();
|
super();
|
||||||
this.insertionsListeners = new InsertionListeners();
|
this.insertionsListeners = new InsertionListeners();
|
||||||
this.executor = executorService;
|
this.executor = executorService;
|
||||||
|
|
@ -27,8 +27,10 @@ import basics.route.Driver;
|
||||||
import basics.route.Vehicle;
|
import basics.route.Vehicle;
|
||||||
import basics.route.VehicleRoute;
|
import basics.route.VehicleRoute;
|
||||||
|
|
||||||
|
|
||||||
class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsCalculator{
|
class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsCalculator{
|
||||||
|
|
||||||
|
|
||||||
private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class);
|
private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class);
|
||||||
|
|
||||||
private JobInsertionCostsCalculator jic;
|
private JobInsertionCostsCalculator jic;
|
||||||
|
|
@ -67,9 +69,13 @@ class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsC
|
||||||
|
|
||||||
for(int i=0;i<nOfDepartureTimes;i++){
|
for(int i=0;i<nOfDepartureTimes;i++){
|
||||||
double neighborStartTime_earlier = currentStart - (i+1)*timeSlice;
|
double neighborStartTime_earlier = currentStart - (i+1)*timeSlice;
|
||||||
if(neighborStartTime_earlier > earliestDeparture) vehicleDepartureTimes.add(neighborStartTime_earlier);
|
// if(neighborStartTime_earlier > earliestDeparture) {
|
||||||
|
vehicleDepartureTimes.add(neighborStartTime_earlier);
|
||||||
|
// }
|
||||||
double neighborStartTime_later = currentStart + (i+1)*timeSlice;
|
double neighborStartTime_later = currentStart + (i+1)*timeSlice;
|
||||||
if(neighborStartTime_later < latestEnd) vehicleDepartureTimes.add(neighborStartTime_later);
|
// if(neighborStartTime_later < latestEnd) {
|
||||||
|
vehicleDepartureTimes.add(neighborStartTime_later);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
InsertionData bestIData = null;
|
InsertionData bestIData = null;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ import basics.route.VehicleFleetManager;
|
||||||
import basics.route.TourActivity.JobActivity;
|
import basics.route.TourActivity.JobActivity;
|
||||||
import basics.route.VehicleRoute;
|
import basics.route.VehicleRoute;
|
||||||
|
|
||||||
final class Gendreau implements SearchStrategyModule{
|
public final class Gendreau implements SearchStrategyModule{
|
||||||
|
|
||||||
private final static Logger log = Logger.getLogger(Gendreau.class);
|
private final static Logger log = Logger.getLogger(Gendreau.class);
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ final class Gendreau implements SearchStrategyModule{
|
||||||
this.shareOfJobsToRuin = shareOfJobsToRuin;
|
this.shareOfJobsToRuin = shareOfJobsToRuin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gendreau(VehicleRoutingProblem vrp, RuinStrategy ruin, InsertionStrategy insertionStrategy) {
|
public Gendreau(VehicleRoutingProblem vrp, RuinStrategy ruin, InsertionStrategy insertionStrategy, VehicleFleetManager vehicleFleetManager) {
|
||||||
super();
|
super();
|
||||||
InsertionListeners insertionListeners = new InsertionListeners();
|
InsertionListeners insertionListeners = new InsertionListeners();
|
||||||
insertionListeners.addAllListeners(insertionStrategy.getListeners());
|
insertionListeners.addAllListeners(insertionStrategy.getListeners());
|
||||||
|
|
@ -72,6 +72,7 @@ final class Gendreau implements SearchStrategyModule{
|
||||||
this.ruin = ruin;
|
this.ruin = ruin;
|
||||||
this.vrp = vrp;
|
this.vrp = vrp;
|
||||||
this.insertionStrategy = insertionStrategy;
|
this.insertionStrategy = insertionStrategy;
|
||||||
|
this.fleetManager = vehicleFleetManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -88,10 +89,10 @@ final class Gendreau implements SearchStrategyModule{
|
||||||
this.nOfIterations = nOfIterations;
|
this.nOfIterations = nOfIterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFleetManager(VehicleFleetManager vehicleFleetManager) {
|
// public void setFleetManager(VehicleFleetManager vehicleFleetManager) {
|
||||||
this.fleetManager = vehicleFleetManager;
|
// this.fleetManager = vehicleFleetManager;
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
|
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
|
||||||
|
|
@ -119,14 +120,14 @@ final class Gendreau implements SearchStrategyModule{
|
||||||
VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute();
|
VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute();
|
||||||
copiedRoutes.add(emptyRoute1);
|
copiedRoutes.add(emptyRoute1);
|
||||||
insertionStrategy.insertJobs(Arrays.asList(emptyRoute1), Arrays.asList(targetJob));
|
insertionStrategy.insertJobs(Arrays.asList(emptyRoute1), Arrays.asList(targetJob));
|
||||||
// routeAlgorithm.insertJob(targetJob, routeAlgorithm.calculateBestInsertion(emptyRoute1, targetJob, Double.MAX_VALUE), emptyRoute1);
|
|
||||||
unassignedJobs.remove(targetJob);
|
unassignedJobs.remove(targetJob);
|
||||||
|
|
||||||
VehicleRoute emptyRoute2 = VehicleRoute.emptyRoute();
|
VehicleRoute emptyRoute2 = VehicleRoute.emptyRoute();
|
||||||
copiedRoutes.add(emptyRoute2);
|
copiedRoutes.add(emptyRoute2);
|
||||||
Job job2 = jobsInRoute.get(1);
|
Job job2 = jobsInRoute.get(1);
|
||||||
insertionStrategy.insertJobs(Arrays.asList(emptyRoute2), Arrays.asList(job2));
|
insertionStrategy.insertJobs(Arrays.asList(emptyRoute2), Arrays.asList(job2));
|
||||||
// routeAlgorithm.insertJob(job2, routeAlgorithm.calculateBestInsertion(emptyRoute2, job2, Double.MAX_VALUE), emptyRoute2);
|
|
||||||
unassignedJobs.remove(job2);
|
unassignedJobs.remove(job2);
|
||||||
|
|
||||||
insertionStrategy.insertJobs(copiedRoutes, unassignedJobs);
|
insertionStrategy.insertJobs(copiedRoutes, unassignedJobs);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package algorithms;
|
|
||||||
|
|
||||||
public interface InsertionStrategyBuilder {
|
|
||||||
|
|
||||||
public InsertionStrategy build();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -18,7 +18,7 @@ package algorithms;
|
||||||
|
|
||||||
import basics.VehicleRoutingProblem;
|
import basics.VehicleRoutingProblem;
|
||||||
|
|
||||||
interface InsertionStrategyFactory {
|
public interface InsertionStrategyFactory {
|
||||||
|
|
||||||
public InsertionStrategy createStrategy(VehicleRoutingProblem vrp);
|
public InsertionStrategy createStrategy(VehicleRoutingProblem vrp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,194 @@ import basics.route.VehicleRoute;
|
||||||
*/
|
*/
|
||||||
final class RuinRadial implements RuinStrategy {
|
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 = Logger.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 = Logger.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 = Logger.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.");
|
||||||
|
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 {
|
static class ReferencedJob {
|
||||||
private Job job;
|
private Job job;
|
||||||
private double distance;
|
private double distance;
|
||||||
|
|
@ -74,14 +262,12 @@ final class RuinRadial implements RuinStrategy {
|
||||||
|
|
||||||
private double fractionOfAllNodes2beRuined;
|
private double fractionOfAllNodes2beRuined;
|
||||||
|
|
||||||
private Map<String, TreeSet<ReferencedJob>> distanceNodeTree = new HashMap<String, TreeSet<ReferencedJob>>();
|
|
||||||
|
|
||||||
private Random random = RandomNumberGeneration.getRandom();
|
private Random random = RandomNumberGeneration.getRandom();
|
||||||
|
|
||||||
private JobDistance jobDistance;
|
|
||||||
|
|
||||||
private RuinListeners ruinListeners;
|
private RuinListeners ruinListeners;
|
||||||
|
|
||||||
|
private JobNeighborhoods jobNeighborhoods;
|
||||||
|
|
||||||
public void setRandom(Random random) {
|
public void setRandom(Random random) {
|
||||||
this.random = random;
|
this.random = random;
|
||||||
}
|
}
|
||||||
|
|
@ -96,41 +282,46 @@ final class RuinRadial implements RuinStrategy {
|
||||||
public RuinRadial(VehicleRoutingProblem vrp, double fraction2beRemoved, JobDistance jobDistance) {
|
public RuinRadial(VehicleRoutingProblem vrp, double fraction2beRemoved, JobDistance jobDistance) {
|
||||||
super();
|
super();
|
||||||
this.vrp = vrp;
|
this.vrp = vrp;
|
||||||
this.jobDistance = jobDistance;
|
|
||||||
this.fractionOfAllNodes2beRuined = fraction2beRemoved;
|
this.fractionOfAllNodes2beRuined = fraction2beRemoved;
|
||||||
ruinListeners = new RuinListeners();
|
ruinListeners = new RuinListeners();
|
||||||
calculateDistancesFromJob2Job();
|
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);
|
logger.info("intialise " + this);
|
||||||
}
|
//<<<<<<< HEAD
|
||||||
|
// }
|
||||||
private void calculateDistancesFromJob2Job() {
|
//
|
||||||
logger.info("preprocess distances between locations ...");
|
// private void calculateDistancesFromJob2Job() {
|
||||||
StopWatch stopWatch = new StopWatch();
|
// logger.info("preprocess distances between locations ...");
|
||||||
stopWatch.start();
|
// StopWatch stopWatch = new StopWatch();
|
||||||
int nuOfDistancesStored = 0;
|
// stopWatch.start();
|
||||||
for (Job i : vrp.getJobs().values()) {
|
// int nuOfDistancesStored = 0;
|
||||||
TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
|
// for (Job i : vrp.getJobs().values()) {
|
||||||
new Comparator<ReferencedJob>() {
|
// TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
|
||||||
@Override
|
// new Comparator<ReferencedJob>() {
|
||||||
public int compare(ReferencedJob o1, ReferencedJob o2) {
|
// @Override
|
||||||
if (o1.getDistance() <= o2.getDistance()) {
|
// public int compare(ReferencedJob o1, ReferencedJob o2) {
|
||||||
return 1;
|
// if (o1.getDistance() <= o2.getDistance()) {
|
||||||
} else {
|
// return 1;
|
||||||
return -1;
|
// } else {
|
||||||
}
|
// return -1;
|
||||||
}
|
// }
|
||||||
});
|
// }
|
||||||
distanceNodeTree.put(i.getId(), treeSet);
|
// });
|
||||||
for (Job j : vrp.getJobs().values()) {
|
// distanceNodeTree.put(i.getId(), treeSet);
|
||||||
double distance = jobDistance.getDistance(i, j);
|
// for (Job j : vrp.getJobs().values()) {
|
||||||
ReferencedJob refNode = new ReferencedJob(j, distance);
|
// double distance = jobDistance.getDistance(i, j);
|
||||||
treeSet.add(refNode);
|
// ReferencedJob refNode = new ReferencedJob(j, distance);
|
||||||
nuOfDistancesStored++;
|
// treeSet.add(refNode);
|
||||||
}
|
// nuOfDistancesStored++;
|
||||||
}
|
// }
|
||||||
stopWatch.stop();
|
// }
|
||||||
logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
|
// stopWatch.stop();
|
||||||
(distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
|
// logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
|
||||||
|
// (distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
|
||||||
|
//=======
|
||||||
|
//>>>>>>> refs/heads/master
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -145,11 +336,11 @@ final class RuinRadial implements RuinStrategy {
|
||||||
@Override
|
@Override
|
||||||
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
|
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
|
||||||
if(vehicleRoutes.isEmpty()){
|
if(vehicleRoutes.isEmpty()){
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
|
int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
|
||||||
if (nOfJobs2BeRemoved == 0) {
|
if (nOfJobs2BeRemoved == 0) {
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
Job randomJob = pickRandomJob();
|
Job randomJob = pickRandomJob();
|
||||||
Collection<Job> unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
|
Collection<Job> unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
|
||||||
|
|
@ -162,14 +353,20 @@ final class RuinRadial implements RuinStrategy {
|
||||||
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
|
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
|
||||||
ruinListeners.ruinStarts(vehicleRoutes);
|
ruinListeners.ruinStarts(vehicleRoutes);
|
||||||
List<Job> unassignedJobs = new ArrayList<Job>();
|
List<Job> unassignedJobs = new ArrayList<Job>();
|
||||||
TreeSet<ReferencedJob> tree = distanceNodeTree.get(targetJob.getId());
|
int nNeighbors = nOfJobs2BeRemoved - 1;
|
||||||
Iterator<ReferencedJob> descendingIterator = tree.descendingIterator();
|
removeJob(targetJob,vehicleRoutes);
|
||||||
int counter = 0;
|
unassignedJobs.add(targetJob);
|
||||||
while (descendingIterator.hasNext() && counter < nOfJobs2BeRemoved) {
|
Iterator<Job> neighborhoodIterator = jobNeighborhoods.getNearestNeighborsIterator(nNeighbors, targetJob);
|
||||||
ReferencedJob refJob = descendingIterator.next();
|
while(neighborhoodIterator.hasNext()){
|
||||||
Job job = refJob.getJob();
|
Job job = neighborhoodIterator.next();
|
||||||
|
removeJob(job,vehicleRoutes);
|
||||||
unassignedJobs.add(job);
|
unassignedJobs.add(job);
|
||||||
counter++;
|
}
|
||||||
|
ruinListeners.ruinEnds(vehicleRoutes, unassignedJobs);
|
||||||
|
return unassignedJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeJob(Job job, Collection<VehicleRoute> vehicleRoutes) {
|
||||||
boolean removed = false;
|
boolean removed = false;
|
||||||
for (VehicleRoute route : vehicleRoutes) {
|
for (VehicleRoute route : vehicleRoutes) {
|
||||||
removed = route.getTourActivities().removeJob(job);;
|
removed = route.getTourActivities().removeJob(job);;
|
||||||
|
|
@ -179,9 +376,6 @@ final class RuinRadial implements RuinStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ruinListeners.ruinEnds(vehicleRoutes, unassignedJobs);
|
|
||||||
return unassignedJobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Job pickRandomJob() {
|
private Job pickRandomJob() {
|
||||||
int totNuOfJobs = vrp.getJobs().values().size();
|
int totNuOfJobs = vrp.getJobs().values().size();
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ public class VehicleRoutingAlgorithmFactoryImpl implements VehicleRoutingAlgorit
|
||||||
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp) {
|
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp) {
|
||||||
this.stateManager.addActivityVisitor(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), this.stateManager));
|
this.stateManager.addActivityVisitor(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), this.stateManager));
|
||||||
this.stateManager.addActivityVisitor(new UpdateMaxLoad(this.stateManager));
|
this.stateManager.addActivityVisitor(new UpdateMaxLoad(this.stateManager));
|
||||||
|
this.stateManager.addActivityVisitor(new UpdateActivityTimes(vrp.getTransportCosts()));
|
||||||
VehicleRoutingAlgorithm algorithm = new VehicleRoutingAlgorithm(vrp, searchStrategyManager);
|
VehicleRoutingAlgorithm algorithm = new VehicleRoutingAlgorithm(vrp, searchStrategyManager);
|
||||||
algorithm.getAlgorithmListeners().addListener(stateManager);
|
algorithm.getAlgorithmListeners().addListener(stateManager);
|
||||||
algorithm.getSearchStrategyManager().addSearchStrategyModuleListener(stateManager);
|
algorithm.getSearchStrategyManager().addSearchStrategyModuleListener(stateManager);
|
||||||
|
|
|
||||||
|
|
@ -814,10 +814,9 @@ public class VehicleRoutingAlgorithms {
|
||||||
insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, routeStates, prioListeners, executorService, nuOfThreads, constraintManager);
|
insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, routeStates, prioListeners, executorService, nuOfThreads, constraintManager);
|
||||||
algorithmListeners.addAll(prioListeners);
|
algorithmListeners.addAll(prioListeners);
|
||||||
}
|
}
|
||||||
Gendreau gendreau = new Gendreau(vrp, ruin, insertion);
|
Gendreau gendreau = new Gendreau(vrp, ruin, insertion, vehicleFleetManager);
|
||||||
gendreau.setShareOfJobsToRuin(share);
|
gendreau.setShareOfJobsToRuin(share);
|
||||||
gendreau.setNuOfIterations(iterations);
|
gendreau.setNuOfIterations(iterations);
|
||||||
gendreau.setFleetManager(vehicleFleetManager);
|
|
||||||
definedClasses.put(strategyModuleKey, gendreau);
|
definedClasses.put(strategyModuleKey, gendreau);
|
||||||
return gendreau;
|
return gendreau;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,15 @@ import basics.VehicleRoutingProblem;
|
||||||
import basics.VehicleRoutingProblemSolution;
|
import basics.VehicleRoutingProblemSolution;
|
||||||
import basics.algo.SearchStrategy.DiscoveredSolution;
|
import basics.algo.SearchStrategy.DiscoveredSolution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks algorithm prematurely based on specified time.
|
||||||
|
*
|
||||||
|
* <p>Note, TimeBreaker must be registered as AlgorithmListener <br>
|
||||||
|
* <code>agorithm.getAlgorithmListeners().addListener(this);</code>
|
||||||
|
*
|
||||||
|
* @author stefan
|
||||||
|
*
|
||||||
|
*/
|
||||||
public class TimeBreaker implements PrematureAlgorithmBreaker, AlgorithmStartsListener{
|
public class TimeBreaker implements PrematureAlgorithmBreaker, AlgorithmStartsListener{
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(TimeBreaker.class);
|
private static Logger logger = Logger.getLogger(TimeBreaker.class);
|
||||||
|
|
@ -33,9 +42,18 @@ public class TimeBreaker implements PrematureAlgorithmBreaker, AlgorithmStartsLi
|
||||||
|
|
||||||
private double startTime;
|
private double startTime;
|
||||||
|
|
||||||
public TimeBreaker(double time) {
|
/**
|
||||||
|
* Constructs TimeBreaker that breaks algorithm prematurely based on specified time.
|
||||||
|
*
|
||||||
|
* <p>Note, TimeBreaker must be registered as AlgorithmListener <br>
|
||||||
|
* <code>agorithm.getAlgorithmListeners().addListener(this);</code>
|
||||||
|
*
|
||||||
|
* @author stefan
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public TimeBreaker(double time_in_seconds) {
|
||||||
super();
|
super();
|
||||||
this.timeThreshold = time;
|
this.timeThreshold = time_in_seconds;
|
||||||
logger.info("initialise " + this);
|
logger.info("initialise " + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,16 @@ import basics.VehicleRoutingProblem;
|
||||||
import basics.VehicleRoutingProblemSolution;
|
import basics.VehicleRoutingProblemSolution;
|
||||||
import basics.algo.SearchStrategy.DiscoveredSolution;
|
import basics.algo.SearchStrategy.DiscoveredSolution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks algorithm prematurely based on variationCoefficient.
|
||||||
|
*
|
||||||
|
* <p>Note that this must be registered in algorithm<br>
|
||||||
|
* <code>algorithm.getAlgorithmListeners().addListener(this);</code>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author stefan
|
||||||
|
*
|
||||||
|
*/
|
||||||
public class VariationCoefficientBreaker implements PrematureAlgorithmBreaker, IterationStartsListener, AlgorithmStartsListener, IterationEndsListener{
|
public class VariationCoefficientBreaker implements PrematureAlgorithmBreaker, IterationStartsListener, AlgorithmStartsListener, IterationEndsListener{
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(VariationCoefficientBreaker.class);
|
private static Logger logger = Logger.getLogger(VariationCoefficientBreaker.class);
|
||||||
|
|
@ -42,6 +52,16 @@ public class VariationCoefficientBreaker implements PrematureAlgorithmBreaker, I
|
||||||
|
|
||||||
private VehicleRoutingProblemSolution lastAccepted = null;
|
private VehicleRoutingProblemSolution lastAccepted = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks algorithm prematurely based on variationCoefficient.
|
||||||
|
*
|
||||||
|
* <p>Note that this must be registered in algorithm<br>
|
||||||
|
* <code>algorithm.getAlgorithmListeners().addListener(this);</code>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author stefan
|
||||||
|
*
|
||||||
|
*/
|
||||||
public VariationCoefficientBreaker(int nuOfIterations, double variationCoefficientThreshold) {
|
public VariationCoefficientBreaker(int nuOfIterations, double variationCoefficientThreshold) {
|
||||||
super();
|
super();
|
||||||
this.nuOfIterations = nuOfIterations;
|
this.nuOfIterations = nuOfIterations;
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,5 @@ public interface VehicleRoutingActivityCosts {
|
||||||
*/
|
*/
|
||||||
public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle);
|
public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle);
|
||||||
|
|
||||||
// public Parameter getParameter(TourActivity tourAct, Vehicle vehicle, Driver driver);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ import basics.VehicleRoutingProblemSolution;
|
||||||
|
|
||||||
public class Solutions {
|
public class Solutions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @deprecated use bestOf instead.
|
||||||
|
* @param solutions
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static VehicleRoutingProblemSolution getBest(Collection<VehicleRoutingProblemSolution> solutions){
|
public static VehicleRoutingProblemSolution getBest(Collection<VehicleRoutingProblemSolution> solutions){
|
||||||
VehicleRoutingProblemSolution best = null;
|
VehicleRoutingProblemSolution best = null;
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,7 @@ public class BuildPDVRPWithShipmentsAlgoFromScratchTest {
|
||||||
int nuOfThreads = 10;
|
int nuOfThreads = 10;
|
||||||
executorService = Executors.newFixedThreadPool(nuOfThreads);
|
executorService = Executors.newFixedThreadPool(nuOfThreads);
|
||||||
|
|
||||||
BestInsertionBuilder bestIBuilder = new BestInsertionBuilder(vrp, fleetManager, stateManager);
|
BestInsertionBuilder bestIBuilder = new BestInsertionBuilder(vrp, fleetManager, stateManager,constraintManager);
|
||||||
bestIBuilder.setConstraintManager(constraintManager);
|
|
||||||
bestIBuilder.setConcurrentMode(executorService, nuOfThreads);
|
bestIBuilder.setConcurrentMode(executorService, nuOfThreads);
|
||||||
InsertionStrategy bestInsertion = bestIBuilder.build();
|
InsertionStrategy bestInsertion = bestIBuilder.build();
|
||||||
|
|
||||||
|
|
@ -187,7 +186,7 @@ public class BuildPDVRPWithShipmentsAlgoFromScratchTest {
|
||||||
// System.out.println("ini: costs="+iniSolution.getCost()+";#routes="+iniSolution.getRoutes().size());
|
// System.out.println("ini: costs="+iniSolution.getCost()+";#routes="+iniSolution.getRoutes().size());
|
||||||
vra.addInitialSolution(iniSolution);
|
vra.addInitialSolution(iniSolution);
|
||||||
|
|
||||||
vra.setNuOfIterations(100);
|
vra.setNuOfIterations(10);
|
||||||
// vra.setPrematureBreak(500);
|
// vra.setPrematureBreak(500);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,8 +201,7 @@ public class GendreauPostOptTest {
|
||||||
InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc);
|
InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc);
|
||||||
insertionStrategy.addListener(stateUpdater);
|
insertionStrategy.addListener(stateUpdater);
|
||||||
insertionStrategy.addListener(new VehicleSwitched(fleetManager));
|
insertionStrategy.addListener(new VehicleSwitched(fleetManager));
|
||||||
Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy);
|
Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy, fleetManager);
|
||||||
postOpt.setFleetManager(fleetManager);
|
|
||||||
|
|
||||||
VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol);
|
VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol);
|
||||||
newSolution.setCost(getCosts(newSolution,states));
|
newSolution.setCost(getCosts(newSolution,states));
|
||||||
|
|
@ -261,10 +260,10 @@ public class GendreauPostOptTest {
|
||||||
InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc);
|
InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc);
|
||||||
insertionStrategy.addListener(stateUpdater);
|
insertionStrategy.addListener(stateUpdater);
|
||||||
insertionStrategy.addListener(new VehicleSwitched(fleetManager));
|
insertionStrategy.addListener(new VehicleSwitched(fleetManager));
|
||||||
Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy);
|
Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy, fleetManager);
|
||||||
postOpt.setShareOfJobsToRuin(1.0);
|
postOpt.setShareOfJobsToRuin(1.0);
|
||||||
postOpt.setNuOfIterations(1);
|
postOpt.setNuOfIterations(1);
|
||||||
postOpt.setFleetManager(fleetManager);
|
|
||||||
// postOpt.setWithFix(withFixCost);
|
// postOpt.setWithFix(withFixCost);
|
||||||
VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol);
|
VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol);
|
||||||
newSolution.setCost(getCosts(newSolution,states));
|
newSolution.setCost(getCosts(newSolution,states));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package algorithms;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import algorithms.RuinRadial.JobNeighborhoodsImpl;
|
||||||
|
import basics.Job;
|
||||||
|
import basics.Service;
|
||||||
|
import basics.VehicleRoutingProblem;
|
||||||
|
|
||||||
|
public class JobNeighborhoodsImplTest {
|
||||||
|
|
||||||
|
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", 1).setCoord(Coordinate.newInstance(0, 5)).build();
|
||||||
|
s2 = Service.Builder.newInstance("s2", 1).setCoord(Coordinate.newInstance(0, 4)).build();
|
||||||
|
s3 = Service.Builder.newInstance("s3", 1).setCoord(Coordinate.newInstance(0, 3)).build();
|
||||||
|
s4 = Service.Builder.newInstance("s4", 1).setCoord(Coordinate.newInstance(0, 2)).build();
|
||||||
|
|
||||||
|
s5 = Service.Builder.newInstance("s5", 1).setCoord(Coordinate.newInstance(0, 6)).build();
|
||||||
|
s6 = Service.Builder.newInstance("s6", 1).setCoord(Coordinate.newInstance(0, 7)).build();
|
||||||
|
s7 = Service.Builder.newInstance("s7", 1).setCoord(Coordinate.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(){
|
||||||
|
JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance);
|
||||||
|
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(){
|
||||||
|
JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance);
|
||||||
|
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(){
|
||||||
|
JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance);
|
||||||
|
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(){
|
||||||
|
JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance);
|
||||||
|
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 whenRequestingMoreNeighborsThanExisting_itShouldReturnMaxNeighbors(){
|
||||||
|
JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance);
|
||||||
|
jn.initialise();
|
||||||
|
Iterator<Job> iter = jn.getNearestNeighborsIterator(100, target);
|
||||||
|
List<Service> services = new ArrayList<Service>();
|
||||||
|
while(iter.hasNext()){
|
||||||
|
services.add((Service) iter.next());
|
||||||
|
}
|
||||||
|
assertEquals(6,services.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package algorithms;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import algorithms.RuinRadial.JobNeighborhoodsImpl;
|
||||||
|
import algorithms.RuinRadial.JobNeighborhoodsImplWithCapRestriction;
|
||||||
|
import basics.Job;
|
||||||
|
import basics.Service;
|
||||||
|
import basics.VehicleRoutingProblem;
|
||||||
|
|
||||||
|
public class JobNeighborhoodsWithCapRestrictionImplTest {
|
||||||
|
|
||||||
|
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", 1).setCoord(Coordinate.newInstance(0, 5)).build();
|
||||||
|
s2 = Service.Builder.newInstance("s2", 1).setCoord(Coordinate.newInstance(0, 4)).build();
|
||||||
|
s3 = Service.Builder.newInstance("s3", 1).setCoord(Coordinate.newInstance(0, 3)).build();
|
||||||
|
s4 = Service.Builder.newInstance("s4", 1).setCoord(Coordinate.newInstance(0, 2)).build();
|
||||||
|
|
||||||
|
s5 = Service.Builder.newInstance("s5", 1).setCoord(Coordinate.newInstance(0, 6)).build();
|
||||||
|
s6 = Service.Builder.newInstance("s6", 1).setCoord(Coordinate.newInstance(0, 7)).build();
|
||||||
|
s7 = Service.Builder.newInstance("s7", 1).setCoord(Coordinate.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(){
|
||||||
|
JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(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(){
|
||||||
|
JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(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(){
|
||||||
|
JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(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(){
|
||||||
|
JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(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 whenRequestingMoreNeighborsThanExisting_itShouldReturnMaxNeighbors(){
|
||||||
|
JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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 examples;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.configuration.XMLConfiguration;
|
||||||
|
|
||||||
|
import util.Coordinate;
|
||||||
|
import util.Solutions;
|
||||||
|
import algorithms.VehicleRoutingAlgorithms;
|
||||||
|
import analysis.SolutionPlotter;
|
||||||
|
import analysis.SolutionPrinter;
|
||||||
|
import analysis.SolutionPrinter.Print;
|
||||||
|
import basics.Service;
|
||||||
|
import basics.VehicleRoutingAlgorithm;
|
||||||
|
import basics.VehicleRoutingProblem;
|
||||||
|
import basics.VehicleRoutingProblemSolution;
|
||||||
|
import basics.io.AlgorithmConfig;
|
||||||
|
import basics.io.VrpXMLWriter;
|
||||||
|
import basics.route.Vehicle;
|
||||||
|
import basics.route.VehicleImpl;
|
||||||
|
import basics.route.VehicleImpl.Builder;
|
||||||
|
import basics.route.VehicleType;
|
||||||
|
import basics.route.VehicleTypeImpl;
|
||||||
|
|
||||||
|
public class ConfigureAlgorithmInCodeInsteadOfPerXml {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
/*
|
||||||
|
* some preparation - create output folder
|
||||||
|
*/
|
||||||
|
File dir = new File("output");
|
||||||
|
// if the directory does not exist, create it
|
||||||
|
if (!dir.exists()){
|
||||||
|
System.out.println("creating directory ./output");
|
||||||
|
boolean result = dir.mkdir();
|
||||||
|
if(result) System.out.println("./output created");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get a vehicle type-builder and build a type with the typeId "vehicleType" and a capacity of 2
|
||||||
|
*/
|
||||||
|
VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType", 2);
|
||||||
|
VehicleType vehicleType = vehicleTypeBuilder.build();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get a vehicle-builder and build a vehicle located at (10,10) with type "vehicleType"
|
||||||
|
*/
|
||||||
|
Builder vehicleBuilder = VehicleImpl.Builder.newInstance("vehicle");
|
||||||
|
vehicleBuilder.setLocationCoord(Coordinate.newInstance(10, 10));
|
||||||
|
vehicleBuilder.setType(vehicleType);
|
||||||
|
Vehicle vehicle = vehicleBuilder.build();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* build services at the required locations, each with a capacity-demand of 1.
|
||||||
|
*/
|
||||||
|
Service service1 = Service.Builder.newInstance("1", 1).setCoord(Coordinate.newInstance(5, 7)).build();
|
||||||
|
Service service2 = Service.Builder.newInstance("2", 1).setCoord(Coordinate.newInstance(5, 13)).build();
|
||||||
|
|
||||||
|
Service service3 = Service.Builder.newInstance("3", 1).setCoord(Coordinate.newInstance(15, 7)).build();
|
||||||
|
Service service4 = Service.Builder.newInstance("4", 1).setCoord(Coordinate.newInstance(15, 13)).build();
|
||||||
|
|
||||||
|
|
||||||
|
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
|
||||||
|
vrpBuilder.addVehicle(vehicle);
|
||||||
|
vrpBuilder.addService(service1).addService(service2).addService(service3).addService(service4);
|
||||||
|
|
||||||
|
VehicleRoutingProblem problem = vrpBuilder.build();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the algorithm out-of-the-box.
|
||||||
|
*/
|
||||||
|
AlgorithmConfig algorithmConfig = getAlgorithmConfig();
|
||||||
|
VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.createAlgorithm(problem,algorithmConfig);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* and search a solution
|
||||||
|
*/
|
||||||
|
Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the best
|
||||||
|
*/
|
||||||
|
VehicleRoutingProblemSolution bestSolution = Solutions.getBest(solutions);
|
||||||
|
|
||||||
|
new VrpXMLWriter(problem, solutions).write("output/problem-with-solution.xml");
|
||||||
|
|
||||||
|
SolutionPrinter.print(bestSolution,Print.VERBOSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* plot
|
||||||
|
*/
|
||||||
|
SolutionPlotter.plotSolutionAsPNG(problem, bestSolution, "output/solution.png", "solution");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AlgorithmConfig getAlgorithmConfig() {
|
||||||
|
AlgorithmConfig config = new AlgorithmConfig();
|
||||||
|
XMLConfiguration xmlConfig = config.getXMLConfiguration();
|
||||||
|
xmlConfig.setProperty("iterations", 2000);
|
||||||
|
xmlConfig.setProperty("construction.insertion[@name]","bestInsertion");
|
||||||
|
|
||||||
|
xmlConfig.setProperty("strategy.memory", 1);
|
||||||
|
String searchStrategy = "strategy.searchStrategies.searchStrategy";
|
||||||
|
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).selector[@name]","selectBest");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).acceptor[@name]","acceptNewRemoveWorst");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).modules.module(0)[@name]","ruin_and_recreate");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).ruin[@name]","randomRuin");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).ruin.share","0.3");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).insertion[@name]","bestInsertion");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(0).probability","0.5");
|
||||||
|
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).selector[@name]","selectBest");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).acceptor[@name]","acceptNewRemoveWorst");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).modules.module(0)[@name]","ruin_and_recreate");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).ruin[@name]","radialRuin");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).ruin.share","0.15");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).insertion[@name]","bestInsertion");
|
||||||
|
xmlConfig.setProperty(searchStrategy + "(1).probability","0.5");
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
4
pom.xml
4
pom.xml
|
|
@ -34,8 +34,8 @@
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>GNU General Public License, version 2 (GPL-2.0)</name>
|
<name>GNU Lesser General Public Licence, version 3.0</name>
|
||||||
<url>http://opensource.org/licenses/GPL-2.0</url>
|
<url>http://opensource.org/licenses/LGPL-3.0</url>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue