From f8ea447cb9f6934c41650760c47e07fdbb1d62c9 Mon Sep 17 00:00:00 2001 From: oblonski Date: Wed, 2 Nov 2016 14:23:23 +0100 Subject: [PATCH] add max distance feature - related to #285 --- .../VehicleDependentTraveledDistance.java | 120 ++++++++++++++++++ .../constraint/MaxDistanceConstraint.java | 117 +++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/VehicleDependentTraveledDistance.java create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java 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/problem/constraint/MaxDistanceConstraint.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java new file mode 100644 index 00000000..9e3f790c --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java @@ -0,0 +1,117 @@ +/* + * 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.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]; + 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; + if(!iFacts.getRoute().isEmpty()){ + currentDistance = stateManager.getRouteState(iFacts.getRoute(),iFacts.getNewVehicle(), distanceId,Double.class); + } + + double distanceNewAct2nextAct = distanceCalculator.getDistance(newAct.getLocation(), nextAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle()); + double distancePrevAct2NextAct = distanceCalculator.getDistance(prevAct.getLocation(), nextAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle()); + if(nextAct instanceof End && !iFacts.getNewVehicle().isReturnToDepot()){ + distanceNewAct2nextAct = 0; + distancePrevAct2NextAct = 0; + } + double distancePrevAct2NewAct = distanceCalculator.getDistance(prevAct.getLocation(), newAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle()); + double additionalDistance = distancePrevAct2NewAct + distanceNewAct2nextAct - distancePrevAct2NextAct; + + double maxDistance = getMaxDistance(iFacts.getNewVehicle()); + if(currentDistance + additionalDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED; + + if(newAct instanceof DeliverShipment){ + int iIndexOfPickup = iFacts.getRelatedActivityContext().getInsertionIndex(); + TourActivity pickup = iFacts.getAssociatedActivities().get(0); + TourActivity actBeforePickup; + TourActivity actAfterPickup = iFacts.getRoute().getActivities().get(iIndexOfPickup); + if(iIndexOfPickup > 0) actBeforePickup = iFacts.getRoute().getActivities().get(iIndexOfPickup-1); + else actBeforePickup = iFacts.getRoute().getStart(); + double additionalDistanceOfPickup = distanceCalculator.getDistance(actBeforePickup.getLocation(),pickup.getLocation(),0,iFacts.getNewVehicle()) + + distanceCalculator.getDistance(pickup.getLocation(),actAfterPickup.getLocation(),0,iFacts.getNewVehicle()) + - distanceCalculator.getDistance(actBeforePickup.getLocation(), actAfterPickup.getLocation(),0,iFacts.getNewVehicle()); + + if(currentDistance + additionalDistance + additionalDistanceOfPickup > maxDistance){ + return ConstraintsStatus.NOT_FULFILLED; + } + } + + return ConstraintsStatus.FULFILLED; + } + + private boolean hasMaxDistance(Vehicle newVehicle){ + 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; + } +}