MaCh3  2.6.1
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 MACH3_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  bool foundDeltaCP = false;
64  bool foundDelm23 = false;
65 
66  // Loop over the systematics and find the osc_cov systematic and the delta_cp parameter number
67  MACH3LOG_INFO("Looping over systematics to find delta_cp parameter");
68  MACH3LOG_INFO("Number of systematics: {}", systematics.size());
69 
70  for (size_t iCov = 0; iCov < systematics.size(); iCov++){
71  auto* syst = systematics[static_cast<int>(iCov)];
72  for (int i = 0; i < syst->GetNumParams(); i++) {
73  if (syst->GetParName(i) == "delta_cp") {
74  MACH3LOG_INFO("Found delta_cp parameter in systematic {} at index {}", syst->GetName(), i);
75  oscCovVar = static_cast<int>(iCov);
77  foundDeltaCP = true;
78  }
79  if (syst->GetParName(i) == "delm2_23") {
80  MACH3LOG_INFO("Found delm2_23 parameter in systematic {} at index {}", syst->GetName(), i);
82  foundDelm23 = true;
83  }
84  }
85  }
86 
87  // if we didn't find both parameters we need to throw
88  if (!foundDeltaCP) {
89  MACH3LOG_ERROR("Could not find delta_cp parameter in osc_cov systematic");
90  throw MaCh3Exception(__FILE__, __LINE__, "Could not find delta_cp parameter in osc_cov systematic");
91  }
92  if (!foundDelm23) {
93  MACH3LOG_ERROR("Could not find delm2_23 parameter in osc_cov systematic");
94  throw MaCh3Exception(__FILE__, __LINE__, "Could not find delm2_23 parameter in osc_cov systematic");
95  }
96 }
97 
98 void MulticanonicalMCMCHandler::InitializeMulticanonicalHandlerConfig(Manager* fitMan, std::vector<ParameterHandlerBase*>& systematics) {
99  FindOscCovParams(systematics);
100 
101  const auto mcmcConfig = fitMan->raw()["General"]["MCMC"];
102 
103  // Get the multicanonical beta value from the configuration file
104  // This acts as a global bias strength factor
105  multicanonicalBeta = Get<double>(mcmcConfig["Multicanonical"]["Beta"], __FILE__, __LINE__);
106  MACH3LOG_INFO("Setting multicanonical beta to {}", multicanonicalBeta);
107 
109  // delete the umbrella section, thats not ideal
110  multicanonicalSpline = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Spline"]["SplineMode"], false, __FILE__, __LINE__);
111 
112  const std::string biasFunctionName = GetFromManager<std::string>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaBiasFunction"], "", __FILE__, __LINE__);
113  const bool hasBiasFunction = !biasFunctionName.empty();
114 
115  // Spline and umbrella modes are mutually exclusive
116  if (multicanonicalSpline && hasBiasFunction) {
117  MACH3LOG_ERROR("Cannot use multicanonical spline together with umbrella bias function selection.");
118  throw MaCh3Exception(__FILE__, __LINE__, "Cannot use multicanonical spline together with umbrella bias function selection.");
119  }
120 
121  // Umbrella mode requires an explicit bias function selection
122  if (!multicanonicalSpline && !hasBiasFunction) {
123  MACH3LOG_ERROR("Multicanonical umbrella mode requires UmbrellaBiasFunction to be set (gaussian, vonMises, or generalisedGaussian).");
124  throw MaCh3Exception(__FILE__, __LINE__, "Multicanonical umbrella mode requires UmbrellaBiasFunction to be set.");
125  }
126 
127  // Parse and set the umbrella bias function enum
128  if (hasBiasFunction) {
129  umbrellaBiasFunction = ParseBiasFunction(biasFunctionName);
130  umbrellaBiasFunctionName = biasFunctionName;
131  MACH3LOG_INFO("Using umbrella bias function {}", umbrellaBiasFunctionName);
132  }
133 
134  // setup for spline bias mode
135  if (multicanonicalSpline) {
136  auto splineFileName = Get<std::string>(mcmcConfig["Multicanonical"]["Spline"]["SplineFile"], __FILE__, __LINE__);
137  TFile* splineFile = M3::Open(splineFileName.c_str(), "READ",__FILE__, __LINE__);
138 
139  // grab the splines and do a quick check that they are evaluatable
140  TSpline3* dcp_spline_IO_fromfile = static_cast<TSpline3*>(splineFile->Get("dcp_spline_IO"));
141  dcp_spline_IO = static_cast<TSpline3*>(dcp_spline_IO_fromfile->Clone("dcp_spline_IO"));
142  MACH3LOG_INFO("Using multicanonical spline from file {}", splineFileName);
143  dcp_spline_IO->Eval(0.0); // check that the spline is valid
144  MACH3LOG_INFO("Spline evaluated at 0.0 gives value: {}", dcp_spline_IO->Eval(0.0));
145 
146  TSpline3* dcp_spline_NO_fromfile = static_cast<TSpline3*>(splineFile->Get("dcp_spline_NO"));
147  dcp_spline_NO = static_cast<TSpline3*>(dcp_spline_NO_fromfile->Clone("dcp_spline_NO"));
148  MACH3LOG_INFO("Using multicanonical spline from file {}", splineFileName);
149  dcp_spline_NO->Eval(0.0); // check that the spline is valid
150  MACH3LOG_INFO("Spline evaluated at 0.0 gives value {}", dcp_spline_NO->Eval(0.0));
151 
152  splineFile->Close();
153  splineFile = nullptr;
154  } else {
155  // Umbrella mode with explicit bias function selection
156  MACH3LOG_INFO("Using umbrella multicanonical method with bias function {}", umbrellaBiasFunctionName);
157  umbrellaMean = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaMean"], 0, __FILE__, __LINE__);
158  MACH3LOG_INFO("Setting multicanonical mean to {}", umbrellaMean);
159 
160  umbrellaNumber = GetFromManager<int>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaNumber"], 5, __FILE__, __LINE__);
161 
162  // dynamically adjust the width of the gaussians to ensure a certain level
163  // of overlap? be careful, not enough windows here will lead to posterior
164  // (rather than bias) dominated results
165  umbrellaOverlapMode = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["AutoOverlapMode"], false, __FILE__, __LINE__);
166 
167  if (umbrellaOverlapMode) {
168  MACH3LOG_INFO("Setting width based on # of sigma overlapping between umbrellas");
169  umbrellaSigmaOverlap = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["SigmaOverlap"], 3.0, __FILE__, __LINE__);
170  MACH3LOG_INFO("Setting umbrella number to {}", umbrellaNumber);
171  umbrellaWidth = TMath::Pi() / ((umbrellaNumber - 1) * (umbrellaSigmaOverlap));
172  } else {
173  // just grab the width directly from the config
174  umbrellaWidth = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaWidth"], (2 * TMath::Pi()) / umbrellaNumber, __FILE__, __LINE__);
175  MACH3LOG_INFO("Setting width based on value in config {}", umbrellaWidth);
176  }
177 
178  // set individual step scale for dcp, so that the ratio of the step scale to
179  // the multicanonical sigma is stepscale/1sigmaerror = 1/2pi
180  umbrellaAdjustStepScale = GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaAdjustStepScale"], false, __FILE__, __LINE__);
181  umbrellaStepScaleFactor = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaStepScaleFactor"], 1.0, __FILE__, __LINE__);
182 
183  AdjustUmbrellaStepScale(systematics);
184 
185  // Set the flip window flag for the oscillation systematic DO NOT USE THIS
186  // flipWindow =
187  // GetFromManager<bool>(mcmcConfig["Multicanonical"]["Umbrella"]["FlipWindow"],
188  // false, __FILE__, __LINE__); MACH3LOG_INFO("Flip Window: {}", flipWindow);
189  }
190 
191  // initialize von Mises parameters
193  double temp_vonMises_sigma;
194  temp_vonMises_sigma = GetFromManager<double>(mcmcConfig["Multicanonical"]["Umbrella"]["UmbrellaWidth"], umbrellaWidth, __FILE__, __LINE__);
195  vonMises_kappa = 1.0 / (temp_vonMises_sigma * temp_vonMises_sigma);
196  vonMises_I0_kappa = TMath::BesselI0(vonMises_kappa);
197  MACH3LOG_INFO("Using von Mises distribution with kappa = {} and I0(kappa) = {}", vonMises_kappa, vonMises_I0_kappa);
198  }
199 
201  MACH3LOG_INFO("Using generalised Gaussian with mean {} and width {}", umbrellaMean, umbrellaWidth);
202  }
203 }
204 
205 void MulticanonicalMCMCHandler::AdjustUmbrellaStepScale(const std::vector<ParameterHandlerBase*>& systematics) {
207  MACH3LOG_INFO("Adjusting umbrella step scale to keep ratio of step scale to multicanonical sigma constant");
208  MACH3LOG_INFO("Setting umbrella step scale factor to {}", umbrellaStepScaleFactor);
209  double stepScale = (umbrellaWidth * umbrellaStepScaleFactor) / (2.0 * TMath::Pi());
210  MACH3LOG_INFO("Setting individual step scale for multicanonical separate to {}", stepScale);
211  systematics[oscCovVar]->SetIndivStepScale(multicanonicalVar, stepScale);
212  MACH3LOG_INFO("Setting individual step scale for {} systematic to {}", multicanonicalVar, stepScale);
213  } else {
214  MACH3LOG_INFO("Not adjusting umbrella step scale, using value in OscCov config");
215  }
216 }
217 
218 void MulticanonicalMCMCHandler::InitializeMulticanonicalParams(std::vector<ParameterHandlerBase*>& systematics) {
219  // Set starting point of chain to umbrella center for non-spline umbrella modes
220  if (!multicanonicalSpline) {
221  systematics[oscCovVar]->PrintPreFitCurrPropValues();
222  systematics[oscCovVar]->SetParCurrProp(multicanonicalVar, umbrellaMean);
223  MACH3LOG_INFO("Setting starting point of chain to mean value for multicanonical separate: {}", umbrellaMean);
224  // pass the mean to the covarianceOsc object for parameter flipping DO NOT USE: UNTESTED
225  // if (flipWindow) {
226  // auto* oscCov = dynamic_cast<covarianceOsc*>(syst);
227  // if (oscCov) {
228  // oscCov->setFlipWindow(flipWindow);
229  // oscCov->setMulticanonicalSeparateMean(umbrellaMean);
230  // }
231  //}
232  systematics[oscCovVar]->PrintPreFitCurrPropValues();
233  MACH3LOG_INFO("Setting starting point of chain to umbrella center: {}", umbrellaMean);
234  }
235 }
236 
238  // calculate the Log form of the von Mises instead to avoid numerical issues
239  // and return directly
240  double log_vonMises = vonMises_kappa * std::cos(deltacp - umbrellaMean) - std::log(2 * TMath::Pi() * vonMises_I0_kappa);
241  // return the log likelihood, ie the log of the normalised von Mises
242  return -log_vonMises * (multicanonicalBeta);
243 }
244 
245 // this now sorts through the available bias functions in a single function
246 double MulticanonicalMCMCHandler::GetMulticanonicalWeight(double deltacp, double delm23) {
247  if (multicanonicalSpline) {
248  return GetMulticanonicalWeightSpline(deltacp, delm23);
249  }
250 
251  switch (umbrellaBiasFunction) {
253  return GetMulticanonicalWeightGaussian(deltacp);
255  return GetMulticanonicalWeightVonMises(deltacp);
257  return GetMulticanonicalWeightGenGaussian(deltacp);
258  }
259 
260  return GetMulticanonicalWeightGaussian(deltacp);
261 }
262 
263 double MulticanonicalMCMCHandler::GetMulticanonicalWeightSpline(double deltacp, double delm23) {
264  double dcp_spline_val;
265 
266  if (delm23 < 0) {
267  dcp_spline_val = dcp_spline_IO->Eval(deltacp);
268  return -(-std::log(dcp_spline_val) + std::log(dcp_spline_IO->Eval(-TMath::Pi() / 2))) * (multicanonicalBeta); // do I want this offset?? does it matter?
269  } else {
270  dcp_spline_val = dcp_spline_NO->Eval(deltacp);
271  return -(-std::log(dcp_spline_val) + std::log(dcp_spline_NO->Eval(-TMath::Pi() / 2))) * (multicanonicalBeta);
272  }
273  // std::cout << "Evaluating spline at delta_cp = " << deltacp << " gives value
274  // " << dcp_spline_val << "with -log lh of :" << -log(dcp_spline_val) <<
275  // std::endl;
276 }
277 
279  const double inv_sqrt_2pi = 1 / std::sqrt(2 * TMath::Pi());
280  const double neg_half_sigma_sq = -1 / (2 * umbrellaWidth * umbrellaWidth);
281  // return the log likelihood, ie the log of the normalised gaussian
282  return (-std::log(inv_sqrt_2pi * (1 / umbrellaWidth) * std::exp(neg_half_sigma_sq * (deltacp - umbrellaMean) * (deltacp - umbrellaMean)))) * (multicanonicalBeta);
283 }
284 
285 double MulticanonicalMCMCHandler::generalisedGaussian2(double x, double mean, double width) {
286  constexpr int n = 2; // this controls the tightness of the gaussian fixed at 2 for now due to normalisation
287  // 1/4 * Gamma(1/4) = 0.906402477055 (this factor from 2n/Gamma(1/2n) for n=2)
288  const double normFactor = 1 / ((M3::UmbrellaGaussianNormFactor) * 2 * std::sqrt(2) * width); // the normalisation is a little ugly (uses gamma functions), im just going to hardcode them for now
289  double likelihood = normFactor * std::exp(-std::pow((std::pow(x - mean, 2) / (2 * std::pow(width, 2))), n));
290  return likelihood;
291 }
292 
293 double MulticanonicalMCMCHandler::circularDistance(double x, double mean) { return std::atan2(std::sin(x - mean), std::cos(x - mean)); }
294 
296  // implemenetation of the generalised gaussian as a bias function
297  // for now with a fixed n = 2 for simplicity
298  double g0 = generalisedGaussian2(deltacp, umbrellaMean, umbrellaWidth); // these two repeats are required for wrapping the gaussian around -pi and pi
299  double g1 = generalisedGaussian2(deltacp, umbrellaMean - 2 * TMath::Pi(), umbrellaWidth);
300  double g2 = generalisedGaussian2(deltacp, umbrellaMean + 2 * TMath::Pi(), umbrellaWidth);
301 #ifdef MACH3_DEBUG
302  if (debugStream && debugEnabled) (*debugStream) << " g0: " << g0 << " g1: " << g1 << " g2: " << g2 << std::endl;
303 #endif
304  return -std::log(g0 + g1 + g2) * (multicanonicalBeta);
305 }
306 
307 double MulticanonicalMCMCHandler::GetMulticanonicalWeightTripleGaussian(double deltacp) { // pretty much deprecated at this point, just here for testing
308  // precalculated constants
309  constexpr double inv_sqrt_2pi = 0.3989422804014337;
310  double sigma = umbrellaWidth;
311  const double neg_half_sigma_sq = -1 / (2 * sigma * sigma);
312  // three gaussians centered at -pi, 0, pi with sigma pre-defined above
313  double exp1 = std::exp(neg_half_sigma_sq * (deltacp - TMath::Pi()) * (deltacp - TMath::Pi()));
314  double exp2 = std::exp(neg_half_sigma_sq * (deltacp) * (deltacp));
315  double exp3 = std::exp(neg_half_sigma_sq * (deltacp + TMath::Pi()) * (deltacp + TMath::Pi()));
317 
318  // return the log likelihood, ie the log of the normalised sum of the
319  // gaussians
320  return -std::log(inv_sqrt_2pi * (1 / sigma) * (exp1 + exp2 + exp3)) * (multicanonicalBeta);
321 }
#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.
constexpr double UmbrellaGaussianNormFactor
@ kGaussian
Assumes gaussian prior.