tsdive.eval.martingale_alarm
¶
martingale_alarm(
log_values: Iterable[float], delta: float
) -> int | None
First index where the log martingale reaches log(1 / delta), else None.
Ville's inequality: under exchangeability of the calibration and
stream scores, the probability that the martingale ever reaches
1 / delta is at most delta, so the bound covers the whole
record and not each window separately. The bound rests on that
exchangeability; any fitted centre and scale that produce the scores
must come from a fit set disjoint from the calibration and stream.
delta must lie in (0, 1), else ValueError.
Examples:
>>> from tsdive.eval import martingale_alarm
>>> martingale_alarm([0.0, 1.5, 3.2], delta=0.05) # log(1 / 0.05) is 2.996
2
>>> martingale_alarm([0.0, 1.5], delta=0.05) is None
True