Space, Observable and Range¶
Inside zfit, Space
defines the domain of objects by specifying the observables/axes and maybe also
the limits. Any model and data needs to be specified in a certain domain, which is usually done using the
obs
argument. It is crucial that the axis used by the observable of the data and the model match, and this matching is
handle by the Space
class.
obs = zfit.Space("x")
model = zfit.pdf.Gauss(obs=obs, ...)
data = zfit.Data.from_numpy(obs=obs, ...)
Definitions¶
Space: an n-dimensional definition of a domain (either by using one or more observables or axes), with or without limits.
Note
compared to `RooFit`, a space is **not* the equivalent of an observable but rather corresponds
to an object combining a set of observables (which of course can be of size 1). Furthermore,
there is a strong distinction in zfit between a Space
(or observables)
and a Parameter
, both conceptually and in terms of implementation and usage.*
Observable: a string defining the axes; a named axes.
(for advanced usage only, can be skipped on first read) Axis: integer defining the axes internally of a model. There is always a mapping of observables <-> axes once inside a model.
Limit The range on a certain axis. Typically defines an interval.
Since every object has a well defined domain, it is possible to combine them in an unambiguous way
obs1 = zfit.Space(['x', 'y'])
obs2 = zfit.Space(['z', 'y'])
model1 = zfit.pdf.Gauss(obs=obs1, ...)
model2 = zfit.pdf.Gauss(obs=obs2, ...)
# creating a composite pdf
product = model1 * model2
# OR, equivalently
product = zfit.pdf.ProductPDF([model1, model2])
The product
is now defined in the space with observables [‘x’, ‘y’, ‘z’]. Any Data
object
to be combined with product
has to be specified in the same space.
# create the space
combined_obs = obs1 * obs2
data = zfit.Data.from_numpy(obs=combined_obs, ...)
Now we have a Data
object that is defined in the same domain as product and can be used to build a loss function.
Limits¶
In many places, just defining the observables is not enough and an interval, specified by its limits, is required. Examples are a normalization range, the limits of an integration or sampling in a certain region.
Simple, 1-dimensional limits can be specified as follows. Operations like addition (creating a space with two intervals) or combination (increase the dimensionality) are also possible.
simple_limit1 = zfit.Space(obs='obs1', limits=(-5, 1))
simple_limit2 = zfit.Space(obs='obs1', limits=(3, 7.5))
added_limits = simple_limit1 + simple_limit2
In this case, added_limits is now a Space
with observable ‘obs1’ defined in the intervals
(-5, 1) and (3, 7.5). This can be useful, e.g., when fitting in two regions.
An example of the product of different Space
instances has been shown before as combined_obs
.
Defining limits¶
To define simple, 1-dimensional limits, a tuple with two numbers is enough. For anything more complicated, the definition works as follows:
first_limit_lower = (low_1_obs1, low_1_obs2,...)
first_limit_upper = (up_1_obs1, up_1_obs2,...)
second_limit_lower = (low_2_obs1, low_2_obs2,...)
second_limit_upper = (up_2_obs1, up_2_obs2,...)
...
lower = (first_limit_lower, second_limit_lower, ...)
upper = (first_limit_upper, second_limit_upper, ...)
limits = (lower, upper)
space1 = zfit.Space(obs=['obs1', 'obs2', ...], limits=limits)
This defines the area from
- low_1_obs1 to up_1_obs1 in the first observable ‘obs1’;
- low_1_obs2 to up_1_obs2 in the second observable ‘obs2’;
- …
the area from
- low_2_obs1 to up_2_obs1 in the first observable ‘obs1’;
- low_2_obs2 to up_2_obs2 in the second observable ‘obs2’;
- …
and so on.
A working code example of Space
handling is provided in spaces.py in
examples.