From 1895db76024569ba562dfb02b02134bc12925088 Mon Sep 17 00:00:00 2001 From: Emanuel Gull Date: Thu, 24 Sep 2026 15:23:36 +0200 Subject: [PATCH 1/9] Return failure status for command errors --- ALPS3_MIGRATION_PLAN.md | 4 ++-- src/maxent.cpp | 10 ++++++---- test/regression/MANIFEST.md | 2 +- test/regression/REFERENCE_CHANGES.md | 10 ++++++++++ test/regression/cases.py | 4 ++-- test/regression/reference/cli_missing_beta.h5 | Bin 15472 -> 20664 bytes 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index f2a81fe..e3483b0 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -408,8 +408,8 @@ Boost boundaries for now. **2.3C: command-line behavior** (updates the CLI references on purpose) -* B1: non-zero exit code on errors, together with `boost::diagnostic_information` - → `e.what()` (moved here from 2.2). +* **Done 2026-09-24:** B1: non-zero exit code on errors, together with + `boost::diagnostic_information` → `e.what()` (moved here from 2.2). * B16: accept `half-lorentzian` as well as `half lorentzian`, and show the real `CUT` default in `--help.grids`. * B10: a `SEED` parameter for the bootstrap errors (moved here from 2.2). diff --git a/src/maxent.cpp b/src/maxent.cpp index 8d4639a..0067e57 100644 --- a/src/maxent.cpp +++ b/src/maxent.cpp @@ -11,11 +11,11 @@ #include "maxent.hpp" #include -#include int main(int argc,const char** argv) { + try { alps::params parms(argc,argv); MaxEntSimulation::define_parameters(parms); //other help messages @@ -95,7 +95,6 @@ int main(int argc,const char** argv) else basename=parms["BASENAME"].as(); - try{ if(exitEarly){ throw std::runtime_error("Critical parameters not defined"); } @@ -163,11 +162,14 @@ int main(int argc,const char** argv) my_sim.run(); my_sim.evaluate(); } + return 0; } catch(const std::exception &e){ - std::cerr << "Caught Exception " << boost::diagnostic_information(e); + std::cerr << "Caught Exception: " << e.what() << '\n'; + return 1; } catch(...){ - std::cerr << "Caught Exception" << boost::current_exception_diagnostic_information(); + std::cerr << "Caught unknown exception\n"; + return 1; } } diff --git a/test/regression/MANIFEST.md b/test/regression/MANIFEST.md index 5a3fd03..22fa4f3 100644 --- a/test/regression/MANIFEST.md +++ b/test/regression/MANIFEST.md @@ -59,7 +59,7 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`. | `cli_help` | --help output (`--help`) | none | 8 | 0.0 s | | | `cli_help_grids` | --help.grids output (`--help.grids`) | none | 8 | 0.0 s | B16 | | `cli_help_models` | --help.models output (`--help.models`) | none | 8 | 0.0 s | | -| `cli_missing_beta` | error output when BETA is missing (B1: exit code is 0) | `test/regression/inputs/cli_missing_beta` | 8 | 0.0 s | B1; expects exception | +| `cli_missing_beta` | error output and failing exit status when BETA is missing | `test/regression/inputs/cli_missing_beta` | 8 | 0.4 s | expects exception | ## Full set (examples as shipped, opt-in) diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index 503ff6d..854f4f8 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -4,6 +4,16 @@ Every regeneration of files in `reference/` is logged here: date, commit, reason, affected cases, and the largest difference to the previous references (from `compare.py --report`). Newest entry first. +## 2026-09-24: command-line failures return nonzero (B1) + +* **Source:** `modernize/step2.3c`, based on merge commit `454f7ee`. +* **Reason:** expected behavior change: Maxent now returns status 1 on errors + and reports standard exceptions with `e.what()` instead of Boost diagnostic + formatting. +* **Cases:** `cli_missing_beta` only. Exit status changed from 0 to 1; stderr + changed from the multi-line Boost diagnostic to + `Caught Exception: Critical parameters not defined`. + ## 2026-09-24: legendre_convert reference added * **Source:** `modernize/step2` merge commit `a57f150`; the utility is still diff --git a/test/regression/cases.py b/test/regression/cases.py index 43b89b2..faf5c36 100644 --- a/test/regression/cases.py +++ b/test/regression/cases.py @@ -73,7 +73,7 @@ ("cli_help", ["--help"], "--help output"), ("cli_help_models", ["--help.models"], "--help.models output"), ("cli_help_grids", ["--help.grids"], "--help.grids output"), - ("cli_missing_beta", None, "error output when BETA is missing (B1: exit code is 0)"), + ("cli_missing_beta", None, "error output and failing exit status when BETA is missing"), ] @@ -115,7 +115,7 @@ def all_cases(): for name, args, covers in CLI: if args is None: cases.append(dict(name=name, set="cli", inputs="test/regression/inputs/" + name, - param="case.param", args=[], covers=covers, flags=["B1"], + param="case.param", args=[], covers=covers, flags=[], expect="exception")) else: cases.append(dict(name=name, set="cli", inputs=None, param=None, diff --git a/test/regression/reference/cli_missing_beta.h5 b/test/regression/reference/cli_missing_beta.h5 index 0c603d9e744c5e4952e49e57ce4d03d019df74f0..9051b9c42cf3f422c1132825d17c6d8b6d612510 100644 GIT binary patch delta 1005 zcmZ8gO-K}B7@qIj-TB>(MT;gg_cJKhZI_*0(=|P`3X*kM`@y8BT*zuA7ioS~=2($OOM7v;D%hAf;mRq;iQ3j-<8}_!HR5(YoV2azw0(BQ?(=zabYt5R zD0>so*4@!^n26)>M;JF3yeb||<%{>(gM8s}q4Vj$c-UZ zD6yGBsbm&31>m(fe1tk$Zr`yS{1l(yx56aZ38&E+JdScON)+rVT~hGBcr>KZolq?w zT9IqBp)Mb?J&bB6jrG3-;pi>GVKDwBR*l$A`)gL5GgMP{JnV3m+jXM@EFx0YrW?O3 z2DEf+G)XJnP15E~+;-jQLzo47C6gd+kvNC7#J3*6is>KBTT2jA^(O(V>9Fj)J&r}O zc&?7==mr|`Fvb!LEFsdJm?MjnK_ZdbY~nP*Kzsv}gV#tFACjB37g|51S`r`AcP+J& z)?-cQIMpx&irHR~eofWpdlv|;X51aqmY2J_P;wMz*?{7n~8%<%VI delta 1058 zcmb_bOH30{6rK0@bUHvOA8Do7Ofc%SCAI~Dc1pAzF%ma`Q6w6Lrc7i^D9yA6*brQ3 zj2j(qLU4y0W4dB83F(GJWupsOxa!gsF(xFqGY}Hre3*f~Z!_6DdSWT^w#X40YJ5{Q9UTN@9I`w9L4~mbqcKW!)}T; z1Kn^TdCx`+JWo#9r~&qpqc&=UJw6CIK5jEjaChjYjXbbb7htMq&AfjDaNwFC2FB_3 z(88>kXN4PgGOSc&=cHm$%0Ff~A0LWHY0v5!B2z)C7OX-2<8HQPd2I5Jg`2mYzZ_@L3o|tapjz%?!ILk Date: Thu, 24 Sep 2026 15:24:50 +0200 Subject: [PATCH 2/9] Accept documented half-lorentzian grid --- ALPS3_MIGRATION_PLAN.md | 4 ++-- src/maxent.cpp | 4 ++-- src/maxent_grid.cpp | 3 +-- test/gridTest.cpp | 17 +++++++++++++++-- test/regression/MANIFEST.md | 4 ++-- test/regression/REFERENCE_CHANGES.md | 10 ++++++++++ test/regression/cases.py | 4 ++-- .../inputs/t_grid_half_lorentzian/case.param | 2 +- test/regression/make_inputs.py | 2 +- test/regression/reference/cli_help_grids.h5 | Bin 15472 -> 20664 bytes 10 files changed, 36 insertions(+), 14 deletions(-) diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index e3483b0..e471f8c 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -410,8 +410,8 @@ Boost boundaries for now. * **Done 2026-09-24:** B1: non-zero exit code on errors, together with `boost::diagnostic_information` → `e.what()` (moved here from 2.2). -* B16: accept `half-lorentzian` as well as `half lorentzian`, and show the real - `CUT` default in `--help.grids`. +* **Done 2026-09-24:** B16: accept `half-lorentzian` as well as + `half lorentzian`, and show the real `CUT` default in `--help.grids`. * B10: a `SEED` parameter for the bootstrap errors (moved here from 2.2). **2.3D: numerical changes** (each its own commit and `REFERENCE_CHANGES.md` entry) diff --git a/src/maxent.cpp b/src/maxent.cpp index 0067e57..e1e35af 100644 --- a/src/maxent.cpp +++ b/src/maxent.cpp @@ -62,8 +62,8 @@ int main(int argc,const char** argv) std::cout << "For more information see examples/grids.pdf\n" << std::endl; std::cout <l`PpukikQ~9#xbR>Ey+5!jt#Lph{emg9dszpd331ETNyZIAoMhr zJ)i?UY@W(48UmvTLtzLWRCe(X@FHG>&YK>@r1_GJnM2;^dEfVY-serePNTg=)^fC6 zw+h$jCdK&hIXW-e+sjmKk8$0n!#ibuj^K@uz{}XGQ2jOV^A!xif}Yn3oaT4^5d2uX zJX!b9tWPk$vp7T8ko8_d+{tTz*uyVeQ zh1?E0#U$z_9ePQo`vqo=G(fUhXR$OuFkalj{KO2Y;)Sv6JTb>8H4VHi&v}j&bJ%yI z*|tQnI3m-JL(bC3GNG+XxM$fz)rw3vMqQ+sY=I`uRI*re^NkCZ{me1PR(MkL#;j`@ zENNGjcGYhs=n=1njN-m{J7he*ic=wrp(I62z^-s5tciG(j%pH4XpyFj&stPdaQ0BA zil<2gMDoaD-oekcTy=hpEolWoXwka?&IEb&V7R|vj5;|hW6%_ z+;ut_!$IUM?IqOyIu*GnXMQ81l|0LY@KDBHdbtk20LWeio3$$f=dp6tO zx?2zHbr((IFmjr{PpbVXN}~zHGD)P4b?>LLmJ-4QwHdyfw+OZ_ZmDqe-Uh>)C5vY( z<3=o1yPuo6qJBaub`xI`J1E4ZTy*AW#|FczWs8@rgOf6fn#8xPj}A^L>h}w2qct06 z$8BRH=6~yU%yvDZ2Rq~~8JQit;-jK#*nFn@(`tW$Ldy8)qr9Hd0J*%Wm$qeHhiI#+ zoMWo+BAe2-<7q+~T0YMc(#4;-U9+{Dk@j=?BBifO$jQrQ$u( Date: Thu, 24 Sep 2026 15:29:51 +0200 Subject: [PATCH 3/9] Make bootstrap errors reproducible --- ALPS3_MIGRATION_PLAN.md | 10 +++++--- src/maxent.hpp | 1 + src/maxent_helper.cpp | 8 +++--- src/maxent_simulation.cpp | 1 + test/regression/MANIFEST.md | 5 ++-- test/regression/PROVENANCE.md | 7 +++++ test/regression/README.md | 5 ++-- test/regression/REFERENCE_CHANGES.md | 9 +++++++ test/regression/cases.py | 1 + test/regression/generate.py | 8 +++--- .../inputs/t_generate_err/case.param | 9 +++++++ .../regression/inputs/t_generate_err/data.dat | 24 ++++++++++++++++++ test/regression/make_inputs.py | 7 +++++ test/regression/manifest.py | 2 +- test/regression/reference/cli_help.h5 | Bin 23752 -> 26888 bytes test/regression/reference/t_generate_err.h5 | Bin 0 -> 33656 bytes 16 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 test/regression/inputs/t_generate_err/case.param create mode 100644 test/regression/inputs/t_generate_err/data.dat create mode 100644 test/regression/reference/t_generate_err.h5 diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index e471f8c..2316012 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -223,9 +223,11 @@ Measured facts that the design relies on: | Targeted cases | Covariance (text, HDF5); `DATA_IN_HDF5`; `X_i`/`SIGMA_i` input; explicit `TAU_i`; T=0, time-bosonic, anomalous (PH and non-PH) kernels; every default model incl. tabulated; every grid; `MODEL_RUNS` (varspec) | same, plus a component harness that dumps the kernel matrix, its singular values, the grid and the discretized default model for small sizes | | CLI snapshots | `--help`, `--help.models`, `--help.grids`, missing-parameter error | exact text (exit codes once B1 is fixed) | +`GENERATE_ERR` is covered by `t_generate_err`; its bootstrap output is +reproducible because `SEED` defaults to 0 (B10). + Not captured: -* `GENERATE_ERR`: seeded from the clock (B10). It gets a reference after the seed parameter is added. * The LAPACK SVD path: compared against the Eigen results within tolerance, with no separate reference. Cases that run through suspected bugs record current behavior and are flagged @@ -406,13 +408,15 @@ need careful checking at large l and argument) and `program_options` (no standard equivalent). Reconfirmed 2026-09-24: these are intentional retained Boost boundaries for now. -**2.3C: command-line behavior** (updates the CLI references on purpose) +**2.3C: command-line behavior — Done 2026-09-24** (updates the CLI references on purpose) * **Done 2026-09-24:** B1: non-zero exit code on errors, together with `boost::diagnostic_information` → `e.what()` (moved here from 2.2). * **Done 2026-09-24:** B16: accept `half-lorentzian` as well as `half lorentzian`, and show the real `CUT` default in `--help.grids`. -* B10: a `SEED` parameter for the bootstrap errors (moved here from 2.2). +* **Done 2026-09-24:** B10: a `SEED` parameter for the bootstrap errors, + defaulting to 0 for reproducibility (moved here from 2.2). Added the + `t_generate_err` regression reference for the deterministic output. **2.3D: numerical changes** (each its own commit and `REFERENCE_CHANGES.md` entry) diff --git a/src/maxent.hpp b/src/maxent.hpp index fb4bf39..1df6877 100644 --- a/src/maxent.hpp +++ b/src/maxent.hpp @@ -70,6 +70,7 @@ public : void generateCovariantErr(const vector_type& A_in, const double alpha, ofstream_ &os); private: + std::mt19937::result_type bootstrap_seed_; ///discretized and normalized version of the default model. vector_type def_; ///check that the default model is non-zero diff --git a/src/maxent_helper.cpp b/src/maxent_helper.cpp index 78f2624..406a8ad 100644 --- a/src/maxent_helper.cpp +++ b/src/maxent_helper.cpp @@ -11,7 +11,6 @@ #include "maxent.hpp" #include -#include #include #include #include "maxent_backcont.hpp" @@ -21,7 +20,9 @@ //NOTE: size1= rows; size2=columns MaxEntHelper::MaxEntHelper(alps::params& p) : -MaxEntParameters(p) , def_(nfreq()), text_output(p["TEXT_OUTPUT"]) +MaxEntParameters(p), +bootstrap_seed_(p["SEED"].as()), +def_(nfreq()), text_output(p["TEXT_OUTPUT"]) { for (int i=0; i(std::time(0))); + std::mt19937 rng(bootstrap_seed_); std::vector noise_vecs; int max_it = 10000; diff --git a/src/maxent_simulation.cpp b/src/maxent_simulation.cpp index 5282782..0e761c5 100644 --- a/src/maxent_simulation.cpp +++ b/src/maxent_simulation.cpp @@ -56,6 +56,7 @@ void MaxEntSimulation::define_parameters(alps::params &p){ p.define("NORM",1.0,"NORM"); p.define("BACKCONTINUE",true,"Output A(omega) back to imaginary axis"); p.define("GENERATE_ERR",false,"Generate a bootstrap approximation for error bars"); + p.define("SEED",0,"Seed for reproducible bootstrap error bars"); //********************************* p.define("BETA","beta, inverse temperature"); p.define("NDAT","# of input points"); diff --git a/test/regression/MANIFEST.md b/test/regression/MANIFEST.md index e15543b..54846a9 100644 --- a/test/regression/MANIFEST.md +++ b/test/regression/MANIFEST.md @@ -24,6 +24,7 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`. |---|---|---|---|---|---| | `t_cov_hdf5` | DATA_IN_HDF5 with /Covariance, frequency PH | `test/regression/inputs/t_cov_hdf5` | 20 | 0.1 s | | | `t_cov_text` | covariance matrix from text file, time fermionic | `test/regression/inputs/t_cov_text` | 20 | 0.1 s | | +| `t_generate_err` | bootstrap error bars with the default SEED=0 | `test/regression/inputs/t_generate_err` | 21 | 0.0 s | | | `t_grid_half_lorentzian` | grid: half-lorentzian alias | `test/regression/inputs/t_grid_half_lorentzian` | 20 | 0.1 s | | | `t_grid_linear` | grid: linear | `test/regression/inputs/t_grid_linear` | 20 | 0.1 s | | | `t_grid_log` | grid: log | `test/regression/inputs/t_grid_log` | 20 | 0.1 s | | @@ -119,10 +120,10 @@ Rules are matched in order against ':'; the first match applies. | `\.chi2\.dat$` | 0 | 1e-07 | observed 1.7e-9 | | `:files/` | 1e-15 | 2e-06 | spectra, back-continuations, grids, all other outputs; observed 1.5e-7 (GCC 15, u2_time_fast) | -Total compared datasets: 1192 in 60 cases. +Total compared datasets: 1213 in 61 cases. ## Known limitations * The Legendre example is extremely ill-conditioned (minimal chi2 ~ 1e-17): rounding-level differences in the kernel change its alpha probabilities and Ng by up to 3e-3 and 3e-2 between compilers, so its end-to-end tolerances are loose. Kernel changes are caught by the Legendre kernel matrices in `components.h5`, compared at 1e-14. (Checks: Boost Gauss-Kronrod quadrature and, since step 2.2, the closed form used in maxent_kernel.cpp reproduce the GSL Legendre kernels of the references to 2e-16 and 3.3e-16.) * `-O0` builds take more than 30 minutes for the full `legendre` case. -* `*.spex.dat`, `*.fits.dat` and `*.booterr.dat` are not compared (see README.md). +* `*.spex.dat` and `*.fits.dat` are not compared (see README.md). diff --git a/test/regression/PROVENANCE.md b/test/regression/PROVENANCE.md index f7de757..642140d 100644 --- a/test/regression/PROVENANCE.md +++ b/test/regression/PROVENANCE.md @@ -54,6 +54,13 @@ Its synthetic Gaussian-spectrum input has zero error bars, so the clock-seeded bootstrap is deterministic; three consecutive generate-and-compare runs passed. +## Bootstrap-error reference (added 2026-09-24) + +`t_generate_err.h5` was generated from `modernize/step2.3c` after commit +`379b2f0`, using the Release build with AppleClang 21, libc++, C++17, and the +local ALPSCore installation. It uses the new default `SEED=0`; two independent +generations were identical. The reference includes `case.out.booterr.dat`. + ## Determinism Two independent generations with the reference build were compared with diff --git a/test/regression/README.md b/test/regression/README.md index eb1dc3e..9fe6207 100644 --- a/test/regression/README.md +++ b/test/regression/README.md @@ -73,8 +73,9 @@ Each result file holds: copied dataset by dataset at full precision. Text tables (`*.dat`) are loaded as float64, but only carry the 10 significant digits Maxent writes. `*.spex.dat` and `*.fits.dat` (spectrum and fit at every α; large, and - covered by `chi2.dat`, the α probabilities and the `*_back.dat` files) and - `*.booterr.dat` (random, B10) are not packed. + covered by `chi2.dat`, the α probabilities and the `*_back.dat` files) are + not packed. Bootstrap error output is deterministic (`SEED=0` by default) + and is covered by `t_generate_err`. * `/log/scalars/`: values parsed from the logs: minimal χ², posterior probability of the default model, Ng, number of singular values kept, and the number of α values where the minimizer hit `MAX_IT`. diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index 98155f6..a1fb37a 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -4,6 +4,15 @@ Every regeneration of files in `reference/` is logged here: date, commit, reason, affected cases, and the largest difference to the previous references (from `compare.py --report`). Newest entry first. +## 2026-09-24: reproducible bootstrap errors (B10) + +* **Source:** `modernize/step2.3c` after commit `379b2f0`. +* **Reason:** bootstrap error generation now uses the user-selectable `SEED` + parameter, which defaults to 0 instead of the wall clock. +* **Cases:** added `t_generate_err`, including `booterr.dat`; updated + `cli_help` for the new parameter. No existing numerical reference changed. +* **Verification:** two independent runs with the default seed were identical. + ## 2026-09-24: grid spelling and help corrected (B16) * **Source:** `modernize/step2.3c`, based on merge commit `454f7ee`. diff --git a/test/regression/cases.py b/test/regression/cases.py index 3caf7ce..cbd42c4 100644 --- a/test/regression/cases.py +++ b/test/regression/cases.py @@ -66,6 +66,7 @@ ("t_grid_quadratic", "grid: quadratic", []), ("t_grid_log", "grid: log", []), ("t_grid_linear", "grid: linear", []), + ("t_generate_err", "bootstrap error bars with the default SEED=0", []), ("t_model_runs", "MODEL_RUNS=2 (flat, gaussian): per-model outputs and varspec", []), ] diff --git a/test/regression/generate.py b/test/regression/generate.py index 8d5007c..b9f5d76 100644 --- a/test/regression/generate.py +++ b/test/regression/generate.py @@ -18,7 +18,7 @@ Not packed: *.spex.dat and *.fits.dat (spectrum and fit for every alpha: they grow like N_ALPHA x NFREQ or N_ALPHA x NDAT, and are covered by chi2.dat, the -alpha probabilities and the *_back.dat files) and *.booterr.dat (random, B10). +alpha probabilities and the *_back.dat files). """ import argparse @@ -40,7 +40,7 @@ sys.path.insert(0, str(HERE)) from cases import all_cases # noqa: E402 -SKIP_OUTPUTS = re.compile(r"\.(spex|fits|booterr)\.dat$") +SKIP_OUTPUTS = re.compile(r"\.(spex|fits)\.dat$") SCALARS = { "minimal_chi2": re.compile(r"^minimal chi2: (\S+)", re.M), "posterior_probability": re.compile(r"^posterior probability of the default model: (\S+)", re.M), @@ -114,8 +114,8 @@ def run_case(case, programs, outdir, provenance, timeout=TIMEOUT): before = {p.relative_to(workdir).as_posix() for p in workdir.rglob("*")} cmd = [str(programs[case["program"]])] + ([case["param"]] if case["param"] else []) + case["args"] p, seconds = run(cmd, workdir, timeout) - # maxent always exits with 0 (B1) and reports errors as 'Caught Exception'; - # the other programs report failure through their exit code + # Maxent reports exceptions with a non-zero exit code and a diagnostic; + # the other programs report failure through their exit code alone. status = "timeout" if p.returncode is None else \ "exception" if "Caught Exception" in p.stderr else \ "failed" if case["program"] != "maxent" and p.returncode != 0 else "ok" diff --git a/test/regression/inputs/t_generate_err/case.param b/test/regression/inputs/t_generate_err/case.param new file mode 100644 index 0000000..e8c3cf9 --- /dev/null +++ b/test/regression/inputs/t_generate_err/case.param @@ -0,0 +1,9 @@ +BETA=5.0 +NFREQ=40 +N_ALPHA=8 +NDAT=24 +DATASPACE=frequency +KERNEL=fermionic +PARTICLE_HOLE_SYMMETRY=true +DATA="data.dat" +GENERATE_ERR=true diff --git a/test/regression/inputs/t_generate_err/data.dat b/test/regression/inputs/t_generate_err/data.dat new file mode 100644 index 0000000..d74bcda --- /dev/null +++ b/test/regression/inputs/t_generate_err/data.dat @@ -0,0 +1,24 @@ +0.62831853071795862 -0.80874290991409159 0.0001 +1.8849555921538759 -0.44014716084961508 0.0001 +3.1415926535897931 -0.29286728018013575 0.0001 +4.3982297150257104 -0.21712083835923829 0.0001 +5.6548667764616276 -0.17165635623034461 0.0001 +6.911503837897544 -0.14166307358889488 0.0001 +8.1681408993334621 -0.12060505637657916 0.0001 +9.4247779607693793 -0.10485266703834568 0.0001 +10.681415022205297 -0.092950172398290121 0.0001 +11.938052083641214 -0.083168742498363424 0.0001 +13.194689145077131 -0.075316139276522379 0.0001 +14.451326206513048 -0.068980526475614631 0.0001 +15.707963267948966 -0.063426614004070431 0.0001 +16.964600329384883 -0.058669718153089162 0.0001 +18.2212373908208 -0.054868212835967246 0.0001 +19.477874452256717 -0.051194487824511997 0.0001 +20.734511513692635 -0.048183803227436109 0.0001 +21.991148575128552 -0.045353889185727227 0.0001 +23.247785636564469 -0.04311132391388374 0.0001 +24.504422698000386 -0.040812383784974739 0.0001 +25.7610597594363 -0.038789286218401768 0.0001 +27.017696820872221 -0.037114935461959209 0.0001 +28.274333882308138 -0.035393491249622627 0.0001 +29.530970943744059 -0.033879643568396858 0.0001 diff --git a/test/regression/make_inputs.py b/test/regression/make_inputs.py index 5f5f794..4f0722d 100644 --- a/test/regression/make_inputs.py +++ b/test/regression/make_inputs.py @@ -309,6 +309,13 @@ def make(name, case_dir): grid = {"lorentzian": "lorentzian", "half_lorentzian": "half-lorentzian", "quadratic": "quadratic", "log": "log", "linear": "linear"}[name[len("t_grid_"):]] base_frequency_ph(case_dir, name, [("FREQUENCY_GRID", grid)]) + elif name == "t_generate_err": + wn, y = freq_ph_data(name) + write_columns(case_dir / "data.dat", wn, y, np.full_like(y, NOISE)) + write_param(case_dir, [("BETA", BETA), ("NFREQ", 40), ("N_ALPHA", 8), + ("NDAT", wn.size), ("DATASPACE", "frequency"), + ("KERNEL", "fermionic"), ("PARTICLE_HOLE_SYMMETRY", "true"), + ("DATA", '"data.dat"'), ("GENERATE_ERR", "true")]) elif name in ("kk_imag_to_real_green", "kk_imag_to_real_self"): source, dataset, scale = { "kk_imag_to_real_green": ("u0_frequency", "files/in.out.avspec.dat", -np.pi), diff --git a/test/regression/manifest.py b/test/regression/manifest.py index d5ba99d..88bd198 100644 --- a/test/regression/manifest.py +++ b/test/regression/manifest.py @@ -87,7 +87,7 @@ def main(): "quadrature and, since step 2.2, the closed form used in maxent_kernel.cpp reproduce the GSL " "Legendre kernels of the references to 2e-16 and 3.3e-16.)", "* `-O0` builds take more than 30 minutes for the full `legendre` case.", - "* `*.spex.dat`, `*.fits.dat` and `*.booterr.dat` are not compared (see README.md).", ""] + "* `*.spex.dat` and `*.fits.dat` are not compared (see README.md).", ""] (HERE / "MANIFEST.md").write_text("\n".join(lines)) print(f"wrote MANIFEST.md: {len(cases)} cases, {total} datasets") diff --git a/test/regression/reference/cli_help.h5 b/test/regression/reference/cli_help.h5 index 9b86415d167761530ceb779a9741a92e79c1fa0f..da90fd53de9ebdabce4580c5aa7cf5a94f93f84e 100644 GIT binary patch delta 1169 zcmc&zOHUI~6u$TNF^__6FvQ^@y=jz`8q)!(MK*%5u}~f*7%gaoB7>qxNuh?|BWz4? z$MhJZJB*u#m6I{3jV4{Vz=ejGxNxOzh)Y&#OeD^$9m5~+ZqE76Ir+|a&b@c@6<+y> zSA&F|mP6;r8iA!mGZ}X)>vsvy4k6?0IMg=BFYH2%w|+c;uuBH9RR|%$%zk&7HXiFv z>R!r(h+39)LrvrCwE|;eY=u`2nhq6BYq6oNJ$1f_nHQs`Wle)hR@*Y4Ek{;9=mL4W z1B66 zb*FbY7CTLYx!L*YE3?uhh4Dab_pqdSQ<*HyN^{xFWbX3x#Ee8IGMR<>g=~6`O4+QI zoJeQqX>d}SO6O)4=*{#@PU@s$3x6th7SbWAZXW93t&I~|Ie-hY^0c`uf@oUyUTe@A zvP*>gv6rwUZsa3Ag4*?w#RyAw+N|O6Bfx&Yx*Q2$BF44pH=uYSNxI@tb@8=2-a`qj z_4{Bo%HkwEj*e+aAe&Jr_FpnKA$I)#O>kuq1agTZ++xC%Bq+*H{bkfZXkZtTDIeYe zJmNR5{A#Z@KW}MAUkCJUY^7MT)vw3Pue{VJD-U%BH8ik~7*26Ci3u9gw
yigBI zLNCXTl($grK3_ouZn(!P$N`_+gB9e2ql}GQ@Wyqfl5@kIIy=WetzdJq@LsTU931|q zl81YY&EnC1%6?~_6=0c9VFH+oVh5by2C)1m&nalV6-h27|dC7|(WcLGkISISB zwd74`y6|x93_KiXjdh@$!1bRt(Wg#yh`YWyE4{*$Tf>9W6(7f&T^~Lv-m($JpT(Mu zhH$P_u#q3riW zNB|eq0tup4O_5>zt)@r_qm>#7W4Kx%BY4-SN06y0YSMAsnqU9JKky@8jN$8*k}W%o z6Gg{lsBbc&I3~lGSDgNc(RWY;yJM$dH^CaC0Kz!VN(mG_@;xbSsuD}QY~i(xS+F+C zm9t0ZR+mg%x9Zl8xwiM{oUD0(cCL9h9vZ^u{d+g$(aKXZ0WxWfv_$eBZLM=(pG~#s`Rv!HwgkwiWm-I5)!nAm`eFz`=ES;p+jt!3+7`= zHkfq@TvQSlooI+jlGR}lg@Kj%z9I8{O2;SAn@pC`tj*z-NaRCsPBLrF$$tW$vhAu- zP9DOCOEGfMyz2i7K0@>C@Sixo)`9Vxc96>gi2Nb#KMbbws;A>B&B~I@F^S2%9DK@| zOne%x)oXb}0v!CI;GazUhWF}hC$k5sWGjWn^9S&o>`wAB50X-q$#SXOP!1j-&>mXr zUz1d%XDNOam5b;=p^}$}EVNUv9vEZ#*ay7vIK>w=N5ygm&ZISSQP>`^9rJY1lUZwu z<;+|R?JhDrp77U4Bqo}Z%qFdo;j1N@m=U}YF=KS*IK~{uF)`doZL;3X^zG86a|eYW zkuYWm2OdZG)XXKB+nKmn6PJ{vOEk12<4`-PIQ-~K4v_2Jp(GDx4bO>pTO?>xID@%G zTc$;{Hi?rPwI*#si=osUc?%^+A|27$YR^5-f?m2ZGbuGaA9K8|XQQts>H!=L*)`uNFx)P4e8?a0?t_-NE> zrv#lrm!Q=%(Q!IeAfu4`DpkI|N+)Atl9@B`&(&~+I zT1IE)TjMyeW1sMD;h}+iRPM1%u;<@BFqSmiPoC9k;(ezPQ959Jbr2($YCn)Ay^78 z-keN{##CN#g)grwe@8)P+zcKvKgTk%Dqka61LQvbngCH&g~DH1R#uf`C2f`a1ZaH9 z)K@A4%GUS!*lKd$d5*C>>}W#w1;maDI>tiF%GLg82T{yeub zy7H^6zDnhz)|9QU_9rnjueR43YpPbeKN;5bs&d8XXv?@NQjIRUsYg_OY{E}a)qzTSFKb5fnk&y zxyn!D=cFB7wxCdww;PRL*^)kTrJp9iiI=f#QK6(pqfrHv;i-~ql>DGY2Twkk1p}!_ zvYpuwupwYWz=nVg0UH7~1Z)Vr1OzJcyDJ(KAz(fH5=64y;x$4*@NZ*R2laQ@QO^(f ziK_69IpMq_zhyPPyl~y#p0IJCj=U;%GKMshI-W>||3BAy_Bx&rf2PoCa;$7dQoti@ z1j)z6kQ7>%(uCwI`Vak!X;e{(e-{1!pIx~y1SvosaC^P&ij2ed6~6zR>`B@YNt2J4iF-{s=V#yjhBfN&G zz)FAPMT5JrMJ7=N3+5Gn1!ABWS7gE z$F1~`w>ox?ZgAK#t^P+L4a(i4mRJL}Es2Q9se4?X?)4ov$J105pwH%GVdZVr9DJ%Vg=|{Pz$4<+!n+IzTZ%}BZ+uvGyc6PmOmeTJR zOb9(K(&JrvIn6?wET3j9W%s9@ww52SJ#}Uax7M6gP@9 zlHyGDf~0z(dPGt^QoSRo-l?4+shv=}LQ=b;c8H{QNbMF$?Uvd(lG-`#7f9MKXg@;I zenk5nEA4l*pR&?^S|xS<=yr|IBd_1id@;}Ynx*IQV&y)MC&=f)45#enMV5#zefm9Y z?#LS7Zt|OV_EF2%XY(fQpHqjGeSY6fcKD!W%fdMB(*^a}!9(Auvu;$L<=_mrPTFOS z*+bWsoVD}YZP70uGOPXohCSlS)csJs!&2Hroj&$t3wCCLd(}2~w^ zUNdxC`WDNL`df#D6}M)q&Hp-j{ElxdE4rL`Cp+4UZR)xDP4x#GEv-Jx3Us~Hmc4Of zNUw=sv2N^7Kc*{}T3RQ(z3IlJ4y@9%!RclLmRQJm z?8~lhK5FKw_@ivXobUQ`_Kn%=YroHazq69Pv*PgfV?p+8eA;iB4UTxRPu6)YYqIDv zn$djkbiKioyVI{**R%n1ME$D|^tv-F|{);zHcm2w$|8-0MGpfqiFRl77QkiQ;)Lv!P|K-vjf7`WB z)W5lY=R*&&MEy5(xcTXavqk;?>Y1K%W`U@Gc13^Xwe6sPnQX_qT>bnJA?SFs@s9Gj z2hs74hx}J=$w!S!zbOdrz5-P*&Ho|&espn z=#698UUq)o2HVMA#|&&A?>cqBc9B`~E4GJm)DEuwvgnq9`x)I#?d)*z*cVAktQxl0^ujfWZI66cSX+8QpA4j0V`hI-oicK*ehoBKIk0;cP z2}Ku1=6enDiL|csbB|1@zUu5`^w};)uHmC8=uv7zw^=O~B3SQxUp+ZJZ{GK)b(88# z)Y%8nutwkR?~zoDVBKGD`55~SvkOq8Uw>;6u9x zQ)k1ktorT{y6XL7&;DK)5%>j+jmCY=%sY&nhv~b;*F1t254aLuD!+ulKakz~?A*tV zk0SSVZ|kS7=luu!PL-|=y^6q3keS^#u-4XNXlQ_;!+P!*((FxhpVs|40)K&5&1pN% zMgD{?%8TDa`;MUtN7iK7wY-VIZ{R+E`9amW6UeXaz4~)boMsRvG>9{tRFTWz|Hz@Jch z-`T$2!b0SfCLfmMe;REY+V;~4tL`B1D||A2MU&$noJMVrZFU$j{xov)u9iA(*Bu1@ zg%ywcMAb$`=JmBH>~^74vM%EfCx{jNTPT30!6Zocbv z1pWq}ZELDUymbam(}va$IbVcO#DA(iSjpF?euu)vYeF9MK7-PXm#eFe7a=1v@KI0? z>vaUzr<9Hedc&?56{&LuW%Vur{s{OV^>Mf`D60?+ZSEcF(f%&Thd{o~J3b5OpHqaQ zIv-Ejnt2!6hoF6v((WgJ{O)PQuGteFmUkQQL4eQJ&2cfNPKD?_h0oNhHSPkw2=KKV zviwG1>?!o9<;gm?jdwsl2=tR)qwsO7C#O)l*XW+Bx!a&$1o}PoRgi+|e-izu=-BI_ z^KGyX1ojc+wc@~+ZpRTjes-vvZwc5J0{gl$Y0uSQ_5^ymIVSEw&6{AK2<&q~r$&b+ z?L3Bx27KiBmE{`PHv;>}m^bZ96NIC=6QrSpkJH44zZL2s?CvA-C`7X;%=^^G^fkB1yU0kaA<-DjPK z@ri};>CJl^PQ+zRLrr#fiwffABk+sB_;%gkGbXKCAR5bd-@5GjK)9ZpQ`qO$c;spI zmw|sXWs~EO4G%xF(qX(?mbw03(?z?jbQtG6Ga|;OVf?A(7kbgNtw@J)XWQu|KX>gR(qX(gr@x#0j#i|@I1}vk`;E2n z&(M*S4*tz&_)!0*^uFNVl*$SIO=-KpzbVB5{F_qTz`rTQ8T^}4y?}pHsz>l|O7#x@ zO{txLe^Y8#)DDr<4#B@EwOjCSO6?r{o6>#({!M8=0{^D8-$6f?_S4GdgAM6K2j=tA z{EN>Ai(x+hTCMkBUWfNM@S*Q=62>c!(UN^-5_*&nTWr0^SGHakHgND;H{hC;E^H+L zV%hREuoy0tEibGYq&Ko&r@_O|)T8tY>j`a(Q;YHn(fh!JmkX2v>l(o2|23`5-lXdd zY5v9SEte&-zi#UfX?zH2G5GPM{Y{#GaXd0Q6Fgooe@kWYRFUrxsin~gglhZ$Pe*|G zd8^Bew4v)Heimu_CsRVMzw}D^{7RZtv@cJW=5i^44U%3dpD(7_e93-I={~{|TFzfv zzW&R~N&Oen_ykxN03GJ-h0(6+a!}Qy5a#bvyM_6?)Xri4F6|dEf0yQ=9n9Zh9S(Yg_tQbyPX|755Z_P#@MWn}aHjZv+P%s4VeIGP`)P+(mvd{b6XO#i zQjJ|x`ik$T;``~GdWLAbSz>$wdRWq_Srzg9^iT6kE`_fX;}h2Xf<8!UAikeodd0a~ zJvZ_Fw92)CuS=vDpAeav_T|IkFK+#`QkUl_tT%*-ySj{V2Ndu!}mC@EAX~Ej^lC;+lb?~HXWOTgl}`$4Py(H5#y z_8U#z_~>21PxMi%eb_Iwd)pxF2O6uMgZ(~BVs2nR&n50#?AM8zn2!B8@9hc2ew&t4 z24X+W@*UT)U#8AEZ|sL@eDo6byR13=HTJW#{JHce!LRZ`@doTi$>|({{U#nQR$)KM z@bIVDFVe*07WRWc3Unw}Nb3O~;JcLGXDGdI4JyZeDpx%!C&(Q^+i`}r3)(r8w!47h zu!!PvkmBTbjNk_A@)w;iR5ssU*OPFDb;pa|$5!v}i9ki~H)V7k2CXXdAp}gqo2&Wn zx_}>{lIb_Fv8M3d69-C3q4Rv;Cw%rW1qym)tv+wb&H~xp6JnL^EJ=xBVAO!eA z`r`hC;jIaDtOUWylKiX9O6_W4k3W_N#=ZODe_Zb0cJ<}i)xy7pT~+!V?Mp1g+x=UT>>92l$gN9YI(|Nt%?6kP&5ay!gG!R zf+Vf1pMpI?`r_litSh-S3nEcTUdQoYh;AjvLJ9tqY)xQZalfCH^xPA8PB;vB?qw^H zp=S-Ftfc3jfId8iKlk!0C%!zpVnaz|UvIm5d3ZD&_; z-*)Kti8zjJ`_Lb7yxPR|_Bbx>_og#&{MlOn6dY%EtMw%uPu6elFdR2#%p>t(C4uX3 z9N7BzFXMQx?#tTYxURDo>*DyW)=f#AmS#_H9FNs=UNanbbJ(z zmwjUhPE|({+UyQ#QXgA?B9v^^`$xf zjQ84IF+UFt4=#|a&M z|GUcx%Vg+{HbIUbynQRU&ku_dGIR2St0pf=xLIX=@JZJ_0m^D8gAE&!Cnn#!9sJH3%Q~*@W*WOUQjDhV91p4fR}%<*2cb$Yn?61i4#9lXhfBkapGjgtYT^0%^C? zw*-eq3WCevnFJ??RRlLO#`v-%mC0b=Rp_Q@yblNVT}@s-3h&2(eOJBG3-G=i*muRO zUyJwWz`m;?bNAzYIB4vGrSXZx0^$7u$N}k#_se4H=Y^h5!u82MjcA`j%s__~$hE9}u0`PPy09g(clTc1Le z@vT*cZ`E>qpOKf}+MX1hXd)BT%J|kV!?#*FzV)Q?(jUo4#6VQWSNO~-;0xbo;Yqf` zhJXzL8v-^2{$U7I^t)Ta^IJGn3YDL?BEVbaOY-1H5}w1tA^bWMzr;=B+VMZ-WK_w0 zq8a`tsC+a5BUB?5j8UJQ#Dpjn%$P(|yztvbW)nxAZ}1%y;khdu9gRjk*GaGC%c%I@ zUy>`@GJ4&J=r-`VDB;;D9K!QdIQnq-w~^X1o!YcfYGnNCnjFG&Q8@I8{13)49eYId z?ZiJ)`JcPue;`k<=f5jTF3afW#Gm Date: Thu, 24 Sep 2026 15:31:43 +0200 Subject: [PATCH 4/9] Fix time-bosonic zero-frequency limit --- ALPS3_MIGRATION_PLAN.md | 3 ++- src/maxent_kernel.cpp | 6 +++--- test/regression/MANIFEST.md | 6 +++--- test/regression/PROVENANCE.md | 9 +++++++++ test/regression/REFERENCE_CHANGES.md | 11 +++++++++++ test/regression/cases.py | 2 +- .../regression/components/dump_components.cpp | 5 +++-- test/regression/reference/components.h5 | Bin 66936 -> 74640 bytes .../reference/t_kernel_time_bosonic.h5 | Bin 56728 -> 62112 bytes 9 files changed, 32 insertions(+), 10 deletions(-) diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index 2316012..f196ad1 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -420,7 +420,8 @@ Boost boundaries for now. **2.3D: numerical changes** (each its own commit and `REFERENCE_CHANGES.md` entry) -* B7b: time-bosonic kernel: use the ω→0 limit only at ω = 0. +* **Done 2026-09-24:** B7b: time-bosonic kernel uses the ω→0 limit only + at ω = 0. Updated the targeted and component references. * B15: remove the `float` casts in the log grid. * B11 (optional): `BDCSVD` instead of `JacobiSVD`; measure against the references before deciding. diff --git a/src/maxent_kernel.cpp b/src/maxent_kernel.cpp index b5ec5c6..3e782c2 100644 --- a/src/maxent_kernel.cpp +++ b/src/maxent_kernel.cpp @@ -79,10 +79,10 @@ K_(ndat_,nfreq_) else if (ktype_==time_bosonic_kernel) { for (int i=0; i -Infity } } diff --git a/test/regression/MANIFEST.md b/test/regression/MANIFEST.md index 54846a9..a499fb5 100644 --- a/test/regression/MANIFEST.md +++ b/test/regression/MANIFEST.md @@ -34,7 +34,7 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`. | `t_kernel_anomalous_nonph` | anomalous kernel, frequency non-PH | `test/regression/inputs/t_kernel_anomalous_nonph` | 24 | 0.1 s | | | `t_kernel_anomalous_ph` | anomalous kernel, frequency PH | `test/regression/inputs/t_kernel_anomalous_ph` | 24 | 0.1 s | | | `t_kernel_legendre_bosonic` | Legendre bosonic kernel | `test/regression/inputs/t_kernel_legendre_bosonic` | 24 | 0.1 s | B17 (Legendre bosonic uses the fermionic integrand) | -| `t_kernel_time_bosonic` | time bosonic kernel, OMEGA_MIN=0 | `test/regression/inputs/t_kernel_time_bosonic` | 24 | 0.1 s | B7 (fixed; K(i,0)=T assumes the grid starts at 0) | +| `t_kernel_time_bosonic` | time bosonic kernel, OMEGA_MIN=0 | `test/regression/inputs/t_kernel_time_bosonic` | 24 | 0.3 s | | | `t_kernel_tzero` | T=0 kernel, OMEGA_MIN=0 | `test/regression/inputs/t_kernel_tzero` | 20 | 0.1 s | | | `t_model_double_gaussian` | default model: double gaussian | `test/regression/inputs/t_model_double_gaussian` | 20 | 0.1 s | | | `t_model_double_lorentzian` | default model: double lorentzian | `test/regression/inputs/t_model_double_lorentzian` | 20 | 0.1 s | | @@ -96,7 +96,7 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`. | Case | Covers | Inputs | Datasets | Runtime | Flags | |---|---|---|---|---|---| -| `components` | kernel matrices (11 reachable kernel types), grids (5 types, odd/even), default models (13), singular values | `components/dump_components.cpp` | 47 | 0.0 s | | +| `components` | kernel matrices (11 reachable kernel types), grids (5 types, odd/even), default models (13), singular values | `components/dump_components.cpp` | 49 | 0.4 s | | ## Tolerances @@ -120,7 +120,7 @@ Rules are matched in order against ':'; the first match applies. | `\.chi2\.dat$` | 0 | 1e-07 | observed 1.7e-9 | | `:files/` | 1e-15 | 2e-06 | spectra, back-continuations, grids, all other outputs; observed 1.5e-7 (GCC 15, u2_time_fast) | -Total compared datasets: 1213 in 61 cases. +Total compared datasets: 1215 in 61 cases. ## Known limitations diff --git a/test/regression/PROVENANCE.md b/test/regression/PROVENANCE.md index 642140d..162f256 100644 --- a/test/regression/PROVENANCE.md +++ b/test/regression/PROVENANCE.md @@ -61,6 +61,15 @@ passed. local ALPSCore installation. It uses the new default `SEED=0`; two independent generations were identical. The reference includes `case.out.booterr.dat`. +## Time-bosonic references updated (B7b, 2026-09-24) + +The `kernel_time_bosonic` datasets in `components.h5` and the +`t_kernel_time_bosonic.h5` reference were regenerated from +`modernize/step2.3c` after commit `46b5610`, using the Release build with +AppleClang 21 and libc++. All other component datasets are unchanged. +The component reference also adds a symmetric-grid time-bosonic kernel to +cover a negative first frequency and the exact ω=0 branch. + ## Determinism Two independent generations with the reference build were compared with diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index a1fb37a..84bdbf3 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -4,6 +4,17 @@ Every regeneration of files in `reference/` is logged here: date, commit, reason, affected cases, and the largest difference to the previous references (from `compare.py --report`). Newest entry first. +## 2026-09-24: time-bosonic zero-frequency limit corrected (B7b) + +* **Source:** `modernize/step2.3c` after commit `46b5610`. +* **Reason:** the time-bosonic kernel previously assigned the ω→0 limit to + column 0 regardless of that column's frequency. It now uses the limit only + when ω is exactly zero and evaluates the kernel formula everywhere else. +* **Cases:** `t_kernel_time_bosonic` and `components`; added a symmetric-grid + time-bosonic component that contains both negative and exactly zero ω. +* **Largest differences:** 2.202e-3 relative in the kernel component; 5.373e-4 + relative in χ² and 1.190e-2 relative in the small spectrum variance. + ## 2026-09-24: reproducible bootstrap errors (B10) * **Source:** `modernize/step2.3c` after commit `379b2f0`. diff --git a/test/regression/cases.py b/test/regression/cases.py index cbd42c4..33b6714 100644 --- a/test/regression/cases.py +++ b/test/regression/cases.py @@ -44,7 +44,7 @@ ("t_param_xi", "data as X_i/SIGMA_i in parameter file, frequency PH", []), ("t_param_taui", "X_i/SIGMA_i/TAU_i in parameter file, time fermionic", []), ("t_kernel_tzero", "T=0 kernel, OMEGA_MIN=0", []), - ("t_kernel_time_bosonic", "time bosonic kernel, OMEGA_MIN=0", ["B7 (fixed; K(i,0)=T assumes the grid starts at 0)"]), + ("t_kernel_time_bosonic", "time bosonic kernel, OMEGA_MIN=0", []), ("t_kernel_anomalous_ph", "anomalous kernel, frequency PH", []), ("t_kernel_anomalous_nonph", "anomalous kernel, frequency non-PH", []), ("t_kernel_legendre_bosonic", "Legendre bosonic kernel", ["B17 (Legendre bosonic uses the fermionic integrand)"]), diff --git a/test/regression/components/dump_components.cpp b/test/regression/components/dump_components.cpp index b1fb3ef..55d243b 100644 --- a/test/regression/components/dump_components.cpp +++ b/test/regression/components/dump_components.cpp @@ -58,9 +58,9 @@ alps::params base_params() { // ---------------------------------------------------------------- kernels void dump_kernel(const std::string &name, const std::string &dataspace, const std::string &kernel_name, - bool ph, int ndat, double omega_min, double omega_max, bool time_input) { + bool ph, int ndat, double omega_min, double omega_max, bool time_input, + int nfreq = 24) { alps::params p = base_params(); - const int nfreq = 24; p["NDAT"] = ndat; p["NFREQ"] = nfreq; p["DATASPACE"] = dataspace; @@ -137,6 +137,7 @@ int main(int argc, char **argv) { // kernels: every kernel type reachable through set_kernel_type (the time-Legendre types are not, B5) dump_kernel("time_fermionic", "time", "fermionic", false, 9, -6, 6, true); dump_kernel("time_bosonic", "time", "bosonic", false, 9, 0, 6, true); + dump_kernel("time_bosonic_symmetric", "time", "bosonic", false, 9, -6, 6, true, 25); dump_kernel("time_tzero", "time", "tzero", false, 9, 0, 6, true); dump_kernel("legendre_fermionic", "legendre", "fermionic", true, 6, -6, 6, false); dump_kernel("legendre_bosonic", "legendre", "bosonic", true, 6, -6, 6, false); diff --git a/test/regression/reference/components.h5 b/test/regression/reference/components.h5 index 7717fffa7fccc3e9962cab2278af211bbca43889..088f09ac2c963582a86f498160e7942b50af60e6 100644 GIT binary patch delta 6560 zcmeHMdt6gT7QXjFLK5Cu+xR4+XjC2%5P2vQlt)&r2CbFii-)nW@)A)IwE-1HZ7p$7 zL7~!>QmU>JFDfcJX%!6=F^CEkA7I%oTBT@fMSrfl_mUaxcKb)S+ui-g@Z&f4J9Ex= zzBxB{hI{DVVwrovGFQ&z4%3bHl}Ra@q9`f%%YPiWAO0h==ozSDMz?MR6D8E|e~*#m zTJx?HrM6@}ExKtzifpj18F~nGP&P_Bns9Uo%%Q4~AT@s#VTNRy5(#0SK}kVnR%W}I zlA>}3V?Vih6k%wP~WmLkD>zO~EDdqcCNV1Cu^Tryl~}w6wCI zWCQeiyPkR2XUH+Dw`PWfP?U}C!~WWwQD%)%SZdTb^F=wvU7qF(Dc0Wcwiz2RHpZE8 z1cS#l=8?k1Z_P5}9E?o`W-Pbt83QQw5woGpY@kdHW-P@uQzw~mKE@GEW-J%#^&ioL zL-|{P>n!FjH}SI=OISDA*H$oYjDtie-Mm3Wtl`bow*U!9RmB%HaF{7291Sne?G>o@<`YP~KygITO3dnYZ^C(}xkG ze0Z>g%@i(Co4$X9erbrQEe2W*rWn%J4Xbh|hsetO>NX8h5*;8QQlt-*jl*EQPW&R0`OpP#rDd%^h0*qaZaDZ6Z5Ee4(AmXN__Db#CVJ^ zh8iPjBahNCk}cs8`6?+Ac<$Cc85U}qhVu)d&LG>TNE2$`b+mrRIO#k(R~{wZ7;Qwy zSM03X_^1|rGADcRkRkObO!LUb(Xj!IvM_$-uWv++Gk1B`hcuz3NbJN9$7y?s2r)@S5)&ar7!gW@%b)F6 zJ^8KyRoE_K)?_uJyywdUy%t_YJ0*?s%Hk&U?c(}b%DC%D^5pJx>h4W6_&IIw?AL}? z-~WEu&>(F)+F3f*Qk~p^rkWI`wceem%-W;feZ(X5(cHh+QBOO_2(TSsKY-(anW!CL zVt^9?oETss01E?HDCiNc{jLEN^?CG0pxqU;O1^YORpwP>^W)78QSv5qa;7pl-2N-{ z_~@$X~ z-qx*XQ{$ZrHFr?&NbJOqkJIvjgoqH6L?kg0LWB{aM0i+3px;|AjVN&7s^}#Ht{}br z?CNg@UPYm3VC<@4*UJBv0rEw@iMZXqMUc7Xi=jsqMK&jTlv}Lf~gl_d4lwf>pLbgqZgFwFql%IXdrMugNn!a?o)cT$Y z-Dj7KvA$Q2aypbVpL=|XE|`+??gmvO-^+%$%|lM2PSyTv7xsRPV*cJIYht~+cO-V= zcR`B-2@xSCiAZ81ga{)-iSW9=?LA7x)}Z~HFP?e!Mh$9DJ78RuX+-mnbl#0xcL7bT zSRMTB^J)|saQLRQ_AI(HblPoHZ9rPpyyZvsojrkSoToZIDJ@4=_N}SRs{aW2l$<>@ z=iw?;eJOgYyxD<_0NVlf12_&~9)O7fP6TjbfQ0}o3}B&RfRzKN4$WXx>!GW*go&EV z?Om`-D`Fh}$nRpU_{8m)q|-Zz@$uR{ncvc)!ZMh20B?llK|=v|fNf$Y?B+JiG5}o* zsvP^{^MgMw`A>hCTP7h#xk0Pb-+HxI zzS=8a?Uk?g%Kz71>Cn4ZdbjucbX#IA9KZF1aMzY+O4~c|$E_!Y`il={F`R#_u~JSNem}f&h(PI{=;!6- zWuf#ht$giJ7_A&r0J_2}Ux527r?62)5Sd9t2ae@Y%&=m_Hf(BuM zZdfo`z~Q)Rdv})#@Zjq3Q*$0vg3!NQR9xA42E?7-=zl8j3t)Rta%%O|^B|_=PITDp zT2Le&y6xdgZ9Q154xhhfSOc&xy?i1d`!Xn567Cgw^)finHt-w6iUzPp_3e>&M_=q+ zNdd-Xc%JxMSNw~j!I3v-Y}~&j z5kzNnofa%e2V2T>EPi~p9vIHLm!-Prf)Af1J<7dU0D#|jD@CJ9z=4(dC4bm|2uuoT zp4yag3?#b;)!mtifJ^emm6nm`z~1l;F;iv6-h~u%Sj!tmVuw^55P}KvF`++BupTEo zhXrV{pb#u@9~OKAFJRWC4by`f?ZDe~C9H@U3eGIuUb}3$7m&9p#?|D7fQ@_Ww2O^@ zo&#QUz1MoIG#+&Bu{<|&!D_HB*4Ab0XYT`6H=$+gv?5TD9bD(#eh}P1A3q#bdJ+T{ zvGW5$t9w^MFqtpH{v@KVgNlvEs>i1^w3@I#$wI8#r>M z^V62K?SXYis#-t4{~}No>Qp8i@CLB+9!D>-9t$duPh4d4g$j^cI2%`{8duB@SCUkC zxE7b5JSM|1CJrQkl$?~h0hjn7PDlhy!Gemhz=2pWS-{V4iS`%n$^Y_}Xu-eUlV5jB zNQwWmr$qbf9+UstQ=;Xb^WU~}{XQF)mfz`q)6Vq^8yDG%|BbUm`+FWHzu_!--F({p z{4jy#`58S&0V0?^cQBP+zaNhyv{tDzQUQI>5PcYKjUr8`jPh9?bzxLi!6PYdLscHdjVH!*zbA)9ijanH9%rFLY9!Nj zi%m$iqb=mODY$kS<6D(fJy^AfCNl2qweud{3;V!4|RUXMPQeD&Q z&(l5Pee0zH*fH66mxF+gW0&?-F>K|%3XAbE$;*z7;&&DPR*iy&1+}$@Qg=S+eRznSf1cRZ7_M$5Hm% zr2}Awbc#5)uen!9LtEjTUc|se;lf^I0j~;Y^dd{>X7v&Y;Aufv@0<{>?f-Jl3igw} z=$0@1FD>O;bNwFID#NH&1Uk-NM;6j0+x~L`54P_3}``)(2865v+!_>!HO5oT>-#5pdOt z?rxI~a#2z7&8lZjyy&J%ot#B$&@Kj1c_=nM+5@gf5vx)bw|j?~P|p4X`-_*u`OfG2 zo$u%SnVHL%JEkk1-8(!@8aCg{JVwU_Gk{?jwe*W0mGpxj&ZEyTHc!|S9L45_T2E|Z zAsANCVE4RVH_B{%1-W4v z@J8r4Xz*6CDvjA14h927s`>1ydfp3146)ii5;3?x7FP2OL~K>@2MEQ3wC8 zh6QOeW-yGx{N+2w&KM%YKn&y1k%5I`{X{ZQQ=O?91luXrt{_;?QooT{hQ5I*TM6Dy zac2d=Dttf;GluE35l0bmFuBbHcT-%IORy7KyRiMR5Yaz13)95)Jsdy zrop?JU1s|s(UDT<2ePTxfVpAkI2r6*C@^IO5|Vbjq-m7gLVcZi zc!rZA=5w=krgZ2W)ZfZr+mpm<=?`44@*>O5X2j6kQ{KkU0u_rKiQl4Hi$T3D%Yn|W zE34Vobrl^;-x?MkUXNz}q028YumO$oaI{ReHliD8!npbwO{g{W<-W4KyQtMNw5clj z0rJo0{;V(RKvR8FUSwwcgv!3W@HiO(`z~syP``T~%`^5BHBe#=EtE_PAEH9s!t7M& zawHq4zjFwJ0-KM@;i@GCce~&KPk#Y6nwPz03evO_E{~+-7F$>B&vcXndzaIr0 zul-y4^8)l_@$YwPnpLP}Wfr#lg2R1&zVLA0Jl;m~_&CDDHxXif4k^TINnw5-5yH0+ zVf=m~l;!O(VSO=c*#UMV_TI4{xpg!3Bo;oN!C0prP(yDR@4*Rs1SpU24Mu6zL__Z>}N)4#|cNFmEh qTu9WmmwWb=>VB@1i_ZHs9^#`P;#i=@mpLh_CQ)6lUI{hdXa5(~vk`Ux diff --git a/test/regression/reference/t_kernel_time_bosonic.h5 b/test/regression/reference/t_kernel_time_bosonic.h5 index b4d4029912475f0d18efec37532366389d792834..316c6ea38df01709a25212572b2156228d420192 100644 GIT binary patch literal 62112 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 gG%H|Mz^s5-0kZ;T1xih@zM=Vn6`_ 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 CN4U}e From 086c17d603986941a4f8e17a1433e3e714368611 Mon Sep 17 00:00:00 2001 From: Emanuel Gull Date: Thu, 24 Sep 2026 15:32:25 +0200 Subject: [PATCH 5/9] Keep logarithmic grid calculations in double precision --- ALPS3_MIGRATION_PLAN.md | 4 +++- src/maxent_grid.cpp | 9 ++++----- test/regression/REFERENCE_CHANGES.md | 8 ++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index f196ad1..8fe546f 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -422,7 +422,9 @@ Boost boundaries for now. * **Done 2026-09-24:** B7b: time-bosonic kernel uses the ω→0 limit only at ω = 0. Updated the targeted and component references. -* B15: remove the `float` casts in the log grid. +* **Done 2026-09-24:** B15: replaced the log-grid `float` casts with explicit + `double` conversions. Existing grids are bit-identical (the integer values + used by the tests and normal runs are exactly representable as `float`). * B11 (optional): `BDCSVD` instead of `JacobiSVD`; measure against the references before deciding. diff --git a/src/maxent_grid.cpp b/src/maxent_grid.cpp index 1391b50..5f7f973 100644 --- a/src/maxent_grid.cpp +++ b/src/maxent_grid.cpp @@ -46,18 +46,18 @@ void grid::initialize_linear_grid() { void grid::initialize_logarithmic_grid(double t_min) { double t_max = 0.5; - double scale = std::log(t_max / t_min) / ((float) ((nfreq_ / 2 - 1))); + double scale = std::log(t_max / t_min) / static_cast(nfreq_ / 2 - 1); t_array_[nfreq_ / 2] = 0.5; for (int i = 0; i < nfreq_ / 2; ++i) { t_array_[nfreq_ / 2 + i + 1] = 0.5 - + t_min * std::exp(((float) (i)) * scale); + + t_min * std::exp(static_cast(i) * scale); t_array_[nfreq_ / 2 - i - 1] = 0.5 - - t_min * std::exp(((float) (i)) * scale); + - t_min * std::exp(static_cast(i) * scale); } //if we have an odd # of frequencies, this catches the last element if (nfreq_ % 2 != 0) t_array_[nfreq_ / 2 + nfreq_ / 2 + 1] = 0.5 - + t_min * std::exp(((float) (nfreq_) / 2) * scale); + + t_min * std::exp(static_cast(nfreq_) / 2 * scale); } void grid::initialize_quadratic_grid(double spread) { @@ -100,4 +100,3 @@ void grid::initialize_lorentzian_grid(double cut) { } - diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index 84bdbf3..fc6ccb6 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -4,6 +4,14 @@ Every regeneration of files in `reference/` is logged here: date, commit, reason, affected cases, and the largest difference to the previous references (from `compare.py --report`). Newest entry first. +## 2026-09-24: log-grid float casts removed (B15) + +* **Source:** `modernize/step2.3c` after commit `b1d7c8b`. +* **Reason:** log-grid intermediates now remain explicitly in double + precision instead of passing integer indices through `float`. +* **Cases:** `t_grid_log` and both log-grid component sizes were measured. + They are bit-identical, so no reference was regenerated. + ## 2026-09-24: time-bosonic zero-frequency limit corrected (B7b) * **Source:** `modernize/step2.3c` after commit `46b5610`. From c548da6e83d9af2e3187acf11d09968637d4d766 Mon Sep 17 00:00:00 2001 From: Emanuel Gull Date: Thu, 24 Sep 2026 15:34:26 +0200 Subject: [PATCH 6/9] Record JacobiSVD benchmark decision --- ALPS3_MIGRATION_PLAN.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index 8fe546f..4639749 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -418,15 +418,18 @@ Boost boundaries for now. defaulting to 0 for reproducibility (moved here from 2.2). Added the `t_generate_err` regression reference for the deterministic output. -**2.3D: numerical changes** (each its own commit and `REFERENCE_CHANGES.md` entry) +**2.3D: numerical changes — Done 2026-09-24** (each accepted change its own +commit and `REFERENCE_CHANGES.md` entry) * **Done 2026-09-24:** B7b: time-bosonic kernel uses the ω→0 limit only at ω = 0. Updated the targeted and component references. * **Done 2026-09-24:** B15: replaced the log-grid `float` casts with explicit `double` conversions. Existing grids are bit-identical (the integer values used by the tests and normal runs are exactly representable as `float`). -* B11 (optional): `BDCSVD` instead of `JacobiSVD`; measure against the - references before deciding. +* **Measured 2026-09-24; keep `JacobiSVD`:** `BDCSVD` did not improve the + large Legendre case (57.49 s, effectively unchanged), while its different + singular vectors changed the deterministic bootstrap-error result by about + 3%. The optional B11 change therefore has no demonstrated benefit here. Keep the `MaxEntSimulation` public getters stable, because the tests use them. From 8b2ff05cab5690147d44469a6681bbf3d3cd2f3d Mon Sep 17 00:00:00 2001 From: Emanuel Gull Date: Thu, 24 Sep 2026 15:42:20 +0200 Subject: [PATCH 7/9] Classify all nonzero Maxent exits as failures --- src/maxent.cpp | 2 +- test/regression/generate.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/maxent.cpp b/src/maxent.cpp index e1e35af..5fdba78 100644 --- a/src/maxent.cpp +++ b/src/maxent.cpp @@ -169,7 +169,7 @@ int main(int argc,const char** argv) return 1; } catch(...){ - std::cerr << "Caught unknown exception\n"; + std::cerr << "Caught Exception: unknown exception\n"; return 1; } } diff --git a/test/regression/generate.py b/test/regression/generate.py index b9f5d76..79908d7 100644 --- a/test/regression/generate.py +++ b/test/regression/generate.py @@ -114,11 +114,12 @@ def run_case(case, programs, outdir, provenance, timeout=TIMEOUT): before = {p.relative_to(workdir).as_posix() for p in workdir.rglob("*")} cmd = [str(programs[case["program"]])] + ([case["param"]] if case["param"] else []) + case["args"] p, seconds = run(cmd, workdir, timeout) - # Maxent reports exceptions with a non-zero exit code and a diagnostic; - # the other programs report failure through their exit code alone. + # Maxent's expected failure cases are classified as exceptions; any + # other program's nonzero exit is a generic failure. Exit status is + # authoritative so diagnostic wording cannot hide a failed command. status = "timeout" if p.returncode is None else \ - "exception" if "Caught Exception" in p.stderr else \ - "failed" if case["program"] != "maxent" and p.returncode != 0 else "ok" + "exception" if case["program"] == "maxent" and p.returncode != 0 else \ + "failed" if p.returncode != 0 else "ok" with h5py.File(out, "w") as h5: for key in ("name", "set", "covers"): h5.attrs[key] = case[key] From bdf5ec5b0f81c38f23756fec04e705c02063a1e4 Mon Sep 17 00:00:00 2001 From: Emanuel Gull Date: Thu, 24 Sep 2026 15:53:30 +0200 Subject: [PATCH 8/9] Strengthen regression failure and seed coverage --- ALPS3_MIGRATION_PLAN.md | 3 ++- test/regression/MANIFEST.md | 3 ++- test/regression/PROVENANCE.md | 14 +++++++--- test/regression/README.md | 3 ++- test/regression/REFERENCE_CHANGES.md | 8 ++++++ test/regression/cases.py | 1 + test/regression/generate.py | 18 ++++++++----- .../inputs/t_generate_err_seed/case.param | 10 ++++++++ .../inputs/t_generate_err_seed/data.dat | 24 ++++++++++++++++++ test/regression/make_inputs.py | 13 ++++++---- .../reference/t_generate_err_seed.h5 | Bin 0 -> 33656 bytes 11 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 test/regression/inputs/t_generate_err_seed/case.param create mode 100644 test/regression/inputs/t_generate_err_seed/data.dat create mode 100644 test/regression/reference/t_generate_err_seed.h5 diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index 4639749..d36dd4d 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -416,7 +416,8 @@ Boost boundaries for now. `half lorentzian`, and show the real `CUT` default in `--help.grids`. * **Done 2026-09-24:** B10: a `SEED` parameter for the bootstrap errors, defaulting to 0 for reproducibility (moved here from 2.2). Added the - `t_generate_err` regression reference for the deterministic output. + `t_generate_err` regression reference for the deterministic default and + `t_generate_err_seed` to verify that an explicit non-default seed is used. **2.3D: numerical changes — Done 2026-09-24** (each accepted change its own commit and `REFERENCE_CHANGES.md` entry) diff --git a/test/regression/MANIFEST.md b/test/regression/MANIFEST.md index a499fb5..14a139e 100644 --- a/test/regression/MANIFEST.md +++ b/test/regression/MANIFEST.md @@ -25,6 +25,7 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`. | `t_cov_hdf5` | DATA_IN_HDF5 with /Covariance, frequency PH | `test/regression/inputs/t_cov_hdf5` | 20 | 0.1 s | | | `t_cov_text` | covariance matrix from text file, time fermionic | `test/regression/inputs/t_cov_text` | 20 | 0.1 s | | | `t_generate_err` | bootstrap error bars with the default SEED=0 | `test/regression/inputs/t_generate_err` | 21 | 0.0 s | | +| `t_generate_err_seed` | bootstrap error bars with explicit SEED=1234 | `test/regression/inputs/t_generate_err_seed` | 21 | 0.1 s | | | `t_grid_half_lorentzian` | grid: half-lorentzian alias | `test/regression/inputs/t_grid_half_lorentzian` | 20 | 0.1 s | | | `t_grid_linear` | grid: linear | `test/regression/inputs/t_grid_linear` | 20 | 0.1 s | | | `t_grid_log` | grid: log | `test/regression/inputs/t_grid_log` | 20 | 0.1 s | | @@ -120,7 +121,7 @@ Rules are matched in order against ':'; the first match applies. | `\.chi2\.dat$` | 0 | 1e-07 | observed 1.7e-9 | | `:files/` | 1e-15 | 2e-06 | spectra, back-continuations, grids, all other outputs; observed 1.5e-7 (GCC 15, u2_time_fast) | -Total compared datasets: 1215 in 61 cases. +Total compared datasets: 1236 in 62 cases. ## Known limitations diff --git a/test/regression/PROVENANCE.md b/test/regression/PROVENANCE.md index 162f256..06f392c 100644 --- a/test/regression/PROVENANCE.md +++ b/test/regression/PROVENANCE.md @@ -59,7 +59,10 @@ passed. `t_generate_err.h5` was generated from `modernize/step2.3c` after commit `379b2f0`, using the Release build with AppleClang 21, libc++, C++17, and the local ALPSCore installation. It uses the new default `SEED=0`; two independent -generations were identical. The reference includes `case.out.booterr.dat`. +generations were identical. `t_generate_err_seed.h5` was added after commit +`8b2ff05` with explicit `SEED=1234`; its bootstrap values differ from the +default-seed reference, so the case detects an ignored `SEED` parameter. Both +references include `case.out.booterr.dat`. ## Time-bosonic references updated (B7b, 2026-09-24) @@ -72,9 +75,12 @@ cover a negative first frequency and the exact ω=0 branch. ## Determinism -Two independent generations with the reference build were compared with -`compare.py --report`: all 1165 datasets (1157 numeric, 8 text) in 56 cases -were bit-identical. +For the initial 2026-09-23 baseline, two independent generations were compared +with `compare.py --report`: all 1165 datasets (1157 numeric, 8 text) in the +original 56 cases were bit-identical. After the subsequently documented +reference additions and updates, the current inventory is 1236 compared +datasets (1228 numeric, 8 text) in 62 cases; see `MANIFEST.md` for the generated +current count. ## Variant builds (tolerance measurement) diff --git a/test/regression/README.md b/test/regression/README.md index 9fe6207..dbc9bbe 100644 --- a/test/regression/README.md +++ b/test/regression/README.md @@ -75,7 +75,8 @@ Each result file holds: `*.spex.dat` and `*.fits.dat` (spectrum and fit at every α; large, and covered by `chi2.dat`, the α probabilities and the `*_back.dat` files) are not packed. Bootstrap error output is deterministic (`SEED=0` by default) - and is covered by `t_generate_err`. + and is covered by `t_generate_err`; `t_generate_err_seed` verifies an + explicit non-default seed. * `/log/scalars/`: values parsed from the logs: minimal χ², posterior probability of the default model, Ng, number of singular values kept, and the number of α values where the minimizer hit `MAX_IT`. diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index fc6ccb6..5ce29a6 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -4,6 +4,14 @@ Every regeneration of files in `reference/` is logged here: date, commit, reason, affected cases, and the largest difference to the previous references (from `compare.py --report`). Newest entry first. +## 2026-09-24: explicit bootstrap seed coverage + +* **Source:** `modernize/step2.3c` after commit `8b2ff05`. +* **Reason:** added review-requested coverage proving that a non-default + `SEED` value is parsed and used, rather than only testing the default. +* **Cases:** added `t_generate_err_seed` with `SEED=1234`, including + `booterr.dat`. No existing reference changed. + ## 2026-09-24: log-grid float casts removed (B15) * **Source:** `modernize/step2.3c` after commit `b1d7c8b`. diff --git a/test/regression/cases.py b/test/regression/cases.py index 33b6714..8e21c54 100644 --- a/test/regression/cases.py +++ b/test/regression/cases.py @@ -67,6 +67,7 @@ ("t_grid_log", "grid: log", []), ("t_grid_linear", "grid: linear", []), ("t_generate_err", "bootstrap error bars with the default SEED=0", []), + ("t_generate_err_seed", "bootstrap error bars with explicit SEED=1234", []), ("t_model_runs", "MODEL_RUNS=2 (flat, gaussian): per-model outputs and varspec", []), ] diff --git a/test/regression/generate.py b/test/regression/generate.py index 79908d7..5c0afb1 100644 --- a/test/regression/generate.py +++ b/test/regression/generate.py @@ -114,12 +114,18 @@ def run_case(case, programs, outdir, provenance, timeout=TIMEOUT): before = {p.relative_to(workdir).as_posix() for p in workdir.rglob("*")} cmd = [str(programs[case["program"]])] + ([case["param"]] if case["param"] else []) + case["args"] p, seconds = run(cmd, workdir, timeout) - # Maxent's expected failure cases are classified as exceptions; any - # other program's nonzero exit is a generic failure. Exit status is - # authoritative so diagnostic wording cannot hide a failed command. - status = "timeout" if p.returncode is None else \ - "exception" if case["program"] == "maxent" and p.returncode != 0 else \ - "failed" if p.returncode != 0 else "ok" + # An expected Maxent exception has both the documented exit status and + # diagnostic signature. Crashes and other nonzero exits remain generic + # failures rather than accidentally satisfying an exception case. + if p.returncode is None: + status = "timeout" + elif (case["program"] == "maxent" and p.returncode == 1 + and "Caught Exception:" in p.stderr): + status = "exception" + elif p.returncode != 0: + status = "failed" + else: + status = "ok" with h5py.File(out, "w") as h5: for key in ("name", "set", "covers"): h5.attrs[key] = case[key] diff --git a/test/regression/inputs/t_generate_err_seed/case.param b/test/regression/inputs/t_generate_err_seed/case.param new file mode 100644 index 0000000..ad67256 --- /dev/null +++ b/test/regression/inputs/t_generate_err_seed/case.param @@ -0,0 +1,10 @@ +BETA=5.0 +NFREQ=40 +N_ALPHA=8 +NDAT=24 +DATASPACE=frequency +KERNEL=fermionic +PARTICLE_HOLE_SYMMETRY=true +DATA="data.dat" +GENERATE_ERR=true +SEED=1234 diff --git a/test/regression/inputs/t_generate_err_seed/data.dat b/test/regression/inputs/t_generate_err_seed/data.dat new file mode 100644 index 0000000..0761e7d --- /dev/null +++ b/test/regression/inputs/t_generate_err_seed/data.dat @@ -0,0 +1,24 @@ +0.62831853071795862 -0.80894880667637248 0.0001 +1.8849555921538759 -0.44019733388594223 0.0001 +3.1415926535897931 -0.29291600972048348 0.0001 +4.3982297150257104 -0.21719871564894569 0.0001 +5.6548667764616276 -0.17171978252239106 0.0001 +6.911503837897544 -0.14168934901630889 0.0001 +8.1681408993334621 -0.120751250037853 0.0001 +9.4247779607693793 -0.10487493631655458 0.0001 +10.681415022205297 -0.092788745501617295 0.0001 +11.938052083641214 -0.083292221267268457 0.0001 +13.194689145077131 -0.07531290771598094 0.0001 +14.451326206513048 -0.068880601972683461 0.0001 +15.707963267948966 -0.063455290123107522 0.0001 +16.964600329384883 -0.058928770545329384 0.0001 +18.2212373908208 -0.054671549118618913 0.0001 +19.477874452256717 -0.05110961361960175 0.0001 +20.734511513692635 -0.048125331414762759 0.0001 +21.991148575128552 -0.045274588841275706 0.0001 +23.247785636564469 -0.043109624384509007 0.0001 +24.504422698000386 -0.040458368685770935 0.0001 +25.7610597594363 -0.038700189324099167 0.0001 +27.017696820872221 -0.036979006682130645 0.0001 +28.274333882308138 -0.035553307818426777 0.0001 +29.530970943744059 -0.033876214481664849 0.0001 diff --git a/test/regression/make_inputs.py b/test/regression/make_inputs.py index 4f0722d..b7f904b 100644 --- a/test/regression/make_inputs.py +++ b/test/regression/make_inputs.py @@ -309,13 +309,16 @@ def make(name, case_dir): grid = {"lorentzian": "lorentzian", "half_lorentzian": "half-lorentzian", "quadratic": "quadratic", "log": "log", "linear": "linear"}[name[len("t_grid_"):]] base_frequency_ph(case_dir, name, [("FREQUENCY_GRID", grid)]) - elif name == "t_generate_err": + elif name in ("t_generate_err", "t_generate_err_seed"): wn, y = freq_ph_data(name) write_columns(case_dir / "data.dat", wn, y, np.full_like(y, NOISE)) - write_param(case_dir, [("BETA", BETA), ("NFREQ", 40), ("N_ALPHA", 8), - ("NDAT", wn.size), ("DATASPACE", "frequency"), - ("KERNEL", "fermionic"), ("PARTICLE_HOLE_SYMMETRY", "true"), - ("DATA", '"data.dat"'), ("GENERATE_ERR", "true")]) + entries = [("BETA", BETA), ("NFREQ", 40), ("N_ALPHA", 8), + ("NDAT", wn.size), ("DATASPACE", "frequency"), + ("KERNEL", "fermionic"), ("PARTICLE_HOLE_SYMMETRY", "true"), + ("DATA", '"data.dat"'), ("GENERATE_ERR", "true")] + if name == "t_generate_err_seed": + entries.append(("SEED", 1234)) + write_param(case_dir, entries) elif name in ("kk_imag_to_real_green", "kk_imag_to_real_self"): source, dataset, scale = { "kk_imag_to_real_green": ("u0_frequency", "files/in.out.avspec.dat", -np.pi), diff --git a/test/regression/reference/t_generate_err_seed.h5 b/test/regression/reference/t_generate_err_seed.h5 new file mode 100644 index 0000000000000000000000000000000000000000..e12fd1bd5d354925edf833ff0c4bed24f22d4182 GIT binary patch literal 33656 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 Date: Thu, 24 Sep 2026 16:02:36 +0200 Subject: [PATCH 9/9] Isolate and test regression validation --- test/regression/CMakeLists.txt | 4 ++ test/regression/PROVENANCE.md | 7 +-- test/regression/REFERENCE_CHANGES.md | 4 +- test/regression/generate.py | 21 ++++---- .../inputs/t_generate_err_seed/data.dat | 48 +++++++++--------- test/regression/make_inputs.py | 3 +- .../reference/t_generate_err_seed.h5 | Bin 33656 -> 33656 bytes test/regression/test_generate.py | 32 ++++++++++++ 8 files changed, 81 insertions(+), 38 deletions(-) create mode 100644 test/regression/test_generate.py diff --git a/test/regression/CMakeLists.txt b/test/regression/CMakeLists.txt index 97e109b..8882557 100644 --- a/test/regression/CMakeLists.txt +++ b/test/regression/CMakeLists.txt @@ -27,6 +27,10 @@ if(NOT have_python_modules) return() endif() +add_test(NAME regression_harness_unit + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_generate.py) +set_tests_properties(regression_harness_unit PROPERTIES LABELS unit) + function(maxent_regression_test name sets label) set(results ${CMAKE_CURRENT_BINARY_DIR}/results-${name}) add_test(NAME regression_${name}_generate diff --git a/test/regression/PROVENANCE.md b/test/regression/PROVENANCE.md index 06f392c..8f3e1b7 100644 --- a/test/regression/PROVENANCE.md +++ b/test/regression/PROVENANCE.md @@ -1,9 +1,10 @@ # Provenance of the references -The references in `reference/` were generated on 2026-09-23 from the +Most references in `reference/` were generated on 2026-09-23 from the **original numerics** of Maxent, before any modernization of the code. -The same information is stored as `provenance.*` attributes at the root of -every reference file. +References added or deliberately regenerated later are identified in the +dated sections below and in `REFERENCE_CHANGES.md`. Corresponding provenance +is stored as `provenance.*` attributes at each reference root. ## Source diff --git a/test/regression/REFERENCE_CHANGES.md b/test/regression/REFERENCE_CHANGES.md index 5ce29a6..ebb3feb 100644 --- a/test/regression/REFERENCE_CHANGES.md +++ b/test/regression/REFERENCE_CHANGES.md @@ -10,7 +10,9 @@ references (from `compare.py --report`). Newest entry first. * **Reason:** added review-requested coverage proving that a non-default `SEED` value is parsed and used, rather than only testing the default. * **Cases:** added `t_generate_err_seed` with `SEED=1234`, including - `booterr.dat`. No existing reference changed. + `booterr.dat`. It uses the exact input of the default-seed case, isolating + the seed as the only cause of bootstrap differences. No existing reference + changed. ## 2026-09-24: log-grid float casts removed (B15) diff --git a/test/regression/generate.py b/test/regression/generate.py index 5c0afb1..f59208c 100644 --- a/test/regression/generate.py +++ b/test/regression/generate.py @@ -62,6 +62,17 @@ def run(cmd, cwd, timeout=TIMEOUT): return p, time.monotonic() - t0 +def classify_status(program, returncode, stderr): + """Classify command completion without mistaking crashes for exceptions.""" + if returncode is None: + return "timeout" + if program == "maxent" and returncode == 1 and "Caught Exception:" in stderr: + return "exception" + if returncode != 0: + return "failed" + return "ok" + + def pack_outputs(h5, workdir, before): files = h5.create_group("files") for path in sorted(workdir.rglob("*")): @@ -117,15 +128,7 @@ def run_case(case, programs, outdir, provenance, timeout=TIMEOUT): # An expected Maxent exception has both the documented exit status and # diagnostic signature. Crashes and other nonzero exits remain generic # failures rather than accidentally satisfying an exception case. - if p.returncode is None: - status = "timeout" - elif (case["program"] == "maxent" and p.returncode == 1 - and "Caught Exception:" in p.stderr): - status = "exception" - elif p.returncode != 0: - status = "failed" - else: - status = "ok" + status = classify_status(case["program"], p.returncode, p.stderr) with h5py.File(out, "w") as h5: for key in ("name", "set", "covers"): h5.attrs[key] = case[key] diff --git a/test/regression/inputs/t_generate_err_seed/data.dat b/test/regression/inputs/t_generate_err_seed/data.dat index 0761e7d..d74bcda 100644 --- a/test/regression/inputs/t_generate_err_seed/data.dat +++ b/test/regression/inputs/t_generate_err_seed/data.dat @@ -1,24 +1,24 @@ -0.62831853071795862 -0.80894880667637248 0.0001 -1.8849555921538759 -0.44019733388594223 0.0001 -3.1415926535897931 -0.29291600972048348 0.0001 -4.3982297150257104 -0.21719871564894569 0.0001 -5.6548667764616276 -0.17171978252239106 0.0001 -6.911503837897544 -0.14168934901630889 0.0001 -8.1681408993334621 -0.120751250037853 0.0001 -9.4247779607693793 -0.10487493631655458 0.0001 -10.681415022205297 -0.092788745501617295 0.0001 -11.938052083641214 -0.083292221267268457 0.0001 -13.194689145077131 -0.07531290771598094 0.0001 -14.451326206513048 -0.068880601972683461 0.0001 -15.707963267948966 -0.063455290123107522 0.0001 -16.964600329384883 -0.058928770545329384 0.0001 -18.2212373908208 -0.054671549118618913 0.0001 -19.477874452256717 -0.05110961361960175 0.0001 -20.734511513692635 -0.048125331414762759 0.0001 -21.991148575128552 -0.045274588841275706 0.0001 -23.247785636564469 -0.043109624384509007 0.0001 -24.504422698000386 -0.040458368685770935 0.0001 -25.7610597594363 -0.038700189324099167 0.0001 -27.017696820872221 -0.036979006682130645 0.0001 -28.274333882308138 -0.035553307818426777 0.0001 -29.530970943744059 -0.033876214481664849 0.0001 +0.62831853071795862 -0.80874290991409159 0.0001 +1.8849555921538759 -0.44014716084961508 0.0001 +3.1415926535897931 -0.29286728018013575 0.0001 +4.3982297150257104 -0.21712083835923829 0.0001 +5.6548667764616276 -0.17165635623034461 0.0001 +6.911503837897544 -0.14166307358889488 0.0001 +8.1681408993334621 -0.12060505637657916 0.0001 +9.4247779607693793 -0.10485266703834568 0.0001 +10.681415022205297 -0.092950172398290121 0.0001 +11.938052083641214 -0.083168742498363424 0.0001 +13.194689145077131 -0.075316139276522379 0.0001 +14.451326206513048 -0.068980526475614631 0.0001 +15.707963267948966 -0.063426614004070431 0.0001 +16.964600329384883 -0.058669718153089162 0.0001 +18.2212373908208 -0.054868212835967246 0.0001 +19.477874452256717 -0.051194487824511997 0.0001 +20.734511513692635 -0.048183803227436109 0.0001 +21.991148575128552 -0.045353889185727227 0.0001 +23.247785636564469 -0.04311132391388374 0.0001 +24.504422698000386 -0.040812383784974739 0.0001 +25.7610597594363 -0.038789286218401768 0.0001 +27.017696820872221 -0.037114935461959209 0.0001 +28.274333882308138 -0.035393491249622627 0.0001 +29.530970943744059 -0.033879643568396858 0.0001 diff --git a/test/regression/make_inputs.py b/test/regression/make_inputs.py index b7f904b..eba4efa 100644 --- a/test/regression/make_inputs.py +++ b/test/regression/make_inputs.py @@ -310,7 +310,8 @@ def make(name, case_dir): "quadratic": "quadratic", "log": "log", "linear": "linear"}[name[len("t_grid_"):]] base_frequency_ph(case_dir, name, [("FREQUENCY_GRID", grid)]) elif name in ("t_generate_err", "t_generate_err_seed"): - wn, y = freq_ph_data(name) + # Keep the scientific input identical so the two cases isolate SEED. + wn, y = freq_ph_data("t_generate_err") write_columns(case_dir / "data.dat", wn, y, np.full_like(y, NOISE)) entries = [("BETA", BETA), ("NFREQ", 40), ("N_ALPHA", 8), ("NDAT", wn.size), ("DATASPACE", "frequency"), diff --git a/test/regression/reference/t_generate_err_seed.h5 b/test/regression/reference/t_generate_err_seed.h5 index e12fd1bd5d354925edf833ff0c4bed24f22d4182..7dca1ee6807de3c3555b3138fbbdfb7dfe81aeb0 100644 GIT binary patch literal 33656 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 MHUw-4JRb!957dpVc>n+a literal 33656 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