# The `se_method="auto"` policy This page is written before the feature. It states what `auto` will do, why each rule is the one the shipped map supports, and what the switch costs. The code in `python/hawkes_engine/se_policy.py` implements this page; `docs/preregistration_auto.md` freezes the study that tests whether the switch helps. `auto` adds no mathematics to the estimator. It is a decision layer above the fit: the likelihood, the gradient, the Hessian and the bootstrap are unchanged. ## The question `auto` answers The H6 study measured the inverse-Hessian standard errors against the sampling spread of the same estimates, per cell and per block, and shipped the result as `data/trust/v1_excitation_only.csv`. The ratio `q = mean reported SE / empirical SD` is below 1 wherever the interval is narrower than the spread it describes. A user who calls `fit` cannot be expected to look that up, read a distance, and choose a method. `auto` does it for them, and records what it did. ## The rule `auto` runs the fit, looks the fitted regime up in the shipped map, and takes one of four actions. 1. **The map does not cover the regime** (`status == "unknown"`). Use the Hessian. `se_source="hessian"`, and `se_note` says the interval is uncalibrated because nothing was measured there. `auto` does **not** bootstrap here. Bootstrapping would produce a number with no measured property behind it and would imply knowledge the map does not have; the Hessian interval at least has the same asymptotic theory as the point estimate, and the note says what it is. 2. **The map says the estimator is undefined** (`status == "estimator undefined in this regime"`). Return no standard error. `se_source="none"`, no `se`, no intervals, `reliable="unreliable"`. `se_note` names the regime axis that failed, which is `beta_min*T` when the window holds too few decay times and the no-standard-error rate when most fits in the cell returned none. A number must never be returned here: in these cells the measured rate of fits with a usable standard error is at most 0.5, and the map's own rule made that the definition of the flag. 3. **The map is measured and a block is miscalibrated in a known direction.** Use the parametric bootstrap with `B = 200`. This is the case the release exists for. 4. **The map is measured and no block is miscalibrated.** Use the Hessian. ### The threshold, and why it is two Monte Carlo standard errors The map reports, per block, `q` with a bootstrap standard error `se_q`. The rule compares each **estimated** block (a block the caller fixed is not estimated and is not tested) against ``` q + 2 * se_q < 1 (the interval is narrower than the spread) q - 2 * se_q > 1 (the interval is wider than the spread) ``` The 2 is not a convention imported from elsewhere. Each cell in the map is a mean over 2000 replications, so `se_q` is the Monte Carlo uncertainty of the measured ratio itself. If the 2-SE interval around `q` excludes 1, then the direction of the miscalibration is resolved at the level the study's own replication count supports: the observed shortfall is larger than the noise in measuring it. If the interval contains 1, the map cannot distinguish this cell from a correctly calibrated one, and switching method on that evidence would be switching on noise. Two Monte Carlo standard errors rather than 1.96 is deliberate. It is the conventional two-sided rule, it is stated to the user as "twice the study's own error bar", and the difference from 1.96 changes no decision anywhere in the shipped map (see the table below). **A consequence, stated plainly.** At this threshold, **every** measured cell in the shipped map fires: 61 of the 61 cells that are not estimator-undefined carry at least one block whose 2-SE interval excludes 1. The map does not certify a single regime in which the Hessian interval is measurably adequate. `auto` therefore chooses the bootstrap for every regime the map has measured, and the Hessian only where the map is silent or where the caller's projection exceeds the time limit. That is a real property of the measurement, not a defect of the rule, and the release notes say so. It is also why the cost control below is not optional. The blocks, over the 61 defined cells: | Block | `q + 2 se < 1` | `q - 2 se > 1` | Neither | |---|---|---|---| | `mu` | 40 | 1 | 20 | | `alpha` | 32 | 12 | 17 | | `beta` | 46 | 11 | 4 | ## The cost of the switch, and the limit on it The bootstrap refits the model `B = 200` times. Measured on the machine the capability table describes, at M = 4, N = 1e5, `B = 200` costs 8.5 min on one worker, 2.3 min on four, and **86 s on the default 24-process pool**. A switch that silently spends minutes is not acceptable in a function whose other path costs milliseconds, so the policy has three parts: - **Project before spending.** `auto` estimates the cost from the work it has already done: the wall time of the fit it just ran, times `B`, divided by the number of workers the pool would use. That is a projection from a measurement of this machine on this data, not a constant from another machine. - **Record it.** When `auto` chooses the bootstrap, the decision it stores in `FitResult` carries the projected seconds, and the fit's `se_note` says the switch happened and what it was projected to cost. - **Refuse to blow through a limit.** `auto_max_seconds` defaults to **120**. If the projection exceeds it, `auto` does not bootstrap: it returns the Hessian interval, sets `se_source="hessian"`, and the note says that a bootstrap was indicated, what it was projected to cost, and that the limit prevented it. A warning is raised. The interval is then the uncalibrated one, and the note says which limit stopped the switch, so the user can raise `auto_max_seconds` deliberately. The default of 120 s is chosen against the capability table: it admits the bootstrap at M = 4, N = 1e5 on the default pool (86 s, measured) and refuses the same configuration on one worker (8.5 min, measured), which is the boundary between a wait a user will accept inside a fit call and one they will not. ## What `auto` does not do - It does not fix bias. The point estimate is unchanged; only the width of an interval it reports can change. - It does not make the undefined region estimable. Where `beta_min*T` is small enough the decay is not identified, `auto` refuses to report an interval rather than papering over it. - It does not widen beyond what the bootstrap measures. `auto` never scales a Hessian interval by a constant; it either uses the Hessian as it is or replaces it with the bootstrap's. - It does not extrapolate. Outside the measured box it says so and uses the Hessian. - It does not cover `shared_decay=True`, which is a different model with `M` decay rates and is outside the measured map. ## The record Every `auto` fit carries a `FitResult.auto` decision record with the fields the audit needs: the map status, the cell, the distance, the `q` and `se_q` it read per block, the threshold, the action taken with its reason, the projected and limit seconds, and whether the bootstrap ran. `FitResult.se_source` is one of `"hessian"`, `"bootstrap"`, `"none"`; `FitResult.se_note` is one sentence. Both survive `to_dict` / `from_dict` unchanged. A number whose provenance is not in the object is a number the user has to trust. ## Verifying it Whether the switch actually improves coverage is a measurement, not an argument. `docs/preregistration_auto.md` freezes that measurement before it runs, and `studies/results/auto/` holds the result. If the bootstrap does not move coverage toward nominal, that is the finding, and it ships as one.