Skip to content

Commit

Permalink
Merge branch 'master' into bw
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-eschle committed Apr 3, 2020
2 parents bc4e0b4 + e14ae09 commit 43b4e90
Show file tree
Hide file tree
Showing 14 changed files with 348 additions and 90 deletions.
1 change: 1 addition & 0 deletions .git_archival.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref-names: $Format:%D$
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.git_archival.txt export-subst
20 changes: 10 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
language: python
os:
- linux
matrix:
include:
- python: 3.7
dist: xenial
sudo: true
- python: 3.6
install: pip install -U tox-travis
python:
- "3.6"
- "3.7"
install: pip install -U pip
script:
- tox
- pip install git+https://github.com/zfit/zfit
- pip install -U .
#- pip install git+https://github.com/zfit/zfit
- pip install -e .
- bash utils/ci/test_examples.sh
- pip install -r requirements_dev.txt
- ls
- ls /home/travis/build
- coverage run --source=/home/travis/build/zfit --branch -m pytest --basetemp={envtmpdir}
- coverage report
after_success:
- pip install coveralls
- coveralls
Expand Down
32 changes: 31 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,34 @@ zfit physics
------------

Tools and models to extend `zfit <https://github.com/zfit/zfit>`_.
The structure of this package is still in flow, so before opening any PR you may want to open an issue to discuss how your proposed functionality will fit into `zfit-physics`.
The structure of this package is still in flow, so before opening any PR
you may want to open an issue to discuss how your proposed functionality will fit into `zfit-physics`.

It is currently under heavy development. Use it to test currently experimental
features. It offers a similar structure are zfit and will in the future be
simply integrated into the zfit namespace (given that zfit-physics is installed).

Installing
----------

zfit-physics is available on conda-forge and pip. If possible, use a conda or virtual environment and do:

For conda:

.. code-block:: console
$ conda install zfit-physics -c conda-forge
For pip (if you don't use conda):

.. code-block:: console
$ pip install zfit-physics
For the newest development version, you can install the version from git with

.. code-block:: console
$ pip install git+https://github.com/zfit/zfit-physics
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
zfit>=0.3.1
zfit>=0.3.7
numpy
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"requirements have to be separated by one blank line.")
requirements_dev_split = requirements_dev.index("")

setup_requirements = requirements_dev[:requirements_dev_split]
setup_requirements = ["pip>9",
"setuptools_scm",
"setuptools_scm_git_archive"]
test_requirements = requirements_dev[requirements_dev_split + 1:] # +1: skip empty line

setup(
Expand Down Expand Up @@ -59,6 +61,6 @@
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/zfit/zfit-physics',
version='0.0.1',
use_scm_version=True,
zip_safe=False,
)
26 changes: 17 additions & 9 deletions tests/test_pdf_conv.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Example test for a pdf or function"""
import pytest
import zfit
import tensorflow as tf
# Important, do the imports below
from zfit.core.testing import setup_function, teardown_function, tester

Expand All @@ -14,16 +15,23 @@

def test_conv_simple():
# test special properties here
n_points = 2000
obs = zfit.Space("obs1", limits=(-5, 5))
gauss1 = zfit.pdf.Gauss(0., 1., obs=obs)
uniform1 = zfit.pdf.Uniform(-1, 1., obs=obs)
conv = zphys.unstable.pdf.ConvPDF(func=lambda x: uniform1.pdf(x),
kernel=lambda x: gauss1.pdf(x),
limits=obs, obs=obs)
param1 = zfit.Parameter('param1', -3)
param2 = zfit.Parameter('param2', 0.3)
gauss1 = zfit.pdf.Gauss(0., param2, obs=obs)
uniform1 = zfit.pdf.Uniform(param1, param2, obs=obs)
conv = zphys.unstable.pdf.NumConvPDFUnbinnedV1(func=uniform1,
kernel=gauss1,
limits=obs, obs=obs)

x = np.linspace(-5, 5, 1000)
x = tf.linspace(-5., 5., n_points)
probs = conv.pdf(x=x)
integral = conv.integrate(limits=obs)
probs_np = zfit.run(probs)
assert pytest.approx(1, rel=1e-3) == zfit.run(integral)
assert len(probs_np) == 1000
probs_np = probs.numpy()
assert pytest.approx(1, rel=1e-3) == integral.numpy()
assert len(probs_np) == n_points
# import matplotlib.pyplot as plt
# plt.plot(x, probs_np)
# plt.show()
# assert len(conv.get_dependents(only_floating=False)) == 2 # TODO: activate again with fixed params
10 changes: 5 additions & 5 deletions tests/test_pdf_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def test_special_property1():

# register the pdf here and provide sets of working parameter configurations

def _gauss_params_factory():
mu_ = zfit.Parameter('mu_cb', param1_true)
sigma_ = zfit.Parameter('sigma_cb', param2_true)
return {"mu": mu_, "sigma": sigma_}
def gauss_params_factory():
mu = zfit.Parameter('mu', param1_true)
sigma = zfit.Parameter('sigma', param2_true)
return {"mu": mu, "sigma": sigma}


tester.register_pdf(pdf_class=zfit.pdf.Gauss, params_factories=_gauss_params_factory())
tester.register_pdf(pdf_class=zfit.pdf.Gauss, params_factories=gauss_params_factory)
45 changes: 41 additions & 4 deletions tests/test_pdf_kde.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Example test for a pdf or function"""

import tensorflow as tf
import zfit
import numpy as np
# Important, do the imports below
Expand All @@ -12,18 +12,55 @@
param2_true = 1.2


def test_simple_kde():
def test_simple_kde_3d():
# test special properties here
data = np.random.normal(size=(100, 3))
sigmas = [0.5, 1., 2]
sigma = zfit.Parameter("sigma", 0.5)

sigmas = [sigma, 1., 2]
lower = ((-5, -5, -5),)
upper = ((5, 5, 5),)
obs = zfit.Space(["obs1", "obs2", "obs3"], limits=(lower, upper))

kde = zphys.unstable.pdf.GaussianKDE(data=data, bandwidth=sigmas, obs=obs)

probs = kde.pdf(x=data + 0.03)
probs_np = zfit.run(probs)
probs_np = probs.numpy()
from zfit.loss import UnbinnedNLL
data = np.random.normal(size=(100, 3))

data = zfit.Data.from_tensor(tensor=data, obs=obs)
nll = UnbinnedNLL(model=kde, data=data)

minimizer = zfit.minimize.Minuit()

minimum = minimizer.minimize(loss=nll)
assert minimum.converged


def test_simple_kde_1d():
# test special properties here
# zfit.settings.options['numerical_grad'] = True
data = tf.random.normal(shape=(100, 1))
# data = np.random.normal(size=(100, 1))
sigma = zfit.Parameter("sigma", 0.5)
lower = ((-5,),)
upper = ((5,),)
obs = zfit.Space(["obs1"], limits=(lower, upper))

kde = zphys.unstable.pdf.GaussianKDE(data=data, bandwidth=sigma, obs=obs)

probs = kde.pdf(x=data + 0.03)

from zfit.loss import UnbinnedNLL
data = tf.random.normal(shape=(100, 1))
data = zfit.Data.from_tensor(tensor=data, obs=obs)
nll = UnbinnedNLL(model=kde, data=data)

minimizer = zfit.minimize.Minuit()

minimum = minimizer.minimize(loss=nll)
assert minimum.converged


# register the pdf here and provide sets of working parameter configurations
Expand Down
3 changes: 2 additions & 1 deletion zfit_physics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
"""Top-level package for zfit."""
from pkg_resources import get_distribution

__version__ = '0.0.1'
__version__ = get_distribution(__name__).version

__license__ = "BSD 3-Clause"
__copyright__ = "Copyright 2019, zfit"
Expand Down
128 changes: 85 additions & 43 deletions zfit_physics/models/pdf_conv.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,105 @@
from functools import reduce
from typing import Callable, Union

import tensorflow as tf
import tensorflow_probability as tfp
import zfit
from zfit import ztf
from zfit.util import ztyping
import zfit.models.functor
from zfit import ztf, z
from zfit.util import exception
from zfit.util.container import convert_to_container
from zfit.util.exception import DueToLazynessNotImplementedError
from zfit.util import ztyping
from zfit.util.exception import WorkInProgressError


class ConvPDF(zfit.pdf.BasePDF):
def __init__(self, func: Union[Callable, zfit.pdf.BasePDF], kernel: Union[Callable, zfit.pdf.BasePDF],
class NumConvPDFUnbinnedV1(zfit.models.functor.BaseFunctor):
def __init__(self, func: zfit.pdf.BasePDF, kernel: zfit.pdf.BasePDF,
limits: ztyping.ObsTypeInput, obs: ztyping.ObsTypeInput,
ndraws: int = 20000, name: str = "Convolution"):
"""Numerical Convolutional pdf of _func_ convoluted with _kernel_.
ndraws: int = 20000, name: str = "Convolution", experimental_pdf_normalized=False):
"""Numerical Convolution pdf of *func* convoluted with *kernel*.
Args:
func (callable): Function that takes x and return the function value. Here x is a `Data` with the obs and limits
of _limits_. Can also be a PDF.
kernel (callable): Kernel function that takes x and return the function value. Here x is a `Data` with the obs and limits
of _limits_. Can also be a PDF.
limits (:py:class:`zfit.Space`): Limits of the numerical integration
obs (:py:class:`zfit.Space`):
func (:py:class:`zfit.pdf.BasePDF`): PDF with `pdf` method that takes x and returns the function value.
Here x is a `Data` with the obs and limits of *limits*.
kernel (:py:class:`zfit.pdf.BasePDF`): PDF with `pdf` method that takes x acting as the kernel.
Here x is a `Data` with the obs and limits of *limits*.
limits (:py:class:`zfit.Space`): Limits for the numerical integration.
obs (:py:class:`zfit.Space`): Observables of the class
ndraws (int): Number of draws for the mc integration
name (str):
name (str): Human readable name of the pdf
"""
super().__init__(obs=obs, params={}, name=name)
super().__init__(obs=obs, pdfs=[func, kernel], params={}, name=name)
limits = self._check_input_limits(limits=limits)
if limits.n_limits == 0:
raise exception.LimitsNotSpecifiedError("obs have to have limits to define where to integrate over.")
if limits.n_limits > 1:
raise DueToLazynessNotImplementedError("Multiple Limits not implemented")

if isinstance(func, zfit.pdf.BasePDF):
func = lambda x: func.unnormalized_pdf(x=x)
if isinstance(kernel, zfit.pdf.BasePDF):
kernel = lambda x: kernel.unnormalized_pdf(x=x)

lower = limits.lower[0]
upper = limits.upper[0]
lower = ztf.convert_to_tensor(lower, dtype=self.dtype)
upper = ztf.convert_to_tensor(upper, dtype=self.dtype)
samples_normed = tfp.mcmc.sample_halton_sequence(dim=limits.n_obs, num_results=ndraws, dtype=self.dtype,
randomized=False)
samples = samples_normed * (upper - lower) + lower # samples is [0, 1], stretch it
samples = zfit.Data.from_tensor(obs=limits, tensor=samples)

self._grid_points = samples # true vars
self._kernel_func = kernel # callable func of reco - true vars
self._func_values = func(samples) # func of true vars
self._conv_limits = limits
raise WorkInProgressError("Multiple Limits not implemented")

# if not isinstance(func, zfit.pdf.BasePDF):
# raise TypeError(f"func has to be a PDF, not {type(func)}")
# if isinstance(kernel, zfit.pdf.BasePDF):
# raise TypeError(f"kernel has to be a PDF, not {type(kernel)}")

# func = lambda x: func.unnormalized_pdf(x=x)
# kernel = lambda x: kernel.unnormalized_pdf(x=x)

self._grid_points = None # true vars # callable func of reco - true vars
self._func_values = None
self.conv_limits = limits
self._ndraws = ndraws
self._experimental_pdf_normalized = experimental_pdf_normalized

@z.function
def _unnormalized_pdf(self, x):
area = self._conv_limits.area()
limits = self.conv_limits
area = limits.area()

samples = self._grid_points
func_values = self._func_values
# if func_values is None:
if True:
# create sample for numerical integral
lower = limits.lower[0]
upper = limits.upper[0]
lower = ztf.convert_to_tensor(lower, dtype=self.dtype)
upper = ztf.convert_to_tensor(upper, dtype=self.dtype)
samples_normed = tfp.mcmc.sample_halton_sequence(dim=limits.n_obs, num_results=self._ndraws,
dtype=self.dtype,
randomized=False)
samples = samples_normed * (upper - lower) + lower # samples is [0, 1], stretch it
samples = zfit.Data.from_tensor(obs=limits, tensor=samples)
self._grid_points = samples

func_values = self.pdfs[0].unnormalized_pdf(samples) # func of true vars
self._func_values = func_values

return tf.map_fn(
lambda xi: area * tf.reduce_mean(self._func_values * self._kernel_func(xi - self._grid_points)),
lambda xi: area * tf.reduce_mean(func_values * self.pdfs[1].unnormalized_pdf(xi - samples)),
x.value()) # func of reco vars

@z.function
def _pdf(self, x, norm_range):
if not self._experimental_pdf_normalized:
raise NotImplementedError

limits = self.conv_limits
area = limits.area()

samples = self._grid_points
func_values = self._func_values
# if func_values is None:
if True:
# create sample for numerical integral
lower = limits.lower[0]
upper = limits.upper[0]
lower = ztf.convert_to_tensor(lower, dtype=self.dtype)
upper = ztf.convert_to_tensor(upper, dtype=self.dtype)
samples_normed = tfp.mcmc.sample_halton_sequence(dim=limits.n_obs, num_results=self._ndraws,
dtype=self.dtype,
randomized=False)
samples = samples_normed * (upper - lower) + lower # samples is [0, 1], stretch it
samples = zfit.Data.from_tensor(obs=limits, tensor=samples)
self._grid_points = samples

func_values = self.pdfs[0].pdf(samples) # func of true vars
self._func_values = func_values

return tf.map_fn(
lambda xi: area * tf.reduce_mean(func_values * self.pdfs[1].pdf(xi - samples)),
x.value())
Loading

0 comments on commit 43b4e90

Please sign in to comment.