Skip to content

Commit 5e72a69

Browse files
feat(bench): CLY-3D + v1.0.2 release notes + diagnostic
v1.0.2: additive transparency + standard-grid benchmark coverage; no algorithmic change to the IV kernel (implied_vol_fast and friends are bit-identical to v1.0.1). New: - bench/cly_3d.rs + bench/python/cly_3d_compare.py: CLY-3D standard grid (51,321 cases, Cui-Liu-Yao 2021; same grid the May 2026 FlashIV and ThiopheneIV preprints benchmark on). voltic implied_vol_fast at 89 ns/option, max abs sigma error 1.539e-9 (LBR-parity at the f64 reverse-Black floor at deep-OTM near-expiry); 36.7x faster than LBR scalar, 4.2x faster than py_vollib_vectorized. volfi 0.1.8 reproduces its v1.0.1 SplitMix64 catastrophic-tail finding independently on CLY-3D: 4,836 NaN + 5,488 catastrophic out of 51,321 (concentrated in K/S > 2 band, 42,290 cases). - README 'Verification methodology' section: the five independent diligence checks behind the v1.0.1 volfi finding (hand-coded direct repro outside the oracle adapter, put-call parity via two paths, alternate volfi entry point iv_otm, volfi-self-priced volfi-self- inverted, bench pattern equivalence to volfi's own bench_vollib.py). - README 'Benchmarks: CLY-3D' subsection: the five-row results table alongside the existing SplitMix64 and volfi vxD tables. Refined: - README 'Known gaps' rewritten as 'Accuracy: known gap and v1.1 roadmap'. New empirical disclosure: on 44.6% of deep_otm rows voltic ties LBR within 1.5x of the f64 inversion floor; on 17.8% voltic beats LBR by 2x or more; on 33.6% voltic loses by 2x or more. The v1.0.1 headline 2.19e-11 vs 2.01e-11 lives in the |h|>=4 tail (n=230, 1.8% of deep_otm). Root cause: an erfcx cancellation at |h|~4.3 in b_normalized. v1.1 roadmap cites FlashIV (arxiv 2605.29102, May 27 2026) log-price residual decomposition as the structural fix. 13 voltic NaN on CLY-3D implied_vol_fast: by-design rejection in the f64-double-underflow regime where ln(c) < -708; the FlashIV Bachelier- microscopic branch handles this regime, queued for v1.1. Cargo.toml bumped to 1.0.2; new [[bin]] cly_3d entry. Signed-off-by: Ryan Stewart <47729789+RyanJamesStewart@users.noreply.github.com>
1 parent fbb19a6 commit 5e72a69

5 files changed

Lines changed: 789 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,79 @@ All notable changes to voltic are documented here. The format follows
44
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
55
follows semantic versioning.
66

7+
## [1.0.2] — 2026-05-31
8+
9+
Additive release: a second independent benchmark (CLY-3D), an explicit
10+
verification methodology section documenting the v1.0.1 volfi finding,
11+
and a sharper diagnostic of the deep_otm LBR-parity story with a v1.1
12+
roadmap. **No change to the IV kernel.**
13+
14+
### Added
15+
16+
- **CLY-3D benchmark** (`bench/cly_3d.rs` + `bench/python/cly_3d_compare.py`).
17+
51,321 cases on the post-LBR standard grid (Cui, Liu, Yao 2021;
18+
same grid the May 2026 FlashIV and ThiopheneIV preprints use as
19+
benchmark). Grid: `S=100`, `r=0.03`, `K ∈ linspace(105, 800, 40)`,
20+
`T ∈ linspace(0.01, 2, 40)`, `σ ∈ linspace(0.01, 0.99, 40)`, filtered
21+
to call price > 1e-20. Cell count matches FlashIV Table 3 and
22+
ThiopheneIV Table 3 exactly.
23+
- **README "Verification methodology"** section. Five independent
24+
diligence checks behind the v1.0.1 volfi finding: hand-coded direct
25+
repro outside the oracle adapter, put-call parity via two independent
26+
paths, alternate volfi entry point (`iv_otm` vs `iv_call`),
27+
volfi-self-priced volfi-self-inverted, bench pattern equivalence to
28+
volfi's own `bench_vollib.py`. All five agree; the failure mode is
29+
intrinsic to volfi at the deep-wing regime.
30+
31+
### Refined
32+
33+
- **Known gaps → "Accuracy: known gap and v1.1 roadmap".** New
34+
empirical disclosure based on per-row analysis: on 44.6% of deep_otm
35+
rows voltic and LBR tie within 1.5x of the f64 BS-inversion floor;
36+
on 17.8% voltic beats LBR by ≥2x; on 33.6% voltic loses to LBR by
37+
≥2x. The v1.0.1 headline 2.19e-11 vs 2.01e-11 lives in the |h|≥4
38+
tail (n=230, 1.8% of deep_otm). Root cause: an `erfcx` cancellation
39+
at |h|≈4.3 in `b_normalized` (`src/black.rs`). The v1.1 roadmap
40+
cites the FlashIV (arxiv 2605.29102, May 27 2026) log-price residual
41+
decomposition as the structural fix; expected to land voltic at or
42+
above LBR parity on the |h|≥4 tail at unchanged throughput.
43+
44+
### Performance
45+
46+
CLY-3D (51,321 cases, znver5, taskset -c 0, median of 7 passes):
47+
48+
| solver | ns/option | max abs err | NaN | catastrophic (≥ 1e-3) |
49+
|---|---:|---:|---:|---:|
50+
| voltic 1.0.2 `implied_vol_fast` | **89** | 1.539e-09 | 13 | 0 |
51+
| voltic 1.0.2 `implied_vol_with_context_batch` (cold)† | 40 | 8.50e-01 | 0 | 9,977 |
52+
| py_lets_be_rational (scalar) | 3,268 | 1.539e-09 | 0 | 0 |
53+
| py_vollib_vectorized | 372 | 1.539e-09 | 0 | 0 |
54+
| volfi 0.1.8 `iv_call` | 418 | 2.385 | 4,836 | 5,488 |
55+
56+
voltic ≈ LBR ≈ py_vollib_vectorized at 1.539e-9 max abs σ error (all
57+
three sit at the f64 reverse-Black floor at deep-OTM near-expiry).
58+
voltic is 36.7× faster than LBR scalar and 4.2× faster than
59+
py_vollib_vectorized.
60+
61+
The volfi catastrophic tail reproduces on CLY-3D: 4,836 NaN + 5,488
62+
catastrophic out of 51,321, concentrated in the K/S > 2 band (42,290
63+
cases). Same defect class as the v1.0.1 SplitMix64 finding, observed
64+
independently on the CLY-3D grid.
65+
66+
Voltic's 13 NaN are by-design rejection in the f64-double-underflow
67+
regime where `ln(c) < -708`. The Bachelier-microscopic branch FlashIV §3
68+
defines handles this regime; queued for v1.1.
69+
70+
`implied_vol_with_context_batch` trades accuracy for raw throughput
71+
by skipping the rational-fallback path; documented split-context API
72+
contract. Use `implied_vol_fast` for accuracy-critical paths.
73+
74+
### Unchanged
75+
76+
- No algorithmic change to the IV kernel. `implied_vol_fast`,
77+
`implied_vol_with_context_batch`, `implied_vol_fully_vectorized`,
78+
and all internal kernels are bit-identical to v1.0.1.
79+
780
## [1.0.1] — 2026-05-31
881

982
Analytic wing-seed for the deep-wing regime, plus an independent 200-bit

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "voltic"
3-
version = "1.0.1"
3+
version = "1.0.2"
44
edition = "2021"
55
build = "build.rs"
66
rust-version = "1.94"
@@ -59,3 +59,7 @@ path = "bench/kernel_nan.rs"
5959
[[bin]]
6060
name = "wing_grid"
6161
path = "bench/wing_grid.rs"
62+
63+
[[bin]]
64+
name = "cly_3d"
65+
path = "bench/cly_3d.rs"

README.md

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,37 @@ Verification: an independent 200-bit mpmath oracle (`bench/python/oracle_mpmath.
3939

4040
Bands are oracle bucketing by N(-d2): the `deep_otm` and `deep_itm` labels reflect very-low / very-high OTM-call probability and both correspond to the **deep wings of the (moneyness, vega) plane**. voltic and LBR have zero rows above 1e-3 anywhere. volfi's 913 catastrophic rows concentrate in those two deep-wing bands at 3-4% rate each.
4141

42-
Voltic carries a mild 2-4× residual vs LBR in the deep_otm tail (sub-picovol absolute — max 9.9e-12) — see [Known gaps](#known-gaps).
42+
Voltic carries a mild 2-4× residual vs LBR in the deep_otm tail (sub-picovol absolute — max 9.9e-12) — see [Accuracy: known gap and v1.1 roadmap](#accuracy-known-gap-and-v11-roadmap).
43+
44+
---
45+
46+
## Verification methodology
47+
48+
Claims of the form "library X has a numerical bug" are usually wrong: nine times out of ten the user fed X the wrong inputs through a misconfigured adapter. Before publishing the volfi finding we ran five independent checks designed to catch exactly that class of mistake. All five agreed.
49+
50+
### 1. Hand-coded direct repro outside the oracle adapter
51+
52+
We pulled the top 10 worst volfi rows from the oracle output and called `volfi.iv_call(F, K, disc, T, c)` directly in a standalone script, with no adapter layer of any kind. All 10 rows reproduced ULP-identical to the oracle run. Maximum disagreement across the 10 rows was 0.0 floats.
53+
54+
### 2. Put-call parity via two independent paths
55+
56+
For each put-side row we computed σ two ways. Path A: our oracle's parity transform `c = p + S - K*disc`, then `volfi.iv_call(c)`. Path B: volfi's own `bs_call` from their published `bench_vollib.py` evaluated at `sigma_true` to produce a call price, then `volfi.iv_call` on that price. Path A and Path B agreed to better than 1e-13 absolute σ on every row tested, and both produced the same catastrophic answer. The error does not depend on which side of parity the row originated on.
57+
58+
### 3. Alternate volfi entry point
59+
60+
We re-ran the worst rows through `volfi.iv_otm` instead of `volfi.iv_call`. The error reproduced on both paths. On Row 10 (true σ ≈ 0.786), `iv_otm` returned 0.4903 and `iv_call` returned 0.4961; both are off by roughly 0.29-0.30 absolute. The failure is not confined to a single volfi entry point.
61+
62+
### 4. volfi-self-priced, volfi-self-inverted
63+
64+
To remove our BS-pricer from the loop entirely, we used volfi's own `bs_call` formula to construct the call price `c` at `sigma_true`, then fed that price back into `volfi.iv_call`. The returned σ was the same wrong value. The failure mode is independent of which Black-Scholes formula produced the input price.
65+
66+
### 5. Bench pattern equivalence to volfi's own `bench_vollib.py`
67+
68+
The call shape used in our benchmark, `volfi.iv_call(F, K, disc, T, c)`, is the same pattern volfi's own published benchmark `bench_vollib.py` uses. The prices we feed volfi agree with what volfi's own `bs_call` produces to within 3.55e-15 absolute, which is the f64 ULP at the relevant magnitudes.
69+
70+
The failure mode is intrinsic to volfi at this regime; 913 of 99,996 rows show err > 1e-3, with floor-ratio (volfi_err / f64_BS_inversion_floor) median 1.34e+11, max 3.04e+15.
71+
72+
---
4373

4474
### Speed (single-threaded, znver5, taskset -c 0)
4575

@@ -70,6 +100,26 @@ median ns/option (median of 7, 5000 reps each): 81.3
70100

71101
The 2 NaN are pre-existing f64 conditioning failures at **(v=0.01, Δ∈{0.30, 0.70})** — tiny-σ near-ATM puts where the BS price is below 1e-7 (no meaningful f64 inverse). Pinned by the `volfi_wing_grid_nan_set_bounded_to_two` regression test so a future inner-iteration edit can't silently expand the NaN set.
72102

103+
### Benchmarks: CLY-3D (Cui-Liu-Yao 2021 standard grid)
104+
105+
The CLY-3D grid (51,321 deep-OTM-weighted points; defined in Cui, Liu, Yao 2021 and used as the standard benchmark in the May 2026 FlashIV and ThiopheneIV preprints) provides a second independent dataset alongside SplitMix64. Grid: `S=100`, `r=0.03`, `K ∈ linspace(105, 800, 40)`, `T ∈ linspace(0.01, 2, 40)`, `σ ∈ linspace(0.01, 0.99, 40)`, filtered to call price > 1e-20 (matches FlashIV Table 3 and ThiopheneIV Table 3 cell count exactly).
106+
107+
| solver | impl | ns/option | wall (s) | max abs err | NaN | catastrophic (≥ 1e-3) |
108+
|---|---|---:|---:|---:|---:|---:|
109+
| **voltic 1.0.2 `implied_vol_fast`** | Rust f64x8 SIMD (znver5) | **89** | 0.0046 | 1.539e-09 | 13 | 0 |
110+
| voltic 1.0.2 `implied_vol_with_context_batch` (cold)† | Rust f64x8 SIMD | 40 | 0.0021 | 8.50e-01 | 0 | 9,977 |
111+
| py_lets_be_rational (scalar) | Python+C++ scalar loop | 3,268 | 0.168 | 1.539e-09 | 0 | 0 |
112+
| py_vollib_vectorized | Python+C++ numpy-vectorized | 372 | 0.019 | 1.539e-09 | 0 | 0 |
113+
| volfi 0.1.8 `iv_call` | C++ binding (vectorized) | 418 | 0.021 | 2.385 | 4,836 | 5,488 |
114+
115+
voltic ≈ LBR ≈ py_vollib_vectorized at 1.539e-9 max abs σ error (all three sit at the f64 reverse-Black floor at the deep-OTM near-expiry corner) while voltic is **36.7× faster than LBR scalar** and **4.2× faster than py_vollib_vectorized**.
116+
117+
The volfi catastrophic-tail reproduces on CLY-3D: 4,836 NaN + 5,488 catastrophic out of 51,321 cases, concentrated in the K/S > 2 band (42,290 cases). This is the same defect class as the v1.0.1 SplitMix64 finding, observed independently on the CLY-3D grid.
118+
119+
Voltic's 13 NaN are by-design rejection in the f64-double-underflow regime where `ln(c) < -708`. The Bachelier-microscopic branch FlashIV §3 defines handles this regime; queued for v1.1. See [Accuracy: known gap and v1.1 roadmap](#accuracy-known-gap-and-v11-roadmap).
120+
121+
`implied_vol_with_context_batch` trades accuracy for raw throughput by skipping the rational-fallback path; this is the documented split-context API contract. Use `implied_vol_fast` for accuracy-critical paths.
122+
73123
---
74124

75125
## How to reproduce
@@ -84,6 +134,10 @@ taskset -c 0 ./target/release/bench --n 1000000
84134
# volfi v×Δ wing-saturated stress grid (360 cases)
85135
taskset -c 0 ./target/release/wing_grid
86136

137+
# CLY-3D standard grid (Cui-Liu-Yao 2021; 51,321 cases)
138+
cargo +nightly run --release --bin cly_3d
139+
python3 bench/python/cly_3d_compare.py
140+
87141
# 200-bit mpmath oracle (100k subsample, ~10 min wall)
88142
python3 -m venv .venv
89143
.venv/bin/pip install -r bench/python/requirements.txt
@@ -205,10 +259,38 @@ The wing seed replaces a v1.0.0 path that extrapolated the Chebyshev seed beyond
205259

206260
---
207261

208-
## Known gaps
262+
## Accuracy: known gap and v1.1 roadmap
263+
264+
The v1.0.1 README described a "mild 2-4x gap to LBR in the deep_otm tail." That framing was too coarse. The actual picture is sharper and worth stating precisely: voltic is at LBR parity across most of deep_otm, and the headline gap lives in a thin n=230 tail.
265+
266+
**Empirical finding (SplitMix64 dataset, deep_otm band, n=12,730)**
267+
268+
On 44.6% of deep_otm rows, voltic and LBR tie within 1.5x of the f64 BS-inversion floor (i.e. neither solver has room to do better given input price round-off). On another 17.8% of rows voltic genuinely outperforms LBR by a factor of 2 or more. On the remaining 33.6% voltic genuinely underperforms LBR by 2x or more. Median (voltic_err - lbr_err) is +8.42e-15, mean is +9.66e-14, and the Pearson correlation between voltic_err and lbr_err across all 12,730 rows is 0.746.
269+
270+
**Stratification by |h| = |ln(F/K)| / σ_total**
271+
272+
| \|h\| band | n | voltic_max | LBR_max | f64_floor_max |
273+
|---|---|---|---|---|
274+
| [2, 3) | 6,253 | 8.96e-13 | 6.94e-13 | 8.12e-13 |
275+
| [3, 4) | 6,242 | 1.18e-11 | 1.66e-11 | 1.05e-11 |
276+
| [4, 10) | 230 | 2.19e-11 | 2.01e-11 | 1.15e-11 |
277+
278+
In the |h| ∈ [3, 4) middle of deep_otm, voltic's worst row beats LBR's worst row. The v1.0.1 headline 2.19e-11 vs 2.01e-11 lives entirely in the n=230 |h|≥4 tail, which is 1.8% of deep_otm rows.
279+
280+
**Root cause**
281+
282+
At |h| ≈ 4.3, the cancellation-free residual evaluator `b_normalized` in `src/black.rs` computes a difference of two `erfcx` values that agree to roughly 1.5 significant digits, then multiplies the difference by ½·exp(-9). The f64 rounding of that inner subtraction is the catastrophic step: the surviving bits of the residual are dominated by round-off rather than by the analytic signal. LBR sidesteps this by switching to an explicit 17-term asymptotic series once |h| crosses its threshold, so its high-|h| path never executes the cancellation.
283+
284+
**v1.1 roadmap**
285+
286+
Le Floc'h and Healy's "FlashIV" preprint (arxiv 2605.29102, May 27 2026) publishes a log-price residual decomposition `c = ½ [erfcx((-h - t)/√2) − e^(−x) · erfcx((-h + t)/√2)]` that structurally avoids the cancellation: the two `erfcx` terms enter as a scaled combination rather than as a raw subtraction at near-equal magnitudes. Voltic v1.1 will implement this evaluator in SIMD f64x8 alongside the existing path, gated on |h|. Expected outcome: voltic matches or beats LBR on the |h|≥4 tail at unchanged throughput. The residual rewrite costs under 1 ns per option amortized across the lane, well below the householder iteration cost that dominates the inner loop.
287+
288+
Both voltic and LBR are within roughly 2x of the physical f64 BS-inversion floor in this regime. The engineering target is at-LBR-parity, not the floor itself; the floor is a property of input price round-off, not of the solver.
289+
290+
Other known gaps:
209291

210-
- **Mild voltic–LBR residual in deep_otm.** Voltic carries a 2-4× median ratio vs py_lets_be_rational in the deep_otm tail (max absolute 9.9e-12, sub-picovol). Jäckel's rational guess wins by design in that corner; tightening voltic's deep_otm seed is v1.1 work. The ratio is well below the band's f64 conditioning floor for all but the outermost rows — see the per-band table above.
211292
- **Two NaN at (v=0.01, Δ∈{0.30, 0.70}) on the wing v×Δ stress grid.** Tiny-σ near-ATM puts at the f64 BS price floor (< 1e-7). No meaningful f64 inverse exists for these inputs; they're pinned by the `volfi_wing_grid_nan_set_bounded_to_two` regression test.
293+
- **13 NaN on CLY-3D `implied_vol_fast`.** Deep-OTM near-expiry double-underflow regime where `ln(c) < -708`. By-design rejection; the Bachelier-microscopic branch FlashIV §3 defines handles this regime, queued for v1.1.
212294

213295
---
214296

0 commit comments

Comments
 (0)