TF1::GetX when Y value not found?

Hi, I’m trying to use the TF1::GetX method to calculate the asymmetric half-widths of a function, but in some degenerate cases, the half-width point is outside the function range (or doesn’t exist). What does TF1::GetX return in that case? I tried looking deeper into the Brent solver thing, but it wasn’t ever clear.

Here is my example PyROOT code that tries to do this:

f_double_expo = ROOT.TF1("f_double_expo","expo(0)+expo(2)",minx,maxx)
f_p = g.Fit(f_double_expo,"RSWM")
mode = f_double_expo.GetMaximumX(minx,maxx,1e-10,1000)
mode_y = f_double_expo(mode)
upper_width = f_double_expo.GetX(mode_y/2.0,mode,maxx,1e-10,1000)
lower_width = f_double_expo.GetX(mode_y/2.0,minx,mode,1e-10,1000)

The problem in this particular example is that upper_width and lower_width are both equal! In fact, lower_width ends up being greater than mode, even though I set mode as an upper limit. What is going on?



I think if you search for a value x which has a given y in the range [xmin,xmax], if it does not exists, it will return xmim or xmax, depending which one is closer to y.

Best Regards


Thanks, that seems consistent with my (minimal) tests, but it would be nice for this to be documented in TF1::GetX(), or at least in the Brent solver documentation.


After trying to write some code to account for returning the endpoints when it doesn’t find the given Y value, I’ve found that it’s not actually working like that.

When the two endpoints are equal, the X value that is returned is outside of the whole interval, as if I had not put in any endpoints at all. Here is a minimal PyROOT example that shows this. You can copy and %paste it into an IPython shell, or save it as a file and run it with a regular python interpreter with PyROOT.

import ROOT

f = ROOT.TF1("f","[0]*TMath::Exp(-[1]*x)",0,1)

minx = f.GetXmin()
maxx = f.GetXmax()

mode_x = f.GetMaximumX(minx,maxx)
mode_y = f(mode_x)

print minx,mode_x,maxx,mode_y # Prints 0.0 0.0 1.0 1.0

upper_halfwidth = f.GetX(mode_y/2.0,mode_x,maxx)
lower_halfwidth = f.GetX(mode_y/2.0,minx,mode_x)

print upper_halfwidth, lower_halfwidth # Prints 0.693147180517 0.693147180517

Note that upper_halfwidth and lower_halfwidth are equal, and lower_halfwidth is outside the range specified by the second GetX() call.

I know that in my example the exponential function has only an upper halfwidth as it is monotonic, so the mode_x and minx are degenerate. Nevertheless, that shouldn’t let GetX() return a value outside the desired range!

Edit: if you change the function to a Gaussian like so:

f = ROOT.TF1("f","gaus",-2,2)

then it behaves as expected. If the function limits are too small (say, -0.5, 0.5 instead of -2, 2), then it finds the endpoints, also as expected.


1 Like


The problem comes from the fact that your interval you are searching is empty. If xmim >= xmax then the full function range is used when calling TF1::GetX. This explains the result you get.
This also should be better documented ! We will do this.

Maybe, if you want to do something more sophisticated and control better the algorithm, you can use the ROOT::Math::RootFinder class in ROOT.
See … algorithms (the paragraph Using the plugin manager).
In particular, if you use any of the GSL algorithm, (e.g. kGSL_BRENT) you should have also better diagnostic.

Best Regards


Thanks. For my purposes I will just add checks in case minx == mode_x or maxx == mode_x, and raise an exception if the value found is one of the endpoints.