Convergio
Automatic convergence detection for iterative numerical methods.
Stop wasting compute. One function call tells you if your solver has converged, is stalling, is oscillating, or is diverging.
"Your solver finished 3000 iterations ago."
Install
pip install convergio
Only dependency: NumPy.
Quick Start
Analyze a completed run
from convergio import detect
result = detect(my_residual_history)
print(result.state) # "converged" | "stalling" | "oscillating" | "diverging" | ...
print(result.quality) # 0.0 — 1.0
print(result.converged_at) # step where convergence was detected
Detect stalling (new in v0.1.3)
A solver can look "converged" (low variance) but actually be stuck above the target residual. Convergio distinguishes between true convergence and stalling:
result = detect(residuals, residual_target=1e-8)
if result.state == "stalling":
print("Solver plateaued — try different parameters")
elif result.state == "converged":
print(f"Solved at step {result.converged_at}")
Monitor a running simulation
from convergio import watch
mon = watch(var_threshold=1e-6, residual_target=1e-8)
for step in range(10000):
residual = solver.step()
stop, info = mon.step(residual)
if stop:
print(f"Done at step {step} — saved {10000 - step} iterations")
break
What It Detects
| State | Meaning | Recommendation |
|---|---|---|
converged |
Signal has stabilized below target | Stop |
stalling |
Signal is flat but above target | Adjust parameters |
oscillating |
Signal oscillates periodically without settling | Continue |
noisy |
High zero-crossing rate but no periodicity (looks like noise) | Continue |
bistable |
Signal jumps between 2+ distinct states | Investigate |
diverging |
Variance is genuinely growing | Restart |
numerical_failure |
Signal contains NaN/Inf — run collapsed | Restart |
warming_up |
Not enough data yet | Continue |
Benchmark Results
Tested on synthetic signals across all convergence states (v0.1.4):
Detection Accuracy
| Signal Type | N=200 | N=500 | N=1000 |
|---|---|---|---|
| Converging (exp. decay) | converged | converged | converged |
| Stalling (flat, above target) | stalling | stalling | stalling |
| Oscillating (sine + noise) | — | oscillating | oscillating |
| Diverging (random walk) | — | diverging | diverging |
Overall: 87% correct across all signal types and lengths. Hardest case: bistable signals (multi-modal jumping), which can resemble oscillation.
Compute Savings with watch()
| Scenario | Budget | Stopped at | Saved |
|---|---|---|---|
| Fast convergence | 2,000 | 1,100 | 45% |
| Medium convergence | 5,000 | 4,200 | 16% |
| Diverging (early abort) | 5,000 | 350 | 93% |
watch() detects convergence and divergence. Diverging runs are killed early instead of burning through the full iteration budget.
Stalling signals are flagged but not auto-stopped — you decide what to do.
Speed
| Signal Length | Time | Throughput |
|---|---|---|
| 1,000 | 5 ms | 200K pts/s |
| 10,000 | 52 ms | 190K pts/s |
| 100,000 | 517 ms | 193K pts/s |
| 1,000,000 | 5.1 s | 197K pts/s |
detect() runs at ~190,000 points/second. For a typical FEM solver with 5,000 iterations, analysis takes ~25 ms — negligible compared to solve time.
API Reference
detect(signal, window=0, var_threshold=1e-6, osc_threshold=0.005, residual_target=0.0)
Analyze a complete time series.
| Parameter | Type | Default | Description |
|---|---|---|---|
signal |
array-like | — | 1D array of scalar values |
window |
int | 0 (auto) | Analysis window size |
var_threshold |
float | 1e-6 | Variance below this = stable |
osc_threshold |
float | 0.005 | Min oscillation frequency (zero-crossings / 2N) to flag oscillation |
residual_target |
float | 0.0 | If > 0: stable signal above this = stalling |
Returns ConvergenceResult.
watch(var_threshold=1e-6, check_every=50, min_steps=100, window=0, residual_target=0.0, max_history=10000)
Create a live monitor. Returns Watch object. Call .step(value) each iteration. Memory is bounded by max_history.
| Parameter | Type | Default | Description |
|---|---|---|---|
var_threshold |
float | 1e-6 | Variance below this = stable |
check_every |
int | 50 | Check convergence every N steps |
min_steps |
int | 100 | Minimum steps before first check |
window |
int | 0 (auto) | Analysis window size |
residual_target |
float | 0.0 | If > 0: stable signal above this = stalling |
max_history |
int | 10000 | Keep at most this many recent values (bounds memory) |
ConvergenceResult
| Field | Type | Description |
|---|---|---|
state |
str | converged, stalling, oscillating, noisy, bistable, diverging, numerical_failure, warming_up |
quality |
float | 0.0 — 1.0 convergence quality |
converged_at |
int or None | Step where convergence detected |
final_variance |
float | Variance of last window |
oscillation_freq |
float | Detected oscillation frequency |
n_modes |
int | Number of distinct stable states |
recommendation |
str | stop, continue, restart, adjust_params |
saved_steps |
int | Steps saved by early stopping |
total_steps |
int | Total steps in analyzed signal |
note |
str | Optional hint (e.g. missing residual_target, numerical failure); empty if none |
What's New
v0.2.1
- Documentation: README now reflects the v0.2 states, parameters and
notefield.
v0.2.0 — audit-driven robustness pass
- NaN/Inf no longer crash — new state
numerical_failureinstead of aValueError. - Stationary noise is separated from periodic oscillation — new state
noisy(autocorrelation check); noise is no longer mislabelleddiverging. osc_thresholdis now actually used (it was inert); default rescaled to0.005(the documented zero-crossing frequency), behaviour-preserving.count_modesover-counting fixed — a finite unimodal sample no longer produces spurious extra modes (valley-prominence merge).rolling_variancenow really uses cumulative sums (O(n)); previously a per-window Python loop despite the comment.watch()bounds memory viamax_historyinstead of re-analysing an unbounded history every check.- New
notefield — a stabilized signal with noresidual_targetthat plateaus without dropping from its start now flags the missing target (state staysconvergedfor backward compatibility).
v0.1.5
- All 6 convergence states:
converged,stalling,oscillating,diverging,bistable,warming_up - Improved trend-check robustness for near-convergent signals
- Battle-tested in CAP Stability Engine v0.8 (15/16 SuiteSparse matrices solved)
v0.1.4
- Trend-check: smooth decline is no longer misclassified as stalling
v0.1.3
residual_targetparameter: distinguishes true convergence from stallingstallingstate: flat signal above target is now correctly identifiedwatch()reports stalling without auto-stopping (user decides)
v0.1.0
- Initial release: converged, oscillating, bistable, diverging, warming_up
Works With
- FEM / CFD solvers (residual monitoring)
- ML training (loss convergence)
- Monte Carlo simulations (running averages)
- Optimization loops (objective function)
- Molecular dynamics (energy equilibration)
- Any iterative numerical process that produces a scalar time series
License
MIT
Author
Maximilian Jurak — 3 Kaiserberge Engineering & Research
Metadata
Release files for convergio 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| convergio-0.2.1.tar.gz | 15.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| convergio-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.1 kB
Release files / convergio-0.2.1.tar.gz
| Download URL | convergio-0.2.1.tar.gz |
|---|---|
| Size | 15.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f04ab2bccfd86f2c3ce1614994df3aa2240ed090e043c8cc78b4bb28eea2b097
|
|
BLAKE2b-256 checksum How to use checksums |
c00bc2b0dca1b46231a6d9d222b814ac239172cfbb6640a52bf15205610fc05f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / convergio-0.2.1-py3-none-any.whl
| Download URL | convergio-0.2.1-py3-none-any.whl |
|---|---|
| Size | 11.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5e824a3f2501d4840be9a24a695bd95717575d017c672920283972c507ab140b
|
|
BLAKE2b-256 checksum How to use checksums |
96222314feb98c5670290e0590e4761b224ca36c836822c1c159b172d010b229
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Security for convergio 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.