diff --git a/.travis.settings.xml b/.travis.settings.xml new file mode 100644 index 00000000..fd37913e --- /dev/null +++ b/.travis.settings.xml @@ -0,0 +1,27 @@ + + + + + + packagecloud-graphhopper + ${env.PACKAGECLOUD_TOKEN} + + + diff --git a/.travis.yml b/.travis.yml index 2406cbd9..40e6f9fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,20 @@ jdk: - oraclejdk7 - oraclejdk8 +deploy: + provider: script + script: "cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy" + skip_cleanup: true + on: + tags: true + +# do not install anything instead return true via unix command true +install: true +script: mvn clean test +notifications: + email: + - github@graphhopper.com + +# enable container-based stack +sudo: false + diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7957f3..7a7da75f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Change-log ========== +**v1.7.2** @ 2017-06-08 +- see [Whats new](https://github.com/graphhopper/jsprit/blob/master/WHATS_NEW.md) + +**v1.7.1** @ 2017-05-11 +- see [Whats new](https://github.com/graphhopper/jsprit/blob/master/WHATS_NEW.md) + +**v1.7** @ 2017-01-12 +- see [Whats new](https://github.com/graphhopper/jsprit/blob/master/WHATS_NEW.md) + **v1.6.2** @ 2016-02-02 new features: diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 262988d3..c5cfe7b6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,9 +1,11 @@ The following people have contributed code, bug fixes and ideas. * [Abraham Gausachs](https://github.com/agausachs) + * [Balage1551](https://github.com/balage1551) * [Giulio Collura](https://github.com/gcollura) * [Gwénaël Rault](https://github.com/braktar) * [Heinrich Filter](https://github.com/HeinrichFilter) + * [jie31best](https://github.com/jie31best) * Josh Pilkington * [Julia Loikova](https://github.com/Jullil) * [muzuro](https://github.com/muzuro) diff --git a/README.md b/README.md index 8037f981..4255b26f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ jsprit ====== [![Build Status](https://travis-ci.org/graphhopper/jsprit.svg?branch=master)](https://travis-ci.org/graphhopper/jsprit) -jsprit is a java based, open source toolkit for solving rich traveling salesman (TSP) and vehicle routing problems (VRP). -It is lightweight, flexible and easy-to-use, and based on a single all-purpose meta-heuristic currently solving +jsprit is a java based, open source toolkit for solving rich [Traveling Salesman Problems(TSP)](http://en.wikipedia.org/wiki/Travelling_salesman_problem") and [Vehicle Routing Problems(VRP)](http://neo.lcc.uma.es/vrp/vehicle-routing-problem/). +It is lightweight, flexible and easy-to-use, and based on a single all-purpose [meta-heuristic](../docs/Meta-Heuristic.md) currently solving + - Capacitated VRP - Multiple Depot VRP - VRP with Time Windows @@ -16,47 +17,45 @@ It is lightweight, flexible and easy-to-use, and based on a single all-purpose < - Various combination of these types Setting up the problem, defining additional constraints, modifying the algorithms and visualising the discovered solutions is as easy and handy as -reading classical VRP instances to benchmark your algorithm. It is fit for change and extension due to a modular design and a comprehensive set of unit and integration-tests. [More features ...](https://github.com/jsprit/jsprit/wiki/features) +reading classical VRP instances to benchmark your algorithm. It is fit for change and extension due to a modular design and a comprehensive set of unit and integration-tests. [More features ...](../docs/Features.textile) -##Getting Started and Documentation -Please visit [docs](https://github.com/graphhopper/jsprit/blob/master/docs/Home.md) to learn more. For older versions (jsprit then it is easier to keep track of your topic. -####Issue Tracker: +#### Issue Tracker: For bugs, feature requests or similar use the [issue tracker](https://github.com/jsprit/jsprit/issues). -####Email: -If you cannot get help in the mailing list or you just do not want to discuss your topic publicly, send an email to: - -info@graphhopper.com +#### Email: +If you cannot get help in the mailing list or you just do not want to discuss your topic publicly, [contact us via mail](https://graphhopper.com/#contact) -##About +## About The jsprit-project has been created by [Stefan Schröder](https://github.com/oblonski) and is maintained by [GraphHopper](https://graphhopper.com/). It is motivated by two issues. First, you can find vehicle routing problems **everywhere** in the world of distributing and moving things and people. This probably explains why there is an almost endless list of papers and algorithms to tackle these problems. However, there are only [very few open source implementations](https://github.com/graphhopper/jsprit/blob/master/docs/Other-Projects.md) and even fewer projects that can deal with real world problems that usually have many side-constraints. diff --git a/WHATS_NEW.md b/WHATS_NEW.md index 462a7cc0..cba7d625 100644 --- a/WHATS_NEW.md +++ b/WHATS_NEW.md @@ -1,15 +1,30 @@ WHATS NEW ========== ------------------------------ -?? new release **v1.7** +2017-06-08 new release **v1.7.2** +- one can now set custom properties/user defined data (see pr: https://github.com/graphhopper/jsprit/pull/348) + +For example, if one wants to enrich a service with an arbitrary number of custom properties, set a map like this: + +
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 ""; - } - - private String date(String date, String metaData) { - return ""; - } - - 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.0jsprit-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 acts = vrp.getActivities(d2); c.getAssociatedActivities().add(acts.get(0)); @@ -116,7 +172,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 acts = vrp.getActivities(d2); c.getAssociatedActivities().add(acts.get(0)); @@ -142,9 +198,9 @@ public class MaxTimeInVehicleConstraintTest { stateManager.addStateUpdater(updater); stateManager.informInsertionStarts(Arrays.asList(r),new ArrayList()); - MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager); - JobInsertionContext c = new JobInsertionContext(r,shipment,v,r.getDriver(),0.); - List acts = vrp.getActivities(shipment); + MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager, vrp); + JobInsertionContext c = new JobInsertionContext(r, s1,v,r.getDriver(),0.); + List acts = vrp.getActivities(s1); c.getAssociatedActivities().add(acts.get(0)); c.getAssociatedActivities().add(acts.get(1)); @@ -169,8 +225,8 @@ public class MaxTimeInVehicleConstraintTest { // stateManager.informInsertionStarts(Arrays.asList(r),new ArrayList()); // // MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(), vrp.getActivityCosts(), latestStartId, stateManager); -// JobInsertionContext c = new JobInsertionContext(r,shipment,v,r.getDriver(),0.); -// List acts = vrp.getActivities(shipment); +// JobInsertionContext c = new JobInsertionContext(r,s1,v,r.getDriver(),0.); +// List acts = vrp.getActivities(s1); // c.getAssociatedActivities().add(acts.get(0)); // c.getAssociatedActivities().add(acts.get(1)); // @@ -192,14 +248,14 @@ public class MaxTimeInVehicleConstraintTest { StateId latestStartId = stateManager.createStateId("latest-start-id"); Map maxTimes = new HashMap<>(); - maxTimes.put("shipment",25d); + maxTimes.put("s1",25d); UpdateMaxTimeInVehicle updater = new UpdateMaxTimeInVehicle(stateManager,latestStartId,vrp.getTransportCosts(), vrp.getActivityCosts()); stateManager.addStateUpdater(updater); stateManager.informInsertionStarts(Arrays.asList(r),new ArrayList()); - MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager); - JobInsertionContext c = new JobInsertionContext(r,shipment,v,r.getDriver(),0.); - List acts = vrp.getActivities(shipment); + MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager, vrp); + JobInsertionContext c = new JobInsertionContext(r, s1,v,r.getDriver(),0.); + List acts = vrp.getActivities(s1); c.getAssociatedActivities().add(acts.get(0)); c.getAssociatedActivities().add(acts.get(1)); @@ -223,14 +279,14 @@ public class MaxTimeInVehicleConstraintTest { StateId latestStartId = stateManager.createStateId("latest-start-id"); Map maxTimes = new HashMap<>(); - maxTimes.put("shipment",25d); + maxTimes.put("s1",25d); UpdateMaxTimeInVehicle updater = new UpdateMaxTimeInVehicle(stateManager,latestStartId,vrp.getTransportCosts(), vrp.getActivityCosts()); stateManager.addStateUpdater(updater); stateManager.informInsertionStarts(Arrays.asList(r),new ArrayList()); - MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager); - JobInsertionContext c = new JobInsertionContext(r,shipment,v,r.getDriver(),0.); - List acts = vrp.getActivities(shipment); + MaxTimeInVehicleConstraint constraint = new MaxTimeInVehicleConstraint(vrp.getTransportCosts(),vrp.getActivityCosts() , latestStartId, stateManager, vrp); + JobInsertionContext c = new JobInsertionContext(r, s1,v,r.getDriver(),0.); + List acts = vrp.getActivities(s1); c.getAssociatedActivities().add(acts.get(0)); c.getAssociatedActivities().add(acts.get(1)); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/VehicleDependentTraveledDistanceTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/VehicleDependentTraveledDistanceTest.java new file mode 100644 index 00000000..a1ed5145 --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/constraint/VehicleDependentTraveledDistanceTest.java @@ -0,0 +1,357 @@ +/* + * 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.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.cost.TransportDistance; +import com.graphhopper.jsprit.core.problem.job.Delivery; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Pickup; +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.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 com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.util.ManhattanCosts; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.*; + +import static org.mockito.Mockito.mock; + +/** + * Created by schroeder on 18/05/16. + */ +public class VehicleDependentTraveledDistanceTest { + + StateManager stateManager; + + VehicleRoute route; + + StateId traveledDistanceId; + + Vehicle vehicle; + + Vehicle vehicle2; + + VehicleRoutingProblem vrp; + + Delivery d1,d2,newDelivery; + + Pickup pickup; + + Shipment s1; + + Map maxDistanceMap; + + + @Before + public void doBefore(){ + vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0,0)).build(); + vehicle2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance(10,10)).build(); + + maxDistanceMap = new HashMap<>(); + maxDistanceMap.put(vehicle,200d); + maxDistanceMap.put(vehicle2,200d); + + d1 = Delivery.Builder.newInstance("d1").setLocation(Location.newInstance(10,10)).build(); + d2 = Delivery.Builder.newInstance("d2").setLocation(Location.newInstance(20,15)).build(); + pickup = Pickup.Builder.newInstance("pickup").setLocation(Location.newInstance(50,50)).build(); + s1 = Shipment.Builder.newInstance("s1").setPickupLocation(Location.newInstance(35,30)) + .setDeliveryLocation(Location.newInstance(20,25)).build(); + + newDelivery = Delivery.Builder.newInstance("new").setLocation(Location.newInstance(-10,10)).build(); + + vrp = VehicleRoutingProblem.Builder.newInstance() + .setRoutingCost(new ManhattanCosts()).addVehicle(vehicle).addVehicle(vehicle2) + .addJob(d1).addJob(d2).addJob(s1).addJob(pickup).addJob(newDelivery).build(); + + route = VehicleRoute.Builder.newInstance(vehicle).setJobActivityFactory(vrp.getJobActivityFactory()) + .addDelivery(d1).addDelivery(d2).addPickup(s1).addPickup(pickup).addDelivery(s1).build(); + + stateManager = new StateManager(vrp); + + traveledDistanceId = stateManager.createStateId("traveledDistance"); + + com.graphhopper.jsprit.core.algorithm.state.VehicleDependentTraveledDistance traveledDistance = + new com.graphhopper.jsprit.core.algorithm.state.VehicleDependentTraveledDistance(new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return new ManhattanCosts().getDistance(from,to,departureTime,vehicle); + } + },stateManager,traveledDistanceId,Arrays.asList(vehicle,vehicle2)); + + stateManager.addStateUpdater(traveledDistance); + stateManager.informInsertionStarts(Arrays.asList(route), Collections.emptyList()); + } + + @Test + public void whenEndLocationIsSet_constraintShouldWork(){ + VehicleImpl vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0,0)) + .setEndLocation(Location.newInstance(10,0)).build(); + Pickup pickup = Pickup.Builder.newInstance("pickup").setLocation(Location.newInstance(10,0)).build(); + vrp = VehicleRoutingProblem.Builder.newInstance().addVehicle(vehicle).addJob(pickup).build(); + route = VehicleRoute.emptyRoute(); + maxDistanceMap = new HashMap<>(); + maxDistanceMap.put(vehicle,5d); + + MaxDistanceConstraint maxDistanceConstraint = + new MaxDistanceConstraint(new StateManager(vrp), traveledDistanceId, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportTime(from,to,departureTime, null, vehicle); + } + },maxDistanceMap); + JobInsertionContext context = new JobInsertionContext(route,pickup,vehicle,null,0); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context, + new Start(vehicle.getStartLocation(),0,Double.MAX_VALUE),vrp.getActivities(pickup).get(0), + new End(vehicle.getEndLocation(),0,Double.MAX_VALUE),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + } + + /* + vehicle: 200.0 +vehicle (max distance): 200.0 +vehicle2: 160.0 +vehicle2 (max distance): 180.0 + */ + @Test + public void insertNewInVehicleShouldFail(){ + MaxDistanceConstraint maxDistanceConstraint = + new MaxDistanceConstraint(stateManager, traveledDistanceId, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportTime(from,to,departureTime, null, vehicle); + } + },maxDistanceMap); + JobInsertionContext context = new JobInsertionContext(route,newDelivery,vehicle,null,0); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,route.getStart(),newAct(),act(0),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(0),newAct(),act(1),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(1),newAct(),act(2),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(2),newAct(),act(3),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(3),newAct(),act(4),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(4),newAct(),route.getEnd(),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + } + + + @Test + public void insertNewInVehicle2ShouldBeCorrect(){ + //current distance vehicle2: 160 allowed: 200 + MaxDistanceConstraint maxDistanceConstraint = + new MaxDistanceConstraint(stateManager, traveledDistanceId, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportTime(from,to,departureTime, null, vehicle); + } + },maxDistanceMap); + JobInsertionContext context = new JobInsertionContext(route,newDelivery,vehicle2,null,0); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,route.getStart(),newAct(),act(0),0).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + //additional distance: 20+35-15=40 + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(0),newAct(),act(1),0).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + //additional distance: 35+65-30=70 + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(1),newAct(),act(2),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + //additional distance: 65+100-35 + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(2),newAct(),act(3),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + //additional distance: 100+45-55 + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(3),newAct(),act(4),0).equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)); + //additional distance: 45+20-25 + Assert.assertTrue(maxDistanceConstraint.fulfilled(context,act(4),newAct(),route.getEnd(),0).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + } + + private TourActivity act(int i) { + return route.getActivities().get(i); + } + + private TourActivity newAct(){ + return vrp.getActivities(newDelivery).get(0); + } + + @Test + public void traveledDistanceShouldBeCorrect(){ + Assert.assertEquals(20d,stateManager.getActivityState(route.getActivities().get(0),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(35d,stateManager.getActivityState(route.getActivities().get(1),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(65d,stateManager.getActivityState(route.getActivities().get(2),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(100d,stateManager.getActivityState(route.getActivities().get(3),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(155d,stateManager.getActivityState(route.getActivities().get(4),vehicle,traveledDistanceId,Double.class),0.01); + + } + + @Test + public void traveledDistanceWithVehicle2ShouldBeCorrect(){ + Assert.assertEquals(0d,stateManager.getActivityState(route.getActivities().get(0),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(15d,stateManager.getActivityState(route.getActivities().get(1),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(45d,stateManager.getActivityState(route.getActivities().get(2),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(80d,stateManager.getActivityState(route.getActivities().get(3),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(135d,stateManager.getActivityState(route.getActivities().get(4),vehicle2,traveledDistanceId,Double.class),0.01); + + } + + @Test + public void distanceOfShipmentInRoute(){ + double traveledDistanceBeforePickup = stateManager.getActivityState(route.getActivities().get(2), vehicle,traveledDistanceId, Double.class); + double traveledDistanceBeforeDelivery = stateManager.getActivityState(route.getActivities().get(4), vehicle, traveledDistanceId, Double.class); + Assert.assertEquals(90d,traveledDistanceBeforeDelivery-traveledDistanceBeforePickup,0.01); + } + + @Test + public void distanceOfShipmentInRouteVehicle2(){ + double traveledDistanceBeforePickup = stateManager.getActivityState(route.getActivities().get(2), vehicle2,traveledDistanceId, Double.class); + double traveledDistanceBeforeDelivery = stateManager.getActivityState(route.getActivities().get(4), vehicle2, traveledDistanceId, Double.class); + Assert.assertEquals(90d,traveledDistanceBeforeDelivery-traveledDistanceBeforePickup,0.01); + } + + @Test + public void distanceOfPickupInRoute(){ + double traveledDistanceBeforePickup = stateManager.getActivityState(route.getActivities().get(3), vehicle, traveledDistanceId, Double.class); + double total = stateManager.getRouteState(route, vehicle,traveledDistanceId, Double.class); + Assert.assertEquals(100d,total-traveledDistanceBeforePickup,0.01); + } + + @Test + public void distanceOfPickupInRouteVehicle2(){ + double traveledDistanceBeforePickup = stateManager.getActivityState(route.getActivities().get(3), vehicle2, traveledDistanceId, Double.class); + double total = stateManager.getRouteState(route, vehicle2,traveledDistanceId, Double.class); + Assert.assertEquals(80d,total-traveledDistanceBeforePickup,0.01); + } + + @Test + public void distanceToTravelShouldBeCorrect(){ + double total = stateManager.getRouteState(route, vehicle, traveledDistanceId, Double.class); + Assert.assertEquals(180d,total - stateManager.getActivityState(route.getActivities().get(0),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(165d, total - stateManager.getActivityState(route.getActivities().get(1), vehicle, traveledDistanceId, Double.class), 0.01); + Assert.assertEquals(135d,total - stateManager.getActivityState(route.getActivities().get(2),vehicle,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(100d, total - stateManager.getActivityState(route.getActivities().get(3), vehicle, traveledDistanceId, Double.class), 0.01); + Assert.assertEquals(45d, total - stateManager.getActivityState(route.getActivities().get(4), vehicle, traveledDistanceId, Double.class), 0.01); + + } + + @Test + public void distanceToTravelShouldBeCorrectVehicle2(){ + double total = stateManager.getRouteState(route, vehicle2, traveledDistanceId, Double.class); + Assert.assertEquals(160d,total - stateManager.getActivityState(route.getActivities().get(0),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(145d, total - stateManager.getActivityState(route.getActivities().get(1), vehicle2, traveledDistanceId, Double.class), 0.01); + Assert.assertEquals(115d,total - stateManager.getActivityState(route.getActivities().get(2),vehicle2,traveledDistanceId,Double.class),0.01); + Assert.assertEquals(80d, total - stateManager.getActivityState(route.getActivities().get(3), vehicle2, traveledDistanceId, Double.class), 0.01); + Assert.assertEquals(25d, total - stateManager.getActivityState(route.getActivities().get(4), vehicle2, traveledDistanceId, Double.class), 0.01); + + } + + @Test + public void whenAddingDeliverShipment_constraintShouldWork() { + Shipment shipment = Shipment.Builder.newInstance("s") + .setPickupLocation(Location.newInstance(0, 3)) + .setDeliveryLocation(Location.newInstance(4, 0)) + .build(); + VehicleImpl vehicle = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.newInstance(0, 0)) + .build(); + final VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() + .addJob(shipment) + .addVehicle(vehicle) + .build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + JobInsertionContext context = new JobInsertionContext(route, shipment, vehicle, null, 0); + context.getAssociatedActivities().add(vrp.getActivities(shipment).get(0)); + context.getAssociatedActivities().add(vrp.getActivities(shipment).get(1)); + maxDistanceMap = new HashMap<>(); + maxDistanceMap.put(vehicle,12d); + + StateManager stateManager = new StateManager(vrp); + MaxDistanceConstraint maxDistanceConstraint = + new MaxDistanceConstraint(stateManager, traveledDistanceId, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportTime(from,to,departureTime, null, vehicle); + } + },maxDistanceMap); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context, + new Start(vehicle.getStartLocation(), 0, Double.MAX_VALUE), + vrp.getActivities(shipment).get(0), + new End(vehicle.getEndLocation(), 0, Double.MAX_VALUE), + 0).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + + ActivityContext pickupContext = new ActivityContext(); + pickupContext.setArrivalTime(3); + pickupContext.setEndTime(3); + pickupContext.setInsertionIndex(0); + context.setRelatedActivityContext(pickupContext); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context, + vrp.getActivities(shipment).get(0), + vrp.getActivities(shipment).get(1), + new End(vehicle.getEndLocation(), 0, Double.MAX_VALUE), + 3).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + } + + @Test + public void whenAddingDeliverShipmentWithVehDiffStartEndLocs_constraintShouldWork() { + Shipment shipment = Shipment.Builder.newInstance("s") + .setPickupLocation(Location.newInstance(0, 1)) + .setDeliveryLocation(Location.newInstance(4, 1)) + .build(); + VehicleImpl vehicle = VehicleImpl.Builder.newInstance("v") + .setStartLocation(Location.newInstance(0, 0)) + .setEndLocation(Location.newInstance(0, 4)) + .build(); + final VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() + .addJob(shipment) + .addVehicle(vehicle) + .build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + JobInsertionContext context = new JobInsertionContext(route, shipment, vehicle, null, 0); + context.getAssociatedActivities().add(vrp.getActivities(shipment).get(0)); + context.getAssociatedActivities().add(vrp.getActivities(shipment).get(1)); + maxDistanceMap = new HashMap<>(); + maxDistanceMap.put(vehicle,10d); + + StateManager stateManager = new StateManager(vrp); + MaxDistanceConstraint maxDistanceConstraint = + new MaxDistanceConstraint(stateManager, traveledDistanceId, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportTime(from,to,departureTime, null, vehicle); + } + },maxDistanceMap); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context, + new Start(vehicle.getStartLocation(), 0, Double.MAX_VALUE), + vrp.getActivities(shipment).get(0), + new End(vehicle.getEndLocation(), 0, Double.MAX_VALUE), + 0).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + + ActivityContext pickupContext = new ActivityContext(); + pickupContext.setArrivalTime(1); + pickupContext.setEndTime(1); + pickupContext.setInsertionIndex(0); + context.setRelatedActivityContext(pickupContext); + Assert.assertTrue(maxDistanceConstraint.fulfilled(context, + vrp.getActivities(shipment).get(0), + vrp.getActivities(shipment).get(1), + new End(vehicle.getEndLocation(), 0, Double.MAX_VALUE), + 1).equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)); + } +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/DeliveryTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/DeliveryTest.java index 9b616a67..1391ceae 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/DeliveryTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/DeliveryTest.java @@ -17,11 +17,18 @@ */ package com.graphhopper.jsprit.core.problem.job; -import com.graphhopper.jsprit.core.problem.Location; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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 static org.junit.Assert.*; +import com.graphhopper.jsprit.core.problem.Location; public class DeliveryTest { @@ -33,9 +40,9 @@ public class DeliveryTest { @Test public void whenAddingTwoCapDimension_nuOfDimsShouldBeTwo() { Delivery one = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .addSizeDimension(0, 2) - .addSizeDimension(1, 4) - .build(); + .addSizeDimension(0, 2) + .addSizeDimension(1, 4) + .build(); assertEquals(2, one.getSize().getNuOfDimensions()); assertEquals(2, one.getSize().get(0)); assertEquals(4, one.getSize().get(1)); @@ -45,7 +52,7 @@ public class DeliveryTest { @Test public void whenPickupIsBuiltWithoutSpecifyingCapacity_itShouldHvCapWithOneDimAndDimValOfZero() { Delivery one = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(0, one.getSize().get(0)); } @@ -53,7 +60,7 @@ public class DeliveryTest { @Test public void whenPickupIsBuiltWithConstructorWhereSizeIsSpecified_capacityShouldBeSetCorrectly() { Delivery one = Delivery.Builder.newInstance("s").addSizeDimension(0, 1).setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(1, one.getSize().get(0)); } @@ -61,7 +68,7 @@ public class DeliveryTest { @Test public void whenAddingSkills_theyShouldBeAddedCorrectly() { Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); + .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("ScrewDriver")); } @@ -69,7 +76,7 @@ public class DeliveryTest { @Test public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly() { Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); + .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("drilL")); } @@ -77,7 +84,7 @@ public class DeliveryTest { @Test public void whenAddingSkillsCaseSensV2_theyShouldBeAddedCorrectly() { Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("screwDriver").build(); + .addRequiredSkill("screwDriver").build(); assertFalse(s.getRequiredSkills().containsSkill("drill")); assertFalse(s.getRequiredSkills().containsSkill("drilL")); } @@ -85,21 +92,21 @@ public class DeliveryTest { @Test public void nameShouldBeAssigned() { Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setName("name").build(); + .setName("name").build(); assertEquals("name", s.getName()); } @Test public void whenSettingPriorities_itShouldBeSetCorrectly(){ Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(3).build(); + .setPriority(3).build(); Assert.assertEquals(3, s.getPriority()); } @Test public void whenNotSettingPriorities_defaultShouldBe(){ Delivery s = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .build(); + .build(); Assert.assertEquals(2, s.getPriority()); } @@ -119,4 +126,16 @@ public class DeliveryTest { } + @Test + public void whenSettingUserData_itIsAssociatedWithTheJob() { + Delivery one = Delivery.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .setUserData(new HashMap()).build(); + Delivery two = Delivery.Builder.newInstance("s2").setLocation(Location.newInstance("loc")).setUserData(42) + .build(); + Delivery three = Delivery.Builder.newInstance("s3").setLocation(Location.newInstance("loc")).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/job/PickupTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/PickupTest.java index 6e066d07..7e2fbf6f 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/PickupTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/PickupTest.java @@ -17,11 +17,18 @@ */ package com.graphhopper.jsprit.core.problem.job; -import com.graphhopper.jsprit.core.problem.Location; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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 static org.junit.Assert.*; +import com.graphhopper.jsprit.core.problem.Location; public class PickupTest { @@ -33,9 +40,9 @@ public class PickupTest { @Test public void whenAddingTwoCapDimension_nuOfDimsShouldBeTwo() { Pickup one = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .addSizeDimension(0, 2) - .addSizeDimension(1, 4) - .build(); + .addSizeDimension(0, 2) + .addSizeDimension(1, 4) + .build(); assertEquals(2, one.getSize().getNuOfDimensions()); assertEquals(2, one.getSize().get(0)); assertEquals(4, one.getSize().get(1)); @@ -45,7 +52,7 @@ public class PickupTest { @Test public void whenPickupIsBuiltWithoutSpecifyingCapacity_itShouldHvCapWithOneDimAndDimValOfZero() { Pickup one = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(0, one.getSize().get(0)); } @@ -53,7 +60,7 @@ public class PickupTest { @Test public void whenPickupIsBuiltWithConstructorWhereSizeIsSpecified_capacityShouldBeSetCorrectly() { Pickup one = Pickup.Builder.newInstance("s").addSizeDimension(0, 1).setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(1, one.getSize().get(0)); } @@ -61,7 +68,7 @@ public class PickupTest { @Test public void whenAddingSkills_theyShouldBeAddedCorrectly() { Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); + .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("ScrewDriver")); @@ -70,7 +77,7 @@ public class PickupTest { @Test public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly() { Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); + .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("drilL")); } @@ -78,7 +85,7 @@ public class PickupTest { @Test public void whenAddingSkillsCaseSensV2_theyShouldBeAddedCorrectly() { Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("screwDriver").build(); + .addRequiredSkill("screwDriver").build(); assertFalse(s.getRequiredSkills().containsSkill("drill")); assertFalse(s.getRequiredSkills().containsSkill("drilL")); } @@ -86,7 +93,7 @@ public class PickupTest { @Test public void nameShouldBeAssigned() { Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setName("name").build(); + .setName("name").build(); assertEquals("name", s.getName()); } @@ -94,17 +101,29 @@ public class PickupTest { @Test public void whenSettingPriorities_itShouldBeSetCorrectly(){ Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(3).build(); + .setPriority(3).build(); Assert.assertEquals(3, s.getPriority()); } @Test public void whenNotSettingPriorities_defaultShouldBe(){ Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .build(); + .build(); Assert.assertEquals(2, s.getPriority()); } + @Test + public void whenSettingUserData_itIsAssociatedWithTheJob() { + Pickup one = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .setUserData(new HashMap()).build(); + Pickup two = Pickup.Builder.newInstance("s2").setLocation(Location.newInstance("loc")).setUserData(42).build(); + Pickup three = Pickup.Builder.newInstance("s3").setLocation(Location.newInstance("loc")).build(); + + assertTrue(one.getUserData() instanceof Map); + assertEquals(42, two.getUserData()); + assertNull(three.getUserData()); + } + @Test(expected = UnsupportedOperationException.class) public void whenAddingMaxTimeInVehicle_itShouldThrowEx(){ Pickup s = Pickup.Builder.newInstance("s").setLocation(Location.newInstance("loc")) diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java index 8deda4ba..9e94e18d 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java @@ -17,17 +17,25 @@ */ package com.graphhopper.jsprit.core.problem.job; -import com.graphhopper.jsprit.core.problem.Location; -import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import org.junit.Assert; import org.junit.Test; -import java.util.HashSet; -import java.util.Set; - -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsCollectionContaining.hasItem; -import static org.junit.Assert.*; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; public class ServiceTest { @@ -53,7 +61,7 @@ public class ServiceTest { Service one = Service.Builder.newInstance("service").addSizeDimension(0, 10).setLocation(Location.newInstance("foo")).build(); Service two = Service.Builder.newInstance("service").addSizeDimension(0, 10).setLocation(Location.newInstance("fo")).build(); serviceSet.add(one); -// assertTrue(serviceSet.contains(two)); + // assertTrue(serviceSet.contains(two)); serviceSet.remove(two); assertTrue(serviceSet.isEmpty()); } @@ -67,16 +75,16 @@ public class ServiceTest { @Test public void whenAddingTwoCapDimension_nuOfDimsShouldBeTwo() { Service one = Service.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .addSizeDimension(0, 2) - .addSizeDimension(1, 4) - .build(); + .addSizeDimension(0, 2) + .addSizeDimension(1, 4) + .build(); assertEquals(2, one.getSize().getNuOfDimensions()); } @Test public void whenShipmentIsBuiltWithoutSpecifyingCapacity_itShouldHvCapWithOneDimAndDimValOfZero() { Service one = Service.Builder.newInstance("s").setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(0, one.getSize().get(0)); } @@ -84,7 +92,7 @@ public class ServiceTest { @Test public void whenShipmentIsBuiltWithConstructorWhereSizeIsSpecified_capacityShouldBeSetCorrectly() { Service one = Service.Builder.newInstance("s").addSizeDimension(0, 1).setLocation(Location.newInstance("foofoo")) - .build(); + .build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(1, one.getSize().get(0)); } @@ -116,71 +124,71 @@ public class ServiceTest { } - @Test - public void whenSettingLocationCoord_itShouldBeSetCorrectly(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance(1, 2)).build(); - assertEquals(1.0,s.getLocation().getCoordinate().getX(),0.01); - assertEquals(2.0,s.getLocation().getCoordinate().getY(),0.01); + @Test + public void whenSettingLocationCoord_itShouldBeSetCorrectly(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance(1, 2)).build(); assertEquals(1.0,s.getLocation().getCoordinate().getX(),0.01); assertEquals(2.0,s.getLocation().getCoordinate().getY(),0.01); - } + assertEquals(1.0,s.getLocation().getCoordinate().getX(),0.01); + assertEquals(2.0,s.getLocation().getCoordinate().getY(),0.01); + } - @Test(expected=IllegalArgumentException.class) - public void whenSettingNeitherLocationIdNorCoord_throwsException(){ - @SuppressWarnings("unused") - Service s = Service.Builder.newInstance("s").build(); - } + @Test(expected=IllegalArgumentException.class) + public void whenSettingNeitherLocationIdNorCoord_throwsException(){ + @SuppressWarnings("unused") + Service s = Service.Builder.newInstance("s").build(); + } - @Test(expected=IllegalArgumentException.class) - public void whenServiceTimeSmallerZero_throwIllegalStateException(){ - @SuppressWarnings("unused") - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setServiceTime(-1).build(); - } + @Test(expected=IllegalArgumentException.class) + public void whenServiceTimeSmallerZero_throwIllegalStateException(){ + @SuppressWarnings("unused") + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setServiceTime(-1).build(); + } - @Test - public void whenSettingServiceTime_itShouldBeSetCorrectly(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setServiceTime(1).build(); - assertEquals(1.0,s.getServiceDuration(),0.01); - } + @Test + public void whenSettingServiceTime_itShouldBeSetCorrectly(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setServiceTime(1).build(); + assertEquals(1.0,s.getServiceDuration(),0.01); + } - @Test(expected=IllegalArgumentException.class) - public void whenTimeWindowIsNull_throwException(){ - @SuppressWarnings("unused") - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setTimeWindow(null).build(); - } + @Test(expected=IllegalArgumentException.class) + public void whenTimeWindowIsNull_throwException(){ + @SuppressWarnings("unused") + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setTimeWindow(null).build(); + } - @Test - public void whenSettingTimeWindow_itShouldBeSetCorrectly(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setTimeWindow(TimeWindow.newInstance(1.0, 2.0)).build(); - assertEquals(1.0,s.getTimeWindow().getStart(),0.01); - assertEquals(2.0,s.getTimeWindow().getEnd(),0.01); - } + @Test + public void whenSettingTimeWindow_itShouldBeSetCorrectly(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")).setTimeWindow(TimeWindow.newInstance(1.0, 2.0)).build(); + assertEquals(1.0,s.getTimeWindow().getStart(),0.01); + assertEquals(2.0,s.getTimeWindow().getEnd(),0.01); + } - @Test - public void whenAddingSkills_theyShouldBeAddedCorrectly(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); - assertTrue(s.getRequiredSkills().containsSkill("drill")); - assertTrue(s.getRequiredSkills().containsSkill("drill")); - assertTrue(s.getRequiredSkills().containsSkill("ScrewDriver")); - } + @Test + public void whenAddingSkills_theyShouldBeAddedCorrectly(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); + assertTrue(s.getRequiredSkills().containsSkill("drill")); + assertTrue(s.getRequiredSkills().containsSkill("drill")); + assertTrue(s.getRequiredSkills().containsSkill("ScrewDriver")); + } - @Test - public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); - assertTrue(s.getRequiredSkills().containsSkill("drill")); - assertTrue(s.getRequiredSkills().containsSkill("drilL")); - } + @Test + public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); + assertTrue(s.getRequiredSkills().containsSkill("drill")); + assertTrue(s.getRequiredSkills().containsSkill("drilL")); + } @Test public void whenAddingSeveralTimeWindows_itShouldBeSetCorrectly(){ TimeWindow tw1 = TimeWindow.newInstance(1.0, 2.0); TimeWindow tw2 = TimeWindow.newInstance(3.0, 5.0); Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addTimeWindow(tw1) - .addTimeWindow(tw2) - .build(); + .addTimeWindow(tw1) + .addTimeWindow(tw2) + .build(); assertEquals(2, s.getTimeWindows().size()); assertThat(s.getTimeWindows(),hasItem(is(tw1))); assertThat(s.getTimeWindows(),hasItem(is(tw2))); @@ -189,7 +197,7 @@ public class ServiceTest { @Test public void whenAddingTimeWindow_itShouldBeSetCorrectly(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addTimeWindow(TimeWindow.newInstance(1.0, 2.0)).build(); + .addTimeWindow(TimeWindow.newInstance(1.0, 2.0)).build(); assertEquals(1.0, s.getTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getTimeWindow().getEnd(), 0.01); } @@ -200,7 +208,7 @@ public class ServiceTest { @Test public void whenAddingSkillsCaseSensV2_theyShouldBeAddedCorrectly() { Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addRequiredSkill("screwDriver").build(); + .addRequiredSkill("screwDriver").build(); assertFalse(s.getRequiredSkills().containsSkill("drill")); assertFalse(s.getRequiredSkills().containsSkill("drilL")); } @@ -208,66 +216,73 @@ public class ServiceTest { @Test public void nameShouldBeAssigned() { Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setName("name").build(); + .setName("name").build(); assertEquals("name", s.getName()); } - @Test - public void shouldKnowMultipleTimeWindows(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addTimeWindow(TimeWindow.newInstance(0., 10.)).addTimeWindow(TimeWindow.newInstance(20., 30.)) - .setName("name").build(); - assertEquals(2,s.getTimeWindows().size()); - } + @Test + public void shouldKnowMultipleTimeWindows(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .addTimeWindow(TimeWindow.newInstance(0., 10.)).addTimeWindow(TimeWindow.newInstance(20., 30.)) + .setName("name").build(); + assertEquals(2,s.getTimeWindows().size()); + } - @Test(expected = IllegalArgumentException.class) - public void whenMultipleTWOverlap_throwEx(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addTimeWindow(TimeWindow.newInstance(0.,10.)) - .addTimeWindow(TimeWindow.newInstance(5., 30.)) - .setName("name").build(); - } + @Test(expected = IllegalArgumentException.class) + public void whenMultipleTWOverlap_throwEx(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .addTimeWindow(TimeWindow.newInstance(0.,10.)) + .addTimeWindow(TimeWindow.newInstance(5., 30.)) + .setName("name").build(); + } - @Test(expected = IllegalArgumentException.class) - public void whenMultipleTWOverlap2_throwEx(){ - Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .addTimeWindow(TimeWindow.newInstance(20., 30.)) - .addTimeWindow(TimeWindow.newInstance(0., 25.)) - .setName("name").build(); - } + @Test(expected = IllegalArgumentException.class) + public void whenMultipleTWOverlap2_throwEx(){ + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .addTimeWindow(TimeWindow.newInstance(20., 30.)) + .addTimeWindow(TimeWindow.newInstance(0., 25.)) + .setName("name").build(); + } @Test public void whenSettingPriorities_itShouldBeSetCorrectly(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(1).build(); + .setPriority(1).build(); Assert.assertEquals(1, s.getPriority()); } @Test public void whenSettingPriorities_itShouldBeSetCorrectly2(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(3).build(); + .setPriority(3).build(); Assert.assertEquals(3, s.getPriority()); } + @Test + public void whenSettingPriorities_itShouldBeSetCorrectly3() { + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .setPriority(10).build(); + Assert.assertEquals(10, s.getPriority()); + } + @Test public void whenNotSettingPriorities_defaultShouldBe2(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .build(); + .build(); Assert.assertEquals(2, s.getPriority()); } @Test(expected = IllegalArgumentException.class) public void whenSettingIncorrectPriorities_itShouldThrowException(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(30).build(); + .setPriority(30).build(); } @Test(expected = IllegalArgumentException.class) public void whenSettingIncorrectPriorities_itShouldThrowException2(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) - .setPriority(0).build(); + .setPriority(0).build(); } @@ -285,4 +300,17 @@ public class ServiceTest { Assert.assertEquals(Double.MAX_VALUE, s.getMaxTimeInVehicle(),0.001); } + + @Test + public void whenSettingUserData_itIsAssociatedWithTheJob() { + Service one = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .setUserData(new HashMap()).build(); + Service two = Service.Builder.newInstance("s2").setLocation(Location.newInstance("loc")).setUserData(42) + .build(); + Service three = Service.Builder.newInstance("s3").setLocation(Location.newInstance("loc")).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/job/ShipmentTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java index c9df07c5..15226680 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java @@ -17,25 +17,34 @@ */ package com.graphhopper.jsprit.core.problem.job; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +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.problem.Location; import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; import com.graphhopper.jsprit.core.util.Coordinate; import com.graphhopper.jsprit.core.util.TestUtils; -import org.junit.Assert; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsCollectionContaining.hasItem; -import static org.junit.Assert.*; public class ShipmentTest { @Test public void whenTwoShipmentsHaveTheSameId_theyReferencesShouldBeUnEqual() { Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, 10).setPickupLocation(Location.Builder.newInstance().setId("foo").build()). - setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); + setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); Shipment two = Shipment.Builder.newInstance("s").addSizeDimension(0, 10).setPickupLocation(Location.Builder.newInstance().setId("foo").build()). - setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); + setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); assertTrue(one != two); } @@ -43,9 +52,9 @@ public class ShipmentTest { @Test public void whenTwoShipmentsHaveTheSameId_theyShouldBeEqual() { Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, 10).setPickupLocation(Location.Builder.newInstance().setId("foo").build()). - setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); + setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); Shipment two = Shipment.Builder.newInstance("s").addSizeDimension(0, 10).setPickupLocation(Location.Builder.newInstance().setId("foo").build()). - setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); + setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); assertTrue(one.equals(two)); } @@ -53,7 +62,7 @@ public class ShipmentTest { @Test public void whenShipmentIsInstantiatedWithASizeOf10_theSizeShouldBe10() { Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, 10).setPickupLocation(Location.Builder.newInstance().setId("foo").build()). - setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); + setDeliveryLocation(TestUtils.loc("foofoo")).setPickupServiceTime(10).setDeliveryServiceTime(20).build(); assertEquals(10, one.getSize().get(0)); } @@ -61,24 +70,24 @@ public class ShipmentTest { public void whenShipmentIsBuiltWithNegativeDemand_itShouldThrowException() { @SuppressWarnings("unused") Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, -10) - .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) - .setDeliveryLocation(TestUtils.loc("foofoo")).build(); + .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) + .setDeliveryLocation(TestUtils.loc("foofoo")).build(); } @Test(expected = IllegalArgumentException.class) public void whenShipmentIsBuiltWithNegativeDemand_itShouldThrowException_v2() { @SuppressWarnings("unused") Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, -10) - .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) - .setDeliveryLocation(TestUtils.loc("foofoo")).build(); + .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) + .setDeliveryLocation(TestUtils.loc("foofoo")).build(); } @Test(expected = IllegalArgumentException.class) public void whenIdIsNull_itShouldThrowException() { @SuppressWarnings("unused") Shipment one = Shipment.Builder.newInstance(null).addSizeDimension(0, 10) - .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) - .setDeliveryLocation(TestUtils.loc("foofoo")).build(); + .setPickupLocation(Location.Builder.newInstance().setId("foo").build()) + .setDeliveryLocation(TestUtils.loc("foofoo")).build(); } @Test @@ -115,7 +124,7 @@ public class ShipmentTest { @Test public void whenPickupCoordIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s") - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").setCoordinate(Coordinate.newInstance(1, 2)).build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").setCoordinate(Coordinate.newInstance(1, 2)).build()).build(); assertEquals(1.0, s.getPickupLocation().getCoordinate().getX(), 0.01); assertEquals(2.0, s.getPickupLocation().getCoordinate().getY(), 0.01); assertEquals(1.0, s.getPickupLocation().getCoordinate().getX(), 0.01); @@ -126,7 +135,7 @@ public class ShipmentTest { @Test public void whenDeliveryLocationIdIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s") - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals("delLoc", s.getDeliveryLocation().getId()); assertEquals("delLoc", s.getDeliveryLocation().getId()); } @@ -135,8 +144,8 @@ public class ShipmentTest { @Test public void whenDeliveryCoordIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setDeliveryLocation(TestUtils.loc("delLoc", Coordinate.newInstance(1, 2))) - .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) - .build(); + .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) + .build(); assertEquals(1.0, s.getDeliveryLocation().getCoordinate().getX(), 0.01); assertEquals(2.0, s.getDeliveryLocation().getCoordinate().getY(), 0.01); assertEquals(1.0, s.getDeliveryLocation().getCoordinate().getX(), 0.01); @@ -146,22 +155,22 @@ public class ShipmentTest { @Test public void whenPickupServiceTimeIsNotSet_itShouldBeZero() { Shipment s = Shipment.Builder.newInstance("s") - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(0.0, s.getPickupServiceTime(), 0.01); } @Test public void whenDeliveryServiceTimeIsNotSet_itShouldBeZero() { Shipment s = Shipment.Builder.newInstance("s") - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(0.0, s.getDeliveryServiceTime(), 0.01); } @Test public void whenPickupServiceTimeIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s") - .setPickupServiceTime(2.0) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setPickupServiceTime(2.0) + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(2.0, s.getPickupServiceTime(), 0.01); } @@ -169,13 +178,13 @@ public class ShipmentTest { public void whenPickupServiceIsSmallerThanZero_itShouldThrowException() { @SuppressWarnings("unused") Shipment s = Shipment.Builder.newInstance("s").setPickupServiceTime(-2.0) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); } @Test public void whenDeliveryServiceTimeIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setDeliveryServiceTime(2.0) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(2.0, s.getDeliveryServiceTime(), 0.01); } @@ -201,7 +210,7 @@ public class ShipmentTest { @Test public void whenPickupTimeWindowIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setPickupTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getPickupTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getPickupTimeWindow().getEnd(), 0.01); } @@ -222,7 +231,7 @@ public class ShipmentTest { @Test public void whenDeliveryTimeWindowIsSet_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setDeliveryTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getDeliveryTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getDeliveryTimeWindow().getEnd(), 0.01); } @@ -230,7 +239,7 @@ public class ShipmentTest { @Test public void whenUsingAddDeliveryTimeWindow_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").addDeliveryTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getDeliveryTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getDeliveryTimeWindow().getEnd(), 0.01); } @@ -238,7 +247,7 @@ public class ShipmentTest { @Test public void whenUsingAddDeliveryTimeWindow2_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").addDeliveryTimeWindow(1, 2) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getDeliveryTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getDeliveryTimeWindow().getEnd(), 0.01); } @@ -248,7 +257,7 @@ public class ShipmentTest { TimeWindow tw1 = TimeWindow.newInstance(1,2); TimeWindow tw2 = TimeWindow.newInstance(4,5); Shipment s = Shipment.Builder.newInstance("s").addDeliveryTimeWindow(tw1).addDeliveryTimeWindow(tw2) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(s.getDeliveryTimeWindows().size(),2); assertThat(s.getDeliveryTimeWindows(),hasItem(is(tw1))); assertThat(s.getDeliveryTimeWindows(),hasItem(is(tw2))); @@ -257,7 +266,7 @@ public class ShipmentTest { @Test(expected = IllegalArgumentException.class) public void whenAddingMultipleOverlappingDeliveryTimeWindows_itShouldThrowException() { Shipment s = Shipment.Builder.newInstance("s").addDeliveryTimeWindow(1, 3).addDeliveryTimeWindow(2,5) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getDeliveryTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getDeliveryTimeWindow().getEnd(), 0.01); } @@ -267,7 +276,7 @@ public class ShipmentTest { @Test public void whenUsingAddPickupTimeWindow_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").addPickupTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getPickupTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getPickupTimeWindow().getEnd(), 0.01); } @@ -275,7 +284,7 @@ public class ShipmentTest { @Test public void whenUsingAddPickupTimeWindow2_itShouldBeDoneCorrectly() { Shipment s = Shipment.Builder.newInstance("s").addPickupTimeWindow(1, 2) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getPickupTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getPickupTimeWindow().getEnd(), 0.01); } @@ -285,7 +294,7 @@ public class ShipmentTest { TimeWindow tw1 = TimeWindow.newInstance(1,2); TimeWindow tw2 = TimeWindow.newInstance(4,5); Shipment s = Shipment.Builder.newInstance("s").addPickupTimeWindow(tw1).addPickupTimeWindow(tw2) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(s.getPickupTimeWindows().size(),2); assertThat(s.getPickupTimeWindows(), hasItem(is(tw1))); assertThat(s.getPickupTimeWindows(), hasItem(is(tw2))); @@ -294,7 +303,7 @@ public class ShipmentTest { @Test(expected = IllegalArgumentException.class) public void whenAddingMultipleOverlappingPickupTimeWindows_itShouldThrowException() { Shipment s = Shipment.Builder.newInstance("s").addPickupTimeWindow(1, 3).addPickupTimeWindow(2,5) - .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); + .setDeliveryLocation(TestUtils.loc("delLoc")).setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()).build(); assertEquals(1.0, s.getPickupTimeWindow().getStart(), 0.01); assertEquals(2.0, s.getPickupTimeWindow().getEnd(), 0.01); } @@ -305,26 +314,26 @@ public class ShipmentTest { public void whenShipmentHasNegativeCapacityVal_throwIllegalStateExpception() { @SuppressWarnings("unused") Shipment one = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("foo").build()) - .setDeliveryLocation(TestUtils.loc("foofoo")) - .addSizeDimension(0, -2) - .build(); + .setDeliveryLocation(TestUtils.loc("foofoo")) + .addSizeDimension(0, -2) + .build(); } @Test public void whenAddingTwoCapDimension_nuOfDimsShouldBeTwo() { Shipment one = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("foo").build()) - .setDeliveryLocation(TestUtils.loc("foofoo")) - .addSizeDimension(0, 2) - .addSizeDimension(1, 4) - .build(); + .setDeliveryLocation(TestUtils.loc("foofoo")) + .addSizeDimension(0, 2) + .addSizeDimension(1, 4) + .build(); assertEquals(2, one.getSize().getNuOfDimensions()); } @Test public void whenShipmentIsBuiltWithoutSpecifyingCapacity_itShouldHvCapWithOneDimAndDimValOfZero() { Shipment one = Shipment.Builder.newInstance("s") - .setPickupLocation(Location.Builder.newInstance().setId("foo").setCoordinate(Coordinate.newInstance(0, 0)).build()) - .setDeliveryLocation(TestUtils.loc("foofoo")).build(); + .setPickupLocation(Location.Builder.newInstance().setId("foo").setCoordinate(Coordinate.newInstance(0, 0)).build()) + .setDeliveryLocation(TestUtils.loc("foofoo")).build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(0, one.getSize().get(0)); } @@ -332,8 +341,8 @@ public class ShipmentTest { @Test public void whenShipmentIsBuiltWithConstructorWhereSizeIsSpecified_capacityShouldBeSetCorrectly() { Shipment one = Shipment.Builder.newInstance("s").addSizeDimension(0, 1) - .setPickupLocation(Location.Builder.newInstance().setId("foo").setCoordinate(Coordinate.newInstance(0, 0)).build()) - .setDeliveryLocation(TestUtils.loc("foofoo")).build(); + .setPickupLocation(Location.Builder.newInstance().setId("foo").setCoordinate(Coordinate.newInstance(0, 0)).build()) + .setDeliveryLocation(TestUtils.loc("foofoo")).build(); assertEquals(1, one.getSize().getNuOfDimensions()); assertEquals(1, one.getSize().get(0)); } @@ -341,8 +350,8 @@ public class ShipmentTest { @Test public void whenAddingSkills_theyShouldBeAddedCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("loc").build()) - .setDeliveryLocation(TestUtils.loc("delLoc")) - .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); + .setDeliveryLocation(TestUtils.loc("delLoc")) + .addRequiredSkill("drill").addRequiredSkill("screwdriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("ScrewDriver")); @@ -351,9 +360,9 @@ public class ShipmentTest { @Test public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly() { Shipment s = Shipment.Builder.newInstance("s") - .setPickupLocation(Location.Builder.newInstance().setId("pick").build()) - .setDeliveryLocation(TestUtils.loc("del")) - .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); + .setPickupLocation(Location.Builder.newInstance().setId("pick").build()) + .setDeliveryLocation(TestUtils.loc("del")) + .addRequiredSkill("DriLl").addRequiredSkill("screwDriver").build(); assertTrue(s.getRequiredSkills().containsSkill("drill")); assertTrue(s.getRequiredSkills().containsSkill("drilL")); } @@ -361,8 +370,8 @@ public class ShipmentTest { @Test public void whenAddingSkillsCaseSensV2_theyShouldBeAddedCorrectly() { Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("loc").build()) - .setDeliveryLocation(TestUtils.loc("del")) - .addRequiredSkill("screwDriver").build(); + .setDeliveryLocation(TestUtils.loc("del")) + .addRequiredSkill("screwDriver").build(); assertFalse(s.getRequiredSkills().containsSkill("drill")); assertFalse(s.getRequiredSkills().containsSkill("drilL")); } @@ -370,15 +379,15 @@ public class ShipmentTest { @Test public void nameShouldBeAssigned() { Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("loc").build()) - .setDeliveryLocation(TestUtils.loc("del")) - .setName("name").build(); + .setDeliveryLocation(TestUtils.loc("del")) + .setName("name").build(); assertEquals("name", s.getName()); } @Test public void whenSettingLocation_itShouldWork() { Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.Builder.newInstance().setId("loc").build()) - .setDeliveryLocation(Location.Builder.newInstance().setId("del").build()).build(); + .setDeliveryLocation(Location.Builder.newInstance().setId("del").build()).build(); assertEquals("loc", s.getPickupLocation().getId()); assertEquals("loc", s.getPickupLocation().getId()); assertEquals("del", s.getDeliveryLocation().getId()); @@ -388,43 +397,64 @@ public class ShipmentTest { @Test public void whenSettingPriorities_itShouldBeSetCorrectly(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) - .setDeliveryLocation(Location.newInstance("loc")) - .setPriority(1).build(); + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(1).build(); Assert.assertEquals(1, s.getPriority()); } @Test public void whenSettingPriorities_itShouldBeSetCorrectly2(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) - .setDeliveryLocation(Location.newInstance("loc")) - .setPriority(3).build(); + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(3).build(); Assert.assertEquals(3, s.getPriority()); } + @Test + public void whenSettingPriorities_itShouldBeSetCorrectly3() { + Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(10).build(); + Assert.assertEquals(10, s.getPriority()); + } + @Test public void whenNotSettingPriorities_defaultShouldBe2(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) - .setDeliveryLocation(Location.newInstance("loc")) - .build(); + .setDeliveryLocation(Location.newInstance("loc")) + .build(); Assert.assertEquals(2, s.getPriority()); } @Test(expected = IllegalArgumentException.class) public void whenSettingIncorrectPriorities_itShouldThrowException(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) - .setDeliveryLocation(Location.newInstance("loc")) - .setPriority(30).build(); + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(30).build(); } @Test(expected = IllegalArgumentException.class) public void whenSettingIncorrectPriorities_itShouldThrowException2(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) - .setDeliveryLocation(Location.newInstance("loc")) - .setPriority(0).build(); + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(0).build(); } + @Test + public void whenSettingUserData_itIsAssociatedWithTheJob() { + Shipment one = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) + .setDeliveryLocation(Location.newInstance("loc")).setUserData(new HashMap()).build(); + Shipment two = Shipment.Builder.newInstance("s2").setPickupLocation(Location.newInstance("loc")) + .setDeliveryLocation(Location.newInstance("loc")).setUserData(42).build(); + Shipment three = Shipment.Builder.newInstance("s3").setPickupLocation(Location.newInstance("loc")) + .setDeliveryLocation(Location.newInstance("loc")).build(); + + assertTrue(one.getUserData() instanceof Map); + assertEquals(42, two.getUserData()); + assertNull(three.getUserData()); + } @Test public void whenAddingMaxTimeInVehicle_itShouldBeSet(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")).setDeliveryLocation(Location.newInstance("loc")) diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java index cd3f7e48..99ac6ab7 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java @@ -18,12 +18,20 @@ package com.graphhopper.jsprit.core.problem.vehicle; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + import com.graphhopper.jsprit.core.problem.Location; import com.graphhopper.jsprit.core.problem.job.Break; import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; -import org.junit.Test; - -import static org.junit.Assert.*; public class VehicleImplTest { @@ -39,10 +47,10 @@ public class VehicleImplTest { @Test public void whenAddingDriverBreak_itShouldBeAddedCorrectly() { VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); - Break aBreak = (Break) Break.Builder.newInstance("break").setTimeWindow(TimeWindow.newInstance(100, 200)).setServiceTime(30).build(); + Break aBreak = Break.Builder.newInstance("break").setTimeWindow(TimeWindow.newInstance(100, 200)).setServiceTime(30).build(); Vehicle v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("start")) - .setType(type1).setEndLocation(Location.newInstance("start")) - .setBreak(aBreak).build(); + .setType(type1).setEndLocation(Location.newInstance("start")) + .setBreak(aBreak).build(); assertNotNull(v.getBreak()); assertEquals(100., v.getBreak().getTimeWindow().getStart(), 0.1); assertEquals(200., v.getBreak().getTimeWindow().getEnd(), 0.1); @@ -54,7 +62,7 @@ public class VehicleImplTest { public void whenAddingSkills_theyShouldBeAddedCorrectly() { VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); Vehicle v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("start")).setType(type1).setEndLocation(Location.newInstance("start")) - .addSkill("drill").addSkill("screwdriver").build(); + .addSkill("drill").addSkill("screwdriver").build(); assertTrue(v.getSkills().containsSkill("drill")); assertTrue(v.getSkills().containsSkill("drill")); assertTrue(v.getSkills().containsSkill("screwdriver")); @@ -64,7 +72,7 @@ public class VehicleImplTest { public void whenAddingSkillsCaseSens_theyShouldBeAddedCorrectly() { VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); Vehicle v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("start")).setType(type1).setEndLocation(Location.newInstance("start")) - .addSkill("drill").addSkill("screwdriver").build(); + .addSkill("drill").addSkill("screwdriver").build(); assertTrue(v.getSkills().containsSkill("drill")); assertTrue(v.getSkills().containsSkill("dRill")); assertTrue(v.getSkills().containsSkill("ScrewDriver")); @@ -233,9 +241,23 @@ public class VehicleImplTest { public void whenAddingSkillsCaseSensV2_theyShouldBeAddedCorrectly() { VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); Vehicle v = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("start")).setType(type1).setEndLocation(Location.newInstance("start")) - .addSkill("drill").build(); + .addSkill("drill").build(); assertFalse(v.getSkills().containsSkill("ScrewDriver")); } + @Test + public void whenSettingUserData_itIsAssociatedWithTheVehicle() { + VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + Vehicle one = VehicleImpl.Builder.newInstance("v").setType(type1) + .setStartLocation(Location.newInstance("start")).setUserData(new HashMap()).build(); + Vehicle two = VehicleImpl.Builder.newInstance("v").setType(type1) + .setStartLocation(Location.newInstance("start")).setUserData(42).build(); + Vehicle three = VehicleImpl.Builder.newInstance("v").setType(type1) + .setStartLocation(Location.newInstance("start")).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/vehicle/VehicleTypeImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java index 85478347..f0159376 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java @@ -17,9 +17,15 @@ */ package com.graphhopper.jsprit.core.problem.vehicle; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; -import static org.junit.Assert.*; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; public class VehicleTypeImplTest { @@ -32,18 +38,18 @@ public class VehicleTypeImplTest { @Test public void whenAddingTwoCapDimension_nuOfDimsShouldBeTwo() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("t") - .addCapacityDimension(0, 2) - .addCapacityDimension(1, 4) - .build(); + .addCapacityDimension(0, 2) + .addCapacityDimension(1, 4) + .build(); assertEquals(2, type.getCapacityDimensions().getNuOfDimensions()); } @Test public void whenAddingTwoCapDimension_dimValuesMustBeCorrect() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("t") - .addCapacityDimension(0, 2) - .addCapacityDimension(1, 4) - .build(); + .addCapacityDimension(0, 2) + .addCapacityDimension(1, 4) + .build(); assertEquals(2, type.getCapacityDimensions().get(0)); assertEquals(4, type.getCapacityDimensions().get(1)); } @@ -152,4 +158,15 @@ public class VehicleTypeImplTest { } + @Test + public void whenSettingUserData_itIsAssociatedWithTheVehicleType() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").setUserData(new HashMap()) + .build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setUserData(42).build(); + VehicleType three = VehicleTypeImpl.Builder.newInstance("type").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/util/FastVehicleRoutingTransportCostsMatrixTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrixTest.java index 8acb99b9..bde92c11 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrixTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrixTest.java @@ -40,6 +40,7 @@ public class FastVehicleRoutingTransportCostsMatrixTest { assertEquals(2., matrix.getDistance(2, 1), 0.1); } + @Test public void whenAddingDistanceToAsymmetricMatrix_itShouldReturnCorrectValues() { FastVehicleRoutingTransportCostsMatrix.Builder matrixBuilder = FastVehicleRoutingTransportCostsMatrix.Builder.newInstance(3, false); @@ -62,6 +63,18 @@ public class FastVehicleRoutingTransportCostsMatrixTest { assertEquals(2., matrix.getTransportTime(loc(2), loc(1), 0.0, null, null), 0.1); } + @Test + public void whenAddingTimeAndDistanceToSymmetricMatrix_itShouldReturnCorrectValues2() { + FastVehicleRoutingTransportCostsMatrix.Builder matrixBuilder = FastVehicleRoutingTransportCostsMatrix.Builder.newInstance(3, true); + matrixBuilder.addTransportTimeAndDistance(1, 2, 2.,100.); + FastVehicleRoutingTransportCostsMatrix matrix = matrixBuilder.build(); + assertEquals(2., matrix.getTransportTime(loc(1), loc(2), 0.0, null, null), 0.1); + assertEquals(2., matrix.getTransportTime(loc(2), loc(1), 0.0, null, null), 0.1); + + assertEquals(100., matrix.getDistance(loc(1), loc(2), 0.0, null), 0.1); + assertEquals(100., matrix.getDistance(loc(2), loc(1), 0.0, null), 0.1); + } + @Test public void whenAddingTimeToAsymmetricMatrix_itShouldReturnCorrectValues() { FastVehicleRoutingTransportCostsMatrix.Builder matrixBuilder = FastVehicleRoutingTransportCostsMatrix.Builder.newInstance(3, false); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java new file mode 100644 index 00000000..aee8a95d --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -0,0 +1,156 @@ +/* + * 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.VehicleRoutingAlgorithm; +import com.graphhopper.jsprit.core.algorithm.box.Jsprit; +import com.graphhopper.jsprit.core.algorithm.state.StateId; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.MaxDistanceConstraint; +import com.graphhopper.jsprit.core.problem.cost.TransportDistance; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleType; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import org.apache.commons.math3.stat.Frequency; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by schroeder on 06/02/17. + */ +public class UnassignedJobReasonTrackerTest { + + Vehicle vehicle; + + @Before + public void doBefore() { + VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType").addCapacityDimension(0, 1); + VehicleType vehicleType = vehicleTypeBuilder.build(); + VehicleImpl.Builder vehicleBuilder = VehicleImpl.Builder.newInstance("vehicle"); + vehicleBuilder.setStartLocation(Location.newInstance(10, 10)); + vehicleBuilder.setType(vehicleType); + vehicleBuilder.setEarliestStart(0).setLatestArrival(100); + vehicle = vehicleBuilder.build(); + } + + @Test + public void shouldReturnCorrectCapacityReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 5).setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(3, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectSkillReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 1).addRequiredSkill("ice").setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(1, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectTWReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 1).setTimeWindow(TimeWindow.newInstance(110, 200)).setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(2, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectMaxDistanceReasonCode() { + Service service = Service.Builder.newInstance("1").setLocation(Location.newInstance(51, 0)).build(); + + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0, 0)).build(); + + final VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service).build(); + + StateManager stateManager = new StateManager(vrp); + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + StateId maxDistance = stateManager.createStateId("max-distance"); + Map distMap = new HashMap<>(); + distMap.put(vehicle, 100d); + MaxDistanceConstraint distanceConstraint = new MaxDistanceConstraint(stateManager, maxDistance, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportCost(from, to, departureTime, null, vehicle); + } + }, distMap); + constraintManager.addConstraint(distanceConstraint, ConstraintManager.Priority.CRITICAL); + + VehicleRoutingAlgorithm vra = Jsprit.Builder.newInstance(vrp).setStateAndConstraintManager(stateManager, constraintManager) + .buildAlgorithm(); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(4, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void testFreq() { + Frequency frequency = new Frequency(); + frequency.addValue("VehicleDependentTimeWindowHardActivityConstraint"); + frequency.addValue("b"); + frequency.addValue("VehicleDependentTimeWindowHardActivityConstraint"); + + Iterator, Long>> entryIterator = frequency.entrySetIterator(); + while (entryIterator.hasNext()) { + Map.Entry, Long> e = entryIterator.next(); + System.out.println(e.getKey().toString() + " " + e.getValue()); + } + } +} diff --git a/jsprit-examples/input/p01_mod b/jsprit-examples/input/p01_mod new file mode 100644 index 00000000..f7126b87 --- /dev/null +++ b/jsprit-examples/input/p01_mod @@ -0,0 +1,56 @@ +2 4 50 4 +0 2000 + 1 37 52 0 7 1 4 1 2 4 8 + 2 49 49 0 30 1 4 1 2 4 8 + 3 52 64 0 16 1 4 1 2 4 8 + 4 20 26 0 9 1 4 1 2 4 8 + 5 40 30 0 21 1 4 1 2 4 8 + 6 21 47 0 15 1 4 1 2 4 8 + 7 17 63 0 19 1 4 1 2 4 8 + 8 31 62 0 23 1 4 1 2 4 8 + 9 52 33 0 11 1 4 1 2 4 8 +10 51 21 0 5 1 4 1 2 4 8 +11 42 41 0 19 1 4 1 2 4 8 +12 31 32 0 29 1 4 1 2 4 8 +13 5 25 0 23 1 4 1 2 4 8 +14 12 42 0 21 1 4 1 2 4 8 +15 36 16 0 10 1 4 1 2 4 8 +16 52 41 0 15 1 4 1 2 4 8 +17 27 23 0 3 1 4 1 2 4 8 +18 17 33 0 41 1 4 1 2 4 8 +19 13 13 0 9 1 4 1 2 4 8 +20 57 58 0 28 1 4 1 2 4 8 +21 62 42 0 8 1 4 1 2 4 8 +22 42 57 0 8 1 4 1 2 4 8 +23 16 57 0 16 1 4 1 2 4 8 +24 8 52 0 10 1 4 1 2 4 8 +25 7 38 0 28 1 4 1 2 4 8 +26 27 68 0 7 1 4 1 2 4 8 +27 30 48 0 15 1 4 1 2 4 8 +28 43 67 0 14 1 4 1 2 4 8 +29 58 48 0 6 1 4 1 2 4 8 +30 58 27 0 19 1 4 1 2 4 8 +31 37 69 0 11 1 4 1 2 4 8 +32 38 46 0 12 1 4 1 2 4 8 +33 46 10 0 23 1 4 1 2 4 8 +34 61 33 0 26 1 4 1 2 4 8 +35 62 63 0 17 1 4 1 2 4 8 +36 63 69 0 6 1 4 1 2 4 8 +37 32 22 0 9 1 4 1 2 4 8 +38 45 35 0 15 1 4 1 2 4 8 +39 59 15 0 14 1 4 1 2 4 8 +40 5 6 0 7 1 4 1 2 4 8 +41 10 17 0 27 1 4 1 2 4 8 +42 21 10 0 13 1 4 1 2 4 8 +43 5 64 0 11 1 4 1 2 4 8 +44 30 15 0 16 1 4 1 2 4 8 +45 39 10 0 10 1 4 1 2 4 8 +46 32 39 0 5 1 4 1 2 4 8 +47 25 32 0 25 1 4 1 2 4 8 +48 25 55 0 17 1 4 1 2 4 8 +49 48 28 0 18 1 4 1 2 4 8 +50 56 37 0 10 1 4 1 2 4 8 +51 20 20 0 0 0 0 +52 30 40 0 0 0 0 +53 50 30 0 0 0 0 +54 60 50 0 0 0 0 diff --git a/jsprit-examples/input/p08 b/jsprit-examples/input/p08 index e272d5d8..a2ab1f28 100644 --- a/jsprit-examples/input/p08 +++ b/jsprit-examples/input/p08 @@ -1,254 +1,254 @@ -2 14 249 2 -310 500 -310 500 - 1 -99 -97 0 6 1 2 1 2 - 2 -59 50 0 72 1 2 1 2 - 3 0 14 0 93 1 2 1 2 - 4 -17 -66 0 28 1 2 1 2 - 5 -69 -19 0 5 1 2 1 2 - 6 31 12 0 43 1 2 1 2 - 7 5 -41 0 1 1 2 1 2 - 8 -12 10 0 36 1 2 1 2 - 9 -64 70 0 53 1 2 1 2 - 10 -12 85 0 63 1 2 1 2 - 11 -18 64 0 25 1 2 1 2 - 12 -77 -16 0 50 1 2 1 2 - 13 -53 88 0 57 1 2 1 2 - 14 83 -24 0 1 1 2 1 2 - 15 24 41 0 66 1 2 1 2 - 16 17 21 0 37 1 2 1 2 - 17 42 96 0 51 1 2 1 2 - 18 -65 0 0 47 1 2 1 2 - 19 -47 -26 0 88 1 2 1 2 - 20 85 36 0 75 1 2 1 2 - 21 -35 -54 0 48 1 2 1 2 - 22 54 -21 0 40 1 2 1 2 - 23 64 -17 0 8 1 2 1 2 - 24 55 89 0 69 1 2 1 2 - 25 17 -25 0 93 1 2 1 2 - 26 -61 66 0 29 1 2 1 2 - 27 -61 26 0 5 1 2 1 2 - 28 17 -72 0 53 1 2 1 2 - 29 79 38 0 8 1 2 1 2 - 30 -62 -2 0 24 1 2 1 2 - 31 -90 -68 0 53 1 2 1 2 - 32 52 66 0 13 1 2 1 2 - 33 -54 -50 0 47 1 2 1 2 - 34 8 -84 0 57 1 2 1 2 - 35 37 -90 0 9 1 2 1 2 - 36 -83 49 0 74 1 2 1 2 - 37 35 -1 0 83 1 2 1 2 - 38 7 59 0 96 1 2 1 2 - 39 12 48 0 42 1 2 1 2 - 40 57 95 0 80 1 2 1 2 - 41 92 28 0 22 1 2 1 2 - 42 -3 97 0 56 1 2 1 2 - 43 -7 52 0 43 1 2 1 2 - 44 42 -15 0 12 1 2 1 2 - 45 77 -43 0 73 1 2 1 2 - 46 59 -49 0 32 1 2 1 2 - 47 25 91 0 8 1 2 1 2 - 48 69 -19 0 79 1 2 1 2 - 49 -82 -14 0 79 1 2 1 2 - 50 74 -70 0 4 1 2 1 2 - 51 69 59 0 14 1 2 1 2 - 52 29 33 0 17 1 2 1 2 - 53 -97 9 0 19 1 2 1 2 - 54 -58 9 0 44 1 2 1 2 - 55 28 93 0 5 1 2 1 2 - 56 7 73 0 37 1 2 1 2 - 57 -28 73 0 100 1 2 1 2 - 58 -76 55 0 62 1 2 1 2 - 59 41 42 0 90 1 2 1 2 - 60 92 40 0 57 1 2 1 2 - 61 -84 -29 0 44 1 2 1 2 - 62 -12 42 0 37 1 2 1 2 - 63 51 -45 0 80 1 2 1 2 - 64 -37 46 0 60 1 2 1 2 - 65 -97 35 0 95 1 2 1 2 - 66 14 89 0 56 1 2 1 2 - 67 60 58 0 56 1 2 1 2 - 68 -63 -75 0 9 1 2 1 2 - 69 -18 34 0 39 1 2 1 2 - 70 -46 -82 0 15 1 2 1 2 - 71 -86 -79 0 4 1 2 1 2 - 72 -43 -30 0 58 1 2 1 2 - 73 -44 7 0 73 1 2 1 2 - 74 -3 -20 0 5 1 2 1 2 - 75 36 41 0 12 1 2 1 2 - 76 -30 -94 0 3 1 2 1 2 - 77 79 -62 0 8 1 2 1 2 - 78 51 70 0 31 1 2 1 2 - 79 -61 -26 0 48 1 2 1 2 - 80 6 94 0 3 1 2 1 2 - 81 -19 -62 0 52 1 2 1 2 - 82 -20 51 0 99 1 2 1 2 - 83 -81 37 0 29 1 2 1 2 - 84 7 31 0 12 1 2 1 2 - 85 52 12 0 50 1 2 1 2 - 86 83 -91 0 98 1 2 1 2 - 87 -7 -92 0 4 1 2 1 2 - 88 82 -74 0 56 1 2 1 2 - 89 -70 85 0 24 1 2 1 2 - 90 -83 -30 0 33 1 2 1 2 - 91 71 -61 0 45 1 2 1 2 - 92 85 11 0 98 1 2 1 2 - 93 66 -48 0 4 1 2 1 2 - 94 78 -87 0 36 1 2 1 2 - 95 9 -79 0 72 1 2 1 2 - 96 -36 4 0 26 1 2 1 2 - 97 66 39 0 71 1 2 1 2 - 98 92 -17 0 84 1 2 1 2 - 99 -46 -79 0 21 1 2 1 2 -100 -30 -63 0 99 1 2 1 2 -101 -42 63 0 33 1 2 1 2 -102 20 42 0 84 1 2 1 2 -103 15 98 0 74 1 2 1 2 -104 1 -17 0 93 1 2 1 2 -105 64 20 0 25 1 2 1 2 -106 -96 85 0 39 1 2 1 2 -107 93 -29 0 42 1 2 1 2 -108 -40 -84 0 77 1 2 1 2 -109 86 35 0 68 1 2 1 2 -110 91 36 0 50 1 2 1 2 -111 62 -8 0 42 1 2 1 2 -112 -24 4 0 71 1 2 1 2 -113 11 96 0 85 1 2 1 2 -114 -53 62 0 78 1 2 1 2 -115 -28 -71 0 64 1 2 1 2 -116 7 -4 0 5 1 2 1 2 -117 95 -9 0 93 1 2 1 2 -118 -3 17 0 18 1 2 1 2 -119 53 -90 0 38 1 2 1 2 -120 58 -19 0 29 1 2 1 2 -121 -83 84 0 81 1 2 1 2 -122 -1 49 0 4 1 2 1 2 -123 -4 17 0 23 1 2 1 2 -124 -82 -3 0 11 1 2 1 2 -125 -43 47 0 86 1 2 1 2 -126 6 -6 0 2 1 2 1 2 -127 70 99 0 31 1 2 1 2 -128 68 -29 0 54 1 2 1 2 -129 -94 -30 0 87 1 2 1 2 -130 -94 -20 0 17 1 2 1 2 -131 -21 77 0 81 1 2 1 2 -132 64 37 0 72 1 2 1 2 -133 -70 -19 0 10 1 2 1 2 -134 88 65 0 50 1 2 1 2 -135 2 29 0 25 1 2 1 2 -136 33 57 0 71 1 2 1 2 -137 -70 6 0 85 1 2 1 2 -138 -38 -56 0 51 1 2 1 2 -139 -80 -95 0 29 1 2 1 2 -140 -5 -39 0 55 1 2 1 2 -141 8 -22 0 45 1 2 1 2 -142 -61 -76 0 100 1 2 1 2 -143 76 -22 0 38 1 2 1 2 -144 49 -71 0 11 1 2 1 2 -145 -30 -68 0 82 1 2 1 2 -146 1 34 0 50 1 2 1 2 -147 77 79 0 39 1 2 1 2 -148 -58 64 0 6 1 2 1 2 -149 82 -97 0 87 1 2 1 2 -150 -80 55 0 83 1 2 1 2 -151 81 -86 0 22 1 2 1 2 -152 39 -49 0 24 1 2 1 2 -153 -67 72 0 69 1 2 1 2 -154 -25 -89 0 97 1 2 1 2 -155 -44 -95 0 65 1 2 1 2 -156 32 -68 0 97 1 2 1 2 -157 -17 49 0 79 1 2 1 2 -158 93 49 0 79 1 2 1 2 -159 99 81 0 46 1 2 1 2 -160 10 -49 0 52 1 2 1 2 -161 63 -41 0 39 1 2 1 2 -162 38 39 0 94 1 2 1 2 -163 -28 39 0 97 1 2 1 2 -164 -2 -47 0 18 1 2 1 2 -165 38 8 0 3 1 2 1 2 -166 -42 -6 0 23 1 2 1 2 -167 -67 88 0 19 1 2 1 2 -168 19 93 0 40 1 2 1 2 -169 40 27 0 49 1 2 1 2 -170 -61 56 0 96 1 2 1 2 -171 43 33 0 58 1 2 1 2 -172 -18 -39 0 15 1 2 1 2 -173 -69 19 0 21 1 2 1 2 -174 75 -18 0 56 1 2 1 2 -175 31 85 0 67 1 2 1 2 -176 25 58 0 10 1 2 1 2 -177 -16 36 0 36 1 2 1 2 -178 91 15 0 84 1 2 1 2 -179 60 -39 0 59 1 2 1 2 -180 49 -47 0 85 1 2 1 2 -181 42 33 0 60 1 2 1 2 -182 16 -81 0 33 1 2 1 2 -183 -78 53 0 62 1 2 1 2 -184 53 -80 0 70 1 2 1 2 -185 -46 -26 0 79 1 2 1 2 -186 -25 -54 0 98 1 2 1 2 -187 69 -46 0 99 1 2 1 2 -188 0 -78 0 18 1 2 1 2 -189 -84 74 0 55 1 2 1 2 -190 -16 16 0 75 1 2 1 2 -191 -63 -14 0 94 1 2 1 2 -192 51 -77 0 89 1 2 1 2 -193 -39 61 0 13 1 2 1 2 -194 5 97 0 19 1 2 1 2 -195 -55 39 0 19 1 2 1 2 -196 70 -14 0 90 1 2 1 2 -197 0 95 0 35 1 2 1 2 -198 -45 7 0 76 1 2 1 2 -199 38 -24 0 3 1 2 1 2 -200 50 -37 0 11 1 2 1 2 -201 59 71 0 98 1 2 1 2 -202 -73 -96 0 92 1 2 1 2 -203 -29 72 0 1 1 2 1 2 -204 -47 12 0 2 1 2 1 2 -205 -88 -61 0 63 1 2 1 2 -206 -88 36 0 57 1 2 1 2 -207 -46 -3 0 50 1 2 1 2 -208 26 -37 0 19 1 2 1 2 -209 -39 -67 0 24 1 2 1 2 -210 92 27 0 14 1 2 1 2 -211 -80 -31 0 18 1 2 1 2 -212 93 -50 0 77 1 2 1 2 -213 -20 -5 0 28 1 2 1 2 -214 -22 73 0 72 1 2 1 2 -215 -4 -7 0 49 1 2 1 2 -216 54 -48 0 58 1 2 1 2 -217 -70 39 0 84 1 2 1 2 -218 54 -82 0 58 1 2 1 2 -219 29 41 0 41 1 2 1 2 -220 -87 51 0 98 1 2 1 2 -221 -96 -36 0 77 1 2 1 2 -222 49 8 0 57 1 2 1 2 -223 -5 54 0 39 1 2 1 2 -224 -26 43 0 99 1 2 1 2 -225 -11 60 0 83 1 2 1 2 -226 40 61 0 54 1 2 1 2 -227 82 35 0 86 1 2 1 2 -228 -92 12 0 2 1 2 1 2 -229 -93 -86 0 14 1 2 1 2 -230 -66 63 0 42 1 2 1 2 -231 -72 -87 0 14 1 2 1 2 -232 -57 -84 0 55 1 2 1 2 -233 23 52 0 2 1 2 1 2 -234 -56 -62 0 18 1 2 1 2 -235 -19 59 0 17 1 2 1 2 -236 63 -14 0 22 1 2 1 2 -237 -13 38 0 28 1 2 1 2 -238 -19 87 0 3 1 2 1 2 -239 44 -84 0 96 1 2 1 2 -240 98 -17 0 53 1 2 1 2 -241 -16 62 0 15 1 2 1 2 -242 3 66 0 36 1 2 1 2 -243 26 22 0 98 1 2 1 2 -244 -38 -81 0 78 1 2 1 2 -245 70 -80 0 92 1 2 1 2 -246 17 -35 0 65 1 2 1 2 -247 96 -83 0 64 1 2 1 2 -248 -77 80 0 43 1 2 1 2 -249 -14 44 0 50 1 2 1 2 -250 -33 33 0 0 0 0 -251 33 -33 0 0 0 0 +2 14 249 2 +310 500 +310 500 + 1 -99 -97 0 6 1 2 1 2 + 2 -59 50 0 72 1 2 1 2 + 3 0 14 0 93 1 2 1 2 + 4 -17 -66 0 28 1 2 1 2 + 5 -69 -19 0 5 1 2 1 2 + 6 31 12 0 43 1 2 1 2 + 7 5 -41 0 1 1 2 1 2 + 8 -12 10 0 36 1 2 1 2 + 9 -64 70 0 53 1 2 1 2 + 10 -12 85 0 63 1 2 1 2 + 11 -18 64 0 25 1 2 1 2 + 12 -77 -16 0 50 1 2 1 2 + 13 -53 88 0 57 1 2 1 2 + 14 83 -24 0 1 1 2 1 2 + 15 24 41 0 66 1 2 1 2 + 16 17 21 0 37 1 2 1 2 + 17 42 96 0 51 1 2 1 2 + 18 -65 0 0 47 1 2 1 2 + 19 -47 -26 0 88 1 2 1 2 + 20 85 36 0 75 1 2 1 2 + 21 -35 -54 0 48 1 2 1 2 + 22 54 -21 0 40 1 2 1 2 + 23 64 -17 0 8 1 2 1 2 + 24 55 89 0 69 1 2 1 2 + 25 17 -25 0 93 1 2 1 2 + 26 -61 66 0 29 1 2 1 2 + 27 -61 26 0 5 1 2 1 2 + 28 17 -72 0 53 1 2 1 2 + 29 79 38 0 8 1 2 1 2 + 30 -62 -2 0 24 1 2 1 2 + 31 -90 -68 0 53 1 2 1 2 + 32 52 66 0 13 1 2 1 2 + 33 -54 -50 0 47 1 2 1 2 + 34 8 -84 0 57 1 2 1 2 + 35 37 -90 0 9 1 2 1 2 + 36 -83 49 0 74 1 2 1 2 + 37 35 -1 0 83 1 2 1 2 + 38 7 59 0 96 1 2 1 2 + 39 12 48 0 42 1 2 1 2 + 40 57 95 0 80 1 2 1 2 + 41 92 28 0 22 1 2 1 2 + 42 -3 97 0 56 1 2 1 2 + 43 -7 52 0 43 1 2 1 2 + 44 42 -15 0 12 1 2 1 2 + 45 77 -43 0 73 1 2 1 2 + 46 59 -49 0 32 1 2 1 2 + 47 25 91 0 8 1 2 1 2 + 48 69 -19 0 79 1 2 1 2 + 49 -82 -14 0 79 1 2 1 2 + 50 74 -70 0 4 1 2 1 2 + 51 69 59 0 14 1 2 1 2 + 52 29 33 0 17 1 2 1 2 + 53 -97 9 0 19 1 2 1 2 + 54 -58 9 0 44 1 2 1 2 + 55 28 93 0 5 1 2 1 2 + 56 7 73 0 37 1 2 1 2 + 57 -28 73 0 100 1 2 1 2 + 58 -76 55 0 62 1 2 1 2 + 59 41 42 0 90 1 2 1 2 + 60 92 40 0 57 1 2 1 2 + 61 -84 -29 0 44 1 2 1 2 + 62 -12 42 0 37 1 2 1 2 + 63 51 -45 0 80 1 2 1 2 + 64 -37 46 0 60 1 2 1 2 + 65 -97 35 0 95 1 2 1 2 + 66 14 89 0 56 1 2 1 2 + 67 60 58 0 56 1 2 1 2 + 68 -63 -75 0 9 1 2 1 2 + 69 -18 34 0 39 1 2 1 2 + 70 -46 -82 0 15 1 2 1 2 + 71 -86 -79 0 4 1 2 1 2 + 72 -43 -30 0 58 1 2 1 2 + 73 -44 7 0 73 1 2 1 2 + 74 -3 -20 0 5 1 2 1 2 + 75 36 41 0 12 1 2 1 2 + 76 -30 -94 0 3 1 2 1 2 + 77 79 -62 0 8 1 2 1 2 + 78 51 70 0 31 1 2 1 2 + 79 -61 -26 0 48 1 2 1 2 + 80 6 94 0 3 1 2 1 2 + 81 -19 -62 0 52 1 2 1 2 + 82 -20 51 0 99 1 2 1 2 + 83 -81 37 0 29 1 2 1 2 + 84 7 31 0 12 1 2 1 2 + 85 52 12 0 50 1 2 1 2 + 86 83 -91 0 98 1 2 1 2 + 87 -7 -92 0 4 1 2 1 2 + 88 82 -74 0 56 1 2 1 2 + 89 -70 85 0 24 1 2 1 2 + 90 -83 -30 0 33 1 2 1 2 + 91 71 -61 0 45 1 2 1 2 + 92 85 11 0 98 1 2 1 2 + 93 66 -48 0 4 1 2 1 2 + 94 78 -87 0 36 1 2 1 2 + 95 9 -79 0 72 1 2 1 2 + 96 -36 4 0 26 1 2 1 2 + 97 66 39 0 71 1 2 1 2 + 98 92 -17 0 84 1 2 1 2 + 99 -46 -79 0 21 1 2 1 2 +100 -30 -63 0 99 1 2 1 2 +101 -42 63 0 33 1 2 1 2 +102 20 42 0 84 1 2 1 2 +103 15 98 0 74 1 2 1 2 +104 1 -17 0 93 1 2 1 2 +105 64 20 0 25 1 2 1 2 +106 -96 85 0 39 1 2 1 2 +107 93 -29 0 42 1 2 1 2 +108 -40 -84 0 77 1 2 1 2 +109 86 35 0 68 1 2 1 2 +110 91 36 0 50 1 2 1 2 +111 62 -8 0 42 1 2 1 2 +112 -24 4 0 71 1 2 1 2 +113 11 96 0 85 1 2 1 2 +114 -53 62 0 78 1 2 1 2 +115 -28 -71 0 64 1 2 1 2 +116 7 -4 0 5 1 2 1 2 +117 95 -9 0 93 1 2 1 2 +118 -3 17 0 18 1 2 1 2 +119 53 -90 0 38 1 2 1 2 +120 58 -19 0 29 1 2 1 2 +121 -83 84 0 81 1 2 1 2 +122 -1 49 0 4 1 2 1 2 +123 -4 17 0 23 1 2 1 2 +124 -82 -3 0 11 1 2 1 2 +125 -43 47 0 86 1 2 1 2 +126 6 -6 0 2 1 2 1 2 +127 70 99 0 31 1 2 1 2 +128 68 -29 0 54 1 2 1 2 +129 -94 -30 0 87 1 2 1 2 +130 -94 -20 0 17 1 2 1 2 +131 -21 77 0 81 1 2 1 2 +132 64 37 0 72 1 2 1 2 +133 -70 -19 0 10 1 2 1 2 +134 88 65 0 50 1 2 1 2 +135 2 29 0 25 1 2 1 2 +136 33 57 0 71 1 2 1 2 +137 -70 6 0 85 1 2 1 2 +138 -38 -56 0 51 1 2 1 2 +139 -80 -95 0 29 1 2 1 2 +140 -5 -39 0 55 1 2 1 2 +141 8 -22 0 45 1 2 1 2 +142 -61 -76 0 100 1 2 1 2 +143 76 -22 0 38 1 2 1 2 +144 49 -71 0 11 1 2 1 2 +145 -30 -68 0 82 1 2 1 2 +146 1 34 0 50 1 2 1 2 +147 77 79 0 39 1 2 1 2 +148 -58 64 0 6 1 2 1 2 +149 82 -97 0 87 1 2 1 2 +150 -80 55 0 83 1 2 1 2 +151 81 -86 0 22 1 2 1 2 +152 39 -49 0 24 1 2 1 2 +153 -67 72 0 69 1 2 1 2 +154 -25 -89 0 97 1 2 1 2 +155 -44 -95 0 65 1 2 1 2 +156 32 -68 0 97 1 2 1 2 +157 -17 49 0 79 1 2 1 2 +158 93 49 0 79 1 2 1 2 +159 99 81 0 46 1 2 1 2 +160 10 -49 0 52 1 2 1 2 +161 63 -41 0 39 1 2 1 2 +162 38 39 0 94 1 2 1 2 +163 -28 39 0 97 1 2 1 2 +164 -2 -47 0 18 1 2 1 2 +165 38 8 0 3 1 2 1 2 +166 -42 -6 0 23 1 2 1 2 +167 -67 88 0 19 1 2 1 2 +168 19 93 0 40 1 2 1 2 +169 40 27 0 49 1 2 1 2 +170 -61 56 0 96 1 2 1 2 +171 43 33 0 58 1 2 1 2 +172 -18 -39 0 15 1 2 1 2 +173 -69 19 0 21 1 2 1 2 +174 75 -18 0 56 1 2 1 2 +175 31 85 0 67 1 2 1 2 +176 25 58 0 10 1 2 1 2 +177 -16 36 0 36 1 2 1 2 +178 91 15 0 84 1 2 1 2 +179 60 -39 0 59 1 2 1 2 +180 49 -47 0 85 1 2 1 2 +181 42 33 0 60 1 2 1 2 +182 16 -81 0 33 1 2 1 2 +183 -78 53 0 62 1 2 1 2 +184 53 -80 0 70 1 2 1 2 +185 -46 -26 0 79 1 2 1 2 +186 -25 -54 0 98 1 2 1 2 +187 69 -46 0 99 1 2 1 2 +188 0 -78 0 18 1 2 1 2 +189 -84 74 0 55 1 2 1 2 +190 -16 16 0 75 1 2 1 2 +191 -63 -14 0 94 1 2 1 2 +192 51 -77 0 89 1 2 1 2 +193 -39 61 0 13 1 2 1 2 +194 5 97 0 19 1 2 1 2 +195 -55 39 0 19 1 2 1 2 +196 70 -14 0 90 1 2 1 2 +197 0 95 0 35 1 2 1 2 +198 -45 7 0 76 1 2 1 2 +199 38 -24 0 3 1 2 1 2 +200 50 -37 0 11 1 2 1 2 +201 59 71 0 98 1 2 1 2 +202 -73 -96 0 92 1 2 1 2 +203 -29 72 0 1 1 2 1 2 +204 -47 12 0 2 1 2 1 2 +205 -88 -61 0 63 1 2 1 2 +206 -88 36 0 57 1 2 1 2 +207 -46 -3 0 50 1 2 1 2 +208 26 -37 0 19 1 2 1 2 +209 -39 -67 0 24 1 2 1 2 +210 92 27 0 14 1 2 1 2 +211 -80 -31 0 18 1 2 1 2 +212 93 -50 0 77 1 2 1 2 +213 -20 -5 0 28 1 2 1 2 +214 -22 73 0 72 1 2 1 2 +215 -4 -7 0 49 1 2 1 2 +216 54 -48 0 58 1 2 1 2 +217 -70 39 0 84 1 2 1 2 +218 54 -82 0 58 1 2 1 2 +219 29 41 0 41 1 2 1 2 +220 -87 51 0 98 1 2 1 2 +221 -96 -36 0 77 1 2 1 2 +222 49 8 0 57 1 2 1 2 +223 -5 54 0 39 1 2 1 2 +224 -26 43 0 99 1 2 1 2 +225 -11 60 0 83 1 2 1 2 +226 40 61 0 54 1 2 1 2 +227 82 35 0 86 1 2 1 2 +228 -92 12 0 2 1 2 1 2 +229 -93 -86 0 14 1 2 1 2 +230 -66 63 0 42 1 2 1 2 +231 -72 -87 0 14 1 2 1 2 +232 -57 -84 0 55 1 2 1 2 +233 23 52 0 2 1 2 1 2 +234 -56 -62 0 18 1 2 1 2 +235 -19 59 0 17 1 2 1 2 +236 63 -14 0 22 1 2 1 2 +237 -13 38 0 28 1 2 1 2 +238 -19 87 0 3 1 2 1 2 +239 44 -84 0 96 1 2 1 2 +240 98 -17 0 53 1 2 1 2 +241 -16 62 0 15 1 2 1 2 +242 3 66 0 36 1 2 1 2 +243 26 22 0 98 1 2 1 2 +244 -38 -81 0 78 1 2 1 2 +245 70 -80 0 92 1 2 1 2 +246 17 -35 0 65 1 2 1 2 +247 96 -83 0 64 1 2 1 2 +248 -77 80 0 43 1 2 1 2 +249 -14 44 0 50 1 2 1 2 +250 -33 33 0 0 0 0 +251 33 -33 0 0 0 0 diff --git a/jsprit-examples/pom.xml b/jsprit-examples/pom.xml index 0600d0d3..11a1b637 100644 --- a/jsprit-examples/pom.xml +++ b/jsprit-examples/pom.xml @@ -20,7 +20,7 @@ com.graphhopper jsprit - 1.6.3-SNAPSHOT + 1.7.3-SNAPSHOT 4.0.0 diff --git a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/BuildAlgorithmFromScratch.java b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/BuildAlgorithmFromScratch.java index df30c0ee..72cd3426 100644 --- a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/BuildAlgorithmFromScratch.java +++ b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/BuildAlgorithmFromScratch.java @@ -20,7 +20,6 @@ package com.graphhopper.jsprit.examples; import com.graphhopper.jsprit.analysis.toolbox.AlgorithmEventsRecorder; -import com.graphhopper.jsprit.analysis.toolbox.AlgorithmEventsViewer; import com.graphhopper.jsprit.core.algorithm.PrettyAlgorithmBuilder; import com.graphhopper.jsprit.core.algorithm.SearchStrategy; import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; @@ -116,17 +115,18 @@ public class BuildAlgorithmFromScratch { final VehicleRoutingProblem vrp = vrpBuilder.build(); VehicleRoutingAlgorithm vra = createAlgorithm(vrp); - vra.setMaxIterations(100); + vra.setMaxIterations(2000); AlgorithmEventsRecorder eventsRecorder = new AlgorithmEventsRecorder(vrp, "output/events.dgs.gz"); eventsRecorder.setRecordingRange(90, 100); vra.addListener(eventsRecorder); VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); SolutionPrinter.print(vrp, solution, SolutionPrinter.Print.VERBOSE); - AlgorithmEventsViewer viewer = new AlgorithmEventsViewer(); - viewer.setRuinDelay(3); - viewer.setRecreationDelay(1); - viewer.display("output/events.dgs.gz"); + +// AlgorithmEventsViewer viewer = new AlgorithmEventsViewer(); +// viewer.setRuinDelay(3); +// viewer.setRecreationDelay(1); +// viewer.display("output/events.dgs.gz"); } @@ -146,10 +146,11 @@ public class BuildAlgorithmFromScratch { //regret insertion InsertionBuilder iBuilder = new InsertionBuilder(vrp, fleetManager, stateManager, constraintManager); iBuilder.setInsertionStrategy(InsertionBuilder.Strategy.REGRET); + iBuilder.setFastRegret(true); RegretInsertionFast regret = (RegretInsertionFast) iBuilder.build(); DefaultScorer scoringFunction = new DefaultScorer(vrp); - scoringFunction.setDepotDistanceParam(0.2); - scoringFunction.setTimeWindowParam(-.2); + scoringFunction.setDepotDistanceParam(0.0); + scoringFunction.setTimeWindowParam(0.0); regret.setScoringFunction(scoringFunction); /* diff --git a/jsprit-instances/pom.xml b/jsprit-instances/pom.xml index 5f7aace3..5f657739 100644 --- a/jsprit-instances/pom.xml +++ b/jsprit-instances/pom.xml @@ -20,7 +20,7 @@ com.graphhopper jsprit - 1.6.3-SNAPSHOT + 1.7.3-SNAPSHOT 4.0.0 diff --git a/jsprit-io/pom.xml b/jsprit-io/pom.xml index 343cfe84..1886f762 100644 --- a/jsprit-io/pom.xml +++ b/jsprit-io/pom.xml @@ -22,8 +22,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> com.graphhopper - jsprit - 1.6.3-SNAPSHOT + jsprit + 1.7.3-SNAPSHOT 4.0.0 diff --git a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLReader.java b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLReader.java index 0b5148c3..504d39c4 100644 --- a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLReader.java +++ b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLReader.java @@ -701,7 +701,8 @@ public class VrpXMLReader { List breakTWConfigs = vehicleConfig.configurationsAt("breaks.timeWindows.timeWindow"); if (!breakTWConfigs.isEmpty()) { String breakDurationString = vehicleConfig.getString("breaks.duration"); - Break.Builder current_break = Break.Builder.newInstance(vehicleId); + String id = vehicleConfig.getString("breaks.id"); + Break.Builder current_break = Break.Builder.newInstance(id); current_break.setServiceTime(Double.parseDouble(breakDurationString)); for (HierarchicalConfiguration twConfig : breakTWConfigs) { current_break.addTimeWindow(TimeWindow.newInstance(twConfig.getDouble("start"), twConfig.getDouble("end"))); diff --git a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLWriter.java b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLWriter.java index 15dd7913..57ba77f2 100644 --- a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLWriter.java +++ b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/problem/VrpXMLWriter.java @@ -41,9 +41,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; +import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -94,8 +92,36 @@ public class VrpXMLWriter { public void write(String filename) { if (!filename.endsWith(".xml")) filename += ".xml"; log.info("write vrp: " + filename); + XMLConf xmlConfig = createXMLConfiguration(); + + try { + xmlConfig.setFileName(filename); + Writer out = new FileWriter(filename); + XMLSerializer serializer = new XMLSerializer(out, createOutputFormat()); + serializer.serialize(xmlConfig.getDocument()); + out.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public OutputStream write() { + XMLConf xmlConfig = createXMLConfiguration(); + OutputStream out = new ByteArrayOutputStream(); + + try { + XMLSerializer serializer = new XMLSerializer(out, createOutputFormat()); + serializer.serialize(xmlConfig.getDocument()); + out.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return out; + } + + private XMLConf createXMLConfiguration() { XMLConf xmlConfig = new XMLConf(); - xmlConfig.setFileName(filename); xmlConfig.setRootElementName("problem"); xmlConfig.setAttributeSplittingDisabled(true); xmlConfig.setDelimiterParsingDisabled(true); @@ -123,10 +149,6 @@ public class VrpXMLWriter { writeSolutions(xmlConfig); - OutputFormat format = new OutputFormat(); - format.setIndenting(true); - format.setIndent(5); - try { Document document = xmlConfig.createDoc(); @@ -138,17 +160,14 @@ public class VrpXMLWriter { } catch (ConfigurationException e) { throw new RuntimeException(e); } + return xmlConfig; + } - try { - Writer out = new FileWriter(filename); - XMLSerializer serializer = new XMLSerializer(out, format); - serializer.serialize(xmlConfig.getDocument()); - out.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - - + private OutputFormat createOutputFormat() { + OutputFormat format = new OutputFormat(); + format.setIndenting(true); + format.setIndent(5); + return format; } private void writeInitialRoutes(XMLConf xmlConfig) { @@ -203,12 +222,12 @@ public class VrpXMLWriter { xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act(" + actCounter + ")[@type]", act.getName()); if (act instanceof TourActivity.JobActivity) { Job job = ((TourActivity.JobActivity) act).getJob(); - if (job instanceof Service) { + if (job instanceof Break) { + xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act(" + actCounter + ").breakId", job.getId()); + } else if (job instanceof Service) { xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act(" + actCounter + ").serviceId", job.getId()); } else if (job instanceof Shipment) { xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act(" + actCounter + ").shipmentId", job.getId()); - } else if (job instanceof Break) { - xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act(" + actCounter + ").breakId", job.getId()); } else { throw new IllegalStateException("cannot write solution correctly since job-type is not know. make sure you use either service or shipment, or another writer"); } diff --git a/jsprit-io/src/main/resources/vrp_xml_schema.xsd b/jsprit-io/src/main/resources/vrp_xml_schema.xsd index 9d67a9d9..04a36b26 100644 --- a/jsprit-io/src/main/resources/vrp_xml_schema.xsd +++ b/jsprit-io/src/main/resources/vrp_xml_schema.xsd @@ -395,6 +395,7 @@ + diff --git a/jsprit-io/src/test/java/com/graphhopper/jsprit/io/problem/VrpXMLWriterTest.java b/jsprit-io/src/test/java/com/graphhopper/jsprit/io/problem/VrpXMLWriterTest.java index e75e1ba8..0075600f 100644 --- a/jsprit-io/src/test/java/com/graphhopper/jsprit/io/problem/VrpXMLWriterTest.java +++ b/jsprit-io/src/test/java/com/graphhopper/jsprit/io/problem/VrpXMLWriterTest.java @@ -31,9 +31,11 @@ import com.graphhopper.jsprit.core.util.Coordinate; import com.graphhopper.jsprit.core.util.Solutions; import com.graphhopper.jsprit.io.util.TestUtils; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -42,77 +44,15 @@ import static org.junit.Assert.*; public class VrpXMLWriterTest { - private String infileName; - - @Before - public void doBefore() { - infileName = "src/test/resources/infiniteWriterV2Test.xml"; - } - - @Test - public void whenWritingInfiniteVrp_itWritesCorrectly() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - builder.setFleetSize(VehicleRoutingProblem.FleetSize.INFINITE); - VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleImpl vehicle = VehicleImpl.Builder.newInstance("myVehicle").setStartLocation(TestUtils.loc("loc")).setType(type).build(); - builder.addVehicle(vehicle); - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - } - - @Test - public void whenWritingFiniteVrp_itWritesCorrectly() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - builder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - builder.addVehicle(v1); - builder.addVehicle(v2); - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - } - - @Test - public void t() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - builder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - builder.addVehicle(v1); - builder.addVehicle(v2); - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - } - @Test public void whenWritingServices_itWritesThemCorrectly() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); - + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Service s1 = Service.Builder.newInstance("1").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); Service s1_read = (Service) vrp.getJobs().get("1"); @@ -128,11 +68,7 @@ public class VrpXMLWriterTest { Service s1 = Service.Builder.newInstance("1").setName("cleaning").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Service s1_read = (Service) readVrp.getJobs().get("1"); assertTrue(s1_read.getName().equals("cleaning")); } @@ -146,11 +82,7 @@ public class VrpXMLWriterTest { .setDeliveryLocation(TestUtils.loc("del")).build(); VehicleRoutingProblem vrp = builder.addJob(s1).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Shipment s1_read = (Shipment) readVrp.getJobs().get("1"); assertTrue(s1_read.getName().equals("cleaning")); Assert.assertEquals(1, s1_read.getPickupLocation().getIndex()); @@ -167,32 +99,20 @@ public class VrpXMLWriterTest { Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); Service s1_read = (Service) vrp.getJobs().get("1"); - Assert.assertEquals(2, s1_read.getSize().getNuOfDimensions()); - Assert.assertEquals(20, s1_read.getSize().get(0)); - Assert.assertEquals(200, s1_read.getSize().get(1)); + assertEquals(2, s1_read.getSize().getNuOfDimensions()); + assertEquals(20, s1_read.getSize().get(0)); + assertEquals(200, s1_read.getSize().get(1)); } @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenLocationIdsOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10) .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) @@ -205,29 +125,17 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); - Assert.assertEquals("pickLoc", ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getId()); - Assert.assertEquals("delLoc", ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getId()); + assertEquals("pickLoc", ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getId()); + assertEquals("delLoc", ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getId()); } @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenPickupTimeWindowsOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10) .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) @@ -240,30 +148,18 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); - Assert.assertEquals(1.0, ((Shipment) readVrp.getJobs().get("1")).getPickupTimeWindow().getStart(), 0.01); - Assert.assertEquals(2.0, ((Shipment) readVrp.getJobs().get("1")).getPickupTimeWindow().getEnd(), 0.01); + assertEquals(1.0, ((Shipment) readVrp.getJobs().get("1")).getPickupTimeWindow().getStart(), 0.01); + assertEquals(2.0, ((Shipment) readVrp.getJobs().get("1")).getPickupTimeWindow().getEnd(), 0.01); } @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenDeliveryTimeWindowsOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10) .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) @@ -276,29 +172,17 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); - Assert.assertEquals(3.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryTimeWindow().getStart(), 0.01); - Assert.assertEquals(4.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryTimeWindow().getEnd(), 0.01); + assertEquals(3.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryTimeWindow().getStart(), 0.01); + assertEquals(4.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryTimeWindow().getEnd(), 0.01); } @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenDeliveryServiceTimeOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10) .setPickupLocation(Location.Builder.newInstance().setId("pickLoc").build()) @@ -311,11 +195,7 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); assertEquals(100.0, ((Shipment) readVrp.getJobs().get("1")).getPickupServiceTime(), 0.01); @@ -325,15 +205,7 @@ public class VrpXMLWriterTest { @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenLocationIdOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10) .setPickupLocation(TestUtils.loc(Coordinate.newInstance(1, 2))).setDeliveryLocation(TestUtils.loc("delLoc")).setPickupTimeWindow(TimeWindow.newInstance(1, 2)) @@ -345,18 +217,14 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); - Assert.assertEquals("[x=1.0][y=2.0]", ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getId()); + assertEquals("[x=1.0][y=2.0]", ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getId()); } @Test - public void whenWritingVehicles_vehShouldHave2Skills() { + public void whenWritingVehicles_vehShouldHave3Skills() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); VehicleImpl v = VehicleImpl.Builder.newInstance("v1").addSkill("SKILL5").addSkill("skill1").addSkill("Skill2") @@ -364,70 +232,12 @@ public class VrpXMLWriterTest { builder.addVehicle(v); VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - Vehicle veh1 = getVehicle("v1", readVrp); - - Assert.assertEquals(3, veh1.getSkills().values().size()); - } - - @Test - public void whenWritingVehicles_vehShouldContain_skill5() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleImpl v = VehicleImpl.Builder.newInstance("v1").addSkill("SKILL5").addSkill("skill1").addSkill("Skill2") - .setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - builder.addVehicle(v); - - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle veh1 = getVehicle("v1", readVrp); + assertEquals(3, veh1.getSkills().values().size()); assertTrue(veh1.getSkills().containsSkill("skill5")); - } - - @Test - public void whenWritingVehicles_vehShouldContain_skill1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleImpl v = VehicleImpl.Builder.newInstance("v1").addSkill("SKILL5").addSkill("skill1").addSkill("Skill2") - .setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - builder.addVehicle(v); - - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - Vehicle veh1 = getVehicle("v1", readVrp); - assertTrue(veh1.getSkills().containsSkill("skill1")); - } - - @Test - public void whenWritingVehicles_vehShouldContain_skill2() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleImpl v = VehicleImpl.Builder.newInstance("v1").addSkill("SKILL5").addSkill("skill1").addSkill("Skill2") - .setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - builder.addVehicle(v); - - VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - Vehicle veh1 = getVehicle("v1", readVrp); - assertTrue(veh1.getSkills().containsSkill("skill2")); } @@ -439,14 +249,10 @@ public class VrpXMLWriterTest { builder.addVehicle(v); VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle veh = getVehicle("v1", readVrp); - Assert.assertEquals(0, veh.getSkills().values().size()); + assertEquals(0, veh.getSkills().values().size()); } private Vehicle getVehicle(String v1, VehicleRoutingProblem readVrp) { @@ -468,89 +274,17 @@ public class VrpXMLWriterTest { .setDeliveryTimeWindow(TimeWindow.newInstance(3, 4)).setPickupServiceTime(100).setDeliveryServiceTime(50).build(); VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - - Assert.assertEquals(3, readVrp.getJobs().get("1").getRequiredSkills().values().size()); - } - - @Test - public void whenWritingShipments_shipmentShouldContain_skill1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - Shipment s = Shipment.Builder.newInstance("1").addRequiredSkill("skill1").addRequiredSkill("skill2").addRequiredSkill("skill3") - .addSizeDimension(0, 10) - .setPickupLocation(TestUtils.loc(Coordinate.newInstance(1, 2))) - .setDeliveryLocation(TestUtils.loc("delLoc", Coordinate.newInstance(5, 6))) - .setPickupTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryTimeWindow(TimeWindow.newInstance(3, 4)).setPickupServiceTime(100).setDeliveryServiceTime(50).build(); - - VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); + assertEquals(3, readVrp.getJobs().get("1").getRequiredSkills().values().size()); assertTrue(readVrp.getJobs().get("1").getRequiredSkills().containsSkill("skill1")); - } - - @Test - public void whenWritingShipments_shipmentShouldContain_skill2() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - Shipment s = Shipment.Builder.newInstance("1").addRequiredSkill("skill1").addRequiredSkill("Skill2").addRequiredSkill("skill3") - .addSizeDimension(0, 10) - .setPickupLocation(TestUtils.loc(Coordinate.newInstance(1, 2))) - .setDeliveryLocation(TestUtils.loc("delLoc", Coordinate.newInstance(5, 6))) - .setPickupTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryTimeWindow(TimeWindow.newInstance(3, 4)).setPickupServiceTime(100).setDeliveryServiceTime(50).build(); - - VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - assertTrue(readVrp.getJobs().get("1").getRequiredSkills().containsSkill("skill2")); - } - - @Test - public void whenWritingShipments_shipmentShouldContain_skill3() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - Shipment s = Shipment.Builder.newInstance("1").addRequiredSkill("skill1").addRequiredSkill("Skill2").addRequiredSkill("skill3") - .addSizeDimension(0, 10) - .setPickupLocation(TestUtils.loc(Coordinate.newInstance(1, 2))) - .setDeliveryLocation(TestUtils.loc("delLoc", Coordinate.newInstance(5, 6))) - .setPickupTimeWindow(TimeWindow.newInstance(1, 2)) - .setDeliveryTimeWindow(TimeWindow.newInstance(3, 4)).setPickupServiceTime(100).setDeliveryServiceTime(50).build(); - - VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - assertTrue(readVrp.getJobs().get("1").getRequiredSkills().containsSkill("skill3")); } @Test public void whenWritingShipments_readingThemAgainMustReturnTheWrittenLocationCoordinateOfS1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(TestUtils.loc("loc")).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); Shipment s1 = Shipment.Builder.newInstance("1").addSizeDimension(0, 10).setPickupLocation(TestUtils.loc(Coordinate.newInstance(1, 2))) .setDeliveryLocation(TestUtils.loc("delLoc", Coordinate.newInstance(5, 6))) @@ -564,18 +298,14 @@ public class VrpXMLWriterTest { VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); assertEquals(2, readVrp.getJobs().size()); - Assert.assertEquals(1.0, ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getCoordinate().getX(), 0.01); - Assert.assertEquals(2.0, ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getCoordinate().getY(), 0.01); + assertEquals(1.0, ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getCoordinate().getX(), 0.01); + assertEquals(2.0, ((Shipment) readVrp.getJobs().get("1")).getPickupLocation().getCoordinate().getY(), 0.01); - Assert.assertEquals(5.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getCoordinate().getX(), 0.01); - Assert.assertEquals(6.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getCoordinate().getY(), 0.01); + assertEquals(5.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getCoordinate().getX(), 0.01); + assertEquals(6.0, ((Shipment) readVrp.getJobs().get("1")).getDeliveryLocation().getCoordinate().getY(), 0.01); } @Test @@ -597,23 +327,34 @@ public class VrpXMLWriterTest { .setDeliveryTimeWindow(TimeWindow.newInstance(7, 8)).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + assertEquals(3, (readVrp.getJobs().get("1")).getSize().getNuOfDimensions()); + assertEquals(10, (readVrp.getJobs().get("1")).getSize().get(0)); + assertEquals(0, (readVrp.getJobs().get("1")).getSize().get(1)); + assertEquals(100, (readVrp.getJobs().get("1")).getSize().get(2)); - Assert.assertEquals(3, (readVrp.getJobs().get("1")).getSize().getNuOfDimensions()); - Assert.assertEquals(10, (readVrp.getJobs().get("1")).getSize().get(0)); - Assert.assertEquals(0, (readVrp.getJobs().get("1")).getSize().get(1)); - Assert.assertEquals(100, (readVrp.getJobs().get("1")).getSize().get(2)); - - Assert.assertEquals(1, (readVrp.getJobs().get("2")).getSize().getNuOfDimensions()); - Assert.assertEquals(20, (readVrp.getJobs().get("2")).getSize().get(0)); + assertEquals(1, (readVrp.getJobs().get("2")).getSize().getNuOfDimensions()); + assertEquals(20, (readVrp.getJobs().get("2")).getSize().get(0)); } @Test public void whenWritingVehicleV1_itsStartLocationMustBeWrittenCorrectly() { + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); + + Service s1 = Service.Builder.newInstance("1").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); + Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); + + VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); + + Vehicle v = getVehicle("v1", readVrp.getVehicles()); + assertEquals("loc", v.getStartLocation().getId()); + assertEquals("loc", v.getEndLocation().getId()); + + } + + private VehicleRoutingProblem.Builder twoVehicleTypesAndImpls() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); @@ -623,74 +364,27 @@ public class VrpXMLWriterTest { builder.addVehicle(v1); builder.addVehicle(v2); - - Service s1 = Service.Builder.newInstance("1").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); - Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); - - VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - - Vehicle v = getVehicle("v1", readVrp.getVehicles()); - Assert.assertEquals("loc", v.getStartLocation().getId()); - Assert.assertEquals("loc", v.getEndLocation().getId()); - + return builder; } @Test - public void whenWritingService_itShouldHaveTheCorrectNuSkills() { + public void whenWritingService_itShouldContain_bothSkills() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - Service s = Service.Builder.newInstance("1").addRequiredSkill("sKill1").addRequiredSkill("skill2").addSizeDimension(0, 1) - .setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); + //skill names are case-insensitive + Service s = Service.Builder.newInstance("1").addRequiredSkill("skill1").addRequiredSkill("SKILL2").addSizeDimension(0, 1) + .setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - - Assert.assertEquals(2, readVrp.getJobs().get("1").getRequiredSkills().values().size()); - } - - @Test - public void whenWritingService_itShouldContain_skill1() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - Service s = Service.Builder.newInstance("1").addRequiredSkill("sKill1").addRequiredSkill("skill2").addSizeDimension(0, 1) - .setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); - - VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); + assertEquals(2, readVrp.getJobs().get("1").getRequiredSkills().values().size()); assertTrue(readVrp.getJobs().get("1").getRequiredSkills().containsSkill("skill1")); - } - - @Test - public void whenWritingService_itShouldContain_skill2() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - - Service s = Service.Builder.newInstance("1").addRequiredSkill("sKill1").addRequiredSkill("skill2").addSizeDimension(0, 1) - .setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); - - VehicleRoutingProblem vrp = builder.addJob(s).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - assertTrue(readVrp.getJobs().get("1").getRequiredSkills().containsSkill("skill2")); } + @Test public void whenWritingVehicleV1_itDoesNotReturnToDepotMustBeWrittenCorrectly() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); @@ -708,11 +402,7 @@ public class VrpXMLWriterTest { Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v1", readVrp.getVehicles()); assertFalse(v.isReturnToDepot()); @@ -734,11 +424,7 @@ public class VrpXMLWriterTest { Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v1", readVrp.getVehicles()); assertEquals("vehType", v.getType().getTypeId()); @@ -760,15 +446,11 @@ public class VrpXMLWriterTest { Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v2", readVrp.getVehicles()); assertEquals("vehType2", v.getType().getTypeId()); - Assert.assertEquals(200, v.getType().getCapacityDimensions().get(0)); + assertEquals(200, v.getType().getCapacityDimensions().get(0)); } @@ -776,35 +458,6 @@ public class VrpXMLWriterTest { public void whenWritingVehicleV2_readingItsLocationsAgainReturnsCorrectLocations() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); - VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); - VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setReturnToDepot(false).setStartLocation(TestUtils.loc("loc")).setType(type1).build(); - VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2") - .setStartLocation(TestUtils.loc("startLoc", Coordinate.newInstance(1, 2))) - .setEndLocation(TestUtils.loc("endLoc", Coordinate.newInstance(4, 5))).setType(type2).build(); - - builder.addVehicle(v1); - builder.addVehicle(v2); - - Service s1 = Service.Builder.newInstance("1").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc")).setServiceTime(2.0).build(); - Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); - - VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); - - Vehicle v = getVehicle("v2", readVrp.getVehicles()); - Assert.assertEquals("startLoc", v.getStartLocation().getId()); - Assert.assertEquals("endLoc", v.getEndLocation().getId()); - } - - @Test - public void whenWritingVehicleV2_readingItsLocationsCoordsAgainReturnsCorrectLocationsCoords() { - VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); - VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("vehType").addCapacityDimension(0, 20).build(); VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("vehType2").addCapacityDimension(0, 200).build(); VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setReturnToDepot(false) @@ -820,20 +473,18 @@ public class VrpXMLWriterTest { Service s2 = Service.Builder.newInstance("2").addSizeDimension(0, 1).setLocation(TestUtils.loc("loc2")).setServiceTime(4.0).build(); VehicleRoutingProblem vrp = builder.addJob(s1).addJob(s2).build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v2", readVrp.getVehicles()); - Assert.assertEquals(1.0, v.getStartLocation().getCoordinate().getX(), 0.01); - Assert.assertEquals(2.0, v.getStartLocation().getCoordinate().getY(), 0.01); - - Assert.assertEquals(4.0, v.getEndLocation().getCoordinate().getX(), 0.01); - Assert.assertEquals(5.0, v.getEndLocation().getCoordinate().getY(), 0.01); + assertEquals("startLoc", v.getStartLocation().getId()); + assertEquals("endLoc", v.getEndLocation().getId()); + assertEquals(1.0, v.getStartLocation().getCoordinate().getX(), 0.01); + assertEquals(2.0, v.getStartLocation().getCoordinate().getY(), 0.01); + assertEquals(4.0, v.getEndLocation().getCoordinate().getX(), 0.01); + assertEquals(5.0, v.getEndLocation().getCoordinate().getY(), 0.01); } + @Test public void whenWritingVehicleWithSeveralCapacityDimensions_itShouldBeWrittenAndRereadCorrectly() { VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); @@ -850,17 +501,13 @@ public class VrpXMLWriterTest { builder.addVehicle(v2); VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v", readVrp.getVehicles()); - Assert.assertEquals(3, v.getType().getCapacityDimensions().getNuOfDimensions()); - Assert.assertEquals(100, v.getType().getCapacityDimensions().get(0)); - Assert.assertEquals(1000, v.getType().getCapacityDimensions().get(1)); - Assert.assertEquals(10000, v.getType().getCapacityDimensions().get(2)); + assertEquals(3, v.getType().getCapacityDimensions().getNuOfDimensions()); + assertEquals(100, v.getType().getCapacityDimensions().get(0)); + assertEquals(1000, v.getType().getCapacityDimensions().get(1)); + assertEquals(10000, v.getType().getCapacityDimensions().get(2)); } @Test @@ -879,16 +526,12 @@ public class VrpXMLWriterTest { builder.addVehicle(v2); VehicleRoutingProblem vrp = builder.build(); - new VrpXMLWriter(vrp, null).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - new VrpXMLReader(vrpToReadBuilder, null).read(infileName); - VehicleRoutingProblem readVrp = vrpToReadBuilder.build(); + VehicleRoutingProblem readVrp = writeAndRereadXml(vrp); Vehicle v = getVehicle("v", readVrp.getVehicles()); - Assert.assertEquals(11, v.getType().getCapacityDimensions().getNuOfDimensions()); - Assert.assertEquals(0, v.getType().getCapacityDimensions().get(9)); - Assert.assertEquals(10000, v.getType().getCapacityDimensions().get(10)); + assertEquals(11, v.getType().getCapacityDimensions().getNuOfDimensions()); + assertEquals(0, v.getType().getCapacityDimensions().get(9)); + assertEquals(10000, v.getType().getCapacityDimensions().get(10)); } private Vehicle getVehicle(String string, Collection vehicles) { @@ -916,14 +559,10 @@ public class VrpXMLWriterTest { List solutions = new ArrayList(); solutions.add(solution); - new VrpXMLWriter(vrp, solutions).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - List solutionsToRead = new ArrayList(); - new VrpXMLReader(vrpToReadBuilder, solutionsToRead).read(infileName); + List solutionsToRead = writeAndRereadXmlWithSolutions(vrp, solutions); assertEquals(1, solutionsToRead.size()); - Assert.assertEquals(10., Solutions.bestOf(solutionsToRead).getCost(), 0.01); + assertEquals(10., Solutions.bestOf(solutionsToRead).getCost(), 0.01); assertTrue(Solutions.bestOf(solutionsToRead).getUnassignedJobs().isEmpty()); } @@ -948,16 +587,46 @@ public class VrpXMLWriterTest { List solutions = new ArrayList(); solutions.add(solution); - new VrpXMLWriter(vrp, solutions).write(infileName); - - VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); - List solutionsToRead = new ArrayList(); - new VrpXMLReader(vrpToReadBuilder, solutionsToRead).read(infileName); + List solutionsToRead = writeAndRereadXmlWithSolutions(vrp, solutions); assertEquals(1, solutionsToRead.size()); - Assert.assertEquals(10., Solutions.bestOf(solutionsToRead).getCost(), 0.01); - Assert.assertEquals(1, Solutions.bestOf(solutionsToRead).getUnassignedJobs().size()); - Assert.assertEquals("2", Solutions.bestOf(solutionsToRead).getUnassignedJobs().iterator().next().getId()); + assertEquals(10., Solutions.bestOf(solutionsToRead).getCost(), 0.01); + assertEquals(1, Solutions.bestOf(solutionsToRead).getUnassignedJobs().size()); + assertEquals("2", Solutions.bestOf(solutionsToRead).getUnassignedJobs().iterator().next().getId()); + } + + @Test + public void outputStreamAndFileContentsAreEqual() throws IOException { + VehicleRoutingProblem.Builder builder = twoVehicleTypesAndImpls(); + VehicleRoutingProblem vrp = builder.build(); + + VrpXMLWriter vrpXMLWriter = new VrpXMLWriter(vrp, null); + ByteArrayOutputStream os = (ByteArrayOutputStream) vrpXMLWriter.write(); + + String outputStringFromFile = new String(os.toByteArray()); + String outputStringFromStream = new VrpXMLWriter(vrp, null).write().toString(); + + assertEquals(outputStringFromFile, outputStringFromStream); + + } + + private VehicleRoutingProblem writeAndRereadXml(VehicleRoutingProblem vrp) { + VrpXMLWriter vrpXMLWriter = new VrpXMLWriter(vrp, null); + ByteArrayOutputStream os = (ByteArrayOutputStream) vrpXMLWriter.write(); + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); + new VrpXMLReader(vrpToReadBuilder, null).read(is); + return vrpToReadBuilder.build(); + } + + private List writeAndRereadXmlWithSolutions(VehicleRoutingProblem vrp, List solutions) { + VrpXMLWriter vrpXMLWriter = new VrpXMLWriter(vrp, solutions); + ByteArrayOutputStream os = (ByteArrayOutputStream) vrpXMLWriter.write(); + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + VehicleRoutingProblem.Builder vrpToReadBuilder = VehicleRoutingProblem.Builder.newInstance(); + List solutionsToRead = new ArrayList(); + new VrpXMLReader(vrpToReadBuilder, solutionsToRead).read(is); + return solutionsToRead; } } diff --git a/pom.xml b/pom.xml index da7ba9ec..7b669fe8 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ com.graphhopper jsprit - 1.6.3-SNAPSHOT + 1.7.3-SNAPSHOT pom @@ -56,6 +56,7 @@ HEAD + github https://github.com/graphhopper/jsprit/issues @@ -81,6 +82,15 @@ + + + + io.packagecloud.maven.wagon + maven-packagecloud-wagon + 0.0.4 + + + src/main/java src/test/java target @@ -180,9 +190,13 @@ + + packagecloud-graphhopper + packagecloud+https://packagecloud.io/graphhopper/jsprit + - ossrh - https://oss.sonatype.org/content/repositories/snapshots + packagecloud-graphhopper + packagecloud+https://packagecloud.io/graphhopper/jsprit

" + date + "" + date + "