Where are ROOT's Python submodules documented?

I am trying to chase down a really nasty problem where SciPy’s normal 64-bit LAPACK is getting “replaced” somehow with a 32-bit LAPACK which then refuses to do the data fitting I’ve asked for via scipy.optimize.curve_fit(). Not ROOT’s problem, except…

I would really like to know where I can find documentation for all of the things I can import to my Python session using from ROOT import [something]. Even better, I would love to see some documentation which maps those ROOT submodules to the regular (C++) ROOT libraries (libCore, libROOTDataFrame, libMatrix, etc.).

I am trying to isolate exactly where LAPACK is getting stomped on, by importing the minimal thing which triggers it. import ROOT does not. So where is the documentation I’m searching for?

Hi @Michael_Kelsey,

what’s your ROOT version and operating system? How did you install ROOT? How did you install scipy? That might help to understand the issue with LAPACK.

There is almost nothing in the ROOT module, also no submodules. It is just a facade that dynamically looks up C++ functions, classes and namespaces and generates Python wrappers for these on the fly. Therefore, it’s not sustainable to maintain a documentation for everything that can be imported via the ROOT module. So you need to look at the C++ ROOT documentation and translate it in your mind (and there are also indications for the occasional Pythonization in the docs).

About your LAPACK issue, which is probably the more important one to solve here. Can you maybe post the crash that you get?

I’m aware already of similar problems with BLAS. For example on Alma 9, the default system BLAS library clashes with the BLAS that you get with the NumPy package when installing it from pip. And ROOT uses the system libraries (TMVA uses BLAS in particular). So on alma9, we have to compile ROOT against a different BLAS version that is not the default to avoid crashes.

Similar problems come from TensorFlow or PyTorch. They also have builtin linear algebra libraries that clash with the system libraries. Maybe you use these libraries somewhere in your code, and ROOT is not the problem?

Just to start with, I’m not able to reproduce the LAPACK issue with just import ROOT. But I have evidence that there are other things which could be from ROOT import X, or maybe import ROOT.Y, that load additional shared libraries.

We’re using ROOT 6.28-something right now. This error occurs (only, so far!) in an Apptainer build (Ubuntu 22) which includes Python 3.10.12, ROOT 6.28, Numpy 2.0.2, SciPy 1.14.1, and of course lots of low level stuff. Numpy, SciPy, etc. are all installed with pip.

The ROOT libraries are installed in /usr/local/lib, and I see /usr/local/lib/ROOT/, but I don’t see submodules etc., so this is where my understanding stops.

I understand. In this way, it’s analogous to G4python. All the more reason to want something written down somewhere that says, “If you want the RooFit stuff, use this import command.” “If you want RDataFrame, use this.” And so on.

Oh, I think I see your point. The from ROOT import X action can use any individual ROOT class (like TTree, TChain) as X. You’re quite right that re-documenting all of that is crazy. I’m more interested in the mapping between submodules and ROOT library names. For example, to get RDataFrame, one uses import ROOT.RDF I think, but the library is libROOTDataFrame.so.

Where would I find a list, table, whatever of the possible import ROOT.X?

Traceback (most recent call last):
  File "/scratch/user/kelsey/CATs-LAPACK_problem/./fitter_test.py", line 68, in <module>
    params, _ = curve_fit(TESshape, bins[fitStart:fitEnd], trace[fitStart:fitEnd],
  File "/usr/local/lib/python3.10/dist-packages/scipy/optimize/_minpack_py.py", line 1033, in curve_fit
    _, s, VT = svd(res.jac, full_matrices=False)
  File "/usr/local/lib/python3.10/dist-packages/scipy/linalg/_decomp_svd.py", line 156, in svd
    lwork = _compute_lwork(gesXd_lwork, a1.shape[0], a1.shape[1],
  File "/usr/local/lib/python3.10/dist-packages/scipy/linalg/lapack.py", line 1011, in _compute_lwork
    return _check_work_float(ret[0].real, dtype, int_dtype)
  File "/usr/local/lib/python3.10/dist-packages/scipy/linalg/lapack.py", line 1031, in _check_work_float
    raise ValueError("Too large work array required -- computation "
ValueError: Too large work array required -- computation cannot be performed with standard 32-bit LAPACK.

You can see that the actual error is deep inside SciPy. I trigger it with curve_fit(), but searching StackOverflow etc. shows that the ValueError can be raised by any number of fitting or other things.

Reproducibility is “easy”, but is currently SuperCDMS-specific. We have a Python-only superclass of RDataFrame, which is part of an analysis tools package called “CATs” (CDMS Analysis Tools). The latter doesn’t use TensorFlow, PyTorch, etc. If I import cats (anything from it, not specifically CDataFrame), before my two lines

import numpy
from scipy.optimize import curve_fit

Then I get the 32-bit LAPACK complaint above. If I don’t import our CATs module at all, my fit works just as expected. If I import CATs after scipy, my fit also works as expected.

I have used a find/grep pipe to collect every import and from...import line from the CATs package. There are low-level Python things, there’s Numpy, and there’s ROOT related things:

from ROOT              import gInterpreter, TChain, TTree
from ROOT            import RDataFrame, TChain, TTree
from ROOT import Numba
from ROOT.RDF          import AsRNode
import ROOT

What I’m struggling with now is that if I try any one of those individual ROOT-related imports, I cannot trigger the 32-bit LAPACK complaint.

So now I’m trying to identify the relevant .so libraries, by using lsof -p to collect and diff the lists of what’s been loaded. This is why I would really like a mapping between what I can import from ROOT in Python, and which shared library that triggers. I can get that mapping myself, provided I have documentation for “what I can import from ROOT”. Hence my posted question.

Following up. I played the lsof -p game with the above imports. Using import ROOT as the baseline for libraries, I found that each of these individual lines added the same libraries to the process:

from ROOT.RDF import AsRNode

or

from ROOT import RDataFrame

The additional ROOT libraries pulled in by either of those lines were:

> /usr/local/lib/libGpad.so.6.28.10
> /usr/local/lib/libGraf3d.so.6.28.10
> /usr/local/lib/libGraf.so.6.28.10
> /usr/local/lib/libHist.so.6.28.10
> /usr/local/lib/libMathCore.so.6.28.10
> /usr/local/lib/libMatrix.so.6.28.10
> /usr/local/lib/libROOTDataFrame.so.6.28.10
> /usr/local/lib/libROOTNTuple.so.6.28.10
> /usr/local/lib/libROOTVecOps.so.6.28.10
> /usr/local/lib/libTreePlayer.so.6.28.10

This is what my original post was asking for. What kind of import or from ROOT import action would I use to pull in only libTreePlayer.so? What would I use to pull in only libROOTNTuple.so? Etc.

That said, I’ve tried using ldd on each of those libraries above. They all seem to be pulled in by ROOTDataFrame, which is fine, and none of them seem to directly pull in BLAS or LAPACK.

I don’t know how I would test to see if one of them had a built-in LAPACK that could be causing undesired function-call resolution in my original problem.