1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

improve JobDistance for RadialRuin

This commit is contained in:
Stefan Schroeder 2013-06-18 09:28:33 +02:00
parent b0de3eef86
commit 3587c7fe94
8 changed files with 233 additions and 145 deletions

View file

@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import util.EuclideanDistanceCalculator;
import basics.Job;
import basics.Service;
class EuclideanServiceDistance implements JobDistance {
public EuclideanServiceDistance() {
super();
}
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
if(s_i.getCoord() == null || s_j.getCoord() == null) throw new IllegalStateException("cannot calculate euclidean distance. since service coords are missing");
avgCost = EuclideanDistanceCalculator.calculateDistance(s_i.getCoord(), s_j.getCoord());
}
} else {
throw new UnsupportedOperationException(
"currently, this class just works with shipments and services.");
}
return avgCost;
}
}

View file

@ -12,14 +12,25 @@
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingTransportCosts;
/**
* Calculator that calculates average distance between two jobs based on the input-transport costs.
*
* <p>If the distance between two jobs cannot be calculated with input-transport costs, it tries the euclidean distance between these jobs.
*
* @author stefan schroeder
*
*/
class JobDistanceAvgCosts implements JobDistance {
private static Logger log = Logger.getLogger(JobDistanceAvgCosts.class);
private VehicleRoutingTransportCosts costs;
public JobDistanceAvgCosts(VehicleRoutingTransportCosts costs) {
@ -28,29 +39,21 @@ class JobDistanceAvgCosts implements JobDistance {
}
/**
* Calculates and returns the average distance between two jobs based on the input-transport costs.
*
* <p>If the distance between two jobs cannot be calculated with input-transport costs, it tries the euclidean distance between these jobs.
*/
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
avgCost = calcDist(s_i, s_j);
}
} else {
throw new UnsupportedOperationException(
@ -59,8 +62,18 @@ class JobDistanceAvgCosts implements JobDistance {
return avgCost;
}
private double calcDist(String from, String to) {
return costs.getTransportCost(from, to, 0.0, null, null);
private double calcDist(Service s_i, Service s_j) {
double distance;
try{
distance = costs.getTransportCost(s_i.getLocationId(), s_j.getLocationId(), 0.0, null, null);
return distance;
}
catch(IllegalStateException e){
// now try the euclidean distance between these two services
}
EuclideanServiceDistance euclidean = new EuclideanServiceDistance();
distance = euclidean.calculateDistance(s_i, s_j);
return distance;
}
}

View file

@ -31,19 +31,6 @@ class JobDistanceBeeline implements JobDistance {
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;

View file

@ -359,6 +359,13 @@ public class VehicleRoutingAlgorithms {
private VehicleRoutingAlgorithms(){}
/**
* Creates a {@link VehicleRoutingAlgorithm} from a AlgorithConfig based on the input vrp.
*
* @param vrp
* @param algorithmConfig
* @return {@link VehicleRoutingAlgorithm}
*/
public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp, final AlgorithmConfig algorithmConfig){
return createAlgo(vrp,algorithmConfig.getXMLConfiguration());
}
@ -368,6 +375,13 @@ public class VehicleRoutingAlgorithms {
return createAlgo(vrp,config);
}
/**
* Read and creates a {@link VehicleRoutingAlgorithm} from an url.
*
* @param vrp
* @param configURL
* @return {@link VehicleRoutingProblem}
*/
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final URL configURL){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
@ -375,6 +389,13 @@ public class VehicleRoutingAlgorithms {
return createAlgo(vrp,algorithmConfig.getXMLConfiguration());
}
/**
* Read and creates {@link VehicleRoutingAlgorithm} from config-file.
*
* @param vrp
* @param configFileName
* @return
*/
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final String configFileName){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
@ -572,6 +593,7 @@ public class VehicleRoutingAlgorithms {
StrategyModuleKey strategyModuleKey = new StrategyModuleKey(modKey);
SearchStrategyModule definedModule = definedClasses.get(strategyModuleKey);
if(definedModule != null) return definedModule;
if(moduleName.equals("ruin_and_recreate")){
String ruin_name = moduleConfig.getString("ruin[@name]");
if(ruin_name == null) throw new IllegalStateException("module.ruin[@name] is missing.");
@ -586,7 +608,17 @@ public class VehicleRoutingAlgorithms {
ruin = getRandomRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin);
}
else if(ruin_name.equals("radialRuin")){
ruin = getRadialRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin);
String ruin_distance = moduleConfig.getString("ruin.distance");
JobDistance jobDistance;
if(ruin_distance == null) jobDistance = new JobDistanceAvgCosts(vrp.getTransportCosts());
else {
if(ruin_distance.equals("euclidean")){
jobDistance = new EuclideanServiceDistance();
}
else throw new IllegalStateException("does not know ruin.distance " + ruin_distance + ". either ommit ruin.distance then the "
+ "default is used or use 'euclidean'");
}
ruin = getRadialRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin, jobDistance);
}
else throw new IllegalStateException("ruin[@name] " + ruin_name + " is not known. Use either randomRuin or radialRuin.");
@ -643,15 +675,16 @@ public class VehicleRoutingAlgorithms {
};
return module;
}
if(moduleName.equals("bestInsertion") || moduleName.equals("regretInsertion")){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
AbstractInsertionStrategy insertion = getInsertionStrategy(moduleConfig, vrp, vehicleFleetManager, activityStates,
definedClasses, modKey, prioListeners);
SearchStrategyModule module = getModule(moduleName, insertion, vrp);
definedClasses.put(strategyModuleKey, module);
algorithmListeners.addAll(prioListeners);
return module;
}
// if(moduleName.equals("bestInsertion") || moduleName.equals("regretInsertion")){
// List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
// AbstractInsertionStrategy insertion = getInsertionStrategy(moduleConfig, vrp, vehicleFleetManager, activityStates,
// definedClasses, modKey, prioListeners);
// SearchStrategyModule module = getModule(moduleName, insertion, vrp);
// definedClasses.put(strategyModuleKey, module);
// algorithmListeners.addAll(prioListeners);
// return module;
// }
// if(moduleName.equals("regretInsertion")){
// List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
// AbstractInsertionKey insertionKey = new AbstractInsertionKey(modKey);
@ -664,20 +697,30 @@ public class VehicleRoutingAlgorithms {
// algorithmListeners.addAll(prioListeners);
// return module;
// }
if(moduleName.equals("randomRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRandomRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
if(moduleName.equals("radialRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRadialRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
// if(moduleName.equals("randomRuin")){
// double shareToRuin = moduleConfig.getDouble("share");
// RuinStrategy ruin = getRandomRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
// SearchStrategyModule module = getModule(moduleName, ruin);
// definedClasses.put(strategyModuleKey, module);
// return module;
// }
// if(moduleName.equals("radialRuin")){
// double shareToRuin = moduleConfig.getDouble("share");
// String ruin_distance = moduleConfig.getString("distance");
// JobDistance jobDistance;
// if(ruin_distance == null) jobDistance = new JobDistanceAvgCosts(vrp.getTransportCosts());
// else {
// if(ruin_distance.equals("euclidean")){
// jobDistance = new EuclideanServiceDistance();
// }
// else throw new IllegalStateException("does not know ruin.distance " + ruin_distance + ". either ommit ruin.distance then the "
// + "default is used or use 'euclidean'");
// }
// RuinStrategy ruin = getRadialRuin(vrp, activityStates,definedClasses, modKey, shareToRuin, jobDistance);
// SearchStrategyModule module = getModule(moduleName, ruin);
// definedClasses.put(strategyModuleKey, module);
// return module;
// }
if(moduleName.equals("gendreauPostOpt")){
int iterations = moduleConfig.getInt("iterations");
double share = moduleConfig.getDouble("share");
@ -734,13 +777,11 @@ public class VehicleRoutingAlgorithms {
return bestInsertion;
}
private static RuinStrategy getRadialRuin(VehicleRoutingProblem vrp,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, double shareToRuin) {
private static RuinStrategy getRadialRuin(VehicleRoutingProblem vrp, RouteStates activityStates, TypedMap definedClasses, ModKey modKey, double shareToRuin, JobDistance jobDistance) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRadial.newInstance(vrp, shareToRuin, new JobDistanceAvgCosts(vrp.getTransportCosts()), new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
ruin = RuinRadial.newInstance(vrp, shareToRuin, jobDistance, new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
return ruin;
@ -778,12 +819,6 @@ public class VehicleRoutingAlgorithms {
double penalty = 0.0;
if(jobsInSolution != vrp.getJobs().values().size()){
throw new IllegalStateException("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
//// throw new IllegalStateException("solution not valid\n" +
//// "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("a penalty of 1000 is added for each unassigned customer");
// penalty = (vrp.getJobs().values().size() - jobsInSolution)*1000.0;
// logger.warn("penalty = " + penalty);
}
double totalCost = RouteUtils.getTotalCost(vrpSolution.getRoutes());
vrpSolution.setCost(totalCost + penalty);
@ -841,37 +876,37 @@ public class VehicleRoutingAlgorithms {
}
private static SearchStrategyModule getModule(final String moduleName, final RuinStrategy ruin) {
return new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
ruin.ruin(vrpSolution.getRoutes());
return vrpSolution;
}
@Override
public String toString() {
return "[name="+ruin+"]";
}
@Override
public String getName() {
return moduleName;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// TODO Auto-generated method stub
}
};
}
// private static SearchStrategyModule getModule(final String moduleName, final RuinStrategy ruin) {
//
//
// return new SearchStrategyModule() {
//
// private Logger logger = Logger.getLogger(SearchStrategyModule.class);
//
// @Override
// public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
// ruin.ruin(vrpSolution.getRoutes());
// return vrpSolution;
// }
//
// @Override
// public String toString() {
// return "[name="+ruin+"]";
// }
//
// @Override
// public String getName() {
// return moduleName;
// }
//
// @Override
// public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// // TODO Auto-generated method stub
//
// }
//
// };
// }

View file

@ -242,6 +242,12 @@ public class VehicleRoutingProblem {
return this;
}
/**
* Sets the neighborhood.
*
* @param neighborhood
* @return
*/
public Builder setNeighborhood(Neighborhood neighborhood){
this.neighborhood = neighborhood;
return this;
@ -261,6 +267,13 @@ public class VehicleRoutingProblem {
return this;
}
/**
* Builds the {@link VehicleRoutingProblem}.
*
* <p>If {@link VehicleRoutingTransportCosts} are not set, {@link CrowFlyCosts} is used.
*
* @return {@link VehicleRoutingProblem}
*/
public VehicleRoutingProblem build() {
log.info("build problem ...");
if(transportCosts == null){
@ -275,6 +288,12 @@ public class VehicleRoutingProblem {
return this;
}
/**
* Adds a collection of jobs.
*
* @param jobs
* @return
*/
public Builder addAllJobs(Collection<Job> jobs) {
for(Job j : jobs){
addJob(j);
@ -282,6 +301,12 @@ public class VehicleRoutingProblem {
return this;
}
/**
* Adds a collection of vehicles.
*
* @param vehicles
* @return
*/
public Builder addAllVehicles(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
addVehicle(v);
@ -289,11 +314,20 @@ public class VehicleRoutingProblem {
return this;
}
/**
* Gets an unmodifiable collection of already added vehicles.
*
* @return collection of vehicles
*/
public Collection<Vehicle> getAddedVehicles(){
return Collections.unmodifiableCollection(vehicles);
}
/**
* Gets an unmodifiable collection of already added services.
*
* @return collection of services
*/
public Collection<Service> getAddedServices(){
return Collections.unmodifiableCollection(services);
}

View file

@ -135,7 +135,7 @@
<xs:complexType name="ruinType">
<xs:sequence>
<xs:element name="share">
<xs:element name="share" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:double">
<xs:minInclusive value="0.0"/>
@ -143,6 +143,13 @@
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="distance" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="euclidean"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" use="required">
<xs:simpleType>

View file

@ -230,43 +230,7 @@ public class TestAlgorithmReader {
for(SearchStrategy strat : algo.getSearchStrategyManager().getStrategies()){
nOfModules += strat.getSearchStrategyModules().size();
}
assertEquals(6, nOfModules);
}
@Test
public void whenCreatingAlgorithm_nOfUniqueInstancesOfInsertionModulesIsCorrect(){
VehicleRoutingAlgorithm algo = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, config);
int nOfBestInsertions = 0;
Set<SearchStrategyModule> uniqueStrategies = new HashSet<SearchStrategyModule>();
for(SearchStrategy strat : algo.getSearchStrategyManager().getStrategies()){
for(SearchStrategyModule module : strat.getSearchStrategyModules()){
if(module.getName().equals("bestInsertion")){
nOfBestInsertions++;
uniqueStrategies.add(module);
}
}
}
assertEquals(3, nOfBestInsertions);
assertEquals(2, uniqueStrategies.size());
}
@Test
public void whenCreatingAlgorithm_nOfUniqueInstancesOfRuinModulesIsCorrect(){
VehicleRoutingAlgorithm algo = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, config);
int nOfRuinModules = 0;
Set<SearchStrategyModule> uniqueStrategies = new HashSet<SearchStrategyModule>();
for(SearchStrategy strat : algo.getSearchStrategyManager().getStrategies()){
for(SearchStrategyModule module : strat.getSearchStrategyModules()){
if(module.getName().endsWith("Ruin")){
nOfRuinModules++;
uniqueStrategies.add(module);
}
}
}
assertEquals(3, nOfRuinModules);
assertEquals(2, uniqueStrategies.size());
assertEquals(3, nOfModules);
}
@Test

View file

@ -34,10 +34,11 @@
<selector name="selectBest"/>
<acceptor name="acceptNewRemoveWorst"/>
<modules>
<module name="randomRuin">
<share>0.5</share>
</module>
<module name="bestInsertion">
<module name="ruin_and_recreate">
<ruin name="randomRuin">
<share>0.5</share>
</ruin>
<insertion name="bestInsertion"/>
</module>
</modules>
<probability>0.4</probability>
@ -47,10 +48,12 @@
<selector name="selectBest"/>
<acceptor name="acceptNewRemoveWorst"/>
<modules>
<module name="randomRuin">
<share>0.1</share>
<module name="ruin_and_recreate">
<ruin name="randomRuin">
<share>0.1</share>
</ruin>
<insertion name="bestInsertion"/>
</module>
<module name="bestInsertion"></module>
</modules>
<probability>0.4</probability>
</searchStrategy>
@ -59,11 +62,12 @@
<selector name="selectBest"/>
<acceptor name="acceptNewRemoveWorst"/>
<modules>
<module name="radialRuin">
<share>0.3</share>
<distanceMeasure>euclid</distanceMeasure>
<module name="ruin_and_recreate">
<ruin name="radialRuin">
<share>0.3</share>
</ruin>
<insertion name="bestInsertion" id="1"/>
</module>
<module name="bestInsertion" id="1"></module>
</modules>
<probability>0.2</probability>
</searchStrategy>