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

Merge branch 'master' into multiple-cap-constraints

Conflicts:
	jsprit-core/src/main/java/jsprit/core/problem/vehicle/VehicleType.java
	jsprit-core/src/main/java/jsprit/core/problem/vehicle/VehicleTypeImpl.java
	jsprit-core/src/test/java/jsprit/core/problem/vehicle/VehicleImplTest.java
	jsprit-core/src/test/java/jsprit/core/problem/vehicle/VehicleTypeImplTest.java
This commit is contained in:
oblonski 2014-01-16 15:15:16 -05:00
commit 2993202d49
35 changed files with 2623 additions and 64 deletions

View file

@ -20,7 +20,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import jsprit.core.problem.cost.VehicleRoutingTransportCosts;
@ -29,7 +32,9 @@ import jsprit.core.problem.job.Job;
import jsprit.core.problem.job.Service;
import jsprit.core.problem.job.Shipment;
import jsprit.core.problem.solution.route.activity.TourActivity;
import jsprit.core.problem.vehicle.PenaltyVehicleType;
import jsprit.core.problem.vehicle.Vehicle;
import jsprit.core.problem.vehicle.VehicleImpl;
import jsprit.core.problem.vehicle.VehicleType;
import jsprit.core.problem.vehicle.VehicleTypeImpl;
import jsprit.core.util.Coordinate;
@ -79,6 +84,57 @@ public class VehicleRoutingProblem {
*/
public static class Builder {
/**
* Two locTypeKeys are equal if they have the same locationId and typeId
*
* @author schroeder
*
*/
private static class LocTypeKey {
String locationId;
String typeId;
public LocTypeKey(String locationId, String typeId) {
super();
this.locationId = locationId;
this.typeId = typeId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((locationId == null) ? 0 : locationId.hashCode());
result = prime * result
+ ((typeId == null) ? 0 : typeId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LocTypeKey other = (LocTypeKey) obj;
if (locationId == null) {
if (other.locationId != null)
return false;
} else if (!locationId.equals(other.locationId))
return false;
if (typeId == null) {
if (other.typeId != null)
return false;
} else if (!typeId.equals(other.typeId))
return false;
return true;
}
}
/**
* Returns a new instance of this builder.
*
@ -138,6 +194,16 @@ public class VehicleRoutingProblem {
}
};
private boolean addPenaltyVehicles = false;
private double penaltyFactor = 1.0;
private Double penaltyFixedCosts = null;
/**
* @deprecated use static method .newInstance() instead
*/
@Deprecated
public Builder() {
jobs = new HashMap<String, Job>();
vehicles = new ArrayList<Vehicle>();
@ -169,7 +235,7 @@ public class VehicleRoutingProblem {
/**
* Returns the unmodifiable map of locations (mapped by their id).
*
* @return
* @return map with locations
*/
public Map<String,Coordinate> getLocationMap(){
return Collections.unmodifiableMap(coordinates);
@ -209,10 +275,10 @@ public class VehicleRoutingProblem {
/**
* Sets the type of fleetSize.
*
* <p>FleetSize is either FleetSize.INFINITE or FleetSize.FINITE
* <p>FleetSize is either FleetSize.INFINITE or FleetSize.FINITE. By default it is FleetSize.INFINITE.
*
* @param fleetSize
* @return
* @return this builder
*/
public Builder setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
@ -225,7 +291,7 @@ public class VehicleRoutingProblem {
* <p>Note that job.getId() must be unique, i.e. no job (either it is a shipment or a service) is allowed to have an already allocated id.
*
* @param job
* @return
* @return this builder
* @throws IllegalStateException if job is neither a shipment nor a service, or jobId has already been added.
*/
public Builder addJob(Job job) {
@ -254,7 +320,7 @@ public class VehicleRoutingProblem {
*
*
* @param vehicle
* @return
* @return this builder
*/
public Builder addVehicle(Vehicle vehicle) {
vehicles.add(vehicle);
@ -271,7 +337,7 @@ public class VehicleRoutingProblem {
* <p>By default it is set to zero.
*
* @param activityCosts
* @return
* @return this builder
* @see VehicleRoutingActivityCosts
*/
public Builder setActivityCosts(VehicleRoutingActivityCosts activityCosts){
@ -292,9 +358,46 @@ public class VehicleRoutingProblem {
logger.warn("set routing costs crowFlyDistance.");
transportCosts = new CrowFlyCosts(getLocations());
}
if(addPenaltyVehicles){
if(fleetSize.equals(FleetSize.INFINITE)){
logger.warn("penaltyType and FleetSize.INFINITE does not make sense. thus no penalty-types are added.");
}
else{
addPenaltyVehicles();
}
}
return new VehicleRoutingProblem(this);
}
private void addPenaltyVehicles() {
Set<LocTypeKey> locTypeKeys = new HashSet<LocTypeKey>();
List<Vehicle> uniqueVehicles = new ArrayList<Vehicle>();
for(Vehicle v : vehicles){
LocTypeKey key = new LocTypeKey(v.getLocationId(),v.getType().getTypeId());
if(!locTypeKeys.contains(key)){
uniqueVehicles.add(v);
locTypeKeys.add(key);
}
}
for(Vehicle v : uniqueVehicles){
double fixed = v.getType().getVehicleCostParams().fix * penaltyFactor;
if(penaltyFixedCosts!=null){
fixed = penaltyFixedCosts;
}
VehicleTypeImpl t = VehicleTypeImpl.Builder.newInstance(v.getType().getTypeId(), v.getCapacity())
.setCostPerDistance(penaltyFactor*v.getType().getVehicleCostParams().perDistanceUnit)
.setCostPerTime(penaltyFactor*v.getType().getVehicleCostParams().perTimeUnit)
.setFixedCost(fixed)
.build();
PenaltyVehicleType penType = new PenaltyVehicleType(t,penaltyFactor);
String vehicleId = "penaltyVehicle_" + v.getLocationId() + "_" + t.getTypeId();
Vehicle penVehicle = VehicleImpl.Builder.newInstance(vehicleId).setEarliestStart(v.getEarliestDeparture())
.setLatestArrival(v.getLatestArrival()).setLocationCoord(v.getCoord()).setLocationId(v.getLocationId())
.setReturnToDepot(v.isReturnToDepot()).setType(penType).build();
addVehicle(penVehicle);
}
}
public Builder addLocation(String id, Coordinate coord) {
coordinates.put(id, coord);
return this;
@ -304,7 +407,7 @@ public class VehicleRoutingProblem {
* Adds a collection of jobs.
*
* @param jobs which is a collection of jobs that subclasses Job
* @return
* @return this builder
*/
public Builder addAllJobs(Collection<? extends Job> jobs) {
for(Job j : jobs){
@ -317,7 +420,7 @@ public class VehicleRoutingProblem {
* Adds a collection of vehicles.
*
* @param vehicles
* @return
* @return this builder
*/
public Builder addAllVehicles(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
@ -335,17 +438,61 @@ public class VehicleRoutingProblem {
return Collections.unmodifiableCollection(vehicles);
}
/**
* Gets an unmodifiable collection of already added vehicle-types.
*
* @returns collection of vehicle-types
*/
public Collection<VehicleType> getAddedVehicleTypes(){
return Collections.unmodifiableCollection(vehicleTypes);
}
/**
* Adds constraint to problem.
*
* @param constraint
* @return
* @return this builder
*/
public Builder addConstraint(jsprit.core.problem.constraint.Constraint constraint){
constraints.add(constraint);
return this;
}
/**
* Adds penaltyVehicles, i.e. for every unique vehicle-location and type combination a penalty-vehicle is constructed having penaltyFactor times higher fixed and variable costs
* (see .addPenaltyVehicles(double penaltyFactor, double penaltyFixedCosts) if fixed costs = 0.0).
*
* <p>This only makes sense for FleetSize.FINITE. Thus, penaltyVehicles are only added if is FleetSize.FINITE.
* <p>The id of penaltyVehicles is constructed as follows vehicleId = "penaltyVehicle" + "_" + {locationId} + "_" + {typeId}.
* <p>By default: no penalty-vehicles are added
*
* @param penaltyFactor
* @return this builder
*/
public Builder addPenaltyVehicles(double penaltyFactor){
this.addPenaltyVehicles = true;
this.penaltyFactor = penaltyFactor;
return this;
}
/**
* Adds penaltyVehicles, i.e. for every unique vehicle-location and type combination a penalty-vehicle is constructed having penaltyFactor times higher fixed and variable costs.
* <p>This method takes penaltyFixedCosts as absolute value in contrary to the method without penaltyFixedCosts where fixedCosts is the product of penaltyFactor and typeFixedCosts.
* <p>This only makes sense for FleetSize.FINITE. Thus, penaltyVehicles are only added if is FleetSize.FINITE.
* <p>The id of penaltyVehicles is constructed as follows vehicleId = "penaltyVehicle" + "_" + {locationId} + "_" + {typeId}.
* <p>By default: no penalty-vehicles are added
*
* @param penaltyFactor
* @param penaltyFixedCosts which is an absolute penaltyValue (in contrary to penaltyFactor)
* @return this builder
*/
public Builder addPenaltyVehicles(double penaltyFactor, double penaltyFixedCosts){
this.addPenaltyVehicles = true;
this.penaltyFactor = penaltyFactor;
this.penaltyFixedCosts = penaltyFixedCosts;
return this;
}
/**
* Returns an unmodifiable collection of already added jobs.
*

View file

@ -27,7 +27,6 @@ public class FiniteFleetManagerFactory implements VehicleFleetManagerFactory{
private Collection<Vehicle> vehicles;
/**
* Constucts the factory.
*
@ -40,9 +39,14 @@ public class FiniteFleetManagerFactory implements VehicleFleetManagerFactory{
/**
* Creates the finite fleetmanager.
*
* @return VehicleFleetManager
* @throws IllegalStateManager if vehicles == null or vehicles.isEmpty()
*/
@Override
public VehicleFleetManager createFleetManager() {
if(vehicles == null) throw new IllegalStateException("vehicles is null. this must not be.");
if(vehicles.isEmpty()) throw new IllegalStateException("vehicle-collection is empty. this must not be");
return new VehicleFleetManagerImpl(vehicles);
}

View file

@ -94,12 +94,14 @@ public class VehicleImpl implements Vehicle {
}
/**
* Sets the {@link VehicleType}.
* Sets the {@link VehicleType}.<br>
*
* @param type
* @throws IllegalStateException if type is null
* @return this builder
*/
public Builder setType(VehicleType type){
if(type==null) throw new IllegalStateException("type cannot be null.");
this.type = type;
return this;
}
@ -166,6 +168,9 @@ public class VehicleImpl implements Vehicle {
/**
* Builds and returns the vehicle.
*
* <p>if {@link VehicleType} is not set, default vehicle-type is set with id="default" and
* capacity=0
*
* @return vehicle
* @throw IllegalStateException if both locationId and locationCoord is not set
*/
@ -189,6 +194,8 @@ public class VehicleImpl implements Vehicle {
/**
* Returns empty/noVehicle which is a vehicle having no capacity, no type and no reasonable id.
*
* <p>NoVehicle has id="noVehicle" and extends {@link VehicleImpl}
*
* @return emptyVehicle
*/
public static NoVehicle createNoVehicle(){
@ -219,9 +226,14 @@ public class VehicleImpl implements Vehicle {
returnToDepot = builder.returnToDepot;
}
/**
* Returns String with attributes of this vehicle
*
* <p>String has the following format [attr1=val1][attr2=val2]...[attrn=valn]
*/
@Override
public String toString() {
return "[id="+id+"][type="+type+"][locationId="+locationId+"][coord=" + coord + "]";
return "[id="+id+"][type="+type+"][locationId="+locationId+"][coord=" + coord + "][isReturnToDepot=" + isReturnToDepot() + "]";
}
public Coordinate getCoord() {

View file

@ -19,17 +19,49 @@ package jsprit.core.problem.vehicle;
import jsprit.core.problem.Capacity;
import jsprit.core.problem.vehicle.VehicleTypeImpl.VehicleCostParams;
/**
* Basic interface for vehicle-type-data.
*
* @author schroeder
*
*/
public interface VehicleType {
/**
* Returns typeId.
*
* @return typeId
*/
public String getTypeId();
/**
* Returns capacity.
*
* <p>In future versions there will be a capacity-object with an arbitrary number of capacity dimensions. (stefan,11.01.14)
*
* @return cap
*/
public int getCapacity();
/**
* Returns capacity dimensions.
*
* @return {@link Capacity}
*/
public Capacity getCapacityDimensions();
/**
* Returns maximum velocity of this vehicle-type.
*
* @return max velocity
*/
public double getMaxVelocity();
/**
* Return the cost-parameter of this vehicle-type.
*
* @return parameter
*/
public VehicleCostParams getVehicleCostParams();
}

View file

@ -16,17 +16,31 @@
******************************************************************************/
package jsprit.core.problem.vehicle;
import jsprit.core.problem.Capacity;
/**
* Implementation of {@link VehicleType}.
*
* <p>Two vehicle-types are equal if they have the same typeId.
*
* @author schroeder
*
*/
public class VehicleTypeImpl implements VehicleType {
/**
* CostParameter consisting of fixed cost parameter, time-based cost parameter and distance-based cost parameter.
*
* @author schroeder
*
*/
public static class VehicleCostParams {
public static VehicleTypeImpl.VehicleCostParams newInstance(double fix, double perTimeUnit,double perDistanceUnit){
return new VehicleCostParams(fix, perTimeUnit, perDistanceUnit);
}
public final double fix;
public final double perTimeUnit;
public final double perDistanceUnit;
@ -44,10 +58,28 @@ public class VehicleTypeImpl implements VehicleType {
}
}
/**
* Builder that builds the vehicle-type.
*
* @author schroeder
*
*/
public static class Builder{
/**
* Returns a new instance.
*
* <p>Input parameters are id and capacity. Note that two vehicle-types are equal
* if they have the same vehicleId.
*
* @param id
* @param capacity
* @return the vehicleType builder
* @throws IllegalStateException if capacity is smaller than zero or id is null
*/
public static VehicleTypeImpl.Builder newInstance(String id, int capacity){
if(capacity < 0) throw new IllegalStateException("capacity cannot be smaller than zero");
if(id == null) throw new IllegalStateException("typeId must be null");
Builder builder = new Builder(id,capacity);
builder.addCapacityDimension(0, capacity);
return builder;
@ -72,7 +104,13 @@ public class VehicleTypeImpl implements VehicleType {
private Capacity capacityDimensions;
public Builder(String id, int capacity) {
/**
* Constructs the builder.
*
* @param id
* @param capacity
*/
private Builder(String id, int capacity) {
super();
this.id = id;
this.capacity = capacity;
@ -82,14 +120,68 @@ public class VehicleTypeImpl implements VehicleType {
this.id = id;
}
public VehicleTypeImpl.Builder setMaxVelocity(double inMeterPerSeconds){ this.maxVelo = inMeterPerSeconds; return this; }
/**
* Sets the maximum velocity this vehicle-type can go [in meter per seconds].
*
* @param inMeterPerSeconds
* @return this builder
* @throws IllegalStateException if velocity is smaller than zero
*/
public VehicleTypeImpl.Builder setMaxVelocity(double inMeterPerSeconds){
if(inMeterPerSeconds < 0.0) throw new IllegalStateException("velocity cannot be smaller than zero");
this.maxVelo = inMeterPerSeconds; return this;
}
public VehicleTypeImpl.Builder setFixedCost(double fixedCost) { this.fixedCost = fixedCost; return this; }
/**
* Sets the fixed costs of the vehicle-type.
*
* <p>by default it is 0.
*
* @param fixedCost
* @return this builder
* @throws IllegalStateException if fixedCost is smaller than zero
*/
public VehicleTypeImpl.Builder setFixedCost(double fixedCost) {
if(fixedCost < 0.0) throw new IllegalStateException("fixed costs cannot be smaller than zero");
this.fixedCost = fixedCost;
return this;
}
public VehicleTypeImpl.Builder setCostPerDistance(double perDistance){ this.perDistance = perDistance; return this; }
/**
* Sets the cost per distance unit, for instance per meter.
*
* <p>by default it is 1.0
*
* @param perDistance
* @return this builder
* @throws IllegalStateException if perDistance is smaller than zero
*/
public VehicleTypeImpl.Builder setCostPerDistance(double perDistance){
if(perDistance < 0.0) throw new IllegalStateException("cost per distance must not be smaller than zero");
this.perDistance = perDistance;
return this;
}
public VehicleTypeImpl.Builder setCostPerTime(double perTime){ this.perTime = perTime; return this; }
/**
* Sets cost per time unit, for instance per second.
*
* <p>by default it is 0.0
*
* @param perTime
* @return this builder
* @throws IllegalStateException if costPerTime is smaller than zero
*/
public VehicleTypeImpl.Builder setCostPerTime(double perTime){
if(perTime < 0.0) throw new IllegalStateException();
this.perTime = perTime;
return this;
}
/**
* Builds the vehicle-type.
*
* @return VehicleTypeImpl
*/
public VehicleTypeImpl build(){
capacityDimensions = capacityBuilder.build();
return new VehicleTypeImpl(this);
@ -112,6 +204,9 @@ public class VehicleTypeImpl implements VehicleType {
return result;
}
/**
* Two vehicle-types are equal if they have the same vehicleId.
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
@ -135,9 +230,10 @@ public class VehicleTypeImpl implements VehicleType {
private final VehicleTypeImpl.VehicleCostParams vehicleCostParams;
private double maxVelocity;
private Capacity capacityDimensions;
private final Capacity capacityDimensions;
private final double maxVelocity;
/**
* @deprecated use builder instead
@ -147,6 +243,11 @@ public class VehicleTypeImpl implements VehicleType {
return new VehicleTypeImpl(typeId, capacity, para);
}
/**
* priv constructor constructing vehicle-type
*
* @param builder
*/
private VehicleTypeImpl(VehicleTypeImpl.Builder builder){
typeId = builder.id;
capacity = builder.capacity;
@ -155,13 +256,21 @@ public class VehicleTypeImpl implements VehicleType {
capacityDimensions = builder.capacityDimensions;
}
/**
* @deprecated use Builder.newInstance(...) instead.
*
* @param typeId
* @param capacity
* @param vehicleCostParams
*/
@Deprecated
public VehicleTypeImpl(String typeId, int capacity,VehicleTypeImpl.VehicleCostParams vehicleCostParams) {
super();
this.typeId = typeId;
this.capacity = capacity;
this.vehicleCostParams = vehicleCostParams;
capacityDimensions = Capacity.Builder.newInstance().addDimension(0, capacity).build();
this.capacityDimensions = Capacity.Builder.newInstance().addDimension(0, capacity).build();
this.maxVelocity = Double.MAX_VALUE;
}
/* (non-Javadoc)