graph LR G["gradient g"] --> FWD["forward sub\nw = Lโปยน g"] FWD --> Z["z = Dโปยน w"] Z --> OM["omega = wแต z"] OM --> CUT["cut strategy\nrho, sigma, delta"] CUT --> BACK["back sub\nq = Lโปแต z"] BACK --> XC["x_c -= (rho/omega) q"] CUT --> R1["rank-1 update\nof L, D"] style G fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style FWD fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style Z fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style OM fill:#fff9c4,stroke:#f57f17,stroke-width:3px style CUT fill:#fff9c4,stroke:#f57f17,stroke-width:3px style BACK fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style XC fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px style R1 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px
Stable / Ell ratio (lower = Ell faster) ๐ Python 0.97ร โ a tie โ๏ธ C++ 1.04ร ๐ฆ Rust 1.23ร
Wall time ยท Ell (blue) vs EllStable (red) ๐ Python 3.89 s 37.97 s ยท 9.8ร โ๏ธ C++ 184 ms 209 ms ยท 1.14ร ๐ฆ Rust broken โ 0 iterations ๐
graph LR N["dimension n"] --> PY["๐ Python\nratio explodes\n(interpreter loops)"] N --> CPP["โ๏ธ C++\nratio flat ~1.1x\n(Ell always ahead)"] N --> RS["๐ฆ Rust\ncrosses over at nโ128\n(Stable wins โฅ128)"] style N fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style PY fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style CPP fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style RS fill:#fff9c4,stroke:#f57f17,stroke-width:3px
Stable/Ell ratio vs dimension n ๐ n=64 122ร ๐ฆ n=64 2.0ร โ๏ธ n=64 1.38ร ๐ฆ n=128 0.90ร โ Stable wins โ๏ธ n=128 1.07ร
graph TD Q["Where does the time go?"] Q --> PY["๐ Python\nEll = one BLAS gemv\nStable = O(nยฒ) Python ops"] Q --> CPP["โ๏ธ C++\nboth are tight loops\nEll's flat rank-1 wins"] Q --> RS["๐ฆ Rust\nEll re-borrowed a slice\nper element in the loop"] PY --> R1["โ 10โ122x slower"] CPP --> R2["โ Ell ~1.1x ahead"] RS --> R3["โ degraded at large n"] style Q fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style PY fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style CPP fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style RS fill:#fff9c4,stroke:#f57f17,stroke-width:3px style R1 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style R2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style R3 fill:#fff9c4,stroke:#f57f17,stroke-width:3px
Rust lowpass-32 bounds before (buggy) lp_sq=161 > up_sq=0.006 ยท 0 iters after (fixed) lp_sq=0.952 < up_sq=1.051 ยท 12 515 iters Now the Rust lowpass actually exercises the ellipsoid method โ and the test runs for ~14 s.
EllStable speedup from vectorization (controlled A/B) n = 2 0.60ร โ slower โ ๏ธ n = 32 2.3ร faster n = 64 4.5ร faster Crossover โ n = 8โ15: below it numpy slice overhead dominates; above it the $O(n^2)$ win dominates.
Stable/Ell after the fix (stable, best-of-3) profit 2-D 1.20ร lowpass-32 par 1.50ร Rand-64 2.15ร Rand-128 0.90ร โ Stable Rand-256 1.11ร Ell is ~20% faster at dim 16โ64; the crossover is now a narrow window around n = 128.
Stable/Ell before (red) vs after (green) Rand-32 17.5ร 7.4ร Rand-64 51.8ร 13.4ร Still the slowest language for EllStable โ but the gap shrank by 2.4ร / 4.0ร.
graph LR M["Measure both\nin the SAME run"] M --> R["Ratio is valid\neven if the machine drifts"] R --> C["Controlled git-stash A/B"] C --> K["Keep what wins\nrecord what loses"] style M fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style R fill:#fff9c4,stroke:#f57f17,stroke-width:3px style C fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style K fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px