Skip to content

BayesianBahn 0.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 09 Sep 21:02
· 1 commit to main since this release

Added

  • A forecast for trains whose run number is too new to have one. IRIS gives
    every run its own number and renumbers at each timetable change, so a train
    that has run for years can arrive with almost nothing behind it: over eleven
    collected days a quarter of arrivals fell through to a class-wide prior that
    knows neither the station nor the hour. Those trains still have a line, and
    the line has run all along. pipeline/build_shards.py now publishes a second
    set of shards keyed by line and station, and the app reads one when — and only
    when — the train's own history comes up short. Walk-forward over 781,000
    archive events at 62 stations, on exactly the events the prior answers today,
    the line scores 0.43 min of CRPS better (95% 0.40..0.46, resampling whole
    trains) with a shard available for 88% of them; across the December 2025
    timetable change, where the population doubles to 11% of events, the same
    0.41. Regional and S-Bahn both gain; long distance almost never carries a
    line number, so there is nothing to say about it either way. The screens say
    which line the numbers came from rather than claiming they are this train's.

Changed

  • A reported delay is now an anchor with an admitted error, not a promise.
    0.3.0 took DB's live delay and adjusted it by what the train's own past runs
    did; where DB reported nothing it used history alone. The adjustment was the
    weaker half. A report is now the centre of its own distribution, widened by
    the error such reports are measured to carry at that much notice — fitted on
    82,482 arrivals, and asymmetric, because a train that has lost time rarely
    makes it back. The connecting train's departure report is treated the same
    way, so a change is the difference of two admitted errors rather than one
    distribution against one promise. Scored against DB over the same thirteen
    days as 0.3.0: arrival CRPS −0.564 → −0.678 minutes, missed connections
    −0.147 → −0.225 Brier, and "every connection" moves from not separated from
    DB
    (+0.001) to −0.005. Over sixteen days the shipped model is −0.695 minutes
    on 296,619 arrivals and −0.242 on 14,835 missed connections.
  • A train's history and its line's are pooled, not switched between. The
    first version of the above picked one or the other. Backtesting the
    combination showed the switch to be a step function fitted to a curve: the
    train's own runs now take n / (n + 8) of the weight and its line's the rest,
    so a fresh run number answers almost entirely from its line and a settled one
    barely notices the line is there. Worth 0.105 min of CRPS against answering
    from the line alone (95% 0.094..0.116) and 0.002 against the number alone
    (95% 0.002..0.003), where no fixed weighting can have both ends —
    half-and-half wins by 0.119 where the number has nothing and loses 0.032
    across the 87% of predictions where it has plenty. End to end, 0.025 min
    against having no line at all, with 0.5% of arrivals left to the class prior
    instead of 4.8%. Above 32 effective runs of its own a train does not read the
    line shard at all, which costs 0.002 and saves a fetch on seven predictions
    in eight. Where the line contributed, the screens name it.
  • Forecasts are faster than before the line existed. The time-of-day filter
    is the only code that touches every run in a shard, and it split and parsed
    two "HH:mm" strings per run on every prediction. HistoricalRun parses its
    planned time once, when the shard is read: a forecast over the largest line
    shard in the country (8,505 runs) takes 0.17 ms instead of 0.64, and an
    ordinary number-only forecast 0.042 ms instead of 0.094.
  • The line shard is a separate lookup, not a second candidate key.
    HistoryRepository.load had always asked for a line-keyed shard when the
    number's key missed. Now that those shards exist, that would have handed a
    line's pooled runs to every caller — including the two-leg model, which pairs
    a candidate's departure and arrival by date and would have joined one train's
    departure to another train's arrival. Line-keying also loses to number-keying
    wherever the number has a history (0.13 min of CRPS, 95% 0.12..0.13), so it
    is now reached only through the fallback that measured better.

Fixed

  • A connection search froze the screen it had just put a spinner on.
    Reported from a Pixel 10 as a hang and repeated crash (#2); the attached trace
    is an ANR, with the main thread five seconds deep in app code. Every
    viewModelScope.launch named no dispatcher, which is the UI thread, and
    IrisClient left the IO dispatcher as soon as the bytes arrived — so the XML
    parse, the history model and the connection mixture all ran where the frames
    are drawn. Nothing could draw while the search ran, so the spinner stopped and
    the result appeared all at once, and on a slow enough route Android killed the
    app instead. Parsing and planning now run off the main thread; only the state
    assignments Compose reads stay on it.
  • The cancellation figure read "n/a" for every train DB had reported on.
    Anchoring on a live report returned before the train's own record was
    consulted, and the cancellation rate comes from that record — so the one
    number that says how often this train simply does not run went blank exactly
    when the app had most to say about it.
  • A change could be answered with no number at all. The probability of
    boarding underflows to exactly zero for a connection far enough out of reach,
    and the rule that always keeps the first candidate then kept it at zero
    weight — a distribution whose weights sum to nothing, which answers NaN rather
    than declining to answer.
  • The on-demand cache grew for the life of the install, and a file written
    during a kill stayed "fresh" for eighteen hours.
    Both are solved problems in
    RFC 9111, so the hand-written disk cache is gone and OkHttp does it: entries
    are evicted, a partial write is never published, and revalidating an unchanged
    shard now costs a round trip instead of the file.