1 #include "../interface/SequentialMinimizer.h"
9 #include "RooRealVar.h"
10 #include "RooAbsReal.h"
11 #include "RooLinkedListIter.h"
12 #include <Math/MinimizerOptions.h>
13 #include <Math/Factory.h>
14 #include <boost/foreach.hpp>
15 #include "../interface/ProfilingTools.h"
16 #define foreach BOOST_FOREACH
18 #define DEBUG_ODM_printf if (0) printf
22 #define DEBUG_SM_printf if (fDebug > 1) printf
23 #define DEBUGV_SM_printf if (fDebug > 2) printf
26 const double GOLD_R1 = 0.61803399 ;
27 const double GOLD_R2 = 1-0.61803399 ;
40 bool done = doloop(steps,ytol,xtol);
43 xstep_ = xi_[2] - xi_[0];
50 if (x0 < xi_[0] || x0 > xi_[2]) {
52 DEBUG_ODM_printf(
"ODM: ALERT: variable %s outside bounds x = [%.4f, %.4f, %.4f], x0 = %.4f\n", name_.c_str(), xi_[0], xi_[1], xi_[2], x0);
59 yi_[0] = eval(xi_[0]);
60 yi_[2] = eval(xi_[2]);
61 if (xtol == 0) xtol = (fabs(xi_[1])+XTOL)*XTOL;
63 DEBUG_ODM_printf(
"ODM: start of improve %s x = [%.4f, %.4f, %.4f], y = [%.4f, %.4f, %.4f]\n", name_.c_str(), xi_[0], xi_[1], xi_[2], yi_[0], yi_[1], yi_[2]);
65 if (yi_[1] <= yi_[0] && yi_[1] <= yi_[2]) {
66 if (ytol > 0 && (
std::max(yi_[2],yi_[0]) - yi_[1]) < ytol) {
67 DEBUG_ODM_printf(
"ODM: immediate ytol for %s: ymin %.8f, ymax %.8f, diff %.8f\n", name_.c_str(), yi_[1],
std::max(yi_[2],yi_[0]),
std::max(yi_[2],yi_[0]) - yi_[1]);
68 if (!force || parabolaStep())
return Unchanged;
70 if (xtol > 0 && (xi_[2] - xi_[0]) < xtol) {
71 DEBUG_ODM_printf(
"ODM: immediate xtol for %s: xmin %.8f, xmax %.8f, diff %.8f\n", name_.c_str(), xi_[0], xi_[2], xi_[2] - xi_[0]);
72 if (!force || parabolaStep())
return Unchanged;
79 assert(xi_[0] < xi_[2]);
80 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
82 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
83 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
85 bool done = doloop(steps,ytol,xtol);
89 assert(xi_[0] < xi_[2]);
90 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
92 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
93 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
96 if (ytol > 0 && fabs(yi_[1] - y0) < ytol) {
97 DEBUG_ODM_printf(
"ODM: final ytol for %s: ymin(old) %.8f, ymin(new) %.8f, diff %.8f\n", name_.c_str(), y0, yi_[1], y0 -yi_[1]);
102 if (xtol > 0 && fabs(xi_[1] - x0) < xtol) {
103 x() = (force ? xi_[1] : x0);
106 DEBUG_ODM_printf(
"ODM: doloop for %s is %s\n", name_.c_str(), done ?
"Done" :
"NotDone");
107 DEBUG_ODM_printf(
"ODM: end of improve %s x = [%.4f, %.4f, %.4f], y = [%.4f, %.4f, %.4f]\n", name_.c_str(), xi_[0], xi_[1], xi_[2], yi_[0], yi_[1], yi_[2]);
109 xstep_ = xi_[2] - xi_[0];
110 return done ? Done : NotDone;
115 if (steps <= 0) steps = 100;
117 if (xtol > 0 && (xi_[2] - xi_[0]) < xtol) {
121 if (ytol > 0 && (
std::max(yi_[2],yi_[0]) - yi_[1]) < ytol) {
122 DEBUG_ODM_printf(
"ODM: intermediate ytol for %s: ymin %.8f, ymax %.8f, diff %.8f\n", name_.c_str(), yi_[1],
std::max(yi_[2],yi_[0]),
std::max(yi_[2],yi_[0]) - yi_[1]);
125 DEBUG_ODM_printf(
"ODM: step %d/%d done for %s: ymin %.8f, ymax %.8f, diff %.8f\n",
i+1, steps, name_.c_str(), yi_[1],
std::max(yi_[2],yi_[0]),
std::max(yi_[2],yi_[0]) - yi_[1]);
126 DEBUG_ODM_printf(
"ODM: %s x = [%.4f, %.4f, %.4f], y = [%.4f, %.4f, %.4f]\n", name_.c_str(), xi_[0], xi_[1], xi_[2], yi_[0], yi_[1], yi_[2]);
134 if (std::isfinite(xmax_-xmin_)) {
135 xstep_ =
std::max(xstep_, 0.2*(xmax_-xmin_));
143 double xtol2 = 2*(fabs(xi_[1])+XTOL)*XTOL;
144 if (xstep_ < xtol2) xstep_ = xtol2;
146 yi_[1] = eval(xi_[1]);
147 xi_[0] =
std::max(xmin_, xi_[1]-xstep_);
148 yi_[0] = (xi_[0] == xi_[1] ? yi_[1] : eval(xi_[0]));
149 xi_[2] =
std::min(xmax_, xi_[1]+xstep_);
150 yi_[2] = (xi_[2] == xi_[1] ? yi_[1] : eval(xi_[2]));
151 assert(xi_[0] < xi_[2]);
152 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
156 if (yi_[0] < yi_[1]) {
159 xi_[0] =
std::max(xmin_, xi_[1]-xstep_);
160 yi_[0] = (xi_[0] == xi_[1] ? yi_[1] : eval(xi_[0]));
161 }
else if (yi_[2] < yi_[1]) {
164 xi_[2] =
std::min(xmax_, xi_[1]+xstep_);
165 yi_[2] = (xi_[2] == xi_[1] ? yi_[1] : eval(xi_[2]));
166 }
else if (yi_[2] == yi_[1] && yi_[1] == yi_[0]) {
169 const int nPoints = 20;
170 double xi[nPoints], yi[nPoints];
171 double dx = (xmax_-xmin_)/nPoints,
x = xmin_ - 0.5*dx;
174 for (
int i = 0;
i < nPoints; ++
i,
x += dx) {
175 xi[
i] =
x; yi[
i] = eval(x);
176 if (yi[
i] != yi_[1]) isConst =
false;
177 if (yi[
i] < yi[iFound]) { iFound =
i; }
180 xi_[0] = (iFound == 0 ? xmin_ : xi[iFound-1]);
181 xi_[2] = (iFound > nPoints-1 ? xmax_ : xi[iFound+1]);
182 xi_[1] = iFound; yi_[1] = yi_[iFound];
192 assert(xi_[0] < xi_[2]);
193 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
195 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
196 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
202 assert(xi_[0] < xi_[2]);
203 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
205 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
206 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
207 if (xi_[0] == xi_[1] || xi_[1] == xi_[2]) {
208 int isame = (xi_[0] == xi_[1] ? 0 : 2);
210 assert(yi_[isame] <= yi_[2-isame]);
211 xi_[1] = 0.5*(xi_[0]+xi_[2]);
212 yi_[1] = eval(xi_[1]);
213 if (yi_[1] < yi_[isame]) {
222 int inear = 2, ifar = 0;
223 if (xi_[2]-xi_[1] > xi_[1] - xi_[0]) {
228 double xc = xi_[1]*GOLD_R1 + xi_[ifar]*GOLD_R2;
229 double yc = eval(xc);
234 xi_[1] = xc; yi_[1] = yc;
236 xi_[ifar] = xc; yi_[ifar] = yc;
240 assert(xi_[0] < xi_[2]);
241 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
243 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
244 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
250 if (xi_[0] == xi_[1] || xi_[1] == xi_[2]) {
253 double dx0 = xi_[1] - xi_[0], dx2 = xi_[1] - xi_[2];
254 double dy0 = yi_[1] - yi_[0], dy2 = yi_[1] - yi_[2];
255 double num = dx0*dx0*dy2 - dx2*dx2*dy0;
256 double den = dx0*dy2 - dx2*dy0;
258 double x = xi_[1] - 0.5*num/den;
259 if (xi_[0] < x && x < xi_[2]) {
267 double xc = parabolaFit();
269 double yc = eval(xc);
274 assert(xi_[0] < xi_[2]);
275 assert(xi_[0] <= xi_[1] && xi_[1] <= xi_[2]);
277 assert(xi_[1] == xi_[0] || yi_[1] <= yi_[0]);
278 assert(xi_[1] == xi_[2] || yi_[1] <= yi_[2]);
287 xi_[0] = xmax_ - (xi_[2]-xi_[0]); yi_[0] = eval(xi_[0]);
288 xi_[1] = xmax_; yi_[1] = eval(xmax_);
289 xi_[2] = xmax_; yi_[2] = yi_[1];
290 }
else if (x == xmin_) {
291 xi_[2] = xmin_ + (xi_[2]-xi_[0]); yi_[2] = eval(xi_[0]);
292 xi_[1] = xmin_; yi_[1] = eval(xmin_);
293 xi_[0] = xmin_; yi_[0] = yi_[1];
295 double dx = xi_[2] - xi_[0];
296 xi_[1] =
x; yi_[0] = eval(x);
297 xi_[0] =
std::max(xmin_, x-dx); yi_[0] = eval(xi_[0]);
298 xi_[2] =
std::min(xmax_, x+dx); yi_[2] = eval(xi_[2]);
303 DEBUG_SM_printf(
"SequentialMinimizer::SetFunction: nDim = %u\n", func.NDim());
305 nFree_ = nDim_ = func_->x.size();
308 workers_.resize(nDim_);
315 minValue_ = std::numeric_limits<double>::quiet_NaN();
322 DEBUGV_SM_printf(
"SequentialMinimizer::SetVariable(idx %u, name %s, val %g, step %g)\n", ivar, name.c_str(), val,
step);
323 assert(ivar < nDim_);
324 func_->x[ivar] = val;
325 workers_[ivar].initUnbound(*func_, ivar, step, name);
326 workers_[ivar].state = Cleared;
331 DEBUGV_SM_printf(
"SequentialMinimizer::SetLimitedVariable(idx %u, name %s, var %g, step %g, min %g, max %g)\n", ivar, name.c_str(), val,
step, lower,
upper);
332 assert(ivar < nDim_);
333 func_->x[ivar] = val;
334 workers_[ivar].init(*func_, ivar, lower, upper, step, name);
335 workers_[ivar].state = Cleared;
340 DEBUGV_SM_printf(
"SequentialMinimizer::SetFixedVariable(idx %u, name %s, var %g)\n", ivar, name.c_str(), val);
341 assert(ivar < nDim_);
342 func_->x[ivar] = val;
343 workers_[ivar].initUnbound(*func_, ivar, 1.0, name);
344 workers_[ivar].state = Fixed;
354 for (
unsigned int i = 0;
i < nDim_; ++
i) {
356 if (!w.
isInit() || w.
state ==
Unknown)
throw std::runtime_error(Form(
"SequentialMinimizer::worker[%u/%u] not initialized!\n",
i, nDim_));
357 if (w.
state != Fixed) {
363 return improve(smallsteps);
368 static int nFailWakeUpAttempts =
runtimedef::get(
"SeqMinimizer_nFailWakeUpAttempts");
371 if (state_ == Cleared)
return minimize(smallsteps);
374 double ytol = Tolerance()/
sqrt(workers_.size());
375 int bigsteps = MaxIterations()*20;
378 std::list<Worker*> doneWorkers;
386 for (
int i = 0;
i < bigsteps; ++
i) {
387 DEBUG_SM_printf(
"Start of loop. Strategy %d, State is %s\n",fStrategy,(state_ == Done ?
"DONE" :
"ACTIVE"));
388 State newstate = Done;
389 int oldActiveWorkers = 0, newActiveWorkers = 0;
390 foreach(
Worker &w, workers_) {
392 if (w.
state == Done || w.
state == Fixed)
continue;
393 iret = w.
improve(smallsteps,ytol);
399 doneWorkers.push_front(&w);
407 if (fStrategy >= 2 && newActiveWorkers <= 30) {
408 DEBUG_SM_printf(
"Middle of loop. Strategy %d, active workers %d: firing full minimizer\n", fStrategy, newActiveWorkers);
409 if (doFullMinim()) newstate = Done;
411 if (newstate == Done) {
412 DEBUG_SM_printf(
"Middle of loop. Strategy %d, State is %s, active workers %d --> %d \n",fStrategy,(state_ == Done ?
"DONE" :
"ACTIVE"), oldActiveWorkers, newActiveWorkers);
413 oldActiveWorkers = 0; newActiveWorkers = 0;
414 double y0 = func_->eval();
415 std::list<Worker*>::iterator it = doneWorkers.begin();
418 Worker* firstWorker = *it;
419 it = doneWorkers.erase(it);
421 while( it != doneWorkers.end()) {
423 if (nFailWakeUpAttempts && w.
nUnaffected >= nFailWakeUpAttempts) { ++it;
continue; }
435 it = doneWorkers.erase(it);
437 if (fStrategy == 0)
break;
440 if (newstate == Active) {
441 firstWorker->
state = Active;
444 double y1 = func_->eval();
447 DEBUG_SM_printf(
"End of loop. Strategy %d, State is %s, active workers %d --> %d \n",fStrategy,(state_ == Done ?
"DONE" :
"ACTIVE"), oldActiveWorkers, newActiveWorkers);
448 if (state_ == Done && newstate == Done) {
450 minValue_ = func_->eval();
455 if (func_->nCalls > MaxFunctionCalls())
break;
459 minValue_ = func_->eval();
467 f_(f), nDim_(nDim), idx_(idx), x_(xi) {}
469 virtual unsigned int NDim()
const {
return nDim_; }
471 virtual double DoEval(
const double *
x)
const {
472 for (
int i = 0;
i < nDim_; ++
i) x_[idx_[
i]] = x[
i];
475 const ROOT::Math::IMultiGenFunction *
f_;
484 if (fullMinimizer_.get() == 0) {
485 fullMinimizer_.reset(ROOT::Math::Factory::CreateMinimizer(
"Minuit2",
""));
486 fullMinimizer_->SetTolerance(Tolerance());
487 fullMinimizer_->SetStrategy(Strategy()-2);
489 subspaceIndices_.clear();
490 for (
int i = 0,
n = workers_.size();
i <
n; ++
i) {
491 if (workers_[
i].
state == Active) subspaceIndices_.push_back(
i);
493 fullMinimizer_->Clear();
495 fullMinimizer_->SetFunction(subfunc);
496 for (
int i = 0, n = subspaceIndices_.size();
i <
n; ++
i) {
497 int j = subspaceIndices_[
i];
499 fullMinimizer_->SetLimitedVariable(
i, w.
name(), func_->x[
j], w.
step(), w.
min(), w.
max());
501 bool ok = fullMinimizer_->Minimize();
503 const double *ximin = fullMinimizer_->X();
505 for (
int i = 0, n = subspaceIndices_.size();
i <
n; ++
i) {
506 func_->x[subspaceIndices_[
i]] = ximin[
i];
509 for (
int i = 0, n = subspaceIndices_.size();
i <
n; ++
i) {
510 int j = subspaceIndices_[
i];
520 #include <TPluginManager.h>
522 static int load_seqmin() {
523 gPluginMgr->AddHandler(
"ROOT::Math::Minimizer",
"SeqMinimizer",
"cmsmath::SequentialMinimizer",
"HiggsAnalysisCombinedLimit",
"SequentialMinimizer(const char *)");
526 static int loaded_seqmin = load_seqmin();
bool minimize(int steps=1, double ytol=0, double xtol=0)
virtual bool SetLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double lower, double upper)
set upper/lower limited variable (override if minimizer supports them )
void seek()
search for a triplet of points bracketing the maximum. return false in case of errors ...
bool minimize(int smallsteps=5)
int get(const char *name)
virtual double DoEval(const double *x) const
virtual bool Minimize()
method to perform the minimization
virtual void SetFunction(const ROOT::Math::IMultiGenFunction &func)
set the function to minimize
void init(const MinimizerContext &ctx, unsigned int idx, double xmin, double xmax, double xstep, const std::string &name)
virtual bool SetVariable(unsigned int ivar, const std::string &name, double val, double step)
set free variable
void initDefault(const MinimizerContext &ctx, unsigned int idx)
const T & max(const T &a, const T &b)
ImproveRet improve(int steps=1, double ytol=0, double xtol=0, bool force=true)
virtual bool SetFixedVariable(unsigned int ivar, const std::string &name, double val)
set fixed variable (override if minimizer supports them )
bool improve(int smallsteps=5)
virtual void Clear()
reset for consecutive minimizations - implement if needed
const ROOT::Math::IMultiGenFunction * f_
const char * cname() const
virtual unsigned int NDim() const
SubspaceMultiGenFunction(const ROOT::Math::IMultiGenFunction *f, int nDim, const int *idx, double *xi)
bool doloop(int steps, double ytol, double xtol)
bool parabolaStep()
do the parabola fit
Basic struct to call a function.
void goldenBisection()
do the golden bisection
const std::string & name() const
double parabolaFit()
do the parabola fit
virtual IBaseFunctionMultiDim * Clone() const