Using the validphys API
Introduction
The API functionality allows the validphys
/reportengine
machinery to be readily leveraged in a development setting such as a
Jupyter notebook. Any action available to validphys
can be invoked
by Python code, with the same parameters as a runcard.
For example:
from validphys.api import API
figs = API.plot_pdfs(pdfs=["NNPDF40_nlo_as_01180"], Q=2)
for f, _ in figs:
f.show()
The API
object provides a high level interface to the validphys
code. Note that the user doesn’t need to know exactly how to load the
PDF, create the relevant grid to be plotted and then pass that to the
plotting function, this is all handled by the underlying code of
reportengine
. This abstraction provides a convenient way to explore
the functionality of validphys
(or any other reportengine
application) as well as to develop further functionality for
validphys
itself.
All the actions available to validphys
are translated into methods
of the API
object. The arguments are the same as the parameters that
the validphys runcard would need to evaluate the action.
Generic Example
An important use case of this functionality is the development Consider
that you wanted to develop a provider which depends on some expensive
providers, defined somewhere in the validphys
modules,
def expensive_provider1(pdf:PDF, Q, theoryid):
...
def expensive_provider2(experiments, ...):
...
Now in a notebook we can do
from validphys.api import API
expensive1 = API.expensive_provider1(pdf="NNPDF40_nlo_as_01180", Q=100, theoryid=208)
expensive2 = API.expensive_provider2(dataset_inputs={"from_": "fit"}, fit="NNPDF40_nlo_as_01180")
We can then define and test our new function (e.g. in a separate notebook cell),
def developing_provider(expensive_provider1, expensive_provider2):
...
test_output = developing_provider(expensive1, expensive2)
expensive1
and expensive2
have already been evaluated using the
validphys machinery, and we just had to declare the validphys2
runcard inputs in order to use those providers. The output of these
expensive function is now saved. So for the remainder of our notebook
session we don’t need to re-run the expensive providers every time we
wish to change something with our developing_provider
. Clearly this
massively reduces the time to develop and test the new provider since,
the expensive providers which the new developing_provider
depends on
are cached for the rest of the jupyter session.
For expensive_provider2
the input was slightly more complicated.
When using the API remember that the input is exactly the same as a
validphys2
runcard. The runcards are in a yaml
format which is
then parsed as a dict
. If it seems more intuitive one can utilise
this when declaring the inputs for the API providers, for example:
input2 = {
"experiments": {
"from_": "fit"
},
"fit": "NNPDF40_nlo_as_01180"
}
expensive2 = API.expensive_provider2(**input2)
The input2
dictionary is visually almost identical the corresponding
validphys2
runcard, we just need to be careful the separate items
with commas, that all dict keys are strings and that the typing is
correct for the various inputs, we can always look up the appropriate
typing by using the validphys --help
functionality.
Creating figures in the validphys
style
If a figure is created using the api, as with the first example:
from validphys.api import API
fig = API.some_plot(...)
fig.show()
you might notice that the style of the plot is very different to those
produce by validphys
. If you want to use the same style as
validphys
then consider using the following commands at the top of
your script or notebook:
import matplotlib
from validphys import mplstyles
matplotlib.style.use(str(mplstyles.smallstyle))
also consider using fig.tight_layout()
which reportengine uses
before saving figures. For the example used earlier we would then have
import matplotlib
from validphys import mplstyles
matplotlib.style.use(str(mplstyles.smallstyle))
from validphys.api import API
figs = API.plot_pdfs(pdfs=["NNPDF40_nlo_as_01180"], Q=2)
for f, _ in figs:
f.tight_layout()
f.show()
Mixing declarative input with custom resources (NOTE: Experimental)
For some actions it is possible to mix declarative input with custom resources.
Take for example xplotting_grid
, which minimally requires us to
specify pdf
, Q
. We see from validphys --help xplotting_grid
that it depends on the provider xgrid
which in turn returns a tuple
of (scale, x_array)
. Using the API we could specify our own custom
xgrid input, but then rely on the API to collect the other relevant
resources, for example:
import numpy as np
from validphys.api import API
new_xgrid = ("linear", np.array([0.1, 0.2])
pdf_grid = API.xplotting_grid(pdf="NNPDF40_nlo_as_01180", Q=2, xgrid=new_xgrid)
The API offers flexibility to mix declarative inputs such as
pdf=<name of pdf>
with python objects
xgrid=(<string>, <numpy.ndarray>)
, note that this is very dependent
on the provider in question and is not guaranteed to work all the time.