hawkes-engine 0.2.0

Released 2026-10-24. Pre-alpha research release.

0.1.0 measured its own weakness and stopped there. It shipped a map that says, for a given regime, how much the reported standard error understates the sampling spread – and left the user to read the map and decide what to do. 0.2.0 answers that in code: fit(..., se_method="auto") consults the map, and where the map measured a shortfall it reports a different interval rather than the one it knows to be too narrow. This release also turns the paper into a submission.

The API may change between 0.x minor versions, and this one adds an argument and three result fields. See ADR-009.

What changed in behaviour

This is the part to read before upgrading.

  • se_method="auto" is new; the default is unchanged. "hessian" is still the default and still behaves exactly as in 0.1.0. "auto" is opt-in, and it may choose the bootstrap, which costs time.

  • A fit with se_method="auto" can take minutes. The bootstrap refits the model 200 times. Before it runs, the projected cost is compared with auto_max_seconds (default 120). The projection is a power law fitted to the four measured serial configurations of the capability table, times the measured efficiency of the process pool; it reproduces every measured row to within three percent. Above the limit, auto keeps the Hessian interval, says in se_note what was indicated and what it would have cost, and emits a RuntimeWarning. It does not silently spend the time.

  • FitResult gains se_source, se_note and auto. se_source is "hessian", "bootstrap" or "none", and it is the field to read before reading se: a result can carry se is None with se_source == "none", which is a statement, not a failure. The serialisation schema is 2; schema-1 documents still load, with se_source reading as "hessian" and no record, because a 0.1.0 document cannot know what it did not record.

  • n_bootstrap below 10 is refused for "bootstrap" and "auto". The bootstrap never reports an interval below ten usable replications; failing at the call is clearer than a fit that runs and returns nothing.

  • fit accepts bootstrap_workers. A study that parallelises over fits should set it to 1; leaving it at None starts one pool per fit.

What auto does when it does not know

The policy is written down in docs/se_policy.md before the feature, and the four cases are:

The map says

auto does

se_source

a block’s 2-SE interval around the measured q excludes 1

bootstrap, if the projection fits in auto_max_seconds

"bootstrap"

the regime is outside the measured box

Hessian, labelled uncalibrated

"hessian"

the estimator is undefined in this regime

no interval at all

"none"

q is indistinguishable from 1, or the bootstrap was refused by the limit

Hessian

"hessian"

The threshold is twice the Monte Carlo standard error of the measured q itself, which is the precision the study’s 2000 replications support. A consequence worth stating: at that threshold every cell in the shipped map fires, so auto chooses the bootstrap wherever the map has measured, and the Hessian only where the map is silent or the projection exceeds the limit. That is a measured property of the estimator, not an artefact of the rule, and the Limits page says so.

The interval study

The switch is tested, not asserted. A second pre-registration was frozen before its harness existed – a fresh document rather than an amendment to H6, because H6 measured no alternative interval and the hypothesis this study tests is not in that document. Three cells at 2000 events, 2000 replications each, both arms fitted to the same simulated event stream so the comparison is paired.

The result, whatever it is, is in studies/results/auto/analysis/report.txt in the repository, with the hypotheses labelled there. A null result is reported as a null result.

The result: the switch works, and it does not overshoot. All three pre-registered hypotheses were confirmed.

rho

coverage, Hessian

coverage, auto

difference from nominal

0.3028

0.9207 +/- 0.0030

0.9609 +/- 0.0017

-0.0229 +/- 0.0023

0.85

0.9378 +/- 0.0020

0.9627 +/- 0.0015

-0.0124 +/- 0.0010

0.95

0.8937 +/- 0.0037

0.9488 +/- 0.0025

-0.0326 +/- 0.0025

H-A1 (the switch lands closer to nominal) is confirmed at all three cells, on the decay block and on the pooled block. H-A2 (it never exceeds 1.05 of nominal) is confirmed: the highest coverage anywhere is 0.9762 on the decay block at rho = 0.95, against a ceiling of 0.9975. H-A3 (the gain is larger at high branching ratio) is confirmed and exploratory, because the map’s own pattern suggested the direction.

Two things the release reports rather than rounds off. The worst Hessian cell is the baseline block at rho = 0.95, at 0.7817, which is the failure the map predicted there. And at that cell 274 of 2000 replications had a fitted branching ratio above 0.95, so auto recorded them as outside the map and kept the Hessian interval; the 0.9488 above is measured over all 2000, so those replications pull it down rather than being excluded.

A third detail, from the same run: in 15 of the 2000 replications at rho = 0.3028 the nearest measured cell was in a different branching-ratio column, because the fitted parameters had drifted; the median distance to the nearest cell was 0.206 grid steps and the largest 1.316. In one of those a fit with a fitted rho near 0.45 landed nearest a shallow-decay cell the map marks undefined, and auto returned no interval. That is the lookup doing what trust.md says it does — it answers from the nearest cell however far that cell is — and it is a limitation of a nearest-neighbour map rather than a fault in the switch.

The paper

paper/main.tex builds to a PDF from one command, and every number and every table in it is emitted by studies/make_paper_tex.py from a committed output; nothing is typed by hand. All seven entries in paper/refs.bib have been checked against the publisher record or the arXiv listing. paper/SUBMISSION.md is the arXiv checklist. Nothing has been submitted to arXiv.

The paper is deposited on HAL, and the deposit is in moderation: HAL assigns a hal- identifier at deposit but does not serve the record publicly until a moderator accepts it. The reserved identifier is hal-05838093, and the record becomes readable at https://hal.science/hal-05838093 when moderation clears.

Until then no public HAL link exists, and this paragraph should not name one. HAL is CNRS-run and needs no endorsement, unlike a first arXiv submission; that is why it was chosen as the route that does not depend on a stranger. The arXiv listing is still pending an endorsement for stat.ME; when either becomes public, this paragraph will say so.

Before 0.2.0 the paper had never been typeset: no session that prepared it had a TeX installation, and the structure checks cannot see a failing compile. The first real one failed, on twenty macros whose names contained digits. A Paper workflow now typesets it on every change and uploads the PDF, and the release notes say so because “builds to a PDF from one command” was, until then, a claim nobody had run.

What this release does not fix

Stated as plainly as 0.1.0 stated its limits.

  • auto does not fix bias. The point estimate is unchanged. Only the width of an interval it reports can change, and only where the map measured a reason to change it.

  • auto does not make the undefined region estimable. Where the window holds too few decay times for the decay to be identified, auto refuses to report an interval. It does not find one.

  • auto does not cover shared_decay=True, which is a different model with M decay rates and is outside the measured map. It says so and uses the Hessian.

  • auto does not extrapolate. Outside the measured box it uses the Hessian and labels the interval uncalibrated.

  • If the bootstrap fails to produce a usable refit often enough, the result is se_source="none" with a note, not a wider interval.

  • Large M at small N remains slow and unreliable, and the calibration map still covers two types and one decay form.

Install

python -m pip install hawkes-engine==0.2.0

Supported platforms and Python versions are unchanged from 0.1.0.

The DOI

This release is archived at DOI 10.5281/zenodo.23620653.

Note that this begins a second Zenodo record. 0.1.0 was deposited by hand as 10.5281/zenodo.23613789, with concept DOI 10.5281/zenodo.23613788; 0.2.0 was deposited by the GitHub integration as 10.5281/zenodo.23620653, with concept DOI 10.5281/zenodo.23620652. The two are separate series, so neither concept DOI resolves to the other’s versions. Cite the version DOI above for this release, and the record’s own concept DOI if you need “all versions of 0.2.0”.

That split is a defect in how the archive is published rather than a choice, and later releases should be deposited into the 23620652 series so that one concept DOI covers everything from 0.2.0 onward. See docs/RELEASING.md.