TGraphAsymmErrors can not convert argument in with pyROOT

I am trying to declare a TGraphAsymmErrors object in pyROOT, but it always errors out with claiming none of the 9 overloaded methods succeeded:

  TGraphAsymmErrors::TGraphAsymmErrors(int n, const float* x, const float* y, const float* exl = 0, const float* exh = 0, const float* eyl = 0, const float* eyh = 0) =>
    could not convert argument 2

I have tried two ways, using variables with and without explicit float typing, and hard coding numbers, and they fail equally. I believe it has to do with the requirement that they be const, but there is no const in python. Both of these fail (all the hard coded numbers are the results of printing the variables in the line following):

            dataGraph = TGraphAsymmErrors(4, 0.0, 235.0, 0.0, 0.0, 15.2375698701, 16.2626895151)
            dataGraph = TGraphAsymmErrors(len(x_val), float(x_val[0]), float(y_val[0]), float(x_errL[0]), float(x_errU[0]), float(y_errL[0]), float(y_errU[0]))

I tried with both ROOT 6.04 and ROOT 6.10 and got the same result. Does anyone know how to fix this? Thanks!

In:

TGraphAsymmErrors::TGraphAsymmErrors(int n, const float* x, const float* y, const float* exl = 0, const float* exh = 0, const float* eyl = 0, const float* eyh = 0)

x, y, exl etc … are arrays of dimension n

Hi – thanks for that. I tried removing the [0] and float conversion to allow it access to the whole array and it gave the same error:

            print x_val  # [0.0, 1.0, 2.0, 3.0]
            print y_val  # [235.0, 194.0, 278.0, 359.0]
            print x_errL  # [0.0, 0.0, 0.0, 0.0]
            print x_errU  # [0.0, 0.0, 0.0, 0.0]
            print y_errL  # [15.237569870143432, 13.842917704782423, 16.574623793312526, 18.83718622844708]
            print y_errU  # [16.262689515120286, 14.871310458808097, 17.597122197558406, 19.856096108577674]
            dataGraph = TGraphAsymmErrors(len(x_val), x_val, y_val, x_errL, x_errU, y_errL, y_errU)

  TGraphAsymmErrors::TGraphAsymmErrors(int n, const float* x, const float* y, const float* exl = 0, const float* exh = 0, const float* eyl = 0, const float* eyh = 0) =>
    could not convert argument 2
  TGraphAsymmErrors::TGraphAsymmErrors(int n, const double* x, const double* y, const double* exl = 0, const double* exh = 0, const double* eyl = 0, const double* eyh = 0) =>
    could not convert argument 2

This may have to do with the type of the variables. @adsherman can you post a (non)working example?

https://sft.its.cern.ch/jira/browse/ROOT-9040

I actually dont have a working example, as I am translating some old C++ ROOT code into pyROOT. This is my non-working example. The whole method is here

    @staticmethod
    def MakePoissonConfidenceLevelErrors(hist):
        """hist must be a TH1"""
        x_val = []
        y_val = []
        x_errU = []
        x_errL = []
        y_errU = []
        y_errL = []

        for b in range(1, hist.GetNbinsX() + 1):
            binEntries = hist.GetBinContent(b)
            if (binEntries > 0):
                binErrUp = HistPlotter.calcPoissonCLUpper(0.68, binEntries) - binEntries
                binErrLow = binEntries - HistPlotter.calcPoissonCLLower(0.68, binEntries)
                x_val.append(hist.GetXaxis().GetBinCenter(b))
                y_val.append(binEntries)
                y_errU.append(binErrUp)
                y_errL.append(binErrLow)
                x_errU.append(0.)  # hist.GetXaxis().GetBinWidth( b )/2.0  )
                x_errL.append(0.)  # hist.GetXaxis().GetBinWidth( b )/2.0  )

        if (len(x_val) > 0):
            print x_val  # [0.0, 1.0, 2.0, 3.0]
            print y_val  # [235.0, 194.0, 278.0, 359.0]
            print x_errL  # [0.0, 0.0, 0.0, 0.0]
            print x_errU  # [0.0, 0.0, 0.0, 0.0]
            print y_errL  # [15.237569870143432, 13.842917704782423, 16.574623793312526, 18.83718622844708]
            print y_errU  # [16.262689515120286, 14.871310458808097, 17.597122197558406, 19.856096108577674]
            dataGraph = TGraphAsymmErrors(len(x_val), x_val, y_val, x_errL, x_errU, y_errL, y_errU)

            return dataGraph
        else:
            return 0

It calls methods calcPoissonCLLower(Upper) which look like this:

    @staticmethod
    def calcPoissonCLLower(q, obs):
        """
         Calculate lower confidence limit
         e.g. to calculate the 68% lower limit for 2 observed events:
         calcPoissonCLLower(0.68, 2.)
         """
        LL = 0.0
        if (obs >= 0):
            a = (1. - q) / 2  # = 0.025 for 95% confidence interval
            LL = TMath.ChisquareQuantile(a, 2. * obs) / 2

        return LL

    @staticmethod
    def calcPoissonCLUpper(q, obs):
        """
         Calculate upper confidence limit
         e.g. to calculate the 68% upper limit for 2 observed events:
         calcPoissonCLUpper(0.68, 2.)
         """
        UL = 0.0
        if (obs >= 0):
            a = 1. - (1. - q) / 2  # = 0.025 for 95% confidence interval
            UL = TMath.ChisquareQuantile(a, 2. * (obs + 1.)) / 2

        return UL

I’ve not tried using TGraph with python lists. I’ve been using numpy arrays, this may be all that is supported. @wlav may have more to say on that matter.

You can wrap them as shown:

import numpy as np
#...
dataGraph = TGraphAsymmErrors(len(x_val), np.array(x_val), np.array(y_val), np.array(x_errL), np.array(x_errU), np.array(y_errL), np.array(y_errU))

Hi thanks! That works. Ill try that in the event that I run into something like this again.

The arguments need to be C-style arrays. I.e. a linear block of memory containing the float values. Python lists are not that: those are Python objects that wrap arrays of pointers to Python objects that wrap C doubles.

Anything that gives a continuous array, properly initialized, should do the trick, but a few work better than others (b/c of the overload selection): python array.array, numpy array, or ctypes LL C-array.

cppyy master does supports some automatic conversions of python sequences, but that was done to support initializer lists (the C++11 feature). With builtin C arrays, such as needed here, there is no saying what the ownership rules are (you have the same problem when using this from C++), so automatic conversion as a general rule is out of the question. It could be ROOT-specific pythonized, of course, using a composer, the same way as was done for buffer returns from TGraphAsymmErrors (see ROOT.py for that example).

You can always select specific overloads based on disp() (__dispatch__() in cppyy master) to get around the problem in the bug report mentioned. Of course, these overload issues are simply fixed in master, which supports the newer buffer interfaces.

Aside, master is also >30% faster. And Python3 (which is fully supported by master) is even better: there, some calls into C++ outperform python to python calls, for example (just talking call overhead here, so even better if useful work is done). And I haven’t even started optimizations: this was just removal of barnacles and other crud that had been growing onto PyROOT/cppyy over the years.

1 Like

Hi,

the key here is to go through a Python data structure which is contiguous in memory as the C arrays expected by the constructor of TGraphAsymmErrors. I choose Python arrays. Hopefully the example below is of help.

In the ROOT team, we are aware of these boundaries which, despite the power of PyROOT, exist between the two languages and are working on those very hard to deliver an even better programming model to our users.

import ROOT
import array

xList = [1,2,3,4]
yList = [1,4,9,16]

x = array.array('d', xList)
y = array.array('d', yList)

g = ROOT.TGraph(len(x),x,y)
g.SetMarkerStyle(ROOT.kFullTriangleUp)
g.SetMarkerColor(ROOT.kPink)

g.Draw("AP")

raw_input("Press enter to quit...")

Cheers,
D

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.