Skip to content

Commit

Permalink
Merge pull request #5342 from opentripplanner/pass-through-optimize-t…
Browse files Browse the repository at this point in the history
…ransfers-service

Pass through optimize transfers service
  • Loading branch information
Bartosz-Kruba authored Oct 19, 2023
2 parents c44b66c + d61d10f commit eb2d679
Show file tree
Hide file tree
Showing 32 changed files with 1,552 additions and 409 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.Objects;
import java.util.function.IntFunction;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import org.opentripplanner.framework.lang.IntUtils;
import org.opentripplanner.framework.lang.StringUtils;

/**
Expand Down Expand Up @@ -55,8 +55,28 @@ public int hashCode() {
*/
@Override
public String toString() {
return (
"(" + (name == null ? "" : name + ", ") + "stops: " + IntUtils.intArrayToString(stops) + ")"
);
return toString(Integer::toString);
}

public String toString(IntFunction<String> nameResolver) {
StringBuilder buf = new StringBuilder("(");
if (name != null) {
buf.append(name).append(", ");
}
buf.append("stops: ");
appendStops(buf, ", ", nameResolver);
return buf.append(")").toString();
}

public void appendStops(StringBuilder buf, String sep, IntFunction<String> nameResolver) {
boolean skipFirst = true;
for (int stop : stops) {
if (skipFirst) {
skipFirst = false;
} else {
buf.append(sep);
}
buf.append(nameResolver.apply(stop));
}
}
}
21 changes: 13 additions & 8 deletions src/main/java/org/opentripplanner/raptor/path/PathBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.opentripplanner.raptor.api.model.RaptorAccessEgress;
import org.opentripplanner.raptor.api.model.RaptorConstants;
import org.opentripplanner.raptor.api.model.RaptorConstrainedTransfer;
import org.opentripplanner.raptor.api.model.RaptorTransfer;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
Expand All @@ -18,19 +19,20 @@

/**
* The path builder is a utility to build paths. The path builder is responsible for reconstructing
* information that was used in decision making inside Raptor, but not kept due to performance
* information that was used in decision-making inside Raptor, but not kept due to performance
* reasons. For example information about the transfer like transfer constraints.
* <p>
* The path builder enforces the same logic as Raptor and generates information like the
* generalized-cost instead of getting it from the stop-arrivals. This is convenient if a path is
* created OUTSIDE Raptor, which is the case in the {@link org.opentripplanner.routing.algorithm.transferoptimization.OptimizeTransferService}.
* created OUTSIDE Raptor, which is the case in the {@link
* org.opentripplanner.routing.algorithm.transferoptimization.OptimizeTransferService}.
* <p>
* The path builder comes in two versions. One which adds new legs to the tail of the path, allowing
* us to add legs starting with the access leg and ending with the egress leg. The other adds legs
* in the opposite order, from egress to access. Hence the forward and reverse mappers are
* in the opposite order, from egress to access. Hence, the forward and reverse mappers are
* simplified using the head and tail builder respectively. See {@link #headPathBuilder(
* RaptorSlackProvider, RaptorCostCalculator, RaptorStopNameResolver,
* RaptorPathConstrainedTransferSearch)} and {@link #tailPathBuilder(RaptorSlackProvider,
* RaptorSlackProvider, int, RaptorCostCalculator, RaptorStopNameResolver,
* RaptorPathConstrainedTransferSearch)} and {@link #tailPathBuilder(RaptorSlackProvider, int,
* RaptorCostCalculator, RaptorStopNameResolver, RaptorPathConstrainedTransferSearch)}.
* <p>
* The builder is also used for creating test data in unit test.
Expand All @@ -53,8 +55,7 @@ public abstract class PathBuilder<T extends RaptorTripSchedule> {
@Nullable
private final RaptorPathConstrainedTransferSearch<T> transferConstraintsSearch;

@Nullable
private int c2;
private int c2 = RaptorConstants.NOT_SET;

// Path leg elements as a double linked list. This makes it easy to look at
// legs before and after in the logic and easy to fork, building alternative
Expand Down Expand Up @@ -177,10 +178,14 @@ public void c2(int c2) {
this.c2 = c2;
}

public int c2() {
return tail.isC2Set() ? tail.c2() : c2;
}

public RaptorPath<T> build() {
updateAggregatedFields();
var pathLegs = createPathLegs(costCalculator, slackProvider);
return new Path<>(iterationDepartureTime, pathLegs, pathLegs.generalizedCostTotal(), c2);
return new Path<>(iterationDepartureTime, pathLegs, pathLegs.generalizedCostTotal(), c2());
}

@Override
Expand Down
30 changes: 21 additions & 9 deletions src/main/java/org/opentripplanner/raptor/path/PathBuilderLeg.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import org.opentripplanner.raptor.spi.RaptorSlackProvider;

/**
* This is the leg implementation for the {@link PathBuilder}. It is a private inner class which
* helps to cache and calculate values before constructing a path.
* This is the leg implementation for the {@link PathBuilder}. It helps to cache and calculate
* values before constructing a path.
*/
public class PathBuilderLeg<T extends RaptorTripSchedule> {

Expand All @@ -40,6 +40,7 @@ public class PathBuilderLeg<T extends RaptorTripSchedule> {

private int fromTime = NOT_SET;
private int toTime = NOT_SET;
private int c2 = RaptorConstants.NOT_SET;

private PathBuilderLeg<T> prev = null;
private PathBuilderLeg<T> next = null;
Expand All @@ -54,6 +55,7 @@ private PathBuilderLeg(PathBuilderLeg<T> other) {
this.fromTime = other.fromTime;
this.toTime = other.toTime;
this.leg = other.leg;
this.c2 = other.c2;

// Mutable fields
if (other.next != null) {
Expand All @@ -72,7 +74,7 @@ private PathBuilderLeg(MyLeg leg) {
}
}

/* factory methods */
/* accessors */

public int fromTime() {
return fromTime;
Expand All @@ -94,8 +96,6 @@ public int toStop() {
return leg.toStop();
}

/* accessors */

public int toStopPos() {
return asTransitLeg().toStopPos();
}
Expand All @@ -104,6 +104,18 @@ public int durationInSec() {
return toTime - fromTime;
}

public int c2() {
return c2;
}

public void c2(int c2) {
this.c2 = c2;
}

public boolean isC2Set() {
return c2 != RaptorConstants.NOT_SET;
}

@Nullable
public RaptorConstrainedTransfer constrainedTransferAfterLeg() {
return isTransit() ? asTransitLeg().constrainedTransferAfterLeg : null;
Expand Down Expand Up @@ -146,6 +158,10 @@ public T trip() {
return asTransitLeg().trip;
}

public PathBuilderLeg<T> prev() {
return prev;
}

public PathBuilderLeg<T> next() {
return next;
}
Expand Down Expand Up @@ -294,10 +310,6 @@ static <T extends RaptorTripSchedule> PathBuilderLeg<T> egress(RaptorAccessEgres
return new PathBuilderLeg<>(new MyEgressLeg(egress));
}

PathBuilderLeg<T> prev() {
return prev;
}

void setPrev(PathBuilderLeg<T> prev) {
this.prev = prev;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,16 @@ private TransitRouterResult route() {
Collection<RaptorPath<TripSchedule>> paths = transitResponse.paths();

if (OTPFeature.OptimizeTransfers.isOn() && !transitResponse.containsUnknownPaths()) {
paths =
TransferOptimizationServiceConfigurator
.createOptimizeTransferService(
transitLayer::getStopByIndex,
requestTransitDataProvider.stopNameResolver(),
serverContext.transitService().getTransferService(),
requestTransitDataProvider,
transitLayer.getStopBoardAlightCosts(),
request.preferences().transfer().optimization()
)
.optimize(transitResponse.paths());
var service = TransferOptimizationServiceConfigurator.createOptimizeTransferService(
transitLayer::getStopByIndex,
requestTransitDataProvider.stopNameResolver(),
serverContext.transitService().getTransferService(),
requestTransitDataProvider,
transitLayer.getStopBoardAlightCosts(),
request.preferences().transfer().optimization(),
raptorRequest.multiCriteria()
);
paths = service.optimize(transitResponse.paths());
}

// Create itineraries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
import org.opentripplanner.model.transfer.TransferService;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.path.RaptorStopNameResolver;
import org.opentripplanner.raptor.api.request.MultiCriteriaRequest;
import org.opentripplanner.raptor.spi.RaptorCostCalculator;
import org.opentripplanner.raptor.spi.RaptorTransitDataProvider;
import org.opentripplanner.routing.algorithm.transferoptimization.OptimizeTransferService;
import org.opentripplanner.routing.algorithm.transferoptimization.api.TransferOptimizationParameters;
import org.opentripplanner.routing.algorithm.transferoptimization.model.MinCostFilterChain;
import org.opentripplanner.routing.algorithm.transferoptimization.model.MinSafeTransferTimeCalculator;
import org.opentripplanner.routing.algorithm.transferoptimization.model.OptimizedPathTail;
import org.opentripplanner.routing.algorithm.transferoptimization.model.PathTailFilter;
import org.opentripplanner.routing.algorithm.transferoptimization.model.TransferWaitTimeCostCalculator;
import org.opentripplanner.routing.algorithm.transferoptimization.model.costfilter.MinCostPathTailFilterFactory;
import org.opentripplanner.routing.algorithm.transferoptimization.model.passthrough.PassThroughPathTailFilter;
import org.opentripplanner.routing.algorithm.transferoptimization.services.OptimizePathDomainService;
import org.opentripplanner.routing.algorithm.transferoptimization.services.TransferGenerator;
import org.opentripplanner.routing.algorithm.transferoptimization.services.TransferOptimizedFilterFactory;
import org.opentripplanner.routing.algorithm.transferoptimization.services.TransferServiceAdaptor;
import org.opentripplanner.transit.model.site.StopLocation;

Expand All @@ -29,21 +30,24 @@ public class TransferOptimizationServiceConfigurator<T extends RaptorTripSchedul
private final RaptorTransitDataProvider<T> transitDataProvider;
private final int[] stopBoardAlightCosts;
private final TransferOptimizationParameters config;
private final MultiCriteriaRequest<T> multiCriteriaRequest;

private TransferOptimizationServiceConfigurator(
IntFunction<StopLocation> stopLookup,
RaptorStopNameResolver stopNameResolver,
TransferService transferService,
RaptorTransitDataProvider<T> transitDataProvider,
int[] stopBoardAlightCosts,
TransferOptimizationParameters config
TransferOptimizationParameters config,
MultiCriteriaRequest<T> multiCriteriaRequest
) {
this.stopLookup = stopLookup;
this.stopNameResolver = stopNameResolver;
this.transferService = transferService;
this.transitDataProvider = transitDataProvider;
this.stopBoardAlightCosts = stopBoardAlightCosts;
this.config = config;
this.multiCriteriaRequest = multiCriteriaRequest;
}

/**
Expand All @@ -57,32 +61,29 @@ > OptimizeTransferService<T> createOptimizeTransferService(
TransferService transferService,
RaptorTransitDataProvider<T> transitDataProvider,
int[] stopBoardAlightCosts,
TransferOptimizationParameters config
TransferOptimizationParameters config,
MultiCriteriaRequest<T> multiCriteriaRequest
) {
return new TransferOptimizationServiceConfigurator<T>(
stopLookup,
stopNameResolver,
transferService,
transitDataProvider,
stopBoardAlightCosts,
config
config,
multiCriteriaRequest
)
.createOptimizeTransferService();
}

private OptimizeTransferService<T> createOptimizeTransferService() {
var pathTransferGenerator = createTransferGenerator(config.optimizeTransferPriority());
var filter = createTransferOptimizedFilter(
config.optimizeTransferPriority(),
config.optimizeTransferWaitTime()
);

if (config.optimizeTransferWaitTime()) {
var transferWaitTimeCalculator = createTransferWaitTimeCalculator();

var transfersPermutationService = createOptimizePathService(
pathTransferGenerator,
filter,
transferWaitTimeCalculator,
transitDataProvider.multiCriteriaCostCalculator()
);
Expand All @@ -95,7 +96,6 @@ private OptimizeTransferService<T> createOptimizeTransferService() {
} else {
var transfersPermutationService = createOptimizePathService(
pathTransferGenerator,
filter,
null,
transitDataProvider.multiCriteriaCostCalculator()
);
Expand All @@ -105,7 +105,6 @@ private OptimizeTransferService<T> createOptimizeTransferService() {

private OptimizePathDomainService<T> createOptimizePathService(
TransferGenerator<T> transferGenerator,
MinCostFilterChain<OptimizedPathTail<T>> transferPointFilter,
TransferWaitTimeCostCalculator transferWaitTimeCostCalculator,
RaptorCostCalculator<T> costCalculator
) {
Expand All @@ -116,7 +115,7 @@ private OptimizePathDomainService<T> createOptimizePathService(
transferWaitTimeCostCalculator,
stopBoardAlightCosts,
config.extraStopBoardAlightCostsFactor(),
transferPointFilter,
createFilter(),
stopNameResolver
);
}
Expand All @@ -140,10 +139,16 @@ private TransferWaitTimeCostCalculator createTransferWaitTimeCalculator() {
);
}

private MinCostFilterChain<OptimizedPathTail<T>> createTransferOptimizedFilter(
boolean transferPriority,
boolean optimizeWaitTime
) {
return TransferOptimizedFilterFactory.filter(transferPriority, optimizeWaitTime);
private PathTailFilter<T> createFilter() {
var filter = new MinCostPathTailFilterFactory<T>(
config.optimizeTransferPriority(),
config.optimizeTransferWaitTime()
)
.createFilter();

if (multiCriteriaRequest.hasPassThroughPoints()) {
filter = new PassThroughPathTailFilter<>(filter, multiCriteriaRequest.passThroughPoints());
}
return filter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.opentripplanner.routing.algorithm.transferoptimization.model;

import java.util.Set;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;

/**
* Filter path tails for a given stopPosition during the optimization process. The algorithm only
* feeds in paths which can be compared. If the head is alighted at a position which gives it an
* advantage over another path (accept boarding at more stops), then the paths are split into
* different sets and the filter is called for each set.
*/
public interface PathTailFilter<T extends RaptorTripSchedule> {
/**
* Filter path while building the paths. The {@code head} of each path is guaranteed to be a
* transit leg, and the {@code boardStopPosition} is guaranteed to be the last position in the
* head leg which will be used for boarding. The {@code boardStopPosition} should be used when
* calculating the property which is used for comparison. If the comparison can be done without
* looking at a stop-position, then this can be ignored.
*/
Set<OptimizedPathTail<T>> filterIntermediateResult(
Set<OptimizedPathTail<T>> elements,
int boardStopPosition
);

/**
* Filter the paths one last time. The {@code head} is not guaranteed to be the access-leg. This
* can be used to insert values into the path or checking if all requirements are meet.
*
*/
Set<OptimizedPathTail<T>> filterFinalResult(Set<OptimizedPathTail<T>> elements);
}
Loading

0 comments on commit eb2d679

Please sign in to comment.