13 #include "TSystemDirectory.h"
17 #pragma GCC diagnostic ignored "-Wfloat-conversion"
18 #pragma GCC diagnostic ignored "-Wold-style-cast"
19 #pragma GCC diagnostic ignored "-Wunused-parameter"
20 #pragma GCC diagnostic ignored "-Wunused-variable"
21 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
22 #pragma GCC diagnostic ignored "-Wconversion"
57 YAML::Node yaml_diag_config = YAML::LoadFile(filename);
58 YAML::Node yaml_config = yaml_diag_config[
"UmbrellaSolver"];
61 if (yaml_config[
"output_file"]) {
62 config.output_file = yaml_config[
"output_file"].as<std::string>();
64 if (yaml_config[
"variable_of_interest"]) {
65 config.variable_of_interest =
66 yaml_config[
"variable_of_interest"].as<std::string>();
68 if (yaml_config[
"max_iterations"]) {
69 config.max_iterations = yaml_config[
"max_iterations"].as<
int>();
71 if (yaml_config[
"tolerance"]) {
72 config.tolerance = yaml_config[
"tolerance"].as<
double>();
74 if (yaml_config[
"print_frequency"]) {
75 config.print_frequency = yaml_config[
"print_frequency"].as<
int>();
77 if (yaml_config[
"dynamic_files"]) {
78 config.dynamic_files = yaml_config[
"dynamic_files"].as<
bool>();
80 config.dynamic_files =
false;
82 if (yaml_config[
"dynamic_pattern"]) {
83 config.dynamic_pattern = yaml_config[
"dynamic_pattern"].as<std::string>();
85 if (yaml_config[
"dynamic_n_windows"]) {
86 config.dynamic_n_windows = yaml_config[
"dynamic_n_windows"].as<
int>();
88 if (yaml_config[
"use_openmp"]) {
89 config.use_openmp = yaml_config[
"use_openmp"].as<
bool>();
94 if (!
config.dynamic_files) {
98 if (yaml_config[
"windows"]) {
99 const YAML::Node &windows = yaml_config[
"windows"];
100 for (
size_t i = 0; i < windows.size(); i++) {
102 window.
name = windows[i][
"name"].as<std::string>();
103 window.
center = windows[i][
"center"].as<
double>();
104 window.
width = windows[i][
"width"].as<
double>();
105 config.windows.push_back(window);
110 if (yaml_config[
"input_files"]) {
111 const YAML::Node &input_files = yaml_config[
"input_files"];
112 for (
size_t i = 0; i < input_files.size() && i <
config.windows.size();
114 config.windows[i].input_file = input_files[i].as<std::string>();
119 for (
int i = 0; i <
config.dynamic_n_windows; i++) {
121 window.
name =
"Window_" + std::to_string(i);
126 config.windows.push_back(window);
130 }
catch (
const YAML::Exception &e) {
131 std::cerr <<
"Error parsing YAML file " << filename <<
": " << e.what()
141 return exp(-0.5 * pow((x - center) / width, 2)) / (width * sqrt(2 * TMath::Pi()));
152 double log_I0 = kappa - 0.5 * log(2 * TMath::Pi() * kappa);
153 return exp(kappa * cos(x - center) - log_I0 - log(2 * TMath::Pi()));
155 I0_kappa = TMath::BesselI0(kappa);
156 return exp(kappa * cos(x - center)) / (2 * TMath::Pi() * I0_kappa);
164 const double normFactor = 1 / ((0.906402477055) * 2 * std::sqrt(2) * width);
166 double likelihood = normFactor * std::exp(-std::pow((std::pow(x - mean, 2) / (2 * std::pow(width, 2))), n));
178 double multicanonicalBeta = 1.0;
179 return (g0 + g1 + g2) * (multicanonicalBeta);
184 double summedWindowsWeighted(
double x,
const std::vector<WindowConfig> &windows,
const std::vector<double> &z_values) {
186 for (
size_t k = 0; k < windows.size(); k++) {
189 window_val =
vonMisesWindow(x, windows[k].center, windows[k].vonMises_kappa);
191 window_val =
gaussianWindow(x, windows[k].center, windows[k].width);
195 std::cout <<
"Unrecognised BiasFunction!!" << std::endl;
197 sum += window_val / z_values[k];
205 std::vector<std::vector<std::vector<double>>>
buildWindowCache(
const std::vector<WindowConfig> &windows,
const std::vector<std::vector<double>> &samples,
bool use_openmp =
true) {
207 int n_windows = windows.size();
208 std::vector<std::vector<std::vector<double>>> cache(n_windows);
210 for (
int i = 0; i < n_windows; i++) {
211 cache[i].resize(n_windows);
212 for (
int j = 0; j < n_windows; j++) {
213 cache[i][j].resize(samples[i].size());
219 #pragma omp parallel for collapse(2) schedule(dynamic)
221 for (
int i = 0; i < n_windows; i++) {
222 for (
int j = 0; j < n_windows; j++) {
223 for (
size_t s = 0; s < samples[i].size(); s++) {
225 cache[i][j][s] =
vonMisesWindow(samples[i][s], windows[j].center, windows[j].vonMises_kappa);
227 cache[i][j][s] =
gaussianWindow(samples[i][s], windows[j].center, windows[j].width);
231 std::cout <<
"Unrecognised function!!!!!" << std::endl;
237 for (
int i = 0; i < n_windows; i++) {
238 for (
int j = 0; j < n_windows; j++) {
239 for (
size_t s = 0; s < samples[i].size(); s++) {
241 cache[i][j][s] =
vonMisesWindow(samples[i][s], windows[j].center, windows[j].vonMises_kappa);
243 cache[i][j][s] =
gaussianWindow(samples[i][s], windows[j].center, windows[j].width);
247 std::cout <<
"Unrecognised function!!!!!" << std::endl;
261 size_t cache_size_bytes = 0;
262 for (
int i = 0; i < n_windows; i++) {
263 for (
int j = 0; j < n_windows; j++) {
264 cache_size_bytes += cache[i][j].size() *
sizeof(double);
267 std::cout <<
"Window cache size: " << cache_size_bytes / (1024.0 * 1024.0) <<
" MB" << std::endl;
269 std::cout <<
"Window cache built successfully." << std::endl;
270 TFile *cache_file =
TFile::Open(
"window_cache_debug_histograms.root",
"RECREATE");
271 for (
int i = 0; i < n_windows; i++) {
272 for (
int j = 0; j < n_windows; j++) {
273 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j);
274 TH1D *window_cache_hist =
new TH1D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415);
275 for (
size_t s = 0; s < cache[i][j].size(); s++) {
276 window_cache_hist->AddBinContent(window_cache_hist->FindBin(samples[i][s]));
278 window_cache_hist->Write();
285 for (
int i = 0; i < n_windows; i++) {
286 for (
int j = 0; j < n_windows; j++) {
287 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j) +
"_cache_2D";
288 TH2D *window_cache_2D_hist =
new TH2D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415, 100, -1000, 10);
289 for (
size_t s = 0; s < cache[i][j].size(); s++) {
291 window_cache_2D_hist->Fill(samples[i][s], log(cache[i][j][s]));
294 window_cache_2D_hist->SetStats(1);
295 window_cache_2D_hist->Write();
306 std::vector<std::vector<double>>
calcFmatrix(std::vector<double> &z_current,
307 const std::vector<WindowConfig> &windows,
308 const std::vector<std::vector<double>> &samples,
309 const std::vector<std::vector<std::vector<double>>> &window_cache,
310 bool use_openmp =
true) {
312 int n_windows = windows.size();
313 std::vector<std::vector<double>> F(n_windows, std::vector<double>(n_windows, 0.0));
315 std::vector<double> z_inv = z_current;
316 for (
size_t i = 0; i < z_current.size(); i++) {
317 if (z_current[i] > 0) {
318 z_inv[i] = 1.0 / z_current[i];
322 std::cerr <<
"Warning: z_current[" << i <<
"] is non-positive (" << z_current[i] <<
"). Setting its inverse to 0 in F matrix calculation." << std::endl;
329 #pragma omp parallel for schedule(dynamic)
331 for (
int i = 0; i < n_windows; i++) {
332 std::vector<double> denominator_cache(samples[i].size(), 0.0);
333 for (
size_t s = 0; s < samples[i].size(); s++) {
334 double denominator = 0.0;
335 for (
int k = 0; k < n_windows; k++) {
336 denominator += window_cache[i][k][s] * z_inv[k];
338 denominator_cache[s] = 1 / denominator;
341 for (
int j = 0; j < n_windows; j++) {
345 for (
size_t s = 0; s < samples[i].size(); s++) {
346 double sample = samples[i][s];
347 double window_j = window_cache[i][j][s];
348 double denominator = denominator_cache[s];
350 if (denominator > 0) {
351 double integrand = (window_j * z_inv[i]) * denominator;
355 printf(
"Denominator is zero for sample %f in window %d, "
362 printf(
"F[%d][%d] sum: %f, count: %d\\n", i, j, sum, count);
366 F[i][j] = sum / count;
371 for (
int i = 0; i < n_windows; i++) {
372 std::vector<double> denominator_cache(samples[i].size(), 0.0);
373 for (
size_t s = 0; s < samples[i].size(); s++) {
374 double denominator = 0.0;
375 for (
int k = 0; k < n_windows; k++) {
376 denominator += window_cache[i][k][s] * z_inv[k];
378 denominator_cache[s] = 1 / denominator;
381 for (
int j = 0; j < n_windows; j++) {
385 for (
size_t s = 0; s < samples[i].size(); s++) {
386 double sample = samples[i][s];
387 double window_j = window_cache[i][j][s];
388 double denominator = denominator_cache[s];
390 if (denominator > 0) {
391 double integrand = (window_j * z_inv[i]) * denominator;
395 printf(
"Denominator is zero for sample %f in window %d, "
402 printf(
"F[%d][%d] sum: %f, count: %d\\n", i, j, sum, count);
406 F[i][j] = sum / count;
416 std::vector<double>
zSolver(
const std::vector<double> &z_current,
417 const std::vector<WindowConfig> &windows,
418 const std::vector<std::vector<double>> &samples,
419 const std::vector<std::vector<std::vector<double>>> &window_cache,
420 bool use_openmp =
true,
bool verbose =
false,
421 int *total_lines =
nullptr) {
423 int n_windows = windows.size();
424 if (verbose && !use_openmp) {
425 std::cout <<
"Using single-threaded computation for F matrix..."
430 std::vector<double> z_working = z_current;
431 std::vector<std::vector<double>> F =
432 calcFmatrix(z_working, windows, samples, window_cache, use_openmp);
452 std::vector<double> z_new(n_windows, 0.0);
453 for (
int i = 0; i < n_windows; i++) {
454 for (
int j = 0; j < n_windows; j++) {
455 z_new[i] += z_current[j] * F[j][i];
474 double z_magnitude = 0.0;
475 for (
int i = 0; i < n_windows; i++) {
476 z_magnitude += z_new[i] * z_new[i];
478 z_magnitude = sqrt(z_magnitude);
479 if (z_magnitude > 0) {
480 for (
int i = 0; i < n_windows; i++) {
481 z_new[i] /= z_magnitude;
491 bool checkConvergence(
const std::vector<double> &z_current,
const std::vector<double> &z_prev,
double tolerance) {
492 double sum_diffs = 0.0;
493 for (
size_t i = 0; i < z_current.size(); i++) {
502 sum_diffs += std::abs(z_current[i] - z_prev[i]);
508 if (sum_diffs / z_current.size() > tolerance) {
return false;}
513 std::vector<double>
getZDiffs(
const std::vector<double> &z_current,
const std::vector<double> &z_prev) {
514 std::vector<double> diffs(z_current.size(), 0.0);
515 for (
size_t i = 0; i < z_current.size(); i++) {
516 diffs[i] = std::abs(z_current[i] - z_prev[i]);
526 static std::deque<std::vector<double>> z_history;
527 static std::vector<double> previous_moving_average;
528 static int stagnant_iterations = 0;
530 constexpr
int moving_average_window = 500;
531 constexpr
int stagnant_required = 500;
532 const double bound = tolerance;
534 if (z_current.empty()) {
539 if (!z_history.empty() && z_history.front().size() != z_current.size()) {
541 previous_moving_average.clear();
542 stagnant_iterations = 0;
545 z_history.push_back(z_current);
546 if ((
int)z_history.size() > moving_average_window) {
547 z_history.pop_front();
551 if ((
int)z_history.size() < moving_average_window) {
555 std::vector<double> moving_average(z_current.size(), 0.0);
556 for (
const auto &z_vec : z_history) {
557 for (
size_t i = 0; i < z_vec.size(); i++) {
558 moving_average[i] += z_vec[i];
561 for (
size_t i = 0; i < moving_average.size(); i++) {
562 moving_average[i] /= moving_average_window;
565 if (previous_moving_average.empty()) {
566 previous_moving_average = moving_average;
570 bool all_within_bound =
true;
571 for (
size_t i = 0; i < moving_average.size(); i++) {
572 if (std::abs(moving_average[i] - previous_moving_average[i]) > bound) {
573 all_within_bound =
false;
578 if (all_within_bound) {
579 stagnant_iterations++;
581 stagnant_iterations = 0;
584 previous_moving_average = moving_average;
586 if (stagnant_iterations == stagnant_required) {
587 std::cout <<
"Convergence appears stalled: moving-average change stayed within " << bound <<
" for " << stagnant_required <<
" iterations." << std::endl;
590 return stagnant_iterations >= stagnant_required;
596 std::cout <<
"=== Umbrella Sampling Z-Factor Solver ===" << std::endl;
599 std::cout <<
"Debugging OpenMP availability..." << std::endl;
602 std::cout <<
"_OPENMP is defined with value: " << _OPENMP << std::endl;
603 std::cout <<
"OpenMP version: " << _OPENMP << std::endl;
604 std::cout <<
"Max threads available: " << omp_get_max_threads() << std::endl;
606 std::cout <<
"_OPENMP is NOT defined - OpenMP not available" << std::endl;
609 std::cout <<
"Loading configuration from: " << config_file << std::endl;
615 std::cerr <<
"Error: No windows defined in configuration and dynamic file loading is disabled." << std::endl;
619 std::cout <<
"Variable of interest: " <<
config.variable_of_interest << std::endl;
620 std::cout <<
"Output file: " <<
config.output_file << std::endl;
624 bool openmp_available =
true;
625 std::cout <<
"OpenMP: AVAILABLE" << std::endl;
627 std::cout <<
"OpenMP: ENABLED (using " << omp_get_max_threads() <<
" threads)" << std::endl;
630 bool openmp_available =
false;
631 std::cout <<
"OpenMP: NOT AVAILABLE" << std::endl;
633 std::cout <<
"OpenMP: NOT AVAILABLE - falling back to single-threaded execution" << std::endl;
634 std::cout <<
"Note: For OpenMP support, try compiling with: g++ -fopenmp ..." << std::endl;
635 std::cout <<
"Or ensure OpenMP library is properly loaded in ROOT" << std::endl;
636 config.use_openmp =
false;
638 std::cout <<
"OpenMP: DISABLED (single-threaded execution)" << std::endl;
643 std::vector<std::vector<double>> samples;
645 if (!
config.dynamic_files) {
646 samples.resize(
config.windows.size());
648 samples.resize(
config.dynamic_n_windows);
650 std::vector<TFile *> input_files;
651 std::vector<TTree *> input_trees;
653 if (!
config.dynamic_files) {
654 std::cout <<
"Using static input files from configuration." << std::endl;
655 for (
size_t i = 0; i <
config.windows.size(); i++) {
656 std::cout <<
"Loading file: " <<
config.windows[i].input_file << std::endl;
659 if (!file || file->IsZombie()) {
660 std::cerr <<
"Error: Cannot open file " <<
config.windows[i].input_file << std::endl;
664 TTree *tree = (TTree *)file->Get(
"posteriors");
666 std::cerr <<
"Error: Cannot find 'posteriors' tree in " <<
config.windows[i].input_file << std::endl;
671 input_files.push_back(file);
672 input_trees.push_back(tree);
675 std::cout <<
"Dynamic file loading enabled. Searching for files in directory: " <<
config.dynamic_pattern << std::endl;
677 TSystemDirectory dir(
"",
config.dynamic_pattern.c_str());
678 TList *files = dir.GetListOfFiles();
683 while ((file = (TSystemFile *)next())) {
684 std::string filename = file->GetName();
685 if (filename.find(
".root") == std::string::npos) {
686 std::cout <<
"Skipping non-root file: " << filename << std::endl;
690 std::string full_path =
config.dynamic_pattern +
"/" + filename;
691 std::cout <<
"Found file: " << full_path << std::endl;
693 TFile *root_file =
TFile::Open(full_path.c_str(),
"READ");
694 if (!root_file || root_file->IsZombie()) {
695 std::cerr <<
"Error: Cannot open file " << full_path << std::endl;
696 throw std::runtime_error(
"Cannot open file: " + full_path);
699 TTree *tree = (TTree *)root_file->Get(
"posteriors");
701 std::cerr <<
"Error: Cannot find 'posteriors' tree in " << full_path << std::endl;
703 throw std::runtime_error(
"Missing 'posteriors' tree in file: " + full_path);
708 input_files.push_back(root_file);
709 input_trees.push_back(tree);
710 std::cout <<
"Loaded tree 'posteriors' from file: " << full_path << std::endl;
713 std::cerr <<
"Error: No files found matching pattern: " <<
config.dynamic_pattern << std::endl;
714 throw std::runtime_error(
"No files found matching pattern: " +
config.dynamic_pattern);
717 if (file_count !=
config.dynamic_n_windows) {
718 std::cerr <<
"Error: Number of files found (" << file_count <<
") does not match expected dynamic_n_windows (" <<
config.dynamic_n_windows <<
")." << std::endl;
719 throw std::runtime_error(
"File count mismatch for dynamic loading.");
723 for (
size_t i = 0; i < input_trees.size(); i++) {
724 TTree *tree = input_trees[i];
725 TFile *file = input_files[i];
727 std::cout <<
"\nProcessing file " << i + 1 <<
"/" << input_trees.size() <<
": " << file->GetName() << std::endl;
729 TMacro *macro = (TMacro *)file->Get(
"MaCh3_Config");
731 std::cout <<
"Found MaCh3_Config macro in file." << std::endl;
734 std::stringstream yaml_text;
735 TList *lines = macro->GetListOfLines();
736 for (
int iline = 0; iline < lines->GetEntries(); ++iline) {
737 TObjString *line = (TObjString *)lines->At(iline);
738 yaml_text << line->GetString().Data() <<
"\n";
742 YAML::Node macro_yaml = YAML::Load(yaml_text.str());
743 YAML::Node umbrellaConfig =
744 macro_yaml[
"General"][
"MCMC"][
"Multicanonical"];
746 config.windows[i].center = umbrellaConfig[
"Umbrella"][
"UmbrellaMean"].as<
double>();
747 std::cout <<
"Window " << i <<
" center updated to " <<
config.windows[i].center << std::endl;
750 std::string biasString = umbrellaConfig[
"Umbrella"][
"UmbrellaBiasFunction"].as<std::string>();
752 if (biasString ==
"gaussian") {
754 std::cout <<
"Window wieghted with gaussian" << std::endl;
755 }
else if (biasString ==
"generalisedGaussian") {
757 std::cout <<
"Window weighted with generalised gaussian" << std::endl;
758 }
else if (biasString ==
"vonMises") {
760 std::cout <<
"Window weighted with vonMises" << std::endl;
762 std::cout <<
"Unrecognised Bias" << std::endl;
764 config.windows[i].umbrellaBiasFunction = biasMode;
768 double vonMises_sigma = umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"].as<
double>();
769 config.windows[i].vonMises_kappa = 1.0 / (vonMises_sigma * vonMises_sigma);
770 config.windows[i].width = vonMises_sigma;
771 std::cout <<
"Window " << i <<
" using von Mises: sigma = " << vonMises_sigma <<
", kappa = " <<
config.windows[i].vonMises_kappa << std::endl;
774 config.windows[i].width = umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"].as<
double>();
775 config.windows[i].vonMises_kappa = -1.0;
776 std::cout <<
"Window " << i <<
" using Gaussian: width = " <<
config.windows[i].width << std::endl;
779 }
catch (
const std::exception &e) {
780 std::cerr <<
"Warning: Could not parse macro as YAML: " << e.what() << std::endl;
786 tree->SetBranchAddress(
config.variable_of_interest.c_str(), &var_value);
787 tree->SetBranchAddress(
"LogL", &logL_value);
789 Long64_t nentries = tree->GetEntries();
790 Long64_t filtered_entries = 0;
791 std::cout <<
"Window " << i <<
": " << nentries <<
" entries" << std::endl;
793 for (Long64_t entry = 0; entry < nentries; entry++) {
794 tree->GetEntry(entry);
800 samples[i].push_back(var_value);
811 for (
size_t i = 0; i <
config.windows.size(); i++) {
812 for (
size_t j = i + 1; j <
config.windows.size(); j++) {
813 if (
config.windows[i].center >
config.windows[j].center) {
815 std::swap(samples[i], samples[j]);
816 std::swap(input_trees[i], input_trees[j]);
817 std::swap(input_files[i], input_files[j]);
823 std::cout <<
"\nFinal window configurations after sorting:" << std::endl;
824 for (
size_t i = 0; i <
config.windows.size(); i++) {
825 std::cout <<
"Window " << i <<
": center = " <<
config.windows[i].center
826 <<
", width = " <<
config.windows[i].width
828 <<
", vonMises_kappa = " <<
config.windows[i].vonMises_kappa
829 <<
", samples = " << samples[i].size() << std::endl;
833 std::vector<double> z_current(
config.windows.size(), 1.0);
834 std::vector<double> z_prev(
config.windows.size(), 1.0);
837 bool hacky_start =
false;
847 0.023968629123202176, 0.024927005713178161, 0.026030888791054529,
848 0.036203405237770721, 0.04004944137212621, 0.055993616350153479,
849 0.079251266929094608, 0.065860139904686643, 0.067944181615205768,
850 0.055237804689517243, 0.054778778031073304, 0.066196102148964917,
851 0.059298667596959342, 0.049864361722134341, 0.048890393249559315,
852 0.05284144211204099, 0.050606183191239794, 0.037329936801427918,
853 0.025159187577401387, 0.01487697249782439, 0.0074395236463036573,
854 0.0040535024095969992, 0.0025347709967512371, 0.0015590961074484638,
855 0.00083136243905723782, 0.00047599269828616062, 0.0004454414833514952,
856 0.00045205403592812914, 0.00062098732584897902, 0.00091722187629238541,
857 0.0012407301891732216, 0.0023494590898020503, 0.0041640866720417773,
858 0.0080599626292932776, 0.011429554834689368, 0.018117848911520615};
861 std::cout <<
"!!!!!!!starting from hacky start vector!!!!!!" << std::endl;
864 std::vector<std::vector<double>> z_evolution;
866 std::cout <<
"\nStarting iterative z-solver..." << std::endl;
869 bool openmp_works =
false;
871 std::cout <<
"Testing OpenMP parallelization..." << std::endl;
873 int max_threads = omp_get_max_threads();
878 std::cout <<
"Max threads reported: " << max_threads << std::endl;
881 int actual_threads = 1;
891 actual_threads = omp_get_num_threads();
895 std::cout <<
"Actual threads in parallel region: " << actual_threads << std::endl;
899 if (actual_threads > 1) {
900 std::cout <<
"OpenMP is working correctly with " << actual_threads <<
" threads" << std::endl;
902 }
else if (max_threads > 1) {
903 std::cout <<
"WARNING: OpenMP pragmas not working in CLING - falling back to single-threaded" << std::endl;
904 openmp_works =
false;
906 openmp_works =
false;
910 std::cout <<
"Precomputing window cache..." << std::endl;
913 std::vector<std::vector<std::vector<double>>> window_cache =
buildWindowCache(
config.windows, samples, openmp_works);
916 bool save_matrix =
true;
918 TFile *F_file =
nullptr;
920 size_t pos =
config.output_file.find(
".root");
921 std::string base_name = (pos != std::string::npos) ?
config.output_file.substr(0, pos) :
config.output_file;
922 F_file =
TFile::Open((base_name +
"_matrix_evolution.root").c_str(),
"RECREATE");
923 if (!F_file || F_file->IsZombie()) {
924 std::cerr <<
"Error: Cannot create file " << base_name +
"_matrix_evolution.root" << std::endl;
928 std::vector<std::vector<double>> initial_F =
calcFmatrix(z_current,
config.windows, samples, window_cache, openmp_works);
929 int n_windows =
config.windows.size();
930 TH2D initial_F_TH2D(
"F_matrix_initial",
"Initial F matrix;Window j;Window i", n_windows, 0, n_windows, n_windows, 0, n_windows);
931 for (
int i = 0; i < n_windows; i++) {
932 for (
int j = 0; j < n_windows; j++) {initial_F_TH2D.SetBinContent(j + 1, i + 1, initial_F[i][j]);
936 std::cout <<
"Saving initial F matrix to file..." << std::endl;
937 initial_F_TH2D.Write();
941 auto start_time = std::chrono::high_resolution_clock::now();
942 auto last_print_time = start_time;
944 bool converged_robustness_check =
false;
948 if (!converged_robustness_check) {
949 std::cout <<
"\nStarting iterative solver with convergence checks..." << std::endl;
953 int total_output_lines = 0;
954 for (
int iteration = 0; iteration <
config.max_iterations; iteration++) {
955 if (iteration %
config.print_frequency == 0 || iteration == 1) {
956 auto current_time = std::chrono::high_resolution_clock::now();
961 std::cout <<
"\033[" << total_output_lines <<
"A";
962 std::cout <<
"\033[J";
966 double avg_relative_change = 0.0;
968 for (
size_t i = 0; i < z_current.size(); i++) {
969 double rel_change = std::abs(z_current[i] - z_prev[i]) / std::max(std::abs(z_current[i]), 1e-10);
970 avg_relative_change += rel_change;
972 avg_relative_change /= z_current.size();
976 std::cout <<
"Iteration " << std::setw(6) << iteration <<
", z values: [";
977 for (
size_t i = 0; i < z_current.size(); i++) {
978 std::cout << std::setw(10) << std::fixed << std::setprecision(5) << z_current[i];
979 if (i < z_current.size() - 1)
983 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_print_time);
984 double avg_time_per_iteration = duration.count() / double(
config.print_frequency);
985 std::cout <<
"] (avg: " << std::setw(6) << std::setprecision(1) << avg_time_per_iteration <<
" ms/iter)" << std::endl;
986 std::cout <<
"Avg relative change: " << std::scientific << std::setprecision(3) << avg_relative_change <<
" (target: " <<
config.tolerance <<
")" << std::endl;
987 total_output_lines = 2;
989 std::cout <<
"]" << std::endl;
990 total_output_lines = 1;
993 last_print_time = current_time;
997 z_current =
zSolver(z_current,
config.windows, samples, window_cache, openmp_works, iteration %
config.print_frequency == 0, &total_output_lines);
998 z_evolution.push_back(z_current);
1004 if (save_matrix && (iteration < 15 || iteration %
config.print_frequency == 0)) {
1005 std::vector<std::vector<double>> F_matrix =
calcFmatrix(z_current,
config.windows, samples, window_cache, openmp_works);
1007 int n_windows =
config.windows.size();
1008 TH2D F_TH2D(Form(
"F_matrix_iter_%02d", iteration),Form(
"F matrix at iteration %02d;Window j;Window i", iteration), n_windows, 0, n_windows, n_windows, 0, n_windows);
1009 for (
int i = 0; i < n_windows; i++) {
1010 for (
int j = 0; j < n_windows; j++) {
1011 F_TH2D.SetBinContent(j + 1, i + 1, F_matrix[i][j]);
1016 TTree *z_tree =
new TTree(Form(
"z_saved_iter_%02d", iteration), Form(
"Z vector at iteration %02d", iteration));
1017 z_tree->Branch(
"z_saved", &z_current);
1020 std::cout <<
"Saving F matrix for iteration " << iteration <<
" to file..." << std::endl;
1032 bool apply_robustness_check =
true;
1033 if (iteration % 100 == 0 &&
1035 if (!converged_robustness_check && apply_robustness_check) {
1036 std::cout <<
"\nConvergence check passed at iteration " << iteration <<
". Starting robustness check with random perturbation..." << std::endl;
1037 converged_robustness_check =
true;
1040 std::vector<double> z_perturbed = z_current;
1041 for (
size_t i = 0; i < z_perturbed.size(); i++) {
1042 double perturbation = (rand() / RAND_MAX - 0.5) * z_perturbed[i];
1043 std::cout <<
"Applying perturbation of " << std::scientific << std::setprecision(6) << perturbation <<
" to z[" << i <<
"] = " << std::scientific << std::setprecision(6) << z_perturbed[i] << std::endl;
1044 z_perturbed[i] += perturbation;
1045 if (z_perturbed[i] < 0)
1046 z_perturbed[i] = abs(z_perturbed[i]);
1048 z_current = z_perturbed;
1049 std::cout <<
"\nApplied random perturbation to z values for robustness "
1050 "check.\n" << std::endl;
1054 std::cout <<
"\nConvergence achieved at iteration " << iteration << std::endl;
1056 std::cout <<
"\nConvergence appears to be stalled at iteration " << iteration << std::endl;
1059 if (iteration ==
config.max_iterations - 1) {
1060 std::cout <<
"Reached maximum iterations without convergence." << std::endl;
1063 auto end_time = std::chrono::high_resolution_clock::now();
1064 auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
1065 double avg_time_total = total_duration.count() / double(iteration + 1);
1069 std::cout <<
"\nTerminating at iteration " << iteration << std::endl;
1070 std::cout <<
"Average time per iteration: " << avg_time_total <<
" ms" << std::endl;
1076 std::cout <<
"\nFinal z values: [";
1077 for (
size_t i = 0; i < z_current.size(); i++) {
1078 std::cout << std::fixed << std::setprecision(5) << z_current[i];
1079 if (i < z_current.size() - 1)
1082 std::cout <<
"]" << std::endl;
1086 if (!output_file || output_file->IsZombie()) {
1087 throw std::runtime_error(
"Cannot create output file: " +
config.output_file);
1092 TTree *combined_tree =
new TTree(
"posteriors",
"Combined Posterior Distributions");
1095 double sin2th_12, sin2th_23, sin2th_13, delm2_12, delm2_23, delta_cp;
1096 double baseline, density, LogL, accProb, stepTime;
1098 std::vector<double> LogL_samples(6);
1099 double LogL_systematic_osc_cov;
1100 double umbrella_weight;
1105 combined_tree->Branch(
"sin2th_12", &sin2th_12,
"sin2th_12/D");
1106 combined_tree->Branch(
"sin2th_23", &sin2th_23,
"sin2th_23/D");
1107 combined_tree->Branch(
"sin2th_13", &sin2th_13,
"sin2th_13/D");
1108 combined_tree->Branch(
"delm2_12", &delm2_12,
"delm2_12/D");
1109 combined_tree->Branch(
"delm2_23", &delm2_23,
"delm2_23/D");
1110 combined_tree->Branch(
"delta_cp", &delta_cp,
"delta_cp/D");
1111 combined_tree->Branch(
"baseline", &baseline,
"baseline/D");
1112 combined_tree->Branch(
"density", &density,
"density/D");
1113 combined_tree->Branch(
"LogL", &LogL,
"LogL/D");
1114 combined_tree->Branch(
"accProb", &accProb,
"accProb/D");
1115 combined_tree->Branch(
"step", &step,
"step/I");
1116 combined_tree->Branch(
"stepTime", &stepTime,
"stepTime/D");
1117 for (
int i = 0; i < 6; i++) {
1118 combined_tree->Branch(Form(
"LogL_sample_%d", i), &LogL_samples[i], Form(
"LogL_sample_%d/D", i));
1120 combined_tree->Branch(
"LogL_systematic_osc_cov", &LogL_systematic_osc_cov,
"LogL_systematic_osc_cov/D");
1121 combined_tree->Branch(
"umbrella_weight", &umbrella_weight,
"umbrella_weight/D");
1122 combined_tree->Branch(
"window_id", &window_id,
"window_id/I");
1125 for (
size_t i = 0; i < input_trees.size(); i++) {
1126 TTree *tree = input_trees[i];
1129 tree->SetBranchAddress(
"sin2th_12", &sin2th_12);
1130 tree->SetBranchAddress(
"sin2th_23", &sin2th_23);
1131 tree->SetBranchAddress(
"sin2th_13", &sin2th_13);
1132 tree->SetBranchAddress(
"delm2_12", &delm2_12);
1133 tree->SetBranchAddress(
"delm2_23", &delm2_23);
1134 tree->SetBranchAddress(
"delta_cp", &delta_cp);
1135 tree->SetBranchAddress(
"baseline", &baseline);
1136 tree->SetBranchAddress(
"density", &density);
1137 tree->SetBranchAddress(
"LogL", &LogL);
1138 tree->SetBranchAddress(
"accProb", &accProb);
1139 tree->SetBranchAddress(
"step", &step);
1140 tree->SetBranchAddress(
"stepTime", &stepTime);
1141 for (
int j = 0; j < 6; j++) {
1142 tree->SetBranchAddress(Form(
"LogL_sample_%d", j), &LogL_samples[j]);
1144 tree->SetBranchAddress(
"LogL_systematic_osc_cov", &LogL_systematic_osc_cov);
1146 Long64_t nentries = tree->GetEntries();
1148 if (z_current[i] == 0) {
1149 std::cout <<
"WARNING: Z value for window " << i <<
" is zero, skipping weighting for this window to avoid division by zero." << std::endl;
1152 for (Long64_t entry = 0; entry < nentries; entry++) {
1153 tree->GetEntry(entry);
1155 if (z_current[i] == 0) {
1156 umbrella_weight = 0.0;
1163 umbrella_weight = denominator;
1165 if (combined_tree->Fill() < 0) {
1166 throw std::runtime_error(
"Failed writing output tree. Check disk quota/space and write permissions for: " +
config.output_file);
1173 TCanvas *c1 =
new TCanvas(
"c1",
"Z Evolution", 800, 600);
1175 std::vector<TGraph*> z_graphs(
config.windows.size());
1176 TLegend *legend =
new TLegend(0.7, 0.7, 0.9, 0.9);
1179 double ymin = std::numeric_limits<double>::max();
1181 for (
size_t i = 0; i <
config.windows.size(); i++) {
1182 std::vector<double> iterations, z_vals;
1183 for (
size_t j = 0; j < z_evolution.size(); j++) {
1184 iterations.push_back(j);
1185 z_vals.push_back(z_evolution[j][i]);
1188 for (
double val : z_vals) {
1195 z_graphs[i] =
new TGraph(iterations.size(), &iterations[0], &z_vals[0]);
1196 z_graphs[i]->SetLineColor(i + 1);
1197 z_graphs[i]->SetLineWidth(2);
1198 z_graphs[i]->SetName(Form(
"z_evolution_window_%lu", i));
1199 z_graphs[i]->SetTitle(
"Evolution of Z Values");
1202 z_graphs[i]->GetXaxis()->SetTitle(
"Iteration");
1203 z_graphs[i]->GetYaxis()->SetTitle(
"Z Value");
1204 z_graphs[i]->Draw(
"AL");
1206 z_graphs[i]->Draw(
"L SAME");
1209 legend->AddEntry(z_graphs[i], Form(
"Window %lu", i),
"l");
1210 z_graphs[i]->Write();
1214 z_graphs[0]->SetMaximum(ymax);
1215 z_graphs[0]->SetMinimum(ymin);
1221 combined_tree->Write();
1224 TH1D *h_delta_cp =
new TH1D(
"h_delta_cp_weighted",
"Weighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1225 TH1D *h_delta_cp_unweighted =
new TH1D(
"h_delta_cp_unweighted",
"Unweighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1227 combined_tree->Draw(
"delta_cp>>h_delta_cp_weighted",
"umbrella_weight",
"goff");
1228 combined_tree->Draw(
"delta_cp>>h_delta_cp_unweighted",
"",
"goff");
1230 h_delta_cp->Write();
1231 h_delta_cp_unweighted->Write();
1234 Long64_t total_entries = combined_tree->GetEntries();
1236 output_file->Close();
1239 for (TFile *file : input_files) {
1243 std::cout <<
"\nUmbrella sampling outputs created (make sure to check for convergence issues)!" << std::endl;
1244 std::cout <<
"Output written to: " <<
config.output_file << std::endl;
1245 std::cout <<
"Combined tree contains " << total_entries <<
" entries with umbrella weights." << std::endl;
1250 std::string config_file =
"umbrella_config.yaml";
1252 config_file = argv[1];
1255 std::cout <<
"Running compiled version with OpenMP support" << std::endl;
1259 }
catch (
const std::exception &e) {
1260 std::cerr <<
"Error: " << e.what() << std::endl;
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
std::vector< double > getZDiffs(const std::vector< double > &z_current, const std::vector< double > &z_prev)
int main(int argc, char *argv[])
double summedWindowsWeighted(double x, const std::vector< WindowConfig > &windows, const std::vector< double > &z_values)
double vonMisesWindow(double x, double center, double kappa)
double generalisedGaussian2(double x, double mean, double width)
std::vector< std::vector< double > > calcFmatrix(std::vector< double > &z_current, const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, const std::vector< std::vector< std::vector< double >>> &window_cache, bool use_openmp=true)
bool checkConvergence(const std::vector< double > &z_current, const std::vector< double > &z_prev, double tolerance)
A few different convergence checks.
UmbrellaConfig parseYAMLConfig(const std::string &filename)
std::vector< double > zSolver(const std::vector< double > &z_current, const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, const std::vector< std::vector< std::vector< double >>> &window_cache, bool use_openmp=true, bool verbose=false, int *total_lines=nullptr)
bool checkConvergenceStalled(const std::vector< double > &z_current, const std::vector< double > &z_prev, double tolerance)
double GetMulticanonicalWeightGenGaussian(double deltacp, double mean, double width)
std::vector< std::vector< std::vector< double > > > buildWindowCache(const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, bool use_openmp=true)
double gaussianWindow(double x, double center, double width)
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ bool debug_mode
void UmbrellaSolver(const std::string &config_file="umbrella_config.yaml")
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.
@ kGaussian
Assumes gaussian prior.
std::string variable_of_interest
std::vector< WindowConfig > windows
std::string dynamic_pattern
M3::BiasFunction umbrellaBiasFunction