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
/home/docs/checkouts/ UserWarning: TensorFlow warnings are by default suppressed by zfit. In order to show them, set the environment variable ZFIT_DISABLE_TF_WARNINGS=0. In order to suppress the TensorFlow warnings AND this warning, set ZFIT_DISABLE_TF_WARNINGS=1.
/home/docs/checkouts/ UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.8.0 and strictly below 2.11.0 (nightly versions are not supported). 
 The versions of TensorFlow you are currently using is 2.11.0 and is not supported. 
Some things might work, some things might not.
If you were to encounter a bug, do not file an issue.
If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. 
You can find the compatibility matrix in TensorFlow Addon's readme:
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!

obs = zfit.Space('obs1', limits=(-3, 6))

data_np = np.random.random(size=1000)
data =, 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)
probs_np =
[0.2603526  0.22185739 0.53765694 0.55440227 0.23965349 0.56418128
 0.23270662 0.441085   0.38860562 0.56175658 0.38245255 0.56244126
 0.52882103 0.47512278 0.5580549  0.56405825 0.39417902 0.29292409
 0.31678946 0.23429832]

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)


Advanced Custom PDF#

Subclass BasePDF. The _unnormalized_pdf has to be overriden and, in addition, the __init__.

Any of the public main methods (pdf, integrate, partial_integrate etc.) can always be overriden by implementing the function with a leading underscore, e.g. implement _pdf to directly controls pdf, the API is the same as the public function without the name. In case, during execution of your own method, it is found to be a bad idea to have overridden the default methods, throwing a NotImplementedError will restore the default behavior.