Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DRT detour constraints (based on PR #2834) #2997

Merged
merged 15 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.matsim.contrib.drt.optimizer.insertion;

import org.matsim.contrib.drt.passenger.DrtRequest;

/**
* This insertion cost calculator performs additional check on the maximum ride duration, on top of the original InsertionCostCalculator
* @author: Nico Kühnel (nkuehnel), Chengqi Lu (luchengqi7)
* */
public class MaxDetourInsertionCostCalculator implements InsertionCostCalculator {
private final InsertionCostCalculator delegate;

public MaxDetourInsertionCostCalculator(InsertionCostCalculator delegate) {
this.delegate = delegate;
}

@Override
public double calculate(DrtRequest drtRequest, InsertionGenerator.Insertion insertion, InsertionDetourTimeCalculator.DetourTimeInfo detourTimeInfo) {
if (violatesDetour(drtRequest, detourTimeInfo)) {
return INFEASIBLE_SOLUTION_COST;
}
return delegate.calculate(drtRequest, insertion, detourTimeInfo);
}

private boolean violatesDetour(DrtRequest drtRequest, InsertionDetourTimeCalculator.DetourTimeInfo detourTimeInfo) {
// Check if the max travel time constraint for the newly inserted request is violated
double rideDuration = detourTimeInfo.dropoffDetourInfo.arrivalTime - detourTimeInfo.pickupDetourInfo.departureTime;
return drtRequest.getMaxRideDuration() < rideDuration;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public static AcceptedDrtRequest createFromOriginalRequest(DrtRequest request) {
.earliestStartTime(request.getEarliestStartTime())
.latestStartTime(request.getLatestStartTime())
.latestArrivalTime(request.getLatestArrivalTime())
.maxRideDuration(request.getMaxRideDuration())
.build();
}

Expand All @@ -46,12 +47,14 @@ public static AcceptedDrtRequest createFromOriginalRequest(DrtRequest request) {
private final double earliestStartTime;
private final double latestStartTime;
private final double latestArrivalTime;
private final double maxRideDuration;

private AcceptedDrtRequest(Builder builder) {
request = builder.request;
earliestStartTime = builder.earliestStartTime;
latestStartTime = builder.latestStartTime;
latestArrivalTime = builder.latestArrivalTime;
maxRideDuration = builder.maxRideDuration;
}

public static Builder newBuilder() {
Expand All @@ -64,6 +67,7 @@ public static Builder newBuilder(AcceptedDrtRequest copy) {
builder.earliestStartTime = copy.getEarliestStartTime();
builder.latestStartTime = copy.getLatestStartTime();
builder.latestArrivalTime = copy.getLatestArrivalTime();
builder.maxRideDuration = copy.getMaxRideDuration();
return builder;
}

Expand All @@ -82,6 +86,9 @@ public double getLatestStartTime() {
public double getLatestArrivalTime() {
return latestArrivalTime;
}
public double getMaxRideDuration() {
return maxRideDuration;
}

public Id<Request> getId() {
return request.getId();
Expand Down Expand Up @@ -122,6 +129,7 @@ public static final class Builder {
private double earliestStartTime;
private double latestStartTime;
private double latestArrivalTime;
private double maxRideDuration;

private Builder() {
}
Expand All @@ -146,6 +154,11 @@ public Builder latestArrivalTime(double val) {
return this;
}

public Builder maxRideDuration(double val) {
this.maxRideDuration = val;
return this;
}

public AcceptedDrtRequest build() {
return new AcceptedDrtRequest(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class DrtRequest implements PassengerRequest {
private final double earliestStartTime;
private final double latestStartTime;
private final double latestArrivalTime;
private final double maxRideDuration;

private final List<Id<Person>> passengerIds = new ArrayList<>();
private final String mode;
Expand All @@ -52,6 +53,7 @@ private DrtRequest(Builder builder) {
earliestStartTime = builder.earliestStartTime;
latestStartTime = builder.latestStartTime;
latestArrivalTime = builder.latestArrivalTime;
maxRideDuration = builder.maxRideDuration;
passengerIds.addAll(builder.passengerIds);
mode = builder.mode;
fromLink = builder.fromLink;
Expand All @@ -69,6 +71,7 @@ public static Builder newBuilder(DrtRequest copy) {
builder.earliestStartTime = copy.getEarliestStartTime();
builder.latestStartTime = copy.getLatestStartTime();
builder.latestArrivalTime = copy.getLatestArrivalTime();
builder.maxRideDuration = copy.getMaxRideDuration();
builder.passengerIds = new ArrayList<>(copy.getPassengerIds());
builder.mode = copy.getMode();
builder.fromLink = copy.getFromLink();
Expand Down Expand Up @@ -100,6 +103,10 @@ public double getLatestArrivalTime() {
return latestArrivalTime;
}

public double getMaxRideDuration() {
return maxRideDuration;
}

@Override
public Link getFromLink() {
return fromLink;
Expand Down Expand Up @@ -133,6 +140,7 @@ public String toString() {
.add("earliestStartTime", earliestStartTime)
.add("latestStartTime", latestStartTime)
.add("latestArrivalTime", latestArrivalTime)
.add("maxRideDuration", maxRideDuration)
.add("passengerIds", passengerIds.stream().map(Object::toString).collect(Collectors.joining(",")))
.add("mode", mode)
.add("fromLink", fromLink)
Expand All @@ -146,6 +154,7 @@ public static final class Builder {
private double earliestStartTime;
private double latestStartTime;
private double latestArrivalTime;
private double maxRideDuration;
private List<Id<Person>> passengerIds = new ArrayList<>();
private String mode;
private Link fromLink;
Expand Down Expand Up @@ -179,6 +188,11 @@ public Builder latestArrivalTime(double val) {
return this;
}

public Builder maxRideDuration(double maxRideDuration) {
this.maxRideDuration = maxRideDuration;
return this;
}

public Builder passengerIds(List<Id<Person>> val) {
passengerIds = new ArrayList<>(val);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ public DrtRequest createRequest(Id<Request> id, List<Id<Person>> passengerIds, R
DrtRoute drtRoute = (DrtRoute)route;
double latestDepartureTime = departureTime + drtRoute.getMaxWaitTime();
double latestArrivalTime = departureTime + drtRoute.getTravelTime().seconds();
double maxRideDuration = drtRoute.getMaxRideTime();

eventsManager.processEvent(
new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerIds, fromLink.getId(), toLink.getId(),
drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime));
drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime, maxRideDuration));

DrtRequest request = DrtRequest.newBuilder()
.id(id)
Expand All @@ -66,6 +67,7 @@ public DrtRequest createRequest(Id<Request> id, List<Id<Person>> passengerIds, R
.earliestStartTime(departureTime)
.latestStartTime(latestDepartureTime)
.latestArrivalTime(latestArrivalTime)
.maxRideDuration(maxRideDuration)
.submissionTime(submissionTime)
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.matsim.contrib.drt.passenger;

import java.util.Optional;

public class MaxDetourOfferAcceptor implements DrtOfferAcceptor{
private final double promisedPickupTimeWindow;

public MaxDetourOfferAcceptor(double promisedPickupTimeWindow) {
this.promisedPickupTimeWindow = promisedPickupTimeWindow;
}

@Override
public Optional<AcceptedDrtRequest> acceptDrtOffer(DrtRequest request, double departureTime, double arrivalTime) {
double updatedPickupTimeWindow = Math.min(departureTime
+ promisedPickupTimeWindow, request.getLatestStartTime());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a different name than promisedPickupTimeWindow. We usually think of a time window as a [a,b) period, where a and b are time stamps. Here, promisedPickupTimeWindow is a duration and represents a time buffer.

In the drt config group, we use maxAllowedPickupDelay instetad of promisedPickupTimeWindow. I think we should also use this name in this class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also updatedPickupTimeWindow should be renamed. Maybe updatedLatestStartTime?

return Optional.of(AcceptedDrtRequest
.newBuilder()
.request(request)
.earliestStartTime(request.getEarliestStartTime())
.latestArrivalTime(Math.min(updatedPickupTimeWindow + request.getMaxRideDuration(), request.getLatestArrivalTime()))
.latestStartTime(updatedPickupTimeWindow).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,26 @@ public class DrtRequestSubmittedEvent extends PassengerRequestSubmittedEvent {
public static final String ATTRIBUTE_EARLIEST_DEPARTURE_TIME = "earliestDepartureTime";
public static final String ATTRIBUTE_LATEST_PICKUP_TIME = "latestPickupTime";
public static final String ATTRIBUTE_LATEST_DROPOFF_TIME = "latestDropoffTime";
public static final String ATTRIBUTE_MAX_RIDE_DURATION = "maxRideDuration";

private final double unsharedRideTime;
private final double unsharedRideDistance;

private final double earliestDepartureTime;
private final double latestPickupTime;
private final double latestDropoffTime;
private final double maxRideDuration;

public DrtRequestSubmittedEvent(double time, String mode, Id<Request> requestId, List<Id<Person>> personIds,
Id<Link> fromLinkId, Id<Link> toLinkId, double unsharedRideTime, double unsharedRideDistance,
double earliestDepartureTime, double latestPickupTime, double latestDropoffTime) {
double earliestDepartureTime, double latestPickupTime, double latestDropoffTime, double maxRideDuration ) {
super(time, mode, requestId, personIds, fromLinkId, toLinkId);
this.unsharedRideTime = unsharedRideTime;
this.unsharedRideDistance = unsharedRideDistance;
this.earliestDepartureTime = earliestDepartureTime;
this.latestPickupTime = latestPickupTime;
this.latestDropoffTime = latestDropoffTime;
this.maxRideDuration = maxRideDuration;
}

@Override
Expand Down Expand Up @@ -90,6 +93,9 @@ public final double getLatestDropoffTime() {
return latestDropoffTime;
}

public double getMaxRideDuration() {
return maxRideDuration;
}
@Override
public Map<String, String> getAttributes() {
Map<String, String> attr = super.getAttributes();
Expand All @@ -98,6 +104,7 @@ public Map<String, String> getAttributes() {
attr.put(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, earliestDepartureTime + "");
attr.put(ATTRIBUTE_LATEST_PICKUP_TIME, latestPickupTime + "");
attr.put(ATTRIBUTE_LATEST_DROPOFF_TIME, latestDropoffTime + "");
attr.put(ATTRIBUTE_MAX_RIDE_DURATION, maxRideDuration + "");
return attr;
}

Expand All @@ -118,7 +125,8 @@ public static DrtRequestSubmittedEvent convert(GenericEvent event) {
double earliestDepartureTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, "NaN"));
double latestPickupTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_PICKUP_TIME, "NaN"));
double latestDropoffTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_DROPOFF_TIME, "NaN"));
double maxRideDuration = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_MAX_RIDE_DURATION, "NaN"));
return new DrtRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId, unsharedRideTime,
unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime);
unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime, maxRideDuration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public double getMaxTravelTime() {
return getTravelTime().seconds(); // currently DrtRoute.travelTime is set to the max allowed travel time
}

public double getMaxRideTime() {
return routeDescription.getMaxRideTime();
}

public void setDirectRideTime(double directRideTime) {
this.routeDescription.setDirectRideTime(directRideTime);
}
Expand All @@ -86,6 +90,10 @@ public void setUnsharedPath(VrpPathWithTravelData unsharedPath) {
this.routeDescription.setUnsharedPath(links);
}

public void setMaxRideTime(double maxRideTime) {
this.routeDescription.setMaxRideTime(maxRideTime);
}


@Override
public String getRouteDescription() {
Expand Down Expand Up @@ -149,12 +157,18 @@ public static class RouteDescription {
private OptionalTime maxWaitTime = OptionalTime.undefined();
private OptionalTime directRideTime = OptionalTime.undefined();
private List<String> unsharedPath = new ArrayList<String>();
private OptionalTime maxRideTime = OptionalTime.undefined();

@JsonProperty("directRideTime")
public double getDirectRideTime() {
return directRideTime.isUndefined() ? OptionalTime.undefined().seconds() : directRideTime.seconds();
}

@JsonProperty("maxRideTime")
public double getMaxRideTime() {
return maxRideTime.seconds();
}

@JsonProperty("maxWaitTime")
public double getMaxWaitTime() {
return maxWaitTime.isUndefined() ? OptionalTime.undefined().seconds() : maxWaitTime.seconds();
Expand All @@ -169,13 +183,16 @@ public void setDirectRideTime(double directRideTime) {
this.directRideTime = OptionalTime.defined(directRideTime);
}

public void setMaxRideTime(double maxRideTime) {
this.maxRideTime = OptionalTime.defined(maxRideTime);
}

public void setMaxWaitTime(double maxWaitTime) {
this.maxWaitTime = OptionalTime.defined(maxWaitTime);
}

public void setUnsharedPath(List<String> unsharedPath) {
this.unsharedPath = unsharedPath;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,30 @@ static double getMaxTravelTime(DrtConfigGroup drtCfg, double unsharedRideTime) {
drtCfg.maxTravelTimeAlpha * unsharedRideTime + drtCfg.maxTravelTimeBeta);
}

/**
* Calculates the maximum ride time defined as: drtCfg.maxDetourAlpha * unsharedRideTime + drtCfg.maxDetourBeta
*
* @param drtCfg
* @param unsharedRideTime ride time of the direct (shortest-time) route
* @return maximum ride time
*/
static double getMaxRideTime(DrtConfigGroup drtCfg, double unsharedRideTime) {
return Math.min(unsharedRideTime + drtCfg.maxAbsoluteDetour, drtCfg.maxDetourAlpha * unsharedRideTime + drtCfg.maxDetourBeta);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if we should use unsharedRideTime + drtCfg.maxAbsoluteDetour in both getMaxTravelTime() and getMaxRideTime().

Imposing this constraint on maxTravelTime implies it is also imposed on maxRideTime, because maxTravelTime >= maxRideTime.

}

public Route createRoute(double departureTime, Link accessActLink, Link egressActLink, Person person,
Attributes tripAttributes, RouteFactories routeFactories) {
VrpPathWithTravelData unsharedPath = VrpPaths.calcAndCreatePath(accessActLink, egressActLink, departureTime,
router, travelTime);
double unsharedRideTime = unsharedPath.getTravelTime();//includes first & last link
double maxTravelTime = getMaxTravelTime(drtCfg, unsharedRideTime);
double maxRideDuration = getMaxRideTime(drtCfg, unsharedRideTime);
double unsharedDistance = VrpPaths.calcDistance(unsharedPath);//includes last link

DrtRoute route = routeFactories.createRoute(DrtRoute.class, accessActLink.getId(), egressActLink.getId());
route.setDistance(unsharedDistance);
route.setTravelTime(maxTravelTime);
route.setMaxRideTime(maxRideDuration);
route.setDirectRideTime(unsharedRideTime);
route.setMaxWaitTime(drtCfg.maxWaitTime);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@ public static DrtConfigGroup getSingleModeDrtConfig(Config config) {
@PositiveOrZero
public double maxAbsoluteDetour = Double.POSITIVE_INFINITY;// [s]

@Parameter
@Comment(
"Defines the maximum allowed absolute detour based on the unsharedRideTime. A linear combination similar to travel time constrain is used"
+ "This is the ratio part")
@DecimalMin("1.0")
public double maxDetourAlpha = Double.POSITIVE_INFINITY;

@Parameter
@Comment(
"Defines the maximum allowed absolute detour based on the unsharedRideTime. A linear combination similar to travel time constrain is used"
+ "This is the constant part")
@PositiveOrZero
public double maxDetourBeta = Double.POSITIVE_INFINITY;// [s]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two comments should mention that the detour is computed from the latest promised pickup time and not from the actual pickup time.

I think we should also mention the following somewhere (maybe in MaxDetourInsertionCostCalculator):
By taking into account the latest promised pickup time (instead of the actual pickup time), we simplify the handing of this constraint, because the actual pickup time remains unknown until the moment of picking up the passenger.

BTW. In a simulation setup without congestion, the actual pickup time is always less than or equal to the promised time. With congestion there is no such a guarantee.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @michalmac! I will update the descriptions and comments based on the comments.


@Parameter
@Comment(
"Defines the maximum delay allowed from the initial scheduled pick up time. Once a estimated pick up time is determined, the DRT optimizer"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rewrite the second sentence in this way?
"Once the initial pickup time is offered, he DRT optimizer must not exceed the promised time by the maxAllowedPickupDelay amount of time."

+ "should try to keep this promise. By default, this limit is disabled. If enabled, a value between 120 and 240 is a good choice.")
@PositiveOrZero
public double maxAllowedPickupDelay = Double.POSITIVE_INFINITY;// [s]

@Parameter
@Comment("If true, the max travel and wait times of a submitted request"
+ " are considered hard constraints (the request gets rejected if one of the constraints is violated)."
Expand Down Expand Up @@ -250,7 +271,7 @@ private void initSingletonParameterSets() {
addDefinition(DrtRequestInsertionRetryParams.SET_NAME, DrtRequestInsertionRetryParams::new,
() -> drtRequestInsertionRetryParams,
params -> drtRequestInsertionRetryParams = (DrtRequestInsertionRetryParams)params);

//prebooking (optional)
addDefinition(PrebookingParams.SET_NAME, PrebookingParams::new,
() -> prebookingParams,
Expand Down
Loading
Loading