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
 
                        
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
sigflavandtagflav, since you integrate over them. In case you have a partial integral, you can access them throughx, which acts as the argument in the_unnormalized_pdfcase.Space from axes
A
Spacedefines 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,sigflavis 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 aSpacethat is defined in terms ofaxes.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