Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions ALPS3_MIGRATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -406,20 +408,29 @@ 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)

* 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).

**2.3D: numerical changes** (each its own commit and `REFERENCE_CHANGES.md` entry)

* B7b: time-bosonic kernel: use the ω→0 limit only at ω = 0.
* B15: remove the `float` casts in the log grid.
* B11 (optional): `BDCSVD` instead of `JacobiSVD`; measure against the
references before deciding.
**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`.
* **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 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)

* **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`).
* **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.

Expand Down
14 changes: 8 additions & 6 deletions src/maxent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

#include "maxent.hpp"
#include <alps/utilities/fs/remove_extensions.hpp>
#include <boost/exception/diagnostic_information.hpp>


int main(int argc,const char** argv)
{
try {
alps::params parms(argc,argv);
MaxEntSimulation::define_parameters(parms);
//other help messages
Expand Down Expand Up @@ -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 <<std::left << std::setw(15)<< "Grid Name" <<'\t' << "option=default" << std::endl;
std::cout <<std::left << std::setw(15)<< "=========" <<'\t' << "==============" << std::endl;
std::cout <<std::left << std::setw(15)<< "lorentzian" <<'\t' << "CUT=0.1" << "\n"
<<std::left << std::setw(15)<< "half-lorentzian"<<'\t' << "CUT=0.1" << "\n"
std::cout <<std::left << std::setw(15)<< "lorentzian" <<'\t' << "CUT=0.01" << "\n"
<<std::left << std::setw(15)<< "half-lorentzian"<<'\t' << "CUT=0.01" << "\n"
<<std::left << std::setw(15)<< "quadratic" <<'\t' << "SPREAD=4" << "\n"
<<std::left << std::setw(15)<< "log" <<'\t' << "LOG_MIN=0.0001" << "\n"
<<std::left << std::setw(15)<< "linear" <<'\t' << "---" << std::endl;
Expand Down Expand Up @@ -95,7 +95,6 @@ int main(int argc,const char** argv)
else
basename=parms["BASENAME"].as<std::string>();

try{
if(exitEarly){
throw std::runtime_error("Critical parameters not defined");
}
Expand Down Expand Up @@ -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 Exception: unknown exception\n";
return 1;
}
}
1 change: 1 addition & 0 deletions src/maxent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions src/maxent_grid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ t_array_(nfreq_+1){
if (p_f_grid =="lorentzian") {
initialize_lorentzian_grid(cut);
}
else if (p_f_grid=="half lorentzian") {
else if (p_f_grid=="half lorentzian" || p_f_grid=="half-lorentzian") {
initialize_half_lorentzian_grid(cut);
}
else if (p_f_grid=="quadratic") {
Expand All @@ -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<double>(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<double>(i) * scale);
t_array_[nfreq_ / 2 - i - 1] = 0.5
- t_min * std::exp(((float) (i)) * scale);
- t_min * std::exp(static_cast<double>(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<double>(nfreq_) / 2 * scale);
}

void grid::initialize_quadratic_grid(double spread) {
Expand Down Expand Up @@ -100,5 +100,3 @@ void grid::initialize_lorentzian_grid(double cut) {
}




8 changes: 4 additions & 4 deletions src/maxent_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "maxent.hpp"
#include <cmath>
#include <ctime>
#include <Eigen/Eigenvalues>
#include <Eigen/Cholesky>
#include "maxent_backcont.hpp"
Expand All @@ -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<std::mt19937::result_type>()),
def_(nfreq()), text_output(p["TEXT_OUTPUT"])
{
for (int i=0; i<nfreq(); ++i)
def_[i] = MaxEntParameters::Default().D(omega_coord(i)) * delta_omega(i);
Expand Down Expand Up @@ -355,8 +356,7 @@ void MaxEntHelper::generateCovariantErr(const vector_type& A, const double alpha
A_u(i) = sqrt(A(i));
A_u = u*A_u;

std::mt19937 rng;
rng.seed(static_cast<unsigned int>(std::time(0)));
std::mt19937 rng(bootstrap_seed_);

std::vector<vector_type> noise_vecs;
int max_it = 10000;
Expand Down
6 changes: 3 additions & 3 deletions src/maxent_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ K_(ndat_,nfreq_)
else if (ktype_==time_bosonic_kernel) {
for (int i=0; i<ndat_; ++i) {
double tau=tau_points_[i];
K_(i,0) = T_;
for (int j=1; j<nfreq_; ++j) {
for (int j=0; j<nfreq_; ++j) {
double omega = freq[j];
K_(i,j) = 0.5*omega * (std::exp(-omega*tau) + std::exp(-omega*(1./T_-tau))) / (1 - std::exp(-omega/T_));
K_(i,j) = omega == 0.0 ? T_ :
0.5*omega * (std::exp(-omega*tau) + std::exp(-omega*(1./T_-tau))) / (1 - std::exp(-omega/T_));
if(std::isnan(K_(i,j))) K_(i,j)=0; //the limit of the function above for omega -> -Infity
}
}
Expand Down
1 change: 1 addition & 0 deletions src/maxent_simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void MaxEntSimulation::define_parameters(alps::params &p){
p.define<double>("NORM",1.0,"NORM");
p.define<bool>("BACKCONTINUE",true,"Output A(omega) back to imaginary axis");
p.define<bool>("GENERATE_ERR",false,"Generate a bootstrap approximation for error bars");
p.define<unsigned int>("SEED",0,"Seed for reproducible bootstrap error bars");
//*********************************
p.define<double>("BETA","beta, inverse temperature");
p.define<int>("NDAT","# of input points");
Expand Down
17 changes: 15 additions & 2 deletions test/gridTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,21 @@ TEST(Grid,HalfLorentzianEven){

grid g(p);
EXPECT_EQ(g(0),0);
EXPECT_EQ(g(NFREQ),1);
EXPECT_EQ(g(NFREQ),1);
}
TEST(Grid,HalfLorentzianHyphenAlias){
alps::params p;
MaxEntSimulation::define_parameters(p);
const int NFREQ=20;
p["NFREQ"] = NFREQ;

p["FREQUENCY_GRID"] = "half lorentzian";
grid spaced(p);
p["FREQUENCY_GRID"] = "half-lorentzian";
grid hyphenated(p);

for(int i=0;i<=NFREQ;i++)
EXPECT_EQ(hyphenated(i),spaced(i));
}
TEST(Grid,QuadraticEven){
alps::params p;
Expand Down Expand Up @@ -147,4 +161,3 @@ TEST(Grid,LinearEven){
}



4 changes: 4 additions & 0 deletions test/regression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 9 additions & 7 deletions test/regression/MANIFEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ 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_grid_half_lorentzian` | grid: half lorentzian | `test/regression/inputs/t_grid_half_lorentzian` | 20 | 0.1 s | B16 (help text says 'half-lorentzian') |
| `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 | |
| `t_grid_lorentzian` | grid: lorentzian | `test/regression/inputs/t_grid_lorentzian` | 20 | 0.1 s | |
Expand All @@ -33,7 +35,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 | |
Expand All @@ -57,9 +59,9 @@ Generated by `manifest.py` from `reference/`, `cases.py` and `tolerances.json`.
| Case | Covers | Inputs | Datasets | Runtime | Flags |
|---|---|---|---|---|---|
| `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_grids` | --help.grids output (`--help.grids`) | none | 8 | 0.0 s | |
| `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)

Expand Down Expand Up @@ -95,7 +97,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

Expand All @@ -119,10 +121,10 @@ Rules are matched in order against '<case>:<dataset>'; 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: 1236 in 62 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).
35 changes: 29 additions & 6 deletions test/regression/PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -54,11 +55,33 @@ 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. `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)

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
`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)

Expand Down
6 changes: 4 additions & 2 deletions test/regression/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ 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`; `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`.
Expand Down
Loading
Loading