Skip to content

Commit

Permalink
Merge pull request #5249 from opentripplanner/pass-through_raptor-cha…
Browse files Browse the repository at this point in the history
…nges

Pass through raptor changes
  • Loading branch information
Bartosz-Kruba authored Aug 18, 2023
2 parents 1db7e45 + b68d4ae commit 5ea91e7
Show file tree
Hide file tree
Showing 91 changed files with 3,009 additions and 315 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.index.ItemVisitor;
import org.locationtech.jts.index.SpatialIndex;
import org.opentripplanner.framework.lang.IntBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -118,20 +118,19 @@ public final void query(Envelope envelope, ItemVisitor visitor) {

@Override
public final boolean remove(Envelope envelope, final Object item) {
// This iterates over the entire rectangular envelope of the edge rather than the segments making it up.
// It will be inefficient for very long edges, but creating a new remove method mirroring the more efficient
// insert logic is not trivial and would require additional testing of the spatial index.
// TODO determine why this is an atomic integer when nEntries is not. Is this intended to be threadsafe?
// Perhaps so it can be final and used inside a lambda function?
final AtomicInteger removedCount = new AtomicInteger();
// This iterates over the entire rectangular envelope of the edge rather than the segments
// making it up. It will be inefficient for very long edges, but creating a new remove method
// mirroring the more efficient insert logic is not trivial and would require additional
// testing of the spatial index.
final IntBox removedCount = new IntBox(0);
visit(
envelope,
false,
(bin, mapKey) -> {
boolean removed = bin.remove(item);
if (removed) {
nEntries--;
removedCount.addAndGet(1);
removedCount.inc();
}
return removed;
}
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/org/opentripplanner/framework/lang/IntBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.opentripplanner.framework.lang;

/**
* An IntBox is a writable container for an int. The most common use-case for this class is to
* be able to set an integer value inside a lambda callback where local variables is not
* accessible.
*/
public final class IntBox {

private int value;

public IntBox(int value) {
this.value = value;
}

public int get() {
return value;
}

public void set(int value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || o.getClass() != IntBox.class) {
return false;
}
return value == ((IntBox) o).value;
}

@Override
public int hashCode() {
return Integer.hashCode(value);
}

@Override
public String toString() {
return Integer.toString(value);
}

public void inc() {
++value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public interface RaptorPath<T extends RaptorTripSchedule> extends Comparable<Rap
*/
int c1();

int c2();

/**
* The first leg/path of this journey - which is linked to the next and so on. The leg can contain
* sub-legs, for example: walk-flex-walk.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,23 @@ public class MultiCriteriaRequest<T extends RaptorTripSchedule> {
@Nullable
private final RaptorTransitPriorityGroupCalculator transitPriorityCalculator;

@Nullable
private final PassThroughPoints passThroughPoints;

@Nullable
private final Double relaxCostAtDestination;

private MultiCriteriaRequest() {
this.relaxC1 = RelaxFunction.NORMAL;
this.transitPriorityCalculator = null;
this.passThroughPoints = null;
this.relaxCostAtDestination = null;
}

public MultiCriteriaRequest(Builder<T> builder) {
this.relaxC1 = Objects.requireNonNull(builder.relaxC1());
this.transitPriorityCalculator = builder.transitPriorityCalculator();
this.passThroughPoints = builder.passThroughPoints();
this.relaxCostAtDestination = builder.relaxCostAtDestination();
}

Expand Down Expand Up @@ -71,6 +76,10 @@ public Optional<RaptorTransitPriorityGroupCalculator> transitPriorityCalculator(
return Optional.ofNullable(transitPriorityCalculator);
}

public Optional<PassThroughPoints> passThroughPoints() {
return Optional.ofNullable(passThroughPoints);
}

/**
* Whether to accept non-optimal trips if they are close enough - if and only if they represent an
* optimal path for their given iteration. In other words this slack only relaxes the pareto
Expand Down Expand Up @@ -101,13 +110,19 @@ public boolean equals(Object o) {
return (
Objects.equals(relaxC1, that.relaxC1) &&
Objects.equals(transitPriorityCalculator, that.transitPriorityCalculator) &&
Objects.equals(passThroughPoints, that.passThroughPoints) &&
Objects.equals(relaxCostAtDestination, that.relaxCostAtDestination)
);
}

@Override
public int hashCode() {
return Objects.hash(relaxC1, transitPriorityCalculator, relaxCostAtDestination);
return Objects.hash(
relaxC1,
transitPriorityCalculator,
passThroughPoints,
relaxCostAtDestination
);
}

@Override
Expand All @@ -116,15 +131,21 @@ public String toString() {
.of(MultiCriteriaRequest.class)
.addObj("relaxC1", relaxC1, RelaxFunction.NORMAL)
.addObj("transitPriorityCalculator", transitPriorityCalculator)
.addObj("passThroughPoints", passThroughPoints)
.addNum("relaxCostAtDestination", relaxCostAtDestination)
.toString();
}

public boolean includeC2() {
return passThroughPoints != null || transitPriorityCalculator != null;
}

public static class Builder<T extends RaptorTripSchedule> {

private final MultiCriteriaRequest<T> original;
private RelaxFunction relaxC1;
private RaptorTransitPriorityGroupCalculator transitPriorityCalculator = null;
private PassThroughPoints passThroughPoints = null;
private Double relaxCostAtDestination = null;

public Builder(MultiCriteriaRequest<T> original) {
Expand Down Expand Up @@ -152,6 +173,17 @@ public Builder<T> withTransitPriorityCalculator(RaptorTransitPriorityGroupCalcul
return this;
}

@Nullable
public PassThroughPoints passThroughPoints() {
return passThroughPoints;
}

@Nullable
public Builder<T> withPassThroughPoints(PassThroughPoints value) {
passThroughPoints = value;
return this;
}

@Nullable
@Deprecated
public Double relaxCostAtDestination() {
Expand All @@ -175,6 +207,7 @@ public String toString() {
.of(MultiCriteriaRequest.Builder.class)
.addObj("relaxC1", relaxC1)
.addObj("transitPriorityCalculator", transitPriorityCalculator)
.addObj("passThroughPoints", passThroughPoints)
.addNum("relaxCostAtDestination", relaxCostAtDestination)
.toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.opentripplanner.raptor.api.request;

import java.util.Arrays;
import java.util.BitSet;
import java.util.Objects;
import java.util.stream.IntStream;

/**
* A collection of stop indexes used to define a pass through-point.
*/
public class PassThroughPoint {

private final int[] stops;

public PassThroughPoint(int[] stops) {
Objects.requireNonNull(stops);
if (stops.length == 0) {
throw new IllegalArgumentException("At least one stop is required");
}
this.stops = Arrays.copyOf(stops, stops.length);
}

/**
* This is a convenient accessor method used inside Raptor. It converts the list stops to a
* bit-set. Add other access methods if needed.
*/
public BitSet asBitSet() {
return IntStream.of(stops).collect(BitSet::new, BitSet::set, BitSet::or);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PassThroughPoint that = (PassThroughPoint) o;
return Arrays.equals(stops, that.stops);
}

@Override
public int hashCode() {
return Arrays.hashCode(stops);
}

@Override
public String toString() {
return "(stops: " + Arrays.toString(stops) + ")";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.opentripplanner.raptor.api.request;

import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import org.opentripplanner.framework.tostring.ToStringBuilder;

/**
* An ordered set of pass through points.
*/
public class PassThroughPoints {

private final List<PassThroughPoint> points;

public PassThroughPoints(List<PassThroughPoint> points) {
this.points = List.copyOf(Objects.requireNonNull(points));
}

public Stream<PassThroughPoint> stream() {
return points.stream();
}

public boolean isEmpty() {
return points.isEmpty();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PassThroughPoints that = (PassThroughPoints) o;
return Objects.equals(points, that.points);
}

@Override
public int hashCode() {
return Objects.hash(points);
}

@Override
public String toString() {
return ToStringBuilder.of(PassThroughPoints.class).addCol("points", points).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ default boolean isFirstRound() {
*/
int c1();

/**
* Whether the model supports accumulated criteria TWO. If C2 is not supported then calling
* {@link #c2()} will result in an exception.
*/
default boolean supportsC2() {
return false;
}

/**
* The accumulated criteria TWO. Can be used for any int criteria used during routing. A
* state with c1 and c2 is created dynamically if c2 is in use, if not this method will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;

/**
* TODO C2 AddJavaDoc
*
* @param <T>
* A pattern ride provide read-only access to a
* {@link org.opentripplanner.raptor.rangeraptor.multicriteria.ride.PatternRide}.
* <p>
* @param <T> The TripSchedule type defined by the user of the raptor API.
*/
public interface PatternRideView<T extends RaptorTripSchedule, A extends ArrivalView<T>> {
A prevArrival();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.IntPredicate;
import javax.annotation.Nullable;
import org.opentripplanner.framework.concurrent.OtpRequestThreadFactory;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
Expand All @@ -10,6 +11,7 @@
import org.opentripplanner.raptor.rangeraptor.DefaultRangeRaptorWorker;
import org.opentripplanner.raptor.rangeraptor.context.SearchContext;
import org.opentripplanner.raptor.rangeraptor.internalapi.Heuristics;
import org.opentripplanner.raptor.rangeraptor.internalapi.PassThroughPointsService;
import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorWorker;
import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorWorkerResult;
import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorWorkerState;
Expand All @@ -32,6 +34,9 @@ public class RaptorConfig<T extends RaptorTripSchedule> {
private final ExecutorService threadPool;
private final RaptorTuningParameters tuningParameters;

/** The service is not final, because it depends on the request. */
private PassThroughPointsService passThroughPointsService = null;

public RaptorConfig(RaptorTuningParameters tuningParameters) {
this.tuningParameters = tuningParameters;
this.threadPool = createNewThreadPool(tuningParameters.searchThreadPoolSize());
Expand All @@ -42,7 +47,9 @@ public static <T extends RaptorTripSchedule> RaptorConfig<T> defaultConfigForTes
}

public SearchContext<T> context(RaptorTransitDataProvider<T> transit, RaptorRequest<T> request) {
return new SearchContext<>(request, tuningParameters, transit);
// The passThroughPointsService is needed to create the context, so we initialize it here.
this.passThroughPointsService = createPassThroughPointsService(request);
return new SearchContext<>(request, tuningParameters, transit, acceptC2AtDestination());
}

public RaptorWorker<T> createStdWorker(
Expand All @@ -60,7 +67,7 @@ public RaptorWorker<T> createMcWorker(
Heuristics heuristics
) {
final SearchContext<T> context = context(transitData, request);
return new McRangeRaptorConfig<>(context)
return new McRangeRaptorConfig<>(context, passThroughPointsService)
.createWorker(
heuristics,
(state, routingStrategy) -> createWorker(context, state, routingStrategy)
Expand Down Expand Up @@ -105,6 +112,10 @@ public RaptorSearchWindowCalculator searchWindowCalculator() {

/* private factory methods */

private static PassThroughPointsService createPassThroughPointsService(RaptorRequest<?> request) {
return McRangeRaptorConfig.passThroughPointsService(request.multiCriteria());
}

private RaptorWorker<T> createWorker(
SearchContext<T> ctx,
RaptorWorkerState<T> workerState,
Expand All @@ -124,6 +135,12 @@ private RaptorWorker<T> createWorker(
);
}

private IntPredicate acceptC2AtDestination() {
return passThroughPointsService.isNoop()
? null
: passThroughPointsService.acceptC2AtDestination();
}

@Nullable
private ExecutorService createNewThreadPool(int size) {
return size > 0
Expand Down
Loading

0 comments on commit 5ea91e7

Please sign in to comment.