Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooPolyFunc.cxx
Go to the documentation of this file.
1/*****************************************************************************
2 * Project: RooFit *
3 * Package: RooFitModels *
4 * @(#)root/roofit:$Id$
5 * Authors: *
6 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
7 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
8 * *
9 * Copyright (c) 2021: *
10 * CERN, Switzerland *
11 * *
12 * Redistribution and use in source and binary forms, *
13 * with or without modification, are permitted according to the terms *
14 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
15 *****************************************************************************/
16
17/** \class RooPolyFunc
18 \ingroup Roofit
19
20RooPolyFunc implements a polynomial function in multi-variables.
21The polynomial coefficients are implemented as doubles and are not
22part of the RooFit computation graph.
23**/
24
25#include "RooPolyFunc.h"
26
27#include "RooAbsReal.h"
28#include "RooArgList.h"
29#include "RooArgSet.h"
30#include "RooDerivative.h"
31#include "RooMsgService.h"
32#include "RooRealVar.h"
33
34#include <utility>
35
36using namespace std;
37using namespace RooFit;
38
40
41////////////////////////////////////////////////////////////////////////////////
42/// coverity[UNINIT_CTOR]
43
44void RooPolyFunc::addTerm(double coefficient)
45{
46 int n_terms = _terms.size();
47 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
48 std::string term_name = Form("%s_t%d", GetName(), n_terms);
49 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
50 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
51 RooArgList exponents{};
52
53 for (const auto &var : _vars) {
54 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), 0);
55 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), 0);
56 exponents.addOwned(std::move(exponent));
57 }
58
59 termList->addOwned(std::move(exponents));
60 termList->addOwned(std::move(coeff));
61 _terms.push_back(std::move(termList));
62}
63
64void RooPolyFunc::addTerm(double coefficient, const RooAbsReal &var1, int exp1)
65{
66 int n_terms = _terms.size();
67 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
68 std::string term_name = Form("%s_t%d", GetName(), n_terms);
69 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
70 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
71 RooArgList exponents{};
72
73 // linear iterate over all the variables, create var1^exp1 ..vark^0
74 for (const auto &var : _vars) {
75 int exp = 0;
76 if (strcmp(var1.GetName(), var->GetName()) == 0)
77 exp += exp1;
78 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), exp);
79 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), exp);
80 exponents.addOwned(std::move(exponent));
81 }
82
83 termList->addOwned(std::move(exponents));
84 termList->addOwned(std::move(coeff));
85 _terms.push_back(std::move(termList));
86}
87
88void RooPolyFunc::addTerm(double coefficient, const RooAbsReal &var1, int exp1, const RooAbsReal &var2, int exp2)
89{
90
91 int n_terms = _terms.size();
92 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
93 std::string term_name = Form("%s_t%d", GetName(), n_terms);
94 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
95 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
96 RooArgList exponents{};
97
98 for (const auto &var : _vars) {
99 int exp = 0;
100 if (strcmp(var1.GetName(), var->GetName()) == 0)
101 exp += exp1;
102 if (strcmp(var2.GetName(), var->GetName()) == 0)
103 exp += exp2;
104 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), exp);
105 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), exp);
106 exponents.addOwned(std::move(exponent));
107 }
108 termList->addOwned(std::move(exponents));
109 termList->addOwned(std::move(coeff));
110 _terms.push_back(std::move(termList));
111}
112
113void RooPolyFunc::addTerm(double coefficient, const RooAbsCollection &exponents)
114{
115 if (exponents.size() != _vars.size()) {
116 coutE(InputArguments) << "RooPolyFunc::addTerm(" << GetName() << ") WARNING: number of exponents ("
117 << exponents.size() << ") provided do not match the number of variables (" << _vars.size()
118 << ")" << endl;
119 }
120 int n_terms = _terms.size();
121 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
122 std::string term_name = Form("%s_t%d", GetName(), n_terms);
123 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
124 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
125 termList->addOwned(exponents);
126 termList->addOwned(std::move(coeff));
127 _terms.push_back(std::move(termList));
128}
129
130////////////////////////////////////////////////////////////////////////////////
131/// Default constructor
132
134
135////////////////////////////////////////////////////////////////////////////////
136/// Parameterised constructor
137
138RooPolyFunc::RooPolyFunc(const char *name, const char *title, const RooAbsCollection &vars)
139 : RooAbsReal(name, title), _vars("x", "list of dependent variables", this)
140{
141 for (const auto &var : vars) {
142 if (!dynamic_cast<RooAbsReal *>(var)) {
143 std::stringstream ss;
144 ss << "RooPolyFunc::ctor(" << GetName() << ") ERROR: coefficient " << var->GetName()
145 << " is not of type RooAbsReal";
146 const std::string errorMsg = ss.str();
147 coutE(InputArguments) << errorMsg << std::endl;
148 throw std::runtime_error(errorMsg);
149 }
150 _vars.add(*var);
151 }
152}
153
154////////////////////////////////////////////////////////////////////////////////
155/// Copy constructor
156
157RooPolyFunc::RooPolyFunc(const RooPolyFunc &other, const char *name)
158 : RooAbsReal(other, name), _vars("vars", this, other._vars)
159{
160 for (auto const &term : other._terms) {
161 _terms.emplace_back(std::make_unique<RooListProxy>(term->GetName(), this, *term));
162 }
163}
164
165////////////////////////////////////////////////////////////////////////////////
166/// Return to RooPolyFunc as a string
167
168std::string RooPolyFunc::asString() const
169{
170 std::stringstream ss;
171 bool first = true;
172 for (const auto &term : _terms) {
173 size_t n_vars = term->size() - 1;
174 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
175 if (coef->getVal() > 0 && !first)
176 ss << "+";
177 ss << coef->getVal();
178 first = true;
179 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
180 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
181 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
182 if (exp->getVal() == 0)
183 continue;
184 if (first)
185 ss << " * (";
186 else
187 ss << "*";
188 ss << "pow(" << var->GetName() << "," << exp->getVal() << ")";
189 first = false;
190 }
191 if (!first)
192 ss << ")";
193 }
194 return ss.str();
195}
196
197////////////////////////////////////////////////////////////////////////////////
198/// Evaluate value of Polynomial.
200{
201 // Calculate and return value of polynomial
202 double poly_sum(0.0);
203 for (const auto &term : _terms) {
204 double poly_term(1.0);
205 size_t n_vars = term->size() - 1;
206 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
207 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
208 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
209 poly_term *= pow(var->getVal(), exp->getVal());
210 }
211 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
212 poly_sum += coef->getVal() * poly_term;
213 }
214 return poly_sum;
215}
216
217void setCoordinates(const RooAbsCollection &observables, std::vector<double> const &observableValues)
218// set all observables to expansion co-ordinate
219{
220 std::size_t iObs = 0;
221 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
222 var->setVal(observableValues[iObs++]);
223 }
224}
225
226void fixObservables(const RooAbsCollection &observables)
227{
228 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
229 var->setConstant(true);
230 }
231}
232
233//////////////////////////////////////////////////////////////////////////////
234/// Taylor expanding given function in terms of observables around
235/// observableValues. Supports expansions upto order 2.
236/// \param[in] func Function of variables that is taylor expanded.
237/// \param[in] observables Set of variables to perform the expansion.
238/// It's type is RooArgList to ensure that it is always ordered the
239/// same as the observableValues vector. However, duplicate
240/// observables are still not allowed.
241/// \param[in] order Order of the expansion (0,1,2 supported).
242/// \param[in] observableValues Coordinates around which expansion is
243/// performed. If empty, the nominal observable values are taken, if
244/// the size matches the size of the observables RooArgSet, the
245/// values are mapped to the observables in matching order. If it
246/// contains only one element, the same single value is used for all
247/// observables.
248/// \param[in] eps1 Precision for first derivative and second derivative.
249/// \param[in] eps2 Precision for second partial derivative of cross-derivative.
250std::unique_ptr<RooPolyFunc>
251RooPolyFunc::taylorExpand(const char *name, const char *title, RooAbsReal &func, const RooArgList &observables,
252 int order, std::vector<double> const &observableValues, double eps1, double eps2)
253{
254 // Create the taylor expansion polynomial
255 auto taylorPoly = std::make_unique<RooPolyFunc>(name, title, observables);
256
257 // Verify that there are no duplicate observables
258 {
259 RooArgSet obsSet;
260 for (RooAbsArg *obs : observables) {
261 obsSet.add(*obs, /*silent*/ true); // we can be silent now, the error will come later
262 }
263 if (obsSet.size() != observables.size()) {
264 std::stringstream errorMsgStream;
265 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: duplicate input observables!";
266 const auto errorMsg = errorMsgStream.str();
267 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
268 throw std::invalid_argument(errorMsg);
269 }
270 }
271
272 // Figure out the observable values around which to exapnd
273 std::vector<double> obsValues;
274 if (observableValues.empty()) {
275 obsValues.reserve(observables.size());
276 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
277 obsValues.push_back(var->getVal());
278 }
279 } else if (observableValues.size() == 1) {
280 obsValues.resize(observables.size());
281 std::fill(obsValues.begin(), obsValues.end(), observableValues[0]);
282 } else if (observableValues.size() == observables.size()) {
283 obsValues = observableValues;
284 } else {
285 std::stringstream errorMsgStream;
286 errorMsgStream << "RooPolyFunc::taylorExpand(" << name
287 << ") ERROR: observableValues must be empty, contain one element, or match observables.size()!";
288 const auto errorMsg = errorMsgStream.str();
289 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
290 throw std::invalid_argument(errorMsg);
291 }
292
293 // taylor expansion can be performed only for order 0, 1, 2 currently
294 if (order >= 3 || order <= 0) {
295 std::stringstream errorMsgStream;
296 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: order must be 0, 1, or 2";
297 const auto errorMsg = errorMsgStream.str();
298 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
299 throw std::invalid_argument(errorMsg);
300 }
301
302 setCoordinates(observables, obsValues);
303
304 // estimate taylor expansion polynomial for different orders
305 // f(x) = f(x=x0)
306 // + \sum_i + \frac{df}{dx_i}|_{x_i=x_i^0}(x - x_i^0)
307 // + \sum_{i,j} 0.5 * \frac{d^f}{dx_i x_j}
308 // ( x_i - x_i^0 )( x_j - x_j^0 )
309 // note in the polynomial the () brackets are expanded out
310 for (int i_order = 0; i_order <= order; ++i_order) {
311 switch (i_order) {
312 case 0: {
313 taylorPoly->addTerm(func.getVal());
314 break;
315 }
316 case 1: {
317 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
318 double var1_val = var->getVal();
319 auto deriv = func.derivative(*var, 1, eps2);
320 double deriv_val = deriv->getVal();
321 setCoordinates(observables, obsValues);
322 taylorPoly->addTerm(deriv_val, *var, 1);
323 if (var1_val != 0.0) {
324 taylorPoly->addTerm(deriv_val * var1_val * -1.0);
325 }
326 }
327 break;
328 }
329 case 2: {
330 for (auto *var1 : static_range_cast<RooRealVar *>(observables)) {
331 double var1_val = var1->getVal();
332 auto deriv1 = func.derivative(*var1, 1, eps1);
333 for (auto *var2 : static_range_cast<RooRealVar *>(observables)) {
334 double var2_val = var2->getVal();
335 double deriv_val = 0.0;
336 if (strcmp(var1->GetName(), var2->GetName()) == 0) {
337 auto deriv2 = func.derivative(*var2, 2, eps2);
338 deriv_val = 0.5 * deriv2->getVal();
339 setCoordinates(observables, obsValues);
340 } else {
341 auto deriv2 = deriv1->derivative(*var2, 1, eps2);
342 deriv_val = 0.5 * deriv2->getVal();
343 setCoordinates(observables, obsValues);
344 }
345 taylorPoly->addTerm(deriv_val, *var1, 1, *var2, 1);
346 if (var1_val != 0.0 || var2_val != 0.0) {
347 taylorPoly->addTerm(deriv_val * var1_val * var2_val);
348 taylorPoly->addTerm(deriv_val * var2_val * -1.0, *var1, 1);
349 taylorPoly->addTerm(deriv_val * var1_val * -1.0, *var2, 1);
350 }
351 }
352 }
353 break;
354 }
355 }
356 }
357 return taylorPoly;
358}
#define oocoutE(o, a)
#define coutE(a)
void fixObservables(const RooAbsCollection &observables)
void setCoordinates(const RooAbsCollection &observables, std::vector< double > const &observableValues)
#define ClassImp(name)
Definition Rtypes.h:377
char name[80]
Definition TGX11.cxx:110
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2467
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:74
RooAbsCollection is an abstract container object that can hold multiple RooAbsArg objects.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:62
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:91
RooDerivative * derivative(RooRealVar &obs, Int_t order=1, double eps=0.001)
Return function representing first, second or third order derivative of this function.
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
bool add(const RooAbsArg &var, bool valueServer, bool shapeServer, bool silent)
Overloaded RooCollection_t::add() method insert object into set and registers object as server to own...
RooPolyFunc implements a polynomial function in multi-variables.
Definition RooPolyFunc.h:28
RooListProxy _vars
Definition RooPolyFunc.h:62
double evaluate() const override
Evaluation.
RooPolyFunc()
Default constructor.
void addTerm(double coefficient)
coverity[UNINIT_CTOR]
static std::unique_ptr< RooPolyFunc > taylorExpand(const char *name, const char *title, RooAbsReal &func, const RooArgList &observables, int order=1, std::vector< double > const &observableValues={}, double eps1=1e-6, double eps2=1e-3)
Taylor expanding given function in terms of observables around observableValues.
std::string asString() const
Return to RooPolyFunc as a string.
std::vector< std::unique_ptr< RooListProxy > > _terms
Definition RooPolyFunc.h:63
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:40
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition Common.h:18
@ InputArguments
Definition first.py:1