Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
MinuitFcnGrad.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * PB, Patrick Bos, Netherlands eScience Center, p.bos@esciencecenter.nl
5 *
6 * Copyright (c) 2021, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13#include "MinuitFcnGrad.h"
14
15#include "RooMinimizer.h"
16#include "RooMsgService.h"
17#include "RooAbsPdf.h"
18
19#include <iomanip> // std::setprecision
20
21namespace RooFit {
22namespace TestStatistics {
23
24/** \class MinuitFcnGrad
25 *
26 * \brief Minuit-RooMinimizer interface which synchronizes parameter data and coordinates evaluation of likelihood
27 * (gradient) values
28 *
29 * This class provides an interface between RooFit and Minuit. It synchronizes parameter values from Minuit, calls
30 * calculator classes to evaluate likelihood and likelihood gradient values and returns them to Minuit. The Wrapper
31 * objects do the actual calculations. These are constructed inside the MinuitFcnGrad constructor using the RooAbsL
32 * likelihood passed in to the constructor, usually directly from RooMinimizer, with which this class is intimately
33 * coupled, being a RooAbsMinimizerFcn implementation. MinuitFcnGrad inherits from ROOT::Math::IMultiGradFunction as
34 * well, which allows it to be used as the FCN and GRAD parameters Minuit expects.
35 *
36 * \note The class is not intended for use by end-users. We recommend to either use RooMinimizer with a RooAbsL derived
37 * likelihood object, or to use a higher level entry point like RooAbsPdf::fitTo() or RooAbsPdf::createNLL().
38 */
39
40/// \param[in] _likelihood L
41/// \param[in] context RooMinimizer that creates and owns this class.
42/// \param[in] parameters The vector of ParameterSettings objects that describe the parameters used in the Minuit
43/// \param[in] likelihoodMode Lmode
44/// \param[in] likelihoodGradientMode Lgrad
45/// \param[in] verbose true for verbose output
46/// Fitter. Note that these must match the set used in the Fitter used by \p context! It can be passed in from
47/// RooMinimizer with fitter()->Config().ParamsSettings().
48MinuitFcnGrad::MinuitFcnGrad(const std::shared_ptr<RooFit::TestStatistics::RooAbsL> &_likelihood, RooMinimizer *context,
49 std::vector<ROOT::Fit::ParameterSettings> &parameters, LikelihoodMode likelihoodMode,
50 LikelihoodGradientMode likelihoodGradientMode)
51 : RooAbsMinimizerFcn(*_likelihood->getParameters(), context), minuit_internal_x_(NDim(), 0),
52 minuit_external_x_(NDim(), 0)
53{
54 synchronizeParameterSettings(parameters, true);
55
56 calculation_is_clean = std::make_shared<WrapperCalculationCleanFlags>();
57
58 likelihood = LikelihoodWrapper::create(likelihoodMode, _likelihood, calculation_is_clean);
59 if (likelihoodMode == LikelihoodMode::multiprocess && likelihoodGradientMode == LikelihoodGradientMode::multiprocess) {
61 } else {
63 }
64 gradient =
65 LikelihoodGradientWrapper::create(likelihoodGradientMode, _likelihood, calculation_is_clean, getNDim(), _context);
66
67 likelihood->synchronizeParameterSettings(parameters);
69 likelihood_in_gradient->synchronizeParameterSettings(parameters);
70 }
71 gradient->synchronizeParameterSettings(this, parameters);
72
73 // Note: can be different than RooGradMinimizerFcn/LikelihoodGradientSerial, where default options are passed
74 // (ROOT::Math::MinimizerOptions::DefaultStrategy() and ROOT::Math::MinimizerOptions::DefaultErrorDef())
75 likelihood->synchronizeWithMinimizer(ROOT::Math::MinimizerOptions());
77 likelihood_in_gradient->synchronizeWithMinimizer(ROOT::Math::MinimizerOptions());
78 }
79 gradient->synchronizeWithMinimizer(ROOT::Math::MinimizerOptions());
80}
81
82double MinuitFcnGrad::DoEval(const double *x) const
83{
84 bool parameters_changed = syncParameterValuesFromMinuitCalls(x, false);
85
86 // Calculate the function for these parameters
87 // RooAbsReal::setHideOffset(false);
88 auto likelihood_here(gradient->isCalculating() ? likelihood_in_gradient : likelihood);
89 likelihood_here->evaluate();
90 double fvalue = likelihood_here->getResult().Sum();
91 calculation_is_clean->likelihood = true;
92 // RooAbsReal::setHideOffset(true);
93
94 if (!parameters_changed) {
95 return fvalue;
96 }
97
98 if (!std::isfinite(fvalue) || RooAbsReal::numEvalErrors() > 0 || fvalue > 1e30) {
99
100 if (cfg().printEvalErrors >= 0) {
101
102 if (cfg().doEEWall) {
103 oocoutW(nullptr, Eval) << "MinuitFcnGrad: Minimized function has error status." << std::endl
104 << "Returning maximum FCN so far (" << _maxFCN
105 << ") to force MIGRAD to back out of this region. Error log follows" << std::endl;
106 } else {
107 oocoutW(nullptr, Eval) << "MinuitFcnGrad: Minimized function has error status but is ignored"
108 << std::endl;
109 }
110
111 bool first(true);
112 ooccoutW(static_cast<RooAbsArg *>(nullptr), Eval) << "Parameter values: ";
113 for (const auto rooAbsArg : *_floatParamList) {
114 auto var = static_cast<const RooRealVar *>(rooAbsArg);
115 if (first) {
116 first = false;
117 } else {
118 ooccoutW(static_cast<RooAbsArg *>(nullptr), Eval) << ", ";
119 }
120 ooccoutW(static_cast<RooAbsArg *>(nullptr), Eval) << var->GetName() << "=" << var->getVal();
121 }
122 ooccoutW(static_cast<RooAbsArg *>(nullptr), Eval) << std::endl;
123
125 ooccoutW(static_cast<RooAbsArg *>(nullptr), Eval) << std::endl;
126 }
127
128 if (cfg().doEEWall) {
129 fvalue = _maxFCN + 1;
130 }
131
133 _numBadNLL++;
134 } else if (fvalue > _maxFCN) {
135 _maxFCN = fvalue;
136 }
137
138 // Optional logging
139 if (cfg().verbose) {
140 std::cout << "\nprevFCN" << (likelihood_here->isOffsetting() ? "-offset" : "") << " = " << std::setprecision(10)
141 << fvalue << std::setprecision(4) << " ";
142 std::cout.flush();
143 }
144
145 finishDoEval();
146 return fvalue;
147}
148
149/// Minuit calls (via FcnAdapters etc) DoEval or Gradient with a set of parameters x.
150/// This function syncs these values to the proper places in RooFit.
151///
152/// The first twist, and reason this function is more complicated than one may imagine, is that Minuit internally uses a
153/// transformed parameter space to account for parameter boundaries. Whether we receive these Minuit "internal"
154/// parameter values or "regular"/untransformed RooFit parameter space values depends on the situation.
155/// - The values that arrive here via DoEval are always "normal" parameter values, since Minuit transforms these
156/// back into regular space before passing to DoEval (see MnUserFcn::operator() which wraps the Fcn(Gradient)Base
157/// in ModularFunctionMinimizer::Minimize and is used for direct function calls from that point on in the minimizer).
158/// These can thus always be safely synced with this function's RooFit parameters using SetPdfParamVal.
159/// - The values that arrive here via Gradient will be in internal coordinates if that is
160/// what this class expects, and indeed this is the case for MinuitFcnGrad's current implementation. This is
161/// communicated to Minuit via MinuitFcnGrad::returnsInMinuit2ParameterSpace. Inside Minuit, that function determines
162/// whether this class's gradient calculator is wrapped inside a AnalyticalGradientCalculator, to which Minuit passes
163/// "external" parameter values, or as an ExternalInternalGradientCalculator, which gets "internal" parameter values.
164/// Long story short: when MinuitFcnGrad::returnsInMinuit2ParameterSpace() returns true, Minuit will pass "internal"
165/// values to Gradient. These cannot be synced with this function's RooFit parameters using
166/// SetPdfParamVal, unless a manual transformation step is performed in advance. However, they do need to be passed
167/// on to the gradient calculator, since indeed we expect values there to be in "internal" space. However, this is
168/// calculator dependent. Note that in the current MinuitFcnGrad implementation we do not actually allow for
169/// calculators in "external" (i.e. regular RooFit parameter space) values, since
170/// MinuitFcnGrad::returnsInMinuit2ParameterSpace is hardcoded to true. This should in a future version be changed so
171/// that the calculator (the wrapper) is queried for this information.
172/// Because some gradient calculators may also use the regular RooFit parameters (e.g. for calculating the likelihood's
173/// value itself), this information is also passed on to the gradient wrapper. Vice versa, when updated "internal"
174/// parameters are passed to Gradient, the likelihood may be affected as well. Even though a
175/// transformation from internal to "external" may be necessary before the values can be used, the likelihood can at
176/// least log that its parameter values are possibly no longer in sync with those of the gradient.
177///
178/// The second twist is that the Minuit external parameters may still be different from the ones used in RooFit. This
179/// happens when Minuit tries out values that lay outside the RooFit parameter's range(s). RooFit's setVal (called
180/// inside SetPdfParamVal) then clips the RooAbsArg's value to one of the range limits, instead of setting it to the
181/// value Minuit intended. When this happens, i.e. syncParameterValuesFromMinuitCalls is called with
182/// minuit_internal = false and the values do not match the previous values stored in minuit_internal_x_ *but* the
183/// values after SetPdfParamVal did not get set to the intended value, the minuit_internal_roofit_x_mismatch_ flag is
184/// set. This information can be used by calculators, if desired, for instance when a calculator does not want to make
185/// use of the range information in the RooAbsArg parameters.
186bool MinuitFcnGrad::syncParameterValuesFromMinuitCalls(const double *x, bool minuit_internal) const
187{
188 bool a_parameter_has_been_updated = false;
189 if (minuit_internal) {
191 throw std::logic_error("Updating Minuit-internal parameters only makes sense for (gradient) calculators that "
192 "are defined in Minuit-internal parameter space.");
193 }
194
195 for (std::size_t ix = 0; ix < NDim(); ++ix) {
196 bool parameter_changed = (x[ix] != minuit_internal_x_[ix]);
197 if (parameter_changed) {
198 minuit_internal_x_[ix] = x[ix];
199 }
200 a_parameter_has_been_updated |= parameter_changed;
201 }
202
203 if (a_parameter_has_been_updated) {
204 calculation_is_clean->set_all(false);
205 likelihood->updateMinuitInternalParameterValues(minuit_internal_x_);
207 likelihood_in_gradient->updateMinuitInternalParameterValues(minuit_internal_x_);
208 }
209 gradient->updateMinuitInternalParameterValues(minuit_internal_x_);
210 }
211 } else {
212 bool a_parameter_is_mismatched = false;
213
214 for (std::size_t ix = 0; ix < NDim(); ++ix) {
215 // Note: the return value of SetPdfParamVal does not always mean that the parameter's value in the RooAbsReal
216 // changed since last time! If the value was out of range bin, setVal was still called, but the value was not
217 // updated.
218 SetPdfParamVal(ix, x[ix]);
219 minuit_external_x_[ix] = x[ix];
220 // The above is why we need minuit_external_x_. The minuit_external_x_ vector can also be passed to
221 // LikelihoodWrappers, if needed, but typically they will make use of the RooFit parameters directly. However,
222 // we log in the flag below whether they are different so that calculators can use this information.
223 bool parameter_changed = (x[ix] != minuit_external_x_[ix]);
224 a_parameter_has_been_updated |= parameter_changed;
225 a_parameter_is_mismatched |= (((RooRealVar *)_floatParamList->at(ix))->getVal() != minuit_external_x_[ix]);
226 }
227
228 minuit_internal_roofit_x_mismatch_ = a_parameter_is_mismatched;
229
230 if (a_parameter_has_been_updated) {
231 calculation_is_clean->set_all(false);
232 likelihood->updateMinuitExternalParameterValues(minuit_external_x_);
234 likelihood_in_gradient->updateMinuitExternalParameterValues(minuit_external_x_);
235 }
236 gradient->updateMinuitExternalParameterValues(minuit_external_x_);
237 }
238 }
239 return a_parameter_has_been_updated;
240}
241
242void MinuitFcnGrad::Gradient(const double *x, double *grad) const
243{
246 gradient->fillGradient(grad);
247 calculating_gradient_ = false;
248}
249
250void MinuitFcnGrad::GradientWithPrevResult(const double *x, double *grad, double *previous_grad, double *previous_g2,
251 double *previous_gstep) const
252{
255 gradient->fillGradientWithPrevResult(grad, previous_grad, previous_g2, previous_gstep);
256 calculating_gradient_ = false;
257}
258
259double MinuitFcnGrad::DoDerivative(const double * /*x*/, unsigned int /*icoord*/) const
260{
261 throw std::runtime_error("MinuitFcnGrad::DoDerivative is not implemented, please use Gradient instead.");
262}
263
264bool MinuitFcnGrad::Synchronize(std::vector<ROOT::Fit::ParameterSettings> &parameters)
265{
266 bool returnee = synchronizeParameterSettings(parameters, _optConst);
267 likelihood->synchronizeParameterSettings(parameters);
269 likelihood_in_gradient->synchronizeParameterSettings(parameters);
270 }
271 gradient->synchronizeParameterSettings(parameters);
272
273 likelihood->synchronizeWithMinimizer(_context->fitter()->Config().MinimizerOptions());
275 likelihood_in_gradient->synchronizeWithMinimizer(_context->fitter()->Config().MinimizerOptions());
276 }
277 gradient->synchronizeWithMinimizer(_context->fitter()->Config().MinimizerOptions());
278 return returnee;
279}
280
281} // namespace TestStatistics
282} // namespace RooFit
#define oocoutW(o, a)
#define ooccoutW(o, a)
ROOT::Math::MinimizerOptions & MinimizerOptions()
access to the minimizer control parameter (non const method)
Definition FitConfig.h:167
const FitConfig & Config() const
access to the fit configuration (const method)
Definition Fitter.h:446
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:74
std::unique_ptr< RooArgList > _floatParamList
RooMinimizer::Config const & cfg() const
void printEvalErrors() const
Print information about why evaluation failed.
bool synchronizeParameterSettings(std::vector< ROOT::Fit::ParameterSettings > &parameters, bool optConst)
Informs Minuit through its parameter_settings vector of RooFit parameter properties.
bool SetPdfParamVal(int index, double value) const
Set value of parameter i.
unsigned int getNDim() const
static Int_t numEvalErrors()
Return the number of logged evaluation errors since the last clearing.
static void printEvalErrors(std::ostream &os=std::cout, Int_t maxPerNode=10000000)
Print all outstanding logged evaluation error on the given ostream.
static void clearEvalErrorLog()
Clear the stack of evaluation error messages.
static std::unique_ptr< LikelihoodGradientWrapper > create(LikelihoodGradientMode likelihoodGradientMode, std::shared_ptr< RooAbsL > likelihood, std::shared_ptr< WrapperCalculationCleanFlags > calculationIsClean, std::size_t nDim, RooMinimizer *minimizer)
Factory method.
static std::unique_ptr< LikelihoodWrapper > create(LikelihoodMode likelihoodMode, std::shared_ptr< RooAbsL > likelihood, std::shared_ptr< WrapperCalculationCleanFlags > calculationIsClean)
Factory method.
MinuitFcnGrad(const std::shared_ptr< RooFit::TestStatistics::RooAbsL > &_likelihood, RooMinimizer *context, std::vector< ROOT::Fit::ParameterSettings > &parameters, LikelihoodMode likelihoodMode, LikelihoodGradientMode likelihoodGradientMode)
bool Synchronize(std::vector< ROOT::Fit::ParameterSettings > &parameter_settings) override
Overridden from RooAbsMinimizerFcn to include gradient strategy synchronization.
std::vector< double > minuit_internal_x_
double DoDerivative(const double *x, unsigned int icoord) const override
This override should not be used in this class, so it throws.
double DoEval(const double *x) const override
IMultiGradFunction override necessary for Minuit.
bool syncParameterValuesFromMinuitCalls(const double *x, bool minuit_internal) const
Minuit calls (via FcnAdapters etc) DoEval or Gradient with a set of parameters x.
std::shared_ptr< WrapperCalculationCleanFlags > calculation_is_clean
bool returnsInMinuit2ParameterSpace() const override
std::shared_ptr< LikelihoodWrapper > likelihood_in_gradient
void GradientWithPrevResult(const double *x, double *grad, double *previous_grad, double *previous_g2, double *previous_gstep) const override
void Gradient(const double *x, double *grad) const override
IMultiGradFunction overrides necessary for Minuit.
std::shared_ptr< LikelihoodWrapper > likelihood
std::vector< double > minuit_external_x_
std::shared_ptr< LikelihoodGradientWrapper > gradient
unsigned int NDim() const override
Part of IMultiGradFunction interface, used widely both in Minuit and in RooFit.
RooMinimizer is a wrapper class around ROOT::Fit:Fitter that provides a seamless interface between th...
ROOT::Fit::Fitter * fitter()
Return underlying ROOT fitter object.
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:40
Double_t x[n]
Definition legend1.C:17
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition Common.h:18
Definition first.py:1