diff --git a/ALPS3_MIGRATION_PLAN.md b/ALPS3_MIGRATION_PLAN.md index 5e9e573..2af9773 100644 --- a/ALPS3_MIGRATION_PLAN.md +++ b/ALPS3_MIGRATION_PLAN.md @@ -361,29 +361,62 @@ and Boost.Math in `legendre_convert`. ### 2.3 Code fixes -* Fix B1 through B20 (§1.6), including B7b. B21 (logging convention and verbosity) can go here or into a later step; it changes the CLI regression references on purpose. -* Moved here from 2.2 (they change the CLI references on purpose): replace - `boost::diagnostic_information` by `e.what()` (with B1), add a `SEED` - parameter for the bootstrap (B10), and delete the commented-out ublas and - LAPACK-bindings code. -* `legendre_convert` (decided 2026-09-24), in this order: - 1. Add regression cases for `legendre_convert` first (it has no tests), with - references from the current Boost build, like the `kk` cases in 2.2. - 2. Replace Boost.Random by ``. The `mt19937` engine gives identical - numbers; the normal variates differ (different algorithm), which is - harmless because the error estimate is seeded from the clock. - 3. Replace `boost::math::factorial` by a product (only small arguments - occur). - 4. Replace `boost::math::legendre_p` by the standard three-term recurrence. - `std::legendre` is not an option: libc++ (AppleClang) does not implement - the C++17 special math functions. - Keep `boost::math::sph_bessel` (also missing in libc++; our own version - would need careful checking at large l and argument) and `program_options` - (no standard equivalent). B5, B6 and B7 change reachable behavior, so each gets its own commit with a before/after test. -* Remove all `using namespace boost::numeric;` lines, `#include `, dead commented-out ublas and lapack-bindings code, and the unused `alps::cast`. -* Make `eigen_hdf5.hpp`/`eigen_lapack.hpp` functions `inline`, or move them into `.cpp` files (B8). -* Use `Eigen::Index` for loop indices (B14). Consider `BDCSVD` in place of `JacobiSVD` (B11); that one is a numerics change and needs checking against the references. -* Keep the `MaxEntSimulation` public getters stable, because the tests use them. +Grouped by what each item does to the regression references (agreed +2026-09-24). One PR per group, in this order, each with a Copilot review. +Already done elsewhere: B2, B3 (2.1), B7 (`xi_bose_bugfix`), B20 (Pade removed). + +**2.3A: no change to results** (references must stay bit-identical) + +**Done 2026-09-24.** All Maxent-owned code and utilities build with +`-Wall -Wextra -Wpedantic -Werror`; the fast and full regression suites remain +unchanged. A text-input regression covers trailing blank lines. + +* Fix all compiler warnings (44 at the start of 2.3): B14 (`std::size_t` vs + `Eigen::Index` loops), B12 (member initialization order), unused variables, + misleading indentation, B13 (deprecated `params::get_origin_name()`; the + default `BASENAME` must not change). Then turn on `MAXENT_WERROR` in the `dev` + preset. +* B8: make the functions in `eigen_hdf5.hpp`/`eigen_lapack.hpp` `inline`. +* B4: make the size check in `Backcont::max_error` unconditional, avoiding an + out-of-bounds access in every build type. +* B5, B6: remove the unreachable kernel branches. +* B9: the data-file reading loop (`while (datstream)` reads past EOF); same + results for valid files, plus a test with trailing blank lines. +* Remove the commented-out ublas and LAPACK-bindings code, `` + includes and the unused `alps::cast`. + +**2.3B: `legendre_convert`** (decided 2026-09-24), in this order: + +1. Add regression cases for `legendre_convert` first (it has no tests), with + references from the current Boost build, like the `kk` cases in 2.2. +2. Replace Boost.Random by ``. The `mt19937` engine gives identical + numbers; the normal variates differ (different algorithm), which is + harmless because the error estimate is seeded from the clock. +3. Replace `boost::math::factorial` by a product (only small arguments occur). +4. Replace `boost::math::legendre_p` by the standard three-term recurrence. + `std::legendre` is not an option: libc++ (AppleClang) does not implement the + C++17 special math functions. + +Keep `boost::math::sph_bessel` (also missing in libc++; our own version would +need careful checking at large l and argument) and `program_options` (no +standard equivalent). + +**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. + +Keep the `MaxEntSimulation` public getters stable, because the tests use them. ### 2.4 Hygiene and CI @@ -406,6 +439,17 @@ This is the last task of step 2, still on ALPSCore: 2. **HDF5 seam:** move all archive access into `maxent_io_hdf5.{hpp,cpp}`, with functions such as `write_vector(path, Eigen::VectorXd)`, `read_vector(path)` and `write_params(...)`. Nothing else includes ``. 3. Tests use `std::filesystem::temp_directory_path()` in place of `alps::temporary_filename`. +### 2.6 Physics and algorithms (after 2.3–2.5) + +Moved out of 2.3 (2026-09-24) because they are physics or algorithm changes +rather than cleanup; each needs its own decision: + +* B17: Legendre bosonic kernel (currently the fermionic formula). +* B18: default grid when `OMEGA_MIN=0` (change the default, or warn). +* B19: minimizer divergence (step-size control or a trust region). +* B21: logging convention and verbosity (issue #46). +* B22: performance of the core at large NFREQ. + **Step 2 acceptance:** * Configures with CMake 3.22 through 4.x. @@ -640,3 +684,4 @@ make -j8 && ctest # 6/6 executables, 35 cases pass, ~9 s | 2026-09-23 | 2.x | Pade removed (D7), on branch `modernize/remove-pade`: `pade/` (13 files: 6 built, 5 unbuilt alternatives, header, CMake), `MAXENT_BUILD_PADE`, and the README section. It required GMP (`mpf_class`, 256-bit default precision) and did not compile (B20). | | 2026-09-24 | 2.2 | Step 2.2 done on `modernize/step2.2`: kk regression cases, kk spline, closed-form Legendre kernel (GSL removed), Boost utilities → std. No reference changed; 41/41 tests. ALPSCore#668 merged (GoogleTest 1.18, not installed, CMake 3.16). Found B22 (performance). | | 2026-09-24 | sync | Synced with ALPSCore `master` `560ae112` (#669: C++14 required and default, ALPSCore compiled with exactly `ALPS_CXX_STD`, Boost.Math dependency removed; #670: Doxygen removed). Maxent `d529a14` against it (installed as C++14, Boost 1.88): configures without `Boost_DIR`, compiles as C++17, 41/41 tests, regression unchanged (only the known Legendre rounding-level differences from 2.2). Note: build ALPSCore in a fresh build directory; an old one keeps a stale compiler identification that Maxent's configure then rejects. | +| 2026-09-24 | 2.3A | Warning cleanup and no-output-change fixes complete: Eigen index types, initializer order, unused/deprecated code, inline HDF5/LAPACK helpers, unconditional size validation, unreachable kernel branches, robust three- and five-column text-file extraction, and dead bindings/config code removed. The dev preset enables `MAXENT_WERROR`; 44/44 Release tests pass including full regression, and the LAPACK build is warning-free. | diff --git a/CMakePresets.json b/CMakePresets.json index 890f970..8b4d5b7 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -20,7 +20,10 @@ "name": "dev", "displayName": "Debug", "inherits": "base", - "cacheVariables": {"CMAKE_BUILD_TYPE": "Debug"} + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "MAXENT_WERROR": "ON" + } }, { "name": "asan", diff --git a/legendre_convert/legendre_convert.cpp b/legendre_convert/legendre_convert.cpp index c4f9396..cedbf56 100644 --- a/legendre_convert/legendre_convert.cpp +++ b/legendre_convert/legendre_convert.cpp @@ -324,7 +324,7 @@ double GtBoot(vector_type gl_in, void *arg){ //************************ int main(int argc, char**argv){ namespace po = boost::program_options; - double beta,c1,c2,c3; + double beta,c1,c2; int lmax,numConvergence,maxit,maxn; bool backcontinue = false,enforceTailOff = false,enforceErrCheck=true,continueMatsubara=false; std::string input_gtau_filename,gl_filename; diff --git a/src/eigen_hdf5.hpp b/src/eigen_hdf5.hpp index ad440c1..cdc0073 100644 --- a/src/eigen_hdf5.hpp +++ b/src/eigen_hdf5.hpp @@ -11,7 +11,6 @@ #pragma once #include -#include #include #include @@ -32,7 +31,7 @@ namespace alps { if (value.size()) { std::vector first(get_extent(value[0])); if (true) - for(std::size_t i=1;i size(get_extent(value[i])); if ( first.size() != size.size() @@ -46,7 +45,7 @@ namespace alps { } }; } - void save( + inline void save( archive &ar , std::string const & path , Eigen::VectorXd &value @@ -54,7 +53,6 @@ namespace alps { , std::vector chunk =std::vector() , std::vector offset = std::vector() ) { - using alps::cast; if (ar.is_group(path)) ar.delete_group(path); if (is_continuous::value && value.size() == 0) diff --git a/src/eigen_lapack.hpp b/src/eigen_lapack.hpp index 9d55c64..2755c34 100644 --- a/src/eigen_lapack.hpp +++ b/src/eigen_lapack.hpp @@ -21,9 +21,7 @@ extern "C" void dgesvd_( const char* jobu, const char* jobvt, ///performs a SVD on input matrix K /// returns K = U (S) V^T where S is a vector of the diagonal values of /// matrix Sigma -void lapack_svd(matrix_type &K, vector_type &S, matrix_type &Vt, matrix_type &U){ - - /*boost::numeric::bindings::lapack::gesvd('S', 'S', Kt, S, U_, Vt_);*/ +inline void lapack_svd(matrix_type &K, vector_type &S, matrix_type &Vt, matrix_type &U){ //use 'S' for thin U,Vt matrices char jobu = 'S'; char jobvt = 'S'; diff --git a/src/maxent.cpp b/src/maxent.cpp index 3ce77f9..8d4639a 100644 --- a/src/maxent.cpp +++ b/src/maxent.cpp @@ -89,7 +89,7 @@ int main(int argc,const char** argv) std::string basename; if(parms.defaulted("BASENAME")){ - basename = alps::fs::remove_extensions(parms.get_origin_name()) + ".out"; + basename = alps::fs::remove_extensions(alps::origin_name(parms)) + ".out"; parms["BASENAME"] = basename; } else @@ -151,7 +151,7 @@ int main(int argc,const char** argv) ofstream_ spec_file; spec_file.open((basename+".varspec.dat").c_str()); spec_file << "#omega mean_maxspec stdev_maxspec mean_avspec stdev_avspec" <max_err){ max_err = delta; diff --git a/src/maxent_backcont.hpp b/src/maxent_backcont.hpp index 8acaafc..d3f5edf 100644 --- a/src/maxent_backcont.hpp +++ b/src/maxent_backcont.hpp @@ -11,7 +11,6 @@ #pragma once #include "maxent.hpp" -#include // needed to set up correct bindings ///This class contains necessities to analytically continue a //real function to the imaginary axis diff --git a/src/maxent_helper.cpp b/src/maxent_helper.cpp index 4d7ca75..78f2624 100644 --- a/src/maxent_helper.cpp +++ b/src/maxent_helper.cpp @@ -10,7 +10,6 @@ *****************************************************************************/ #include "maxent.hpp" -#include // needed to set up correct bindings #include #include #include @@ -36,7 +35,7 @@ MaxEntParameters(p) , def_(nfreq()), text_output(p["TEXT_OUTPUT"]) /// this is needed for transform_into_singular_space /// to work safely void MaxEntHelper::checkDefaultModel(const vector_type &D) const{ - for(int i=0;igetKernelType(); - double beta = 1/(pp->T()); bool ph_sym = true; if(k_type == frequency_fermionic_kernel || k_type == frequency_bosonic_kernel || @@ -256,26 +250,26 @@ void MaxEntHelper::backcontinue(ofstream_ &os, const vector_type &A_in,const dou ext_back = G*norm; if(text_output){ if(ph_sym){ - for(int n=0; ninputGrid(n) << " " << G(n)*norm << std::endl; } } else{ - for(int n=0;ninputGrid(n/2) << " " << G(n)*norm << " " << G(n+1)*norm << std::endl; } } } //scale y by error then determine 'error' of integral vector_type y_scaled = y(); - for(int i=0;i &in,vector_type &mean, vector_ty } mean /= in.size(); //compute stddev - for(int i=0;i // needed to set up correct bindings #include "maxent_config.hpp" #include diff --git a/src/maxent_params.cpp b/src/maxent_params.cpp index bdfb493..5f61e88 100644 --- a/src/maxent_params.cpp +++ b/src/maxent_params.cpp @@ -11,7 +11,6 @@ #include "maxent.hpp" #include "eigen_lapack.hpp" -#include // needed to set up correct bindings #include #include #include @@ -51,9 +50,12 @@ void ContiParameters::read_data_from_text_file(const alps::params& p) { std::string dataspace = p["DATASPACE"].as(); to_lower(dataspace); if(dataspace == "time" || dataspace == "legendre" || p["PARTICLE_HOLE_SYMMETRY"]==true){ - while (datstream) { - double index, X_i, dX_i; - datstream >> index >> X_i >> dX_i; + double index, X_i, dX_i; + while (true) { + datstream >> std::ws; + if (datstream.eof()) break; + if (!(datstream >> index >> X_i >> dX_i)) + throw std::runtime_error("malformed data record in: " + fname); if (datIn < ndat()) { inputGrid_(datIn) = index; y_(datIn) = X_i / static_cast(p["NORM"]); @@ -62,7 +64,6 @@ void ContiParameters::read_data_from_text_file(const alps::params& p) { } expectedDatIn++; } - expectedDatIn-=1; } else{ if(ndat()%2 != 0){ @@ -71,9 +72,12 @@ void ContiParameters::read_data_from_text_file(const alps::params& p) { << std::endl; throw std::runtime_error("Your NDAT is odd!");\ } - while (datstream) { - double index, X_i_re, dX_i_re, X_i_im, dX_i_im; - datstream >> index >> X_i_re >> dX_i_re >> X_i_im >> dX_i_im; + double index, X_i_re, dX_i_re, X_i_im, dX_i_im; + while (true) { + datstream >> std::ws; + if (datstream.eof()) break; + if (!(datstream >> index >> X_i_re >> dX_i_re >> X_i_im >> dX_i_im)) + throw std::runtime_error("malformed complex data record in: " + fname); if (datIn < ndat()) { inputGrid_(datIn) = index; inputGrid_(datIn+1) = index; @@ -83,11 +87,8 @@ void ContiParameters::read_data_from_text_file(const alps::params& p) { sigma_(datIn+1) = dX_i_im / static_cast(p["NORM"]); datIn+=2; } - expectedDatIn++; + expectedDatIn+=2; } - //fix for matsubara length - expectedDatIn*=2; - expectedDatIn-=1; } if(p["COVARIANCE_MATRIX"]!="") { std::string fname = p["COVARIANCE_MATRIX"]; @@ -117,13 +118,13 @@ void ContiParameters::read_data_from_hdf5_file(const alps::params& p) { std::stringstream path; path << "/Data"; ar >> alps::make_pvp(path.str(), tmp); - for (std::size_t i = 0; i < ndat(); i++) + for (int i = 0; i < ndat(); i++) y_(i) = tmp[i] / static_cast(p["NORM"]); path.str(""); if (p["COVARIANCE_MATRIX"]=="") { path << "/Error"; ar >> alps::make_pvp(path.str(), tmp); - for (std::size_t i = 0; i < ndat(); i++) + for (int i = 0; i < ndat(); i++) sigma_(i) = tmp[i] / static_cast(p["NORM"]); } else { path << "/Covariance"; @@ -131,8 +132,8 @@ void ContiParameters::read_data_from_hdf5_file(const alps::params& p) { tmp.clear(); tmp.resize(ndat() * ndat()); ar >> alps::make_pvp(path.str(), tmp); - for (std::size_t i = 0; i < ndat(); i++) - for (std::size_t j = 0; j < ndat(); j++) + for (int i = 0; i < ndat(); i++) + for (int j = 0; j < ndat(); j++) cov_(i, j) = tmp[i * ndat() + j]; } } @@ -225,7 +226,6 @@ void ContiParameters::read_covariance_matrix_from_text_file( void ContiParameters::decompose_covariance_matrix(const alps::params& p){ vector_type var(ndat()); - //bindings::lapack::syev('V', bindings::upper(cov_) , var, bindings::lapack::optimal_workspace()); //TODO: check if this truly implements lapack's expected overwrite of cov_ Eigen::SelfAdjointEigenSolver es(cov_); var=es.eigenvalues(); @@ -292,17 +292,12 @@ void MaxEntParameters::truncate_to_singular_space(const vector_type& S) { void MaxEntParameters::singular_value_decompose_kernel(bool verbose, vector_type& S) { - /*boost::numeric::bindings::lapack::gesvd('S', 'S', Kt, S, U_, Vt_); - */ - #ifdef HAVE_LAPACK matrix_type Kt = K_; // gesvd destroys K! lapack_svd(Kt,S, Vt_,U_); #else - const double threshold = std::sqrt(std::numeric_limits::epsilon()) - * nfreq(); Eigen::JacobiSVD svd(K_,Eigen::ComputeThinU | Eigen::ComputeThinV); //svd.setThreshold(threshold); S=svd.singularValues(); @@ -318,7 +313,7 @@ void MaxEntParameters::singular_value_decompose_kernel(bool verbose, std::cout << "# eps = " << sqrt(std::numeric_limits::epsilon()) << std::endl << "# prec = " << prec << std::endl; - for (unsigned int s = 0; s < S.size(); ++s) { + for (Eigen::Index s = 0; s < S.size(); ++s) { if (verbose) std::cout << "# " << s << "\t" << S[s] << "\n"; @@ -387,10 +382,12 @@ MaxEntParameters::MaxEntParameters(alps::params& p) : k_type = ker.getKernelType(); //scale lhs and rhs according to errors, etc. - if (p["COVARIANCE_MATRIX"]!="") + if (p["COVARIANCE_MATRIX"]!="") { decompose_covariance_matrix(p); - - check_high_frequency_limit(y(),k_type); + } + + // This diagnostic applies to both diagonal errors and covariance input. + check_high_frequency_limit(y(),k_type); //Look around Eq. D.5 in Sebastian's thesis. We have sigma_ = sqrt(eigenvalues of covariance matrix) or, //in case of a diagonal covariance matrix, we have sigma_=SIGMA_X. @@ -411,6 +408,3 @@ MaxEntParameters::MaxEntParameters(alps::params& p) : compute_minimal_chi2(); } - - - diff --git a/src/maxent_simulation.cpp b/src/maxent_simulation.cpp index fe4ead8..5282782 100644 --- a/src/maxent_simulation.cpp +++ b/src/maxent_simulation.cpp @@ -10,7 +10,6 @@ *****************************************************************************/ #include "maxent.hpp" -#include // needed to set up correct bindings #include #include #include @@ -27,8 +26,8 @@ MaxEntSimulation::MaxEntSimulation(alps::params &parms) , self(parms["SELF"]) , make_back(parms["BACKCONTINUE"]) , gen_err(parms["GENERATE_ERR"]) -, qvec((int)parms["N_ALPHA"]) , nfreq(parms["NFREQ"].as()) +, qvec((int)parms["N_ALPHA"]) { std::string bn=parms["BASENAME"]; name=bn+'.'; @@ -36,7 +35,7 @@ MaxEntSimulation::MaxEntSimulation(alps::params &parms) const double alpha_min = parms["ALPHA_MIN"]; //Smallest value of \alpha that is tried const double alpha_max = parms["ALPHA_MAX"]; //Largest value of \alpha that is tried alpha[0] = alpha_max; - for (std::size_t a=1; a::const_iterator max_lprob = std::max_element(lprob.begin(), lprob.end()); //const int max_a = max_lprob-lprob.begin(); int max_a,nothing; double max_lprob; max_lprob=lprob.maxCoeff(&max_a,¬hing); @@ -191,14 +189,14 @@ void MaxEntSimulation::evaluate(){ ar << alps::make_pvp("/alpha/values",alpha); vector_type om(spectra[0].size()); - for (int i=0;i=0.) spec[i] = avspec[i]*omega_coord(i)*M_PI; avspec_anom_str << omega_coord(i) << " " << spec[i]<=0.) spec[i] = spectra[max_a][i]*norm*omega_coord(i)*M_PI; maxspec_anom_str << omega_coord(i) << " " << spec[i] << std::endl; @@ -280,24 +278,24 @@ void MaxEntSimulation::evaluate(){ } if(Kernel_type=="bosonic"){ //for the anomalous function: use A(Omega_)=Im chi(Omega_)/(pi Omega_) (as for anomalous) vector_type spec(avspec.size()); - for (std::size_t i=0; i=0.) avspec_anom_str << omega_coord(i) << " " << spec[i]<=0.) spec[i] = spectra[max_a][i]*norm*omega_coord(i); } if (text_output) { ofstream_ maxspec_anom_str;maxspec_anom_str.open((name+"avspec_bose.dat").c_str()); - for (std::size_t i=0; i G; + testing::internal::CaptureStderr(); for(int i=0;i