Skip to content

Commit

Permalink
repeated-raptor profile-analyst updates
Browse files Browse the repository at this point in the history
truly propagate out times from every raptor call
(this fixes problems with max and avg, which were incorrect)
allow skipping every N minutes
use flat arrays for travel time propagation and accumulation
classes for saving histograms at destination vertices, and for generic 2D contiguous array access
  • Loading branch information
abyrd committed Apr 23, 2015
1 parent 76ff32d commit ffa1983
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 137 deletions.
Expand Up @@ -181,7 +181,7 @@ public TimeSurface.RangeSet route () {
// Iterate over street intersections in the vicinity of this particular transit stop.
// Shift the time range at this transit stop, merging it into that for all reachable street intersections.
TimeRange rangeAtTransitStop = times.get(stop);
TObjectIntMap<Vertex> distanceToVertex = stopTreeCache.getDistancesForStop(tstop);
TObjectIntMap<Vertex> distanceToVertex = null; // FIXME stopTreeCache.getDistancesForStop(tstop);
for (TObjectIntIterator<Vertex> iter = distanceToVertex.iterator(); iter.hasNext(); ) {
iter.advance();
Vertex vertex = iter.key();
Expand Down
@@ -0,0 +1,55 @@
package org.opentripplanner.profile;


import java.util.Arrays;

public class Contiguous2DIntArray {

final int dx, dy;
final int[] values;

public Contiguous2DIntArray (int dx, int dy) {
this.dx = dx;
this.dy = dy;
values = new int[dx * dy];
}

public void initialize (int initialValue) {
Arrays.fill(values, initialValue);
}

public void setX (int x, int value) {
Arrays.fill(values, x * dy, (x + 1) * dy, value);
}

public void setY (int y, int value) {
int index = y;
while (index < values.length) {
values[index] = value;
index += dy;
}
}

public int get (int x, int y) {
return values[x * dy + y];
}

public void set (int x, int y, int value) {
values[x * dy + y] = value;
}

public boolean setIfLess (int x, int y, int value) {
int index = x * dy + y;
if (values[index] > value) {
values[index] = value;
return true;
}
return false;
}

public void adjust (int x, int y, int amount) {
int index = x * dy + y;
values[index] += amount;
}

}
Expand Up @@ -580,7 +580,7 @@ private void makeSurfaces() {
TransitStop tstop = graph.index.stopVertexForStop.get(stop);
// Iterate over street intersections in the vicinity of this particular transit stop.
// Shift the time range at this transit stop, merging it into that for all reachable street intersections.
TObjectIntMap<Vertex> distanceToVertex = stopTreeCache.getDistancesForStop(tstop);
TObjectIntMap<Vertex> distanceToVertex = null; //FIXME stopTreeCache.getDistancesForStop(tstop);
for (TObjectIntIterator<Vertex> iter = distanceToVertex.iterator(); iter.hasNext(); ) {
iter.advance();
Vertex vertex = iter.key();
Expand Down
@@ -0,0 +1,40 @@
package org.opentripplanner.profile;

import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.opentripplanner.routing.graph.Vertex;

public class PropagatedHistogramsStore extends Contiguous2DIntArray {

int nBins;

public PropagatedHistogramsStore (int nBins) {
super(Vertex.getMaxIndex(), nBins);
this.nBins = nBins;
}

/** Record a travel time observation at a vertex. Increments the histogram bin for the given travel time. */
private void updateHistogram (int vertexIndex, int travelTimeSeconds) {
int bin = travelTimeSeconds / 60;
if (bin < nBins) {
adjust(vertexIndex, bin, 1);
}
}

/** Return the histogram for a given vertex as an array of ints. */
public TIntList getHistogram (Vertex vertex) {
int index = vertex.getIndex() * dy;
return TIntArrayList.wrap(values).subList(index, index + dy);
}

/**
* Given a list of minimum travel times to all street vertices at a single departure time,
* merge them into the per-street-vertex histograms for the whole time window.
*/
public void mergeIn (int[] timesForVertices) {
for (int v = 0; v < timesForVertices.length; v++) {
updateHistogram (v, timesForVertices[v]);
}
}

}
@@ -0,0 +1,73 @@
package org.opentripplanner.profile;

import org.opentripplanner.analyst.TimeSurface;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;

import java.util.Arrays;

/**
* Stores times propagated to the street network in one-to-many repeated raptor profile routing.
* At each raptor call, we find minimum travel times to each transit stop. Those must be propagated out to the street
* network, giving minimum travel times to each street vertex. Those results are then merged into the summary
* statistics per street vertex over the whole time window (over many RAPTOR calls).
* This class handles the storage and merging of those summary statistics.
*/
public class PropagatedTimesStore {

// Four parallel arrays has worse locality than one big 4|V|-length flat array, but merging per-raptor-call values
// into this summary statistics storage is not the slow part of the algorithm. Optimization should concentrate on
// the propagation of minimum travel times from transit stops to the street vertices.

Graph graph;
int size;
int[] mins, maxs, sums, counts;

public PropagatedTimesStore(Graph graph) {
this.graph = graph;
size = Vertex.getMaxIndex();
mins = new int[size];
maxs = new int[size];
sums = new int[size];
counts = new int[size];
Arrays.fill(mins, Integer.MAX_VALUE);
}

/**
* Merge in min travel times to all vertices from one raptor call, finding minimum-of-min, maximum-of-min, and
* average-of-min travel times.
*/
public void mergeIn(int[] news) {
for (int i = 0; i < size; i++) {
int newValue = news[i];
if (newValue == 0) {
continue;
}
if (mins[i] > newValue) {
mins[i] = newValue;
}
if (maxs[i] < newValue) {
maxs[i] = newValue;
}
sums[i] += newValue;
counts[i] += 1;
}
}

/** You need to pass in a pre-constructed rangeSet because it requires a reference to the profile router. */
public void makeSurfaces(TimeSurface.RangeSet rangeSet) {
for (Vertex vertex : graph.index.vertexForId.values()) {
int min = mins[vertex.getIndex()];
int max = maxs[vertex.getIndex()];
int sum = sums[vertex.getIndex()];
int count = counts[vertex.getIndex()];
if (count <= 0)
continue;
// Count is positive, extrema and sum must also be present
rangeSet.min.times.put(vertex, min);
rangeSet.max.times.put(vertex, max);
rangeSet.avg.times.put(vertex, sum / count);
}
}

}

0 comments on commit ffa1983

Please sign in to comment.