Skip to content

Commit

Permalink
Add offline optimization tool
Browse files Browse the repository at this point in the history
  • Loading branch information
luchengqi7 committed Oct 16, 2023
1 parent e608ecc commit 037a2a5
Show file tree
Hide file tree
Showing 18 changed files with 1,967 additions and 0 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.matsim.contrib.drt.extension.preplanned.optimizer.offlineOptimization.basicStructures;

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Person;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;

import java.util.*;

public record FleetSchedules(
Map<Id<DvrpVehicle>, List<TimetableEntry>> vehicleToTimetableMap,
Map<Id<Person>, Id<DvrpVehicle>> requestIdToVehicleMap,
Map<Id<Person>, GeneralRequest> pendingRequests) {

public static List<TimetableEntry> copyTimetable(List<TimetableEntry> timetable) {
List<TimetableEntry> timetableCopy = new ArrayList<>();
for (TimetableEntry timetableEntry : timetable) {
timetableCopy.add(new TimetableEntry(timetableEntry));
}
return timetableCopy;
}

public static FleetSchedules initializeFleetSchedules(Map<Id<DvrpVehicle>, OnlineVehicleInfo> onlineVehicleInfoMap) {
Map<Id<DvrpVehicle>, List<TimetableEntry>> vehicleToTimetableMap = new LinkedHashMap<>();
for (OnlineVehicleInfo vehicleInfo : onlineVehicleInfoMap.values()) {
vehicleToTimetableMap.put(vehicleInfo.vehicle().getId(), new ArrayList<>());
}
Map<Id<Person>, Id<DvrpVehicle>> requestIdToVehicleMap = new HashMap<>();
Map<Id<Person>, GeneralRequest> rejectedRequests = new LinkedHashMap<>();
return new FleetSchedules(vehicleToTimetableMap, requestIdToVehicleMap, rejectedRequests);
}

public FleetSchedules copySchedule() {
Map<Id<DvrpVehicle>, List<TimetableEntry>> vehicleToTimetableMapCopy = new LinkedHashMap<>();
for (Id<DvrpVehicle> vehicleId : this.vehicleToTimetableMap().keySet()) {
vehicleToTimetableMapCopy.put(vehicleId, copyTimetable(this.vehicleToTimetableMap.get(vehicleId)));
}
Map<Id<Person>, Id<DvrpVehicle>> requestIdToVehicleMapCopy = new HashMap<>(this.requestIdToVehicleMap);
Map<Id<Person>, GeneralRequest> rejectedRequestsCopy = new LinkedHashMap<>(this.pendingRequests);

return new FleetSchedules(vehicleToTimetableMapCopy, requestIdToVehicleMapCopy, rejectedRequestsCopy);
}

public void updateFleetSchedule(Network network, LinkToLinkTravelTimeMatrix linkToLinkTravelTimeMatrix,
Map<Id<DvrpVehicle>, OnlineVehicleInfo> onlineVehicleInfoMap) {
for (Id<DvrpVehicle> vehicleId : onlineVehicleInfoMap.keySet()) {
// When new vehicle enters service, create a new entry for it
this.vehicleToTimetableMap().computeIfAbsent(vehicleId, t -> new ArrayList<>());
if (!onlineVehicleInfoMap.containsKey(vehicleId)) {
// When a vehicle ends service, remove it from the schedule
this.vehicleToTimetableMap().remove(vehicleId);
}
}

for (Id<DvrpVehicle> vehicleId : this.vehicleToTimetableMap().keySet()) {
List<TimetableEntry> timetable = this.vehicleToTimetableMap().get(vehicleId);
if (!timetable.isEmpty()) {
Link currentLink = onlineVehicleInfoMap.get(vehicleId).currentLink();
double currentTime = onlineVehicleInfoMap.get(vehicleId).divertableTime();
for (TimetableEntry timetableEntry : timetable) {
Id<Link> stopLinkId = timetableEntry.getStopType() == TimetableEntry.StopType.PICKUP ?
timetableEntry.getRequest().getFromLinkId() : timetableEntry.getRequest().getToLinkId();
Link stopLink = network.getLinks().get(stopLinkId);
double newArrivalTime = currentTime + linkToLinkTravelTimeMatrix.getTravelTime(currentLink, stopLink, currentTime);
timetableEntry.updateArrivalTime(newArrivalTime);

// Delay the latest arrival time of the stop when necessary (e.g., due to traffic uncertainty), in order to make sure assigned requests will remain feasible
if (timetableEntry.getStopType() == TimetableEntry.StopType.PICKUP) {
double originalLatestDepartureTime = timetableEntry.getRequest().getLatestDepartureTime();
timetableEntry.getRequest().setLatestDepartureTime(Math.max(originalLatestDepartureTime, newArrivalTime));
} else {
double originalLatestArrivalTime = timetableEntry.getRequest().getLatestArrivalTime();
timetableEntry.getRequest().setLatestArrivalTime(Math.max(originalLatestArrivalTime, newArrivalTime));
}

currentTime = timetableEntry.getDepartureTime();
currentLink = stopLink;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.matsim.contrib.drt.extension.preplanned.optimizer.offlineOptimization.basicStructures;

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.population.Person;

public class GeneralRequest {
private final Id<Person> passengerId;
private final Id<Link> fromLinkId;
private final Id<Link> toLinkId;
private final double earliestDepartureTime;

// latest departure time (flexibility is needed to account for traffic uncertainty)
private double latestDepartureTime;
// latest arrival time (flexibility is needed to account for traffic uncertainty)
private double latestArrivalTime;

public GeneralRequest(Id<Person> passengerId, Id<Link> fromLinkId, Id<Link> toLinkId, double earliestDepartureTime,
double latestStartTime, double latestArrivalTime) {
this.passengerId = passengerId;
this.fromLinkId = fromLinkId;
this.toLinkId = toLinkId;
this.earliestDepartureTime = earliestDepartureTime;
this.latestDepartureTime = latestStartTime;
this.latestArrivalTime = latestArrivalTime;
}

public Id<Person> getPassengerId() {
return passengerId;
}

public double getEarliestDepartureTime() {
return earliestDepartureTime;
}

public double getLatestArrivalTime() {
return latestArrivalTime;
}

public double getLatestDepartureTime() {
return latestDepartureTime;
}

public Id<Link> getFromLinkId() {
return fromLinkId;
}

public Id<Link> getToLinkId() {
return toLinkId;
}

public void setLatestArrivalTime(double latestArrivalTime) {
this.latestArrivalTime = latestArrivalTime;
}

public void setLatestDepartureTime(double latestDepartureTime) {
this.latestDepartureTime = latestDepartureTime;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package org.matsim.contrib.drt.extension.preplanned.optimizer.offlineOptimization.basicStructures;

import one.util.streamex.EntryStream;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.network.Node;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.path.VrpPaths;
import org.matsim.contrib.dvrp.router.TimeAsTravelDisutility;
import org.matsim.contrib.zone.Zone;
import org.matsim.contrib.zone.skims.Matrix;
import org.matsim.contrib.zone.skims.TravelTimeMatrices;
import org.matsim.contrib.zone.skims.TravelTimeMatrix;
import org.matsim.core.router.util.TravelTime;

import java.util.*;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toMap;
import static org.matsim.contrib.dvrp.path.VrpPaths.FIRST_LINK_TT;

/**
* Link to link travel time to be used by the offline solver *
*/
public class LinkToLinkTravelTimeMatrix {
private final TravelTimeMatrix nodeToNodeTravelTimeMatrix;
private final TravelTime travelTime;
private final Network network;

LinkToLinkTravelTimeMatrix(Network network, TravelTime travelTime, Set<Id<Link>> relevantLinks, double time) {
this.network = network;
this.travelTime = travelTime;
this.nodeToNodeTravelTimeMatrix = calculateTravelTimeMatrix(relevantLinks, time);
}

public static LinkToLinkTravelTimeMatrix prepareLinkToLinkTravelMatrix(Network network, TravelTime travelTime, FleetSchedules previousSchedules,
Map<Id<DvrpVehicle>, OnlineVehicleInfo> onlineVehicleInfoMap, List<GeneralRequest> newRequests,
double time) {
Set<Id<Link>> relevantLinks = new HashSet<>();

// Vehicle locations
for (OnlineVehicleInfo onlineVehicleInfo : onlineVehicleInfoMap.values()) {
relevantLinks.add(onlineVehicleInfo.currentLink().getId());
}

// Requests locations
// requests on the timetable
for (List<TimetableEntry> timetable : previousSchedules.vehicleToTimetableMap().values()) {
for (TimetableEntry timetableEntry : timetable) {
if (timetableEntry.getStopType() == TimetableEntry.StopType.PICKUP) {
relevantLinks.add(timetableEntry.getRequest().getFromLinkId());
} else {
relevantLinks.add(timetableEntry.getRequest().getToLinkId());
}
}
}

// new requests
for (GeneralRequest request : newRequests) {
relevantLinks.add(request.getFromLinkId());
relevantLinks.add(request.getToLinkId());
}

// Pending rejected requests (i.e., not yet properly inserted and not yet formally rejected)
for (GeneralRequest request : previousSchedules.pendingRequests().values()) {
relevantLinks.add(request.getFromLinkId());
relevantLinks.add(request.getToLinkId());
}

return new LinkToLinkTravelTimeMatrix(network, travelTime, relevantLinks, time);
}

@Deprecated
public void updateFleetSchedule(FleetSchedules previousSchedules,
Map<Id<DvrpVehicle>, OnlineVehicleInfo> onlineVehicleInfoMap) {
for (Id<DvrpVehicle> vehicleId : onlineVehicleInfoMap.keySet()) {
previousSchedules.vehicleToTimetableMap().computeIfAbsent(vehicleId, t -> new ArrayList<>()); // When new vehicle enters service, create a new entry for it
if (!onlineVehicleInfoMap.containsKey(vehicleId)) {
previousSchedules.vehicleToTimetableMap().remove(vehicleId); // When a vehicle ends service, remove it from the schedule
}
}

for (Id<DvrpVehicle> vehicleId : previousSchedules.vehicleToTimetableMap().keySet()) {
List<TimetableEntry> timetable = previousSchedules.vehicleToTimetableMap().get(vehicleId);
if (!timetable.isEmpty()) {
Link currentLink = onlineVehicleInfoMap.get(vehicleId).currentLink();
double currentTime = onlineVehicleInfoMap.get(vehicleId).divertableTime();
for (TimetableEntry timetableEntry : timetable) {
Id<Link> stopLinkId = timetableEntry.getStopType() == TimetableEntry.StopType.PICKUP ?
timetableEntry.getRequest().getFromLinkId() : timetableEntry.getRequest().getToLinkId();
Link stopLink = network.getLinks().get(stopLinkId);
double newArrivalTime = currentTime + this.getTravelTime(currentLink, stopLink, currentTime);
timetableEntry.updateArrivalTime(newArrivalTime);
currentTime = timetableEntry.getDepartureTime();
currentLink = stopLink;
}
}
}
}

public double getTravelTime(Link fromLink, Link toLink, double departureTime) {
if (fromLink.getId().toString().equals(toLink.getId().toString())) {
return 0;
}
double travelTimeFromNodeToNode = nodeToNodeTravelTimeMatrix.getTravelTime(fromLink.getToNode(), toLink.getFromNode(), departureTime);
return FIRST_LINK_TT + travelTimeFromNodeToNode
+ VrpPaths.getLastLinkTT(travelTime, toLink, departureTime + travelTimeFromNodeToNode);
}

private TravelTimeMatrix calculateTravelTimeMatrix(Set<Id<Link>> relevantLinks, double time) {
Map<Node, Zone> zoneByNode = relevantLinks
.stream()
.flatMap(linkId -> Stream.of(network.getLinks().get(linkId).getFromNode(), network.getLinks().get(linkId).getToNode()))
.collect(toMap(n -> n, node -> new Zone(Id.create(node.getId(), Zone.class), "node", node.getCoord()),
(zone1, zone2) -> zone1));
var nodeByZone = EntryStream.of(zoneByNode).invert().toMap();
Matrix nodeToNodeMatrix = TravelTimeMatrices.calculateTravelTimeMatrix(new TravelTimeMatrices.RoutingParams(network, travelTime,
new TimeAsTravelDisutility(travelTime), Runtime.getRuntime().availableProcessors()), nodeByZone, time);

return (fromNode, toNode, departureTime) -> nodeToNodeMatrix.get(zoneByNode.get(fromNode), zoneByNode.get(toNode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.matsim.contrib.drt.extension.preplanned.optimizer.offlineOptimization.basicStructures;

import org.matsim.api.core.v01.network.Link;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;

public record OnlineVehicleInfo(DvrpVehicle vehicle, Link currentLink, double divertableTime) {
}
Loading

0 comments on commit 037a2a5

Please sign in to comment.