Creating your own pdf

Contents

Creating your own pdf#

A core feature of zfit is the ability to create custom pdfs and functions in an simple and straightforward way.

There are two main possibilities to create a custom pdf, an easier for most use-cases and an advanced way..

The simple way#

While the same works for functions, an example with a PDF is shown here.

import numpy as np
import zfit
from zfit import z
No module named 'nlopt'

The first way is the most simple and should only be used for the trivial cases, i.e. if you’re not familiar with Python classes (especially not with the __init__ method).

class MyGauss(zfit.pdf.ZPDF):
    _N_OBS = 1  # dimension, can be omitted
    _PARAMS = ['mean', 'std']  # the name of the parameters

    def _unnormalized_pdf(self, x):
        x = z.unstack_x(x)  # returns a list with the columns: do x, y, z = z.unstack_x(x) for 3D
        mean = self.params['mean']
        std = self.params['std']
        return z.exp(- ((x - mean) / std) ** 2)

Done. Now we can use our pdf already!

The slightly more general way involves overwritting the __init__ and gives you all the possible flexibility: to use custom parameters, to preprocess them etc.

Here we inherit from BasePDF

class MyGauss(zfit.pdf.BasePDF):

    def __init__(self, mean, std, obs, extended=None, norm=None, name=None):
        params = {'mean': mean,  # 'mean' is the name as it will be named in the PDF, mean is just the parameter to create the PDF
                  'std': std
                  }
        super().__init__(obs=obs, params=params, extended=extended, norm=norm,
                         name=name)

    def _unnormalized_pdf(self, x):
        x = z.unstack_x(x)
        mean = self.params['mean']
        std = self.params['std']
        return z.exp(- ((x - mean) / std) ** 2)
obs = zfit.Space('obs1', limits=(-3, 6))

data_np = np.random.random(size=1000)
data = zfit.data.Data.from_numpy(array=data_np, obs=obs)

Create two parameters and an instance of your own pdf

mean = zfit.Parameter("mean", 1.)
std = zfit.Parameter("std", 1.)
my_gauss = MyGauss(obs=obs, mean=mean, std=std)
probs = my_gauss.pdf(data)
print(probs[:20])
tf.Tensor(
[0.51757937 0.41831597 0.54418191 0.542958   0.23268375 0.55285151
 0.27772533 0.56409576 0.51026856 0.55642136 0.20802155 0.56285633
 0.33878087 0.45248238 0.46990824 0.54277072 0.28640948 0.49710957
 0.43360974 0.29180561], shape=(20,), dtype=float64)

If we want to make sure it’s a numpy array, we can use zfit.run

We could improve our PDF by registering an integral

def gauss_integral_from_any_to_any(limits, params, model):
    lower, upper = limits.limit1d
    mean = params['mean']
    std = params['std']
    # write your integral here
    return 42.  # dummy integral, must be a scalar!
limits = zfit.Space(axes=0, limits=(zfit.Space.ANY_LOWER, zfit.Space.ANY_UPPER))
MyGauss.register_analytic_integral(func=gauss_integral_from_any_to_any, limits=limits)

More advanced custom PDFs are introduced in the guide on custom PDFs.