Accessing PDF variables when registering analytic integral

54 Views Asked by At

Firstly, thank you for making a really neat, pythonic tool which can be used as an alternative for RooFit.

I have successfully defined a custom 2D PDF but I am not completely sure on how to register my function's analytic integral:

Analytic integral from WolframAlpha

The PDF is defined as below:

class PdfForDeltaW(zfit.pdf.ZPDF):
    """Pdf to calculate epsilon, w, and delta w, as a function of sig-flav and tag-flav"""

    _N_OBS = 2
    _PARAMS = "epsilon w delta_w mix_prob".split()

    def _unnormalized_pdf(self, x):
        """Calculation of PDF value"""
        sigflav, tagflav = zfit.ztf.unstack_x(x)

        epsilon = self.params["epsilon"]
        w = self.params["w"]
        delta_w = self.params["delta_w"]
        mix_prob = self.params["mix_prob"]

        dilution = 1 - 2 * w
        mixing = 1 - 2 * mix_prob
        return (
                0.5
                * epsilon
                * (1 - sigflav * tagflav * (sigflav * delta_w + dilution * mixing))
        )

From looking at the example on github I am not sure on how I can access the fit observables for use in the calculation (i.e. x, sigflav and y, tagflav for my 2D case) in addition to the fit variables which can be accessed through the params attribute.

In addition, I am not sure how my limits should be defined. I know that both x and y must be in the range [-1, 1]. I think it would be nice to have a bit more clarity on how the zfit.Space.from_axes function should be used, and how this relates to the analytic integral.

Cheers, Colm

1

There are 1 best solutions below

0
On

Thanks for that, let me explain the different things:

Fit variables

If you want to register an integral over the whole PDF, you would not need to access sigflav and tagflav, since you integrate over them. In case you have a partial integral, you can access them through x, which acts as the argument in the _unnormalized_pdf case.

Space from axes

A Space defines your coordinates and the limits/ranges. Usually, as a user of a PDF, this involves observables (like columns of a DataFrame). But if we create a PDF, the observables that it is going to be used with are of course not known, the PDF works "position based", or "axes based". E.g. in your example, sigflav is on axis 0, whatever the observable will be named that the PDF will be used with. This is why when registering the integral, we need to use a Space that is defined in terms of axes.

Integral limits

There can be different control over the limits of a PDF. If needed, more fine grained control can be implemented, but currently available is that you can define an integral from a certain point/or anywhere to a certain point/anywhere.

This is useful since you can register several integrals. Maybe you know the general integral, which has a complicated form. But you also know e.g. that the integral from -1 to 1 is exactly 1. So you can register this as well and give it a higher priority. This means that if you integrate over -1 to 1, the simpler one is used, otherwise the more general form.

In your case, you want to register from ANY to ANY, or use as limits ANY_LOWER, ANY_UPPER, like this

lower_full = ((zfit.Space.ANY_LOWER, zfit.Space.ANY_LOWER),)
upper_full = ((zfit.Space.ANY_UPPER, zfit.Space.ANY_UPPER),)
integral_full_limits = zfit.Space.from_axes(axes=(0, 1),
                                            limits=(lower_full, upper_full))