Automatic convergence detection for iterative numerical methods. Stop wasting compute.
Project description
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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file convergio-0.2.1.tar.gz.
File metadata
- Download URL: convergio-0.2.1.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f04ab2bccfd86f2c3ce1614994df3aa2240ed090e043c8cc78b4bb28eea2b097
|
|
| MD5 |
2899882fc158f985584d65307473d520
|
|
| BLAKE2b-256 |
c00bc2b0dca1b46231a6d9d222b814ac239172cfbb6640a52bf15205610fc05f
|
File details
Details for the file convergio-0.2.1-py3-none-any.whl.
File metadata
- Download URL: convergio-0.2.1-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e824a3f2501d4840be9a24a695bd95717575d017c672920283972c507ab140b
|
|
| MD5 |
a2604207cb3ca650454f0fe19ccfc07e
|
|
| BLAKE2b-256 |
96222314feb98c5670290e0590e4761b224ca36c836822c1c159b172d010b229
|