Skip to content

v3.0.0-rc.5: stabilization, soundness and release fixes - #40

Draft
dancixx wants to merge 552 commits into
mainfrom
release/v3-rc5
Draft

dancixx wants to merge 552 commits into
mainfrom
release/v3-rc5

Conversation

@dancixx

@dancixx dancixx commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Tracking PR for the rc.5 stabilization round: release-pipeline and soundness blockers, the approved breaking API changes, and the stable-release policy, docs and CI. Merged only after the full battery passes on every wave.

Wave 1 benchmarks

Apple M4 Max, shared; rustc +1.99.0 (the pre-wave pairs and Tasks 4–5 ran on +1.98.0, the default then); baselines pre-wave (Task 10) and pre-d18 (= e7079b8); Δ = criterion median against the stored baseline, negative is faster; † = ABBA against a same-session build of the baseline (the reviewer's figure where one exists), used for every doubted row and where no stored pair exists.

pre-wave → Task 14 (rand 0.10 bump and seals; Task 14 = min of 7 runs of a bench binary byte-identical across the task, against the stored pre-wave median)

id (large / small) pre-wave Task 14 Δ reason
Normal/simd/f32/N=32 193.2 µs / 1.936 µs 198.8 µs / 2.003 µs +2.9 % / +3.5 % codegen-neutral: retired instructions equal (Task 12), runs of the identical binary agree within ±0.8 %; the pre-wave session was quieter (the pre-bump binary reads +0.5…+3.5 % against it)
Normal/simd/f32/N=64 198.4 µs / 1.99 µs 202.0 µs / 2.039 µs +1.8 % / +2.5 %
Normal/simd/f64/N=32 206.9 µs / 2.071 µs 211.7 µs / 2.124 µs +2.3 % / +2.6 %
Normal/simd/f64/N=64 210.7 µs / 2.109 µs 216.1 µs / 2.169 µs +2.6 % / +2.9 %
LogNormal/simd/f32 450.2 µs / 4.406 µs 451.5 µs / 4.525 µs +0.3 % / +2.7 %
LogNormal/simd/f64 641.8 µs / 6.417 µs 656.8 µs / 6.57 µs +2.3 % / +2.4 %
Poisson/simd 314.6 µs / 3.161 µs 311.0 µs / 3.11 µs −1.2 % / −1.6 %
Normal/rand_distr/f32 926.4 µs / 9.25 µs 491.3 µs / 4.9 µs −47.0 % / −47.0 % third-party reference row, not our code: rand 0.10's ThreadRng runs chacha20 instead of rand_chacha (Task 12)
Normal/rand_distr/f64 922.8 µs / 9.252 µs 490.9 µs / 4.914 µs −46.8 % / −46.9 % same
LogNormal/rand_distr/f32 1.667 ms / 16.64 µs 568.1 µs / 5.674 µs −65.9 % / −65.9 % same
LogNormal/rand_distr/f64 2.494 ms / 24.66 µs 1.159 ms / 11.6 µs −53.6 % / −53.0 % same
Poisson/rand_distr 4.124 ms / 41.3 µs 3.401 ms / 33.91 µs −17.5 % / −17.9 % same
dist_multicore Normal(ref) 2048², 1T / MT 8.30 ms / 0.85 ms 8.06 ms / 0.80 ms −2.9 % / −5.9 % MT: scheduler noise, jobs of 0.07–3.9 ms swing 20–50 % between runs of one binary; min of 5 against a single pre-wave run
dist_multicore Exp(ref) 2048², 1T / MT 6.58 ms / 0.70 ms 6.60 ms / 0.67 ms +0.3 % / −4.3 % MT: same noise
dist_multicore Poisson(ref) 2048², 1T / MT 9.05 ms / 1.14 ms 9.03 ms / 1.05 ms −0.2 % / −7.9 % MT: same noise
dist_multicore 18 other sample_matrix rows, 1T — — −3.5 … +3.0 % Exp 1024² −3.5 %: min of 5 against a single pre-wave run at 0.01 ms print resolution
dist_multicore 18 other sample_matrix rows, MT — — −15.3 … +11.3 % scheduler noise (Task 12: min of 7 spans −12.5 … +20.8 % both ways while the retired instructions are equal); the binary did not change in Task 14
dist_multicore Normal fill_slice n = 4 … 65 536 0.008 µs … 122.5 µs 0.008 µs … 122.6 µs −1.6 … +0.1 %

pre-d18 → Tasks 16–19 (D18 stateless distributions; large = 100 000 draws; scalar ÷ rand_distr on the same SimdRng, limit 1.3× for Normal and Exp and 1.5× for the rest; dist_multicore = ABBA minima from Task 17's final round (Tasks 16–17) and the reviewers' series (Tasks 18–19), MT swings 20–50 % so read it as ±15 %)

law (task) seeded/f32 seeded/f64 fill_slice/f64 scalar ÷ rand_distr dist_multicore 1T / MT reason
Normal (16) −8.6 % −12.5 % −11.5 %† 0.42× −16.9 % / −11.3 % faster: compile-time lane constants and ziggurat masks (reviewer ABBA −9 … −12 %); ratio is codegen-sensitive: rand_distr's ziggurat is out of line in this build, 1.00× where it inlines
Exp (17) −11.4 % −9.3 % −5.9 %† 0.93× −11.2 % / −10.7 % faster: compile-time lane constants (reviewer ABBA −8 … −14 %)
LogNormal (17) −10.6 % −6.3 % −6.2 %† 0.42× −6.0 % / −6.3 % faster: compile-time lane constants (reviewer ABBA −6 … −10 %)
Cauchy (17) −17.6 % +0.1 %† −3.3 %† 1.44× −5.3 % / −2.1 % f32 faster: compile-time lane constants; f64 at parity (reviewer ABBA); ratio is inherent: the inverse cdf tan(π(u−½)) against rand_distr's tan(πu), libm tan 7.45 vs 5.26 ns
Gamma (17) −16.4 % −12.7 % −12.1 %† 0.82× −13.6 % / −10.7 % faster: compile-time lane constants (reviewer ABBA −10 … −15 %)
Weibull (17) −10.6 % −7.2 % −3.0 %† 0.64× −2.5 % / −3.2 % faster: compile-time lane constants
Beta (17) −17.7 % −13.8 % −12.2 %† 0.34× −12.2 % / −10.6 % faster: compile-time lane constants
ChiSquared (17) −17.8 % −10.1 % −12.3 %† 0.81× −12.7 % / −14.5 % faster: compile-time lane constants
StudentT (17) −20.8 % −12.5 % −11.9 %† 0.76× −12.8 % / −11.1 % faster: compile-time lane constants
Pareto (17) −23.7 % −0.6 %† −1.3 %† 1.17× −3.7 % / −4.7 % f32 faster: the f32 loop inlines its math; f64 at parity (reviewer ABBA), the memset_pattern16 splats left in generic kernels go to Wave 3a
Uniform (17) −6.0 % −6.7 %† −0.3 %† 1.02× +8.0 % / −0.8 % 1T: ruled soundness cost: per-chunk zero pass, MaybeUninit sink in Wave 3a (final-tree ABBA +4.8 %); seeded faster: compile-time lane constants
AlphaStable (18) — −5.8 % −1.3 % no rand_distr law −0.8 % / −1.2 % faster: UnsafeCell state gone (ABBA pop −2.3 %, fill −1.5 %); honest draw 45.7 → 31.5 ns once the CMS constants B and S are stored at construction
NIG (18) — −16.5 % −12.0 % 0.25 … 0.60× −9.4 % / −9.4 % faster: UnsafeCell sub-objects gone (ABBA pop −17.4 %, reviewer −9 … −18 %); ratio codegen-sensitive: LLVM if-converts rand_distr's inverse-Gaussian acceptance test or leaves a mispredicting branch (~10 ns)
InverseGauss (18) — −16.5 %† −8.2 %† 0.18 … 0.77× −9.2 % / −8.1 % faster: UnsafeCell sub-objects gone; no pre-d18 id (group added in Task 18), † = ABBA pop and fill; ratio codegen-sensitive as for NIG
GIG (18) — −5.9 % −9.9 % no rand_distr law — faster: UnsafeCell sub-objects gone (ABBA pop −3.8 %, fill −8.0 %)
TemperedStable (18) — −9.6 % −8.0 % no rand_distr law — faster: same (ABBA pop −8.2 %, fill −7.9 %)
SkewT (18) — −13.5 % −11.8 % no rand_distr law — faster: same (ABBA pop −13.1 %, fill −11.3 %)
Poisson (19) — −12.7 %† −3.4 %† 0.12× +2.3 % / −3.4 % seeded: code placement, identical code reads −12.7 … +1.4 % across builds (ABBA); fill † = ABBA against the old fill (no pre-d18 id); kernel kept out of line (jump rows +28 … 34 % without); 1T final-tree ABBA +3.8 %, MT +13 % (0.3 ms job): already present before Task 20, its code untouched
Binomial (19) — −1.3 % −0.6 % 0.65× +2.5 % / +1.7 % 1T: final-tree ABBA +3.7 %, already present before Task 20 and its code untouched (placement-dominated)
Hypergeometric (19) — −2.5 % −2.6 % 0.06× +1.7 % / −26.2 % 1T: final-tree ABBA +5.8 %, already present before Task 20 and its code untouched (fill_parts out of line took it from +4.7 % to +1.1 % against the pre-Task-19 build); MT: 0.13–0.21 ms job, min −26.2 % / median −3.6 %: noise
Geometric (19) — — — — −7.1 % / −11.0 % no criterion group; faster through shared rng fill code: already −6.8 % at Task 17, before the port
Normal(ref) 2048² — — — — −12.1 % / −6.1 % faster: compile-time lane constants; MT min −6.1 % / median +11.0 %, noise
Exp(ref) 2048² — — — — −5.8 % / −5.5 % faster: compile-time lane constants
Poisson(ref) 2048² — — — — +2.2 % / −0.9 % 1T: final-tree ABBA +3.6 %, already present before Task 20 and its code untouched (placement-dominated)
VG, GH, JohnsonSu, GED, GEV, GPD (18) — −9.7 … +1.2 %† −10.5 … −2.2 %† — — no criterion group; ABBA pop and fill ns/draw (Task 18): all faster or inside the band, GEV pop +1.2 %
Skellam, Dirichlet, Wishart, NCX2, truncated ×4 (19) — — — — — no bench group; covered by the stream fixture (196/204) and the 132-process compare

pre-d18 → Task 20 (jump laws and the particle filter; jump rows = reviewer ABBA, median of 4 interleaved runs per variant)

id pre-d18 final Δ reason
jump_processes/merton/256 2.208 µs 1.776 µs −19.6 % faster: ziggurat SimdNormal replaces cdf-inversion ScalarNormal (Task 20 alone −14.7 % against the pre-Task-20 build, the rest is Tasks 16–19)
jump_processes/kou/256 2.205 µs 1.779 µs −19.3 % same (Task 20 alone −14.0 %)
jump_processes/levy_diffusion/256 2.198 µs 1.784 µs −18.8 % same (Task 20 alone −13.7 %)
jump_processes/merton/4096 26.187 µs 24.262 µs −7.3 % faster: same, a smaller share of the longer path since the jump count is about λ per path whatever n is (Task 20 alone −1.5 %)
jump_processes/kou/4096 26.267 µs 24.210 µs −7.8 % same (Task 20 alone −1.3 %)
jump_processes/levy_diffusion/4096 26.194 µs 24.139 µs −7.8 % same (Task 20 alone −1.9 %)
gaussian_random_walk_transition, d = 1 150.6 … 154.3 ns 11.2 … 11.8 ns ~13× faster faster: one scalar ziggurat draw from the filter's rng with the laws built once, instead of seeding a new SimdNormal stream per component per step; implementer ABBA, 3 rounds, min of 11 rounds of 200 000 calls
gaussian_random_walk_transition, d = 2 293.4 … 300.6 ns 13.65 … 13.73 ns ~21× faster same
gaussian_random_walk_transition, d = 8 1144 … 1175 ns 25.96 … 28.08 ns ~43× faster same
mle density_eval/Ou/Euler 4.30 … 4.40 ns 4.50 … 4.53 ns +3.8 % code placement: a 0.15 ns difference in a 4.5 ns function Task 20 does not touch (−2.4 % against the pre-Task-20 build)

Task 4 special functions and Task 5 ai_surrogate (before → after of the task; Task 4 before = 096541d, Task 5 before = candle 0.9.2)

id before after Δ reason
T4 ProcessGeneration/process/Cts.sample 2.985 ms 38.69 µs −98.7 % faster: scilib's Γ cost 0.5–1.2 ms per call, special::gamma is Lanczos
T4 ProcessGeneration/process/Cgmy.sample 580.5 µs 38.76 µs −93.3 % faster: same
T4 density_eval/Cir/Exact 1.335 µs (returned the 1e-30 floor) 29.05 ns −97.8 % faster and exact: ln_bessel_ie with the Hankel switch at max(50, ν²/2); the old 80-term series was wrong beyond z ≈ 100
T4 density_eval/Cir/transition_pdf 4.855 µs (NaN) 23.89 ns −99.5 % faster and exact: same
T4 density_eval/Cir/{Euler, Kessler, Ozaki, ShojiOzaki, Elerian} 4.84 / 10.68 / 16.90 / 14.01 / 15.12 ns 4.83 / 10.62 / 16.89 / 14.07 / 15.08 ns −0.6 … +0.4 % untouched rows
T4 special/gamma reflection, x < ½ (old (πx).sin() against sinpi) 12.94 … 13.07 ns 13.51 … 13.69 ns +4.4 … +4.8 % exact reduction of πx: +0.6 ns on x < ½ only (x ≥ ½ runs the same code), error next to the poles 2.9 % → ≤ 2.6e-14; CGMYFourier::price_call at y = 0.3 267.75 → 268.83 µs, +0.4 %
T4 special/bessel_ie series / temme / hankel / cir_daily / uniform / reflection / ln_uniform new function, no before (cir_daily: 1.691 µs before the Hankel switch) 20.39 / 127.5 / 6.57 / 12.42 / 28.98 / 328.8 / 27.36 ns — new bessel_i family, after only
T5 ai_surrogate/train_epoch/rows_2048 8.196 ms 6.286 ms −23.3 %† faster: candle 0.9.2 → 0.11.0, no regression on any row (5 alternating runs per binary, LTO off; criterion single run −21.8 %; the bench since builds its model outside the timed loop, 0.11 reference 6.245 ms)
T5 ai_surrogate/jacobian 6.671 ms 2.522 ms −62.2 %† faster: candle 0.9.2 → 0.11.0 (criterion single run −62.2 %)
T5 ai_surrogate/predict_surfaces/rows_1024 0.901 ms 0.748 ms −17.0 %† faster: candle 0.9.2 → 0.11.0 (criterion single run −16.7 %)

Wave 2 benchmarks

Apple M4 Max, shared; rustc +1.99.0; baselines pre-w2-t3, pre-w2-t8, pre-w2-t10 and pre-w2-t13, each saved before its task's change; Δ = criterion median against the stored baseline, negative is faster; a row outside ±3 % was re-measured ABBA (a1 old, b1 new, a2 old, b2 new build, interleaved; a regression only if both b runs exceed both a runs by more than 3 %) and its reason carries the four medians.

pre-w2-t3 → Task 3 (RoughSimd folded into SimdFloatExt; rl_rough, 7 groups, 32/35 rows within ±3 %; shown: the 3 rows outside and, per group, the in-band row with the largest |Δ|)

id pre-w2-t3 Task 3 Δ reason
RL_BlackScholes/H=0.3 22.092 µs 22.164 µs +0.32 %
RL_FOU/H=0.1 15.454 µs 15.394 µs −0.39 %
RL_Heston/H=0.12/256 4.731 µs 4.696 µs −0.74 %
RL_Heston_throughput/sample_par/16 50.932 µs 49.063 µs −3.67 % compare run faster, ABBA a1/b1/a2/b2 48.001 / 52.538 / 52.098 / 52.148 µs: b within +0.8 % / +0.1 % of a2 (a1 is the odd old run), no regression
RL_Heston_throughput/sample_par/256 241.650 µs 244.675 µs +1.25 %
RL_Heston_throughput/sample_par/1024 838.757 µs 786.694 µs −6.21 % baseline run slow, ABBA a1/b1/a2/b2 814.612 / 787.340 / 788.175 / 806.747 µs interleaved: no regression
RL_fBM_by_hurst/bilokon_wong/H=0.05 12.003 µs 12.048 µs +0.38 %
RL_fBM_by_size/bilokon_wong/1024 12.055 µs 12.038 µs −0.14 %
RL_fBM_throughput/sample_batch/64 979.330 µs 936.782 µs −4.34 % baseline run slow, ABBA a1/b1/a2/b2 929.450 / 944.511 / 950.450 / 945.214 µs interleaved: no regression
RL_fBM_throughput/sample_par/64 156.823 µs 153.315 µs −2.24 %

pre-w2-t8 → Task 8 (DistributionExt returns Option, inv_cdf is quantile; 7/7 rows within ±3 %; each row is 4096 calls of the method)

id pre-w2-t8 Task 8 Δ
DistributionExt/pdf/normal 8.190 µs 8.190 µs −0.00 %
DistributionExt/cdf/normal 36.112 µs 36.017 µs −0.26 %
DistributionExt/quantile/normal 49.525 µs 49.324 µs −0.41 %
DistributionExt/pdf/student_t 15.423 µs 15.451 µs +0.19 %
DistributionExt/pdf/johnson_su 30.376 µs 30.383 µs +0.02 %
DistributionExt/cdf/gamma 87.726 µs 87.790 µs +0.07 %
DistributionExt/pdf/poisson 61.340 µs 60.940 µs −0.65 %

cdf/gamma was re-measured at the wave head (4273e77), after Task 9 added two guards to gamma_p on its path (x = ∞, NaN shape): 89.205 µs, +1.69 % against pre-w2-t8, inside ±3 %.

pre-w2-t10 → Task 10 (a finiteness assert on every float parameter of every Simd*::new; sample_ncp below df = 1 builds a Poisson and a Gamma law per draw, the per-draw construction path; 2/2 rows within ±3 %)

id pre-w2-t10 Task 10 Δ
NonCentralChiSquared/sample_ncp/df=0.5/small (1 000 draws) 429.317 µs 427.974 µs −0.31 %
NonCentralChiSquared/sample_ncp/df=0.5/large (100 000 draws) 42.889 ms 42.916 ms +0.06 %

Task 10's re-review also ran ABBA, base 6198ba6 against head d32ff86, on the bessel_ke grid and the VG / GH / NIG / GIG negative log-likelihoods (the Bessel K path whose panics the task removed): every row within ±3 %, worst +0.76 % (no criterion files kept, so per-row medians are not listed).

pre-w2-t13 → Task 13 (NaN propagates through the production max and min reductions; bench targets filtering and mle (evt_fit); 4/6 rows within ±3 %; gev_200_maxima builds block_maxima outside the timed loop)

id pre-w2-t13 Task 13 Δ reason
particle_filter_n500_step 44.476 µs 44.009 µs −1.05 % in band after a fix: folding with max_or_nan measured +4.22 % (min b / max a of ABBA a1/b1/a2/b2 43.422 / 45.254 / 43.395 / 45.898 µs), so the fold keeps f64::max plus a separate NaN flag; fresh ABBA 43.463 / 43.094 / 43.395 / 43.415 µs (−0.85 % / +0.05 %)
metropolis_5k_after_burn1k 211.470 µs 220.353 µs +4.20 % code untouched; ABBA a1/b1/a2/b2 218.275 / 217.334 / 204.532 / 205.277 µs (−0.43 % / +0.36 %): noise
ukf_step_state2_obs1 0.7218 µs 0.7470 µs +3.50 % code untouched; ABBA a1/b1/a2/b2 0.6902 / 0.6928 / 0.6817 / 0.6961 µs (+0.38 % / +2.10 %): inside ±3 %
evt_fit/hill_20k_k500 192.607 µs 188.154 µs −2.31 %
evt_fit/pot_20k_u3 420.432 µs 419.894 µs −0.13 %
evt_fit/gev_200_maxima 422.641 µs 415.849 µs −1.61 %

Task 13 also moved the Malliavin–Thalmaier payoff and localization folds to max_or_nan / min_or_nan; no Thalmaier bench exists yet, so that path is unmeasured here — one is added when Wave 3e touches the engine.

Wave 3c benchmarks

Fresh, same-session measurements of every row of benches/copulas.rs at the tip against a git archive of the wave base c2b6480f (its library, Task 1's bench file and base-API ports of the other rows), then the per-task tables from the task reports. Of the 33 rows, 25 have a base figure and 8 are new. Against the base, 18 rows are faster by more than 3 % (the closed-form and Brent inverses, the NAC density, the fits), 2 sit inside ±3 % (clayton_n10k +2.6 %, vine_fit_d4_n1k +0.1 %), and 5 are slower: four multivariate cdf rows by design (a 4000-draw Monte Carlo sample becomes 4096 Sobol points per query) and bivariate_cdf/gaussian_16q by 11 % (accepted: 0.17 µs per 16 queries, the price of the relative accuracy of Task 16f). The two Gaussian rows routed to Wave 3a and the two rows blamed on code placement (fgm_n10k, clayton_cvm_n300_r10) did not reproduce as regressions in this session; their numbers are under Known regressions.

How the numbers were taken

  • Builds. The wave base is a git archive of c2b6480f with its own target directory. It predates benches/copulas.rs, so it carries Task 1's bench file (18007368) plus ports of the later rows to the base API. A row whose operation the base cannot run (Numerical or Unsupported) has no base figure and is marked new.
  • Runs. Pass 1 (every row, base and HEAD) ran at load 2–3 after a 17-minute wait for a quiet machine. The full ABBA (a1 b1 a2 b2) of every row ran at load 9–12, because the machine did not quieten again within 12 minutes. The undecided rows were re-measured in three more alternations at load 3–6.
  • Rule. A row is a regression only when both b runs exceed both a runs by more than 3 %. The figures are criterion's median point estimates, taken as the median of the run medians.

Wave level: every row of benches/copulas.rs, HEAD against the wave base c2b6480f

Medians in this session (criterion point estimates; the median of the run medians, 3 base runs against 3 or 4 HEAD runs (a fourth HEAD run on 15 rows, which changes no verdict), and 6 for the re-measured rows). "change" is HEAD over base. A row marked new has no base figure.

row base c2b6480f HEAD change ABBA verdict and reason
bivariate_sample/clayton_n10k 168.58 µs 172.94 µs +2.6 % inside the rule (+2.6 % … +3.1 %, quiet pairs); same algorithm, at the edge of the band: 168.0–168.6 against 173.0–173.2 µs in the three quiet pairs; −1.5 … −2.3 % against eb8bc651 and ±0.1 % against ba901c9a (Task 2b's own ABBA read +2.17 % for the row)
bivariate_sample/frank_n2k — 17.52 µs new new row: θ = 4 by the closed form (Task 2b); the base's Frank sampler failed at θ ≥ 2 for n = 2000, and its own row (θ = 1, Brent, below) is the only base figure
bivariate_sample/gaussian_n10k 483.62 µs 462.87 µs -4.3 % inside the rule (-10.4 % … -2.6 %, quiet pairs); same algorithm; the base side is the noisier (480–508 µs)
bivariate_sample/gumbel_brent_n2k 11.912 ms 1.550 ms -87.0 % faster (-90.5 % … -86.9 %); by design: Brent on the stable ln h (Tasks 2b, 16c)
bivariate_sample/bb1_brent_n2k 9.879 ms 2.588 ms -73.8 % faster (-74.1 % … -73.0 %); by design: Brent on ln h over the whole interval (Task 16c); the base's inverse was a bisection
bivariate_sample/bb7_brent_n2k 9.946 ms 3.281 ms -67.0 % faster (-67.7 % … -66.0 %); by design: as BB1
bivariate_sample/marshall_olkin_n10k — 132.47 µs new new row: the base sampler answered Numerical for most n (Task 2)
bivariate_sample/amh_n10k 8.218 ms 32.80 µs -99.6 % faster (-99.7 % … -99.6 %); by design: a closed-form inverse (a cancellation-free quadratic root, Task 2b) replaces the default Brent inversion of partial_derivative
bivariate_sample/fgm_n10k 7.811 ms 25.05 µs -99.7 % faster (-99.8 % … -99.7 %); by design: as AMH, a closed-form quadratic root (Task 2b)
bivariate_sample/plackett_n10k — 32.44 µs new new row: the base sampler answered Numerical at θ = 3
bivariate_sample/t_copula_n256 6.997 ms 244.42 µs -96.5 % faster (-96.6 % … -96.4 %); by design: closed-form h-inverse (Task 2b: 28×)
multivariate_sample/gaussian_d5_n10k 371.27 µs 329.50 µs -11.3 % faster (-16.6 % … -3.5 %, quiet pairs); same algorithm; the row varies by up to 23 % between runs of one binary (base 336–415 µs); no sign of the erfc regression here, see Known regressions
multivariate_sample/t_d5_n10k 4.792 ms 4.517 ms -5.7 % faster (-5.1 % … -4.5 %, quiet pairs); same algorithm; faster by 4.5–5 % in every quiet pair
multivariate_sample/dvine_d4_n10k 5.007 ms 3.381 ms -32.5 % faster (-33.3 % … -29.9 %); faster by a third: the sampler is Aas et al.'s Algorithm 2 (Task 11b: 5.21 → 3.49 ms)
multivariate_sample/nac_clayton_nested_d4_n10k — 6.404 ms new new row: the base could not sample a nested Clayton (Task 13c)
bivariate_cdf/gaussian_16q 1.55 µs 1.72 µs +11.2 % regression (+10.9 % … +11.4 %, quiet pairs); regression, accepted: +0.17 µs for 16 queries (≈ 10 ns each); the cdf holds 1e-12 relative at every ρ and in both tails since Task 16f, and the S/C bookkeeping that buys it was accepted after B3's fix round 1 (+7 % over the absolute-accuracy form: 1.61 → 1.72 µs)
bivariate_cdf/t_16q 670.25 µs 12.34 µs -98.2 % faster (-98.4 % … -98.1 %); by design: Dunnett–Sobel's sums at integer ν and the relative-accuracy march elsewhere replace the base's 64-node Gauss–Legendre h-integral (Task 15: 39.5 µs → 0.94 µs per evaluation at ν = 4)
multivariate_cdf/gaussian_d3_16q 296.48 µs 5.770 ms +1846 % regression (+1792 % … +1905 %); by design: Genz's separation of variables over 4096 Sobol points per query replaces one 4000-draw Monte Carlo sample shared by the 16 queries (Task 15)
multivariate_cdf/t_d3_16q 381.33 µs 7.769 ms +1937 % regression (+1930 % … +1973 %); by design: as the Gaussian row, plus 4096 χ² quantiles per call (Task 15)
multivariate_cdf/dvine_d3_16q 1.551 ms 15.513 ms +900.2 % regression (+880.5 % … +957.4 %); by design: 4096 Sobol points × 16 queries through the inverse-Rosenblatt integrand, O(N·d²) per query (Task 16)
multivariate_cdf/cvine_d4_16q 62.997 ms 196.674 ms +212.2 % regression (+207.6 % … +219.7 %); by design: as the D-vine, with BB1 h_inverse per integrand evaluation (Task 16); base: the Monte Carlo cdf
multivariate_pdf/nac_pdf_d4_n1k 2.467 ms 332.93 µs -86.5 % faster (-88.5 % … -86.5 %); by design: Hofert & Pham's closed form replaces 2^d finite differences of the cdf (Task 13b)
multivariate_pdf/nac_clayton_pdf_d4_n1k — 180.54 µs new new row: the base could not sample a nested Clayton to feed it (Task 13c)
fit/clayton_fit_n2k 82.44 µs 51.97 µs -37.0 % faster (-38.0 % … -36.4 %); faster: the Kolmogorov–Smirnov check no longer runs in fit (Task 7)
fit/plackett_fit_n2k — 59.38 µs new new row: the base sampler cannot draw the data; Task 7's ABBA against 578125b6 measured the Spearman-proxy fit at 83 µs and the exact-τ fit at 1.43 ms, since cut to the series and closed forms of Task 16e
fit/frank_fit_n2k — 52.25 µs new new row: the base sampler cannot draw θ = 4 data (Task 2b)
fit/galambos_fit_n2k 730.36 µs 190.73 µs -73.9 % faster (-73.9 % … -73.3 %); faster: Galambos' τ to 1e-12 by the quadrature of Task 16e
fit/vine_fit_d4_n1k 840.526 ms 841.201 ms +0.1 % inside the rule (-0.2 % … +0.5 %, quiet pairs); same input: the base fits HEAD's stream (sample_with_seed(1000, Deterministic::new(1).next_seed()), Task 4's control); no change
fit/kendall_tau_d4_n1k 9.425 ms 204.98 µs -97.8 % faster (-98.2 % … -97.7 %); by design: Knight's τ-b, O(n log n), against the O(n²) τ-a (Task 11)
fit/gaussian_fit_d4_n1k 84.18 µs 76.24 µs -9.4 % faster (-10.8 % … -9.0 %, quiet pairs); faster; no sign of the erfc regression here, see Known regressions
fit/t_fit_d4_n1k 79.264 ms 57.946 ms -26.9 % faster (-42.5 % … -24.3 %); faster: one Kendall matrix per fit (Task 11) and Brent's localmin for ν (Task 16b-i)
fit/nac_fit_d4_n2k — 551.43 µs new new row: the base's NAC fit was Unsupported (Task 13)
gof/clayton_cvm_n300_r10 1.107 ms 1.065 ms -3.8 % faster (-7.0 % … -4.3 %, quiet pairs); same algorithm; see the placement note

Base-only row from Task 1, measured in this session: bivariate_sample/frank_brent_n2k (θ = 1, Brent) 6.36 ms (base1), 7.18 ms (a1), 7.00 ms (a2); HEAD's closed form at θ = 1 takes 14.2 µs (Task 2b), and at θ ≥ 2 the base failed.

The per-row ABBA runs (a1 b1 a2 b2) behind every verdict above are kept with the wave's evidence.

Known regressions

  • multivariate_sample/gaussian_d5_n10k and fit/gaussian_fit_d4_n1k: routed to Wave 3a; not reproduced in this session. Block B3 measured +36–41 % and +11.7 % against its base 9fa7020c (B2 had seen +10–12 % on the first row) and traced both to fat LTO no longer inlining libm's erfc into the callers of distributions' norm_cdf (nm: the base keeps libm::erf::erfc2 out of line and HEAD folds it into erfc; /usr/bin/sample: the base inlines erfc into the sampling and fit closures, HEAD calls it out of line, 2673 and 1610 samples; no source change on the path). Rebuilt here from a git archive of 9fa7020c, the two rows read −13.8 / −5.0 / −6.2 % and −2.4 / −0.4 / −2.6 % at the tip in three alternated pairs. Against the ruling's acceptance baseline eb8bc651 they read −14.4 / −5.5 / +4.1 % (best of three −1.1 %) and −4.5 / −3.4 / −2.7 %; against the wave base −11.9 / −8.7 / −11.4 % and −9.7 / −9.5 / −10.1 % in the quiet pairs; against ba901c9a −6.8 / −7.8 / −1.9 % and +1.0 / 0.0 / +0.1 %. nm of the five bench binaries (wave base, eb8bc651, 9fa7020c, ba901c9a, tip) lists erfc and erfc2 as separate out-of-line symbols in all five, and gaussian_d5_n10k varies by up to 23 % between runs of one binary (the base alone ran 336–415 µs), so I cannot reproduce the code-generation difference B3 measured. B3's cause was not observed in this session, so Wave 3a re-measures both rows before it changes anything; this build already meets the acceptance (both rows within ±3 % of eb8bc651 by ABBA).
  • fit/vine_fit_d4_n1k (+20.7 %) and bivariate_sample/plackett_n10k (+6 %), B3's two other regressions: fixed in its fix round 1. The BB1, BB7 and Student-t log-densities form their per-call logarithms once (f69cc09d) and the samplers invert their own uniforms through an unchecked inverse (856d8300). At the tip against 9fa7020c: the vine fit +1.3 / +0.2 / +1.6 % (B3's fix round read +1.6 % the same way) and Plackett −1.5 / −3.3 / −3.1 %; against the wave base the vine fit is +0.1 % on the same input.
  • gof/clayton_cvm_n300_r10: code placement, re-measured, not a regression now. Tasks 4 and 12 saw +5 … +7 % with the loop's source unchanged (note below). Here: −4.6 / −4.6 / −6.7 % against the base in the quiet pairs, −3.0 / −4.6 / −3.4 % against eb8bc651, −0.2 / −3.7 / −0.1 % against 9fa7020c, −3.2 / −2.8 / −1.7 % against ba901c9a; the one +7.2 % (b1 against a1) was taken at load 9–12.
  • bivariate_sample/fgm_n10k: code placement, re-measured, not a regression now. B3's last report saw +7–9 % in ordinary builds against ba901c9a (22.9 against 24.6–25.3 µs; level in builds with every function aligned to 128 bytes and every loop to 64, the loop being the base's instruction for instruction). Here: −0.1 / +0.3 / +1.3 % against ba901c9a (best of three +0.7 %, 22.34 against 22.50 µs) and +7.2 / −6.6 / −7.6 % against eb8bc651. The one row that reads over 2 % against ba901c9a is marshall_olkin_n10k (+4.1 / +2.1 / +4.0 %, with min b under max a, so inside the rule; −0.1 / −3.3 / −1.6 % against eb8bc651).
  • bivariate_cdf/gaussian_16q: +11 % against the base, accepted. 1.54 → 1.72 µs for 16 queries, every pair over 7 %; the cdf holds 1e-12 relative accuracy at every ρ and in both tails since Task 16f, and the S/C bookkeeping that buys it was accepted after B3's fix round 1 (+7 % over the absolute-accuracy form: 1.61 → 1.72 µs against 9fa7020c).
  • multivariate_cdf/cvine_d4_16q: a further +10 % in cycles in the final fix, accepted. The BB1 pair's h_inverse now runs the bivariate family's ln h quantile, one inverse per family (+23.4 % instructions, +10.1 % cycles against a164020b).
  • amh_n10k +8.6 %, fgm_n10k +14.1 %, plackett_n10k +8.7 % and frank_n2k +3.8 % in cycles: the open-cube rule, accepted. The cost is one pass that moves an exact 0 or 1 one ulp inside, about 6 instructions a draw on these 38–44-instruction samplers. AMH and FGM stay over 200× faster than at the wave base.
  • By design, not regressions: multivariate_cdf/gaussian_d3_16q +1846 %, t_d3_16q +1937 %, dvine_d3_16q +900 %, cvine_d4_16q +212 % (Tasks 15 and 16: Genz and Sobol QMC replace a 4000-draw Monte Carlo sample). Every other row outside ±3 % is faster.

The clayton_cvm_n300_r10 placement note

The row calls gof_cramer_von_mises, whose bootstrap loop (rosenblatt, cramer_von_mises_independence, pseudo_observations, Clayton::fit) is the same functions at the base and at the tip apart from the seed argument (Task 4) and a different bootstrap draw (Task 5). Two earlier measurements found it 5 % slower with no source on the path to blame, and both traced it to where the linker put the code: Task 4's ABBA read +4.75 … +5.37 %, a hand-rolled copy of the old loop ran 5.1 % slower in the new binary and the new gof only 1.6 % over base; Task 12 read +4.9 … +6.7 %, and an address-normalised disassembly diff showed the loop and 11 of its 12 callees identical, pseudo_observations calling an out-of-line ndarray constructor, three hot functions moved to a different 64-byte alignment, and a variant with all 12 callees identical to the base still +7 %. B3 saw +3.8 %. In this session the row is under the base in every quiet pair (1.06 ms against 1.11–1.14 ms), under eb8bc651 (1.04–1.06 against 1.07–1.09) and under ba901c9a (1.01–1.02 against 1.03–1.05), and the lone +7.2 % is a load-9–12 run: the placement moved with later commits and the row is inside ±3 % today. It would move again with any unrelated change; no bench row or test depends on it.

Final fix rounds (a164020b → 8f2aba11)

The final review's fixes, and the residual round its re-review asked for:

  • The Gaussian h no longer clamps v to [1e-12, 1 − 1e-12].
    • Both coordinates go through normal_quantile, with the point-mass law at v ∈ {0, 1}.
    • x − ρy is formed directly where its rounding bound allows, and from the refined quantile gap past it.
    • So h, its inverse and ln c hold 1e-12 on both sides of the switch: relative for h and its inverse, 1e-12·max(1, |ln c|) for ln c.
    • Rows at 80-digit mpmath check it: 36 h/q rows and 15 gap cells.
  • FGM forms the smaller of 1 ± a as (1 − |θ|) + 2|θ| min(v, 1 − v), so it holds 1e-12 near θ = ±1.
  • No 1e-12 clamp is left in the elliptical transforms, the vine pairs or the samplers.
    • At d = 2, every multivariate density equals its bivariate family to 1e-12.
    • Every copula draw lies in the open unit cube: a coordinate that rounds to exactly 0 or 1 moves one ulp inside.
  • Smaller changes:
    • a column count other than 2 is DimensionMismatch (a ValueError in Python);
    • the vines validate every pair through its family's try_new;
    • partial_derivative is a required method;
    • pickands answers NaN outside [0, 1];
    • the t fit takes sin(πτ/2) uncapped;
    • the Marshall–Olkin h is formed in logarithms at β near 1 and a subnormal v.

No stream pin moved.

How it was measured. The machine was loaded throughout (load 8–65) and never quietened. So the counts that load does not move decide: instructions retired and cycles per row, the minimum of three alternations.

Round 1 ran every row against a git archive of a164020b, also in ABBA. Two of its fixes first cost more:

  • the exact-gap h doubled the D-vine rows;
  • the t fit read +13 % in cycles.

The round's three perf commits brought both back inside the band. These rows ended outside ±3 % in either count:

row instructions cycles cause
multivariate_cdf/cvine_d4_16q +23.4 % +10.1 % the BB1 pair's h_inverse runs the bivariate family's ln h quantile (one inverse per family)
bivariate_sample/fgm_n10k +10.1 % +3.6 % FGM's slope fix
bivariate_sample/gaussian_n10k +1.9 % −5.3 % normal_quantile's subnormal test
multivariate_cdf/dvine_d3_16q +1.7 % −3.5 % the Gaussian h's plain path
fit/kendall_tau_d4_n1k −0.0 % +14.7 % code unchanged, same instructions: placement (+3 to +4 % in two more cycle runs)
fit/nac_fit_d4_n2k −0.4 % +6.8 % placement (+4 % in a second cycle run)
fit/clayton_fit_n2k +0.0 % +3.5 % same instructions: placement
gof/clayton_cvm_n300_r10 −0.0 % −5.6 %

The residual round, against 0f4bbd3b, ended with these rows outside ±3 % in cycles:

row instructions cycles cause
bivariate_sample/fgm_n10k +18.3 % +14.1 % the open-cube pass, about 6 instructions a draw on a 38-instruction sampler
bivariate_sample/plackett_n10k +13.6 % +8.7 % the open-cube pass
bivariate_sample/amh_n10k +13.6 % +8.6 % the open-cube pass, on a 44-instruction sampler
bivariate_sample/frank_n2k +4.2 % +3.8 % the open-cube pass
fit/kendall_tau_d4_n1k −0.0 % +8.0 % same instructions, code unchanged: placement (−4.4 % on a rerun)
gof/clayton_cvm_n300_r10 +0.1 % +8.0 % placement (+7.0 % on a rerun), the wave's documented placement row
fit/frank_fit_n2k +0.0 % +4.6 % same instructions: placement (+1.6 % on a rerun)
fit/plackett_fit_n2k +0.0 % +3.8 % same instructions: placement (+0.8 % on a rerun)

Every other row is within ±3 % in cycles.

Per task: the medians each task recorded against its own baseline

Criterion medians as the task reports recorded them (µs unless marked). Every number below is transcribed from the task's own report; the raw criterion data of the earlier tasks was not kept. A row outside ±3 % carries its accepted reason; "ABBA" is the a1 b1 a2 b2 recipe against a git archive export with its own target dir.

pre-w3c-base (Task 1, the 13 rows as found; the three fit rows re-saved at the 9 s window) and Task 2's new row

row median
bivariate_sample/clayton_n10k 172.25
bivariate_sample/frank_brent_n2k (θ = 1, Brent) 6360.6
bivariate_sample/gaussian_n10k 464.58
multivariate_sample/gaussian_d5_n10k 412.22
multivariate_sample/t_d5_n10k 4862.1
multivariate_sample/dvine_d4_n10k 5031.2
multivariate_cdf/gaussian_d3_16q 320.66
multivariate_cdf/t_d3_16q 385.40
multivariate_cdf/dvine_d3_16q 1586.3
fit/clayton_fit_n2k 82.85 (first capture 80.65)
fit/vine_fit_d4_n1k 438.90 ms (first capture 426.78 ms)
fit/kendall_tau_d4_n1k 9.8729 ms (first capture 9.6173 ms)
gof/clayton_cvm_n300_r10 1073.7
bivariate_sample/marshall_olkin_n10k (new, Task 2) 122.79

Task 2's three untouched rows read +0.85 % / +0.29 % / +1.39 % (clayton, frank, gaussian) against this baseline.

pre-w3c-t2b (Task 2b, closed-form conditional inverses; ABBA medians a1 b1 a2 b2)

row a1 b1 a2 b2 verdict
clayton_n10k 172.45 176.36 172.23 176.19 +2.17 %, within ±3 %
gaussian_n10k 453.31 460.81 463.67 470.43 −0.62 %
marshall_olkin_n10k 138.63 125.79 123.30 123.34 −11.0 %, by design (closed-form inverse)
gumbel_brent_n2k 12073 7850 12135 7773 −36.0 %, by design (Brent on ln h)
t_copula_n256 7050 248.8 7064 248.3 −96.5 % (28×), by design (closed-form h-inverse)
clayton_cvm_n300_r10 1098 1158 1096 1156 +5.27 %, code placement (the gof loop is unchanged)

Frank at θ = 4 has no "before" (every seed failed at n = 2000); after: 18.0–18.3 µs. The old θ = 1 Brent row took 6533/6567 µs against 14.2 µs for the closed form (about 460×).

pre-w3c-t3 (Task 3, ArrayView inputs)

row before after change
bivariate_sample/amh_n10k 33.702 32.887 −2.42 %
bivariate_sample/clayton_n10k 180.430 180.022 −0.23 %
bivariate_sample/fgm_n10k 25.705 25.800 +0.37 %
bivariate_sample/frank_n2k 18.354 18.333 −0.11 %
bivariate_sample/gaussian_n10k 458.534 476.884 +4.00 %, ABBA +2.28 … +4.39 % (mean +3.33 %), second round mean +2.55 %: not both b over both a by 3 %
bivariate_sample/gumbel_brent_n2k 3554.4 3588.5 +0.96 %
bivariate_sample/marshall_olkin_n10k 127.727 126.125 −1.25 %
bivariate_sample/plackett_n10k 33.889 33.838 −0.15 %
bivariate_sample/t_copula_n256 248.496 248.463 −0.01 %
multivariate_sample/gaussian_d5_n10k 459.251 457.100 −0.47 %
multivariate_sample/t_d5_n10k 4843.4 4857.1 +0.28 %
multivariate_sample/dvine_d4_n10k 5020.5 5000.5 −0.40 %
multivariate_cdf/gaussian_d3_16q 315.768 315.128 −0.20 %
multivariate_cdf/t_d3_16q 384.346 377.998 −1.65 %
multivariate_cdf/dvine_d3_16q 1575.0 1570.9 −0.26 %
fit/clayton_fit_n2k 81.273 81.425 +0.19 %
fit/vine_fit_d4_n1k 427.73 ms 427.48 ms −0.06 %
fit/kendall_tau_d4_n1k 9673.8 9692.4 +0.19 %
gof/clayton_cvm_n300_r10 1165.7 1123.7 −3.60 %, ABBA −3.36 … −2.09 %: faster

pre-w3c-t4 (Task 4, every draw takes &seed)

row before after change
bivariate_sample/amh_n10k 33.338 32.873 −1.39 %
bivariate_sample/clayton_n10k 179.480 180.308 +0.46 %
bivariate_sample/fgm_n10k 25.845 25.713 −0.51 %
bivariate_sample/frank_n2k 18.291 18.292 +0.00 %
bivariate_sample/gaussian_n10k 488.896 483.561 −1.09 %
bivariate_sample/gumbel_brent_n2k 3601.2 3617.1 +0.44 %
bivariate_sample/marshall_olkin_n10k 129.026 131.768 +2.13 %
bivariate_sample/plackett_n10k 33.894 33.729 −0.48 %
bivariate_sample/t_copula_n256 248.316 248.910 +0.24 %
fit/clayton_fit_n2k 81.842 80.683 −1.42 %
fit/kendall_tau_d4_n1k 9675.8 9554.2 −1.26 %
fit/vine_fit_d4_n1k 427.63 ms 857.19 ms +100.45 %: the input moved, not the code. The D-vine stream is a declared mover, so the 1000 × 4 data differ and the fit now selects BB1/BB7 edges; same-input control: 425.6 / 429.6 ms (base) against 425.6 / 426.8 ms (task) on the old data, 850.7 / 856.5 against 849.3 / 857.4 on the new
gof/clayton_cvm_n300_r10 1132.0 1181.4 +4.37 %, ABBA +4.75 … +5.37 %: no cause in the code (the bootstrap loop is the same functions with a different seed argument); a hand-rolled copy of the old loop runs 5.1 % slower in the new binary, the new gof 1.6 % over base: code placement
multivariate_cdf/dvine_d3_16q 1579.0 1560.6 −1.17 %
multivariate_cdf/gaussian_d3_16q 314.978 315.505 +0.17 %
multivariate_cdf/t_d3_16q 380.625 379.330 −0.34 %
multivariate_sample/dvine_d4_n10k 5015.5 5083.5 +1.36 %
multivariate_sample/gaussian_d5_n10k 467.131 462.424 −1.01 %
multivariate_sample/t_d5_n10k 4870.5 4834.1 −0.75 %

pre-w3c-t5 (Task 5, the goodness-of-fit test; tip of the fix round against the baseline)

row before after change
bivariate_sample/amh_n10k 33.031 32.851 −0.55 %
bivariate_sample/clayton_n10k 180.420 179.948 −0.26 %
bivariate_sample/fgm_n10k 25.744 25.758 +0.05 %
bivariate_sample/frank_n2k 18.338 18.370 +0.17 %
bivariate_sample/gaussian_n10k 477.697 468.797 −1.86 %
bivariate_sample/gumbel_brent_n2k 3584.5 3570.9 −0.38 %
bivariate_sample/marshall_olkin_n10k 130.252 130.890 +0.49 %
bivariate_sample/plackett_n10k 33.942 33.878 −0.19 %
bivariate_sample/t_copula_n256 248.182 248.565 +0.15 %
fit/clayton_fit_n2k 81.180 80.699 −0.59 %
fit/kendall_tau_d4_n1k 9562.4 9562.4 −0.00 %
fit/vine_fit_d4_n1k 857.01 ms 858.66 ms +0.19 %
gof/clayton_cvm_n300_r10 1178.7 1099.8 −6.70 % (faster; the two check_marginal calls cost 3.99 µs per call)
multivariate_cdf/dvine_d3_16q 1561.5 1565.9 +0.28 %
multivariate_cdf/gaussian_d3_16q 317.394 317.925 +0.17 %
multivariate_cdf/t_d3_16q 381.132 382.445 +0.34 %
multivariate_sample/dvine_d4_n10k 5078.1 5098.0 +0.39 %
multivariate_sample/gaussian_d5_n10k 459.082 455.775 −0.72 %
multivariate_sample/t_d5_n10k 4835.0 4828.6 −0.13 %

pre-w3c-t7 (Task 7, τ helpers and fits that reject τ outside the family; copulas/fit)

row before after change
fit/clayton_fit_n2k 80.75 52.40 −34.9 %, faster (the Kolmogorov–Smirnov check no longer runs)
fit/plackett_fit_n2k none 1436 new row; ABBA against base: 83.06 / 83.64 → 1433 / 1421, ×17, by design (base inverted a Spearman proxy 0.15–0.26 off in τ; the exact τ is a 200-panel quadrature per Brent step)
fit/frank_fit_n2k none 52.59 new row; ABBA −37.7 … −36.0 %
fit/galambos_fit_n2k none 435.2 new row; ABBA −13.4 … −11.5 %
fit/vine_fit_d4_n1k 857.8 ms 830.8 ms −3.1 % (ABBA −6.3 … −5.8 %)
fit/kendall_tau_d4_n1k 9545 9570 +0.35 % (ABBA −0.7 … +0.2 %)

pre-w3c-t9 (Task 9, constructors own the parameter invariants)

The plain compare read every row +18 … +147 % at load 65–94 (another session ran cargo test in the same checkout, a VM held 4–6 cores), so the table is the ABBA: "tight" is the best base median against the best task median over 5–15 alternated pairs (or the best of the two full-run pairs).

row pre-w3c-t9 tight: best base vs best task
bivariate_sample/clayton_n10k 181.7 181.3 vs 181.5 (+0.1 %)
bivariate_sample/frank_n2k 18.46 18.50 vs 18.31 (−1.0 %)
bivariate_sample/gaussian_n10k 484.1 475.6 vs 498.2 (+4.7 %; median pair ratio +2.4 % over 15 pairs; the per-element source is byte-for-byte unchanged, code layout)
bivariate_sample/gumbel_brent_n2k 3602 3608 vs 3471 (−3.8 %)
bivariate_sample/marshall_olkin_n10k 132.1 129.5 vs 128.6 (−0.7 %)
bivariate_sample/amh_n10k 33.69 33.01 vs 33.16 (+0.5 %)
bivariate_sample/fgm_n10k 26.47 26.04 vs 25.89 (−0.6 %)
bivariate_sample/plackett_n10k 34.72 33.86 vs 33.90 (+0.1 %)
bivariate_sample/t_copula_n256 252.6 248.6 vs 248.7 (+0.1 %)
multivariate_sample/gaussian_d5_n10k 490.7 472.3 vs 467.5 (−1.0 %)
multivariate_sample/t_d5_n10k 4897 best of 2: 4958 vs 5038 (+1.6 %)
multivariate_sample/dvine_d4_n10k 5168 5162 vs 5190 (+0.5 %)
multivariate_cdf/gaussian_d3_16q 318.9 best of 2: −1.5 %
multivariate_cdf/t_d3_16q 380.7 best of 2: −4.6 %
multivariate_cdf/dvine_d3_16q 1563 best of 2: −0.2 %
fit/clayton_fit_n2k 51.80 best of 2: +0.0 %
fit/plackett_fit_n2k 1425 best of 2: −0.1 %
fit/frank_fit_n2k 52.80 best of 2: −14.8 % (a-side noise; b = pre −0.9 %)
fit/galambos_fit_n2k 431.2 best of 2: −5.0 % (b = pre −0.3 %)
fit/vine_fit_d4_n1k 804.9 ms best of 2: +0.4 %
fit/kendall_tau_d4_n1k 9516 best of 2: −27.3 % (a-side noise; b = pre +0.8 %)
gof/clayton_cvm_n300_r10 1068 best of 2: −14.2 % (b = pre −3.6 %)

The ABBA rule flags no row.

pre-w3c-t11 (Task 11, τ-b and one pseudo_observations; final code, load 10–12, ABBA a3 b3 a4 b4)

row a3 old b3 new a4 old b4 new new / old verdict
bivariate_sample/amh_n10k (control) 33.25 33.05 33.20 32.99 0.994 noise
multivariate_cdf/gaussian_d3_16q (control) 309.85 306.58 308.10 303.71 0.988 noise
fit/clayton_fit_n2k 51.69 52.58 51.86 52.63 1.016 within +3 %
fit/frank_fit_n2k 58.30 53.12 52.51 54.99 0.976 noise
fit/galambos_fit_n2k 440.88 429.69 428.76 439.32 0.999 noise
fit/plackett_fit_n2k 1428 1420 1422 1422 0.997 noise
fit/gaussian_fit_d4_n1k (code untouched) 83.17 84.10 84.18 88.27 1.030 noise
fit/kendall_tau_d4_n1k 9795 184.9 9516 185.8 0.019 53× faster, by design: τ-a counted all 499 500 pairs of each of the 6 column pairs, Knight's τ-b sorts and merges (O(n log n))
fit/t_fit_d4_n1k 81816 72476 81499 72122 0.885 faster (one Kendall matrix per fit)
fit/vine_fit_d4_n1k 811668 799054 805668 806001 0.992 within noise
gof/clayton_cvm_n300_r10 1099 1033 1093 1022 0.938 faster

pre-w3c-t12 (Task 12, vines carry their variable order and fit selects structure and families; ABBA)

row a1 b1 a2 b2 verdict
fit/vine_fit_d4_n1k 791.05 ms 842.09 797.12 845.09 +5.6 … +6.8 %, by design: fit_vine now tries every candidate family per edge (Student-t profile 13–17 ms, BB1 ≈ 10 ms, BB7 20–240 ms per edge at n = 1000)
multivariate_sample/dvine_d4_n10k 3425 3424 3445 3421 none
multivariate_sample/gaussian_d5_n10k 439.8 439.0 454.1 443.2 none
multivariate_sample/t_d5_n10k 4634 4652 4668 4655 none
bivariate_sample/marshall_olkin_n10k 125.7 127.3 126.6 129.0 none
gof/clayton_cvm_n300_r10 1024.5 1092.6 1041.3 1093.3 +4.9 … +6.7 %: code placement. An address-normalised disassembly diff shows the loop and 11 of 12 callees identical; three hot functions changed their 64-byte alignment, and a variant with all 12 callees identical to base is still +7 %

pre-w3c-t15 (Task 15, deterministic Genz–Bretz cdfs and exact bivariate t cdf; copulas/multivariate_cdf)

row before after change
multivariate_cdf/gaussian_d3_16q 303.70 5462.8 +1699 %, by design: one 4000-draw Monte Carlo sample shared by 16 queries is replaced by 16 × 4096 Sobol integrand evaluations (2 Φ⁻¹ and 2 Φ per point at d = 3)
multivariate_cdf/t_d3_16q 376.26 8853.5 +2242 %, by design: the same, plus 4096 χ² quantiles per call
multivariate_cdf/dvine_d3_16q 1008.4 1010.8 +0.44 %

No bench row covers TCopula::cdf; per evaluation on an export of the base: ν = 4 39.5 µs → 0.94 µs, ν = 4.5 40.3 → 5.5, ν = 31 44.1 → 1.09.

pre-w3c-t16 (Task 16, vine cdfs by Sobol-driven inverse Rosenblatt; ABBA)

row a1 b1 a2 b2 verdict
multivariate_cdf/dvine_d3_16q 1003 16870 1225 15740 by design: 4096 Sobol points × 16 queries through the Rosenblatt integrand, O(N·d²) per query, instead of one shared 4000-draw sample
multivariate_cdf/cvine_d4_16q (new row) 62910 753300 64010 714600 by design: BB1 h_inverse per integrand evaluation
multivariate_sample/dvine_d4_n10k 3400 4105 3620 3457 within 3 % after re-measure (a3 … b5: 3487 / 3419, 3436 / 3377, 3436 / 3784)
bivariate_sample/gaussian_n10k 450.7 540.2 470.1 484.8 re-measure 463.9 / 484.9 / 463.4 / 454.2 / 451.6 / 477.7: none
bivariate_sample/marshall_olkin_n10k 128.5 144.1 129.4 135.4 re-measure at 12 s: 128.6 / 131.9 / 127.4 / 131.1 / 129.3 / 145.8 (b8 at a load spike): none
multivariate_sample/gaussian_d5_n10k 300.4 512 361.5 447.6 re-measure 340.5 / 341.8 / 310.8 / 349 / 304.2 / 340.2: none

@vercel

vercel Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
stochastic-rs Ready Ready Preview Oct 26, 2026 1:52pm UTC

Request Review

…gits, and so do the closed-form and gamma quantiles in both tails
…branches, no ziggurat point mass at hz = MIN
…r, and the GED, gamma, Cauchy and Pareto quantiles keep their tails
…ver, and the GEV moments stay finite past the Γ values' overflow
…al, and the rule test states what its pairs bracket
… takes the log path where the plain one cancels or underflows
…er law, as only a mass below 2⁵³·MIN_POSITIVE can produce one
…pula pins and truncated-normal row it moves are re-pinned
…ve terms above ½, and the GED, gamma and Cauchy quantiles keep their large-β, large-θ and tiny-γ cells
…es, and its concentrated-law test names cells off the point mass
…nential quantile stops at upper, and GED's closed form takes an accurate Γ(1 + 1/β)
…test doc states its constraint, and the Cauchy comment names the quotient's bound

This branch was successfully deployed

1 active deployment
Preview — 13b45fdd Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda Run the CUDA tests on the GPU runner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant