ATLAS Offline Software
Loading...
Searching...
No Matches
ComboHypoToolBase.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
7using namespace TrigCompositeUtils;
8
9
10ComboHypoToolBase::ComboHypoToolBase(const std::string& type, const std::string& name, const IInterface* parent) :
11 base_class(type, name, parent),
12 m_decisionId(HLT::Identifier::fromToolName(name)), m_legMultiplicities()
13{}
14
15
16StatusCode ComboHypoToolBase::setLegMultiplicity(const Combo::MultiplicityReqMap& multiplicityRequiredMap) {
17 const std::string nameOfToolsChain = m_decisionId.name();
18 const Combo::MultiplicityReqMap::const_iterator it = multiplicityRequiredMap.find(nameOfToolsChain);
19
20 if (it == multiplicityRequiredMap.end()) {
21 ATH_MSG_ERROR("ComboHypoTool for " << m_decisionId << " could not find its required multiplcity data in the map supplied by its parent alg.");
22 return StatusCode::FAILURE;
23 }
24
25 m_legMultiplicities = it->second;
26 if (m_legMultiplicities.size() == 0) {
27 ATH_MSG_ERROR("ComboHypoTool for " << m_decisionId << " was listed in the supplied multiplicityRequiredMap, but data was not supplied for any legs.");
28 return StatusCode::FAILURE;
29 }
30
31 m_legDecisionIds.clear();
32 for (size_t i = 0; i < m_legMultiplicities.size(); ++i) {
33 if(m_legMultiplicities.at(i) < 0) {
34 ATH_MSG_ERROR("ComboHypoTool for " << m_decisionId << " has been configured with an impossible multiplicity requirement of " << m_legMultiplicities.at(i) << " on leg " << i);
35 return StatusCode::FAILURE;
36 }
37 ATH_MSG_DEBUG("ComboHypoTool for " << m_decisionId << " will require multiplicity " << m_legMultiplicities.at(i) << " on leg " << i);
38 if (m_legMultiplicities.size() > 1) { // We only have per-leg IDs when there is more than one leg
40 } else { // For one leg, just repete the chain's ID
42 }
43 }
44
45 return StatusCode::SUCCESS;
46}
47
48StatusCode ComboHypoToolBase::decide(Combo::LegDecisionsMap& passingLegs, const EventContext& ctx) const {
49 if (m_legMultiplicities.size() == 0) {
50 ATH_MSG_ERROR("ComboHypoTool for " << m_decisionId << " has not been properly configured. setLegMultiplicity should be called by the parent alg in initalize");
51 return StatusCode::FAILURE;
52 }
53
54 // if no combinations passed, then exit
55 if (passingLegs.size() == 0) {
56 return StatusCode::SUCCESS;
57 }
58
59 ATH_MSG_DEBUG("Looking for legs from " << decisionId() << " in the map. Map contains features for " << passingLegs.size() << " legs, which may be data for many chains.");
60
61 // select the leg decisions from the map with this ID:
62 std::vector<std::vector<Combo::LegDecision>> legDecisions;
63 ATH_CHECK(selectLegs(passingLegs, legDecisions));
64
65 for (size_t legIndex = 0; legIndex < m_legMultiplicities.size(); ++legIndex) {
66 // The static cast is because this gets read as a Gaudi property which only seems to work with vector<int> rather than vector<size_t>
67 if (legDecisions.at(legIndex).size() < static_cast<size_t>(m_legMultiplicities.at(legIndex))) {
68 ATH_MSG_DEBUG("Too few features are found for " << m_decisionId << " on leg " << legIndex <<", require:" << m_legMultiplicities.at(legIndex) << " have:" << legDecisions.at(legIndex).size());
69 ATH_MSG_DEBUG("This ComboHypoTool cannot run in this event, this chain **REJECTS** this event.");
70 eraseFromLegDecisionsMap(passingLegs);
71 if (msgLvl(MSG::DEBUG)) printDebugInformation(passingLegs);
72 return StatusCode::SUCCESS;
73 }
74 }
75
76 // Create and initialise the combinations generator for the requirements of this chain, given the objects available in this event.
78 for (size_t legIndex = 0; legIndex < m_legMultiplicities.size(); ++legIndex) {
79 const size_t choose_any = m_legMultiplicities.at(legIndex);
80 const size_t out_of = legDecisions.at(legIndex).size();
81 nucg.add({out_of, choose_any});
82 ATH_MSG_DEBUG("For leg " << legIndex << " we will be choosing any " << choose_any << " Decision Objects out of " << out_of);
83 }
84
85 std::vector<std::vector<Combo::LegDecision>> passingCombinations;
86
87 size_t warnings = 0, iterations = 0;
88 do {
89
90 const std::vector<size_t> combination = nucg();
91 ++nucg;
92 std::vector<Combo::LegDecision> combinationToCheck;
93
94 size_t location_in_combination = 0;
95 for (size_t legIndex = 0; legIndex < m_legMultiplicities.size(); ++legIndex) {
96 // We loop over however many objects are required on the leg,
97 // but we take their index from the 'combination'. Hence 'object' is not used directly
98 for (size_t object = 0; object < static_cast<size_t>(m_legMultiplicities.at(legIndex)); ++object) {
99 const size_t objectIndex = combination.at(location_in_combination++);
100 combinationToCheck.push_back( legDecisions.at(legIndex).at(objectIndex) );
101 }
102 }
103
104 ++iterations;
105
106 try {
107 if (executeAlg(combinationToCheck, ctx)) {
108 ATH_MSG_DEBUG("Combination " << (iterations - 1) << " decided to be passing");
109 passingCombinations.push_back(std::move(combinationToCheck));
110 if (m_modeOR == true and m_enableOverride) {
111 break;
112 }
113 } else { // the combination failed
114 if (m_modeOR == false and m_enableOverride) {
115 break;
116 }
117 }
118 } catch (std::exception& e) {
119 ATH_MSG_ERROR(e.what());
120 return StatusCode::FAILURE;
121 }
122
123 if ((iterations >= m_combinationsThresholdWarn && warnings == 0) or (iterations >= m_combinationsThresholdBreak)) {
124 ATH_MSG_WARNING("Have so far processed " << iterations << " combinations for " << m_decisionId << " in this event, " << passingCombinations.size() << " passing.");
125 ++warnings;
126 if (iterations >= m_combinationsThresholdBreak) {
127 ATH_MSG_WARNING("Too many combinations! Breaking the loop at this point.");
128 break;
129 }
130 }
131
132 } while (nucg);
133
134
135 if (m_modeOR) {
136
137 ATH_MSG_DEBUG("Passing " << passingCombinations.size() << " combinations out of " << iterations << ", "
138 << m_decisionId << (passingCombinations.size() ? " **ACCEPTS**" : " **REJECTS**") << " this event based on OR logic.");
139
140 if (m_enableOverride) {
141 ATH_MSG_DEBUG("Note: stopped after the first successful combination due to the EnableOverride flag.");
142 }
143
144 } else { // modeAND
145
146 const bool passAll = (passingCombinations.size() == iterations);
147
148 ATH_MSG_DEBUG("Passing " << passingCombinations.size() << " combinations out of " << iterations << ", "
149 << m_decisionId << (passAll ? " **ACCEPTS**" : " **REJECTS**") << " this event based on AND logic.");
150
151 if (m_enableOverride) {
152 ATH_MSG_DEBUG("Note: stopped after the first failed combination due to the EnableOverride flag.");
153 }
154
155 if (not passAll) {
156 passingCombinations.clear();
157 }
158
159 }
160
161 if (not passingCombinations.empty()) { // need partial erasure of the decsions (only those not present in any combination)
162 updateLegDecisionsMap(passingCombinations, passingLegs);
163 } else { // need complete erasure of input decisions
164 eraseFromLegDecisionsMap(passingLegs);
165 }
166
167 if (msgLvl(MSG::DEBUG)) printDebugInformation(passingLegs);
168 return StatusCode::SUCCESS;
169}
170
171
172StatusCode ComboHypoToolBase::selectLegs(const Combo::LegDecisionsMap& IDCombMap, std::vector<std::vector<Combo::LegDecision>>& legDecisions) const
173{
174 /*
175 legDecisions: nested vector like:
176 [(leg0, el0), (leg0, el1), (leg0, el2), (leg0, el3)], <-- All leg0 objects are in legDecisions[0][X]
177 [(leg1, mu0), (leg1, mu1)] <-- All leg1 objects are in legDecisions[1][X]
178 We keep the legID in the std::pair inside the inner vector as these pairs will be flattened into a single vector
179 when individual combinations of objects are passed to executeAlg, pair.first then contains the leg, with pair.second containing the Decision Object.
180 */
181
182 // Extract from IDCombMap the features for this chain. Wrap the features up in a pair along with their leg ID
183 for (size_t legIndex = 0; legIndex < m_legMultiplicities.size(); ++ legIndex) {
184
185 // If the chain has more than one leg, then we have per-leg IDs. Otherwise we just use the chain's ID
186 const HLT::Identifier& legIdentifier = (m_legMultiplicities.size() > 1 ? m_legDecisionIds.at(legIndex) : m_decisionId);
187
188 // Combo::LegDecision is a pair of <DecisionID, ElementLink<Decision>>
189 std::vector<Combo::LegDecision> decisionObjectsOnLeg;
190
191 // Find physics objects on this leg. May be zero.
192 const Combo::LegDecisionsMap::const_iterator it = IDCombMap.find(legIdentifier.numeric());
193
194 if (it != IDCombMap.end()) {
195 for (const ElementLink<DecisionContainer>& el : it->second) {
196 decisionObjectsOnLeg.emplace_back(legIdentifier, el);
197 }
198 }
199
200 legDecisions.push_back(std::move(decisionObjectsOnLeg));
201 }
202
203 if (msgLvl(MSG::DEBUG)) {
204 ATH_MSG_DEBUG("Getting " << legDecisions.size() << " legs to combine, for ID: " << decisionId());
205 size_t count = 0;
206 for (const auto& leg : legDecisions) {
207 ATH_MSG_DEBUG("Leg " << count++ << " --");
208 for (const auto& dEL : leg) {
209 ATH_MSG_DEBUG("-- " << HLT::Identifier(dEL.first) << " container:" << dEL.second.dataID() << ", index:" << dEL.second.index());
210 }
211 }
212 }
213 return StatusCode::SUCCESS;
214}
215
216void ComboHypoToolBase::updateLegDecisionsMap(const std::vector<std::vector<Combo::LegDecision>>& passingComb, Combo::LegDecisionsMap& passingLegs) const {
217 if (msgLvl(MSG::DEBUG)) {
218 size_t count = 0;
219 for (const std::vector<Combo::LegDecision>& comb : passingComb) {
220 ATH_MSG_DEBUG("-- Passing combination " << count++ << " of " << passingComb.size());
221 for (const auto& [id, EL] : comb) {
222 ATH_MSG_DEBUG("-- -- " << HLT::Identifier(id) << " container:" << EL.dataID() << ", index:" << EL.index());
223 }
224 }
225 }
226
227 // remove combinations that didn't pass from the final map passingLegs
228 for (auto& it : passingLegs) {
229 DecisionID legId = it.first;
230 if (not(legId == m_decisionId or (isLegId(legId) and getIDFromLeg(legId) == m_decisionId))) {
231 continue; // Some other chain, ignore it to get faster execution.
232 }
233 std::vector<ElementLink<DecisionContainer>> updatedDecisionObjectsOnLeg;
234 bool update = false;
235 // Loop over all passing combinations, and all Decision Objects in each passing combination. Find Decision Objects on this leg.
236 for (const std::vector<Combo::LegDecision>& comb : passingComb) {
237 for (const auto& [id, EL] : comb) {
238 // Check that this Decision Object is on the correct leg, and that we haven't collated it already from another combination.
239 if (id == legId and std::find(updatedDecisionObjectsOnLeg.begin(), updatedDecisionObjectsOnLeg.end(), EL) == updatedDecisionObjectsOnLeg.end()) {
240 ATH_MSG_VERBOSE("Keeping on leg " << HLT::Identifier(id) << " the Decision Object container:" << EL.dataID() << ", index:" << EL.index());
241 updatedDecisionObjectsOnLeg.push_back(EL);
242 update = true;
243 }
244 }
245 }
246 // only update those concerning this tool ID
247 if (update){
248 it.second = std::move(updatedDecisionObjectsOnLeg);
249 }
250 }
251}
252
253void ComboHypoToolBase::eraseFromLegDecisionsMap(Combo::LegDecisionsMap& passingLegs) const {
254 for (auto& it : passingLegs) {
255 DecisionID id = it.first;
256 if (id == m_decisionId or (isLegId(id) and getIDFromLeg(id) == m_decisionId)) {
257 const size_t nDecisionObjects = it.second.size();
258 it.second.clear();
259 ATH_MSG_VERBOSE("-- Removed " << nDecisionObjects << " from " << id);
260 }
261 }
262}
263
264void ComboHypoToolBase::printDebugInformation(const Combo::LegDecisionsMap& passingLegs) const {
265 ATH_MSG_DEBUG("ComboHypoToolBase: End of " << m_decisionId << ", passing elements are: ");
266 for (const auto& [id, ELV] : passingLegs) {
267 // Only print for this chain
268 if (id == m_decisionId or (isLegId(id) and m_decisionId == getIDFromLeg(id))) {
269 ATH_MSG_DEBUG("-- " << HLT::Identifier(id) << " with " << ELV.size() << " elements");
270 for (const auto& EL : ELV) {
271 ATH_MSG_DEBUG("-- -- container:" << EL.dataID() << ", index:" << EL.index());
272 }
273 }
274 }
275}
276
277
278bool ComboHypoToolBase::executeAlg(const std::vector<Combo::LegDecision>& /*combination*/, const EventContext& /*ctx*/) const {
279 ATH_MSG_ERROR("Do not use ComboHypoToolBase on its own, inherit this class and override executeAlg.");
280 return false;
281}
282
283StatusCode ComboHypoToolBase::decideOnSingleObject(Decision*, const std::vector<const TrigCompositeUtils::DecisionIDContainer*>&) const {
284 ATH_MSG_ERROR("Do not use ComboHypoToolBase on its own, inherit this class and override decideOnSingleObject.");
285 ATH_MSG_ERROR("NOTE: Only if you are also supplying your own decide(...) implimentation, or similar.");
286 ATH_MSG_ERROR("NOTE: Most uses cases should only need to override executeAlg(...).");
287 return StatusCode::FAILURE;
288}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Gaudi::Property< bool > m_modeOR
virtual bool executeAlg(const std::vector< Combo::LegDecision > &combination, const EventContext &ctx) const
Only a dummy implementation exists in ComboHypoToolBase.
StatusCode selectLegs(const Combo::LegDecisionsMap &IDCombMap, std::vector< std::vector< Combo::LegDecision > > &leg_decisions) const
Creates the per-leg vectors of Decision objects starting from the initial LegDecision map,...
virtual StatusCode decideOnSingleObject(TrigCompositeUtils::Decision *, const std::vector< const TrigCompositeUtils::DecisionIDContainer * > &) const
Alternate method called by BPhysics ComboHypoAlgs instead of the base method decide(....
void printDebugInformation(const Combo::LegDecisionsMap &passingLegs) const
Print the output of the tool, after having removed failed Decision Objects.
Gaudi::Property< size_t > m_combinationsThresholdWarn
void eraseFromLegDecisionsMap(Combo::LegDecisionsMap &passingLegs) const
For when the tool rejects all combinations.
HLT::Identifier m_decisionId
The DecisionID of the chain, obtained from the Tool's name.
ComboHypoToolBase(const std::string &type, const std::string &name, const IInterface *parent)
std::vector< HLT::Identifier > m_legDecisionIds
The DecisionIDs of the individual legs, derived from both m_decisionId and m_legMultiplicities.
Gaudi::Property< size_t > m_combinationsThresholdBreak
virtual HLT::Identifier decisionId() const
retrieves this ComboHypoTool's chain's decision ID
std::vector< int > m_legMultiplicities
The number of legs, and the required multiplicity on each leg.
virtual StatusCode decide(Combo::LegDecisionsMap &passingLegs, const EventContext &ctx) const override
retrieves the decisions associated to this decId, make their combinations and apply the algorithm
Gaudi::Property< bool > m_enableOverride
void updateLegDecisionsMap(const std::vector< std::vector< Combo::LegDecision > > &passing_comb, Combo::LegDecisionsMap &passingLegs) const
For when the tool accepts some/all combinations.
StatusCode setLegMultiplicity(const Combo::MultiplicityReqMap &multiplicityRequiredMap)
Sets the number of legs and the multiplicity required on each leg.
TrigCompositeUtils::DecisionID numeric() const
numeric ID
An ensemble of UniqueCombinationGenerator API description.
void add(const UniqueCombinationGenerator &gen)
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
This module defines the arguments passed from the BATCH driver to the BATCH worker.
It used to be useful piece of code for replacing actual SG with other store of similar functionality ...
HLT::Identifier createLegName(const HLT::Identifier &chainIdentifier, size_t counter)
Generate the HLT::Identifier which corresponds to a specific leg of a given chain.
unsigned int DecisionID
HLT::Identifier getIDFromLeg(const HLT::Identifier &legIdentifier)
Generate the HLT::Identifier which corresponds to the chain name from the leg name.
bool isLegId(const HLT::Identifier &legIdentifier)
Recognise whether the chain ID is a leg ID.