Metadata-Version: 2.4
Name: stoppower
Version: 0.1.0rc1
Summary: Dimensionar la ventana de un criterio de detencion por analisis de potencia
Author-email: Maximiliano Rodrigo Speranza <maximiliano.speranza@gmail.com>
License: MIT
Project-URL: Paper, https://doi.org/10.5281/zenodo.21630279
Project-URL: ORCID, https://orcid.org/0009-0005-0413-8554
Keywords: early-stopping,statistical-power,convergence,machine-learning
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# stoppower

[![tests](https://github.com/SperanzaMax/stoppower/actions/workflows/tests.yml/badge.svg)](https://github.com/SperanzaMax/stoppower/actions/workflows/tests.yml)

Dimensionar la ventana de un criterio de detención por análisis de potencia, en vez de elegirla
por costumbre.

```bash
pip install stoppower
```

## El problema

Casi todo el mundo decide que un modelo "convergió" comparando dos o tres evaluaciones y
aflojando la tolerancia cuando la detección falla. Eso ataca el término equivocado. En el caso
medido ([Zenodo 10.5281/zenodo.21630279](https://doi.org/10.5281/zenodo.21630279)), la ganancia
total de detección se descompone en **7.0× por alargar la ventana** y solo **1.31× por cambiar el
estadístico**. La tolerancia casi no mueve la aguja.

Peor: cuando la relación señal-ruido cae por debajo de 1, **ninguna** tolerancia recupera la
decisión. El error tipo I y el tipo II dejan de poder separarse.

## La regla

Para `n` evaluaciones equiespaciadas, el error estándar de la pendiente OLS decrece como
`n^(-3/2)`, contra `n^(-1/2)` del simple promediado:

```
EE(b) = σ · sqrt( 12 / (n(n² − 1)) )
b*   ≥ (z_α + z_β) · EE(b)
```

## Uso

```python
from stoppower import window_for, sigma_from_pilot, evaluate, prereg_text, pts

# 1) ¿Qué ventana necesito para no pasar por alto 1 punto de accuracy cada 2500 pasos?
plan = window_for(b_star=pts(1.0), sigma=0.0082, eval_every=500)
print(plan)      # -> n=11 evaluaciones = 5000 pasos

# 2) ¿Qué potencia tiene la ventana que ya uso?
from stoppower import power_of
power_of(pts(1.0), sigma=0.0082, n=3, eval_every=500)     # -> 0.097

# 3) Decidir con datos
evaluate(steps, accs, b_star=pts(1.0))
```

```bash
stoppower design --b-star 0.01 --sigma 0.0082 --eval-every 500 --prereg
stoppower power  --b-star 0.01 --sigma 0.0082 --n 3 --eval-every 500
stoppower check  historial.csv --b-star 0.01 --span 5000 10000
```


## Un caso real, de punta a punta

Historial de validación de un modelo entrenado 10 000 pasos, evaluando cada 500. La pregunta es
si en el tramo final sigue mejorando o ya se puede cortar.

```bash
$ stoppower check historial.csv --b-star 0.01 --span 5000 10000
  sigma estimada en el tramo 5000-10000: 0.010593
INDECIDIBLE · pendiente +0.006424 por 2500 pasos (IC95 [-0.003474, +0.01632])
  t=1.27 p=0.1017 · n=11 evaluaciones · potencia=0.63 para b*=0.01
  ! potencia 0.63 < 0.8: con esta ventana no se puede afirmar convergencia,
    solo que no se detectó mejora. Alargá la ventana antes de concluir.
```

El criterio de dos puntos habría dicho "convergió". Acá el veredicto es **indecidible**, con el
número al lado: la ventana no tiene potencia para sostener esa afirmación.

Y si en vez del tramo estable se le pide toda la corrida, avisa antes de responder:

```bash
$ stoppower check historial.csv --b-star 0.01 --span 500 10000
  ! el ruido cambia 7.4x dentro de la ventana (sigma=0.06951 en la 1ª mitad vs
    0.009344 en la 2ª, más alto al principio): la ecuación supone ruido constante.
    Acortá la ventana o corrésela al régimen estable.
```

## Tres cosas que lo distinguen de copiar la fórmula

**1. σ se estima en el tramo, no sobre toda la corrida.** El ruido de validación no es constante.
Medido sobre 8 semillas de un mismo modelo:

| paso | 500 | 1000 | 1500 | 2000 | 2500 | 5000 | 7500 |
|---|---|---|---|---|---|---|---|
| SD entre semillas | .101 | .073 | .038 | .010 | .014 | .009 | .011 |

Un factor 9. Quien meta una σ global en la ecuación se equivoca por un factor grande, y por eso
acá el tramo es un argumento obligatorio. `homoscedasticity_check` avisa cuando el supuesto de
ruido constante no se sostiene ni siquiera dentro de la ventana:

```
! el ruido cambia 7.4x dentro de la ventana (sigma=0.0695 en la 1ª mitad vs 0.0093 en la 2ª):
  la ecuación supone ruido constante. Acortá la ventana o corrésela al régimen estable.
```

**2. No confunde "no detecté mejora" con "convergió".** `evaluate` devuelve `converged=None`
cuando la potencia no alcanza, en vez de dar un falso verde:

```
INDECIDIBLE · pendiente +0.0064 por 2500 pasos (IC95 [-0.0035, +0.0163])
  t=1.27 p=0.1017 · n=11 evaluaciones · potencia=0.63 para b*=0.01
  ! potencia 0.63 < 0.8: no se puede afirmar convergencia, solo que no se detectó mejora.
```

**3. Escupe el párrafo pre-registrable.** `prereg_text(plan)` da el texto con σ, su procedencia, el
nivel, la potencia y la ventana resultante — para pegar en el protocolo **antes** de ver los datos.

## Cómo estimar σ sin circularidad

| situación | función |
|---|---|
| el conjunto de validación se remuestrea | `sigma_floor(p, n_val)` — cota analítica, sin entrenar |
| conjunto fijo, tenés un piloto | `sigma_from_pilot(steps, values, span=(desde, hasta))` |
| tenés varias semillas corridas | `sigma_from_runs(runs, at_index=...)` |

Fijar una ventana a partir de un piloto no es lo mismo que recalibrar una tolerancia después de
ver los resultados: se fija el **diseño**, no el veredicto.

## Alcance

Sin dependencias (solo la biblioteca estándar). No entrena, no toca tu bucle, no decide por vos.
No modela el efecto del hardware: está medido que cambiar de backend aporta 0.53× la variación
entre semillas en régimen tardío, despreciable frente a lo que ya se reporta.

## Cita

Speranza, M. R. (2026). *Stopping criteria below the signal-to-noise floor: window length, not
tolerance, governs convergence detection in architecture comparisons.*
[10.5281/zenodo.21630279](https://doi.org/10.5281/zenodo.21630279)

MIT.
