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