@@ -0,0 +1,3579 @@
+package com.conveyal.r5.analyst.network;%0A%0Aimport com.conveyal.r5.OneOriginResult;%0Aimport com.conveyal.r5.analyst.TravelTimeComputer;%0Aimport com.conveyal.r5.analyst.cluster.AnalysisWorkerTask;%0Aimport com.conveyal.r5.transit.TransportNetwork;%0Aimport org.junit.jupiter.api.Test;%0A%0Aimport static com.conveyal.r5.analyst.network.SimpsonDesertTests.SIMPSON_DESERT_CORNER;%0A%0Apublic class RandomFrequencyPhasingTests %7B%0A%0A    /**%0A     * This recreates the problem in issue #740, inspired by the structure of STM Montr%C3%A9al GTFS m%C3%A9tro schedules.%0A     * This involves a pattern with two different frequency trips on two different services. The first service has%0A     * N freq entries, and the second has more than N entries.%0A     *%0A     * Here we make one weekday trip with a single frequency entry, and one weekend trip with two frequency entries.%0A     * We search on the weekend, causing the weekday trip to be filtered out. The Monte Carlo boarding code expects%0A     * an array of two offsets for the weekend trip, but sees a single offset intended for the weekday trip. In addition%0A     * the offsets generated for the weekday service will be in the range (0...30) while the headway of the weekend%0A     * service is much shorter - many generated offsets will exceed the headway. So at least two different assertions%0A     * can fail here, but whether they will actually fail depends on the order of the TripSchedules in the TripPattern%0A     * (the weekend trip must come after the weekday one, so filtering causes a decrease in trip index). This depends%0A     * in turn on the iteration order of the GtfsFeed.trips map, which follows the natural order of its keys so should%0A     * be determined by the lexical order of trip IDs, which are determined by the order in which timetables are added%0A     * to the GridRoute.%0A     *%0A     * The problem exists even if the mismatched trips have the same number of entries but can fail silently as offsets%0A     * are computed for the wrong headways. The output travel time range will even be correct, but the distribution will%0A     * be skewed by the offsets being randomly selected from a different headway, while the headway itself is accurate.%0A     *%0A     * The structure created here can only be created by input GTFS, and cannot be produced by our Conveyal scenarios.%0A     * In scenarios, we always produce exactly one frequency entry per trip. This does not mean scenarios are immune%0A     * to the problem of using offsets from the wrong trip - it's just much harder to detect the problem as all arrays%0A     * are the same length, so it can fail silently.%0A     */%0A    @Test%0A    public void testFilteredTripRandomization () throws Exception %7B%0A%0A        GridLayout gridLayout = new GridLayout(SIMPSON_DESERT_CORNER, 40);%0A        // TODO DSL revision:%0A        // gridLayout.newRoute(/*construct and add to routes*/).horizontal(20).addTimetable()...%0A        gridLayout.routes.add(GridRoute%0A                .newHorizontalRoute(gridLayout, 20, 30)%0A                .addTimetable(GridRoute.Services.WEEKEND, 6, 10, 4)%0A                .addTimetable(GridRoute.Services.WEEKEND, 10, 22, 8)%0A                .pureFrequency()%0A        );%0A        TransportNetwork network = gridLayout.generateNetwork();%0A        AnalysisWorkerTask task = gridLayout.newTaskBuilder()%0A                .weekendMorningPeak()%0A                .setOrigin(20, 20)%0A                .monteCarloDraws(1000)%0A                .build();%0A%0A        TravelTimeComputer computer = new TravelTimeComputer(task, network);%0A        OneOriginResult oneOriginResult = computer.computeTravelTimes();%0A%0A    %7D%0A%0A%7D%0A

add test for mismatched frequency offsets #740