Get coefficient a0 of RooChebychev

Chebyshev polynomials are universally defined with their coefficients.

RooFit assumes the 0-order coefficient a0 to be 1. During the fit the polynomial is automatically normalised to 1 as well (scaling a0 to the appropriate value).

My question is, how one can get that scaled value of the real a0? Even without its uncertainty.

P.S. The numbering of polynomial coefficient is non-evident. It is very unfortunate that in the manual they start with a0, though in fact that is a1.
On page 18 of the recent manual:

RooRealVar c0(“c0”,”coefficient #0”, 1.0,-1.,1.) ;
RooRealVar c1(“c1”,”coefficient #1”, 0.1,-1.,1.) ;
RooRealVar c2(“c2”,”coefficient #2”,-0.1,-1.,1.) ;
RooChebychev bkg(“bkg”,”background p.d.f.”,x,RooArgList(c0,c1,c2)) ;

It would be good to make that numbering reflected in the manual: it should be a1, a2, a3 in that example. Feel free to open an issue for that.
It has led to confusion even on this forum (Chebyshev polynomials parameters - #2 by mdessole).

UPD: and there is not a word in the manual about that non-mathematical numbering.

@jonas could you have a look here?

Hi @ynikitenko!

You can get the normalization integral and then divide the coefficients by it to get the actual coefficients of the normalized pdf.

I have prepared a little demo to show how to do this, comparing also with the analytical Chebychev polynomials:

double cheby_t2(double x, double a0, double a1, double a2) {
   double t0 = 1;
   double t1 = x;
   double t2 = 2*x*t1 - t0;
   return a0 * t0 + a1 * t1 + a2 * t2;
}

void demo() {

RooRealVar x("x", "x", 0, 10);
x.setBins(10);

RooRealVar a1("a1", "a1", 0.5, 0., 1.);
RooRealVar a2("a2", "a2", 0.0, 0., 1.);

RooChebychev cheb("cheb", "Background", x, RooArgSet(a1, a2));

RooArgSet normSet{x};

std::unique_ptr<RooAbsReal> chebIntegral{cheb.createIntegral(normSet)};

double normVal = chebIntegral->getVal();
double a0Val = 1./normVal;
double a1Val = a1.getVal()/normVal;
double a2Val = a2.getVal()/normVal;

std::cout << "norm : " << normVal << std::endl;
std::cout << "a0   : " << a0Val << std::endl;
std::cout << "a1   : " << a1Val << std::endl;
std::cout << "a2   : " << a2Val << std::endl;

// To compare with analytical Chebychev as cross check:
for (int i = 0; i < x.numBins(); ++i) {
    x.setBin(i);
    double xScaled = 2. * (x.getVal() - x.getMin()) / (x.getMax() - x.getMin()) - 1.;
    double ref = cheby_t2(xScaled, a0Val, a1Val, a2Val);
    std::cout << i << " : x=" << x << "  roocheb=" << cheb.getVal(normSet) << "   ref=" << ref << std::endl;
}

}

Here is the output:

   ------------------------------------------------------------------
  | Welcome to ROOT 6.35.01                        https://root.cern |
  | (c) 1995-2024, The ROOT Team; conception: R. Brun, F. Rademakers |
  | Built for linuxx8664gcc on Jan 01 1980, 00:00:00                 |
  | From heads/master@v6-35-01-1714-g978479d9c9                      |
  | With g++ (GCC) 14.2.1 20241116                                   |
  | Try '.help'/'.?', '.demo', '.license', '.credits', '.quit'/'.q'  |
   ------------------------------------------------------------------

root [0]
Processing demo.C...
norm : 10
a0   : 0.1
a1   : 0.05
a2   : 0
0 : x=0.5  roocheb=0.055   ref=0.055
1 : x=1.5  roocheb=0.065   ref=0.065
2 : x=2.5  roocheb=0.075   ref=0.075
3 : x=3.5  roocheb=0.085   ref=0.085
4 : x=4.5  roocheb=0.095   ref=0.095
5 : x=5.5  roocheb=0.105   ref=0.105
6 : x=6.5  roocheb=0.115   ref=0.115
7 : x=7.5  roocheb=0.125   ref=0.125
8 : x=8.5  roocheb=0.135   ref=0.135
9 : x=9.5  roocheb=0.145   ref=0.145
root [1]

About the documentation: the reference guide is not updated for a long time, and we won’t do that anymore.

Maybe you have suggestions to update the reference guide instead, or the RooFit tutorials?

Cheers,
Jonas

1 Like

Hi @jonas!

Thanks for the solution!

I didn’t understand the point with the reference guide (I believe you gave the same link as me).
The RooFit tutorials don’t work for me at the moment (I have to use Python 2 to get rid of the error). I use ROOT 6.30/06, so that should not be a priority, I believe.

Meanwhile I’ve found another solution. The value of the polynomial at 0 is very simple, since each odd contribution is zero, and for n=4k Tn(0) is 1, and for n=4k+2 it is -1. So one can simply subtract

a0 = Chebychev(0) - sum(n=4k+4)(a_n) + sum(n=4k+2)(a_n).

I should think more how to implement that.
One can also calculate the error of a0 by taking the square root of the sum of the variances of each coefficient involved.

P.S. Just in case, here is my error (please try that with the updated version). Everything was compiled with proper flags, since Python 2 works fine with RooFit. So every RooFit tutorial that uses RooRealVar fails in Python 3.13.2.

$ python -c ‘from ROOT import RooRealVar’
Error pythonizing class RooAbsRealLValue:
Traceback (most recent call last):
File “/opt/root/install/lib/ROOT/_pythonization/__init__.py”, line 233, in _invoke
user_pythonizor(klass, fqn)
~~~~~~~~~~~~~~~^^^^^^^^^^^^
File “/opt/root/install/lib/ROOT/_pythonization/_roofit/__init__.py”, line 237, in pythonize_roofit_class
func_new.__doc__ = “Pythonization info\n”
^^^^^^^^^^^^^^^^
AttributeError: ‘int’ object attribute ‘__doc__’ is read-only
Traceback (most recent call last):
File “”, line 1, in
from ROOT import RooRealVar
ImportError: cannot import name ‘RooRealVar’ from ‘ROOT’ (/opt/root/install/lib/ROOT/__init__.py)

This is the code I’ve written in Python to implement my algorithm (its advantage is that it calculates the uncertainty as well):

from math import sqrt


def get_a0(chebyshev, x, coef_list):
    """Get parameter a0 of the RooChebychev
    polynomial *chebyshev(x)* given the list of other 
    coefficients *coef_list*=[a1, a2, a3,...].
    """
    ## Get uncertainty ##
    even_coefs = coef_list[1::2]
    a0_err = sqrt(sum(coef.getError()**2 for coef in even_coefs))

    ## Get value ##
    # get polynomial value at x=0
    x.setVal(0.)
    f0 = chebyshev.getVal(x)
    sum_4k = sum(coef.getVal() for coef in even_coefs[1::2])
    sum_4kplus2 = sum(coef.getVal() for coef in even_coefs[::2])
    a0_val = f0 - sum_4k + sum_4kplus2

    a0 = ROOT.RooRealVar("a0", "a0", a0_val)
    a0.setError(a0_err)
    return a0

It seems to work. Thank you @jonas for the alternative. Please take a look into the other topics discussed here (sorry for mixing them all together).

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.