Service one = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc"))
+ .setUserData(new HashMap<String, Object>()).build();
+
+2017-05-11 new release **v1.7.1**
+- determine [reasons for unassigned jobs](https://github.com/graphhopper/jsprit/issues/180)
+- extend priority levels from 3 to 10 levels
+- a number of minor improvements
+
+2017-01-12 new release **v1.7**
- move packages to [graphhopper.com](https://graphhopper.com/)
- change license from GPLv3 to [Apache v2](https://github.com/graphhopper/jsprit/blob/master/LICENSE.md) to make it even more attractive for other developers and their commercial applications
- pushed binaries to maven central, i.e. made it better accessible and we get rid of our own repo
- outsourced various io operations, e.g. reading writing problem/algorithm to a new module called [jsprit-io](https://github.com/graphhopper/jsprit/tree/master/jsprit-io). this way the core is even more lightweight and less dependent on other libraries
- switched from log4j to the [logger facade slf4j](http://www.slf4j.org/) to allow developers to plugin the logger of their choice
- made it [much more memory efficient](https://github.com/graphhopper/jsprit/issues/230) for large problems
+- fixed [memory leak](https://github.com/graphhopper/jsprit/pull/282)
- add [priority feature](https://github.com/graphhopper/jsprit/issues/242)
- [refine exceptions](https://github.com/graphhopper/jsprit/issues/251) to get a clear separation of IllegalArgument and [IllegalState](https://stackoverflow.com/questions/12698275/whats-the-intended-use-of-illegalstateexception)
+- many further improvements (see https://github.com/graphhopper/jsprit/issues?q=is%3Aissue+is%3Aclosed)
2016-02-02 new release **v1.6.2**
diff --git a/docs/Classical-Problems-Examples.md b/docs/Classical-Problems-Examples.md
index 67e44f95..2ae34087 100644
--- a/docs/Classical-Problems-Examples.md
+++ b/docs/Classical-Problems-Examples.md
@@ -1,9 +1,9 @@
-- Capacitated VRP

-- Multiple Depot VRP 
-- VRP with Time Windows
-- VRP with Backhauls (Deliveries first)
-- VRP with Backhauls (mixed Pickups and Deliveries)
-- VRP with Pickups and Deliveries
-- VRP with Heterogeneous Fleet
-- Traveling Salesman Problem
-- Dial-a-Ride Problem
\ No newline at end of file
+- Capacitated VRP

+- Multiple Depot VRP 
+- VRP with Time Windows
+- VRP with Backhauls (Deliveries first)
+- VRP with Backhauls (mixed Pickups and Deliveries)
+- VRP with Pickups and Deliveries
+- VRP with Heterogeneous Fleet
+- Traveling Salesman Problem
+- Dial-a-Ride Problem
\ No newline at end of file
diff --git a/docs/Dial-a-ride-problem.md b/docs/Dial-a-ride-problem.md
index 6b678523..b94a5f3e 100644
--- a/docs/Dial-a-ride-problem.md
+++ b/docs/Dial-a-ride-problem.md
@@ -1 +1 @@
-You model a dial-a-ride problem much like a vehicle routing problem with pickups and deliveries. The capacity of vehicles can be interpreted as number of seats available. A shipment is here understood as a ride from one location to another (probably you want the shipment to have a capacity-demand of 1). See [VRP with pickups and deliveries](https://github.com/jsprit/jsprit/wiki/VRP-with-pickups-and-deliveries).
\ No newline at end of file
+You model a dial-a-ride problem much like a vehicle routing problem with pickups and deliveries. The capacity of vehicles can be interpreted as number of seats available. A shipment is here understood as a ride from one location to another (probably you want the shipment to have a capacity-demand of 1). See [VRP with pickups and deliveries](../docs/Vrp-with-pickups-and-deliveries.md).
\ No newline at end of file
diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md
index 557acac9..aea9645f 100644
--- a/docs/Getting-Started.md
+++ b/docs/Getting-Started.md
@@ -1,7 +1,7 @@
-####Requirements
-jsprit requires the Java 2 platform (JDK version 1.7.0 or later).
+#### Requirements
+jsprit requires Java 1.7.0 or later.
-####Modules
+#### Modules
jsprit is a multi-module project and consists of:
- jsprit-core
- jsprit-analysis
@@ -9,17 +9,20 @@ jsprit is a multi-module project and consists of:
- jsprit-examples
- jsprit-io
-####Maven way
+#### Maven way
If you want to use the latest release of jsprit-core, add the following lines to your pom:
-<dependency>
+```
+<dependency>
<groupId>com.graphhopper</groupId>
<artifactId>jsprit-core</artifactId>
- <version>1.7-RC1</version>
+ <version>{version}</version>
</dependency>
-
+```
-####Build yourself
+Find the latest versions here: [mvn repository](https://mvnrepository.com/artifact/com.graphhopper/jsprit-core)
+
+#### Build yourself
If you want to build the master branch yourself, do this:
```
@@ -28,7 +31,7 @@ cd jsprit
mvn clean install
```
-####If you do not have an IDE and you want to use Maven
+#### If you do not have an IDE and you want to use Maven
the following documentation is recommended:
@@ -37,6 +40,7 @@ the following documentation is recommended:
Here you learn to setup the Java environment and an Integrated Development Environment (IDE). In the subsection Adding Jars to your Project you learn to integrate external libraries in your project. Just copy/paste the above jsprit releases/snapshots to your pom.xml instead of the GeoTools-artifacts.
#### If you do not want Maven
+
to manage your dependencies, go to [maven central](https://search.maven.org/), search for jsprit and download the latest binaries to put them into your classpath.
Go ahead and show me a [simple example](Simple-Example.md) of how to setup and solve a vehicle routing problem.
diff --git a/docs/More-Examples.md b/docs/More-Examples.md
index e855bb89..dd8b6147 100644
--- a/docs/More-Examples.md
+++ b/docs/More-Examples.md
@@ -1,25 +1,25 @@
-#### [Multiple Depot VRP](Multiple-Depot-VRP)
+#### [Multiple Depot VRP](Multiple-Depot-VRP.md)
- setting up a VRP with Multiple Depots
- defining depots, vehicles and their types
- dealing with finite fleet size
-#### [VRP with time windows](VRP-with-time-windows-example)
+#### [VRP with time windows](VRP-with-time-windows-example.md)
- defining and creating vehicles and their types
- defining services with time-windows and service times
- defining a problem with infinite fleet-size
- reading, creating and running an algorithm
-#### [VRP with backhauls](VRP-with-backhauls-example)
+#### [VRP with backhauls](VRP-with-backhauls-example.md)
- defining and creating pickup and deliveries
- defining backhaul constraint
-#### [VRP with backhauls (with mixed pickup and deliveries)](VRP-with-depot-bounded-pickups-and-deliveries)
+#### [VRP with backhauls (with mixed pickup and deliveries)](VRP-with-depot-bounded-pickups-and-deliveries.md)
- defining and creating depot-bounded pickups and deliveries
-#### [VRP with pickup and delivieries](VRP-with-pickups-and-deliveries)
+#### [VRP with pickup and delivieries](Vrp-with-pickups-and-deliveries.md)
- defining and creating pickups and deliveries
-#### [VRP with heterogeneous fleet](Heterogeneous-Fleet)
+#### [VRP with heterogeneous fleet](Heterogeneous-Fleet.md)
- illustrating different problem types,
- specifying heterogeneous fleet with its vehicles and vehicle-types,
- specifying the algorithm,
diff --git a/docs/Simple-Example.md b/docs/Simple-Example.md
index 091e8cc6..d61aa27e 100644
--- a/docs/Simple-Example.md
+++ b/docs/Simple-Example.md
@@ -16,7 +16,7 @@ First, build a vehicle with its vehicle-type:
/*
* get a vehicle type-builder and build a type with the typeId "vehicleType" and a capacity of 2
- * you are free to add an arbitrary number of capacity dimensions with .addCacpacityDimension(dimensionIndex,dimensionValue)
+ * you are free to add an arbitrary number of capacity dimensions with .addCapacityDimension(dimensionIndex,dimensionValue)
*/
final int WEIGHT_INDEX = 0;
VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType").addCapacityDimension(WEIGHT_INDEX,2);
diff --git a/docs/VRP-with-time-windows-example.md b/docs/VRP-with-time-windows-example.md
index 5a768916..5c5a4e6d 100644
--- a/docs/VRP-with-time-windows-example.md
+++ b/docs/VRP-with-time-windows-example.md
@@ -108,4 +108,4 @@ Collection solutions = algorithm.searchSolutions(
VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions);
-Please visit Simple Example to get to know how you can analyse the solution.
\ No newline at end of file
+Please visit [Simple Example](https://github.com/graphhopper/jsprit/blob/master/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/SimpleExample.java) to get to know how you can analyse the solution.
\ No newline at end of file
diff --git a/docs/Walkthrough Constraints.md b/docs/Walkthrough Constraints.md
index d37d2e4e..f48fc701 100644
--- a/docs/Walkthrough Constraints.md
+++ b/docs/Walkthrough Constraints.md
@@ -2,8 +2,11 @@ It is assumed that you know the basics of the [applied algorithm](Meta-Heuristic
removed job is re-inserted into the ruined solution again (one after another). To actually insert a job, the algorithm
calculates its "best" insertion position. This implies also to check the feasibility of the insertion step
which is in turn dependent on constraints (such as capacity constraints).
-jsprit knows hard and soft constraints. Whereas hard constraints must be met and cannot be broken, soft constraints
-are always fulfilled but uses penalties to express "good" and "bad" insertions.
+
+## jsprit knows hard and soft constraints.
+
+- Hard constraints must be met and cannot be broken
+- soft constraints are always fulfilled but uses penalties to express "good" and "bad" insertions.
jsprit comes with built-in or default constraints (such as capacity and time-window constraints) and allows you to add
custom constraints. However, you can also disable the default constraints. To add custom constraints use
@@ -44,7 +47,7 @@ There are hard constraints at two different levels: at route and activity level.
A route is basically a sequence of activities. Each route has a start- and an end-activity, and in between other activities of type core.problem.solution.route.activity.TourActivity.
-###core.problem.constraint.HardRouteConstraint
+### core.problem.constraint.HardRouteConstraint
A HardRouteConstraint indicates whether a specified job can be inserted into an existing route (along with a specified vehicle). To define it you need to implement the HardRouteConstraint-interface:
HardRouteConstraint constraint = new HardRouteConstraint(){
@@ -61,7 +64,7 @@ The JobInsertionContext tells you the context of the insertion step, i.e. the sp
a specified route (iContext.getRoute()) as well as the vehicle
that should be employed on that route (iContext.getNewVehicle()).
-####Example:
+#### Example:
Assume a vehicle with id="1" is not allowed to serve a job with id="job1" (since it is for example too big to access the customer location). Such a constraint can be easily defined as follows:
final Job jobWithAccessConstraint = routingProblem.getJobs().get("job1");
diff --git a/jsprit-analysis/pom.xml b/jsprit-analysis/pom.xml
index dc12d885..e4dd3e4b 100644
--- a/jsprit-analysis/pom.xml
+++ b/jsprit-analysis/pom.xml
@@ -20,7 +20,7 @@
com.graphhopper
jsprit
- 1.6.3-SNAPSHOT
+ 1.7.3-SNAPSHOT
4.0.0
jsprit-analysis
diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java
deleted file mode 100644
index c1a46be9..00000000
--- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Licensed to GraphHopper GmbH under one or more contributor
- * license agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * GraphHopper GmbH licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.graphhopper.jsprit.analysis.toolbox;
-
-import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm;
-import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithmFactory;
-import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
-import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
-import com.graphhopper.jsprit.core.util.BenchmarkInstance;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-
-public class ComputationalLaboratory {
-
- public static interface LabListener {
-
- }
-
- /**
- * Listener-interface to listen to calculation.
- *
- *
Note that calculations are run concurrently, i.e. a unique task that is distributed to an available thread is
- * {algorithm, instance, run}.
- *
- * @author schroeder
- */
- public static interface CalculationListener extends LabListener {
-
- public void calculationStarts(final BenchmarkInstance p, final String algorithmName, final VehicleRoutingAlgorithm algorithm, final int run);
-
- public void calculationEnds(final BenchmarkInstance p, final String algorithmName, final VehicleRoutingAlgorithm algorithm, final int run, final Collection solutions);
-
- }
-
- public static interface LabStartsAndEndsListener extends LabListener {
-
- public void labStarts(List instances, int noAlgorithms, int runs);
-
- public void labEnds();
- }
-
- /**
- * Collects whatever indicators you require by algorithmName, instanceName, run and indicator.
- *
- * @author schroeder
- */
- public static class DataCollector {
-
- public static class Key {
- private String instanceName;
- private String algorithmName;
- private int run;
- private String indicatorName;
-
- public Key(String instanceName, String algorithmName, int run, String indicatorName) {
- super();
- this.instanceName = instanceName;
- this.algorithmName = algorithmName;
- this.run = run;
- this.indicatorName = indicatorName;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime
- * result
- + ((algorithmName == null) ? 0 : algorithmName
- .hashCode());
- result = prime
- * result
- + ((indicatorName == null) ? 0 : indicatorName
- .hashCode());
- result = prime
- * result
- + ((instanceName == null) ? 0 : instanceName.hashCode());
- result = prime * result + run;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Key other = (Key) obj;
- if (algorithmName == null) {
- if (other.algorithmName != null)
- return false;
- } else if (!algorithmName.equals(other.algorithmName))
- return false;
- if (indicatorName == null) {
- if (other.indicatorName != null)
- return false;
- } else if (!indicatorName.equals(other.indicatorName))
- return false;
- if (instanceName == null) {
- if (other.instanceName != null)
- return false;
- } else if (!instanceName.equals(other.instanceName))
- return false;
- if (run != other.run)
- return false;
- return true;
- }
-
- public String getInstanceName() {
- return instanceName;
- }
-
- public String getAlgorithmName() {
- return algorithmName;
- }
-
- public int getRun() {
- return run;
- }
-
- public String getIndicatorName() {
- return indicatorName;
- }
-
- @Override
- public String toString() {
- return "[algorithm=" + algorithmName + "][instance=" + instanceName + "][run=" + run + "][indicator=" + indicatorName + "]";
- }
-
- }
-
- private final static String SOLUTION_INDICATOR_NAME = "vehicle-routing-problem-solution";
-
- private ConcurrentHashMap data = new ConcurrentHashMap();
-
- private ConcurrentHashMap solutions = new ConcurrentHashMap();
-
- /**
- * Adds a single date by instanceName, algorithmName, run and indicatorName.
- * If there is already an entry for this instance, algorithm, run and indicatorName, it is overwritten.
- *
- * @param instanceName
- * @param algorithmName
- * @param run
- * @param indicatorName
- * @param value
- */
- public void addDate(String instanceName, String algorithmName, int run, String indicatorName, double value) {
- if (indicatorName.equals(SOLUTION_INDICATOR_NAME))
- throw new IllegalArgumentException(indicatorName + " is already used internally. please choose another indicator-name.");
- Key key = new Key(instanceName, algorithmName, run, indicatorName);
- data.put(key, value);
- }
-
- public void addSolution(String instanceName, String algorithmName, int run, VehicleRoutingProblemSolution solution) {
- Key key = new Key(instanceName, algorithmName, run, SOLUTION_INDICATOR_NAME);
- solutions.put(key, solution);
- }
-
- /**
- * Returns a collections of indicator values representing the calculated values of individual runs.
- *
- * @param instanceName
- * @param algorithmName
- * @param indicator
- * @return
- */
- public Collection getData(String instanceName, String algorithmName, String indicator) {
- List values = new ArrayList();
- for (Key key : data.keySet()) {
- if (key.getAlgorithmName().equals(algorithmName) && key.getInstanceName().equals(instanceName) && key.getIndicatorName().equals(indicator)) {
- values.add(data.get(key));
- }
- }
- return values;
- }
-
- /**
- * Returns indicator value.
- *
- * @param instanceName
- * @param algorithmName
- * @param run
- * @param indicator
- * @return
- */
- public Double getDate(String instanceName, String algorithmName, int run, String indicator) {
- return data.get(new Key(instanceName, algorithmName, run, indicator));
- }
-
- public VehicleRoutingProblemSolution getSolution(String instanceName, String algorithmName, int run) {
- return solutions.get(new Key(instanceName, algorithmName, run, "solution"));
- }
-
- /**
- * Returns all keys that have been created. A key is a unique combination of algorithmName, instanceName, run and indicator.
- *
- * @return
- */
- public Set getDataKeySet() {
- return data.keySet();
- }
-
- public Set getSolutionKeySet() {
- return solutions.keySet();
- }
-
- public VehicleRoutingProblemSolution getSolution(Key solutionKey) {
- return solutions.get(solutionKey);
- }
-
- public Collection getSolutions() {
- return solutions.values();
- }
-
- /**
- * Returns date associated to specified key.
- *
- * @param key
- * @return
- */
- public Double getData(Key key) {
- return data.get(key);
- }
-
- }
-
-
- private static class Algorithm {
-
- private String name;
-
- private VehicleRoutingAlgorithmFactory factory;
-
- public Algorithm(String name, VehicleRoutingAlgorithmFactory factory) {
- super();
- this.name = name;
- this.factory = factory;
- }
-
- }
-
- private List benchmarkInstances = new ArrayList();
-
- private int runs = 1;
-
- private Collection listeners = new ArrayList();
-
- private Collection startsAndEndslisteners = new ArrayList();
-
- private List algorithms = new ArrayList();
-
- private Set algorithmNames = new HashSet();
-
- private Set instanceNames = new HashSet();
-
- private int threads = 1;
-
- public ComputationalLaboratory() {
-
- }
-
- /**
- * Adds algorithmFactory by name.
- *
- * @param name
- * @param factory
- * @throws IllegalStateException if there is already an algorithmFactory with the same name
- */
- public void addAlgorithmFactory(String name, VehicleRoutingAlgorithmFactory factory) {
- if (algorithmNames.contains(name))
- throw new IllegalStateException("there is already a algorithmFactory with the same name (algorithmName=" + name + "). unique names are required.");
- algorithms.add(new Algorithm(name, factory));
- algorithmNames.add(name);
- }
-
- public Collection getAlgorithmNames() {
- return algorithmNames;
- }
-
- public Collection getInstanceNames() {
- return instanceNames;
- }
-
- /**
- * Adds instance by name.
- *
- * @param name
- * @param problem
- * @throws IllegalStateException if there is already an instance with the same name.
- */
- public void addInstance(String name, VehicleRoutingProblem problem) {
- if (benchmarkInstances.contains(name))
- throw new IllegalStateException("there is already an instance with the same name (instanceName=" + name + "). unique names are required.");
- benchmarkInstances.add(new BenchmarkInstance(name, problem, null, null));
- instanceNames.add(name);
- }
-
- /**
- * Adds instance.
- *
- * @param instance the instance to be added
- * @throws IllegalStateException if there is already an instance with the same name.
- */
- public void addInstance(BenchmarkInstance instance) {
- if (benchmarkInstances.contains(instance.name))
- throw new IllegalStateException("there is already an instance with the same name (instanceName=" + instance.name + "). unique names are required.");
- benchmarkInstances.add(instance);
- instanceNames.add(instance.name);
- }
-
- /**
- * Adds collection of instances.
- *
- * @param instances collection of instances to be added
- * @throws IllegalStateException if there is already an instance with the same name.
- */
- public void addAllInstances(Collection instances) {
- for (BenchmarkInstance i : instances) {
- addInstance(i);
- }
- }
-
- /**
- * Adds instance by name, and with best known results.
- *
- * @param name
- * @param problem
- * @throws IllegalStateException if there is already an instance with the same name.
- */
- public void addInstance(String name, VehicleRoutingProblem problem, Double bestKnownResult, Double bestKnownVehicles) {
- addInstance(new BenchmarkInstance(name, problem, bestKnownResult, bestKnownVehicles));
- }
-
- /**
- * Adds listener to listen computational experiments.
- *
- * @param listener
- */
- public void addListener(LabListener listener) {
- if (listener instanceof CalculationListener) {
- listeners.add((CalculationListener) listener);
- }
- if (listener instanceof LabStartsAndEndsListener) {
- startsAndEndslisteners.add((LabStartsAndEndsListener) listener);
- }
- }
-
- /**
- * Sets nuOfRuns with same algorithm on same instance.
- * Default is 1
- *
- * @param runs
- */
- public void setNuOfRuns(int runs) {
- this.runs = runs;
- }
-
- /**
- * Runs experiments.
- *
- *
If nuThreads > 1 it runs them concurrently, i.e. individual runs are distributed to available threads. Therefore
- * a unique task is defined by its algorithmName, instanceName and its runNumber.
- *
If you have one algorithm called "myAlgorithm" and one instance called "myInstance", and you need to run "myAlgorithm" on "myInstance" three times
- * with three threads then "myAlgorithm","myInstance",run1 runs on the first thread, "myAlgorithm", "myInstance", run2 on the second etc.
- *
You can register whatever analysisTool you require by implementing and registering CalculationListener. Then your tool is informed just
- * before a calculation starts as well as just after a calculation has been finished.
- *
- * @throws IllegalStateException if either no algorithm or no instance has been specified
- * @see CalculationListener
- */
- public void run() {
- if (algorithms.isEmpty()) {
- throw new IllegalStateException("no algorithm specified. at least one algorithm needs to be specified.");
- }
- if (benchmarkInstances.isEmpty()) {
- throw new IllegalStateException("no instance specified. at least one instance needs to be specified.");
- }
- informStart();
- System.out.println("start benchmarking [nuAlgorithms=" + algorithms.size() + "][nuInstances=" + benchmarkInstances.size() + "][runsPerInstance=" + runs + "]");
- double startTime = System.currentTimeMillis();
- ExecutorService executor = Executors.newFixedThreadPool(threads);
- for (final Algorithm algorithm : algorithms) {
- for (final BenchmarkInstance p : benchmarkInstances) {
- for (int run = 0; run < runs; run++) {
- final int r = run;
- try {
- executor.submit(new Runnable() {
-
- @Override
- public void run() {
- runAlgorithm(p, algorithm, r + 1);
- }
-
- });
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
- try {
- executor.shutdown();
- executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println("benchmarking done [time=" + (System.currentTimeMillis() - startTime) / 1000 + "sec]");
- informEnd();
- }
-
- private void informEnd() {
- for (LabStartsAndEndsListener l : startsAndEndslisteners) {
- l.labEnds();
- }
- }
-
- private void informStart() {
- for (LabStartsAndEndsListener l : startsAndEndslisteners) {
- l.labStarts(benchmarkInstances, algorithms.size(), runs);
- }
- }
-
- /**
- * Sets number of threads.
- *
By default: nuThreads = Runtime.getRuntime().availableProcessors()+1
- *
- * @param threads
- */
- public void setThreads(int threads) {
- this.threads = threads;
- }
-
- private void runAlgorithm(BenchmarkInstance p, Algorithm algorithm, int run) {
- System.out.println("[algorithm=" + algorithm.name + "][instance=" + p.name + "][run=" + run + "][status=start]");
- VehicleRoutingAlgorithm vra = algorithm.factory.createAlgorithm(p.vrp);
- informCalculationStarts(p, algorithm.name, vra, run);
- Collection solutions = vra.searchSolutions();
- System.out.println("[algorithm=" + algorithm.name + "][instance=" + p.name + "][run=" + run + "][status=finished]");
- informCalculationsEnds(p, algorithm.name, vra, run, solutions);
- }
-
- private void informCalculationStarts(BenchmarkInstance p, String name, VehicleRoutingAlgorithm vra, int run) {
- for (CalculationListener l : listeners) l.calculationStarts(p, name, vra, run);
- }
-
- private void informCalculationsEnds(BenchmarkInstance p, String name, VehicleRoutingAlgorithm vra, int run,
- Collection solutions) {
- for (CalculationListener l : listeners) l.calculationEnds(p, name, vra, run, solutions);
- }
-
-}
diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/StrategyAnalyser.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/StrategyAnalyser.java
new file mode 100644
index 00000000..522df545
--- /dev/null
+++ b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/StrategyAnalyser.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.analysis.toolbox;
+
+import com.graphhopper.jsprit.core.algorithm.SearchStrategy;
+import com.graphhopper.jsprit.core.algorithm.listener.AlgorithmEndsListener;
+import com.graphhopper.jsprit.core.algorithm.listener.IterationStartsListener;
+import com.graphhopper.jsprit.core.algorithm.listener.StrategySelectedListener;
+import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
+import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.*;
+
+/**
+ * Created by schroeder on 27/04/17.
+ */
+public class StrategyAnalyser implements AlgorithmEndsListener, StrategySelectedListener, IterationStartsListener {
+
+
+ public static class Strategy {
+
+ private final String id;
+
+ private int selected = 0;
+
+ private int improved = 0;
+
+ private int countNewSolution = 0;
+
+ private List improvements = new ArrayList<>();
+
+ public Strategy(String id) {
+ this.id = id;
+ }
+
+ public void selected() {
+ selected++;
+ }
+
+ public void improvedSolution(double improvement) {
+ improved++;
+ improvements.add(improvement);
+ }
+
+ public void newSolution() {
+ countNewSolution++;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getCountSelected() {
+ return selected;
+ }
+
+ public int getCountImproved() {
+ return improved;
+ }
+
+ public int getCountNewSolution() {
+ return countNewSolution;
+ }
+
+ public List getImprovements() {
+ return improvements;
+ }
+ }
+
+ private Map strategyMap = new HashMap<>();
+
+ private Collection last;
+
+ private Writer out;
+
+ @Override
+ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
+ last = new ArrayList<>(solutions);
+ }
+
+ @Override
+ public void informSelectedStrategy(SearchStrategy.DiscoveredSolution discoveredSolution, VehicleRoutingProblem vehicleRoutingProblem, Collection vehicleRoutingProblemSolutions) {
+ String strategyId = discoveredSolution.getStrategyId();
+ if (!strategyMap.containsKey(strategyId)) {
+ strategyMap.put(strategyId, new Strategy(strategyId));
+ }
+ Strategy strategy = strategyMap.get(strategyId);
+ strategy.selected();
+ if (discoveredSolution.isAccepted()) strategy.newSolution();
+ if (isBetter(vehicleRoutingProblemSolutions, last)) {
+ strategy.improvedSolution(getImprovement(vehicleRoutingProblemSolutions, last));
+
+ }
+ }
+
+ public void setOutWriter(Writer out) {
+ this.out = out;
+ }
+
+ @Override
+ public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
+ if (out == null) out = new PrintWriter(System.out);
+ try {
+ for (String stratId : strategyMap.keySet()) {
+ StrategyAnalyser.Strategy strategy = strategyMap.get(stratId);
+ out.write("id: " + stratId + ", #selected: " + strategy.getCountSelected() + ", #newSolutions: " + strategy.getCountNewSolution()
+ + ", #improvedSolutions: " + strategy.getCountImproved() + ", improvements: " + strategy.getImprovements().toString() + "\n");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private double getImprovement(Collection vehicleRoutingProblemSolutions, Collection last) {
+ for (VehicleRoutingProblemSolution solution : vehicleRoutingProblemSolutions) {
+ for (VehicleRoutingProblemSolution lastSolution : last) {
+ if (solution.getCost() < lastSolution.getCost())
+ return Math.round(lastSolution.getCost() - solution.getCost());
+ }
+ }
+ return 0;
+ }
+
+ private boolean isBetter(Collection vehicleRoutingProblemSolutions, Collection last) {
+ for (VehicleRoutingProblemSolution solution : vehicleRoutingProblemSolutions) {
+ for (VehicleRoutingProblemSolution lastSolution : last) {
+ if (solution.getCost() < lastSolution.getCost()) return true;
+ }
+ }
+ return false;
+ }
+
+ public Map getStrategies() {
+ return strategyMap;
+ }
+}
diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java
deleted file mode 100644
index b8520e7e..00000000
--- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Licensed to GraphHopper GmbH under one or more contributor
- * license agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * GraphHopper GmbH licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.graphhopper.jsprit.analysis.util;
-
-import com.graphhopper.jsprit.core.util.BenchmarkResult;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Collection;
-
-public class HtmlBenchmarkTableWriter implements BenchmarkWriter {
-
- private String filename;
-
- public HtmlBenchmarkTableWriter(String filename) {
- this.filename = filename;
- }
-
- @Override
- public void write(Collection results) {
-
- try {
- BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename)));
- writer.write(openTable() + newline());
- //table head
- writer.write(openRow() + newline());
- writer.write(head("inst") + newline());
- writer.write(head("runs") + newline());
- writer.write(head("Ø time [sec]") + newline());
- writer.write(head("results", 4));
- writer.write(head("vehicles", 4));
- writer.write(head("res*") + newline());
- writer.write(head("veh*") + newline());
- writer.write(closeRow() + newline());
-
- writer.write(openRow() + newline());
- writer.write(head("") + newline());
- writer.write(head("") + newline());
- writer.write(head("") + newline());
- writer.write(head("best") + newline());
- writer.write(head("avg") + newline());
- writer.write(head("worst") + newline());
- writer.write(head("stdev") + newline());
- writer.write(head("best") + newline());
- writer.write(head("avg") + newline());
- writer.write(head("worst") + newline());
- writer.write(head("stdev") + newline());
- writer.write(head("") + newline());
- writer.write(head("") + newline());
- writer.write(closeRow() + newline());
-
- //data
- double sum_avg_time = 0.0;
- double sum_best_result = 0.0;
- double sum_avg_result = 0.0;
- double sum_worst_result = 0.0;
- double sum_dev_result = 0.0;
-
- double sum_best_veh = 0.0;
- double sum_avg_veh = 0.0;
- double sum_worst_veh = 0.0;
- double sum_dev_veh = 0.0;
-
- Integer runs = null;
- Double sum_res_star = null;
- Double sum_veh_star = null;
-
- for (BenchmarkResult result : results) {
- if (runs == null) runs = result.runs;
- writer.write(openRow() + newline());
- writer.write(date(result.instance.name) + newline());
- writer.write(date(Integer.valueOf(result.runs).toString()) + newline());
-
- Double avg_time = round(result.getTimesStats().getMean(), 2);
- writer.write(date(Double.valueOf(avg_time).toString()) + newline());
- //bestRes
- Double best_result = round(result.getResultStats().getMin(), 2);
- writer.write(date(Double.valueOf(best_result).toString()) + newline());
- //avgRes
- Double avg_result = round(result.getResultStats().getMean(), 2);
- writer.write(date(Double.valueOf(avg_result).toString()) + newline());
- //worstRes
- Double worst_result = round(result.getResultStats().getMax(), 2);
- writer.write(date(Double.valueOf(worst_result).toString()) + newline());
- //stdevRes
- Double std_result = round(result.getResultStats().getStandardDeviation(), 2);
- writer.write(date(Double.valueOf(std_result).toString()) + newline());
- //bestVeh
- Double best_vehicle = round(result.getVehicleStats().getMin(), 2);
- writer.write(date(Double.valueOf(best_vehicle).toString()) + newline());
- //avgVeh
- Double avg_vehicle = round(result.getVehicleStats().getMean(), 2);
- writer.write(date(Double.valueOf(avg_vehicle).toString()) + newline());
- //worstVeh
- Double worst_vehicle = round(result.getVehicleStats().getMax(), 2);
- writer.write(date(Double.valueOf(worst_vehicle).toString()) + newline());
- //stdevVeh
- Double std_vehicle = round(result.getVehicleStats().getStandardDeviation(), 2);
- writer.write(date(Double.valueOf(std_vehicle).toString()) + newline());
- //bestKnownRes
- writer.write(date("" + result.instance.bestKnownResult + newline()));
- //bestKnownVeh
- writer.write(date("" + result.instance.bestKnownVehicles + newline()));
- writer.write(closeRow() + newline());
-
- sum_avg_time += avg_time;
- sum_best_result += best_result;
- sum_avg_result += avg_result;
- sum_worst_result += worst_result;
- sum_dev_result += std_result;
-
- sum_best_veh += best_vehicle;
- sum_avg_veh += avg_vehicle;
- sum_worst_veh += worst_vehicle;
- sum_dev_veh += std_vehicle;
-
- if (result.instance.bestKnownResult != null) {
- if (sum_res_star == null) sum_res_star = result.instance.bestKnownResult;
- else sum_res_star += result.instance.bestKnownResult;
- }
- if (result.instance.bestKnownVehicles != null) {
- if (sum_veh_star == null) sum_veh_star = result.instance.bestKnownVehicles;
- else sum_veh_star += result.instance.bestKnownVehicles;
- }
-
- }
- writer.write(openRow() + newline());
- writer.write(date("Ø") + newline());
- writer.write(date("" + runs) + newline());
-
- Double average_time = round(sum_avg_time / (double) results.size(), 2);
- writer.write(date(Double.valueOf(average_time).toString()) + newline());
- //bestRes
- writer.write(date(Double.valueOf(round(sum_best_result / (double) results.size(), 2)).toString()) + newline());
- //avgRes
- Double average_result = round(sum_avg_result / (double) results.size(), 2);
- writer.write(date(Double.valueOf(average_result).toString()) + newline());
- //worstRes
- writer.write(date(Double.valueOf(round(sum_worst_result / (double) results.size(), 2)).toString()) + newline());
- //stdevRes
- writer.write(date(Double.valueOf(round(sum_dev_result / (double) results.size(), 2)).toString()) + newline());
- //bestVeh
- writer.write(date(Double.valueOf(round(sum_best_veh / (double) results.size(), 2)).toString()) + newline());
- //avgVeh
- Double average_vehicles = round(sum_avg_veh / (double) results.size(), 2);
- writer.write(date(Double.valueOf(average_vehicles).toString()) + newline());
- //worstVeh
- writer.write(date(Double.valueOf(round(sum_worst_veh / (double) results.size(), 2)).toString()) + newline());
- //stdevVeh
- writer.write(date(Double.valueOf(round(sum_dev_veh / (double) results.size(), 2)).toString()) + newline());
- //bestKnownRes
- Double delta_res = null;
- if (sum_res_star != null) {
- writer.write(date(Double.valueOf(round(sum_res_star.doubleValue() / (double) results.size(), 2)).toString()) + newline());
- delta_res = (sum_avg_result / sum_res_star - 1) * 100;
- } else writer.write(date("null") + newline());
- //bestKnownVeh
- Double delta_veh = null;
- if (sum_veh_star != null) {
- writer.write(date(Double.valueOf(round(sum_veh_star.doubleValue() / (double) results.size(), 2)).toString()) + newline());
- delta_veh = (sum_avg_veh - sum_veh_star) / (double) results.size();
- } else writer.write(date("null") + newline());
- writer.write(closeRow() + newline());
-
- writer.write(closeTable() + newline());
-
- writer.write("avg. percentage deviation to best-known result: " + round(delta_res, 2) + newline() + newline());
- writer.write("avg. absolute deviation to best-known vehicles: " + round(delta_veh, 2) + newline());
-
- writer.write(openTable() + newline());
- writer.write(openRow() + newline());
- writer.write(date("") + newline());
- writer.write(date("") + newline());
- writer.write(date("") + newline());
- writer.write(date("") + newline());
- writer.write(date(Double.valueOf(average_time).toString(), "align=\"right\"") + newline());
- writer.write(date(Double.valueOf(average_result).toString(), "align=\"right\"") + newline());
- writer.write(date(Double.valueOf(average_vehicles).toString(), "align=\"right\"") + newline());
- if (delta_res != null) {
- writer.write(date(Double.valueOf(round(delta_res, 2)).toString(), "align=\"right\"") + newline());
- } else writer.write(date("n.a.") + newline());
- if (delta_veh != null) {
- writer.write(date(Double.valueOf(round(delta_veh, 2)).toString(), "align=\"right\"") + newline());
- } else writer.write(date("n.a.") + newline());
- writer.write(closeRow() + newline());
- writer.write(closeTable() + newline());
-
-
- writer.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
-
- private String head(String string, int i) {
- return "" + string + " ";
- }
-
- private Double round(Double value, int i) {
- if (value == null) return null;
- long roundedVal = Math.round(value * Math.pow(10, i));
- return (double) roundedVal / (double) (Math.pow(10, i));
- }
-
- private String head(String head) {
- return "" + head + " ";
- }
-
- private String closeTable() {
- return "";
- }
-
- private String openTable() {
- return "";
- }
-
- private String closeRow() {
- return "";
- }
-
- private String date(String date) {
- return "" + date + " ";
- }
-
- private String date(String date, String metaData) {
- return "" + date + " ";
- }
-
- private String newline() {
- return "\n";
- }
-
- private String openRow() {
- return "";
- }
-
-
-}
diff --git a/jsprit-core/pom.xml b/jsprit-core/pom.xml
index 5e2d2512..d925760b 100644
--- a/jsprit-core/pom.xml
+++ b/jsprit-core/pom.xml
@@ -21,7 +21,7 @@
com.graphhopper
jsprit
- 1.6.3-SNAPSHOT
+ 1.7.3-SNAPSHOT
4.0.0
jsprit-core
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java
index 37918884..b60fae9f 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java
@@ -17,6 +17,14 @@
*/
package com.graphhopper.jsprit.core.algorithm;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.graphhopper.jsprit.core.algorithm.SearchStrategy.DiscoveredSolution;
import com.graphhopper.jsprit.core.algorithm.listener.SearchStrategyListener;
import com.graphhopper.jsprit.core.algorithm.listener.SearchStrategyModuleListener;
@@ -30,11 +38,6 @@ import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolutio
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.util.Solutions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collection;
/**
@@ -57,9 +60,8 @@ public class VehicleRoutingAlgorithm {
@Override
public boolean isPrematureBreak(DiscoveredSolution discoveredSolution) {
for (PrematureAlgorithmTermination termination : terminationCriteria) {
- if (termination.isPrematureBreak(discoveredSolution)) {
+ if (termination.isPrematureBreak(discoveredSolution))
return true;
- }
}
return false;
}
@@ -140,30 +142,36 @@ public class VehicleRoutingAlgorithm {
* @param solution the solution to be added
*/
public void addInitialSolution(VehicleRoutingProblemSolution solution) {
+ // We will make changes so let's make a copy
+ solution = VehicleRoutingProblemSolution.copyOf(solution);
verify(solution);
initialSolutions.add(solution);
}
private void verify(VehicleRoutingProblemSolution solution) {
- int nuJobs = 0;
+ Set allJobs = new HashSet(problem.getJobs().values());
+ allJobs.removeAll(solution.getUnassignedJobs());
for (VehicleRoute route : solution.getRoutes()) {
- nuJobs += route.getTourActivities().getJobs().size();
+ allJobs.removeAll(route.getTourActivities().getJobs());
if (route.getVehicle().getIndex() == 0)
throw new IllegalStateException("vehicle used in initial solution has no index. probably a vehicle is used that has not been added to the " +
- " the VehicleRoutingProblem. only use vehicles that have already been added to the problem.");
+ " the VehicleRoutingProblem. only use vehicles that have already been added to the problem.");
for (TourActivity act : route.getActivities()) {
- if (act.getIndex() == 0) {
+ if (act.getIndex() == 0)
throw new IllegalStateException("act in initial solution has no index. activities are created and associated to their job in VehicleRoutingProblem\n." +
- " thus if you build vehicle-routes use the jobActivityFactory from vehicle routing problem like that \n" +
- " VehicleRoute.Builder.newInstance(knownVehicle).setJobActivityFactory(vrp.getJobActivityFactory).addService(..)....build() \n" +
- " then the activities that are created to build the route are identical to the ones used in VehicleRoutingProblem");
- }
+ " thus if you build vehicle-routes use the jobActivityFactory from vehicle routing problem like that \n" +
+ " VehicleRoute.Builder.newInstance(knownVehicle).setJobActivityFactory(vrp.getJobActivityFactory).addService(..)....build() \n" +
+ " then the activities that are created to build the route are identical to the ones used in VehicleRoutingProblem");
}
}
- if (nuJobs != problem.getJobs().values().size()) {
- logger.warn("number of jobs in initial solution ({}) is not equal nuJobs in vehicle routing problem ({})" +
- "\n this might yield unintended effects, e.g. initial solution cannot be improved anymore.", nuJobs, problem.getJobs().values().size());
- }
+
+ solution.getUnassignedJobs().addAll(allJobs);
+ solution.setCost(getObjectiveFunction().getCosts(solution));
+
+ // if (nuJobs != problem.getJobs().values().size()) {
+ // logger.warn("number of jobs in initial solution ({}) is not equal nuJobs in vehicle routing problem ({})" +
+ // "\n this might yield unintended effects, e.g. initial solution cannot be improved anymore.", nuJobs, problem.getJobs().values().size());
+ // }
}
/**
@@ -214,7 +222,9 @@ public class VehicleRoutingAlgorithm {
Collection solutions = new ArrayList(initialSolutions);
algorithmStarts(problem, solutions);
bestEver = Solutions.bestOf(solutions);
- if (logger.isTraceEnabled()) log(solutions);
+ if (logger.isTraceEnabled()) {
+ log(solutions);
+ }
logger.info("iterations start");
for (int i = 0; i < maxIterations; i++) {
iterationStarts(i + 1, problem, solutions);
@@ -222,7 +232,9 @@ public class VehicleRoutingAlgorithm {
counter.incCounter();
SearchStrategy strategy = searchStrategyManager.getRandomStrategy();
DiscoveredSolution discoveredSolution = strategy.run(problem, solutions);
- if (logger.isTraceEnabled()) log(discoveredSolution);
+ if (logger.isTraceEnabled()) {
+ log(discoveredSolution);
+ }
memorizeIfBestEver(discoveredSolution);
selectedStrategy(discoveredSolution, problem, solutions);
if (terminationManager.isPrematureBreak(discoveredSolution)) {
@@ -240,11 +252,15 @@ public class VehicleRoutingAlgorithm {
}
private void addBestEver(Collection solutions) {
- if (bestEver != null) solutions.add(bestEver);
+ if (bestEver != null) {
+ solutions.add(bestEver);
+ }
}
private void log(Collection solutions) {
- for (VehicleRoutingProblemSolution sol : solutions) log(sol);
+ for (VehicleRoutingProblemSolution sol : solutions) {
+ log(sol);
+ }
}
private void log(VehicleRoutingProblemSolution solution) {
@@ -277,9 +293,11 @@ public class VehicleRoutingAlgorithm {
private void memorizeIfBestEver(DiscoveredSolution discoveredSolution) {
if (discoveredSolution == null) return;
- if (bestEver == null) bestEver = discoveredSolution.getSolution();
- else if (discoveredSolution.getSolution().getCost() < bestEver.getCost())
+ if (bestEver == null) {
bestEver = discoveredSolution.getSolution();
+ } else if (discoveredSolution.getSolution().getCost() < bestEver.getCost()) {
+ bestEver = discoveredSolution.getSolution();
+ }
}
@@ -297,10 +315,12 @@ public class VehicleRoutingAlgorithm {
public void addListener(VehicleRoutingAlgorithmListener l) {
algoListeners.addListener(l);
- if (l instanceof SearchStrategyListener)
+ if (l instanceof SearchStrategyListener) {
searchStrategyManager.addSearchStrategyListener((SearchStrategyListener) l);
- if (l instanceof SearchStrategyModuleListener)
+ }
+ if (l instanceof SearchStrategyModuleListener) {
searchStrategyManager.addSearchStrategyModuleListener((SearchStrategyModuleListener) l);
+ }
}
private void iterationEnds(int i, VehicleRoutingProblem problem, Collection solutions) {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/acceptor/SchrimpfAcceptance.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/acceptor/SchrimpfAcceptance.java
index f89960b0..f104a1ca 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/acceptor/SchrimpfAcceptance.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/acceptor/SchrimpfAcceptance.java
@@ -25,7 +25,9 @@ import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolutio
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
@@ -78,7 +80,6 @@ public class SchrimpfAcceptance implements SolutionAcceptor, IterationStartsList
private final int solutionMemory;
-
public SchrimpfAcceptance(int solutionMemory, double alpha) {
this.alpha = alpha;
this.solutionMemory = solutionMemory;
@@ -110,6 +111,32 @@ public class SchrimpfAcceptance implements SolutionAcceptor, IterationStartsList
return solutionAccepted;
}
+ public boolean acceptSolution(VehicleRoutingProblemSolution solution, VehicleRoutingProblemSolution newSolution) {
+ List solutions = new ArrayList<>();
+ solutions.add(solution);
+ boolean solutionAccepted = false;
+ if (solutions.size() < solutionMemory) {
+ solutions.add(newSolution);
+ solutionAccepted = true;
+ } else {
+ VehicleRoutingProblemSolution worst = null;
+ double threshold = getThreshold(currentIteration);
+ for (VehicleRoutingProblemSolution solutionInMemory : solutions) {
+ if (worst == null) worst = solutionInMemory;
+ else if (solutionInMemory.getCost() > worst.getCost()) worst = solutionInMemory;
+ }
+ if (worst == null) {
+ solutions.add(newSolution);
+ solutionAccepted = true;
+ } else if (newSolution.getCost() < worst.getCost() + threshold) {
+ solutions.remove(worst);
+ solutions.add(newSolution);
+ solutionAccepted = true;
+ }
+ }
+ return solutionAccepted;
+ }
+
@Override
public String toString() {
return "[name=SchrimpfAcceptance][alpha=" + alpha + "]";
@@ -136,6 +163,16 @@ public class SchrimpfAcceptance implements SolutionAcceptor, IterationStartsList
this.initialThreshold = initialThreshold;
}
+ public void setMaxIterations(int maxIteration) {
+ this.maxIterations = maxIteration;
+ }
+
+ public void incIteration() {
+ currentIteration++;
+ }
+
+ ;
+
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection solutions) {
reset();
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
index 818767f2..14e33fd3 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
@@ -46,9 +46,7 @@ import com.graphhopper.jsprit.core.util.NoiseMaker;
import com.graphhopper.jsprit.core.util.RandomNumberGeneration;
import com.graphhopper.jsprit.core.util.Solutions;
-import java.util.Collection;
-import java.util.Properties;
-import java.util.Random;
+import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -82,7 +80,9 @@ public class Jsprit {
WORST_BEST("worst_best"),
WORST_REGRET("worst_regret"),
CLUSTER_BEST("cluster_best"),
- CLUSTER_REGRET("cluster_regret");
+ CLUSTER_REGRET("cluster_regret"),
+ STRING_BEST("string_best"),
+ STRING_REGRET("string_regret");
String strategyName;
@@ -112,6 +112,7 @@ public class Jsprit {
WORST_MAX_SHARE("worst.max_share"),
THRESHOLD_ALPHA("threshold.alpha"),
THRESHOLD_INI("threshold.ini"),
+ THRESHOLD_INI_ABS("threshold.ini_abs"),
INSERTION_NOISE_LEVEL("insertion.noise_level"),
INSERTION_NOISE_PROB("insertion.noise_prob"),
RUIN_WORST_NOISE_LEVEL("worst.noise_level"),
@@ -119,7 +120,12 @@ public class Jsprit {
FAST_REGRET("regret.fast"),
MAX_TRANSPORT_COSTS("max_transport_costs"),
CONSTRUCTION("construction"),
- BREAK_SCHEDULING("break_scheduling");
+ BREAK_SCHEDULING("break_scheduling"),
+ STRING_K_MIN("string_kmin"),
+ STRING_K_MAX("string_kmax"),
+ STRING_L_MIN("string_lmin"),
+ STRING_L_MAX("string_lmax");
+
String paraName;
@@ -161,6 +167,12 @@ public class Jsprit {
private SolutionAcceptor solutionAcceptor;
+ private ScoringFunction regretScorer = null;
+
+ private Map customStrategies = new HashMap<>();
+
+ private VehicleFleetManager fleetManager = null;
+
public static Builder newInstance(VehicleRoutingProblem vrp) {
return new Builder(vrp);
}
@@ -176,10 +188,21 @@ public class Jsprit {
defaults.put(Strategy.RADIAL_REGRET.toString(), ".5");
defaults.put(Strategy.RANDOM_BEST.toString(), ".5");
defaults.put(Strategy.RANDOM_REGRET.toString(), ".5");
+
+ defaults.put(Strategy.STRING_BEST.toString(), "0.0");
+ defaults.put(Strategy.STRING_REGRET.toString(), "0.0");
+
+ defaults.put(Parameter.STRING_K_MIN.toString(), "1");
+ defaults.put(Parameter.STRING_K_MAX.toString(), "6");
+ defaults.put(Parameter.STRING_L_MIN.toString(), "10");
+ defaults.put(Parameter.STRING_L_MAX.toString(), "30");
+
defaults.put(Strategy.WORST_BEST.toString(), "0.");
defaults.put(Strategy.WORST_REGRET.toString(), "1.");
defaults.put(Strategy.CLUSTER_BEST.toString(), "0.");
defaults.put(Strategy.CLUSTER_REGRET.toString(), "1.");
+
+
defaults.put(Parameter.FIXED_COST_PARAM.toString(), "0.");
defaults.put(Parameter.VEHICLE_SWITCH.toString(), "true");
defaults.put(Parameter.ITERATIONS.toString(), "2000");
@@ -214,6 +237,16 @@ public class Jsprit {
}
+ public Builder addSearchStrategy(SearchStrategy searchStrategy, double weight) {
+ customStrategies.put(searchStrategy, weight);
+ return this;
+ }
+
+ public Builder setVehicleFleetManager(VehicleFleetManager fleetManager) {
+ this.fleetManager = fleetManager;
+ return this;
+ }
+
public Builder setExecutorService(ExecutorService es, int noThreads) {
this.es = es;
this.noThreads = noThreads;
@@ -266,6 +299,11 @@ public class Jsprit {
return this;
}
+ public Builder setRegretScorer(ScoringFunction scoringFunction) {
+ this.regretScorer = scoringFunction;
+ return this;
+ }
+
public VehicleRoutingAlgorithm buildAlgorithm() {
return new Jsprit(this).create(vrp);
}
@@ -328,6 +366,12 @@ public class Jsprit {
private SolutionAcceptor acceptor;
+ private ScoringFunction regretScorer;
+
+ private final Map customStrategies = new HashMap<>();
+
+ private VehicleFleetManager vehicleFleetManager;
+
private Jsprit(Builder builder) {
this.stateManager = builder.stateManager;
this.constraintManager = builder.constraintManager;
@@ -339,16 +383,25 @@ public class Jsprit {
this.random = builder.random;
this.activityInsertion = builder.activityInsertionCalculator;
this.acceptor = builder.solutionAcceptor;
+ regretScorer = builder.regretScorer;
+ customStrategies.putAll(builder.customStrategies);
+ vehicleFleetManager = builder.fleetManager;
+ }
+
+ private void ini(VehicleRoutingProblem vrp) {
+ if (regretScorer == null) regretScorer = getRegretScorer(vrp);
}
private VehicleRoutingAlgorithm create(final VehicleRoutingProblem vrp) {
- VehicleFleetManager fm;
- if (vrp.getFleetSize().equals(VehicleRoutingProblem.FleetSize.INFINITE)) {
- fm = new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
- } else {
- FiniteFleetManagerFactory finiteFleetManagerFactory = new FiniteFleetManagerFactory(vrp.getVehicles());
- finiteFleetManagerFactory.setRandom(random);
- fm = finiteFleetManagerFactory.createFleetManager();
+ ini(vrp);
+ if (vehicleFleetManager == null) {
+ if (vrp.getFleetSize().equals(VehicleRoutingProblem.FleetSize.INFINITE)) {
+ vehicleFleetManager = new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
+ } else {
+ FiniteFleetManagerFactory finiteFleetManagerFactory = new FiniteFleetManagerFactory(vrp.getVehicles());
+ finiteFleetManagerFactory.setRandom(random);
+ vehicleFleetManager = finiteFleetManagerFactory.createFleetManager();
+ }
}
if (stateManager == null) {
@@ -368,6 +421,14 @@ public class Jsprit {
}
}
+ double fixedCostParam = toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()));
+ IncreasingAbsoluteFixedCosts increasingAbsoluteFixedCosts = null;
+ if (fixedCostParam > 0d) {
+ increasingAbsoluteFixedCosts = new IncreasingAbsoluteFixedCosts(vrp.getJobs().size());
+ increasingAbsoluteFixedCosts.setWeightOfFixCost(fixedCostParam);
+ constraintManager.addConstraint(increasingAbsoluteFixedCosts);
+ }
+
double noiseLevel = toDouble(getProperty(Parameter.INSERTION_NOISE_LEVEL.toString()));
double noiseProbability = toDouble(getProperty(Parameter.INSERTION_NOISE_PROB.toString()));
@@ -449,13 +510,23 @@ public class Jsprit {
random)
);
+ int kMin = toInteger(properties.getProperty(Parameter.STRING_K_MIN.toString()));
+ int kMax = toInteger(properties.getProperty(Parameter.STRING_K_MAX.toString()));
+ int lMin = toInteger(properties.getProperty(Parameter.STRING_L_MIN.toString()));
+ int lMax = toInteger(properties.getProperty(Parameter.STRING_L_MAX.toString()));
+
+ final RuinString stringRuin = new RuinString(vrp, jobNeighborhoods);
+ stringRuin.setNoRoutes(kMin, kMax);
+ stringRuin.setStringLength(lMin, lMax);
+ stringRuin.setRandom(random);
+
AbstractInsertionStrategy regret;
- final DefaultScorer scorer;
+ final ScoringFunction scorer;
boolean fastRegret = Boolean.parseBoolean(getProperty(Parameter.FAST_REGRET.toString()));
if (es != null) {
if(fastRegret){
- RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setConcurrentMode(es, noThreads)
.setFastRegret(true)
@@ -463,45 +534,45 @@ public class Jsprit {
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
.build();
- scorer = getRegretScorer(vrp);
+ scorer = regretScorer;
regretInsertion.setScoringFunction(scorer);
regretInsertion.setDependencyTypes(constraintManager.getDependencyTypes());
regret = regretInsertion;
}
else {
- RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setConcurrentMode(es, noThreads)
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
.build();
- scorer = getRegretScorer(vrp);
+ scorer = regretScorer;
regretInsertion.setScoringFunction(scorer);
regret = regretInsertion;
}
} else {
if(fastRegret) {
- RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setFastRegret(true)
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
.build();
- scorer = getRegretScorer(vrp);
+ scorer = regretScorer;
regretInsertion.setScoringFunction(scorer);
regretInsertion.setDependencyTypes(constraintManager.getDependencyTypes());
regret = regretInsertion;
}
else{
- RegretInsertion regretInsertion = (RegretInsertion) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ RegretInsertion regretInsertion = (RegretInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.REGRET)
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
.build();
- scorer = getRegretScorer(vrp);
+ scorer = regretScorer;
regretInsertion.setScoringFunction(scorer);
regret = regretInsertion;
}
@@ -510,7 +581,7 @@ public class Jsprit {
AbstractInsertionStrategy best;
if (vrp.getJobs().size() < 250 || es == null) {
- BestInsertion bestInsertion = (BestInsertion) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ BestInsertion bestInsertion = (BestInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.BEST)
.considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
@@ -518,7 +589,7 @@ public class Jsprit {
.build();
best = bestInsertion;
} else {
- BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionBuilder(vrp, fm, stateManager, constraintManager)
+ BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionBuilder.Strategy.BEST)
.considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
@@ -532,15 +603,19 @@ public class Jsprit {
IterationStartsListener schrimpfThreshold = null;
if(acceptor == null) {
final SchrimpfAcceptance schrimpfAcceptance = new SchrimpfAcceptance(1, toDouble(getProperty(Parameter.THRESHOLD_ALPHA.toString())));
- schrimpfThreshold = new IterationStartsListener() {
- @Override
- public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
- if (i == 1) {
- double initialThreshold = Solutions.bestOf(solutions).getCost() * toDouble(getProperty(Parameter.THRESHOLD_INI.toString()));
- schrimpfAcceptance.setInitialThreshold(initialThreshold);
+ if (properties.containsKey(Parameter.THRESHOLD_INI_ABS.toString())) {
+ schrimpfAcceptance.setInitialThreshold(Double.valueOf(properties.getProperty(Parameter.THRESHOLD_INI_ABS.toString())));
+ } else {
+ schrimpfThreshold = new IterationStartsListener() {
+ @Override
+ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
+ if (i == 1) {
+ double initialThreshold = Solutions.bestOf(solutions).getCost() * toDouble(getProperty(Parameter.THRESHOLD_INI.toString()));
+ schrimpfAcceptance.setInitialThreshold(initialThreshold);
+ }
}
- }
- };
+ };
+ }
acceptor = schrimpfAcceptance;
}
@@ -569,8 +644,13 @@ public class Jsprit {
final SearchStrategy clusters_best = new SearchStrategy(Strategy.CLUSTER_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
clusters_best.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters));
+ SearchStrategy stringRegret = new SearchStrategy(Strategy.STRING_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ stringRegret.addModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin));
- PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, fm, stateManager, constraintManager);
+ SearchStrategy stringBest = new SearchStrategy(Strategy.STRING_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ stringBest.addModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin));
+
+ PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, vehicleFleetManager, stateManager, constraintManager);
prettyBuilder.setRandom(random);
if (addCoreConstraints) {
prettyBuilder.addCoreStateAndConstraintStuff();
@@ -582,7 +662,14 @@ public class Jsprit {
.withStrategy(worst_best, toDouble(getProperty(Strategy.WORST_BEST.toString())))
.withStrategy(worst_regret, toDouble(getProperty(Strategy.WORST_REGRET.toString())))
.withStrategy(clusters_regret, toDouble(getProperty(Strategy.CLUSTER_REGRET.toString())))
- .withStrategy(clusters_best, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())));
+ .withStrategy(clusters_best, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())))
+ .withStrategy(stringBest, toDouble(getProperty(Strategy.STRING_BEST.toString())))
+ .withStrategy(stringRegret, toDouble(getProperty(Strategy.STRING_REGRET.toString())));
+
+ for (SearchStrategy customStrategy : customStrategies.keySet()) {
+ prettyBuilder.withStrategy(customStrategy, customStrategies.get(customStrategy));
+ }
+
if (getProperty(Parameter.CONSTRUCTION.toString()).equals(Construction.BEST_INSERTION.toString())) {
prettyBuilder.constructInitialSolutionWith(best, objectiveFunction);
} else {
@@ -598,6 +685,7 @@ public class Jsprit {
vra.addListener(noiseConfigurator);
vra.addListener(noise);
vra.addListener(clusters);
+ if (increasingAbsoluteFixedCosts != null) vra.addListener(increasingAbsoluteFixedCosts);
if(toBoolean(getProperty(Parameter.BREAK_SCHEDULING.toString()))) {
vra.addListener(new BreakScheduling(vrp, stateManager, constraintManager));
@@ -619,25 +707,36 @@ public class Jsprit {
private void handleExecutorShutdown(VehicleRoutingAlgorithm vra) {
if (setupExecutorInternally) {
+ final Thread hook = new Thread() {
+ public void run() {
+ if (!es.isShutdown()) {
+ System.err.println("shutdownHook shuts down executorService");
+ es.shutdown();
+ }
+ }
+ };
+ Runtime.getRuntime().addShutdownHook(hook);
vra.addListener(new AlgorithmEndsListener() {
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
es.shutdown();
+ Runtime.getRuntime().removeShutdownHook(hook);
}
});
}
- if (es != null) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
- public void run() {
- if (!es.isShutdown()) {
- System.err.println("shutdowHook shuts down executorService");
- es.shutdown();
- }
- }
- });
- }
+// if (es != null) {
+//
+// Runtime.getRuntime().addShutdownHook(hook);
+// vra.addListener(new AlgorithmEndsListener() {
+// @Override
+// public void informAlgorithmEnds(VehicleRoutingProblem aProblem,
+// Collection aSolutions) {
+// Runtime.getRuntime().removeShutdownHook(hook);
+// }
+// });
+// }
}
String getProperty(String key) {
@@ -685,7 +784,7 @@ public class Jsprit {
}
}
for(Job j : solution.getUnassignedJobs()){
- costs += maxCosts * 2 * (4 - j.getPriority());
+ costs += maxCosts * 2 * (11 - j.getPriority());
}
return costs;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
new file mode 100644
index 00000000..6f3d54a4
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.recreate;
+
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
+import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint;
+import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Created by schroeder on 06/02/17.
+ */
+abstract class AbstractInsertionCalculator implements JobInsertionCostsCalculator {
+
+ InsertionData checkRouteContraints(JobInsertionContext insertionContext, ConstraintManager constraintManager) {
+ for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) {
+ if (!hardRouteConstraint.fulfilled(insertionContext)) {
+ InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+ emptyInsertionData.addFailedConstrainName(hardRouteConstraint.getClass().getSimpleName());
+ return emptyInsertionData;
+ }
+ }
+ return null;
+ }
+
+ ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) {
+ ConstraintsStatus notFulfilled = null;
+ List failed = new ArrayList<>();
+ for (HardActivityConstraint c : constraintManager.getCriticalHardActivityConstraints()) {
+ ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
+ if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) {
+ failedActivityConstraints.add(c.getClass().getSimpleName());
+ return status;
+ } else {
+ if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
+ failed.add(c.getClass().getSimpleName());
+ notFulfilled = status;
+ }
+ }
+ }
+ if (notFulfilled != null) {
+ failedActivityConstraints.addAll(failed);
+ return notFulfilled;
+ }
+
+ for (HardActivityConstraint c : constraintManager.getHighPrioHardActivityConstraints()) {
+ ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
+ if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) {
+ failedActivityConstraints.add(c.getClass().getSimpleName());
+ return status;
+ } else {
+ if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
+ failed.add(c.getClass().getSimpleName());
+ notFulfilled = status;
+ }
+ }
+ }
+ if (notFulfilled != null) {
+ failedActivityConstraints.addAll(failed);
+ return notFulfilled;
+ }
+
+ for (HardActivityConstraint constraint : constraintManager.getLowPrioHardActivityConstraints()) {
+ ConstraintsStatus status = constraint.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
+ if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK) || status.equals(ConstraintsStatus.NOT_FULFILLED)) {
+ failedActivityConstraints.add(constraint.getClass().getSimpleName());
+ return status;
+ }
+ }
+ return ConstraintsStatus.FULFILLED;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
index 7d794a6d..5f5973f5 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
@@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.Random;
public abstract class AbstractInsertionStrategy implements InsertionStrategy {
@@ -92,6 +93,10 @@ public abstract class AbstractInsertionStrategy implements InsertionStrategy {
return badJobs;
}
+ public void markUnassigned(Job unassigned, List reasons) {
+ insertionsListeners.informJobUnassignedListeners(unassigned, reasons);
+ }
+
public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs);
@Override
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AccordingToPriorities.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AccordingToPriorities.java
new file mode 100644
index 00000000..928e41a8
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AccordingToPriorities.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.recreate;
+
+import com.graphhopper.jsprit.core.problem.job.Job;
+
+import java.util.Comparator;
+
+/**
+ * Created by schroeder on 30/06/17.
+ */
+class AccordingToPriorities implements Comparator {
+
+ @Override
+ public int compare(Job o1, Job o2) {
+ return o1.getPriority() - o2.getPriority();
+ }
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
index a78205ae..576e41f4 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
@@ -24,7 +24,10 @@ import com.graphhopper.jsprit.core.util.NoiseMaker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
/**
@@ -63,13 +66,15 @@ public final class BestInsertion extends AbstractInsertionStrategy {
List badJobs = new ArrayList(unassignedJobs.size());
List unassignedJobList = new ArrayList(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
- sometimesSortPriorities(unassignedJobList);
+ Collections.sort(unassignedJobList, new AccordingToPriorities());
for (Job unassignedJob : unassignedJobList) {
Insertion bestInsertion = null;
+ InsertionData empty = new InsertionData.NoInsertionFound();
double bestInsertionCost = Double.MAX_VALUE;
for (VehicleRoute vehicleRoute : vehicleRoutes) {
InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost);
if (iData instanceof InsertionData.NoInsertionFound) {
+ empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
continue;
}
if (iData.getInsertionCost() < bestInsertionCost + noiseMaker.makeNoise()) {
@@ -84,23 +89,16 @@ public final class BestInsertion extends AbstractInsertionStrategy {
bestInsertion = new Insertion(newRoute, newIData);
vehicleRoutes.add(newRoute);
}
+ } else {
+ empty.getFailedConstraintNames().addAll(newIData.getFailedConstraintNames());
+ }
+ if (bestInsertion == null) {
+ badJobs.add(unassignedJob);
+ markUnassigned(unassignedJob, empty.getFailedConstraintNames());
}
- if (bestInsertion == null) badJobs.add(unassignedJob);
else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
-// nextInsertion();
}
return badJobs;
}
- private void sometimesSortPriorities(List unassignedJobList) {
- if(random.nextDouble() < 0.5){
- Collections.sort(unassignedJobList, new Comparator() {
- @Override
- public int compare(Job o1, Job o2) {
- return o1.getPriority() - o2.getPriority();
- }
- });
- }
- }
-
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
index 52d913df..c7e6ae94 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
@@ -27,7 +27,10 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.*;
@@ -99,8 +102,9 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
List badJobs = new ArrayList(unassignedJobs.size());
List unassignedJobList = new ArrayList(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
- sometimesSortPriorities(unassignedJobList);
+ Collections.sort(unassignedJobList, new AccordingToPriorities());
List batches = distributeRoutes(vehicleRoutes, nuOfBatches);
+ List failedConstraintNames = new ArrayList<>();
for (final Job unassignedJob : unassignedJobList) {
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
@@ -118,7 +122,10 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
for (int i = 0; i < batches.size(); i++) {
Future futureIData = completionService.take();
Insertion insertion = futureIData.get();
- if (insertion == null) continue;
+ if (insertion.insertionData instanceof NoInsertionFound) {
+ failedConstraintNames.addAll(insertion.getInsertionData().getFailedConstraintNames());
+ continue;
+ }
if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) {
bestInsertion = insertion;
bestInsertionCost = insertion.getInsertionData().getInsertionCost();
@@ -136,29 +143,24 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
vehicleRoutes.add(newRoute);
batches.get(random.nextInt(batches.size())).routes.add(newRoute);
}
- if (bestInsertion == null) badJobs.add(unassignedJob);
+ if (bestInsertion == null) {
+ badJobs.add(unassignedJob);
+ markUnassigned(unassignedJob, failedConstraintNames);
+ }
else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
}
return badJobs;
}
- private void sometimesSortPriorities(List unassignedJobList) {
- if(random.nextDouble() < 0.5){
- Collections.sort(unassignedJobList, new Comparator() {
- @Override
- public int compare(Job o1, Job o2) {
- return o1.getPriority() - o2.getPriority();
- }
- });
- }
- }
private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
Insertion bestInsertion = null;
+ InsertionData empty = new InsertionData.NoInsertionFound();
double bestInsertionCost = Double.MAX_VALUE;
for (VehicleRoute vehicleRoute : batch.routes) {
InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost);
if (iData instanceof NoInsertionFound) {
+ empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
continue;
}
if (iData.getInsertionCost() < bestInsertionCost) {
@@ -166,6 +168,7 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
bestInsertionCost = iData.getInsertionCost();
}
}
+ if (bestInsertion == null) return new Insertion(null, empty);
return bestInsertion;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java
index 62716618..44401a8b 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java
@@ -32,7 +32,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Random;
-
+@Deprecated
class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsCalculator {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeSchedulingInSlices.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeSchedulingInSlices.java
index 099b6b24..05d9c6c3 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeSchedulingInSlices.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeSchedulingInSlices.java
@@ -27,7 +27,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-
+@Deprecated
class CalculatesServiceInsertionWithTimeSchedulingInSlices implements JobInsertionCostsCalculator {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DecreasingRelativeFixedCosts.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DecreasingRelativeFixedCosts.java
new file mode 100644
index 00000000..4f5b67f4
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DecreasingRelativeFixedCosts.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.graphhopper.jsprit.core.algorithm.recreate;
+
+import com.graphhopper.jsprit.core.algorithm.state.InternalStates;
+import com.graphhopper.jsprit.core.problem.Capacity;
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
+import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter;
+import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public final class DecreasingRelativeFixedCosts extends SolutionCompletenessRatio implements SoftRouteConstraint {
+
+ private static final Logger logger = LoggerFactory.getLogger(DecreasingRelativeFixedCosts.class);
+
+ private double weightDeltaFixCost = 0.5;
+
+ private RouteAndActivityStateGetter stateGetter;
+
+ public DecreasingRelativeFixedCosts(RouteAndActivityStateGetter stateGetter, int noJobs) {
+ super(noJobs);
+ this.stateGetter = stateGetter;
+ logger.debug("initialise {}", this);
+ }
+
+
+ public void setWeightOfFixCost(double weight) {
+ weightDeltaFixCost = weight;
+ logger.debug("set weightOfFixCostSaving to {}", weight);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=DecreasingRelativeFixedCosts][weightOfFixedCostSavings=" + weightDeltaFixCost + "]";
+ }
+
+ private Capacity getCurrentMaxLoadInRoute(VehicleRoute route) {
+ Capacity maxLoad = stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class);
+ if (maxLoad == null) maxLoad = Capacity.Builder.newInstance().build();
+ return maxLoad;
+ }
+
+ @Override
+ public double getCosts(JobInsertionContext insertionContext) {
+ VehicleRoute route = insertionContext.getRoute();
+ Capacity currentLoad = getCurrentMaxLoadInRoute(route);
+ Capacity load = Capacity.addup(currentLoad, insertionContext.getJob().getSize());
+ double currentRelFix = 0d;
+ if (route.getVehicle() != null && !(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
+ currentRelFix = route.getVehicle().getType().getVehicleCostParams().fix * Capacity.divide(currentLoad, route.getVehicle().getType().getCapacityDimensions());
+ }
+ double newRelFix = insertionContext.getNewVehicle().getType().getVehicleCostParams().fix * (Capacity.divide(load, insertionContext.getNewVehicle().getType().getCapacityDimensions()));
+ double decreasingRelativeFixedCosts = (1 - solutionCompletenessRatio) * (newRelFix - currentRelFix);
+ return weightDeltaFixCost * solutionCompletenessRatio * decreasingRelativeFixedCosts;
+ }
+
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
index 4ed97ed6..a08fe6d7 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
@@ -31,14 +31,14 @@ public class DellAmicoFixCostCalculator implements SoftRouteConstraint, Insertio
private int nuOfJobsToRecreate;
- private final JobInsertionConsideringFixCostsCalculator calculator;
+ private final IncreasingAbsoluteFixedCosts calculator;
private final int nuOfJobs;
public DellAmicoFixCostCalculator(final int nuOfJobs, final RouteAndActivityStateGetter stateGetter) {
super();
this.nuOfJobs = nuOfJobs;
- calculator = new JobInsertionConsideringFixCostsCalculator(null, stateGetter);
+ calculator = new IncreasingAbsoluteFixedCosts(nuOfJobs);
}
@Override
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/IncreasingAbsoluteFixedCosts.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/IncreasingAbsoluteFixedCosts.java
new file mode 100644
index 00000000..ffaf2674
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/IncreasingAbsoluteFixedCosts.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.graphhopper.jsprit.core.algorithm.recreate;
+
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
+import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public final class IncreasingAbsoluteFixedCosts extends SolutionCompletenessRatio implements SoftRouteConstraint {
+
+ private static final Logger logger = LoggerFactory.getLogger(IncreasingAbsoluteFixedCosts.class);
+
+ private double weightDeltaFixCost = 0.5;
+
+ public IncreasingAbsoluteFixedCosts(int noJobs) {
+ super(noJobs);
+ logger.debug("initialise {}", this);
+ }
+
+
+ public void setWeightOfFixCost(double weight) {
+ weightDeltaFixCost = weight;
+ logger.debug("set weightOfFixCostSaving to {}", weight);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=IncreasingAbsoluteFixedCosts][weightOfFixedCostSavings=" + weightDeltaFixCost + "]";
+ }
+
+ @Override
+ public double getCosts(JobInsertionContext insertionContext) {
+ final VehicleRoute currentRoute = insertionContext.getRoute();
+ double currentFix = 0d;
+ if (currentRoute.getVehicle() != null && !(currentRoute.getVehicle() instanceof VehicleImpl.NoVehicle)) {
+ currentFix = currentRoute.getVehicle().getType().getVehicleCostParams().fix;
+ }
+ double increasingAbsoluteFixedCosts = solutionCompletenessRatio * (insertionContext.getNewVehicle().getType().getVehicleCostParams().fix - currentFix);
+ return weightDeltaFixCost * solutionCompletenessRatio * increasingAbsoluteFixedCosts;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
index d5b5ef8d..fd78b3e9 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
@@ -31,6 +31,7 @@ import java.util.List;
*/
public class InsertionData {
+
public static class NoInsertionFound extends InsertionData {
public NoInsertionFound() {
@@ -75,6 +76,8 @@ public class InsertionData {
return events;
}
+ private List reasons = new ArrayList<>();
+
/**
* @return the additionalTime
*/
@@ -82,6 +85,14 @@ public class InsertionData {
return additionalTime;
}
+ public void addFailedConstrainName(String name) {
+ reasons.add(name);
+ }
+
+ public List getFailedConstraintNames() {
+ return reasons;
+ }
+
/**
* @param additionalTime the additionalTime to set
*/
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
index ced87182..8d019e2c 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
@@ -33,7 +33,7 @@ class InsertionDataUpdater {
static boolean update(boolean addAllAvailable, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, TreeSet insertionDataSet, int updateRound, Job unassignedJob, Collection routes) {
for(VehicleRoute route : routes) {
- Collection relevantVehicles = new ArrayList();
+ Collection relevantVehicles = new ArrayList<>();
if (!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
relevantVehicles.add(route.getVehicle());
if(addAllAvailable && !initialVehicleIds.contains(route.getVehicle().getId())){
@@ -71,7 +71,7 @@ class InsertionDataUpdater {
};
}
- static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
+ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
ScoredJob bestScoredJob = null;
for(Job j : unassignedJobList){
VehicleRoute bestRoute = null;
@@ -79,6 +79,7 @@ class InsertionDataUpdater {
InsertionData secondBest = null;
TreeSet priorityQueue = priorityQueues[j.getIndex()];
Iterator iterator = priorityQueue.iterator();
+ List failedConstraintNames = new ArrayList<>();
while(iterator.hasNext()){
VersionedInsertionData versionedIData = iterator.next();
if(bestRoute != null){
@@ -86,7 +87,10 @@ class InsertionDataUpdater {
continue;
}
}
- if(versionedIData.getiData() instanceof InsertionData.NoInsertionFound) continue;
+ if (versionedIData.getiData() instanceof InsertionData.NoInsertionFound) {
+ failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraintNames());
+ continue;
+ }
if(!(versionedIData.getRoute().getVehicle() instanceof VehicleImpl.NoVehicle)) {
if (versionedIData.getiData().getSelectedVehicle() != versionedIData.getRoute().getVehicle()) {
if (!switchAllowed) continue;
@@ -136,9 +140,9 @@ class InsertionDataUpdater {
} else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
secondBest = iData;
}
- }
+ } else failedConstraintNames.addAll(iData.getFailedConstraintNames());
if (best == null) {
- badJobs.add(j);
+ badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames));
continue;
}
double score = score(j, best, secondBest, scoringFunction);
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculator.java
deleted file mode 100644
index 19e7cfd9..00000000
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculator.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to GraphHopper GmbH under one or more contributor
- * license agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * GraphHopper GmbH licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.graphhopper.jsprit.core.algorithm.recreate;
-
-import com.graphhopper.jsprit.core.algorithm.state.InternalStates;
-import com.graphhopper.jsprit.core.problem.Capacity;
-import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
-import com.graphhopper.jsprit.core.problem.driver.Driver;
-import com.graphhopper.jsprit.core.problem.job.Job;
-import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
-import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
-import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter;
-import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
-import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-final class JobInsertionConsideringFixCostsCalculator implements JobInsertionCostsCalculator, SoftRouteConstraint {
-
- private static final Logger logger = LoggerFactory.getLogger(JobInsertionConsideringFixCostsCalculator.class);
-
- private final JobInsertionCostsCalculator standardServiceInsertion;
-
- private double weight_deltaFixCost = 0.5;
-
- private double solution_completeness_ratio = 0.5;
-
- private RouteAndActivityStateGetter stateGetter;
-
- public JobInsertionConsideringFixCostsCalculator(final JobInsertionCostsCalculator standardInsertionCalculator, RouteAndActivityStateGetter stateGetter) {
- super();
- this.standardServiceInsertion = standardInsertionCalculator;
- this.stateGetter = stateGetter;
- logger.debug("inialise {}", this);
- }
-
- @Override
- public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownPrice) {
- double fixcost_contribution = getFixCostContribution(currentRoute, jobToInsert, newVehicle);
- if (fixcost_contribution > bestKnownPrice) {
- return InsertionData.createEmptyInsertionData();
- }
- InsertionData iData = standardServiceInsertion.getInsertionData(currentRoute, jobToInsert, newVehicle, newVehicleDepartureTime, newDriver, bestKnownPrice);
- if (iData instanceof InsertionData.NoInsertionFound) {
- return iData;
- }
- double totalInsertionCost = iData.getInsertionCost() + fixcost_contribution;
- InsertionData insertionData = new InsertionData(totalInsertionCost, iData.getPickupInsertionIndex(), iData.getDeliveryInsertionIndex(), newVehicle, newDriver);
- insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
- insertionData.getEvents().addAll(iData.getEvents());
- return insertionData;
- }
-
- private double getFixCostContribution(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle) {
- Capacity currentMaxLoadInRoute = getCurrentMaxLoadInRoute(currentRoute);
- double relFixCost = getDeltaRelativeFixCost(currentRoute, newVehicle, jobToInsert,currentMaxLoadInRoute);
- double absFixCost = getDeltaAbsoluteFixCost(currentRoute, newVehicle, jobToInsert,currentMaxLoadInRoute);
- double deltaFixCost = (1 - solution_completeness_ratio) * relFixCost + solution_completeness_ratio * absFixCost;
- double fixcost_contribution = weight_deltaFixCost * solution_completeness_ratio * deltaFixCost;
- return fixcost_contribution;
- }
-
- public void setWeightOfFixCost(double weight) {
- weight_deltaFixCost = weight;
- logger.debug("set weightOfFixCostSaving to {}", weight);
- }
-
- @Override
- public String toString() {
- return "[name=calculatesServiceInsertionConsideringFixCost][weightOfFixedCostSavings=" + weight_deltaFixCost + "]";
- }
-
- public void setSolutionCompletenessRatio(double ratio) {
- solution_completeness_ratio = ratio;
- }
-
- public double getSolutionCompletenessRatio() { return solution_completeness_ratio; }
-
- private double getDeltaAbsoluteFixCost(VehicleRoute route, Vehicle newVehicle, Job job, Capacity currentMaxLoadInRoute) {
- Capacity load = Capacity.addup(currentMaxLoadInRoute, job.getSize());
- double currentFix = 0.0;
- if (route.getVehicle() != null) {
- if (!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
- currentFix += route.getVehicle().getType().getVehicleCostParams().fix;
- }
- }
- if (!newVehicle.getType().getCapacityDimensions().isGreaterOrEqual(load)) {
- return Double.MAX_VALUE;
- }
- return newVehicle.getType().getVehicleCostParams().fix - currentFix;
- }
-
- private double getDeltaRelativeFixCost(VehicleRoute route, Vehicle newVehicle, Job job, Capacity currentLoad) {
- Capacity load = Capacity.addup(currentLoad, job.getSize());
- double currentRelFix = 0.0;
- if (route.getVehicle() != null) {
- if (!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
- currentRelFix += route.getVehicle().getType().getVehicleCostParams().fix * Capacity.divide(currentLoad, route.getVehicle().getType().getCapacityDimensions());
- }
- }
- if (!newVehicle.getType().getCapacityDimensions().isGreaterOrEqual(load)) {
- return Double.MAX_VALUE;
- }
- double relativeFixCost = newVehicle.getType().getVehicleCostParams().fix * (Capacity.divide(load, newVehicle.getType().getCapacityDimensions())) - currentRelFix;
- return relativeFixCost;
- }
-
- private Capacity getCurrentMaxLoadInRoute(VehicleRoute route) {
- Capacity maxLoad = stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class);
- if (maxLoad == null) maxLoad = Capacity.Builder.newInstance().build();
- return maxLoad;
- }
-
- @Override
- public double getCosts(JobInsertionContext insertionContext) {
- return getFixCostContribution(insertionContext.getRoute(), insertionContext.getJob(), insertionContext.getNewVehicle());
- }
-
-}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java
index 01284d80..1135adb8 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java
@@ -218,10 +218,10 @@ public class JobInsertionCostsCalculatorBuilder {
addAlgorithmListeners(standardLocal.getAlgorithmListener());
addInsertionListeners(standardLocal.getInsertionListener());
if (considerFixedCost) {
- CalculatorPlusListeners withFixed = createCalculatorConsideringFixedCosts(vrp, baseCalculator, states, weightOfFixedCost);
- baseCalculator = withFixed.getCalculator();
- addAlgorithmListeners(withFixed.getAlgorithmListener());
- addInsertionListeners(withFixed.getInsertionListener());
+// CalculatorPlusListeners withFixed = createCalculatorConsideringFixedCosts(vrp, baseCalculator, states, weightOfFixedCost);
+// baseCalculator = withFixed.getCalculator();
+// addAlgorithmListeners(withFixed.getAlgorithmListener());
+// addInsertionListeners(withFixed.getInsertionListener());
}
if (timeScheduling) {
// baseCalculator = new CalculatesServiceInsertionWithTimeSchedulingInSlices(baseCalculator,timeSlice,neighbors);
@@ -309,14 +309,6 @@ public class JobInsertionCostsCalculatorBuilder {
return calculatorPlusListeners;
}
- private CalculatorPlusListeners createCalculatorConsideringFixedCosts(VehicleRoutingProblem vrp, JobInsertionCostsCalculator baseCalculator, RouteAndActivityStateGetter activityStates2, double weightOfFixedCosts) {
- final JobInsertionConsideringFixCostsCalculator withFixCost = new JobInsertionConsideringFixCostsCalculator(baseCalculator, activityStates2);
- withFixCost.setWeightOfFixCost(weightOfFixedCosts);
- CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(withFixCost);
- calcPlusListeners.getInsertionListener().add(new ConfigureFixCostCalculator(vrp, withFixCost));
- return calcPlusListeners;
- }
-
private CalculatorPlusListeners createStandardRoute(final VehicleRoutingProblem vrp, RouteAndActivityStateGetter activityStates2, int forwardLooking, int solutionMemory) {
ActivityInsertionCostsCalculator routeLevelCostEstimator;
if (activityInsertionCostCalculator == null && addDefaultCostCalc) {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java
index cdd7dac2..0f91eab1 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java
@@ -22,6 +22,7 @@ import com.graphhopper.jsprit.core.algorithm.state.InternalStates;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter;
@@ -77,7 +78,9 @@ class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCal
double oldCosts = 0.;
if (iFacts.getRoute().isEmpty()) {
- double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocation(), nextAct.getLocation(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
+ double tp_costs_prevAct_nextAct = 0.;
+ if (newAct instanceof DeliverShipment)
+ tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocation(), nextAct.getLocation(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
oldCosts += tp_costs_prevAct_nextAct;
} else {
double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocation(), nextAct.getLocation(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
index 6e405dec..30a7a98a 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
@@ -105,10 +105,10 @@ public class RegretInsertion extends AbstractInsertionStrategy {
}
}
- List jobs = new ArrayList(unassignedJobs);
+ List jobs = new ArrayList<>(unassignedJobs);
while (!jobs.isEmpty()) {
- List unassignedJobList = new ArrayList(jobs);
- List badJobList = new ArrayList();
+ List unassignedJobList = new ArrayList<>(jobs);
+ List badJobList = new ArrayList<>();
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
@@ -117,9 +117,11 @@ public class RegretInsertion extends AbstractInsertionStrategy {
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
}
- for (Job bad : badJobList) {
- jobs.remove(bad);
- badJobs.add(bad);
+ for (ScoredJob bad : badJobList) {
+ Job unassigned = bad.getJob();
+ jobs.remove(unassigned);
+ badJobs.add(unassigned);
+ markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
}
}
return badJobs;
@@ -132,12 +134,12 @@ public class RegretInsertion extends AbstractInsertionStrategy {
return null;
}
- private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) {
+ private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) {
ScoredJob bestScoredJob = null;
for (Job unassignedJob : unassignedJobList) {
ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
if (scoredJob instanceof ScoredJob.BadJob) {
- badJobs.add(unassignedJob);
+ badJobs.add(scoredJob);
continue;
}
if (bestScoredJob == null) bestScoredJob = scoredJob;
@@ -158,14 +160,17 @@ public class RegretInsertion extends AbstractInsertionStrategy {
InsertionData best = null;
InsertionData secondBest = null;
VehicleRoute bestRoute = null;
-
+ List failedConstraintNames = new ArrayList<>();
double benchmark = Double.MAX_VALUE;
for (VehicleRoute route : routes) {
if (secondBest != null) {
benchmark = secondBest.getInsertionCost();
}
InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
- if (iData instanceof InsertionData.NoInsertionFound) continue;
+ if (iData instanceof InsertionData.NoInsertionFound) {
+ failedConstraintNames.addAll(iData.getFailedConstraintNames());
+ continue;
+ }
if (best == null) {
best = iData;
bestRoute = route;
@@ -191,9 +196,10 @@ public class RegretInsertion extends AbstractInsertionStrategy {
} else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
secondBest = iData;
}
- }
+ } else failedConstraintNames.addAll(iData.getFailedConstraintNames());
if (best == null) {
- return new ScoredJob.BadJob(unassignedJob);
+ ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraintNames);
+ return badJob;
}
double score = score(unassignedJob, best, secondBest, scoringFunction);
ScoredJob scoredJob;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
index fe2146ba..b71ac5ab 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
@@ -109,10 +109,10 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
}
}
- List jobs = new ArrayList(unassignedJobs);
+ List jobs = new ArrayList<>(unassignedJobs);
while (!jobs.isEmpty()) {
- List unassignedJobList = new ArrayList(jobs);
- List badJobList = new ArrayList();
+ List unassignedJobList = new ArrayList<>(jobs);
+ List badJobList = new ArrayList<>();
ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
@@ -121,15 +121,17 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
jobs.remove(bestScoredJob.getJob());
}
- for (Job j : badJobList) {
- jobs.remove(j);
- badJobs.add(j);
+ for (ScoredJob bad : badJobList) {
+ Job unassigned = bad.getJob();
+ jobs.remove(unassigned);
+ badJobs.add(unassigned);
+ markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
}
}
return badJobs;
}
- private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) {
+ private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) {
ScoredJob bestScoredJob = null;
for (final Job unassignedJob : unassignedJobList) {
@@ -148,7 +150,7 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
Future fsj = completionService.take();
ScoredJob sJob = fsj.get();
if (sJob instanceof ScoredJob.BadJob) {
- badJobList.add(sJob.getJob());
+ badJobList.add(sJob);
continue;
}
if (bestScoredJob == null) {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
index 187145f6..be4f5d11 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
@@ -143,8 +143,8 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
int updateRound = 0;
Map updates = new HashMap();
while (!jobs.isEmpty()) {
- List unassignedJobList = new ArrayList(jobs);
- List badJobList = new ArrayList();
+ List unassignedJobList = new ArrayList<>(jobs);
+ List badJobList = new ArrayList<>();
if(!firstRun && lastModified == null) throw new IllegalStateException("ho. this must not be.");
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound,firstRun,lastModified,updates);
if(firstRun) firstRun = false;
@@ -159,9 +159,11 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
lastModified = bestScoredJob.getRoute();
}
else lastModified = null;
- for (Job bad : badJobList) {
- jobs.remove(bad);
- badJobs.add(bad);
+ for (ScoredJob bad : badJobList) {
+ Job unassigned = bad.getJob();
+ jobs.remove(unassigned);
+ badJobs.add(unassigned);
+ markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
}
}
return badJobs;
@@ -172,7 +174,7 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
boolean updatedAllRoutes = false;
for (final Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
- priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator());
+ priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator());
}
if(firstRun) {
updatedAllRoutes = true;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java
index 4369dd01..d805e55d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java
@@ -131,10 +131,10 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
VehicleRoute lastModified = null;
boolean firstRun = true;
int updateRound = 0;
- Map updates = new HashMap();
+ Map updates = new HashMap<>();
while (!jobs.isEmpty()) {
- List unassignedJobList = new ArrayList(jobs);
- List badJobList = new ArrayList();
+ List unassignedJobList = new ArrayList<>(jobs);
+ List badJobList = new ArrayList<>();
if(!firstRun && lastModified == null) throw new IllegalStateException("last modified route is null. this should not be.");
if(firstRun){
updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound, firstRun, lastModified, updates);
@@ -156,9 +156,11 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
lastModified = bestScoredJob.getRoute();
}
else lastModified = null;
- for (Job bad : badJobList) {
- jobs.remove(bad);
- badJobs.add(bad);
+ for (ScoredJob bad : badJobList) {
+ Job unassigned = bad.getJob();
+ jobs.remove(unassigned);
+ badJobs.add(unassigned);
+ markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
}
}
return badJobs;
@@ -167,7 +169,7 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
private void updateInsertionData(TreeSet[] priorityQueues, Collection routes, List unassignedJobList, int updateRound, boolean firstRun, VehicleRoute lastModified, Map updates) {
for (Job unassignedJob : unassignedJobList) {
if(priorityQueues[unassignedJob.getIndex()] == null){
- priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator());
+ priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator());
}
if(firstRun) {
InsertionDataUpdater.update(switchAllowed, initialVehicleIds, fleetManager, insertionCostsCalculator, priorityQueues[unassignedJob.getIndex()], updateRound, unassignedJob, routes);
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RouteLevelActivityInsertionCostsEstimator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RouteLevelActivityInsertionCostsEstimator.java
index e47a48d8..d6fc01b4 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RouteLevelActivityInsertionCostsEstimator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RouteLevelActivityInsertionCostsEstimator.java
@@ -31,7 +31,7 @@ import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivity
import java.util.ArrayList;
import java.util.List;
-
+@Deprecated
class RouteLevelActivityInsertionCostsEstimator implements ActivityInsertionCostsCalculator {
private VehicleRoutingActivityCosts activityCosts;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java
index 48582800..f0f8950f 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java
@@ -21,6 +21,8 @@ package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import java.util.List;
+
/**
* Created by schroeder on 15/10/15.
*/
@@ -28,8 +30,14 @@ class ScoredJob {
static class BadJob extends ScoredJob {
- BadJob(Job job) {
- super(job, 0., null, null, false);
+ BadJob(Job job, List failedConstraintNames) {
+ super(job, 0., getEmptyInsertion(failedConstraintNames), null, false);
+ }
+
+ private static InsertionData getEmptyInsertion(List failedConstraintNames) {
+ InsertionData empty = new InsertionData.NoInsertionFound();
+ empty.getFailedConstraintNames().addAll(failedConstraintNames);
+ return empty;
}
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
index 6aabb134..a6607d26 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
@@ -33,9 +33,9 @@ class Scorer {
if (secondBest == null) { //either there is only one vehicle or there are more vehicles, but they cannot load unassignedJob
//if only one vehicle, I want the job to be inserted with min iCosts
//if there are more vehicles, I want this job to be prioritized since there are no alternatives
- score = (4 - unassignedJob.getPriority()) * (Integer.MAX_VALUE - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
+ score = (11 - unassignedJob.getPriority()) * (Integer.MAX_VALUE - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
} else {
- score = (4 - unassignedJob.getPriority()) * (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
+ score = (11 - unassignedJob.getPriority()) * (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
}
return score;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoringFunction.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoringFunction.java
index 63c44a35..d3c6e103 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoringFunction.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoringFunction.java
@@ -25,6 +25,6 @@ import com.graphhopper.jsprit.core.problem.job.Job;
*/
public interface ScoringFunction {
- public double score(InsertionData best, Job job);
+ double score(InsertionData best, Job job);
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java
index 977ba6ab..9f91ff9b 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java
@@ -18,8 +18,10 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.JobActivityFactory;
-import com.graphhopper.jsprit.core.problem.constraint.*;
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.driver.Driver;
@@ -36,6 +38,8 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
/**
@@ -43,13 +47,13 @@ import java.util.Iterator;
*
* @author schroeder
*/
-final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
+final class ServiceInsertionCalculator extends AbstractInsertionCalculator {
private static final Logger logger = LoggerFactory.getLogger(ServiceInsertionCalculator.class);
- private HardRouteConstraint hardRouteLevelConstraint;
+// private HardRouteConstraint hardRouteLevelConstraint;
- private HardActivityConstraint hardActivityLevelConstraint;
+// private HardActivityConstraint hardActivityLevelConstraint;
private SoftRouteConstraint softRouteConstraint;
@@ -65,12 +69,13 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
private AdditionalAccessEgressCalculator additionalAccessEgressCalculator;
+ private ConstraintManager constraintManager;
+
public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) {
super();
this.transportCosts = routingCosts;
this.activityCosts = activityCosts;
- hardRouteLevelConstraint = constraintManager;
- hardActivityLevelConstraint = constraintManager;
+ this.constraintManager = constraintManager;
softActivityConstraint = constraintManager;
softRouteConstraint = constraintManager;
this.additionalTransportCostsCalculator = additionalTransportCostsCalculator;
@@ -103,9 +108,10 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
/*
check hard constraints at route level
*/
- if (!hardRouteLevelConstraint.fulfilled(insertionContext)) {
- return InsertionData.createEmptyInsertionData();
- }
+ InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager);
+ if (noInsertion != null) return noInsertion;
+
+ Collection failedActivityConstraints = new ArrayList<>();
/*
check soft constraints at route level
@@ -142,7 +148,7 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
ActivityContext activityContext = new ActivityContext();
activityContext.setInsertionIndex(actIndex);
insertionContext.setActivityContext(activityContext);
- ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime);
+ ConstraintsStatus status = fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime, failedActivityConstraints, constraintManager);
if (status.equals(ConstraintsStatus.FULFILLED)) {
double additionalICostsAtActLevel = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime);
double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime);
@@ -163,7 +169,9 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
actIndex++;
}
if(insertionIndex == InsertionData.NO_INDEX) {
- return InsertionData.createEmptyInsertionData();
+ InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+ emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints);
+ return emptyInsertionData;
}
InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(bestTimeWindow.getStart());
@@ -173,4 +181,6 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
+
+
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionOnRouteLevelCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionOnRouteLevelCalculator.java
index b330d689..a600f3f6 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionOnRouteLevelCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionOnRouteLevelCalculator.java
@@ -45,7 +45,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
-
+@Deprecated
final class ServiceInsertionOnRouteLevelCalculator implements JobInsertionCostsCalculator {
private static final Logger logger = LoggerFactory.getLogger(ServiceInsertionOnRouteLevelCalculator.class);
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java
index 87c29c0e..e8698483 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java
@@ -18,8 +18,10 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.JobActivityFactory;
-import com.graphhopper.jsprit.core.problem.constraint.*;
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.driver.Driver;
@@ -36,16 +38,19 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
import java.util.List;
-final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
+final class ShipmentInsertionCalculator extends AbstractInsertionCalculator {
private static final Logger logger = LoggerFactory.getLogger(ShipmentInsertionCalculator.class);
- private HardRouteConstraint hardRouteLevelConstraint;
+ private final ConstraintManager constraintManager;
- private HardActivityConstraint hardActivityLevelConstraint;
+// private HardRouteConstraint hardRouteLevelConstraint;
+//
+// private HardActivityConstraint hardActivityLevelConstraint;
private SoftRouteConstraint softRouteConstraint;
@@ -64,8 +69,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) {
super();
this.activityInsertionCostsCalculator = activityInsertionCostsCalculator;
- this.hardRouteLevelConstraint = constraintManager;
- this.hardActivityLevelConstraint = constraintManager;
+ this.constraintManager = constraintManager;
this.softActivityConstraint = constraintManager;
this.softRouteConstraint = constraintManager;
this.transportCosts = routingCosts;
@@ -99,9 +103,8 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
/*
check hard route constraints
*/
- if (!hardRouteLevelConstraint.fulfilled(insertionContext)) {
- return InsertionData.createEmptyInsertionData();
- }
+ InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager);
+ if (noInsertion != null) return noInsertion;
/*
check soft route constraints
*/
@@ -132,6 +135,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
//pickupShipmentLoop
List activities = currentRoute.getTourActivities().getActivities();
+ List failedActivityConstraints = new ArrayList<>();
while (!tourEnd) {
TourActivity nextAct;
if (i < activities.size()) {
@@ -148,7 +152,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
ActivityContext activityContext = new ActivityContext();
activityContext.setInsertionIndex(i);
insertionContext.setActivityContext(activityContext);
- ConstraintsStatus pickupShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime);
+ ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager);
if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) {
pickupInsertionNotFulfilledBreak = false;
continue;
@@ -194,7 +198,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
ActivityContext activityContext_ = new ActivityContext();
activityContext_.setInsertionIndex(j);
insertionContext.setActivityContext(activityContext_);
- ConstraintsStatus deliverShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
+ ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop, failedActivityConstraints, constraintManager);
if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) {
double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
double deliveryAIC = calculate(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
@@ -230,7 +234,9 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
i++;
}
if (pickupInsertionIndex == InsertionData.NO_INDEX) {
- return InsertionData.createEmptyInsertionData();
+ InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+ emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints);
+ return emptyInsertionData;
}
InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver);
pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart());
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/SolutionCompletenessRatio.java
similarity index 59%
rename from jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculator.java
rename to jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/SolutionCompletenessRatio.java
index 16c7ab03..9f059088 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/SolutionCompletenessRatio.java
@@ -15,50 +15,48 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.graphhopper.jsprit.core.algorithm.recreate;
+package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionStartsListener;
import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobInsertedListener;
-import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import java.util.Collection;
+/**
+ * Created by schroeder on 11/01/17.
+ */
+class SolutionCompletenessRatio implements InsertionStartsListener, JobInsertedListener {
-final class ConfigureFixCostCalculator implements InsertionStartsListener, JobInsertedListener {
+ protected double solutionCompletenessRatio = 0.5;
- private final VehicleRoutingProblem vrp;
-
- private final JobInsertionConsideringFixCostsCalculator calcConsideringFix;
-
- private final double minRatio = 0.5;
+ private final int nuOfJobs;
private int nuOfJobsToRecreate;
- public ConfigureFixCostCalculator(VehicleRoutingProblem vrp, JobInsertionConsideringFixCostsCalculator calcConsideringFix) {
- super();
- this.vrp = vrp;
- this.calcConsideringFix = calcConsideringFix;
+ public SolutionCompletenessRatio(int nuOfJobs) {
+ this.nuOfJobs = nuOfJobs;
}
- @Override
- public String toString() {
- return "[name=configureFixCostCalculator]";
+ public void setSolutionCompletenessRatio(double ratio) {
+ solutionCompletenessRatio = ratio;
+ }
+
+ public double getSolutionCompletenessRatio() {
+ return solutionCompletenessRatio;
}
@Override
public void informInsertionStarts(Collection routes, Collection unassignedJobs) {
this.nuOfJobsToRecreate = unassignedJobs.size();
- double completenessRatio = (1 - ((double) nuOfJobsToRecreate / (double) vrp.getJobs().values().size()));
- calcConsideringFix.setSolutionCompletenessRatio(Math.max(minRatio,completenessRatio));
+ solutionCompletenessRatio = (1 - ((double) nuOfJobsToRecreate / (double) nuOfJobs));
}
@Override
public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) {
nuOfJobsToRecreate--;
- double completenessRatio = (1 - ((double) nuOfJobsToRecreate / (double) vrp.getJobs().values().size()));
- calcConsideringFix.setSolutionCompletenessRatio(Math.max(minRatio,completenessRatio));
+ solutionCompletenessRatio = (1 - ((double) nuOfJobsToRecreate / (double) nuOfJobs));
}
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java
index 9190a2f2..ad522012 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java
@@ -98,7 +98,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
}
Vehicle selectedVehicle = currentRoute.getVehicle();
Driver selectedDriver = currentRoute.getDriver();
- InsertionData bestIData = InsertionData.createEmptyInsertionData();
+ InsertionData bestIData = new InsertionData.NoInsertionFound();
double bestKnownCost_ = bestKnownCost;
Collection relevantVehicles = new ArrayList();
if (!(selectedVehicle instanceof VehicleImpl.NoVehicle)) {
@@ -115,6 +115,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
else depTime = v.getEarliestDeparture();
InsertionData iData = insertionCalculator.getInsertionData(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_);
if (iData instanceof InsertionData.NoInsertionFound) {
+ bestIData.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
continue;
}
if (iData.getInsertionCost() < bestKnownCost_) {
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java
index 37ccd7bb..f0c5e9a8 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java
@@ -24,6 +24,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
public class InsertionListeners {
@@ -74,6 +75,14 @@ public class InsertionListeners {
}
}
+ public void informJobUnassignedListeners(Job unassigned, List reasons) {
+ for (InsertionListener l : listeners) {
+ if (l instanceof JobUnassignedListener) {
+ ((JobUnassignedListener) l).informJobUnassigned(unassigned, reasons);
+ }
+ }
+ }
+
public void addListener(InsertionListener insertionListener) {
listeners.add(insertionListener);
}
diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java
similarity index 72%
rename from jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java
rename to jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java
index 7555f8cc..55a2921d 100644
--- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java
@@ -15,12 +15,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.graphhopper.jsprit.analysis.util;
-import com.graphhopper.jsprit.core.util.BenchmarkResult;
+package com.graphhopper.jsprit.core.algorithm.recreate.listener;
+
+import com.graphhopper.jsprit.core.problem.job.Job;
import java.util.Collection;
-public interface BenchmarkWriter {
- public void write(Collection results);
+/**
+ * Created by schroeder on 06/02/17.
+ */
+public interface JobUnassignedListener extends InsertionListener {
+
+ void informJobUnassigned(Job unassigned, Collection failedConstraintNames);
+
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinString.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinString.java
new file mode 100644
index 00000000..8452dde2
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinString.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.graphhopper.jsprit.core.algorithm.ruin;
+
+import com.graphhopper.jsprit.core.problem.AbstractActivity;
+import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
+import com.graphhopper.jsprit.core.problem.job.Job;
+import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
+import com.graphhopper.jsprit.core.util.RandomUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+
+/**
+ * RuinString is adopted from
+ *
+ * Technical report 7.11.2016
+ * A Fresh Ruin & Recreate Implementation for the Capacitated Vehicle Routing Problem
+ * Jan Christiaens, Greet Vanden Berghe
+ * KU Leuven, Department of Computer Science, CODeS & iMinds-ITEC
+ * Gebr. De Smetstraat 1, 9000 Gent, Belgium, jan.christiaens@cs.kuleuven.be, greet.vandenberghe@cs.kuleuven.be
+ *
+ * https://lirias.kuleuven.be/bitstream/123456789/556398/1/asb_rr_2016.pdf
+ *
+ * @author stefan
+ */
+public final class RuinString extends AbstractRuinStrategy {
+
+ private static Logger logger = LoggerFactory.getLogger(RuinString.class);
+
+ private final VehicleRoutingProblem vrp;
+
+ private final JobNeighborhoods jobNeighborhoods;
+
+ private int kMin = 1;
+
+ private int kMax = 6;
+
+ private int lMin = 30;
+
+ private int lMax = 60;
+
+ public RuinString(VehicleRoutingProblem vrp, JobNeighborhoods jobNeighborhoods) {
+ super(vrp);
+ this.vrp = vrp;
+ this.jobNeighborhoods = jobNeighborhoods;
+ logger.debug("initialise {}", this);
+ }
+
+ public void setNoRoutes(int kMin, int kMax) {
+ this.kMin = kMin;
+ this.kMax = kMax;
+ }
+
+ public void setStringLength(int lMin, int lMax) {
+ this.lMin = lMin;
+ this.lMax = lMax;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=splitRuin]";
+ }
+
+ /**
+ * 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 ruinRoutes(Collection vehicleRoutes) {
+ if (vehicleRoutes.isEmpty() || vrp.getJobs().isEmpty()) {
+ return Collections.emptyList();
+ }
+ int noStrings;
+ if (kMin == kMax) noStrings = kMax;
+ else noStrings = kMin + random.nextInt((kMax - kMin));
+ noStrings = Math.min(noStrings, vehicleRoutes.size());
+ Set unassignedJobs = new HashSet<>();
+ Set ruinedRoutes = new HashSet<>();
+ Job prevJob = RandomUtils.nextJob(vrp.getJobs().values(), random);
+ Iterator neighborhoodIterator = jobNeighborhoods.getNearestNeighborsIterator(kMax * lMax, prevJob);
+ while (neighborhoodIterator.hasNext() && ruinedRoutes.size() <= noStrings) {
+ if (!unassignedJobs.contains(prevJob)) {
+ VehicleRoute route = getRouteOf(prevJob, vehicleRoutes);
+ if (route != null && !ruinedRoutes.contains(route)) {
+ if (random.nextDouble() < .5) {
+ ruinRouteWithStringRuin(route, prevJob, unassignedJobs);
+ } else {
+ ruinRouteWithSplitStringRuin(route, prevJob, unassignedJobs);
+ }
+ ruinedRoutes.add(route);
+ }
+ }
+ prevJob = neighborhoodIterator.next();
+ }
+ return unassignedJobs;
+ }
+
+ private VehicleRoute getRouteOf(Job job, Collection vehicleRoutes) {
+ for (VehicleRoute route : vehicleRoutes) {
+ if (route.getTourActivities().servesJob(job)) return route;
+ }
+ return null;
+ }
+
+ private void ruinRouteWithSplitStringRuin(VehicleRoute seedRoute, Job prevJob, Set unassignedJobs) {
+ int noActivities = seedRoute.getActivities().size();
+ int stringLength;
+ if (lMin == lMax) stringLength = lMin;
+ else stringLength = lMin + random.nextInt(lMax - lMin);
+ stringLength = Math.min(stringLength, seedRoute.getActivities().size());
+
+ int preservedSubstringLength = StringUtil.determineSubstringLength(stringLength, noActivities, random);
+
+ List acts = vrp.getActivities(prevJob);
+ AbstractActivity randomSeedAct = RandomUtils.nextItem(acts, random);
+ int seedIndex = 0;
+
+ int index = 0;
+ for (TourActivity act : seedRoute.getActivities()) {
+ if (act.getIndex() == randomSeedAct.getIndex()) {
+ seedIndex = index;
+ break;
+ }
+ index++;
+ }
+
+ int totalStringLength = stringLength + preservedSubstringLength;
+ List stringBounds = StringUtil.getLowerBoundsOfAllStrings(totalStringLength, seedIndex, noActivities);
+ if (stringBounds.isEmpty()) return;
+ int lowerBound = RandomUtils.nextItem(stringBounds, random);
+
+ List jobs2Remove = new ArrayList<>();
+ int startIndexOfPreservedSubstring = random.nextInt(stringLength);
+ int position = 0;
+ int noStringsInPreservedSubstring = 0;
+ boolean isPreservedSubstring = false;
+ for (int i = lowerBound; i < (lowerBound + totalStringLength); i++) {
+ if (position == startIndexOfPreservedSubstring) {
+ isPreservedSubstring = true;
+ }
+ if (noStringsInPreservedSubstring >= preservedSubstringLength) {
+ isPreservedSubstring = false;
+ }
+ if (!isPreservedSubstring) {
+ TourActivity act = seedRoute.getActivities().get(i);
+ if (act instanceof TourActivity.JobActivity) {
+ Job job = ((TourActivity.JobActivity) act).getJob();
+ if (vrp.getJobs().containsKey(job.getId())) {
+ jobs2Remove.add(job);
+ }
+ }
+ } else noStringsInPreservedSubstring++;
+ position++;
+ }
+ for (Job job : jobs2Remove) {
+ removeJob(job, seedRoute);
+ unassignedJobs.add(job);
+ }
+
+ }
+
+
+ private void ruinRouteWithStringRuin(VehicleRoute seedRoute, Job prevJob, Set unassignedJobs) {
+ int stringLength = lMin + random.nextInt(lMax - lMin);
+ stringLength = Math.min(stringLength, seedRoute.getActivities().size());
+ List acts = vrp.getActivities(prevJob);
+ AbstractActivity randomSeedAct = RandomUtils.nextItem(acts, random);
+ int seedIndex = 0;
+ int noActivities = seedRoute.getActivities().size();
+ int index = 0;
+ for (TourActivity act : seedRoute.getActivities()) {
+ if (act.getIndex() == randomSeedAct.getIndex()) {
+ seedIndex = index;
+ break;
+ }
+ index++;
+ }
+ List stringBounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ if (stringBounds.isEmpty()) return;
+ int lowerBound = RandomUtils.nextItem(stringBounds, random);
+ List jobs2Remove = new ArrayList<>();
+ for (int i = lowerBound; i < (lowerBound + stringLength); i++) {
+ TourActivity act = seedRoute.getActivities().get(i);
+ if (act instanceof TourActivity.JobActivity) {
+ Job job = ((TourActivity.JobActivity) act).getJob();
+ if (vrp.getJobs().containsKey(job.getId())) {
+ jobs2Remove.add(job);
+ }
+ }
+ }
+ for (Job job : jobs2Remove) {
+ removeJob(job, seedRoute);
+ unassignedJobs.add(job);
+ }
+
+ }
+
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtil.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtil.java
new file mode 100644
index 00000000..b9070a1c
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtil.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.ruin;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Created by schroeder on 13/01/17.
+ */
+class StringUtil {
+
+ static List getLowerBoundsOfAllStrings(int length, int seedIndex, int routeLength) {
+ List lowerBounds = new ArrayList<>();
+ for (int i = 1; i <= length; i++) {
+ int lower = seedIndex - (length - i);
+ int upper = seedIndex + (i - 1);
+ if (lower >= 0 && upper < routeLength) {
+ lowerBounds.add(lower);
+ }
+ }
+ return lowerBounds;
+ }
+
+ static int determineSubstringLength(int baseLength, int routeLength, Random random) {
+ if (baseLength == routeLength) return 0;
+ int substringLength = 1;
+ while (baseLength + substringLength < routeLength) {
+ if (random.nextDouble() < 0.01) {
+ return substringLength;
+ } else substringLength++;
+ }
+ return substringLength;
+ }
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/VehicleDependentTraveledDistance.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/VehicleDependentTraveledDistance.java
new file mode 100644
index 00000000..bde4058b
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/VehicleDependentTraveledDistance.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.state;
+
+import com.graphhopper.jsprit.core.problem.Location;
+import com.graphhopper.jsprit.core.problem.cost.TransportDistance;
+import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.ActivityVisitor;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
+import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
+import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeKey;
+
+import java.util.*;
+
+/**
+ * Created by schroeder on 17/05/16.
+ */
+public class VehicleDependentTraveledDistance implements StateUpdater, ActivityVisitor {
+
+ static class State {
+
+ Location prevLocation;
+
+ double distance;
+
+ public State(Location prevLocation, double distance) {
+ this.prevLocation = prevLocation;
+ this.distance = distance;
+ }
+
+ public Location getPrevLocation() {
+ return prevLocation;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+ }
+
+ private final TransportDistance transportDistance;
+
+ private final StateManager stateManager;
+
+ private final StateId traveledDistanceId;
+
+ private VehicleRoute route;
+
+ private List uniqueVehicles;
+
+ private Map states;
+
+ public VehicleDependentTraveledDistance(TransportDistance transportCostMatrices, StateManager stateManager, StateId distanceInRouteId, Collection vehicles) {
+ this.transportDistance = transportCostMatrices;
+ this.stateManager = stateManager;
+ this.traveledDistanceId = distanceInRouteId;
+ uniqueVehicles = getUniqueVehicles(vehicles);
+ }
+
+ private List getUniqueVehicles(Collection vehicles) {
+ Set types = new HashSet<>();
+ List uniqueVehicles = new ArrayList<>();
+ for(Vehicle v : vehicles){
+ if(!types.contains(v.getVehicleTypeIdentifier())){
+ types.add(v.getVehicleTypeIdentifier());
+ uniqueVehicles.add(v);
+ }
+ }
+ return uniqueVehicles;
+ }
+
+ @Override
+ public void begin(VehicleRoute route) {
+ this.route = route;
+ states = new HashMap<>();
+ for(Vehicle v : uniqueVehicles){
+ State state = new State(v.getStartLocation(),0);
+ states.put(v.getVehicleTypeIdentifier(),state);
+ }
+ }
+
+ @Override
+ public void visit(TourActivity activity) {
+ for(Vehicle v : uniqueVehicles){
+ State old = states.get(v.getVehicleTypeIdentifier());
+ double distance = old.getDistance();
+ distance += transportDistance.getDistance(old.getPrevLocation(),activity.getLocation(),0,v);
+ stateManager.putActivityState(activity,v,traveledDistanceId,distance);
+ states.put(v.getVehicleTypeIdentifier(),new State(activity.getLocation(),distance));
+ }
+ }
+
+ @Override
+ public void finish() {
+ for(Vehicle v : uniqueVehicles){
+ State old = states.get(v.getVehicleTypeIdentifier());
+ double distance = old.getDistance();
+ if(v.isReturnToDepot()) {
+ distance += transportDistance.getDistance(old.getPrevLocation(), v.getEndLocation(), 0, v);
+ }
+ stateManager.putRouteState(route,v,traveledDistanceId, distance);
+ }
+ }
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/termination/VariationCoefficientTermination.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/termination/VariationCoefficientTermination.java
index 5e900b35..5f177c3d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/termination/VariationCoefficientTermination.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/termination/VariationCoefficientTermination.java
@@ -30,7 +30,9 @@ import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
@@ -119,9 +121,22 @@ public class VariationCoefficientTermination implements PrematureAlgorithmTermin
}
}
+ public void informIterationEnds(int i, VehicleRoutingProblem problem, VehicleRoutingProblemSolution solution) {
+ informIterationEnds(i, problem, toList(solution));
+ }
+
+ private List toList(VehicleRoutingProblemSolution solution) {
+ List solutions = new ArrayList<>();
+ solutions.add(solution);
+ return solutions;
+ }
+
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
if (lastAccepted == null) lastAccepted = Solutions.bestOf(solutions);
}
+ public void informIterationStarts(int i, VehicleRoutingProblem problem, VehicleRoutingProblemSolution solution) {
+ informIterationStarts(i, problem, toList(solution));
+ }
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractJob.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractJob.java
index 81ef63e7..85a37e80 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractJob.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractJob.java
@@ -26,7 +26,9 @@ import com.graphhopper.jsprit.core.problem.job.Job;
public abstract class AbstractJob implements Job {
private int index;
+ private Object userData;
+ @Override
public int getIndex() {
return index;
}
@@ -35,4 +37,15 @@ public abstract class AbstractJob implements Job {
this.index = index;
}
+ /**
+ * @return User-specific domain data associated by the job
+ */
+ public Object getUserData() {
+ return userData;
+ }
+
+ protected void setUserData(Object userData) {
+ this.userData = userData;
+ }
+
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractVehicle.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractVehicle.java
index bd259b4e..c8f69d2b 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractVehicle.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractVehicle.java
@@ -30,6 +30,7 @@ public abstract class AbstractVehicle implements Vehicle {
private int index;
+ @Override
public int getIndex() {
return index;
}
@@ -44,19 +45,35 @@ public abstract class AbstractVehicle implements Vehicle {
private VehicleTypeKey vehicleIdentifier;
- public int getIndex() {
- return index;
- }
+ private Object userData;
- protected void setIndex(int index) {
- this.index = index;
- }
+ /**
+ * @return User-specific domain data associated with the vehicle
+ */
+ @Override
+ public Object getUserData() {
+ return userData;
+ }
- public VehicleTypeKey getVehicleTypeIdentifier() {
- return vehicleIdentifier;
- }
+ protected void setUserData(Object userData) {
+ this.userData = userData;
+ }
- protected void setVehicleIdentifier(VehicleTypeKey vehicleTypeIdentifier) {
- this.vehicleIdentifier = vehicleTypeIdentifier;
- }
+ @Override
+ public int getIndex() {
+ return index;
+ }
+
+ protected void setIndex(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public VehicleTypeKey getVehicleTypeIdentifier() {
+ return vehicleIdentifier;
+ }
+
+ protected void setVehicleIdentifier(VehicleTypeKey vehicleTypeIdentifier) {
+ this.vehicleIdentifier = vehicleTypeIdentifier;
+ }
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Location.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Location.java
index 00f10bf1..233a71a9 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Location.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Location.java
@@ -64,26 +64,77 @@ public final class Location implements HasIndex, HasId {
private Coordinate coordinate;
+ private String name = "";
+
+ private Object userData;
+
public static Builder newInstance() {
return new Builder();
}
+ /**
+ * Sets user specific domain data associated with the object.
+ *
+ *
+ * The user data is a black box for the framework, it only stores it,
+ * but never interacts with it in any way.
+ *
+ *
+ * @param userData
+ * any object holding the domain specific user data
+ * associated with the object.
+ * @return builder
+ */
+ public Builder setUserData(Object userData) {
+ this.userData = userData;
+ return this;
+ }
+
+ /**
+ * Sets location index
+ *
+ * @param index
+ * @return the builder
+ */
public Builder setIndex(int index) {
if (index < 0) throw new IllegalArgumentException("index must be >= 0");
this.index = index;
return this;
}
+ /**
+ * Sets coordinate of location
+ *
+ * @param coordinate
+ * @return
+ */
public Builder setCoordinate(Coordinate coordinate) {
this.coordinate = coordinate;
return this;
}
+ /**
+ * Sets location id
+ *
+ * @param id
+ * @return
+ */
public Builder setId(String id) {
this.id = id;
return this;
}
+ /**
+ * Adds name, e.g. street name, to location
+ *
+ * @param name
+ * @return
+ */
+ public Builder setName(String name){
+ this.name = name;
+ return this;
+ }
+
public Location build() {
if (id == null && coordinate == null) {
if (index == -1) throw new IllegalArgumentException("either id or coordinate or index must be set");
@@ -107,10 +158,23 @@ public final class Location implements HasIndex, HasId {
private final String id;
+ private final String name;
+
+ private Object userData;
+
private Location(Builder builder) {
+ this.userData = builder.userData;
this.index = builder.index;
this.coordinate = builder.coordinate;
this.id = builder.id;
+ this.name = builder.name;
+ }
+
+ /**
+ * @return User-specific domain data associated by the job
+ */
+ public Object getUserData() {
+ return userData;
}
@Override
@@ -127,6 +191,8 @@ public final class Location implements HasIndex, HasId {
return coordinate;
}
+ public String getName() { return name; }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java
index e93c9e61..544c4fe9 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java
@@ -568,8 +568,6 @@ public class VehicleRoutingProblem {
*/
private final FleetSize fleetSize;
- private final Locations locations;
-
private Map> activityMap;
private int nuActivities;
@@ -591,7 +589,6 @@ public class VehicleRoutingProblem {
this.initialVehicleRoutes = builder.initialRoutes;
this.transportCosts = builder.transportCosts;
this.activityCosts = builder.activityCosts;
- this.locations = builder.getLocations();
this.activityMap = builder.activityMap;
this.nuActivities = builder.activityIndexCounter;
this.allLocations = builder.allLocations;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
index dcd19cd6..1275f825 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
@@ -37,6 +37,7 @@ import java.util.List;
*/
public class ConstraintManager implements HardActivityConstraint, HardRouteConstraint, SoftActivityConstraint, SoftRouteConstraint {
+
public static enum Priority {
CRITICAL, HIGH, LOW
}
@@ -45,7 +46,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
private HardActivityLevelConstraintManager actLevelConstraintManager = new HardActivityLevelConstraintManager();
- private HardRouteLevelConstraintManager routeLevelConstraintManager = new HardRouteLevelConstraintManager();
+ private HardRouteLevelConstraintManager hardRouteConstraintManager = new HardRouteLevelConstraintManager();
private SoftActivityConstraintManager softActivityConstraintManager = new SoftActivityConstraintManager();
@@ -76,6 +77,25 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
resolveConstraints(constraints);
}
+ public Collection getHardRouteConstraints() {
+ return hardRouteConstraintManager.getConstraints();
+ }
+
+ public Collection getCriticalHardActivityConstraints() {
+ return actLevelConstraintManager.getCriticalConstraints();
+ }
+
+ public Collection getHighPrioHardActivityConstraints() {
+ return actLevelConstraintManager.getHighPrioConstraints();
+ }
+
+ public Collection getLowPrioHardActivityConstraints() {
+ return actLevelConstraintManager.getLowPrioConstraints();
+ }
+// public Collection getHardActivityConstraints() {
+// return actLevelConstraintManager.g;
+// }
+
public DependencyType[] getDependencyTypes() {
return dependencyTypes;
}
@@ -103,7 +123,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
constraintTypeKnown = true;
}
if (c instanceof HardRouteConstraint) {
- routeLevelConstraintManager.addConstraint((HardRouteConstraint) c);
+ hardRouteConstraintManager.addConstraint((HardRouteConstraint) c);
constraintTypeKnown = true;
}
if (c instanceof SoftRouteConstraint) {
@@ -152,7 +172,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
}
public void addConstraint(HardRouteConstraint routeLevelConstraint) {
- routeLevelConstraintManager.addConstraint(routeLevelConstraint);
+ hardRouteConstraintManager.addConstraint(routeLevelConstraint);
}
public void addConstraint(SoftActivityConstraint softActivityConstraint) {
@@ -165,7 +185,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
@Override
public boolean fulfilled(JobInsertionContext insertionContext) {
- return routeLevelConstraintManager.fulfilled(insertionContext);
+ return hardRouteConstraintManager.fulfilled(insertionContext);
}
@Override
@@ -176,7 +196,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
public Collection getConstraints() {
List constraints = new ArrayList();
constraints.addAll(actLevelConstraintManager.getAllConstraints());
- constraints.addAll(routeLevelConstraintManager.getConstraints());
+ constraints.addAll(hardRouteConstraintManager.getConstraints());
constraints.addAll(softActivityConstraintManager.getConstraints());
constraints.addAll(softRouteConstraintManager.getConstraints());
return Collections.unmodifiableCollection(constraints);
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java
new file mode 100644
index 00000000..fac022db
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.problem.constraint;
+
+import com.graphhopper.jsprit.core.algorithm.state.StateId;
+import com.graphhopper.jsprit.core.algorithm.state.StateManager;
+import com.graphhopper.jsprit.core.problem.cost.TransportDistance;
+import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.Start;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
+import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Created by schroeder on 11/10/16.
+ */
+public class MaxDistanceConstraint implements HardActivityConstraint{
+
+ private StateManager stateManager;
+
+ private StateId distanceId;
+
+ private TransportDistance distanceCalculator;
+
+ private Double[] maxDistances;
+
+ public MaxDistanceConstraint(StateManager stateManager, StateId distanceId, TransportDistance distanceCalculator, Map maxDistancePerVehicleMap) {
+ this.stateManager = stateManager;
+ this.distanceId = distanceId;
+ this.distanceCalculator = distanceCalculator;
+ makeArray(maxDistancePerVehicleMap);
+ }
+
+ private void makeArray(Map maxDistances) {
+ int maxIndex = getMaxIndex(maxDistances.keySet());
+ this.maxDistances = new Double[maxIndex+1];
+ for(Vehicle v : maxDistances.keySet()){
+ this.maxDistances[v.getIndex()]=maxDistances.get(v);
+ }
+ }
+
+ private int getMaxIndex(Collection vehicles) {
+ int index = 0;
+ for(Vehicle v : vehicles){
+ if(v.getIndex() > index) index = v.getIndex();
+ }
+ return index;
+ }
+
+ @Override
+ public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
+ if(!hasMaxDistance(iFacts.getNewVehicle())) return ConstraintsStatus.FULFILLED;
+ Double currentDistance = 0d;
+ boolean routeIsEmpty = iFacts.getRoute().isEmpty();
+ if(!routeIsEmpty){
+ currentDistance = stateManager.getRouteState(iFacts.getRoute(),iFacts.getNewVehicle(), distanceId,Double.class);
+ }
+ double maxDistance = getMaxDistance(iFacts.getNewVehicle());
+ if(currentDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED_BREAK;
+
+ double distancePrevAct2NewAct = distanceCalculator.getDistance(prevAct.getLocation(), newAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle());
+ double distanceNewAct2nextAct = distanceCalculator.getDistance(newAct.getLocation(), nextAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle());
+ double distancePrevAct2NextAct = distanceCalculator.getDistance(prevAct.getLocation(), nextAct.getLocation(), prevActDepTime, iFacts.getNewVehicle());
+ if(prevAct instanceof Start && nextAct instanceof End) distancePrevAct2NextAct = 0;
+ if(nextAct instanceof End && !iFacts.getNewVehicle().isReturnToDepot()){
+ distanceNewAct2nextAct = 0;
+ distancePrevAct2NextAct = 0;
+ }
+ double additionalDistance = distancePrevAct2NewAct + distanceNewAct2nextAct - distancePrevAct2NextAct;
+ if(currentDistance + additionalDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED;
+
+
+ double additionalDistanceOfPickup = 0;
+ if(newAct instanceof DeliverShipment){
+ int iIndexOfPickup = iFacts.getRelatedActivityContext().getInsertionIndex();
+ TourActivity pickup = iFacts.getAssociatedActivities().get(0);
+ TourActivity actBeforePickup;
+ if(iIndexOfPickup > 0) actBeforePickup = iFacts.getRoute().getActivities().get(iIndexOfPickup-1);
+ else actBeforePickup = new Start(iFacts.getNewVehicle().getStartLocation(),0,Double.MAX_VALUE);
+ TourActivity actAfterPickup;
+ if (iIndexOfPickup < iFacts.getRoute().getActivities().size())
+ actAfterPickup = iFacts.getRoute().getActivities().get(iIndexOfPickup);
+ else
+ actAfterPickup = nextAct;
+ double distanceActBeforePickup2Pickup = distanceCalculator.getDistance(actBeforePickup.getLocation(), pickup.getLocation(), actBeforePickup.getEndTime(), iFacts.getNewVehicle());
+ double distancePickup2ActAfterPickup = distanceCalculator.getDistance(pickup.getLocation(), actAfterPickup.getLocation(), iFacts.getRelatedActivityContext().getEndTime(), iFacts.getNewVehicle());
+ double distanceBeforePickup2AfterPickup = distanceCalculator.getDistance(actBeforePickup.getLocation(), actAfterPickup.getLocation(), actBeforePickup.getEndTime(), iFacts.getNewVehicle());
+ if (routeIsEmpty) distanceBeforePickup2AfterPickup = 0;
+ if (actAfterPickup instanceof End && !iFacts.getNewVehicle().isReturnToDepot()) {
+ distancePickup2ActAfterPickup = 0;
+ distanceBeforePickup2AfterPickup = 0;
+ }
+ additionalDistanceOfPickup = distanceActBeforePickup2Pickup + distancePickup2ActAfterPickup - distanceBeforePickup2AfterPickup;
+ }
+
+
+ if(currentDistance + additionalDistance + additionalDistanceOfPickup > maxDistance){
+ return ConstraintsStatus.NOT_FULFILLED;
+ }
+
+ return ConstraintsStatus.FULFILLED;
+ }
+
+ private boolean hasMaxDistance(Vehicle newVehicle){
+ if(newVehicle.getIndex() >= this.maxDistances.length) return false;
+ return this.maxDistances[newVehicle.getIndex()] != null;
+ }
+
+ private double getMaxDistance(Vehicle newVehicle) {
+ Double maxDistance = this.maxDistances[newVehicle.getIndex()];
+ if(maxDistance == null) return Double.MAX_VALUE;
+ return maxDistance;
+ }
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraint.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraint.java
index 0effe3e0..71c38796 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraint.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraint.java
@@ -20,21 +20,32 @@ package com.graphhopper.jsprit.core.problem.constraint;
import com.graphhopper.jsprit.core.algorithm.state.StateId;
import com.graphhopper.jsprit.core.algorithm.state.StateManager;
+import com.graphhopper.jsprit.core.algorithm.state.UpdateMaxTimeInVehicle;
+import com.graphhopper.jsprit.core.algorithm.state.UpdateVehicleDependentPracticalTimeWindows;
+import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.cost.TransportTime;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
+import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliveryActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
+import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
/**
* Created by schroeder on 15/09/16.
*/
public class MaxTimeInVehicleConstraint implements HardActivityConstraint {
+ private final VehicleRoutingProblem vrp;
+
private final TransportTime transportTime;
private final VehicleRoutingActivityCosts activityCosts;
@@ -43,15 +54,19 @@ public class MaxTimeInVehicleConstraint implements HardActivityConstraint {
private final StateManager stateManager;
- public MaxTimeInVehicleConstraint(TransportTime transportTime, VehicleRoutingActivityCosts activityCosts, StateId latestStartId, StateManager stateManager) {
+ public MaxTimeInVehicleConstraint(TransportTime transportTime, VehicleRoutingActivityCosts activityCosts, StateId latestStartId, StateManager stateManager, VehicleRoutingProblem vrp) {
this.transportTime = transportTime;
this.latestStartId = latestStartId;
this.stateManager = stateManager;
this.activityCosts = activityCosts;
+ this.vrp = vrp;
}
@Override
- public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
+ public ConstraintsStatus fulfilled(final JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
+ boolean newActIsPickup = newAct instanceof PickupActivity;
+ boolean newActIsDelivery = newAct instanceof DeliveryActivity;
+ boolean isShipment = iFacts.getJob() instanceof Shipment;
/*
1. check whether insertion of new shipment satisfies own max-in-vehicle-constraint
2. check whether insertion of new shipment satisfies all other max-in-vehicle-constraints
@@ -75,7 +90,7 @@ public class MaxTimeInVehicleConstraint implements HardActivityConstraint {
if(timeInVehicle > maxTimeInVehicle) return ConstraintsStatus.NOT_FULFILLED;
}
- else if(newAct instanceof PickupActivity){
+ else if(newActIsPickup){
if(iFacts.getAssociatedActivities().size() == 1){
double maxTimeInVehicle = ((TourActivity.JobActivity)newAct).getJob().getMaxTimeInVehicle();
//ToDo - estimate in vehicle time of pickups here - This seems to trickier than I thought
@@ -88,15 +103,46 @@ public class MaxTimeInVehicleConstraint implements HardActivityConstraint {
//************ 2. check whether insertion of new shipment satisfies all other max-in-vehicle-constraints
- double latest;
- if(iFacts.getRoute().isEmpty()) latest = Double.MAX_VALUE;
- else if(nextAct instanceof End){
- latest = stateManager.getRouteState(iFacts.getRoute(),iFacts.getNewVehicle(), latestStartId,Double.class);
- }
- else latest = stateManager.getActivityState(nextAct, iFacts.getNewVehicle(), latestStartId, Double.class);
+ if(newActIsPickup || iFacts.getAssociatedActivities().size() == 1) {
+ double latest;
+ if (iFacts.getRoute().isEmpty()) latest = Double.MAX_VALUE;
+ else if (nextAct instanceof End) {
+ latest = stateManager.getRouteState(iFacts.getRoute(), iFacts.getNewVehicle(), latestStartId, Double.class);
+ } else latest = stateManager.getActivityState(nextAct, iFacts.getNewVehicle(), latestStartId, Double.class);
+
+ if (nextActStart > latest) {
+ return ConstraintsStatus.NOT_FULFILLED;
+ }
+
+ }
+ else if(newActIsDelivery && iFacts.getAssociatedActivities().size() == 2){
+ StateManager localStateManager = new StateManager(vrp);
+ StateId stateId = localStateManager.createStateId("local-slack");
+ UpdateMaxTimeInVehicle updateMaxTimeInVehicle = new UpdateMaxTimeInVehicle(localStateManager,stateId,transportTime,activityCosts);
+ updateMaxTimeInVehicle.setVehiclesToUpdate(new UpdateVehicleDependentPracticalTimeWindows.VehiclesToUpdate() {
+ @Override
+ public Collection get(VehicleRoute route) {
+ return Arrays.asList(iFacts.getNewVehicle());
+ }
+ });
+ updateMaxTimeInVehicle.begin(iFacts.getRoute());
+ List tourActivities = new ArrayList<>(iFacts.getRoute().getActivities());
+ tourActivities.add(iFacts.getRelatedActivityContext().getInsertionIndex(),iFacts.getAssociatedActivities().get(0));
+ for(TourActivity act : tourActivities){
+ updateMaxTimeInVehicle.visit(act);
+ }
+ updateMaxTimeInVehicle.finish(tourActivities,iFacts.getJob());
+
+ double latest;
+ if (iFacts.getRoute().isEmpty()) latest = Double.MAX_VALUE;
+ else if (nextAct instanceof End) {
+ latest = localStateManager.getRouteState(iFacts.getRoute(), iFacts.getNewVehicle(), stateId, Double.class);
+ } else latest = localStateManager.getActivityState(nextAct, iFacts.getNewVehicle(), stateId, Double.class);
+
+ if (nextActStart > latest) {
+ return ConstraintsStatus.NOT_FULFILLED;
+ }
- if(nextActStart > latest){
- return ConstraintsStatus.NOT_FULFILLED;
}
return ConstraintsStatus.FULFILLED;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
index 72b3f473..ab54929e 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
@@ -17,6 +17,8 @@
*/
package com.graphhopper.jsprit.core.problem.job;
+import java.util.Collection;
+
import com.graphhopper.jsprit.core.problem.AbstractJob;
import com.graphhopper.jsprit.core.problem.Capacity;
import com.graphhopper.jsprit.core.problem.Location;
@@ -26,8 +28,6 @@ import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindows;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindowsImpl;
import com.graphhopper.jsprit.core.util.Coordinate;
-import java.util.Collection;
-
/**
* Service implementation of a job.
*
@@ -88,13 +88,12 @@ public class Service extends AbstractJob {
protected TimeWindowsImpl timeWindows;
- private boolean twAdded = false;
+ private boolean twAdded = false;
private int priority = 2;
+ protected Object userData;
- protected double maxTimeInVehicle = Double.MAX_VALUE;
-
- Builder(String id){
+ protected double maxTimeInVehicle = Double.MAX_VALUE;Builder(String id){
this.id = id;
timeWindows = new TimeWindowsImpl();
timeWindows.add(timeWindow);
@@ -141,6 +140,24 @@ public class Service extends AbstractJob {
return this;
}
+ /**
+ * Sets user specific domain data associated with the object.
+ *
+ *
+ * The user data is a black box for the framework, it only stores it,
+ * but never interacts with it in any way.
+ *
+ *
+ * @param userData
+ * any object holding the domain specific user data
+ * associated with the object.
+ * @return builder
+ */
+ public Builder setUserData(Object userData) {
+ this.userData = userData;
+ return this;
+ }
+
/**
* Adds capacity dimension.
*
@@ -216,15 +233,16 @@ public class Service extends AbstractJob {
}
/**
- * Set priority to service. Only 1 = high priority, 2 = medium and 3 = low are allowed.
+ * Set priority to service. Only 1 (very high) to 10 (very low) are allowed.
*
- * Default is 2 = medium.
+ * Default is 2.
*
* @param priority
* @return builder
*/
public Builder setPriority(int priority) {
- if(priority < 1 || priority > 3) throw new IllegalArgumentException("incorrect priority. only 1 = high, 2 = medium and 3 = low is allowed");
+ if (priority < 1 || priority > 10)
+ throw new IllegalArgumentException("incorrect priority. only priority values from 1 to 10 are allowed where 1 = high and 10 is low");
this.priority = priority;
return this;
}
@@ -259,7 +277,8 @@ public class Service extends AbstractJob {
private final double maxTimeInVehicle;
- Service(Builder builder) {
+ Service(Builder> builder) {
+ setUserData(builder.userData);
id = builder.id;
serviceTime = builder.serviceTime;
timeWindow = builder.timeWindow;
@@ -268,14 +287,13 @@ public class Service extends AbstractJob {
skills = builder.skills;
name = builder.name;
location = builder.location;
- timeWindowManager = builder.timeWindows;
+ timeWindowManager = builder.timeWindows;
priority = builder.priority;
- maxTimeInVehicle = builder.maxTimeInVehicle;
- }
+ maxTimeInVehicle = builder.maxTimeInVehicle;}
- public Collection getTimeWindows(){
- return timeWindowManager.getTimeWindows();
- }
+ public Collection getTimeWindows(){
+ return timeWindowManager.getTimeWindows();
+ }
@Override
public String getId() {
@@ -380,6 +398,7 @@ public class Service extends AbstractJob {
*
* @return priority
*/
+ @Override
public int getPriority() {
return priority;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java
index fa11607a..22c551cd 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java
@@ -17,6 +17,8 @@
*/
package com.graphhopper.jsprit.core.problem.job;
+import java.util.Collection;
+
import com.graphhopper.jsprit.core.problem.AbstractJob;
import com.graphhopper.jsprit.core.problem.Capacity;
import com.graphhopper.jsprit.core.problem.Location;
@@ -24,8 +26,6 @@ import com.graphhopper.jsprit.core.problem.Skills;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindowsImpl;
-import java.util.Collection;
-
/**
* Shipment is an implementation of Job and consists of a pickup and a delivery of something.
@@ -89,6 +89,8 @@ public class Shipment extends AbstractJob {
private int priority = 2;
+ public Object userData;
+
public double maxTimeInVehicle = Double.MAX_VALUE;
/**
@@ -110,10 +112,29 @@ public class Shipment extends AbstractJob {
deliveryTimeWindows.add(deliveryTimeWindow);
}
+ /**
+ * Sets user specific domain data associated with the object.
+ *
+ *
+ * The user data is a black box for the framework, it only stores it,
+ * but never interacts with it in any way.
+ *
+ *
+ * @param userData
+ * any object holding the domain specific user data
+ * associated with the object.
+ * @return builder
+ */
+ public Builder setUserData(Object userData) {
+ this.userData = userData;
+ return this;
+ }
+
/**
* Sets pickup location.
*
- * @param pickupLocation pickup location
+ * @param pickupLocation
+ * pickup location
* @return builder
*/
public Builder setPickupLocation(Location pickupLocation) {
@@ -271,7 +292,7 @@ public class Shipment extends AbstractJob {
}
/**
- * Set priority to shipment. Only 1 = high priority, 2 = medium and 3 = low are allowed.
+ * Set priority to shipment. Only 1 (high) to 10 (low) are allowed.
*
* Default is 2 = medium.
*
@@ -279,7 +300,8 @@ public class Shipment extends AbstractJob {
* @return builder
*/
public Builder setPriority(int priority) {
- if(priority < 1 || priority > 3) throw new IllegalArgumentException("incorrect priority. only 1 = high, 2 = medium and 3 = low is allowed");
+ if (priority < 1 || priority > 10)
+ throw new IllegalArgumentException("incorrect priority. only 1 (very high) to 10 (very low) are allowed");
this.priority = priority;
return this;
}
@@ -326,6 +348,7 @@ public class Shipment extends AbstractJob {
private final double maxTimeInVehicle;
Shipment(Builder builder) {
+ setUserData(builder.userData);
this.id = builder.id;
this.pickupServiceTime = builder.pickupServiceTime;
this.pickupTimeWindow = builder.pickupTimeWindow;
@@ -454,6 +477,7 @@ public class Shipment extends AbstractJob {
*
* @return priority
*/
+ @Override
public int getPriority() {
return priority;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java
index c04d1ee1..7dd0c17d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java
@@ -19,6 +19,7 @@ package com.graphhopper.jsprit.core.problem.solution.route;
import com.graphhopper.jsprit.core.problem.AbstractActivity;
import com.graphhopper.jsprit.core.problem.JobActivityFactory;
+import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.driver.Driver;
import com.graphhopper.jsprit.core.problem.driver.DriverImpl;
import com.graphhopper.jsprit.core.problem.job.*;
@@ -196,16 +197,33 @@ public class VehicleRoute {
return this;
}
+ @Deprecated
public Builder addBreak(Break currentbreak) {
if (currentbreak == null) throw new IllegalArgumentException("break must not be null");
return addBreak(currentbreak, currentbreak.getTimeWindow());
}
+ @Deprecated
public Builder addBreak(Break currentbreak, TimeWindow timeWindow) {
if (currentbreak == null) throw new IllegalArgumentException("break must not be null");
return addService(currentbreak,timeWindow);
}
+ public Builder addBreak(Break currentbreak, TimeWindow timeWindow, Location location) {
+ if (currentbreak == null) throw new IllegalArgumentException("break must not be null");
+ return addBreakInternally(currentbreak, timeWindow, location);
+ }
+
+ private Builder addBreakInternally(Break currentBreak, TimeWindow timeWindow, Location breakLocation) {
+ List acts = jobActivityFactory.createActivities(currentBreak);
+ BreakActivity act = (BreakActivity) acts.get(0);
+ act.setTheoreticalEarliestOperationStartTime(timeWindow.getStart());
+ act.setTheoreticalLatestOperationStartTime(timeWindow.getEnd());
+ act.setLocation(breakLocation);
+ tourActivities.addActivity(act);
+ return this;
+ }
+
/**
* Adds a pickup to this route.
*
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java
index 27210957..fd19feac 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java
@@ -72,6 +72,14 @@ public interface Vehicle extends HasId, HasIndex {
public abstract VehicleTypeKey getVehicleTypeIdentifier();
public abstract Skills getSkills();
+ /**
+ * @return User-specific domain data associated with the vehicle
+ */
+ public Object getUserData();
public abstract Break getBreak();
+ // Switch to this as soon as we switct to Java 8:
+ // default Object getUserData() {
+ // return null;
+ // };
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java
index 4930fef0..143cd6a7 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java
@@ -17,12 +17,13 @@
*/
package com.graphhopper.jsprit.core.problem.vehicle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.graphhopper.jsprit.core.problem.AbstractVehicle;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.Skills;
import com.graphhopper.jsprit.core.problem.job.Break;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
@@ -130,6 +131,8 @@ public class VehicleImpl extends AbstractVehicle {
private Break aBreak;
+ private Object userData;
+
private Builder(String id) {
super();
this.id = id;
@@ -148,16 +151,39 @@ public class VehicleImpl extends AbstractVehicle {
return this;
}
+ /**
+ * Sets user specific domain data associated with the object.
+ *
+ *
+ * The user data is a black box for the framework, it only stores it,
+ * but never interacts with it in any way.
+ *
+ *
+ * @param userData
+ * any object holding the domain specific user data
+ * associated with the object.
+ * @return builder
+ */
+ public Builder setUserData(Object userData) {
+ this.userData = userData;
+ return this;
+ }
+
/**
* Sets the flag whether the vehicle must return to depot or not.
*
- *
If returnToDepot is true, the vehicle must return to specified end-location. If you
- * omit specifying the end-location, vehicle returns to start-location (that must to be set). If
- * you specify it, it returns to specified end-location.
*
- *
If returnToDepot is false, the end-location of the vehicle is endogenous.
+ * If returnToDepot is true, the vehicle must return to specified
+ * end-location. If you omit specifying the end-location, vehicle
+ * returns to start-location (that must to be set). If you specify it,
+ * it returns to specified end-location.
+ *
+ *
+ * If returnToDepot is false, the end-location of the vehicle is
+ * endogenous.
*
- * @param returnToDepot true if vehicle need to return to depot, otherwise false
+ * @param returnToDepot
+ * true if vehicle need to return to depot, otherwise false
* @return this builder
*/
public Builder setReturnToDepot(boolean returnToDepot) {
@@ -234,14 +260,13 @@ public class VehicleImpl extends AbstractVehicle {
if (startLocation != null && endLocation != null) {
if (!startLocation.getId().equals(endLocation.getId()) && !returnToDepot)
throw new IllegalArgumentException("this must not be. you specified both endLocationId and open-routes. this is contradictory.
" +
- "if you set endLocation, returnToDepot must be true. if returnToDepot is false, endLocationCoord must not be specified.");
+ "if you set endLocation, returnToDepot must be true. if returnToDepot is false, endLocationCoord must not be specified.");
}
if (startLocation != null && endLocation == null) {
endLocation = startLocation;
}
- if (startLocation == null && endLocation == null) {
+ if (startLocation == null && endLocation == null)
throw new IllegalArgumentException("vehicle requires startLocation. but neither locationId nor locationCoord nor startLocationId nor startLocationCoord has been set");
- }
skills = skillBuilder.build();
return new VehicleImpl(this);
}
@@ -297,6 +322,7 @@ public class VehicleImpl extends AbstractVehicle {
private final Break aBreak;
private VehicleImpl(Builder builder) {
+ setUserData(builder.userData);
id = builder.id;
type = builder.type;
earliestDeparture = builder.earliestStart;
@@ -306,7 +332,7 @@ public class VehicleImpl extends AbstractVehicle {
endLocation = builder.endLocation;
startLocation = builder.startLocation;
aBreak = builder.aBreak;
-// setVehicleIdentifier(new VehicleTypeKey(type.getTypeId(),startLocation.getId(),endLocation.getId(),earliestDeparture,latestArrival,skills));
+ // setVehicleIdentifier(new VehicleTypeKey(type.getTypeId(),startLocation.getId(),endLocation.getId(),earliestDeparture,latestArrival,skills));
setVehicleIdentifier(new VehicleTypeKey(type.getTypeId(), startLocation.getId(), endLocation.getId(), earliestDeparture, latestArrival, skills, returnToDepot));
}
@@ -318,11 +344,11 @@ public class VehicleImpl extends AbstractVehicle {
@Override
public String toString() {
return "[id=" + id + "]" +
- "[type=" + type + "]" +
- "[startLocation=" + startLocation + "]" +
- "[endLocation=" + endLocation + "]" +
- "[isReturnToDepot=" + isReturnToDepot() + "]" +
- "[skills=" + skills + "]";
+ "[type=" + type + "]" +
+ "[startLocation=" + startLocation + "]" +
+ "[endLocation=" + endLocation + "]" +
+ "[isReturnToDepot=" + isReturnToDepot() + "]" +
+ "[skills=" + skills + "]";
}
@@ -346,6 +372,7 @@ public class VehicleImpl extends AbstractVehicle {
return id;
}
+ @Override
public boolean isReturnToDepot() {
return returnToDepot;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleType.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleType.java
index 9847f4ba..e2eee81d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleType.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleType.java
@@ -56,4 +56,13 @@ public interface VehicleType {
public String getProfile();
+ /**
+ * @return User-specific domain data associated with the vehicle type
+ */
+ public Object getUserData();
+
+ // Switch to this as soon as we switct to Java 8:
+ // default Object getUserData() {
+ // return null;
+ // };
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java
index 864d978c..7f033e5c 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java
@@ -116,16 +116,39 @@ public class VehicleTypeImpl implements VehicleType {
private boolean dimensionAdded = false;
+ private Object userData;
+
private Builder(String id) {
this.id = id;
}
+
/**
- * Sets the maximum velocity this vehicle-type can go [in meter per seconds].
+ * Sets user specific domain data associated with the object.
+ *
+ *
+ * The user data is a black box for the framework, it only stores it,
+ * but never interacts with it in any way.
+ *
+ *
+ * @param userData
+ * any object holding the domain specific user data
+ * associated with the object.
+ * @return builder
+ */
+ public Builder setUserData(Object userData) {
+ this.userData = userData;
+ return this;
+ }
+
+ /**
+ * Sets the maximum velocity this vehicle-type can go [in meter per
+ * seconds].
*
* @param inMeterPerSeconds
* @return this builder
- * @throws IllegalArgumentException if velocity is smaller than zero
+ * @throws IllegalArgumentException
+ * if velocity is smaller than zero
*/
public VehicleTypeImpl.Builder setMaxVelocity(double inMeterPerSeconds) {
if (inMeterPerSeconds < 0.0) throw new IllegalArgumentException("velocity cannot be smaller than zero");
@@ -240,8 +263,8 @@ public class VehicleTypeImpl implements VehicleType {
if (dimVal < 0) throw new IllegalArgumentException("capacity value cannot be negative");
if (capacityDimensions != null)
throw new IllegalArgumentException("either build your dimension with build your dimensions with " +
- "addCapacityDimension(int dimIndex, int dimVal) or set the already built dimensions with .setCapacityDimensions(Capacity capacity)." +
- "You used both methods.");
+ "addCapacityDimension(int dimIndex, int dimVal) or set the already built dimensions with .setCapacityDimensions(Capacity capacity)." +
+ "You used both methods.");
dimensionAdded = true;
capacityBuilder.addDimension(dimIndex, dimVal);
return this;
@@ -261,8 +284,8 @@ public class VehicleTypeImpl implements VehicleType {
public Builder setCapacityDimensions(Capacity capacity) {
if (dimensionAdded)
throw new IllegalArgumentException("either build your dimension with build your dimensions with " +
- "addCapacityDimension(int dimIndex, int dimVal) or set the already built dimensions with .setCapacityDimensions(Capacity capacity)." +
- "You used both methods.");
+ "addCapacityDimension(int dimIndex, int dimVal) or set the already built dimensions with .setCapacityDimensions(Capacity capacity)." +
+ "You used both methods.");
this.capacityDimensions = capacity;
return this;
}
@@ -278,7 +301,7 @@ public class VehicleTypeImpl implements VehicleType {
final int prime = 31;
int result = 1;
result = prime * result
- + ((typeId == null) ? 0 : typeId.hashCode());
+ + ((typeId == null) ? 0 : typeId.hashCode());
return result;
}
@@ -314,12 +337,15 @@ public class VehicleTypeImpl implements VehicleType {
private final double maxVelocity;
+ private Object userData;
+
/**
* priv constructor constructing vehicle-type
*
* @param builder
*/
private VehicleTypeImpl(VehicleTypeImpl.Builder builder) {
+ this.userData = builder.userData;
typeId = builder.id;
capacity = builder.capacity;
maxVelocity = builder.maxVelo;
@@ -328,6 +354,14 @@ public class VehicleTypeImpl implements VehicleType {
profile = builder.profile;
}
+ /**
+ * @return User-specific domain data associated with the vehicle
+ */
+ @Override
+ public Object getUserData() {
+ return userData;
+ }
+
/* (non-Javadoc)
* @see basics.route.VehicleType#getTypeId()
*/
@@ -347,8 +381,8 @@ public class VehicleTypeImpl implements VehicleType {
@Override
public String toString() {
return "[typeId=" + typeId + "]" +
- "[capacity=" + capacityDimensions + "]" +
- "[costs=" + vehicleCostParams + "]";
+ "[capacity=" + capacityDimensions + "]" +
+ "[costs=" + vehicleCostParams + "]";
}
@Override
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java
index 4249faba..5e6a5d1d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java
@@ -45,6 +45,8 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
private double[][][] matrix;
+ private final int noLocations;
+
/**
* Creates a new builder returning the matrix-builder.
* If you want to consider symmetric matrices, set isSymmetric to true.
@@ -59,6 +61,7 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
private Builder(int noLocations, boolean isSymmetric) {
this.isSymmetric = isSymmetric;
matrix = new double[noLocations][noLocations][2];
+ this.noLocations = noLocations;
}
/**
@@ -74,11 +77,11 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
return this;
}
- private void add(int fromIndex, int toIndex, int indicatorIndex, double distance) {
+ private void add(int fromIndex, int toIndex, int indicatorIndex, double value) {
if (isSymmetric) {
- if (fromIndex < toIndex) matrix[fromIndex][toIndex][indicatorIndex] = distance;
- else matrix[toIndex][fromIndex][indicatorIndex] = distance;
- } else matrix[fromIndex][toIndex][indicatorIndex] = distance;
+ if (fromIndex < toIndex) matrix[fromIndex][toIndex][indicatorIndex] = value;
+ else matrix[toIndex][fromIndex][indicatorIndex] = value;
+ } else matrix[fromIndex][toIndex][indicatorIndex] = value;
}
/**
@@ -94,6 +97,11 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
return this;
}
+ public Builder addTransportTimeAndDistance(int fromIndex, int toIndex, double time, double distance){
+ addTransportTime(fromIndex, toIndex, time);
+ addTransportDistance(fromIndex,toIndex,distance);
+ return this;
+ }
/**
* Builds the matrix.
*
@@ -110,9 +118,12 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
private final double[][][] matrix;
+ private int noLocations;
+
private FastVehicleRoutingTransportCostsMatrix(Builder builder) {
this.isSymmetric = builder.isSymmetric;
matrix = builder.matrix;
+ noLocations = builder.noLocations;
}
/**
@@ -169,4 +180,9 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic
return costParams.perDistanceUnit * getDistance(from.getIndex(), to.getIndex()) + costParams.perTransportTimeUnit * getTransportTime(from, to, departureTime, driver, vehicle);
}
+ public int getNoLocations() {
+ return noLocations;
+ }
+
+
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java
new file mode 100644
index 00000000..03bf7d1d
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.util;
+
+import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedListener;
+import com.graphhopper.jsprit.core.problem.job.Job;
+import org.apache.commons.math3.stat.Frequency;
+
+import java.util.*;
+
+/**
+ * Created by schroeder on 06/02/17.
+ */
+public class UnassignedJobReasonTracker implements JobUnassignedListener {
+
+ public static String getMostLikelyFailedConstraintName(Frequency failedConstraintNamesFrequency) {
+ if (failedConstraintNamesFrequency == null) return "no reason found";
+ Iterator, Long>> entryIterator = failedConstraintNamesFrequency.entrySetIterator();
+ int maxCount = 0;
+ String mostLikely = null;
+ while (entryIterator.hasNext()) {
+ Map.Entry, Long> entry = entryIterator.next();
+ if (entry.getValue() > maxCount) {
+ Comparable> key = entry.getKey();
+ mostLikely = key.toString();
+ }
+ }
+ return mostLikely;
+ }
+
+ Map failedConstraintNamesFrequencyMapping = new HashMap<>();
+
+ Map codesToHumanReadableReason = new HashMap<>();
+
+ Map failedConstraintNamesToCode = new HashMap<>();
+
+ Set failedConstraintNamesToBeIgnored = new HashSet<>();
+
+ public UnassignedJobReasonTracker() {
+ codesToHumanReadableReason.put(1, "cannot serve required skill");
+ codesToHumanReadableReason.put(2, "cannot be visited within time window");
+ codesToHumanReadableReason.put(3, "does not fit into any vehicle due to capacity");
+ codesToHumanReadableReason.put(4, "cannot be assigned due to max distance constraint of vehicle");
+
+ failedConstraintNamesToCode.put("HardSkillConstraint", 1);
+ failedConstraintNamesToCode.put("VehicleDependentTimeWindowConstraints", 2);
+ failedConstraintNamesToCode.put("ServiceLoadRouteLevelConstraint", 3);
+ failedConstraintNamesToCode.put("PickupAndDeliverShipmentLoadActivityLevelConstraint", 3);
+ failedConstraintNamesToCode.put("ServiceLoadActivityLevelConstraint", 3);
+ failedConstraintNamesToCode.put("MaxDistanceConstraint", 4);
+ }
+
+ public void ignore(String simpleNameOfConstraint) {
+ failedConstraintNamesToBeIgnored.add(simpleNameOfConstraint);
+ }
+
+ @Override
+ public void informJobUnassigned(Job unassigned, Collection failedConstraintNames) {
+ if (!this.failedConstraintNamesFrequencyMapping.containsKey(unassigned.getId())) {
+ this.failedConstraintNamesFrequencyMapping.put(unassigned.getId(), new Frequency());
+ }
+ for (String r : failedConstraintNames) {
+ if (failedConstraintNamesToBeIgnored.contains(r)) continue;
+ this.failedConstraintNamesFrequencyMapping.get(unassigned.getId()).addValue(r);
+ }
+ }
+
+ public void put(String simpleNameOfFailedConstraint, int code, String reason) {
+ if (code <= 20)
+ throw new IllegalArgumentException("first 20 codes are reserved internally. choose a code > 20");
+ codesToHumanReadableReason.put(code, reason);
+ if (failedConstraintNamesToCode.containsKey(simpleNameOfFailedConstraint)) {
+ throw new IllegalArgumentException(simpleNameOfFailedConstraint + " already assigned to code and reason");
+ } else failedConstraintNamesToCode.put(simpleNameOfFailedConstraint, code);
+ }
+
+ /**
+ * For each job id, it returns frequency distribution of failed constraints (simple name of constraint) in an unmodifiable map.
+ *
+ * @return
+ */
+ @Deprecated
+ public Map getReasons() {
+ return Collections.unmodifiableMap(failedConstraintNamesFrequencyMapping);
+ }
+
+ /**
+ * For each job id, it returns frequency distribution of failed constraints (simple name of constraint) in an unmodifiable map.
+ *
+ * @return
+ */
+ public Map getFailedConstraintNamesFrequencyMapping() {
+ return Collections.unmodifiableMap(failedConstraintNamesFrequencyMapping);
+ }
+
+ /**
+ * Returns an unmodifiable map of codes and reason pairs.
+ *
+ * @return
+ */
+ public Map getCodesToReason() {
+ return Collections.unmodifiableMap(codesToHumanReadableReason);
+ }
+
+ /**
+ * Returns an unmodifiable map of constraint names (simple name of constraint) and reason code pairs.
+ *
+ * @return
+ */
+ public Map getFailedConstraintNamesToCode() {
+ return Collections.unmodifiableMap(failedConstraintNamesToCode);
+ }
+
+ public int getCode(String failedConstraintName) {
+ return toCode(failedConstraintName);
+ }
+
+ public String getHumanReadableReason(int code) {
+ return getCodesToReason().get(code);
+ }
+
+ public String getHumanReadableReason(String failedConstraintName) {
+ return getCodesToReason().get(getCode(failedConstraintName));
+ }
+ /**
+ * Returns the most likely reason code i.e. the reason (failed constraint) being observed most often.
+ *
+ * 1 --> "cannot serve required skill
+ * 2 --> "cannot be visited within time window"
+ * 3 --> "does not fit into any vehicle due to capacity"
+ * 4 --> "cannot be assigned due to max distance constraint of vehicle"
+ *
+ * @param jobId
+ * @return
+ */
+ public int getMostLikelyReasonCode(String jobId) {
+ if (!this.failedConstraintNamesFrequencyMapping.containsKey(jobId)) return -1;
+ Frequency reasons = this.failedConstraintNamesFrequencyMapping.get(jobId);
+ String mostLikelyReason = getMostLikelyFailedConstraintName(reasons);
+ return toCode(mostLikelyReason);
+ }
+
+ /**
+ * Returns the most likely reason i.e. the reason (failed constraint) being observed most often.
+ *
+ * @param jobId
+ * @return
+ */
+ public String getMostLikelyReason(String jobId) {
+ if (!this.failedConstraintNamesFrequencyMapping.containsKey(jobId)) return "no reason found";
+ Frequency reasons = this.failedConstraintNamesFrequencyMapping.get(jobId);
+ String mostLikelyReason = getMostLikelyFailedConstraintName(reasons);
+ int code = toCode(mostLikelyReason);
+ if (code == -1) return mostLikelyReason;
+ else return codesToHumanReadableReason.get(code);
+ }
+
+ private int toCode(String mostLikelyReason) {
+ if (failedConstraintNamesToCode.containsKey(mostLikelyReason))
+ return failedConstraintNamesToCode.get(mostLikelyReason);
+ else return -1;
+ }
+
+
+}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/MaxTimeInVehicle_IT.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/MaxTimeInVehicle_IT.java
index 2c5444b2..18285a11 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/MaxTimeInVehicle_IT.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/MaxTimeInVehicle_IT.java
@@ -31,7 +31,6 @@ import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolutio
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
import com.graphhopper.jsprit.core.reporting.SolutionPrinter;
import com.graphhopper.jsprit.core.util.Solutions;
-import org.junit.Assert;
import org.junit.Test;
/**
@@ -43,10 +42,10 @@ public class MaxTimeInVehicle_IT {
public void test(){
Shipment s1 = Shipment.Builder.newInstance("s1").setPickupLocation(Location.newInstance(0,0)).setDeliveryLocation(Location.newInstance(100,0)).setDeliveryServiceTime(10)
- .setMaxTimeInVehicle(90d)
+ .setMaxTimeInVehicle(100d)
.build();
Shipment s2 = Shipment.Builder.newInstance("s2").setPickupLocation(Location.newInstance(0,0)).setDeliveryLocation(Location.newInstance(100,0)).setDeliveryServiceTime(10)
- .setMaxTimeInVehicle(90d)
+ .setMaxTimeInVehicle(100d)
.build();
VehicleImpl v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0,0)).build();
@@ -58,12 +57,12 @@ public class MaxTimeInVehicle_IT {
stateManager.addStateUpdater(new UpdateMaxTimeInVehicle(stateManager,id,vrp.getTransportCosts(),vrp.getActivityCosts()));
ConstraintManager constraintManager = new ConstraintManager(vrp,stateManager);
- constraintManager.addConstraint(new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts(),id,stateManager), ConstraintManager.Priority.CRITICAL);
+ constraintManager.addConstraint(new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts(),id,stateManager, vrp), ConstraintManager.Priority.CRITICAL);
VehicleRoutingAlgorithm vra = Jsprit.Builder.newInstance(vrp).setStateAndConstraintManager(stateManager,constraintManager).buildAlgorithm();
VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());
- Assert.assertEquals(400, solution.getCost(), 0.001);
-// SolutionPrinter.print(vrp,solution, SolutionPrinter.Print.VERBOSE);
+// Assert.assertEquals(400, solution.getCost(), 0.001);
+ SolutionPrinter.print(vrp,solution, SolutionPrinter.Print.VERBOSE);
}
}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/UnassignedJobListTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/UnassignedJobListTest.java
index ce7eed83..49c524b6 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/UnassignedJobListTest.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/UnassignedJobListTest.java
@@ -26,14 +26,17 @@ import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolutio
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
import com.graphhopper.jsprit.core.util.Solutions;
+import com.graphhopper.jsprit.core.util.UnassignedJobReasonTracker;
import org.junit.Test;
import java.util.Collection;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class UnassignedJobListTest {
+
@Test
public void job2ShouldBeInBadJobList_dueToTimeWindow() {
VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
@@ -46,11 +49,17 @@ public class UnassignedJobListTest {
VehicleRoutingProblem vrp = builder.build();
VehicleRoutingAlgorithm algorithm = new GreedySchrimpfFactory().createAlgorithm(vrp);
algorithm.setMaxIterations(10);
+
+ UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker();
+ algorithm.addListener(reasonTracker);
+
Collection solutions = algorithm.searchSolutions();
VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions);
+
assertTrue(!solution.getUnassignedJobs().contains(job1));
assertTrue(solution.getUnassignedJobs().contains(job2));
+ assertEquals(2, reasonTracker.getMostLikelyReasonCode("job2"));
}
@Test
@@ -63,13 +72,19 @@ public class UnassignedJobListTest {
builder.addJob(job2);
VehicleRoutingProblem vrp = builder.build();
+
VehicleRoutingAlgorithm algorithm = new GreedySchrimpfFactory().createAlgorithm(vrp);
algorithm.setMaxIterations(10);
+
+ UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker();
+ algorithm.addListener(reasonTracker);
+
Collection solutions = algorithm.searchSolutions();
VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions);
assertTrue(!solution.getUnassignedJobs().contains(job1));
assertTrue(solution.getUnassignedJobs().contains(job2));
+ assertEquals(3, reasonTracker.getMostLikelyReasonCode("job2"));
}
}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculatorTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculatorTest.java
deleted file mode 100644
index 5ed06ce6..00000000
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureFixCostCalculatorTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to GraphHopper GmbH under one or more contributor
- * license agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * GraphHopper GmbH licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.graphhopper.jsprit.core.algorithm.recreate;
-
-import com.graphhopper.jsprit.core.algorithm.state.StateManager;
-import com.graphhopper.jsprit.core.problem.AbstractJob;
-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 com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.mockito.Mockito.mock;
-
-/**
- * Created by schroeder on 15/08/16.
- */
-public class ConfigureFixCostCalculatorTest {
-
- VehicleRoutingProblem vrp;
-
- @Before
- public void before(){
- VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
- for(int i=0;i<100;i++){
- Service service = Service.Builder.newInstance("" + i).setLocation(Location.newInstance(0)).build();
- vrpBuilder.addJob(service);
- }
- vrp = vrpBuilder.build();
- }
-
- @Test
- public void shouldCalculateCorrectly(){
- List unassigned = new ArrayList<>();
- int count = 1;
- for(String key : vrp.getJobs().keySet()) {
- if(count <= 25) {
- unassigned.add(vrp.getJobs().get(key));
- }
- count++;
- }
- JobInsertionConsideringFixCostsCalculator jicc = new JobInsertionConsideringFixCostsCalculator(mock(JobInsertionCostsCalculator.class),mock(StateManager.class));
- ConfigureFixCostCalculator c = new ConfigureFixCostCalculator(vrp,jicc);
- c.informInsertionStarts(new ArrayList(), unassigned);
- Assert.assertEquals(0.75, jicc.getSolutionCompletenessRatio(), 0.001);
- }
-
- @Test
- public void shouldBeMinRatio(){
- List unassigned = new ArrayList<>();
- int count = 1;
- for(String key : vrp.getJobs().keySet()) {
- if(count <= 75) {
- unassigned.add(vrp.getJobs().get(key));
- }
- count++;
- }
- JobInsertionConsideringFixCostsCalculator jicc = new JobInsertionConsideringFixCostsCalculator(mock(JobInsertionCostsCalculator.class),mock(StateManager.class));
- ConfigureFixCostCalculator c = new ConfigureFixCostCalculator(vrp,jicc);
- c.informInsertionStarts(new ArrayList(), unassigned);
- Assert.assertEquals(0.5, jicc.getSolutionCompletenessRatio(), 0.001);
- }
-
- @Test
- public void shouldBeOne(){
- List unassigned = new ArrayList<>();
- JobInsertionConsideringFixCostsCalculator jicc = new JobInsertionConsideringFixCostsCalculator(mock(JobInsertionCostsCalculator.class),mock(StateManager.class));
- ConfigureFixCostCalculator c = new ConfigureFixCostCalculator(vrp,jicc);
- c.informInsertionStarts(new ArrayList(), unassigned);
- Assert.assertEquals(1.0, jicc.getSolutionCompletenessRatio(), 0.001);
- }
-}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculatorTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculatorTest.java
index f3367979..e35273a8 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculatorTest.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionConsideringFixCostsCalculatorTest.java
@@ -20,6 +20,7 @@ package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.algorithm.state.InternalStates;
import com.graphhopper.jsprit.core.problem.Capacity;
import com.graphhopper.jsprit.core.problem.job.Job;
+import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter;
import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
@@ -34,11 +35,15 @@ import static org.mockito.Mockito.when;
public class JobInsertionConsideringFixCostsCalculatorTest {
- private JobInsertionConsideringFixCostsCalculator calc;
+ private IncreasingAbsoluteFixedCosts absFixedCosts;
- private Vehicle oVehicle;
+ private DecreasingRelativeFixedCosts relFixedCosts;
- private Vehicle nVehicle;
+ private Vehicle small;
+
+ private Vehicle medium;
+
+ private Vehicle large;
private Job job;
@@ -52,194 +57,333 @@ public class JobInsertionConsideringFixCostsCalculatorTest {
job = mock(Job.class);
when(job.getSize()).thenReturn(Capacity.Builder.newInstance().addDimension(0, 50).build());
- oVehicle = mock(Vehicle.class);
- VehicleType oType = VehicleTypeImpl.Builder.newInstance("otype").addCapacityDimension(0, 50).setFixedCost(50.0).build();
- when(oVehicle.getType()).thenReturn(oType);
+ small = mock(Vehicle.class);
+ VehicleType smallType = VehicleTypeImpl.Builder.newInstance("smallType").addCapacityDimension(0, 50).setFixedCost(50.0).build();
+ when(small.getType()).thenReturn(smallType);
- nVehicle = mock(Vehicle.class);
- VehicleType type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 100).setFixedCost(100.0).build();
- when(nVehicle.getType()).thenReturn(type);
+ medium = mock(Vehicle.class);
+ VehicleType mediumType = VehicleTypeImpl.Builder.newInstance("mediumType").addCapacityDimension(0, 100).setFixedCost(100.0).build();
+ when(medium.getType()).thenReturn(mediumType);
- InsertionData iData = new InsertionData(0.0, 1, 1, nVehicle, null);
+ large = mock(Vehicle.class);
+ VehicleType largeType = VehicleTypeImpl.Builder.newInstance("largeType").addCapacityDimension(0, 400).setFixedCost(200.0).build();
+ when(large.getType()).thenReturn(largeType);
+
+ InsertionData iData = new InsertionData(0.0, 1, 1, medium, null);
route = mock(VehicleRoute.class);
- when(jobInsertionCosts.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE)).thenReturn(iData);
+ when(jobInsertionCosts.getInsertionData(route, job, medium, 0.0, null, Double.MAX_VALUE)).thenReturn(iData);
+ when(jobInsertionCosts.getInsertionData(route, job, large, 0.0, null, Double.MAX_VALUE)).thenReturn(new InsertionData(0.0, 1, 1, large, null));
stateGetter = mock(RouteAndActivityStateGetter.class);
when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().build());
- calc = new JobInsertionConsideringFixCostsCalculator(jobInsertionCosts, stateGetter);
+ absFixedCosts = new IncreasingAbsoluteFixedCosts(10);
+ relFixedCosts = new DecreasingRelativeFixedCosts(stateGetter, 10);
}
@Test
public void whenOldVehicleIsNullAndSolutionComplete_itShouldReturnFixedCostsOfNewVehicle() {
- calc.setSolutionCompletenessRatio(1.0);
- calc.setWeightOfFixCost(1.0);
+ absFixedCosts.setSolutionCompletenessRatio(1.0);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(1.0);
+ relFixedCosts.setWeightOfFixCost(1.0);
//(1.*absFix + 0.*relFix) * completeness * weight = (1.*100. + 0.*50.) * 1. * 1. = 100.
- assertEquals(100., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+ assertEquals(100., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs0PercentComplete_itShouldReturnNoFixedCosts() {
- calc.setSolutionCompletenessRatio(0.0);
- calc.setWeightOfFixCost(1.0);
+ absFixedCosts.setSolutionCompletenessRatio(0.0);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.0);
+ relFixedCosts.setWeightOfFixCost(1.0);
//(0.*absFix + 1.*relFix) * completeness * weight = 0.
- assertEquals(0., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
+ assertEquals(0., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.1);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs50PercentComplete_itShouldReturnAvgOfRelFixedAndAbsFixedCostOfNewVehicle() {
- calc.setSolutionCompletenessRatio(0.5);
- calc.setWeightOfFixCost(1.0);
+ absFixedCosts.setSolutionCompletenessRatio(0.5);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.5);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.5*absFix + 0.5*relFix) * 0.5 * 1. = (0.5*100+0.5*50)*0.5*1. = 37.5
- assertEquals(37.5, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(37.5, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs75PercentComplete_itShouldReturnAvgOfRelFixedAndAbsFixedCostOfNewVehicle() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(1.0);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
//(0.75*absFix + 0.25*relFix) * 0.75 * 1.= (0.75*100.+0.25*50.)*0.75*1. = 65.625
- assertEquals(65.625, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(65.625, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionCompleteAndWeightIs05_itShouldReturnHalfOfFixedCostsOfNewVehicle() {
- calc.setSolutionCompletenessRatio(1.0);
- calc.setWeightOfFixCost(.5);
+ absFixedCosts.setSolutionCompletenessRatio(1.0);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(1.0);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(1.*absFix + 0.*relFix) * 1. * 0.5 = (1.*100. + 0.*50.) * 1. * 0.5 = 5.
- assertEquals(50., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(50., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs0PercentCompleteAndWeightIs05_itShouldReturnHalfOfNoFixedCosts() {
- calc.setSolutionCompletenessRatio(0.0);
- calc.setWeightOfFixCost(.5);
+ absFixedCosts.setSolutionCompletenessRatio(0.0);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.0);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.*absFix + 1.*relFix) * 0. * .5 = 0.
- assertEquals(0., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(0., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs50PercentCompleteAndWeightIs05_itShouldReturnHalfOfAvgOfRelFixedAndAbsFixedCostOfNewVehicle() {
- calc.setSolutionCompletenessRatio(0.5);
- calc.setWeightOfFixCost(.5);
+ absFixedCosts.setSolutionCompletenessRatio(0.5);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.5);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
//(0.5*absFix + 0.5*relFix) * 0.5 * 0.= (0.5*100+0.5*50)*0.5*0.5 = 18.75
- assertEquals(18.75, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(18.75, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNullAndSolutionIs75PercentCompleteAndWeightIs05_itShouldReturnHalfOfAvgOfRelFixedAndAbsFixedCostOfNewVehicle() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(0.5);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(0.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(0.5);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
//(0.75*absFix + 0.25*relFix) * 0.75 * 0.5 = (0.75*100.+0.25*50.)*0.75*0.5 = 32.8125
- assertEquals(32.8125, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(32.8125, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionComplete_itShouldReturnHalfOfFixedCostsOfNewVehicle() {
- calc.setSolutionCompletenessRatio(1.0);
- calc.setWeightOfFixCost(1.0);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(1.0);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(1.0);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ when(route.getVehicle()).thenReturn(small);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
//(1.*absFix + 0.*relFix) * completeness * weight = (1.*(100.-50.) + 0.*(50.-0.)) * 1. * 1. = 50.
- assertEquals(50., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(50., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs0PercentComplete_itShouldReturnNoFixedCosts() {
- calc.setSolutionCompletenessRatio(0.0);
- calc.setWeightOfFixCost(1.0);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.0);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.0);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ when(route.getVehicle()).thenReturn(small);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.*absFix + 1.*relFix) * completeness * weight = 0.
- assertEquals(0., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(0., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs50PercentComplete_itShouldCorrectVal() {
- calc.setSolutionCompletenessRatio(0.5);
- calc.setWeightOfFixCost(1.0);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.5);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.5);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ when(route.getVehicle()).thenReturn(small);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.5*absFix + 0.5*relFix) * 0.5 * 1. = (0.5*(100-50)+0.5*(50-0))*0.5*1. = 25.
- assertEquals(25., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(25., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs75PercentComplete_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(1.0);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(1.0);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(1.0);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.75*absFix + 0.25*relFix) * 0.75 * 1.= (0.75*(100.-50.)+0.25*(50.-0.))*0.75*1. = 37.5
- assertEquals(37.5, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(37.5, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(1.0);
- calc.setWeightOfFixCost(.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(1.0);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(1.0);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(1.*absFix + 0.*relFix) * 1. * 0.5 = (1.*(100.-50.) + 0.*(50.-0.)) * 1. * 0.5 = 25.
- assertEquals(25., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(25., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs0PercentCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.0);
- calc.setWeightOfFixCost(.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.0);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.0);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.*absFix + 1.*relFix) * 0. * .5 = 0.
- assertEquals(0., calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(0., absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs50PercentCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.5);
- calc.setWeightOfFixCost(.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.5);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.5);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.5*absFix + 0.5*relFix) * 0.5 * 0.= (0.5*(100-50)+0.5*(50-0))*0.5*0.5 = 12.5
- assertEquals(12.5, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(12.5, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndSolutionIs75PercentCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(0.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(0.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(0.5);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.75*absFix + 0.25*relFix) * 0.75 * 0.5 = (0.75*(100.-50.)+0.25*(50.-0.))*0.75*0.5 = 18.75
- assertEquals(18.75, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(18.75, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndCurrentLoadIs25AndSolutionIs50PercentCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.5);
- calc.setWeightOfFixCost(.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.5);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.5);
+ relFixedCosts.setWeightOfFixCost(.5);
+
+ when(route.getVehicle()).thenReturn(small);
when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().addDimension(0, 25).build());
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.5*absFix + 0.5*relFix) * 0.5 * 0.= (0.5*(100-50)+0.5*(75-25))*0.5*0.5 = 12.5
- assertEquals(12.5, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(12.5, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndCurrentLoadIs25AndSolutionIs75PercentCompleteAndWeightIs05_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(0.5);
- when(route.getVehicle()).thenReturn(oVehicle);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(0.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(0.5);
+
+ when(route.getVehicle()).thenReturn(small);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.75*absFix + 0.25*relFix) * 0.75 * 0.5 = (0.75*(100.-50.)+0.25*(75.-25.))*0.75*0.5 = 18.75
- assertEquals(18.75, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(18.75, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndCurrentLoadIs25AndSolutionIs50PercentCompleteAndWeightIs05WithMultipleCapDims_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(.5);
- calc.setWeightOfFixCost(.5);
+ absFixedCosts.setSolutionCompletenessRatio(.5);
+ absFixedCosts.setWeightOfFixCost(.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(.5);
+ relFixedCosts.setWeightOfFixCost(.5);
when(job.getSize()).thenReturn(Capacity.Builder.newInstance().addDimension(0, 50).addDimension(1, 0).build());
VehicleType oType = VehicleTypeImpl.Builder.newInstance("otype").addCapacityDimension(0, 50).addCapacityDimension(1, 100).setFixedCost(50.0).build();
- when(oVehicle.getType()).thenReturn(oType);
+ when(small.getType()).thenReturn(oType);
VehicleType type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 100).addCapacityDimension(1, 400).setFixedCost(100.0).build();
- when(nVehicle.getType()).thenReturn(type);
+ when(medium.getType()).thenReturn(type);
- when(route.getVehicle()).thenReturn(oVehicle);
+ when(route.getVehicle()).thenReturn(small);
+ when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().addDimension(0, 25).addDimension(1, 100).build());
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+ //(0.5*absFix + 0.5*relFix) * 0.5 * 0.= (0.5*(100-50)+0.5*(75-25))*0.5*0.5 = 12.5
+ /*
+ * (0.5*(100-50)+0.5*(
+ * relFixNew - relFixOld = (75/100+100/400)/2.*100 - ((25/50+100/100)/2.*50.) =
+ * )*0.5*0.5
+ * = (0.5*(100-50)+0.5*((75/100+100/400)/2.*100 - ((25/50+100/100)/2.*50.)))*0.5*0.5
+ * = (0.5*(100-50)+0.5*12.5)*0.5*0.5 = 7.8125
+ */
+ assertEquals(7.8125, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
+ }
+
+ @Test
+ public void whenOldVehicleIsMoreExpensive() {
+ absFixedCosts.setSolutionCompletenessRatio(1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(job.getSize()).thenReturn(Capacity.Builder.newInstance().addDimension(0, 50).addDimension(1, 0).build());
+
+ VehicleType oType = VehicleTypeImpl.Builder.newInstance("otype").addCapacityDimension(0, 50).addCapacityDimension(1, 100).setFixedCost(50.0).build();
+ when(medium.getType()).thenReturn(oType);
+
+ VehicleType type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 100).addCapacityDimension(1, 400).setFixedCost(100.0).build();
+ when(small.getType()).thenReturn(type);
+
+
+ when(route.getVehicle()).thenReturn(small);
when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().addDimension(0, 25).addDimension(1, 100).build());
//(0.5*absFix + 0.5*relFix) * 0.5 * 0.= (0.5*(100-50)+0.5*(75-25))*0.5*0.5 = 12.5
/*
@@ -249,26 +393,125 @@ public class JobInsertionConsideringFixCostsCalculatorTest {
* = (0.5*(100-50)+0.5*((75/100+100/400)/2.*100 - ((25/50+100/100)/2.*50.)))*0.5*0.5
* = (0.5*(100-50)+0.5*12.5)*0.5*0.5 = 7.8125
*/
- assertEquals(7.8125, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(-50d, insertionCost, 0.01);
+ }
+
+ @Test
+ public void smallVSMediumAbsCosts() {
+ absFixedCosts.setSolutionCompletenessRatio(1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(50d, insertionCost, 0.01);
+ }
+
+ @Test
+ public void smallVSLargeAbsCosts() {
+ absFixedCosts.setSolutionCompletenessRatio(1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(route.getVehicle()).thenReturn(small);
+ JobInsertionContext context = new JobInsertionContext(route, job, large, null, 0d);
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(150d, insertionCost, 0.01);
+ }
+
+ @Test
+ public void largeVSMediumAbsCosts() {
+ absFixedCosts.setSolutionCompletenessRatio(1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(route.getVehicle()).thenReturn(large);
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(-100d, insertionCost, 0.01);
+ }
+
+ @Test
+ public void mediumVSLargeAbsCosts() {
+ absFixedCosts.setSolutionCompletenessRatio(1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(route.getVehicle()).thenReturn(medium);
+ JobInsertionContext context = new JobInsertionContext(route, job, large, null, 0d);
+
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(100d, insertionCost, 0.01);
+ }
+
+ @Test
+ public void whenOldVehicleIsMoreExpensive2() {
+ absFixedCosts.setSolutionCompletenessRatio(0.1);
+ absFixedCosts.setWeightOfFixCost(1);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.1);
+ relFixedCosts.setWeightOfFixCost(1);
+
+ when(job.getSize()).thenReturn(Capacity.Builder.newInstance().addDimension(0, 50).addDimension(1, 0).build());
+
+ VehicleType oType = VehicleTypeImpl.Builder.newInstance("otype").addCapacityDimension(0, 50).addCapacityDimension(1, 100).setFixedCost(50.0).build();
+ when(medium.getType()).thenReturn(oType);
+
+ VehicleType type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 100).addCapacityDimension(1, 400).setFixedCost(100.0).build();
+ when(small.getType()).thenReturn(type);
+
+
+ when(route.getVehicle()).thenReturn(small);
+ when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().addDimension(0, 25).addDimension(1, 100).build());
+ /*
+ job = 50
+ abs = (50 - 100) * 0.1 * 0.1 * 1.0 = -0.5
+ rel = ( (75/50+100/100)/2 * 50 - (25/100 + 100/400)/2 * 100) * 0.9 * 0.1 = 3.375
+ c = -0.5 + 3.375 = 2.875
+
+ */
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
+
+ double insertionCost = absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context);
+ assertEquals(2.875, insertionCost, 0.01);
}
@Test
public void whenOldVehicleIsNotNullAndCurrentLoadIs25AndSolutionIs75PercentCompleteAndWeightIs05WithMultipleCapDims_itShouldReturnCorrectVal() {
- calc.setSolutionCompletenessRatio(0.75);
- calc.setWeightOfFixCost(0.5);
+ absFixedCosts.setSolutionCompletenessRatio(0.75);
+ absFixedCosts.setWeightOfFixCost(0.5);
+
+ relFixedCosts.setSolutionCompletenessRatio(0.75);
+ relFixedCosts.setWeightOfFixCost(0.5);
+
when(job.getSize()).thenReturn(Capacity.Builder.newInstance().addDimension(0, 50).addDimension(1, 0).build());
VehicleType oType = VehicleTypeImpl.Builder.newInstance("otype").addCapacityDimension(0, 50).addCapacityDimension(1, 100).setFixedCost(50.0).build();
- when(oVehicle.getType()).thenReturn(oType);
+ when(small.getType()).thenReturn(oType);
VehicleType type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 100).addCapacityDimension(1, 400).setFixedCost(100.0).build();
- when(nVehicle.getType()).thenReturn(type);
+ when(medium.getType()).thenReturn(type);
- when(route.getVehicle()).thenReturn(oVehicle);
+ when(route.getVehicle()).thenReturn(small);
when(stateGetter.getRouteState(route, InternalStates.MAXLOAD, Capacity.class)).thenReturn(Capacity.Builder.newInstance().addDimension(0, 25).addDimension(1, 100).build());
+
+ JobInsertionContext context = new JobInsertionContext(route, job, medium, null, 0d);
//(0.75*absFix + 0.25*relFix) * 0.75 * 0.5 = (0.75*(100.-50.)+0.25*12.5)*0.75*0.5 = 15.234375
- assertEquals(15.234375, calc.getInsertionData(route, job, nVehicle, 0.0, null, Double.MAX_VALUE).getInsertionCost(), 0.01);
+ assertEquals(15.234375, absFixedCosts.getCosts(context) + relFixedCosts.getCosts(context), 0.01);
}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java
index 70dd2ee3..9713744a 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java
@@ -178,7 +178,7 @@ public class ShipmentInsertionCalculatorTest {
insertionCalculator.setJobActivityFactory(activityFactory);
InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE);
- assertEquals(InsertionData.createEmptyInsertionData(), iData);
+ assertTrue(iData instanceof InsertionData.NoInsertionFound);
}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestLocalActivityInsertionCostsCalculator.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestLocalActivityInsertionCostsCalculator.java
index 66ff5063..b9e4f631 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestLocalActivityInsertionCostsCalculator.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestLocalActivityInsertionCostsCalculator.java
@@ -28,6 +28,7 @@ import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.cost.WaitingTimeCosts;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.job.Service;
+import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
@@ -91,6 +92,74 @@ public class TestLocalActivityInsertionCostsCalculator {
return Location.Builder.newInstance().setId(i).build();
}
+ @Test
+ public void whenAddingServiceBetweenDiffStartAndEnd_costMustBeCorrect() {
+ VehicleImpl v = VehicleImpl.Builder.newInstance("v")
+ .setStartLocation(Location.newInstance(0, 0))
+ .setEndLocation(Location.newInstance(20, 0))
+ .build();
+ Service s = Service.Builder.newInstance("s")
+ .setLocation(Location.newInstance(10, 0))
+ .build();
+ VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance()
+ .addVehicle(v)
+ .addJob(s)
+ .build();
+ VehicleRoute route = VehicleRoute.emptyRoute();
+ JobInsertionContext jobInsertionContext =
+ new JobInsertionContext(route, s, v, null, 0);
+ LocalActivityInsertionCostsCalculator localActivityInsertionCostsCalculator =
+ new LocalActivityInsertionCostsCalculator(
+ vrp.getTransportCosts(),
+ vrp.getActivityCosts(),
+ new StateManager(vrp));
+ double cost = localActivityInsertionCostsCalculator.getCosts(
+ jobInsertionContext,
+ new Start(v.getStartLocation(),0,Double.MAX_VALUE),
+ new End(v.getEndLocation(),0,Double.MAX_VALUE),
+ vrp.getActivities(s).get(0),
+ 0);
+ assertEquals(20., cost, Math.ulp(20.));
+ }
+
+ @Test
+ public void whenAddingShipmentBetweenDiffStartAndEnd_costMustBeCorrect() {
+ VehicleImpl v = VehicleImpl.Builder.newInstance("v")
+ .setStartLocation(Location.newInstance(0, 0))
+ .setEndLocation(Location.newInstance(20, 0))
+ .build();
+ Shipment s = Shipment.Builder.newInstance("p")
+ .setPickupLocation(Location.newInstance(10, 0))
+ .setDeliveryLocation(Location.newInstance(10, 7.5))
+ .build();
+ VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance()
+ .addVehicle(v)
+ .addJob(s)
+ .build();
+ VehicleRoute route = VehicleRoute.emptyRoute();
+ JobInsertionContext jobInsertionContext =
+ new JobInsertionContext(route, s, v, null, 0);
+ LocalActivityInsertionCostsCalculator localActivityInsertionCostsCalculator =
+ new LocalActivityInsertionCostsCalculator(
+ vrp.getTransportCosts(),
+ vrp.getActivityCosts(),
+ new StateManager(vrp));
+ double cost = localActivityInsertionCostsCalculator.getCosts(
+ jobInsertionContext,
+ new Start(v.getStartLocation(),0,Double.MAX_VALUE),
+ new End(v.getEndLocation(),0,Double.MAX_VALUE),
+ vrp.getActivities(s).get(0),
+ 0);
+ assertEquals(20., cost, Math.ulp(20.));
+ cost = localActivityInsertionCostsCalculator.getCosts(
+ jobInsertionContext,
+ vrp.getActivities(s).get(0),
+ new End(v.getEndLocation(),0,Double.MAX_VALUE),
+ vrp.getActivities(s).get(1),
+ 0);
+ assertEquals(10, cost, Math.ulp(10.));
+ }
+
@Test
public void whenInsertingActBetweenTwoRouteActs_itCalcsMarginalTpCosts() {
TourActivity prevAct = mock(TourActivity.class);
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtilTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtilTest.java
new file mode 100644
index 00000000..9a8cbbf4
--- /dev/null
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/ruin/StringUtilTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.ruin;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Created by schroeder on 13/01/17.
+ */
+public class StringUtilTest {
+
+ @Test
+ public void test() {
+ int stringLength = 4;
+ int seedIndex = 4;
+ int noActivities = 10;
+ List bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ Assert.assertEquals(4, bounds.size());
+ Assert.assertEquals(1, (int) bounds.get(0));
+ Assert.assertEquals(2, (int) bounds.get(1));
+ Assert.assertEquals(3, (int) bounds.get(2));
+ Assert.assertEquals(4, (int) bounds.get(3));
+
+ }
+
+ @Test
+ public void test2() {
+ int stringLength = 4;
+ int seedIndex = 2;
+ int noActivities = 10;
+ List bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ Assert.assertEquals(3, bounds.size());
+ Assert.assertEquals(0, (int) bounds.get(0));
+ Assert.assertEquals(1, (int) bounds.get(1));
+ Assert.assertEquals(2, (int) bounds.get(2));
+ }
+
+ @Test
+ public void test3() {
+ int stringLength = 4;
+ int seedIndex = 0;
+ int noActivities = 10;
+ List bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ Assert.assertEquals(1, bounds.size());
+ Assert.assertEquals(0, (int) bounds.get(0));
+ }
+
+ @Test
+ public void test4() {
+ int stringLength = 4;
+ int seedIndex = 9;
+ int noActivities = 10;
+ List bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ Assert.assertEquals(1, bounds.size());
+ Assert.assertEquals(6, (int) bounds.get(0));
+ }
+
+ @Test
+ public void test5() {
+ int stringLength = 4;
+ int seedIndex = 8;
+ int noActivities = 10;
+ List bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
+ Assert.assertEquals(2, bounds.size());
+ Assert.assertEquals(5, (int) bounds.get(0));
+ Assert.assertEquals(6, (int) bounds.get(1));
+ }
+}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/LocationTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/LocationTest.java
index 8119b59e..00e41666 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/LocationTest.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/LocationTest.java
@@ -18,10 +18,18 @@
package com.graphhopper.jsprit.core.problem;
-import com.graphhopper.jsprit.core.util.Coordinate;
-import junit.framework.Assert;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
import org.junit.Test;
+import com.graphhopper.jsprit.core.util.Coordinate;
+
/**
* Created by schroeder on 16.12.14.
*/
@@ -34,6 +42,12 @@ public class LocationTest {
Assert.assertTrue(true);
}
+ @Test
+ public void whenNameSet_buildLocation() {
+ Location l = Location.Builder.newInstance().setName("mystreet 6a").setIndex(1).build();
+ Assert.assertEquals("mystreet 6a",l.getName());
+ }
+
@Test
public void whenIndexSetWitFactory_returnCorrectLocation() {
Location l = Location.newInstance(1);
@@ -68,19 +82,31 @@ public class LocationTest {
@Test
public void whenCoordinateSet_build() {
Location l = Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 20)).build();
- Assert.assertEquals(10., l.getCoordinate().getX());
- Assert.assertEquals(20., l.getCoordinate().getY());
+ Assert.assertEquals(10., l.getCoordinate().getX(),0.001);
+ Assert.assertEquals(20., l.getCoordinate().getY(),0.001);
Assert.assertTrue(true);
}
@Test
public void whenCoordinateSetWithFactory_returnCorrectLocation() {
-// Location l = Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10,20)).build();
+ // Location l = Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10,20)).build();
Location l = Location.newInstance(10, 20);
- Assert.assertEquals(10., l.getCoordinate().getX());
- Assert.assertEquals(20., l.getCoordinate().getY());
+ Assert.assertEquals(10., l.getCoordinate().getX(),0.001);
+ Assert.assertEquals(20., l.getCoordinate().getY(),0.001);
Assert.assertTrue(true);
}
+ @Test
+ public void whenSettingUserData_itIsAssociatedWithTheLocation() {
+ Location one = Location.Builder.newInstance().setCoordinate(Coordinate.newInstance(10, 20))
+ .setUserData(new HashMap()).build();
+ Location two = Location.Builder.newInstance().setIndex(1).setUserData(42).build();
+ Location three = Location.Builder.newInstance().setIndex(2).build();
+
+ assertTrue(one.getUserData() instanceof Map);
+ assertEquals(42, two.getUserData());
+ assertNull(three.getUserData());
+ }
+
}
diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraintTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraintTest.java
index c3495570..717b7aef 100644
--- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraintTest.java
+++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/MaxTimeInVehicleConstraintTest.java
@@ -31,6 +31,7 @@ import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.misc.ActivityContext;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
@@ -47,7 +48,9 @@ public class MaxTimeInVehicleConstraintTest {
Delivery d1;
- Shipment shipment;
+ Shipment s1;
+
+ Shipment s2;
Delivery d2;
@@ -68,8 +71,13 @@ public class MaxTimeInVehicleConstraintTest {
private void ini(double maxTime){
d1 = Delivery.Builder.newInstance("d1").setLocation(Location.newInstance(10,0)).build();
- shipment = Shipment.Builder.newInstance("shipment").setPickupLocation(Location.newInstance(20,0))
+
+ s1 = Shipment.Builder.newInstance("s1").setPickupLocation(Location.newInstance(20,0))
.setDeliveryLocation(Location.newInstance(40,0)).setMaxTimeInVehicle(maxTime).build();
+
+ s2 = Shipment.Builder.newInstance("s2").setPickupLocation(Location.newInstance(20,0))
+ .setDeliveryLocation(Location.newInstance(40,0)).setMaxTimeInVehicle(maxTime).build();
+
d2 = Delivery.Builder.newInstance("d2").setLocation(Location.newInstance(30,0)).setServiceTime(10).build();
p1 = Pickup.Builder.newInstance("p1").setLocation(Location.newInstance(10, 0)).build();
@@ -77,11 +85,59 @@ public class MaxTimeInVehicleConstraintTest {
v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0,0)).build();
- vrp = VehicleRoutingProblem.Builder.newInstance().addJob(d1).addJob(shipment).addJob(d2).addJob(p1).addJob(p2)
+ vrp = VehicleRoutingProblem.Builder.newInstance().addJob(d1).addJob(s1).addJob(d2).addJob(p1).addJob(p2)
.addVehicle(v).build();
route = VehicleRoute.Builder.newInstance(v).setJobActivityFactory(vrp.getJobActivityFactory())
- .addDelivery(d1).addPickup(shipment).addDelivery(shipment).build();
+ .addDelivery(d1).addPickup(s1).addDelivery(s1).build();
+ }
+
+ @Test
+ public void shiftOfExistingShipmentsShouldWork(){
+ Vehicle v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0,0)).build();
+
+ Shipment s1 = Shipment.Builder.newInstance("s1").setPickupLocation(Location.newInstance(20,0))
+ .setDeliveryLocation(Location.newInstance(40,0)).setMaxTimeInVehicle(20).build();
+
+ Shipment s2 = Shipment.Builder.newInstance("s2").setPickupLocation(Location.newInstance(20,0))
+ .setPickupServiceTime(10)
+ .setDeliveryLocation(Location.newInstance(40,0)).setMaxTimeInVehicle(20).build();
+
+ VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s1).addJob(s2).addVehicle(v).build();
+
+ VehicleRoute route = VehicleRoute.Builder.newInstance(v).setJobActivityFactory(vrp.getJobActivityFactory())
+ .addPickup(s1).addDelivery(s1).build();
+
+ StateManager stateManager = new StateManager(vrp);
+ StateId latestStartId = stateManager.createStateId("latest-start-id");
+
+ UpdateMaxTimeInVehicle updater = new UpdateMaxTimeInVehicle(stateManager,latestStartId,vrp.getTransportCosts(), vrp.getActivityCosts());
+ stateManager.addStateUpdater(updater);
+ stateManager.informInsertionStarts(Arrays.asList(route),new ArrayList());
+
+ MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager, vrp);
+ JobInsertionContext c = new JobInsertionContext(route,s2,v,route.getDriver(),0.);
+ List acts = vrp.getActivities(s2);
+
+ c.getAssociatedActivities().add(acts.get(0));
+ c.getAssociatedActivities().add(acts.get(1));
+
+ Assert.assertEquals(HardActivityConstraint.ConstraintsStatus.FULFILLED, constraint.fulfilled(c, route.getStart(), acts.get(0), route.getActivities().get(0), 0));
+ Assert.assertEquals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED, constraint.fulfilled(c, act(route,0), acts.get(0), act(route,1), 20));
+ Assert.assertEquals(HardActivityConstraint.ConstraintsStatus.FULFILLED, constraint.fulfilled(c, act(route,1), acts.get(0), route.getEnd(), 40));
+
+ //insert pickup at 0
+ c.setRelatedActivityContext(new ActivityContext());
+ c.getRelatedActivityContext().setArrivalTime(20);
+ c.getRelatedActivityContext().setEndTime(30);
+ c.getRelatedActivityContext().setInsertionIndex(0);
+
+ Assert.assertEquals(HardActivityConstraint.ConstraintsStatus.FULFILLED, constraint.fulfilled(c, acts.get(0), acts.get(1), act(route,0), 30));
+ Assert.assertEquals(HardActivityConstraint.ConstraintsStatus.FULFILLED, constraint.fulfilled(c, act(route,0), acts.get(1), act(route,1), 30));
+ }
+
+ private TourActivity act(VehicleRoute route, int index){
+ return route.getActivities().get(index);
}
@Test
@@ -94,7 +150,7 @@ public class MaxTimeInVehicleConstraintTest {
stateManager.addStateUpdater(updater);
stateManager.informInsertionStarts(Arrays.asList(route),new ArrayList());
- MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager);
+ MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager, vrp);
JobInsertionContext c = new JobInsertionContext(route,d2,v,route.getDriver(),0.);
List