Skip to content

Commit 33c8310

Browse files
committed
update
1 parent 07eeb26 commit 33c8310

File tree

8 files changed

+243
-276
lines changed

8 files changed

+243
-276
lines changed

Examples/Framework/include/ActsExamples/Validation/TrackFinderPerformanceCollector.hpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ class TrackFinderPerformanceCollector {
5252
/// Fill histograms for one event.
5353
///
5454
/// @note The caller must ensure exclusive access (e.g. hold a mutex).
55-
ProcessCode fill(
56-
const AlgorithmContext& ctx, const ConstTrackContainer& tracks,
57-
const SimParticleContainer& particles,
58-
const TrackParticleMatching& trackParticleMatching,
59-
const ParticleTrackMatching& particleTrackMatching,
60-
const InverseMultimap<SimBarcode>& particleMeasurementsMap);
55+
ProcessCode fill(const AlgorithmContext& ctx,
56+
const ConstTrackContainer& tracks,
57+
const SimParticleContainer& particles,
58+
const TrackParticleMatching& trackParticleMatching,
59+
const ParticleTrackMatching& particleTrackMatching,
60+
const InverseMultimap<SimBarcode>& particleMeasurementsMap);
6161

6262
/// Summary count statistics accumulated across all filled events.
6363
struct Stats {
@@ -73,10 +73,14 @@ class TrackFinderPerformanceCollector {
7373

7474
/// Return accumulated event counts.
7575
Stats stats() const {
76-
return {m_nTotalTracks, m_nTotalMatchedTracks,
77-
m_nTotalFakeTracks, m_nTotalDuplicateTracks,
78-
m_nTotalParticles, m_nTotalMatchedParticles,
79-
m_nTotalDuplicateParticles, m_nTotalFakeParticles};
76+
return {m_nTotalTracks,
77+
m_nTotalMatchedTracks,
78+
m_nTotalFakeTracks,
79+
m_nTotalDuplicateTracks,
80+
m_nTotalParticles,
81+
m_nTotalMatchedParticles,
82+
m_nTotalDuplicateParticles,
83+
m_nTotalFakeParticles};
8084
}
8185

8286
/// Emit efficiency/fake/duplicate summary statistics via @p log.

Examples/Framework/src/Validation/TrackFinderPerformanceCollector.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,7 @@ ProcessCode TrackFinderPerformanceCollector::fill(
175175

176176
void TrackFinderPerformanceCollector::logSummary(
177177
const Acts::Logger& log) const {
178-
float eff_tracks =
179-
static_cast<float>(m_nTotalMatchedTracks) / m_nTotalTracks;
178+
float eff_tracks = static_cast<float>(m_nTotalMatchedTracks) / m_nTotalTracks;
180179
float fakeRatio_tracks =
181180
static_cast<float>(m_nTotalFakeTracks) / m_nTotalTracks;
182181
float duplicationRatio_tracks =

Examples/Io/Root/src/RootTrackFinderPerformanceWriter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ ProcessCode RootTrackFinderPerformanceWriter::finalize() {
115115
for (const auto& [name, eff] : m_collector.effPlotTool().efficiencies1D()) {
116116
toRoot(eff)->Write();
117117
}
118-
for (const auto& [name, eff] :
119-
m_collector.effPlotTool().efficiencies2D()) {
118+
for (const auto& [name, eff] : m_collector.effPlotTool().efficiencies2D()) {
120119
toRoot(eff)->Write();
121120
}
122-
for (const auto& eff : m_collector.effPlotTool().trackEffVsEtaInPtRanges()) {
121+
for (const auto& eff :
122+
m_collector.effPlotTool().trackEffVsEtaInPtRanges()) {
123123
toRoot(eff)->Write();
124124
}
125125
for (const auto& eff :

Python/Core/python/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .ActsPythonBindings import *
99
from .ActsPythonBindings import __version__
10+
from .ActsPythonBindings import _demo_histogram1, _demo_profile1, _demo_efficiency1
1011
from . import ActsPythonBindings
1112
from ._adapter import _patch_config
1213
from .histogram import _patch_histogram_types

Python/Core/python/histogram.py

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,68 @@
77
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
88

99

10+
def _import_mplhep():
11+
try:
12+
import mplhep
13+
14+
return mplhep
15+
except ImportError:
16+
print("mplhep is not installed; cannot plot ACTS histograms.")
17+
raise
18+
19+
20+
def plot_histogram(self, ax=None, **kwargs):
21+
import matplotlib.pyplot as plt
22+
23+
mh = _import_mplhep()
24+
ax = ax or plt.subplots()[1]
25+
artists = mh.histplot(self._to_boost_histogram_(), ax=ax, **kwargs)
26+
if label := self.histogram.axis(0).label:
27+
ax.set_xlabel(label)
28+
if self.title:
29+
ax.set_title(self.title)
30+
return artists
31+
32+
33+
def plot_profile(self, ax=None, **kwargs):
34+
import matplotlib.pyplot as plt
35+
36+
mh = _import_mplhep()
37+
ax = ax or plt.subplots()[1]
38+
artists = mh.histplot(self._to_boost_histogram_(), ax=ax, **kwargs)
39+
if label := self.histogram.axis(0).label:
40+
ax.set_xlabel(label)
41+
if self.sampleAxisTitle:
42+
ax.set_ylabel(self.sampleAxisTitle)
43+
if self.title:
44+
ax.set_title(self.title)
45+
return artists
46+
47+
48+
def plot_efficiency(self, ax=None, **kwargs):
49+
import matplotlib.pyplot as plt
50+
51+
mh = _import_mplhep()
52+
ax = ax or plt.subplots()[1]
53+
xlabel = kwargs.pop("xlabel", self.total.axis(0).label)
54+
mh.comp.comparison(
55+
self.accepted._to_boost_histogram_(),
56+
self.total._to_boost_histogram_(),
57+
ax=ax,
58+
xlabel=xlabel,
59+
comparison="efficiency",
60+
**kwargs,
61+
)
62+
if self.title:
63+
ax.set_title(self.title)
64+
65+
66+
def _patch_plot_methods(m):
67+
m.Histogram1.plot = plot_histogram
68+
m.ProfileHistogram1.plot = plot_profile
69+
m.Efficiency1.plot = plot_efficiency
70+
71+
1072
def _patch_histogram_types(m):
1173
"""Patch ACTS histogram types with the boost-histogram protocol.
1274
@@ -23,7 +85,7 @@ def _import_bh():
2385
import boost_histogram as bh
2486

2587
return bh
26-
except ImportError as e:
88+
except ImportError:
2789
print(
2890
"boost_histogram is not installed; cannot convert ACTS histogram to bh.Histogram."
2991
)
@@ -49,17 +111,20 @@ def _profile_to_bh(self):
49111
bh = _import_bh()
50112
h = bh.Histogram(*_bh_axes(bh, self), storage=bh.storage.Mean())
51113
view = h.view()
114+
view["count"] = self.counts()
52115
view["value"] = self.means()
53-
view["variance"] = self.variances()
116+
view["_sum_of_deltas_squared"] = self.sum_of_deltas_squared()
54117
return h
55118

56119
m.BoostProfileHistogram._to_boost_histogram_ = _profile_to_bh
57120

58-
# Histogram<Dim>: delegate to .histogram() which is a BoostHistogram
121+
# Histogram<Dim>: delegate to .histogram which is a BoostHistogram
59122
for cls in (m.Histogram1, m.Histogram2, m.Histogram3):
60-
cls._to_boost_histogram_ = lambda self: self.histogram()._to_boost_histogram_()
123+
cls._to_boost_histogram_ = lambda self: self.histogram._to_boost_histogram_()
61124

62-
# ProfileHistogram<1>: delegate to .histogram() which is a BoostProfileHistogram
125+
# ProfileHistogram<1>: delegate to .histogram which is a BoostProfileHistogram
63126
m.ProfileHistogram1._to_boost_histogram_ = (
64-
lambda self: self.histogram()._to_boost_histogram_()
127+
lambda self: self.histogram._to_boost_histogram_()
65128
)
129+
130+
_patch_plot_methods(m)

Python/Core/src/Utilities.cpp

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "Acts/Utilities/Logger.hpp"
1515
#include "Acts/Utilities/RangeXD.hpp"
1616

17+
#include <cmath>
18+
#include <random>
1719
#include <sstream>
1820
#include <type_traits>
1921
#include <unordered_map>
@@ -58,39 +60,26 @@ static py::array_t<double> copyBins(const Hist& h, ValueFn fn) {
5860

5961
template <std::size_t Dim>
6062
static void bindHistogram(py::module_& m) {
61-
using H = Acts::Experimental::Histogram<Dim>;
62-
py::class_<H>(m, ("Histogram" + std::to_string(Dim)).c_str())
63-
.def(py::init<std::string, std::string, std::array<AxisVariant, Dim>>(),
64-
"name"_a, "title"_a, "axes"_a)
63+
using H = Histogram<Dim>;
64+
py::classh<H>(m, ("Histogram" + std::to_string(Dim)).c_str())
6565
.def_property_readonly("name", &H::name)
6666
.def_property_readonly("title", &H::title)
6767
.def_property_readonly("rank", [](const H&) { return Dim; })
68-
.def("histogram", &H::histogram,
69-
py::return_value_policy::reference_internal)
70-
.def(
71-
"fill", [](H& h, std::array<double, Dim> vals) { h.fill(vals); },
72-
"vals"_a);
68+
.def_property_readonly("histogram", &H::histogram,
69+
py::return_value_policy::reference_internal);
7370
}
7471

7572
template <std::size_t Dim>
7673
static void bindEfficiency(py::module_& m) {
77-
using E = Acts::Experimental::Efficiency<Dim>;
78-
py::class_<E>(m, ("Efficiency" + std::to_string(Dim)).c_str())
79-
.def(py::init<std::string, std::string, std::array<AxisVariant, Dim>>(),
80-
"name"_a, "title"_a, "axes"_a)
74+
using E = Efficiency<Dim>;
75+
py::classh<E>(m, ("Efficiency" + std::to_string(Dim)).c_str())
8176
.def_property_readonly("name", &E::name)
8277
.def_property_readonly("title", &E::title)
8378
.def_property_readonly("rank", [](const E&) { return Dim; })
84-
.def("accepted", &E::acceptedHistogram,
85-
py::return_value_policy::reference_internal)
86-
.def("total", &E::totalHistogram,
87-
py::return_value_policy::reference_internal)
88-
.def(
89-
"fill",
90-
[](E& e, std::array<double, Dim> vals, bool accepted) {
91-
e.fill(vals, accepted);
92-
},
93-
"vals"_a, "accepted"_a);
79+
.def_property_readonly("accepted", &E::acceptedHistogram,
80+
py::return_value_policy::reference_internal)
81+
.def_property_readonly("total", &E::totalHistogram,
82+
py::return_value_policy::reference_internal);
9483
}
9584

9685
/// @brief This adds the classes from Core/Utilities to the python module
@@ -296,17 +285,6 @@ void addUtilities(py::module_& m) {
296285
{
297286
// Single axis type covering regular, variable, and log axes
298287
py::class_<AxisVariant>(m, "Axis")
299-
.def(
300-
py::init([](unsigned int n, double lo, double hi,
301-
std::string label) {
302-
return AxisVariant{BoostRegularAxis{n, lo, hi, std::move(label)}};
303-
}),
304-
"nbins"_a, "lo"_a, "hi"_a, "label"_a = "")
305-
.def(py::init([](std::vector<double> edges, std::string label) {
306-
return AxisVariant{BoostVariableAxis{edges.begin(), edges.end(),
307-
std::move(label)}};
308-
}),
309-
"edges"_a, "label"_a = "")
310288
.def_property_readonly("size", &AxisVariant::size)
311289
.def_property_readonly(
312290
"label",
@@ -336,40 +314,78 @@ void addUtilities(py::module_& m) {
336314
return h.axis(i);
337315
},
338316
"i"_a)
317+
.def("counts",
318+
[](const BoostProfileHist& h) {
319+
return copyBins(h, [](auto& x) {
320+
return static_cast<double>((*x).count());
321+
});
322+
})
339323
.def("means",
340324
[](const BoostProfileHist& h) {
341325
return copyBins(h, [](auto& x) { return (*x).value(); });
342326
})
343-
.def("variances", [](const BoostProfileHist& h) {
344-
return copyBins(h, [](auto& x) { return (*x).variance(); });
327+
.def("sum_of_deltas_squared", [](const BoostProfileHist& h) {
328+
return copyBins(h, [](auto& x) {
329+
const auto n = (*x).count();
330+
return n > 1.0 ? (*x).variance() * (n - 1.0) : 0.0;
331+
});
345332
});
346333

347334
bindHistogram<1>(m);
348335
bindHistogram<2>(m);
349336
bindHistogram<3>(m);
350337

351338
{
352-
using H = ProfileHistogram<1>;
353-
py::class_<H>(m, "ProfileHistogram1")
354-
.def(py::init<std::string, std::string, std::array<AxisVariant, 1>,
355-
std::string>(),
356-
"name"_a, "title"_a, "axes"_a, "sampleAxisTitle"_a)
357-
.def_property_readonly("name", &H::name)
358-
.def_property_readonly("title", &H::title)
359-
.def_property_readonly("rank", [](const H&) { return 1u; })
360-
.def_property_readonly("sampleAxisTitle", &H::sampleAxisTitle)
361-
.def("histogram", &H::histogram,
362-
py::return_value_policy::reference_internal)
363-
.def(
364-
"fill",
365-
[](H& h, std::array<double, 1> vals, double sample) {
366-
h.fill(vals, sample);
367-
},
368-
"vals"_a, "sample"_a);
339+
using P = ProfileHistogram<1>;
340+
py::classh<P>(m, "ProfileHistogram1")
341+
.def_property_readonly("name", &P::name)
342+
.def_property_readonly("title", &P::title)
343+
.def_property_readonly("rank", [](const P&) { return 1u; })
344+
.def_property_readonly("sampleAxisTitle", &P::sampleAxisTitle)
345+
.def_property_readonly("histogram", &P::histogram,
346+
py::return_value_policy::reference_internal);
369347
}
370348

371349
bindEfficiency<1>(m);
372350
bindEfficiency<2>(m);
351+
352+
// Demo factory functions for testing and examples — not public API
353+
m.def("_demo_histogram1", []() -> Histogram1 {
354+
BoostRegularAxis ax(10, 0.0, 10.0, "x [a.u.]");
355+
Histogram1 h("demo", "Demo Histogram", {AxisVariant(ax)});
356+
std::mt19937 rng(42);
357+
std::uniform_real_distribution<double> x(0.0, 10.0);
358+
for (int i = 0; i < 1000; ++i) {
359+
h.fill({x(rng)});
360+
}
361+
return h;
362+
});
363+
364+
m.def("_demo_profile1", []() -> ProfileHistogram1 {
365+
BoostRegularAxis ax(10, 0.0, 10.0, "x [a.u.]");
366+
ProfileHistogram1 h("demo", "Demo Profile", {AxisVariant(ax)}, "y [a.u.]");
367+
std::mt19937 rng(42);
368+
std::uniform_real_distribution<double> x(0.0, 10.0);
369+
std::normal_distribution<double> noise(0.0, 0.1);
370+
for (int i = 0; i < 1000; ++i) {
371+
double xi = x(rng);
372+
h.fill({xi}, std::sin(xi) + noise(rng));
373+
}
374+
return h;
375+
});
376+
377+
m.def("_demo_efficiency1", []() -> Efficiency1 {
378+
BoostRegularAxis ax(10, 0.0, 10.0, "x [a.u.]");
379+
Efficiency1 h("demo", "Demo Efficiency", {AxisVariant(ax)});
380+
std::mt19937 rng(42);
381+
std::uniform_real_distribution<double> x(0.0, 10.0);
382+
for (int i = 0; i < 1000; ++i) {
383+
double xi = x(rng);
384+
std::bernoulli_distribution accepted(1.0 - std::exp(-xi));
385+
h.fill({xi}, accepted(rng));
386+
}
387+
return h;
388+
});
373389
}
374390
}
375391

0 commit comments

Comments
 (0)