diff --git a/preprints/lasso/lasso.tex b/preprints/lasso/lasso.tex index 4a78dc0..1a3130c 100644 --- a/preprints/lasso/lasso.tex +++ b/preprints/lasso/lasso.tex @@ -533,7 +533,8 @@ \section*{Code and data availability} The lockfile fixes the software, not the hardware. Residuals and gaps near $10^{-13}$ come out with different last digits on another CPU or BLAS build, so a rerun will not match the printed values digit for digit. The tests therefore check them against tolerances -between $10^{-12}$ and $10^{-9}$. Costs are given only as ratios, which carry over between +between $10^{-12}$ and $10^{-9}$ on the note's data, and up to $10^{-8}$ on fresh downloads. +Costs are given only as ratios, which carry over between machines; \texttt{uv run python/make\_figures.py timings} measures them. The test problem and the factor models of Table~\ref{tab:robust} are simulated inside @@ -548,9 +549,9 @@ \section*{Code and data availability} adjusted closing prices from 2021-06-01 to 2026-06-01, keeping names with no more than 5\% of days missing. The note used the snapshot with SHA-256 \texttt{b5faa522\ldots9937e}. A later download differs slightly, because the constituents change and Yahoo revises -adjusted prices. On one, from October 2026, the path's KKT residuals stay below -$4\times10^{-12}$, but the QP baseline drifts to $3.4\times10^{-10}$ at one corner, so the -tests allow $10^{-9}$ for that gap on any download other than the note's snapshot. +adjusted prices. On two from October 2026 the path's KKT residuals stay below +$4\times10^{-12}$, but the QP baseline drifts by up to $9\times10^{-10}$ at some corners, so +the tests allow $10^{-8}$ for that gap on any download other than the note's snapshot. \section*{Acknowledgements} diff --git a/preprints/lasso/python/make_figures.py b/preprints/lasso/python/make_figures.py index a76d995..839331a 100644 --- a/preprints/lasso/python/make_figures.py +++ b/preprints/lasso/python/make_figures.py @@ -1441,10 +1441,11 @@ def test_real_data() -> None: """Algorithm 1 on S&P 500 returns, through centred returns (every 10th corner).""" snapshot = hashlib.sha256(SP500.read_bytes()).hexdigest() == SNAPSHOT_SHA256 rd = real_data(sp500_returns(), snapshot, every=10) - # The QP baseline matches the note's snapshot to 1e-10. A fresh fetch can be worse - # conditioned: on one from October 2026 the QP missed a corner by 3.4e-10 while the - # path's own KKT residual stayed below 4e-12, so the gap was the QP's. - assert rd.corner_gap <= (1e-10 if rd.snapshot else 1e-9), rd + # The QP baseline matches the note's snapshot to 1e-10. Fresh fetches are worse + # conditioned: on two from October 2026 the QP missed corners by up to 8.8e-10 while + # the path's own KKT residuals stayed below 4e-12, so the gap was the QP's. Allow a + # decade of headroom on data that are not the snapshot. + assert rd.corner_gap <= (1e-10 if rd.snapshot else 1e-8), rd assert rd.minvar_gap <= 1e-12, rd assert rd.kkt <= 1e-11, rd # The warm-started baseline is exact to rounding on the note's snapshot. On a fresh