English Français

Documentation / User's manual in Python : PDF version

I.3. The Python Interface

I.3. The Python Interface

Accessing Uranie tools is also possible using the Python language.

The PyROOT tool allows to access to ROOT classes from a Python command line or script. Uranie can benefit from this tool as well. All the use-case macros provided in the previous version of this user manual are now available in Python in Chapter XIV.

I.3.1. Python version: greater than 3.8

From Uranie version 4.9, only Python greater than 3.8 can be used due to the use of ROOT version v6.32 (Python 2 is deprecated). Two words of caution about this:

  • The macros provided in Chapter XIV have indeed been tested with Python 3 (upper than 3.8).

  • Note, historically, from Uranie version 4.5 to v4.8, both Python 2 and 3 were used at the same time if it is installed again a ROOT version from v6.20.00 to v6.28.06. In order to get this compatibility, it means that the printing format has to be homogenised and this implies that from Uranie version 4.2, these macros might not be working anymore for Python version below or equal to 2.6.

Today's operating systems usually embed both versions of python and many of these still use the version 2 as a reference (see the block above with both python versions). Given the fact that one can have as many different python versions as possible, it should be possible to specify which one is of interest for the ongoing installation. The following lines should be used to specify the python version to be used, providing that the "dev" packages are indeed installed for this version:

 cmake ${PATH_TO_ROOT_SOURCES} -DPYTHON_EXECUTABLE=${PATH_TO_PYTHON_BIN} -DPYTHON_INCLUDE_DIR=${PATH_TO_PYTHON_INC}  -DPYTHON_LIBRARY=${PATH_TO_PYTHON_LIB} ...

These three specific python flags should be used both for ROOT and Uranie in a coherent way in order to install both platforms with a chosen Python's version. To get the recommended cmake line both for ROOT and Uranie see the README provided with the Uranie-sources.

I.3.2. Environment variables

In order to access ROOT and Uranie from Python, we need to ensure that some environment variables are properly set:

  • $PYTHONPATH must contain the $ROOTSYS/lib directory, where $ROOTSYS is ROOT's installation directory.

  • $LD_LIBRARY_PATH must contain the $ROOTSYS/lib directory.

I.3.3. Using PyROOT

When the environment variables for Uranie and PyROOT are properly set, we can access to the classes as follows:

# Load the ROOT module
import ROOT

# Create a new data server object
tds=ROOT.URANIE.DataServer.TDataServer("myTDS","DataServer for python example")

# Add an attribute to the data server
tds.addAttribute( ROOT.URANIE.DataServer.TNormalDistribution("x", 0.0, 1.0) )

# Create a sampler object
sampler = ROOT.URANIE.Sampler.TSampling(tds, "lhs", 1000)

# Generate data
sampler.generateSample()

# Display the histogram of attribute x
tds.draw("x")

This code should produce a graphic as the one displayed in Figure I.2.

Figure I.2. Histogram produced using PyROOT

Histogram produced using PyROOT

The instructions above can be executed either through the Python command line, or be written in a file (myscript.py in the example below) and run using the command:

python -i myscript.py

The -i option allows to stay in the Python environment at the end of the execution. This prevents the produced image to be automatically closed.

I.3.4. The PyURANIE interface

In the previous example, we can see that the access to Uranie's classes is somewhat tedious. In order to ease the process, a set of specific modules have been created. We call it the PyURANIE interface.

It is then possible to re-write the previous example using these specific modules:

# Load the URANIE module
from ROOT import URANIE

# Load the DataServer and Sampler modules
from URANIE import DataServer, Sampler

# Create a new data server object
tds = DataServer.TDataServer("myTDS","DataServer for the python example")

# Add an attribute to the data server
tds.addAttribute( DataServer.TNormalDistribution("x", 0.0, 1.0) )

# Create a sampler object
sampler = Sampler.TSampling(tds, "lhs", 1000)

# Generate data
sampler.generateSample()

# Display the histogram of attribute x
tds.draw("x")

The access to ROOT classes is provided through the ROOT module.

The PyURANIE interface also allows to use the command:

from URANIE.DataServer import *

This command loads all the classes of the DataServer module in Python and makes them directly accessible. It is similar to the C++ command using namespace URANIE::DataServer. However, in Python, using this command is not recommended. It can create name conflicts and use a large amount of memory.

Finally the equivalent of the rootlogon.C has been written for python, and is called rootlogon.py. It is composed of two parts, as for the one in C++, the second one being the exact equivalent. The first one, on the other hand, is a bit different and this difference arises from the way the language are dealing with loading modules. By doing

from rootlogon import DataServer

one can, for instance, directly creates a TDataServer by doing

toto=DataServer.TDataServer()

The example of rootlogon file for python is the following:

import ROOT

# Create shortcuts if uranie exists
urasys = ROOT.TString(ROOT.gSystem.Getenv("URANIESYS"))
if not urasys.EqualTo(""):
    from ROOT.URANIE import DataServer as DataServer
    from ROOT.URANIE import Sampler as Sampler
    from ROOT.URANIE import Launcher as Launcher
    from ROOT.URANIE import Relauncher as Relauncher
    from ROOT.URANIE import Reoptimizer as Reoptimizer
    from ROOT.URANIE import Sensitivity as Sensitivity
    from ROOT.URANIE import Optimizer as Optimizer
    from ROOT.URANIE import Modeler as Modeler
    from ROOT.URANIE import Calibration as Calibration
    from ROOT.URANIE import UncertModeler as UncertModeler
    from ROOT.URANIE import Reliability as Reliability
    from ROOT.URANIE import XMLProblem as XMLProblem
    from ROOT.URANIE import MpiRelauncher as MpiRelauncher
    pass

# General graphical style
white = 0

# PlotStyle
ROOT.gStyle.SetPalette(1)
ROOT.gStyle.SetOptDate(21)

# Legend
ROOT.gStyle.SetLegendBorderSize(0)
ROOT.gStyle.SetFillStyle(0)

# Pads
ROOT.gStyle.SetPadColor(white)
ROOT.gStyle.SetTitleFillColor(white)
ROOT.gStyle.SetStatColor(white)

#  ====================  Hint ====================
#
#    Might be practical to store this in a convenient place (for instance
#    the ".python" folder in your home directory) or any other place where
#    your $PYTHONPATH is pointing.
#
#    example : export PYTHONPATH=$PYTHONPATH:${HOME}/.mypython/
#
#    It should then be called as "from rootlogon import " + the list of module
#    This would replace the shortcuts created and import done in the rest of
#    the scripts
#
#    Many style issue can be set once and for all here.
#    toto=DataServer.TDataServer()
#

I.3.5. FAQ: a python handbook to interact with Uranie

This section introduces some frequently asked methods to help handling data and objects (as many objects proposed are either ROOT or C++-based). Some of these methods are used throughout the examples in the macros in Chapter XIV, but the aim here is to gather everything that might be of use for a python user that would like to put his hand on Uranie. Most of this methods rely on ROOT version being greater than 6.20 but the modification are small if one uses older versions (as long as it is greater than6).

I.3.5.1. TDataServer <-> array conversion

This section describes how to convert a TDataServer into a numpy.array and vice-versa. A breakdown of the macro show below can be found in Section XIV.2.1

"""
Example of TDataServer into numpy array converter
"""
import numpy as np
from rootlogon import DataServer

# import data into a dataserver
tds = DataServer.TDataServer("tds", "pouet")
tds.fileDataRead("myData.dat")
print("Dumping tds")
tds.scan()

# Define the list of variable to be read
VarList = "x:y"

# Create an array by defining the shape and the type (this array is a matrix)
TdsData = np.empty(shape=(tds.getNPatterns(), len(VarList.split(":"))),
                   dtype=np.float64)
# Dump the data into the created array (the last field empty unless a select is done on the sample)
tds.getTuple().extractData(TdsData, TdsData.size, VarList, tds.getCut().GetTitle())

# Check the content
print("Dumping the array")
print(TdsData)

# Create a new Dataserver and feed it
tds2 = DataServer.TDataServer("brandnew", "pouet")
# Add the attribute in the dataserver and create the tuple
for name in VarList.split(":"):
    index = VarList.split(":").index(name)
    VarData = np.ascontiguousarray(TdsData.transpose()[index])
    tds2.addAttributeUsingData(name, VarData, VarData.size)

print("Dumping new tds")
tds2.Scan("*")

The macro shown above, is an example of how to convert the content stored in a TDataServer and its result is discussed in Chapter XIV.

I.3.5.2. TMatrixD <-> array conversion

This section describes how to convert a TMatrixS into a numpy.array and vice-versa. A breakdown of the macro show below can be found in Section XIV.2.2

"""
Example of TMatrix into numpy array conversion
"""
import numpy as np
import ROOT

# define the number of rows and columns
nrow = 3
ncol = 5

# Initialise and fill a TMatrixD
InMat = ROOT.TMatrixD(nrow, ncol)
for i in range(nrow):
    for j in range(ncol):
        InMat[i][j] = ROOT.gRandom.Gaus(0, 1)

print("Original TMatrixD")
InMat.Print()

# Create the ndarray with the good shape
mat_version = np.frombuffer(InMat.GetMatrixArray(), dtype=np.float64,
                            count=nrow*ncol).reshape(nrow, ncol)
print("Numpy array transformation")
print(mat_version)

# Back to another TMatrixD
OutMat = ROOT.TMatrixD(nrow, ncol)
OutMat.SetMatrixArray(mat_version)
print("\nRecreated TMatrixD")
OutMat.Print()

The macro shown above, is an example of how to convert the content stored in a TMatrixD but also how to go from a numpy.array back to TMatrixD if one needs to provide this to an Uranie's method. Its result is discussed in Section XIV.2.2.

I.3.5.3. Use a C++ function interactively

This section describes how to use a C++ function within the python framework. The idea is that every of our surrogate model discussed later-on on can be exported in C++ and so they can be used either throughout the Uranie classes (for instance to evaluate a set of new points) or, for single evaluation, in either C++ or python. This macro will focus on the latter part. This macro is also detailled Section XIV.2.3.

"""
Example of C++ function in python
"""
import numpy as np
import ROOT

# Loading a function
ROOT.gROOT.LoadMacro("UserFunctions.C")

# Preparing inputs and outputs
inp = np.array([1.2, 0.8])  # input values
out = np.zeros(4)  # output values initialise with 4 zeros

# Call the method, through the ROOT interface
ROOT.operation(inp, out)

# Print the result
print("Results are")
print(out)

The macro shown above, is an example of how to load a function and use it in a single-point estimation. Its result is discussed in Section XIV.2.3.

I.3.5.4. Pass argument "by-reference"

This sections shows how to use function that would have been defined using the "by-reference" prototype, which is specific to C++ . This macro is also detailled Section XIV.2.4.

"""
Example of C++style reference usage
"""
from ctypes import c_double  # for ROOT version greater or equal to 6.20
from rootlogon import DataServer

# create a dataserver and read data
tds = DataServer.TDataServer("pouet", "foo")
tds.fileDataRead("myData.dat")

# compute quantile (this method get the results 'by reference')
proba = 0.9  # ROOT.Double(0.9) for ROOT version lower than 6.20
quant = c_double(0.0)  # ROOT.Double(0.0) for ROOT version lower than 6.20

# Compute the quantile and dump the value returned by reference
tds.computeQuantile("x", proba, quant)
print("Result is ")
print(quant.value)

The macro shown above, is an example of how to load a function and use it in a single-point estimation. Its result is discussed in Section XIV.2.4.

I.3.6. References

The PyROOT environment has a few specificities which it is preferable to be aware of (the rest being discussed already in Section I.2.6). The following websites help to learn about them:

Finally, reference [PyUranie] introduces (in French) some of these problems and shows examples on how to use Uranie with PyROOT.

/language/en