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] Translate RooStats tutorial rs101_limitexample.C to Python #10853

Merged
merged 2 commits into from
Jun 30, 2022

Conversation

busorgin
Copy link
Contributor

@busorgin busorgin commented Jun 29, 2022

This Pull request:

Changes or fixes:

Add rs101_limitexample.py - Converted from rs101_limitexample.C

Checklist:

  • tested changes locally
  • updated the docs (if necessary)

This PR fixes # Part of 8758

@busorgin busorgin requested a review from couet as a code owner June 29, 2022 14:02
@phsft-bot
Copy link
Collaborator

Can one of the admins verify this patch?

## \macro_output
## \macro_code
##
## \author Kyle Cranmer
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
## \author Kyle Cranmer
## \date June 2022
## \authors Artem Busorgin, Kyle Cranmer (C++ version)

Please add also the date, and you can also add your name here to get some credits :)

t.Stop()
t.Print()

dataCanvas.SaveAs("rs101_limitexample.png")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
dataCanvas.SaveAs("rs101_limitexample.png")
dataCanvas.SaveAs("rs101_limitexample.png")
# TODO: The MCMCCalculator has to be destructed first. Otherwise, we can get
# segmentation faults depending on the destruction order, which is random in
# Python. Probably the issue is that some object has a non-owning pointer to
# another object, which it uses in its destructor. This should be fixed either
# in the design of RooStats in C++, or with phythonizations.
del mc

Can you please add this at the bottom please? By deleting mc first, we are avoiding some destruction order fiasco that occasionally causes crashes in this tutorial.

# also plot the points in the markov chain
chainData = mcInt.GetChainAsDataSet()

assert chainData
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert chainData

No asserts in tutorials please (I know they were in the C++ version too, but asserts are only to check if things went well in debug builds or unit tests. For user code like these tutorials, it should never be necessary to use them)

assert chainData
print("plotting the chain data - nentries = ", chainData.numEntries())
chain = ROOT.RooStats.GetAsTTree("chainTreeData", "chainTreeData", chainData)
assert chain
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert chain

No asserts in tutorials


# the points used in the profile construction
parScanData = fc.GetPointsToScan()
assert parScanData
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert parScanData

No asserts in tutorials

y = evt.getRealValue("ratioSigEff")
z = evt.getRealValue("s")
gr.SetPoint(ievt, x, y, z)
# print("{} {} {} {}".format(ievt, x, y, z))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# print("{} {} {} {}".format(ievt, x, y, z))

It's not good to have commented out code in tutorials.

data = ROOT.RooDataSet("exampleData", "exampleData", ROOT.RooArgSet(obs))
data.add(obs)

all = ROOT.RooArgSet(s, ratioBkgEff, ratioSigEff)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
all = ROOT.RooArgSet(s, ratioBkgEff, ratioSigEff)

all is never used. You should delete this line

Comment on lines 36 to 37
modelWithConstraints = wspace.pdf("modelWithConstraints") # get the model
obs = wspace.var("obs") # get the observable
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
modelWithConstraints = wspace.pdf("modelWithConstraints") # get the model
obs = wspace.var("obs") # get the observable
modelWithConstraints = wspace["modelWithConstraints"] # get the model
obs = wspace["obs"] # get the observable

In PyRoot, you can universally get anything from a RooWorkspace with the []-operator. You should use this everytime you see var(), pdf(), or function() in C++ (there are also more occurences of var() to replace in the lines below).


ratioSigEff = wspace.var("ratioSigEff") # get uncertain parameter to constrain
ratioBkgEff = wspace.var("ratioBkgEff") # get uncertain parameter to constrain
constrainedParams = ROOT.RooArgSet(ratioSigEff, ratioBkgEff) # need to constrain these in the fit (should change default behavior)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
constrainedParams = ROOT.RooArgSet(ratioSigEff, ratioBkgEff) # need to constrain these in the fit (should change default behavior)
constrainedParams = {ratioSigEff, ratioBkgEff} # need to constrain these in the fit (should change default behavior)

In PyROOT, you can directly use Python sets instead of RooArgSets


# Create an example dataset with 160 observed events
obs.setVal(160.)
data = ROOT.RooDataSet("exampleData", "exampleData", ROOT.RooArgSet(obs))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
data = ROOT.RooDataSet("exampleData", "exampleData", ROOT.RooArgSet(obs))
data = ROOT.RooDataSet("exampleData", "exampleData", {obs})

all = ROOT.RooArgSet(s, ratioBkgEff, ratioSigEff)

# not necessary
modelWithConstraints.fitTo(data, ROOT.RooFit.Constrain(ROOT.RooArgSet(ratioSigEff, ratioBkgEff)))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
modelWithConstraints.fitTo(data, ROOT.RooFit.Constrain(ROOT.RooArgSet(ratioSigEff, ratioBkgEff)))
modelWithConstraints.fitTo(data, Constrain=constrainedParams)

There was already a set defined for them before. You should reuse that here.

modelConfig.SetParametersOfInterest(paramOfInterest)
modelConfig.SetNuisanceParameters(constrainedParams)
modelConfig.SetObservables(obs)
modelConfig.SetGlobalObservables(ROOT.RooArgSet(gSigEff, gSigBkg))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
modelConfig.SetGlobalObservables(ROOT.RooArgSet(gSigEff, gSigBkg))
modelConfig.SetGlobalObservables({gSigEff, gSigBkg})


modelConfig = ROOT.RooStats.ModelConfig(wspace)
modelConfig.SetPdf(modelWithConstraints)
modelConfig.SetParametersOfInterest(paramOfInterest)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
modelConfig.SetParametersOfInterest(paramOfInterest)
modelConfig.SetParametersOfInterest({s})

The paramOfInterest variable above is used nowhere else. You should better delete it, and directly use {s} here to be more explicit.

wspace.writeToFile("rs101_ws.root")

# First, let's use a Calculator based on the Profile Likelihood Ratio
# plc = ROOT.RooStats.ProfileLikelihoodCalculator(data, modelWithConstraints, paramOfInterest)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# plc = ROOT.RooStats.ProfileLikelihoodCalculator(data, modelWithConstraints, paramOfInterest)

You should remove this commented out code, as there is not good reason to have it here

Copy link
Contributor

@guitargeek guitargeek left a comment

Choose a reason for hiding this comment

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

Hi @busorgin thank you so much for this contribution, which is really appreciated 👍 I which context are you doing this? Are you a RooFit user who wants to contribute, or you were just browsing GitHub for issues you could address?

Please address the requests I have (mostly about Pythonic code, and one fix for the destruction order fiasco).

Also, after applying all these suggestions, could you please format the new tutorial with black?

black --line-length=120 rs101_limitexample.py

@guitargeek guitargeek changed the title Convert rs101_limitexample.C to Python [RF] Convert rs101_limitexample.C to Python Jun 29, 2022
@busorgin
Copy link
Contributor Author

Hi @guitargeek, thanks for the review. I addressed the suggestions and formatted the code.

I was looking for an open source project to contribute to that would be interesting to me, settled on root

@guitargeek
Copy link
Contributor

@phsft-bot build

@phsft-bot
Copy link
Collaborator

Starting build on ROOT-debian10-i386/soversion, ROOT-performance-centos8-multicore/cxx17, ROOT-ubuntu18.04/nortcxxmod, ROOT-ubuntu2004/python3, mac1015/cxx17, mac11/cxx14, windows10/cxx14
How to customize builds

@guitargeek
Copy link
Contributor

Alright, thank you very much! That's very nice you chose ROOT to contribute to!

If you need any guidance on what you could help with, just ask me. Sometimes it's not easy to find the issues that are actually easy to work on, as the code is quite complex.

Are you looking only Python-related issues, or would you also be interested in doing some C++ contributions to beef up your C++ skills and experience?

As for this PR, I'll merge it if the CI bot tests pass.

@busorgin
Copy link
Contributor Author

Thanks, I'll keep that in mind.

I was looking for both C++ and Python. I found #9859, seemed like a good starter item on the C++ side. Open to suggestions if there are other good issues to look into.

@guitargeek guitargeek changed the title [RF] Convert rs101_limitexample.C to Python [RF] Translate RooStats tutorial rs101_limitexample.C to Python Jun 30, 2022
@guitargeek
Copy link
Contributor

Hi @busorgin, okay sounds great! The issue you linked is indeed a very good one to get started with RooFit C++ development. If you have any questions about it, we can follow up in the issue thread there.

Of course, it would also be of great help if you continue doing these RooStats tutorial translations! People use more Python than C++ now, and a better coverage of the Python tutorials is something requested by many users, in particular for RooStats. So among all the "easier" RooFit issues, the RooStats tutorial translations have the highest priority.

Copy link
Contributor

@guitargeek guitargeek left a comment

Choose a reason for hiding this comment

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

LGTM, thank you for this contribution!

  • new tutorial translation runs on my local machine and on the CI
  • the output of the tutorial is the same as for the C++ version

@guitargeek guitargeek merged commit 134e2a1 into root-project:master Jun 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants