Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 std::endl;
37using namespace RooFit;
38
39
40////////////////////////////////////////////////////////////////////////////////
41/// coverity[UNINIT_CTOR]
42
43void RooPolyFunc::addTerm(double coefficient)
44{
45 int n_terms = _terms.size();
46 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
47 std::string term_name = Form("%s_t%d", GetName(), n_terms);
48 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
49 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
51
52 for (const auto &var : _vars) {
53 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), 0);
54 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), 0);
55 exponents.addOwned(std::move(exponent));
56 }
57
58 termList->addOwned(std::move(exponents));
59 termList->addOwned(std::move(coeff));
60 _terms.push_back(std::move(termList));
61}
62
63void RooPolyFunc::addTerm(double coefficient, const RooAbsReal &var1, int exp1)
64{
65 int n_terms = _terms.size();
66 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
67 std::string term_name = Form("%s_t%d", GetName(), n_terms);
68 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
69 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
71
72 // linear iterate over all the variables, create var1^exp1 ..vark^0
73 for (const auto &var : _vars) {
74 int exp = 0;
75 if (strcmp(var1.GetName(), var->GetName()) == 0)
76 exp += exp1;
77 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), exp);
78 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), exp);
79 exponents.addOwned(std::move(exponent));
80 }
81
82 termList->addOwned(std::move(exponents));
83 termList->addOwned(std::move(coeff));
84 _terms.push_back(std::move(termList));
85}
86
87void RooPolyFunc::addTerm(double coefficient, const RooAbsReal &var1, int exp1, const RooAbsReal &var2, int exp2)
88{
89
90 int n_terms = _terms.size();
91 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
92 std::string term_name = Form("%s_t%d", GetName(), n_terms);
93 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
94 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
96
97 for (const auto &var : _vars) {
98 int exp = 0;
99 if (strcmp(var1.GetName(), var->GetName()) == 0)
100 exp += exp1;
101 if (strcmp(var2.GetName(), var->GetName()) == 0)
102 exp += exp2;
103 std::string exponent_name = Form("%s_%s^%d", GetName(), var->GetName(), exp);
104 auto exponent = std::make_unique<RooRealVar>(exponent_name.c_str(), exponent_name.c_str(), exp);
105 exponents.addOwned(std::move(exponent));
106 }
107 termList->addOwned(std::move(exponents));
108 termList->addOwned(std::move(coeff));
109 _terms.push_back(std::move(termList));
110}
111
112void RooPolyFunc::addTerm(double coefficient, const RooAbsCollection &exponents)
113{
114 if (exponents.size() != _vars.size()) {
115 coutE(InputArguments) << "RooPolyFunc::addTerm(" << GetName() << ") WARNING: number of exponents ("
116 << exponents.size() << ") provided do not match the number of variables (" << _vars.size()
117 << ")" << std::endl;
118 }
119 int n_terms = _terms.size();
120 std::string coeff_name = Form("%s_c%d", GetName(), n_terms);
121 std::string term_name = Form("%s_t%d", GetName(), n_terms);
122 auto termList = std::make_unique<RooListProxy>(term_name.c_str(), term_name.c_str(), this);
123 auto coeff = std::make_unique<RooRealVar>(coeff_name.c_str(), coeff_name.c_str(), coefficient);
124 termList->addOwned(exponents);
125 termList->addOwned(std::move(coeff));
126 _terms.push_back(std::move(termList));
127}
128
129////////////////////////////////////////////////////////////////////////////////
130/// Default constructor
131
133
134////////////////////////////////////////////////////////////////////////////////
135/// Parameterised constructor
136
137RooPolyFunc::RooPolyFunc(const char *name, const char *title, const RooAbsCollection &vars)
138 : RooAbsReal(name, title), _vars("x", "list of dependent variables", this)
139{
141}
142
143////////////////////////////////////////////////////////////////////////////////
144/// Copy constructor
145
147 : RooAbsReal(other, name), _vars("vars", this, other._vars)
148{
149 for (auto const &term : other._terms) {
150 _terms.emplace_back(std::make_unique<RooListProxy>(term->GetName(), this, *term));
151 }
152}
153
154////////////////////////////////////////////////////////////////////////////////
155/// Return to RooPolyFunc as a string
156
157std::string RooPolyFunc::asString() const
158{
159 std::stringstream ss;
160 bool first = true;
161 for (const auto &term : _terms) {
162 size_t n_vars = term->size() - 1;
163 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
164 if (coef->getVal() > 0 && !first)
165 ss << "+";
166 ss << coef->getVal();
167 first = true;
168 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
169 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
170 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
171 if (exp->getVal() == 0)
172 continue;
173 if (first) {
174 ss << " * (";
175 } else {
176 ss << "*";
177 }
178 ss << "pow(" << var->GetName() << "," << exp->getVal() << ")";
179 first = false;
180 }
181 if (!first)
182 ss << ")";
183 }
184 return ss.str();
185}
186
187////////////////////////////////////////////////////////////////////////////////
188/// Evaluate value of Polynomial.
190{
191 // Calculate and return value of polynomial
192 double poly_sum(0.0);
193 for (const auto &term : _terms) {
194 double poly_term(1.0);
195 size_t n_vars = term->size() - 1;
196 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
197 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
198 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
199 poly_term *= pow(var->getVal(), exp->getVal());
200 }
201 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
202 poly_sum += coef->getVal() * poly_term;
203 }
204 return poly_sum;
205}
206
207void setCoordinates(const RooAbsCollection &observables, std::vector<double> const &observableValues)
208// set all observables to expansion co-ordinate
209{
210 std::size_t iObs = 0;
211 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
212 var->setVal(observableValues[iObs++]);
213 }
214}
215
216void fixObservables(const RooAbsCollection &observables)
217{
218 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
219 var->setConstant(true);
220 }
221}
222
223//////////////////////////////////////////////////////////////////////////////
224/// Taylor expanding given function in terms of observables around
225/// observableValues. Supports expansions upto order 2.
226/// \param[in] name the name
227/// \param[in] title the title
228/// \param[in] func Function of variables that is taylor expanded.
229/// \param[in] observables Set of variables to perform the expansion.
230/// It's type is RooArgList to ensure that it is always ordered the
231/// same as the observableValues vector. However, duplicate
232/// observables are still not allowed.
233/// \param[in] order Order of the expansion (0,1,2 supported).
234/// \param[in] observableValues Coordinates around which expansion is
235/// performed. If empty, the nominal observable values are taken, if
236/// the size matches the size of the observables RooArgSet, the
237/// values are mapped to the observables in matching order. If it
238/// contains only one element, the same single value is used for all
239/// observables.
240/// \param[in] eps1 Precision for first derivative and second derivative.
241/// \param[in] eps2 Precision for second partial derivative of cross-derivative.
242std::unique_ptr<RooPolyFunc>
243RooPolyFunc::taylorExpand(const char *name, const char *title, RooAbsReal &func, const RooArgList &observables,
244 int order, std::vector<double> const &observableValues, double eps1, double eps2)
245{
246 // Create the taylor expansion polynomial
247 auto taylorPoly = std::make_unique<RooPolyFunc>(name, title, observables);
248
249 // Verify that there are no duplicate observables
250 {
252 for (RooAbsArg *obs : observables) {
253 obsSet.add(*obs, /*silent*/ true); // we can be silent now, the error will come later
254 }
255 if (obsSet.size() != observables.size()) {
256 std::stringstream errorMsgStream;
257 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: duplicate input observables!";
258 const auto errorMsg = errorMsgStream.str();
259 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
260 throw std::invalid_argument(errorMsg);
261 }
262 }
263
264 // Figure out the observable values around which to expand
265 std::vector<double> obsValues;
266 if (observableValues.empty()) {
267 obsValues.reserve(observables.size());
268 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
269 obsValues.push_back(var->getVal());
270 }
271 } else if (observableValues.size() == 1) {
272 obsValues.resize(observables.size());
273 std::fill(obsValues.begin(), obsValues.end(), observableValues[0]);
274 } else if (observableValues.size() == observables.size()) {
276 } else {
277 std::stringstream errorMsgStream;
278 errorMsgStream << "RooPolyFunc::taylorExpand(" << name
279 << ") ERROR: observableValues must be empty, contain one element, or match observables.size()!";
280 const auto errorMsg = errorMsgStream.str();
281 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
282 throw std::invalid_argument(errorMsg);
283 }
284
285 // taylor expansion can be performed only for order 0, 1, 2 currently
286 if (order >= 3 || order <= 0) {
287 std::stringstream errorMsgStream;
288 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: order must be 0, 1, or 2";
289 const auto errorMsg = errorMsgStream.str();
290 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
291 throw std::invalid_argument(errorMsg);
292 }
293
294 setCoordinates(observables, obsValues);
295
296 // estimate taylor expansion polynomial for different orders
297 // f(x) = f(x=x0)
298 // + \sum_i + \frac{df}{dx_i}|_{x_i=x_i^0}(x - x_i^0)
299 // + \sum_{i,j} 0.5 * \frac{d^f}{dx_i x_j}
300 // ( x_i - x_i^0 )( x_j - x_j^0 )
301 // note in the polynomial the () brackets are expanded out
302 for (int i_order = 0; i_order <= order; ++i_order) {
303 switch (i_order) {
304 case 0: {
305 taylorPoly->addTerm(func.getVal());
306 break;
307 }
308 case 1: {
309 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
310 double var1_val = var->getVal();
311 auto deriv = func.derivative(*var, 1, eps2);
312 double deriv_val = deriv->getVal();
313 setCoordinates(observables, obsValues);
314 taylorPoly->addTerm(deriv_val, *var, 1);
315 if (var1_val != 0.0) {
316 taylorPoly->addTerm(deriv_val * var1_val * -1.0);
317 }
318 }
319 break;
320 }
321 case 2: {
322 for (auto *var1 : static_range_cast<RooRealVar *>(observables)) {
323 double var1_val = var1->getVal();
324 auto deriv1 = func.derivative(*var1, 1, eps1);
325 for (auto *var2 : static_range_cast<RooRealVar *>(observables)) {
326 double var2_val = var2->getVal();
327 double deriv_val = 0.0;
328 if (strcmp(var1->GetName(), var2->GetName()) == 0) {
329 auto deriv2 = func.derivative(*var2, 2, eps2);
330 deriv_val = 0.5 * deriv2->getVal();
331 setCoordinates(observables, obsValues);
332 } else {
333 auto deriv2 = deriv1->derivative(*var2, 1, eps2);
334 deriv_val = 0.5 * deriv2->getVal();
335 setCoordinates(observables, obsValues);
336 }
337 taylorPoly->addTerm(deriv_val, *var1, 1, *var2, 1);
338 if (var1_val != 0.0 || var2_val != 0.0) {
340 taylorPoly->addTerm(deriv_val * var2_val * -1.0, *var1, 1);
341 taylorPoly->addTerm(deriv_val * var1_val * -1.0, *var2, 1);
342 }
343 }
344 }
345 break;
346 }
347 }
348 }
349 return taylorPoly;
350}
#define oocoutE(o, a)
#define coutE(a)
void fixObservables(const RooAbsCollection &observables)
void setCoordinates(const RooAbsCollection &observables, std::vector< double > const &observableValues)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
char name[80]
Definition TGX11.cxx:110
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2489
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
Abstract container object that can hold multiple RooAbsArg objects.
Storage_t::size_type size() const
bool addTyped(const RooAbsCollection &list, bool silent=false)
Adds elements of a given RooAbsCollection to the container if they match the specified type.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:103
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:24
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
Variable that can be changed from the outside.
Definition RooRealVar.h:37
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:64
@ InputArguments