How to use ROOT in a Jupyter notebook

ROOT is integrated with the Jupyter notebook technology. There are two alternatives for using ROOT in a notebook:

  1. Python flavour: the default language of the notebook is Python and ROOT is accessed via the PyROOT interface. The user can mark cells to be C++ with the %%cpp magic.
  2. C++ flavour: the notebook is entirely written in C++, thus emulating the ROOT C++ prompt.

Python flavour

In order to use ROOT in a Python notebook, we first need to import the ROOT module. During the import, all notebook related functionalities are activated.

In [1]:
import ROOT
Welcome to ROOTaaS 6.05/01

Now we are ready to use PyROOT. For example, we create a histogram.

In [2]:
h = ROOT.TH1F("gauss","Example histogram",100,-4,4)
h.FillRandom("gaus")

Next we create a canvas, the entity which holds graphics primitives in ROOT.

In [3]:
c = ROOT.TCanvas("myCanvasName","The Canvas Title",800,600)
h.Draw()

For the histogram to be displayed in the notebook, we need to draw the canvas.

In [4]:
c.Draw()

It is not active by default yet, but Javascript visualisation can be activated for testing purposes. The plot below will be interactive: click on it and discover the JSROOT capabilities!

In [5]:
ROOT.enableJSVis()
c.Draw()
ROOT.disableJSVis()

Interleave Python with C++: the %%cpp magic

Thanks to ROOT, it is possibile to write cells in C++ within a Python notebook. This can be done using the %%cpp magic. Magics are a feature of Jupyter notebooks and when importing the ROOT module, the %%cpp magic was registered.

In [6]:
%%cpp
cout << "This is a C++ cell" << endl;
This is a C++ cell

Not bad. On the other hand, ROOT offers much more than this. Thanks to its type system, entities such as functions, classes and variables, created in a C++ cell, can be accessed from within Python.

In [7]:
%%cpp
class A{
    public:
    A(){cout << "Constructor of A!" << endl;}
};
In [8]:
a = ROOT.A()
Constructor of A!

The Python and C++ worlds are so entangled that we can find back in C++ the entities created in Python. To illustrate this, from within a C++ cell, we are going to fit a function in the gauss histogram displayed above and then re-draw the canvas.

In [9]:
%%cpp
gauss->Fit("gaus", "S");
myCanvasName->Draw();
 FCN=65.0502 FROM MIGRAD    STATUS=CONVERGED      54 CALLS          55 TOTAL
                     EDM=9.21369e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                                   STEP         FIRST   
  NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE 
   1  Constant     1.57425e+02   2.76819e+00   8.89596e-03  -5.39870e-04
   2  Mean         1.38321e-02   1.43799e-02   5.69927e-05   8.22299e-04
   3  Sigma        1.00178e+00   1.04857e-02   1.11224e-05  -4.31183e-01

Complete interoperability is possible. Let's move now to the options offered by the %%cpp magic.

The options of the %%cpp magic

The %%cpp magic accepts two options: -d and -a. Their documentation can be seen by typing:

In [10]:
%%cpp?

A window will appear at the bottom of the page, showing the documentation.

ACliC

The first option (-a) allows to compile the cell code with ACLiC. This is not so relevant for performance since the ROOT interpreter just in time compiles the C++ code. Nevertheless, ACLiC is most useful when the automatic creation of dictionaries is required, for example in presence of I/O operations.

In [11]:
%%cpp -a
class CompileMe {
public:
    CompileMe() {}
    void run() {}
};
Info in <TUnixSystem::ACLiC>: creating shared library /home/rw15u099/64a4a5eb_C.so

Let's verify that the dictionary is there:

In [12]:
ROOT.TClass.GetClass("CompileMe").HasDictionary()
Out[12]:
True

Note that the previously created class A has no dictionary.

In [13]:
ROOT.TClass.GetClass("A").HasDictionary()
Out[13]:
False

Declaration of functions

The second option (-d) needs to be used when declaring functions. The interpreter cannot yet detect function declarations by itself: we need to be explicit. This is a limitation which will be lifted in the near future.

In [14]:
%%cpp -d
void f() {
    cout << "This is function f" << endl;
}

As usual, function f can also be accessed from Python.

In [15]:
print "This is again Python"
ROOT.f()
This is again Python
This is function f

ROOT allows to run pure C++ notebooks.

This can be achieved in two ways:

  1. When choosing to instantiate that kernel, the user is presented with a fully-C++ notebook
  2. Switching to C++ programmatically

The manual switch to C++ mentioned above can be done by typing:

In [16]:
ROOT.toCpp()
Notebook is in Cpp mode

Now our notebook behaves as the ROOT prompt, with no need of any magic.

In [17]:
cout << "From this point on..." << endl;
From this point on...
In [18]:
cout << "... it's only C++ ..." << endl;
... it's only C++ ...
In [19]:
cout << "... With the usual goodies!" << endl;
std::unordered_map<int, string> m = {{1, "one"}, {2, "two"}, {3, "three"}}
... With the usual goodies!
(std::unordered_map<int, std::string> &) { 3 => "three", 1 => "one", 2 => "two" }

Remember our C++ function f? ROOT does!

In [20]:
f
(void (*)()) Function @0x7fe5af0b1030
  at :1:
void f() {
    cout << "This is function f" << endl;
}

"Magics"

We put the title in quotes because as for to the Python flavour, the C++ flavour also provides the ACLiC and Declare options. In order to use them, the first line in the cell must contain the word .cpp followed by the option.

In [21]:
.cpp -a
class CompileMe2 {
public:
    CompileMe2() {}
    void run() {}
};
Info in <TUnixSystem::ACLiC>: creating shared library /home/rw15u099/4040e501_C.so
In [22]:
.cpp -d
void f2() {
    cout << "This is function f" << endl;
}
In [23]:
auto cm2 = CompileMe2()
(CompileMe2 &) @0x7fe5db453080
In [24]:
f2()
This is function f