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 std::endl;
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{
142}
143
144////////////////////////////////////////////////////////////////////////////////
145/// Copy constructor
146
147RooPolyFunc::RooPolyFunc(const RooPolyFunc &other, const char *name)
148 : RooAbsReal(other, name), _vars("vars", this, other._vars)
149{
150 for (auto const &term : other._terms) {
151 _terms.emplace_back(std::make_unique<RooListProxy>(term->GetName(), this, *term));
152 }
153}
154
155////////////////////////////////////////////////////////////////////////////////
156/// Return to RooPolyFunc as a string
157
158std::string RooPolyFunc::asString() const
159{
160 std::stringstream ss;
161 bool first = true;
162 for (const auto &term : _terms) {
163 size_t n_vars = term->size() - 1;
164 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
165 if (coef->getVal() > 0 && !first)
166 ss << "+";
167 ss << coef->getVal();
168 first = true;
169 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
170 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
171 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
172 if (exp->getVal() == 0)
173 continue;
174 if (first) {
175 ss << " * (";
176 } else {
177 ss << "*";
178 }
179 ss << "pow(" << var->GetName() << "," << exp->getVal() << ")";
180 first = false;
181 }
182 if (!first)
183 ss << ")";
184 }
185 return ss.str();
186}
187
188////////////////////////////////////////////////////////////////////////////////
189/// Evaluate value of Polynomial.
191{
192 // Calculate and return value of polynomial
193 double poly_sum(0.0);
194 for (const auto &term : _terms) {
195 double poly_term(1.0);
196 size_t n_vars = term->size() - 1;
197 for (size_t i_var = 0; i_var < n_vars; ++i_var) {
198 auto var = dynamic_cast<RooRealVar *>(_vars.at(i_var));
199 auto exp = dynamic_cast<RooRealVar *>(term->at(i_var));
200 poly_term *= pow(var->getVal(), exp->getVal());
201 }
202 auto coef = dynamic_cast<RooRealVar *>(term->at(n_vars));
203 poly_sum += coef->getVal() * poly_term;
204 }
205 return poly_sum;
206}
207
208void setCoordinates(const RooAbsCollection &observables, std::vector<double> const &observableValues)
209// set all observables to expansion co-ordinate
210{
211 std::size_t iObs = 0;
212 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
213 var->setVal(observableValues[iObs++]);
214 }
215}
216
217void fixObservables(const RooAbsCollection &observables)
218{
219 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
220 var->setConstant(true);
221 }
222}
223
224//////////////////////////////////////////////////////////////////////////////
225/// Taylor expanding given function in terms of observables around
226/// observableValues. Supports expansions upto order 2.
227/// \param[in] func Function of variables that is taylor expanded.
228/// \param[in] observables Set of variables to perform the expansion.
229/// It's type is RooArgList to ensure that it is always ordered the
230/// same as the observableValues vector. However, duplicate
231/// observables are still not allowed.
232/// \param[in] order Order of the expansion (0,1,2 supported).
233/// \param[in] observableValues Coordinates around which expansion is
234/// performed. If empty, the nominal observable values are taken, if
235/// the size matches the size of the observables RooArgSet, the
236/// values are mapped to the observables in matching order. If it
237/// contains only one element, the same single value is used for all
238/// observables.
239/// \param[in] eps1 Precision for first derivative and second derivative.
240/// \param[in] eps2 Precision for second partial derivative of cross-derivative.
241std::unique_ptr<RooPolyFunc>
242RooPolyFunc::taylorExpand(const char *name, const char *title, RooAbsReal &func, const RooArgList &observables,
243 int order, std::vector<double> const &observableValues, double eps1, double eps2)
244{
245 // Create the taylor expansion polynomial
246 auto taylorPoly = std::make_unique<RooPolyFunc>(name, title, observables);
247
248 // Verify that there are no duplicate observables
249 {
250 RooArgSet obsSet;
251 for (RooAbsArg *obs : observables) {
252 obsSet.add(*obs, /*silent*/ true); // we can be silent now, the error will come later
253 }
254 if (obsSet.size() != observables.size()) {
255 std::stringstream errorMsgStream;
256 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: duplicate input observables!";
257 const auto errorMsg = errorMsgStream.str();
258 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
259 throw std::invalid_argument(errorMsg);
260 }
261 }
262
263 // Figure out the observable values around which to expand
264 std::vector<double> obsValues;
265 if (observableValues.empty()) {
266 obsValues.reserve(observables.size());
267 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
268 obsValues.push_back(var->getVal());
269 }
270 } else if (observableValues.size() == 1) {
271 obsValues.resize(observables.size());
272 std::fill(obsValues.begin(), obsValues.end(), observableValues[0]);
273 } else if (observableValues.size() == observables.size()) {
274 obsValues = observableValues;
275 } else {
276 std::stringstream errorMsgStream;
277 errorMsgStream << "RooPolyFunc::taylorExpand(" << name
278 << ") ERROR: observableValues must be empty, contain one element, or match observables.size()!";
279 const auto errorMsg = errorMsgStream.str();
280 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
281 throw std::invalid_argument(errorMsg);
282 }
283
284 // taylor expansion can be performed only for order 0, 1, 2 currently
285 if (order >= 3 || order <= 0) {
286 std::stringstream errorMsgStream;
287 errorMsgStream << "RooPolyFunc::taylorExpand(" << name << ") ERROR: order must be 0, 1, or 2";
288 const auto errorMsg = errorMsgStream.str();
289 oocoutE(taylorPoly.get(), InputArguments) << errorMsg << std::endl;
290 throw std::invalid_argument(errorMsg);
291 }
292
293 setCoordinates(observables, obsValues);
294
295 // estimate taylor expansion polynomial for different orders
296 // f(x) = f(x=x0)
297 // + \sum_i + \frac{df}{dx_i}|_{x_i=x_i^0}(x - x_i^0)
298 // + \sum_{i,j} 0.5 * \frac{d^f}{dx_i x_j}
299 // ( x_i - x_i^0 )( x_j - x_j^0 )
300 // note in the polynomial the () brackets are expanded out
301 for (int i_order = 0; i_order <= order; ++i_order) {
302 switch (i_order) {
303 case 0: {
304 taylorPoly->addTerm(func.getVal());
305 break;
306 }
307 case 1: {
308 for (auto *var : static_range_cast<RooRealVar *>(observables)) {
309 double var1_val = var->getVal();
310 auto deriv = func.derivative(*var, 1, eps2);
311 double deriv_val = deriv->getVal();
312 setCoordinates(observables, obsValues);
313 taylorPoly->addTerm(deriv_val, *var, 1);
314 if (var1_val != 0.0) {
315 taylorPoly->addTerm(deriv_val * var1_val * -1.0);
316 }
317 }
318 break;
319 }
320 case 2: {
321 for (auto *var1 : static_range_cast<RooRealVar *>(observables)) {
322 double var1_val = var1->getVal();
323 auto deriv1 = func.derivative(*var1, 1, eps1);
324 for (auto *var2 : static_range_cast<RooRealVar *>(observables)) {
325 double var2_val = var2->getVal();
326 double deriv_val = 0.0;
327 if (strcmp(var1->GetName(), var2->GetName()) == 0) {
328 auto deriv2 = func.derivative(*var2, 2, eps2);
329 deriv_val = 0.5 * deriv2->getVal();
330 setCoordinates(observables, obsValues);
331 } else {
332 auto deriv2 = deriv1->derivative(*var2, 1, eps2);
333 deriv_val = 0.5 * deriv2->getVal();
334 setCoordinates(observables, obsValues);
335 }
336 taylorPoly->addTerm(deriv_val, *var1, 1, *var2, 1);
337 if (var1_val != 0.0 || var2_val != 0.0) {
338 taylorPoly->addTerm(deriv_val * var1_val * var2_val);
339 taylorPoly->addTerm(deriv_val * var2_val * -1.0, *var1, 1);
340 taylorPoly->addTerm(deriv_val * var1_val * -1.0, *var2, 1);
341 }
342 }
343 }
344 break;
345 }
346 }
347 }
348 return taylorPoly;
349}
#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:382
char name[80]
Definition TGX11.cxx:110
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2489
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.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
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:47
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:64
@ InputArguments