MaCh3  2.6.0
Reference Guide
MulticanonicalMCMCHandler.cpp
Go to the documentation of this file.
1 #include <ostream>
2 #include <stdexcept>
6 
10 
11 M3::BiasFunction ParseBiasFunction(const std::string& biasFunctionName) {
12  if (biasFunctionName == "gaussian") {
14  }
15  if (biasFunctionName == "vonMises") {
17  }
18  if (biasFunctionName == "generalisedGaussian") {
20  }
21 
22  throw MaCh3Exception(__FILE__, __LINE__, "Unknown multicanonical bias function: " + biasFunctionName);
23 }
24 
26  // Initialize member variables with defaults
27  oscCovVar = -1;
28  multicanonicalVar = -1;
30  multicanonicalSpline = false;
31  multicanonicalBeta = 1.0;
32  delta_cp_value = 0.0;
33  delm23_value = 0.0;
34  dcp_spline_IO = nullptr;
35  dcp_spline_NO = nullptr;
36  umbrellaMean = 0.0;
37  umbrellaWidth = 1.0;
38  umbrellaNumber = 5;
39  umbrellaOverlapMode = false;
43  flipWindow = false;
44 
45  vonMises_kappa = -1.0;
46  vonMises_I0_kappa = -1.0;
48  umbrellaBiasFunctionName = "gaussian";
49 }
50 
52  // Destructor
53 }
54 
55 #ifdef DEBUG
56 void MulticanonicalMCMCHandler::setDebugStream(std::ostream* os, bool enabled) {
57  debugStream = os;
58  debugEnabled = enabled;
59 }
60 #endif
61 
62 void MulticanonicalMCMCHandler::FindOscCovParams(const std::vector<ParameterHandlerBase*>& systematics) {
63 
64  bool foundDeltaCP = false;
65  bool foundDelm23 = false;
66 
67  // Loop over the systematics and find the osc_cov systematic and the delta_cp parameter number
68  MACH3LOG_INFO("Looping over systematics to find delta_cp parameter");
69  MACH3LOG_INFO("Number of systematics: {}", systematics.size());
70 
71  for (size_t iCov = 0; iCov < systematics.size(); iCov++){
72  auto* syst = systematics[static_cast<int>(iCov)];
73  for (int i = 0; i < syst->GetNumParams(); i++) {
74  if (syst->GetParName(i) == "delta_cp") {
75  MACH3LOG_INFO("Found delta_cp parameter in systematic {} at index {}", syst->GetName(), i);
76  oscCovVar = static_cast<int>(iCov);
78  foundDeltaCP = true;
79  }
80  if (syst->GetParName(i) == "delm2_23") {
81  MACH3LOG_INFO("Found delm2_23 parameter in systematic {} at index {}", syst->GetName(), i);
83  foundDelm23 = true;
84  }
85  }
86  }
87 
88  // if we didn't find both parameters we need to throw
89  if (!foundDeltaCP) {
90  MACH3LOG_ERROR("Could not find delta_cp parameter in osc_cov systematic");
91  throw MaCh3Exception(__FILE__, __LINE__, "Could not find delta_cp parameter in osc_cov systematic");
92  }
93  if (!foundDelm23) {
94  MACH3LOG_ERROR("Could not find delm2_23 parameter in osc_cov systematic");
95  throw MaCh3Exception(__FILE__, __LINE__, "Could not find delm2_23 parameter in osc_cov systematic");
96  }
97 }
98 
99 void MulticanonicalMCMCHandler::InitializeMulticanonicalHandlerConfig(Manager* fitMan, std::vector<ParameterHandlerBase*>& systematics) {
100 
101  FindOscCovParams(systematics);
102 
103  const auto mcmcConfig = fitMan->raw()["General"]["MCMC"];
104 
105  // Get the multicanonical beta value from the configuration file
106  // This acts as a global bias strength factor
107  multicanonicalBeta = mcmcConfig["Multicanonical"]["Beta"].as<double>();
108  MACH3LOG_INFO("Setting multicanonical beta to {}", multicanonicalBeta);
109 
111  // delete the umbrella section, thats not ideal
112  multicanonicalSpline = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Spline"]["SplineMode"], false);
113 
114  const std::string biasFunctionName = GetFromManager<std::string>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaBiasFunction"], "");
115  const bool hasBiasFunction = !biasFunctionName.empty();
116 
117  // Spline and umbrella modes are mutually exclusive
118  if (multicanonicalSpline && hasBiasFunction) {
119  MACH3LOG_ERROR("Cannot use multicanonical spline together with umbrella bias function selection.");
120  throw MaCh3Exception(__FILE__, __LINE__, "Cannot use multicanonical spline together with umbrella bias function selection.");
121  }
122 
123  // Umbrella mode requires an explicit bias function selection
124  if (!multicanonicalSpline && !hasBiasFunction) {
125  MACH3LOG_ERROR("Multicanonical umbrella mode requires UmbrellaBiasFunction to be set (gaussian, vonMises, or generalisedGaussian).");
126  throw MaCh3Exception(__FILE__, __LINE__, "Multicanonical umbrella mode requires UmbrellaBiasFunction to be set.");
127  }
128 
129  // Parse and set the umbrella bias function enum
130  if (hasBiasFunction) {
131  umbrellaBiasFunction = ParseBiasFunction(biasFunctionName);
132  umbrellaBiasFunctionName = biasFunctionName;
133  MACH3LOG_INFO("Using umbrella bias function {}", umbrellaBiasFunctionName);
134  }
135 
136  // setup for spline bias mode
137  if (multicanonicalSpline) {
138 
139  std::string splineFileName = GetFromManager<std::string>(mcmcConfig["Multicanonical"]["Spline"]["SplineFile"], "nofile");
140 
141  TFile* splineFile = M3::Open(splineFileName.c_str(), "READ",__FILE__, __LINE__);
142 
143  // grab the splines and do a quick check that they are evaluatable
144  TSpline3* dcp_spline_IO_fromfile = static_cast<TSpline3*>(splineFile->Get("dcp_spline_IO"));
145  dcp_spline_IO = static_cast<TSpline3*>(dcp_spline_IO_fromfile->Clone("dcp_spline_IO"));
146  MACH3LOG_INFO("Using multicanonical spline from file {}", splineFileName);
147  dcp_spline_IO->Eval(0.0); // check that the spline is valid
148  MACH3LOG_INFO("Spline evaluated at 0.0 gives value: {}", dcp_spline_IO->Eval(0.0));
149 
150  TSpline3* dcp_spline_NO_fromfile = static_cast<TSpline3*>(splineFile->Get("dcp_spline_NO"));
151  dcp_spline_NO = static_cast<TSpline3*>(dcp_spline_NO_fromfile->Clone("dcp_spline_NO"));
152  MACH3LOG_INFO("Using multicanonical spline from file {}", splineFileName);
153  dcp_spline_NO->Eval(0.0); // check that the spline is valid
154  MACH3LOG_INFO("Spline evaluated at 0.0 gives value {}", dcp_spline_NO->Eval(0.0));
155 
156  splineFile->Close();
157  splineFile = nullptr;
158 
159  } else {
160  // Umbrella mode with explicit bias function selection
161  MACH3LOG_INFO("Using umbrella multicanonical method with bias function {}", umbrellaBiasFunctionName);
162  umbrellaMean = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaMean"], 0);
163  MACH3LOG_INFO("Setting multicanonical mean to {}", umbrellaMean);
164 
165  umbrellaNumber = GetFromManager<int>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaNumber"], 5);
166 
167  // dynamically adjust the width of the gaussians to ensure a certain level
168  // of overlap? be careful, not enough windows here will lead to posterior
169  // (rather than bias) dominated results
170  umbrellaOverlapMode = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["AutoOverlapMode"], false);
171 
172  if (umbrellaOverlapMode) {
173  MACH3LOG_INFO("Setting width based on # of sigma overlapping between umbrellas");
174  umbrellaSigmaOverlap = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["SigmaOverlap"], 3.0);
175  MACH3LOG_INFO("Setting umbrella number to {}", umbrellaNumber);
176  umbrellaWidth = TMath::Pi() / ((umbrellaNumber - 1) * (umbrellaSigmaOverlap));
177  } else {
178  // just grab the width directly from the config
179  umbrellaWidth = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaWidth"], (2 * TMath::Pi()) / umbrellaNumber);
180  MACH3LOG_INFO("Setting width based on value in config {}", umbrellaWidth);
181  }
182 
183  // set individual step scale for dcp, so that the ratio of the step scale to
184  // the multicanonical sigma is stepscale/1sigmaerror = 1/2pi
185  umbrellaAdjustStepScale = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaAdjustStepScale"], false);
186  umbrellaStepScaleFactor = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaStepScaleFactor"], 1.0);
187 
188  AdjustUmbrellaStepScale(systematics);
189 
190  // Set the flip window flag for the oscillation systematic DO NOT USE THIS
191  // flipWindow =
192  // GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["FlipWindow"],
193  // false); MACH3LOG_INFO("Flip Window: {}", flipWindow);
194  }
195 
196  // initialize von Mises parameters
198  double temp_vonMises_sigma;
199  temp_vonMises_sigma = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaWidth"], umbrellaWidth);
200  vonMises_kappa = 1.0 / (temp_vonMises_sigma * temp_vonMises_sigma);
201  vonMises_I0_kappa = TMath::BesselI0(vonMises_kappa);
202  MACH3LOG_INFO("Using von Mises distribution with kappa = {} and I0(kappa) = {}", vonMises_kappa, vonMises_I0_kappa);
203  }
204 
206  MACH3LOG_INFO("Using generalised Gaussian with mean {} and width {}", umbrellaMean, umbrellaWidth);
207  }
208 }
209 
210 void MulticanonicalMCMCHandler::AdjustUmbrellaStepScale(const std::vector<ParameterHandlerBase*>& systematics) {
212  MACH3LOG_INFO("Adjusting umbrella step scale to keep ratio of step scale to multicanonical sigma constant");
213  MACH3LOG_INFO("Setting umbrella step scale factor to {}", umbrellaStepScaleFactor);
214  double stepScale = (umbrellaWidth * umbrellaStepScaleFactor) / (2.0 * TMath::Pi());
215  MACH3LOG_INFO("Setting individual step scale for multicanonical separate to {}", stepScale);
216  systematics[oscCovVar]->SetIndivStepScale(multicanonicalVar, stepScale);
217  MACH3LOG_INFO("Setting individual step scale for {} systematic to {}", multicanonicalVar, stepScale);
218  } else {
219  MACH3LOG_INFO("Not adjusting umbrella step scale, using value in OscCov config");
220  }
221 }
222 
223 void MulticanonicalMCMCHandler::InitializeMulticanonicalParams(std::vector<ParameterHandlerBase*>& systematics) {
224  // Set starting point of chain to umbrella center for non-spline umbrella modes
225  if (!multicanonicalSpline) {
226  systematics[oscCovVar]->PrintPreFitCurrPropValues();
227  systematics[oscCovVar]->SetParCurrProp(multicanonicalVar, umbrellaMean);
228  MACH3LOG_INFO("Setting starting point of chain to mean value for multicanonical separate: {}", umbrellaMean);
229  // pass the mean to the covarianceOsc object for parameter flipping DO NOT USE: UNTESTED
230  // if (flipWindow) {
231  // auto* oscCov = dynamic_cast<covarianceOsc*>(syst);
232  // if (oscCov) {
233  // oscCov->setFlipWindow(flipWindow);
234  // oscCov->setMulticanonicalSeparateMean(umbrellaMean);
235  // }
236  //}
237  systematics[oscCovVar]->PrintPreFitCurrPropValues();
238  MACH3LOG_INFO("Setting starting point of chain to umbrella center: {}", umbrellaMean);
239  }
240 }
241 
243  // calculate the Log form of the von Mises instead to avoid numerical issues
244  // and return directly
245  double log_vonMises = vonMises_kappa * std::cos(deltacp - umbrellaMean) - std::log(2 * TMath::Pi() * vonMises_I0_kappa);
246  // return the log likelihood, ie the log of the normalised von Mises
247  return -log_vonMises * (multicanonicalBeta);
248 }
249 
250 // this now sorts through the available bias functions in a single function
251 double MulticanonicalMCMCHandler::GetMulticanonicalWeight(double deltacp, double delm23) {
252  if (multicanonicalSpline) {
253  return GetMulticanonicalWeightSpline(deltacp, delm23);
254  }
255 
256  switch (umbrellaBiasFunction) {
258  return GetMulticanonicalWeightGaussian(deltacp);
260  return GetMulticanonicalWeightVonMises(deltacp);
262  return GetMulticanonicalWeightGenGaussian(deltacp);
263  }
264 
265  return GetMulticanonicalWeightGaussian(deltacp);
266 }
267 
268 double MulticanonicalMCMCHandler::GetMulticanonicalWeightSpline(double deltacp, double delm23) {
269  double dcp_spline_val;
270 
271  if (delm23 < 0) {
272  dcp_spline_val = dcp_spline_IO->Eval(deltacp);
273  return -(-std::log(dcp_spline_val) + std::log(dcp_spline_IO->Eval(-TMath::Pi() / 2))) * (multicanonicalBeta); // do I want this offset?? does it matter?
274  } else {
275  dcp_spline_val = dcp_spline_NO->Eval(deltacp);
276  return -(-std::log(dcp_spline_val) + std::log(dcp_spline_NO->Eval(-TMath::Pi() / 2))) * (multicanonicalBeta);
277  }
278  // std::cout << "Evaluating spline at delta_cp = " << deltacp << " gives value
279  // " << dcp_spline_val << "with -log lh of :" << -log(dcp_spline_val) <<
280  // std::endl;
281 }
282 
284  const double inv_sqrt_2pi = 1 / std::sqrt(2 * TMath::Pi());
285  const double neg_half_sigma_sq = -1 / (2 * umbrellaWidth * umbrellaWidth);
286  // return the log likelihood, ie the log of the normalised gaussian
287  return (-std::log(inv_sqrt_2pi * (1 / umbrellaWidth) * std::exp(neg_half_sigma_sq * (deltacp - umbrellaMean) * (deltacp - umbrellaMean)))) * (multicanonicalBeta);
288 }
289 
290 double MulticanonicalMCMCHandler::generalisedGaussian2(double x, double mean, double width) {
291  constexpr int n = 2; // this controls the tightness of the gaussian fixed at 2 for now due to normalisation
292  // 1/4 * Gamma(1/4) = 0.906402477055 (this factor from 2n/Gamma(1/2n) for n=2)
293  const double normFactor = 1 / ((0.906402477055) * 2 * std::sqrt(2) * width); // the normalisation is a little ugly (uses gamma functions), im just going to hardcode them for now
294  double likelihood = normFactor * std::exp(-std::pow((std::pow(x - mean, 2) / (2 * std::pow(width, 2))), n));
295  return likelihood;
296 }
297 
298 double MulticanonicalMCMCHandler::circularDistance(double x, double mean) { return std::atan2(std::sin(x - mean), std::cos(x - mean)); }
299 
301  // implemenetation of the generalised gaussian as a bias function
302  // for now with a fixed n = 2 for simplicity
303  double g0 = generalisedGaussian2(deltacp, umbrellaMean, umbrellaWidth); // these two repeats are required for wrapping the gaussian around -pi and pi
304  double g1 = generalisedGaussian2(deltacp, umbrellaMean - 2 * TMath::Pi(), umbrellaWidth);
305  double g2 = generalisedGaussian2(deltacp, umbrellaMean + 2 * TMath::Pi(), umbrellaWidth);
306 #ifdef DEBUG
307  if (debugStream && debugEnabled) (*debugStream) << " g0: " << g0 << " g1: " << g1 << " g2: " << g2 << std::endl;
308 #endif
309  return -std::log(g0 + g1 + g2) * (multicanonicalBeta);
310 }
311 
312 double MulticanonicalMCMCHandler::GetMulticanonicalWeightTripleGaussian(double deltacp) { // pretty much deprecated at this point, just here for testing
313  // precalculate constants
314  constexpr double inv_sqrt_2pi = 0.3989422804014337;
315  double sigma = umbrellaWidth;
316  const double neg_half_sigma_sq = -1 / (2 * sigma * sigma);
317  // three gaussians centered at -pi, 0, pi with sigma pre-defined above
318  double exp1 = std::exp(neg_half_sigma_sq * (deltacp - TMath::Pi()) * (deltacp - TMath::Pi()));
319  double exp2 = std::exp(neg_half_sigma_sq * (deltacp) * (deltacp));
320  double exp3 = std::exp(neg_half_sigma_sq * (deltacp + TMath::Pi()) * (deltacp + TMath::Pi()));
322 
323  // return the log likelihood, ie the log of the normalised sum of the
324  // gaussians
325  return -std::log(inv_sqrt_2pi * (1 / sigma) * (exp1 + exp2 + exp3)) * (multicanonicalBeta);
326 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
M3::BiasFunction ParseBiasFunction(const std::string &biasFunctionName)
Custom exception class used throughout MaCh3.
The manager class is responsible for managing configurations and settings.
Definition: Manager.h:16
YAML::Node const & raw() const
Return config.
Definition: Manager.h:47
double vonMises_I0_kappa
Cached I0(kappa) value for the von Mises form.
double umbrellaSigmaOverlap
Requested overlap for evenly spaced umbrellas.
void InitializeMulticanonicalParams(std::vector< ParameterHandlerBase * > &systematics)
Initialise the starting values used by the multicanonical parameter handling.
void InitializeMulticanonicalHandlerConfig(Manager *fitMan, std::vector< ParameterHandlerBase * > &systematics)
Read multicanonical configuration from the yaml via manager and initialise handler state.
double delm23_value
delm2_23 value used during proposal evaluation.
double umbrellaWidth
Umbrella width used for the current configuration.
TSpline3 * dcp_spline_IO
Spline for the IO branch, if spline mode is enabled.
void AdjustUmbrellaStepScale(const std::vector< ParameterHandlerBase * > &systematics)
Adjust the parameter-of-interest's step scale according to the width of the umbrella bias function.
int multicanonicalVar_dm23
Parameter index used for the multicanonical delm2_23 weight.
M3::BiasFunction umbrellaBiasFunction
Selected bias function for multicanonical weights.
double GetMulticanonicalWeightVonMises(double deltacp)
Compute a von Mises multicanonical penalty.
TSpline3 * dcp_spline_NO
Spline for the NO branch, if spline mode is enabled.
bool multicanonicalSpline
Toggle for spline-based multicanonical weights.
bool umbrellaOverlapMode
Toggle for deriving umbrella widths from a desired # of sigma overlaps between umbrellas.
void FindOscCovParams(const std::vector< ParameterHandlerBase * > &systematics)
Locate the systematic object which contains the parameters used by the handler. Stores the parameter ...
double vonMises_kappa
Von Mises kappa parameter. Analogue of sigma for a gaussian.
bool flipWindow
Optional flip-window control.
double delta_cp_value
delta_cp value used during proposal evaluation.
double GetMulticanonicalWeight(double deltacp, double delm23_value)
Compute the multicanonical penalty for the configured bias mode.
std::string umbrellaBiasFunctionName
Configured bias function name for logging.
double GetMulticanonicalWeightSpline(double deltacp, double delm23_value)
Compute the multicanonical penalty using a spline.
double umbrellaMean
Umbrella centre used for the current configuration.
virtual ~MulticanonicalMCMCHandler()
Destructor.
double GetMulticanonicalWeightTripleGaussian(double deltacp)
Compute a triple-Gaussian multicanonical penalty.
double umbrellaStepScaleFactor
Additional scale factor applied when rescaling the step size. This is for fine tuning.
int oscCovVar
Index of the oscillation-covariance systematic in the current fit.
int multicanonicalVar
Parameter index used for the multicanonical delta_cp weight.
int umbrellaNumber
Number of total umbrellas used for the current configuration.
double generalisedGaussian2(double x, double mean, double width)
Wraps the generalised gaussian function for a given x, mean, and width. Required to handle the wrappi...
double GetMulticanonicalWeightGenGaussian(double deltacp)
Compute a generalised-Gaussian multicanonical penalty.
bool umbrellaAdjustStepScale
Toggle for rescaling the step size based on the umbrella width.
double GetMulticanonicalWeightGaussian(double deltacp)
Compute a Gaussian multicanonical penalty.
double multicanonicalBeta
Global scale factor applied to the multicanonical penalty. 1 is full strength, 0 is no penalty.
double circularDistance(double x, double mean)
Compute the circular distance between two angles.
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.