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/readthedocs.org/user_builds/zfit/checkouts/latest/.venv/lib/python3.12/site-packages/zfit/__init__.py:60: 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.
warnings.warn(
2025-01-16 14:47:49.480645: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1737038869.494418 2003 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
E0000 00:00:1737038869.498459 2003 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
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
@zfit.supports()
def _unnormalized_pdf(self, x, params):
x0 = x[0] # using the 0th axis
mean = params['mean']
std = params['std']
return z.exp(- ((x0 - 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, label=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, label=label)
@zfit.supports()
def _unnormalized_pdf(self, x, params):
x0 = x[0] # using the 0th axis
mean = params['mean']
std = params['std']
return z.exp(- ((x0 - mean) / std) ** 2)
obs = zfit.Space('obs1', -3, 6)
data_np = np.random.random(size=1000)
data = zfit.Data(data_np, obs=obs)
2025-01-16 14:47:52.932810: E external/local_xla/xla/stream_executor/cuda/cuda_driver.cc:152] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303)
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.46373369 0.42927452 0.21636689 0.4581734 0.35300346 0.42272391
0.34904079 0.29650338 0.53731698 0.56416782 0.48109953 0.54481379
0.52928329 0.54690905 0.31840048 0.50552511 0.48599765 0.56417652
0.55112653 0.49145882], 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.v1.limits
mean = params['mean']
std = params['std']
# write your integral here
return 42. # dummy integral, must be a scalar!
limits = zfit.Space(axes=0, lower=zfit.Space.ANY_LOWER, upper=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.