Skip to content

[python][UHI] Implement Slicing in TH1 and adapt the UHI backend and tests #18735

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

siliataider
Copy link
Contributor

This Pull request:

Changes or fixes:

Previously, slicing was implemented at the pythonization level: when slicing a histogram, the values within the specified slice were kept and the ones outside were set to 0, without resizing the sliced axis (leading to inconsistencies..).
This PR implements the slicing logic in TH1 directly, adapting the pythonizations to make use of the new internal functions.

Other changes are made to enhance the interface and fully comply with the UHI specifications, improvements can be summarized here:

  • The sliced axis is resized after slicing, which correctly adjusts the statistics.
  • Integration operations now support full-range integration, non-flow integration, and ranged integration.
  • Histograms can now be set with either a scalar, numpy.ndarray, or any ArrayLike instance.
  • Broadcasting is now also supported (e.g., h[0:2, 0:2] = np.array([[42], [3]])).
  • Enhance error handling.

Checklist:

  • tested changes locally

@siliataider siliataider self-assigned this May 15, 2025
@siliataider siliataider removed the request for review from lmoneta May 15, 2025 13:57
@siliataider siliataider force-pushed the UHI-indexing branch 2 times, most recently from 8f118cc to 9e29340 Compare May 15, 2025 14:59
Copy link
Contributor

@silverweed silverweed left a comment

Choose a reason for hiding this comment

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

I know I wasn't a requested reviewer but I have a couple of comments :)

auto setSliceContents = [&](auto... binEdges) {
ROOT::Internal::SetSliceContent(*hSlice, sliceContents, binEdges...);
};
caller(setSliceContents, shiftedBins);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this whole arrangement with variadic templated arguments is really hard to understand and I'm wondering if it can be simplified on the C++ side (at the cost of some increased complexity in the pythonization).
As far as I understand, args are always expected to be int, so this function should probably take a vector or a span thereof rather than being variadic.

Also, since this is quite a long function I would add a doc comment explaining what it does.

}

// Create a new histogram of the same type as this
std::unique_ptr<TH1> hSlice(static_cast<TH1*>(IsA()->GetNew()(nullptr)));
Copy link
Contributor

Choose a reason for hiding this comment

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

What does GetNew()(nullptr) do? I guess it calls the default constructor of the class? Maybe a note would be useful, since unfortunately the typedef of NewFunc_t doesn't bother explaining...

: hSlice->GetBin(fXaxis.GetNbins() + 1, fYaxis.GetNbins() + 1, fZaxis.GetNbins() + 1);
hSlice->SetBinContent(overflowBin, overflowContent);

return hSlice.release();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we releasing the pointer here? I get this is meant to be called by Python, but if someone calls this from C++ and doesn't manually delete the histogram they would be leaking it silently.
Would it maybe be better to return the unique_ptr and then call SetOwner from python explicitly?

Copy link

github-actions bot commented May 15, 2025

Test Results

    18 files      18 suites   3d 20h 26m 55s ⏱️
 2 745 tests  2 686 ✅   0 💤 59 ❌
48 253 runs  47 991 ✅ 202 💤 60 ❌

For more details on these failures, see this check.

Results for commit 33d1f1a.

♻️ This comment has been updated with latest results.

Copy link
Member

@vepadulano vepadulano left a comment

Choose a reason for hiding this comment

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

Thank you a lot for this prompt effort to improve the implementation according to bleeding edge testing! I have a few quick remarks for a first iteration of the review

Comment on lines +231 to +232
template <size_t Dim>
void SetSliceContent(const std::variant<std::vector<Double_t>, Double_t> &input, const std::vector<Int_t>& args) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe also the Dim template argument here could be just converted to an integer parameter to the function?

}

// Create a new histogram of the same type as this
std::unique_ptr<TH1> hSlice(static_cast<TH1*>(IsA()->GetNew()(nullptr)));
Copy link
Member

Choose a reason for hiding this comment

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

I would just call a function like

template <typename T>
std::unique_ptr<TH1> createHistoDefault(const T &histo){
    TDirectory::TContext ctxt{nullptr};
    return std::make_unique<T>();
}

Copy link
Member

Choose a reason for hiding this comment

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

Note that creating the new histogram without attaching it to gDirectory is important

: hSlice->GetBin(fXaxis.GetNbins() + 1, fYaxis.GetNbins() + 1, fZaxis.GetNbins() + 1);
hSlice->SetBinContent(overflowBin, overflowContent);

return hSlice.release();
Copy link
Member

Choose a reason for hiding this comment

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

I also don't understand why we need to release the pointer here, can't we just return a unique_ptr?

friend auto ROOT::Internal::Slice(const T &histo, std::vector<Int_t>& args);

template <size_t Dim>
void SetSliceContent(const std::variant<std::vector<Double_t>, Double_t> &input, const std::vector<Int_t>& args) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you comment on the need for the std::variant parameter? It is not immediately clear to me, 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.

This is to either set the slice with a vector or a scalar,, in principle I could only keep the vector parameter and then construct the std vector from the scalar on the python side...

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.

3 participants