Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RF][PyROOT] Pythonize RooFit::DataError to avoid Python 3 syntax errors #9721

Merged
merged 3 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from ._roodecays import RooDecay, RooBDecay, RooBCPGenDecay, RooBCPEffDecay, RooBMixDecay
from ._roogenfitstudy import RooGenFitStudy
from ._rooglobalfunc import (
DataError,
FitOptions,
Format,
Frame,
Expand Down Expand Up @@ -91,6 +92,7 @@

# list of python functions that are used to pythonize RooGlobalFunc function in RooFit
python_roofit_functions = [
DataError,
FitOptions,
Format,
Frame,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@


@cpp_signature(
"FitOptions(const RooCmdArg& arg1, const RooCmdArg& arg2=RooCmdArg::none(),"
"RooFit::FitOptions(const RooCmdArg& arg1, const RooCmdArg& arg2=RooCmdArg::none(),"
"const RooCmdArg& arg3=RooCmdArg::none(),const RooCmdArg& arg4=RooCmdArg::none(),"
"const RooCmdArg& arg5=RooCmdArg::none(),const RooCmdArg& arg6=RooCmdArg::none()) ;"
)
Expand All @@ -71,7 +71,7 @@ def FitOptions(*args, **kwargs):


@cpp_signature(
"Format(const char* what, const RooCmdArg& arg1=RooCmdArg::none(), const RooCmdArg& arg2=RooCmdArg::none(),"
"RooFit::Format(const char* what, const RooCmdArg& arg1=RooCmdArg::none(), const RooCmdArg& arg2=RooCmdArg::none(),"
"const RooCmdArg& arg3=RooCmdArg::none(),const RooCmdArg& arg4=RooCmdArg::none(),"
"const RooCmdArg& arg5=RooCmdArg::none(),const RooCmdArg& arg6=RooCmdArg::none(),"
"const RooCmdArg& arg7=RooCmdArg::none(),const RooCmdArg& arg8=RooCmdArg::none()) ;"
Expand All @@ -91,7 +91,7 @@ def Format(*args, **kwargs):


@cpp_signature(
"Frame(const RooCmdArg& arg1, const RooCmdArg& arg2=RooCmdArg::none(),"
"RooFit::Frame(const RooCmdArg& arg1, const RooCmdArg& arg2=RooCmdArg::none(),"
"const RooCmdArg& arg3=RooCmdArg::none(), const RooCmdArg& arg4=RooCmdArg::none(),"
"const RooCmdArg& arg5=RooCmdArg::none(), const RooCmdArg& arg6=RooCmdArg::none()) ;"
)
Expand All @@ -107,7 +107,7 @@ def Frame(*args, **kwargs):


@cpp_signature(
"MultiArg(const RooCmdArg& arg1, const RooCmdArg& arg2,"
"RooFit::MultiArg(const RooCmdArg& arg1, const RooCmdArg& arg2,"
"const RooCmdArg& arg3=RooCmdArg::none(),const RooCmdArg& arg4=RooCmdArg::none(),"
"const RooCmdArg& arg5=RooCmdArg::none(),const RooCmdArg& arg6=RooCmdArg::none(),"
"const RooCmdArg& arg7=RooCmdArg::none(),const RooCmdArg& arg8=RooCmdArg::none()) ;"
Expand All @@ -123,7 +123,7 @@ def MultiArg(*args, **kwargs):
return RooFit._MultiArg(*args, **kwargs)


@cpp_signature("YVar(const RooAbsRealLValue& var, const RooCmdArg& arg=RooCmdArg::none()) ;")
@cpp_signature("RooFit::YVar(const RooAbsRealLValue& var, const RooCmdArg& arg=RooCmdArg::none()) ;")
def YVar(*args, **kwargs):
r"""The YVar() function is pythonized with the command argument pythonization.
The keywords must correspond to the CmdArg of the function.
Expand All @@ -138,7 +138,7 @@ def YVar(*args, **kwargs):
return RooFit._YVar(*args, **kwargs)


@cpp_signature("ZVar(const RooAbsRealLValue& var, const RooCmdArg& arg=RooCmdArg::none()) ;")
@cpp_signature("RooFit::ZVar(const RooAbsRealLValue& var, const RooCmdArg& arg=RooCmdArg::none()) ;")
def ZVar(*args, **kwargs):
r"""The ZVar() function is pythonized with the command argument pythonization.
The keywords must correspond to the CmdArg of the function.
Expand All @@ -153,7 +153,7 @@ def ZVar(*args, **kwargs):
return RooFit._ZVar(*args, **kwargs)


@cpp_signature("Slice(std::map<RooCategory*, std::string> const&) ;")
@cpp_signature("RooFit::Slice(std::map<RooCategory*, std::string> const&) ;")
def Slice(*args, **kwargs):
r"""The Slice function is pythonized for converting python dict to std::map.
The keywords must correspond to the CmdArg of the function.
Expand All @@ -172,9 +172,9 @@ def Slice(*args, **kwargs):

@cpp_signature(
[
"Import(const std::map<std::string,RooDataSet*>& ) ;",
"Import(const std::map<std::string,TH1*>&) ;",
"Import(const std::map<std::string,RooDataHist*>&) ;",
"RooFit::Import(const std::map<std::string,RooDataSet*>& ) ;",
"RooFit::Import(const std::map<std::string,TH1*>&) ;",
"RooFit::Import(const std::map<std::string,RooDataHist*>&) ;",
]
)
def Import(*args, **kwargs):
Expand All @@ -193,7 +193,7 @@ def Import(*args, **kwargs):
return RooFit._Import(*args, **kwargs)


@cpp_signature("Link(const std::map<std::string,RooAbsData*>&) ;")
@cpp_signature("RooFit::Link(const std::map<std::string,RooAbsData*>&) ;")
def Link(*args, **kwargs):
r"""The Link function is pythonized for converting python dict to std::map.
The keywords must correspond to the CmdArg of the function.
Expand All @@ -210,49 +210,112 @@ def Link(*args, **kwargs):
return RooFit._Link(*args, **kwargs)


@cpp_signature("LineColor(Color_t color) ;")
@cpp_signature("RooFit::LineColor(Color_t color) ;")
def LineColor(color):
# Redefinition of `LineColor` for matplotlib conventions and string arguments.
r"""The `color` argument doesn't necessarily have to be a ROOT color enum value, like `ROOT.kRed`.
Here is what you can also do in PyROOT:

1. Pass a string with the enum value name instead, e.g.:
~~~ {.py}
pdf.plotOn(frame, LineColor="kRed")
~~~
2. Pass a string with the corresponding single-character color code following the matplotlib convention:
~~~ {.py}
pdf.plotOn(frame, LineColor="r")
~~~
3. Pass a string with the enum value name instead followed by some manipulation of the enum value:
~~~ {.py}
pdf.plotOn(frame, LineColor="kRed+1")
~~~
"""
from cppyy.gbl import RooFit

return RooFit._LineColor(_string_to_root_attribute(color, _color_map))


@cpp_signature("FillColor(Color_t color) ;")
@cpp_signature("RooFit::FillColor(Color_t color) ;")
def FillColor(color):
# Redefinition of `FillColor` for matplotlib conventions and string arguments.
from cppyy.gbl import RooFit

return RooFit._FillColor(_string_to_root_attribute(color, _color_map))

# Copy the docstring from LineColor.
FillColor.__doc__ = LineColor.__doc__


@cpp_signature("MarkerColor(Color_t color) ;")
@cpp_signature("RooFit::MarkerColor(Color_t color) ;")
def MarkerColor(color):
# Redefinition of `MarkerColor` for matplotlib conventions and string arguments.
from cppyy.gbl import RooFit

return RooFit._MarkerColor(_string_to_root_attribute(color, _color_map))

# Copy the docstring from LineColor.
MarkerColor.__doc__ = LineColor.__doc__


@cpp_signature("LineStyle(Style_t style) ;")
@cpp_signature("RooFit::LineStyle(Style_t style) ;")
def LineStyle(style):
# Redefinition of `LineStyle` for matplotlib conventions and string arguments.
from cppyy.gbl import RooFit

return RooFit._LineStyle(_string_to_root_attribute(style, _style_map))


@cpp_signature("FillStyle(Style_t style) ;")
@cpp_signature("RooFit::FillStyle(Style_t style) ;")
def FillStyle(style):
# Redefinition of `FillStyle` for matplotlib conventions and string arguments.
from cppyy.gbl import RooFit

return RooFit._FillStyle(_string_to_root_attribute(style, {}))


@cpp_signature("MarkerStyle(Style_t style) ;")
@cpp_signature("RooFit::MarkerStyle(Style_t style) ;")
def MarkerStyle(style):
# Redefinition of `MarkerStyle` for matplotlib conventions and string arguments.
from cppyy.gbl import RooFit

return RooFit._MarkerStyle(_string_to_root_attribute(style, {}))


@cpp_signature("RooFit::DataError(Int_t) ;")
def DataError(etype):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! It would be good to have this new addition in the Doxygen docs of this file, right? Besides modifying the tutorials.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! I added the docs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is a DataError="SumW2"-like example shown in the docs, sorry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a few lines below the line you made this comment for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw it now, thanks!

r"""Instead of passing an enum value to this function, you can pass a
string with the name of that enum value, for example:

~~~ {.py}
data.plotOn(frame, DataError="SumW2")
# instead of DataError=ROOT.RooAbsData.SumW2
~~~

If you want to use the `"None"` enum value to disable error plotting, you
can also pass `None` directly instead of passing a string:

~~~ {.py}
data.plotOn(frame, DataError=None)
# instead of DataError="None"
~~~
"""
# Redefinition of `DataError` to also accept `str` or `NoneType` to get the
# corresponding enum values from RooAbsData.DataError.
from cppyy.gbl import RooFit

# One of the possible enum values is "None", and we want the user to be
# able to pass None also as a NoneType for convenience.
if etype is None:
etype = "None"

if isinstance(etype, str):
try:
import ROOT
etype = getattr(ROOT.RooAbsData.ErrorType, etype)
except AttributeError as error:
raise ValueError(
'Unsupported error type type passed to DataError().'
+ ' Supported decay types are : "Poisson", "SumW2", "Auto", "Expected", and None.'
)
except Exception as exception:
raise exception

return RooFit._DataError(etype)
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ def _string_to_root_attribute(value, lookup_map):
return getattr(ROOT, lookup_map[value])
else:
try:
return getattr(ROOT, value)
# Here getattr(ROOT, value) would have less overhead, but it's
# also less convenient. With `eval`, the string that is passed
# by the user can also contain postfix operations on the enum
# value, which is often used for columns, e.g. `kGreen+1`.
return eval("ROOT." + value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a test of this new feature e.g. pdf.plotOn(frame, LineColor="kOrange+1") ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's tested in the tutorials

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but ideally all pythonizations should have their own unit tests here, in _pythonization/test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! I have added a new unit test file with this (see the first commit in the PR, which got amended)

except:
raise ValueError(
"Unsupported value passed. The value either has to be the name of an attribute of the ROOT module, or match with one of the following values that get translated to ROOT attributes: {}".format(
Expand Down
1 change: 1 addition & 0 deletions bindings/pyroot/pythonizations/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ if(roofit)
ROOT_ADD_PYUNITTEST(pyroot_roofit_rooabsreal_ploton roofit/rooabsreal_ploton.py)

ROOT_ADD_PYUNITTEST(pyroot_roofit_roolinkedlist roofit/roolinkedlist.py)
ROOT_ADD_PYUNITTEST(pyroot_roofit_rooglobalfunc roofit/rooglobalfunc.py)

# NumPy compatibility
if(NOT MSVC OR win_broken_tests)
Expand Down
33 changes: 33 additions & 0 deletions bindings/pyroot/pythonizations/test/roofit/rooglobalfunc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import unittest

import ROOT


class TestRooGlobalFunc(unittest.TestCase):
"""
Test for RooGlobalFunc pythonizations.
"""

def test_color_codes(self):
"""Test that the color code pythonizations in the functions like
RooFit.LineColor are working as they should.
"""

def code(color):
"""Get the color code that will be obtained by a given argument
passed to RooFit.LineColor.
"""
return ROOT.RooFit.LineColor(color).getInt(0)

# Check that general string to enum pythonization works
self.assertEqual(code(ROOT.kRed), code("kRed"))

# Check that matplotlib-style color strings work
self.assertEqual(code(ROOT.kRed), code("r"))

# Check that postfix operations applied to ROOT color codes work
self.assertEqual(code(ROOT.kRed+1), code("kRed+1"))


if __name__ == "__main__":
unittest.main()
7 changes: 5 additions & 2 deletions documentation/doxygen/print_roofit_pyz_doctrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def write_pyroot_block_for_class(klass):
print("")


def write_pyroot_block_for_member_func(func):
def write_pyroot_block_for_function(func):

if func.__doc__ is None or not hasattr(func, "_cpp_signature"):
return
Expand Down Expand Up @@ -180,7 +180,10 @@ def print_pyroot_blocks_for_cpp_docs():

for func_name in func_names:
func = getattr(python_klass, func_name)
write_pyroot_block_for_member_func(func)
write_pyroot_block_for_function(func)

for python_function in _roofit.python_roofit_functions:
write_pyroot_block_for_function(python_function)


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion roofit/roofitcore/inc/RooGlobalFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ RooCmdArg Cut(const char* cutSpec) ;
RooCmdArg Cut(const RooFormulaVar& cutVar) ;
RooCmdArg Binning(const RooAbsBinning& binning) ;
RooCmdArg Binning(const char* binningName) ;
RooCmdArg Binning(Int_t nBins, Double_t xlo=0., Double_t xhi=0.) ;
RooCmdArg Binning(int nBins, double xlo=0., double xhi=0.) ;
RooCmdArg MarkerStyle(Style_t style) ;
RooCmdArg MarkerSize(Size_t size) ;
RooCmdArg MarkerColor(Color_t color) ;
Expand Down
Loading