Skip to content

Commit c8918b4

Browse files
authored
refactor: optimize examples ambiguity resolution (acts-project#1938)
my first implementation was quite wasteful in terms of CPU resources because I just wanted to get it done to test downstream performance for the vertexing. I optimized the code in a way that it pre computes the shared hits and modifies these numbers when removing a track. performance difference for the current `full_chain_odd.py` without Pythia8 and without Geant4 new ``` identifier time_total_s time_perevent_s Algorithm:AmbiguityResolutionAlgorithm 0.5349 0.005349 ``` vs old ``` identifier time_total_s time_perevent_s Algorithm:AmbiguityResolutionAlgorithm 12.52 0.1252 ```
1 parent 62a5336 commit c8918b4

File tree

8 files changed

+117
-121
lines changed

8 files changed

+117
-121
lines changed
-195 Bytes
Binary file not shown.
-194 Bytes
Binary file not shown.
-59 Bytes
Binary file not shown.
-44 Bytes
Binary file not shown.

Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/AmbiguityResolutionAlgorithm.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ namespace ActsExamples {
2828
class AmbiguityResolutionAlgorithm final : public IAlgorithm {
2929
public:
3030
struct Config {
31-
/// Input source links collection.
32-
std::string inputSourceLinks;
3331
/// Input trajectories collection.
3432
std::string inputTrajectories;
3533
/// Output trajectories collection.
3634
std::string outputTrajectories;
3735

3836
/// Maximum amount of shared hits per track.
3937
std::uint32_t maximumSharedHits = 1;
38+
/// Maximum number of iterations
39+
std::uint32_t maximumIterations = 1000;
4040

4141
/// Minumum number of measurement to form a track.
4242
size_t nMeasurementsMin = 7;

Examples/Algorithms/TrackFinding/src/AmbiguityResolutionAlgorithm.cpp

Lines changed: 111 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
#include <numeric>
2121
#include <stdexcept>
2222

23+
#include <boost/container/flat_map.hpp>
24+
#include <boost/container/flat_set.hpp>
25+
2326
ActsExamples::AmbiguityResolutionAlgorithm::AmbiguityResolutionAlgorithm(
2427
ActsExamples::AmbiguityResolutionAlgorithm::Config cfg,
2528
Acts::Logging::Level lvl)
2629
: ActsExamples::IAlgorithm("AmbiguityResolutionAlgorithm", lvl),
2730
m_cfg(std::move(cfg)) {
28-
if (m_cfg.inputSourceLinks.empty()) {
29-
throw std::invalid_argument("Missing source links input collection");
30-
}
3131
if (m_cfg.inputTrajectories.empty()) {
3232
throw std::invalid_argument("Missing trajectories input collection");
3333
}
@@ -38,148 +38,145 @@ ActsExamples::AmbiguityResolutionAlgorithm::AmbiguityResolutionAlgorithm(
3838

3939
namespace {
4040

41-
// TODO this is somewhat duplicated in TrackFindingAlgorithm.hpp
42-
// TODO we should make a common implementation in the core at some point
43-
std::vector<std::size_t> computeSharedHits(
44-
const ActsExamples::IndexSourceLinkContainer& sourceLinks,
41+
struct State {
42+
std::size_t numberOfTracks{};
43+
44+
std::vector<std::pair<std::size_t, std::size_t>> trackTips;
45+
std::vector<float> trackChi2;
46+
std::vector<ActsExamples::TrackParameters> trackParameters;
47+
std::vector<std::vector<std::size_t>> measurementsPerTrack;
48+
49+
boost::container::flat_map<std::size_t,
50+
boost::container::flat_set<std::size_t>>
51+
tracksPerMeasurement;
52+
std::vector<std::size_t> sharedMeasurementsPerTrack;
53+
54+
boost::container::flat_set<std::size_t> selectedTracks;
55+
};
56+
57+
State computeInitialState(
4558
const ActsExamples::TrajectoriesContainer& trajectories,
46-
const std::vector<uint32_t>& trackIndices,
47-
const std::vector<std::pair<size_t, size_t>>& trackTips) {
48-
std::vector<std::size_t> hitCountPerMeasurement(sourceLinks.size(), 0);
59+
std::size_t nMeasurementsMin) {
60+
State state;
4961

50-
for (auto indexTrack : trackIndices) {
51-
const auto [indexTraj, tip] = trackTips[indexTrack];
52-
const auto& traj = trajectories[indexTraj];
62+
for (std::size_t iTrack = 0, iTraj = 0; iTraj < trajectories.size();
63+
++iTraj) {
64+
const auto& traj = trajectories[iTraj];
65+
for (auto tip : traj.tips()) {
66+
if (!traj.hasTrackParameters(tip)) {
67+
continue;
68+
}
5369

54-
traj.multiTrajectory().visitBackwards(tip, [&](const auto& state) {
55-
if (!state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
56-
return true;
70+
auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(
71+
traj.multiTrajectory(), tip);
72+
if (trajState.nMeasurements < nMeasurementsMin) {
73+
continue;
5774
}
5875

59-
const std::size_t indexHit =
60-
state.getUncalibratedSourceLink()
61-
.template get<ActsExamples::IndexSourceLink>()
62-
.index();
76+
std::vector<std::size_t> measurements;
77+
traj.multiTrajectory().visitBackwards(tip, [&](const auto& hit) {
78+
if (hit.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
79+
std::size_t iMeasurement =
80+
hit.getUncalibratedSourceLink()
81+
.template get<ActsExamples::IndexSourceLink>()
82+
.index();
83+
measurements.push_back(iMeasurement);
84+
}
85+
return true;
86+
});
6387

64-
++hitCountPerMeasurement[indexHit];
88+
++state.numberOfTracks;
6589

66-
return true;
67-
});
68-
}
90+
state.trackTips.emplace_back(iTraj, tip);
91+
state.trackChi2.push_back(trajState.chi2Sum / trajState.NDF);
92+
state.trackParameters.push_back(traj.trackParameters(tip));
93+
state.measurementsPerTrack.push_back(std::move(measurements));
6994

70-
std::vector<std::size_t> sharedHitCountPerTrack(trackIndices.size(), 0);
95+
state.selectedTracks.insert(iTrack);
7196

72-
for (std::size_t i = 0; i < trackIndices.size(); ++i) {
73-
const auto indexTrack = trackIndices[i];
74-
const auto [indexTraj, tip] = trackTips[indexTrack];
75-
const auto& traj = trajectories[indexTraj];
97+
++iTrack;
98+
}
99+
}
76100

77-
traj.multiTrajectory().visitBackwards(tip, [&](const auto& state) {
78-
if (!state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
79-
return true;
80-
}
101+
for (std::size_t iTrack = 0; iTrack < state.numberOfTracks; ++iTrack) {
102+
for (auto iMeasurement : state.measurementsPerTrack[iTrack]) {
103+
state.tracksPerMeasurement[iMeasurement].insert(iTrack);
104+
}
105+
}
81106

82-
const std::size_t indexHit =
83-
state.getUncalibratedSourceLink()
84-
.template get<ActsExamples::IndexSourceLink>()
85-
.index();
107+
state.sharedMeasurementsPerTrack =
108+
std::vector<std::size_t>(state.trackTips.size(), 0);
86109

87-
if (hitCountPerMeasurement[indexHit] > 1) {
88-
++sharedHitCountPerTrack[i];
110+
for (std::size_t iTrack = 0; iTrack < state.numberOfTracks; ++iTrack) {
111+
for (auto iMeasurement : state.measurementsPerTrack[iTrack]) {
112+
if (state.tracksPerMeasurement[iMeasurement].size() > 1) {
113+
++state.sharedMeasurementsPerTrack[iTrack];
89114
}
90-
91-
return true;
92-
});
115+
}
93116
}
94117

95-
return sharedHitCountPerTrack;
118+
return state;
96119
}
97120

98-
std::size_t computeTrackHits(
99-
const Acts::ConstVectorMultiTrajectory& multiTrajectory,
100-
const std::size_t tip) {
101-
std::size_t result = 0;
121+
void removeTrack(State& state, std::size_t iTrack) {
122+
for (auto iMeasurement : state.measurementsPerTrack[iTrack]) {
123+
state.tracksPerMeasurement[iMeasurement].erase(iTrack);
102124

103-
multiTrajectory.visitBackwards(tip, [&](const auto&) { ++result; });
125+
if (state.tracksPerMeasurement[iMeasurement].size() == 1) {
126+
auto jTrack = *std::begin(state.tracksPerMeasurement[iMeasurement]);
127+
--state.sharedMeasurementsPerTrack[jTrack];
128+
}
129+
}
104130

105-
return result;
131+
state.selectedTracks.erase(iTrack);
106132
}
107133

108134
} // namespace
109135

110136
ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionAlgorithm::execute(
111137
const AlgorithmContext& ctx) const {
112-
// Read input data
113-
const auto& sourceLinks =
114-
ctx.eventStore.get<IndexSourceLinkContainer>(m_cfg.inputSourceLinks);
115138
const auto& trajectories =
116139
ctx.eventStore.get<TrajectoriesContainer>(m_cfg.inputTrajectories);
117140

118-
TrackParametersContainer trackParameters;
119-
std::vector<std::pair<size_t, size_t>> trackTips;
141+
auto state = computeInitialState(trajectories, m_cfg.nMeasurementsMin);
120142

121-
for (std::size_t iTraj = 0; iTraj < trajectories.size(); ++iTraj) {
122-
const auto& traj = trajectories[iTraj];
123-
for (auto tip : traj.tips()) {
124-
if (!traj.hasTrackParameters(tip)) {
125-
continue;
126-
}
127-
auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(
128-
traj.multiTrajectory(), tip);
129-
if (trajState.nMeasurements < m_cfg.nMeasurementsMin) {
130-
continue;
131-
}
132-
trackParameters.push_back(traj.trackParameters(tip));
133-
trackTips.emplace_back(iTraj, tip);
134-
}
135-
}
143+
auto sharedMeasurementsComperator = [&state](std::size_t a, std::size_t b) {
144+
return state.sharedMeasurementsPerTrack[a] <
145+
state.sharedMeasurementsPerTrack[b];
146+
};
147+
auto badTrackComperator = [&state](std::size_t a, std::size_t b) {
148+
auto relativeSharedMeasurements = [&state](std::size_t i) {
149+
return 1.0 * state.sharedMeasurementsPerTrack[i] /
150+
state.measurementsPerTrack[i].size();
151+
};
136152

137-
std::vector<uint32_t> hitCount(trackParameters.size(), 0);
138-
for (std::size_t i = 0; i < trackParameters.size(); ++i) {
139-
const auto [iTraj, tip] = trackTips[i];
140-
const auto& traj = trajectories[iTraj];
141-
hitCount[i] = computeTrackHits(traj.multiTrajectory(), tip);
142-
}
143-
144-
std::vector<uint32_t> trackIndices(trackParameters.size());
145-
std::iota(std::begin(trackIndices), std::end(trackIndices), 0);
146-
147-
while (true) {
148-
const auto sharedHits =
149-
computeSharedHits(sourceLinks, trajectories, trackIndices, trackTips);
150-
151-
if (sharedHits.empty() ||
152-
*std::max_element(std::begin(sharedHits), std::end(sharedHits)) <
153-
m_cfg.maximumSharedHits) {
154-
break;
153+
if (relativeSharedMeasurements(a) != relativeSharedMeasurements(b)) {
154+
return relativeSharedMeasurements(a) < relativeSharedMeasurements(b);
155155
}
156-
157-
std::vector<float> relativeSharedHits(trackIndices.size(), 0);
158-
for (std::size_t i = 0; i < trackIndices.size(); ++i) {
159-
const auto indexTrack = trackIndices[i];
160-
relativeSharedHits[i] = 1.0f * sharedHits[i] / hitCount[indexTrack];
156+
return state.trackChi2[a] < state.trackChi2[b];
157+
};
158+
159+
for (std::size_t i = 0; i < m_cfg.maximumIterations; ++i) {
160+
auto maximumSharedMeasurements = *std::max_element(
161+
state.selectedTracks.begin(), state.selectedTracks.end(),
162+
sharedMeasurementsComperator);
163+
ACTS_VERBOSE(
164+
"maximum shared measurements "
165+
<< state.sharedMeasurementsPerTrack[maximumSharedMeasurements]);
166+
if (state.sharedMeasurementsPerTrack[maximumSharedMeasurements] <
167+
m_cfg.maximumSharedHits) {
168+
break;
161169
}
162170

163-
const auto maxRelativeSharedHits = std::max_element(
164-
std::begin(relativeSharedHits), std::end(relativeSharedHits));
165-
const auto index =
166-
std::distance(std::begin(relativeSharedHits), maxRelativeSharedHits);
167-
trackIndices.erase(std::begin(trackIndices) + index);
168-
}
169-
170-
if (trackIndices.size() == trackParameters.size()) {
171-
const auto sharedHits =
172-
computeSharedHits(sourceLinks, trajectories, trackIndices, trackTips);
173-
174-
std::vector<float> relativeSharedHits(trackIndices.size(), 0);
175-
for (std::size_t i = 0; i < trackIndices.size(); ++i) {
176-
const auto indexTrack = trackIndices[i];
177-
relativeSharedHits[i] = 1.0f * sharedHits[i] / hitCount[indexTrack];
178-
}
171+
auto badTrack =
172+
*std::max_element(state.selectedTracks.begin(),
173+
state.selectedTracks.end(), badTrackComperator);
174+
ACTS_VERBOSE("remove track " << badTrack);
175+
removeTrack(state, badTrack);
179176
}
180177

181-
ACTS_INFO("Resolved to " << trackIndices.size() << " tracks from "
182-
<< trackParameters.size());
178+
ACTS_INFO("Resolved to " << state.selectedTracks.size() << " tracks from "
179+
<< state.trackTips.size());
183180

184181
TrajectoriesContainer outputTrajectories;
185182
outputTrajectories.reserve(trajectories.size());
@@ -189,13 +186,13 @@ ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionAlgorithm::execute(
189186
std::vector<Acts::MultiTrajectoryTraits::IndexType> tips;
190187
Trajectories::IndexedParameters parameters;
191188

192-
for (auto iTrack : trackIndices) {
193-
if (trackTips[iTrack].first != iTraj) {
189+
for (auto iTrack : state.selectedTracks) {
190+
if (state.trackTips[iTrack].first != iTraj) {
194191
continue;
195192
}
196-
const auto tip = trackTips[iTrack].second;
193+
const auto tip = state.trackTips[iTrack].second;
197194
tips.push_back(tip);
198-
parameters.emplace(tip, trackParameters[iTrack]);
195+
parameters.emplace(tip, state.trackParameters[iTrack]);
199196
}
200197
if (!tips.empty()) {
201198
outputTrajectories.emplace_back(traj.multiTrajectory(), tips, parameters);

Examples/Python/python/acts/examples/reconstruction.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,6 @@ def addAmbiguityResolution(
12321232

12331233
alg = AmbiguityResolutionAlgorithm(
12341234
level=customLogLevel(),
1235-
inputSourceLinks="sourcelinks",
12361235
inputTrajectories="trajectories",
12371236
outputTrajectories="filteredTrajectories",
12381237
**acts.examples.defaultKWArgs(

Examples/Python/src/TrackFinding.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ void addTrackFinding(Context& ctx) {
310310
.def(py::init(constructor));
311311
}
312312

313-
ACTS_PYTHON_DECLARE_ALGORITHM(
314-
ActsExamples::AmbiguityResolutionAlgorithm, mex,
315-
"AmbiguityResolutionAlgorithm", inputSourceLinks, inputTrajectories,
316-
outputTrajectories, maximumSharedHits, nMeasurementsMin);
313+
ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::AmbiguityResolutionAlgorithm, mex,
314+
"AmbiguityResolutionAlgorithm",
315+
inputTrajectories, outputTrajectories,
316+
maximumSharedHits, nMeasurementsMin);
317317

318318
ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::SeedsToPrototracks, mex,
319319
"SeedsToPrototracks", inputSeeds,

0 commit comments

Comments
 (0)