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.