Skip to content

Commit ad57ddf

Browse files
author
Changhwan Choi
committed
Jet GNN inference, track-origin/vertex definition
1 parent ac87630 commit ad57ddf

7 files changed

Lines changed: 712 additions & 178 deletions

File tree

‎PWGJE/Core/JetTaggingUtilities.h‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,9 +1130,13 @@ void analyzeJetTrackInfo4GNNwExtra(AnalysisJet const& analysisJet, AnyTracks con
11301130

11311131
// Looping over the track info and putting them in the input vector (for GNN b-jet tagging)
11321132
template <typename AnalysisJet, typename AnyTracks>
1133-
void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, std::vector<std::vector<float>>& tracksParams, float trackPtMin = 0.5, float trackDcaXYMax = 10.0, float trackDcaZMax = 10.0, int64_t nMaxConstit = 40)
1133+
void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, std::vector<std::vector<float>>& tracksParams, float trackPtMin = 0.5, float trackDcaXYMax = 10.0, float trackDcaZMax = 10.0, int64_t nMaxConstit = 40, std::vector<std::size_t>* constituentIndices = nullptr)
11341134
{
1135-
for (const auto& constituent : analysisJet.template tracks_as<AnyTracks>()) {
1135+
// Optionally retain constituent-array positions in exactly the model-node
1136+
// order, including replacements when the maximum track count is exceeded.
1137+
auto const tracks = analysisJet.template tracks_as<AnyTracks>();
1138+
for (std::size_t constituentIndex = 0; constituentIndex < tracks.size(); ++constituentIndex) {
1139+
auto const& constituent = tracks[constituentIndex];
11361140

11371141
if (constituent.pt() < trackPtMin || !trackAcceptanceWithDca(constituent, trackDcaXYMax, trackDcaZMax)) {
11381142
continue;
@@ -1142,15 +1146,22 @@ void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /*
11421146

11431147
if (static_cast<int64_t>(tracksParams.size()) < nMaxConstit) {
11441148
tracksParams.emplace_back(std::vector<float>{constituent.pt(), constituent.phi(), constituent.eta(), static_cast<float>(constituent.sign()), std::abs(constituent.dcaXY()) * sign, constituent.sigmadcaXY(), std::abs(constituent.dcaZ()) * sign, constituent.sigmadcaZ()});
1149+
if (constituentIndices) {
1150+
constituentIndices->push_back(constituentIndex);
1151+
}
11451152
} else {
11461153
// If there are more than nMaxConstit constituents in the jet, select only nMaxConstit constituents with the highest DCA_XY significance.
11471154
size_t minIdx = 0;
11481155
for (size_t i = 0; i < tracksParams.size(); ++i) {
11491156
if (tracksParams[i][4] / tracksParams[i][5] < tracksParams[minIdx][4] / tracksParams[minIdx][5])
11501157
minIdx = i;
11511158
}
1152-
if (std::abs(constituent.dcaXY()) * sign / constituent.sigmadcaXY() > tracksParams[minIdx][4] / tracksParams[minIdx][5])
1159+
if (std::abs(constituent.dcaXY()) * sign / constituent.sigmadcaXY() > tracksParams[minIdx][4] / tracksParams[minIdx][5]) {
11531160
tracksParams[minIdx] = std::vector<float>{constituent.pt(), constituent.phi(), constituent.eta(), static_cast<float>(constituent.sign()), std::abs(constituent.dcaXY()) * sign, constituent.sigmadcaXY(), std::abs(constituent.dcaZ()) * sign, constituent.sigmadcaZ()};
1161+
if (constituentIndices) {
1162+
(*constituentIndices)[minIdx] = constituentIndex;
1163+
}
1164+
}
11541165
}
11551166
}
11561167
}
@@ -1241,6 +1252,33 @@ T getDb(const std::vector<T>& logits, double fC = 0.018)
12411252
return std::log(softmaxLogits[0] / (fC * softmaxLogits[1] + (1. - fC) * softmaxLogits[2]));
12421253
}
12431254

1255+
// Track-origin discriminant. Logit order: [background, charm, beauty, PV, other SV].
1256+
template <typename T>
1257+
T getTrackDb(const std::vector<T>& trackLogits, double trackFC)
1258+
{
1259+
constexpr std::size_t NTrackOrigins = 5;
1260+
if (trackLogits.size() != NTrackOrigins) {
1261+
LOG(fatal) << "getTrackDb expects 5 track-origin logits, received " << trackLogits.size();
1262+
}
1263+
1264+
std::array<T, NTrackOrigins> softmaxLogits;
1265+
T maxLogit = *std::max_element(trackLogits.begin(), trackLogits.end());
1266+
T sumLogit = 0.;
1267+
for (std::size_t i = 0; i < NTrackOrigins; ++i) {
1268+
softmaxLogits[i] = std::exp(trackLogits[i] - maxLogit);
1269+
sumLogit += softmaxLogits[i];
1270+
}
1271+
for (auto& probability : softmaxLogits) {
1272+
probability /= sumLogit;
1273+
}
1274+
1275+
T denominator = trackFC * softmaxLogits[1] + (1. - trackFC) * (softmaxLogits[0] + softmaxLogits[3] + softmaxLogits[4]);
1276+
if (denominator == 0.) {
1277+
LOG(debug) << "jettaggingutilities::getTrackDb, zero denominator";
1278+
}
1279+
return std::log(softmaxLogits[2] / denominator);
1280+
}
1281+
12441282
}; // namespace jettaggingutilities
12451283

12461284
#endif // PWGJE_CORE_JETTAGGINGUTILITIES_H_

‎PWGJE/DataModel/JetTagging.h‎

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ JETTAGGING_TABLES_DEF(Full, "F");
170170
// Tables for full inference of GNN-based b-jet tagger (including track origin, vertex predictions)
171171
namespace jetgnnindex
172172
{
173-
DECLARE_SOA_INDEX_COLUMN(JTrack, track); //! index in JTracks of this GNN input track (not the parent AOD Tracks index)
174-
DECLARE_SOA_COLUMN(TrackIndex, trackIndex, uint32_t); //! node index in the GNN input after track selection/truncation; need not match the constituent-array position
173+
DECLARE_SOA_INDEX_COLUMN(JTrack, track); //! index in JTracks of this GNN input track (not the parent AOD Tracks index)
174+
DECLARE_SOA_COLUMN(TrackIndex, trackIndex, uint8_t); //! node index (0..255) in the GNN input after track selection/truncation; need not match the constituent-array position
175175
// DECLARE_SOA_COLUMN(EdgeIndex, edgeIndex, uint8_t); //! unique in-jet edge index for the (i,j) constituent-track pair, i<j, triangular order i*(2N-i-1)/2+(j-i-1)
176176
// DECLARE_SOA_DYNAMIC_COLUMN(TrackIds, trackIds, //! inverse of the triangular edge indexing above: for n constituent tracks, recovers the (i,j) in-jet track index pair (i<j) this edge connects
177177
// [](uint8_t edgeIndex, int n) -> std::pair<uint8_t, uint8_t> {
@@ -188,55 +188,54 @@ DECLARE_SOA_COLUMN(TrackIndex, trackIndex, uint32_t); //! node index in the GNN
188188

189189
namespace jetgnnlabel
190190
{
191-
DECLARE_SOA_COLUMN(Origin, origin, uint8_t); //! trkOrigin from vertexClustering() (0: non-physical primary, 1: charm, 2: beauty, 3: primary vertex, 4: other secondary)
192-
DECLARE_SOA_COLUMN(VertexIndex, vertexIndex, uint8_t); //! trkVtxIndex from vertexClustering() (0: primary vertex, >0: secondary vertex, ranked by distance to PV)
191+
DECLARE_SOA_COLUMN(Origin, origin, uint8_t); //! trkOrigin from vertexClustering() (0: non-physical primary, 1: charm, 2: beauty, 3: primary vertex, 4: other secondary)
192+
DECLARE_SOA_COLUMN(VertexIndex, vertexIndex, int8_t); //! trkVtxIndex from vertexClustering() (-1: unassigned, 0: primary vertex, >0: secondary vertex, ranked by distance to PV)
193193
// DECLARE_SOA_COLUMN(SameVertex, sameVertex, bool); //! true if the (i,j) constituent-track pair of this edge share the same in-jet vertex cluster (same trkVtxIndex from vertexClustering())
194194
} // namespace jetgnnlabel
195195

196196
namespace jetgnnscore
197197
{
198-
DECLARE_SOA_COLUMN(ScoreML, scoreML, float); //! GNN prediction score (track/node level, or edge level symmetrised e.g. averaged from the N*N GNN edge matrix)
199-
DECLARE_SOA_COLUMN(OriginScores, originScores, std::vector<float>); //! one score per origin class, in jetgnnlabel::Origin order (0..4); producer defines logits/probabilities
198+
DECLARE_SOA_COLUMN(ScoreML, scoreML, float); //! GNN discriminant: one track-origin D_b per GnnTracks row
200199
} // namespace jetgnnscore
201200

202-
// GNNTrack has one row per evaluated (jet, track) pair. Its jet and track indices
201+
// GnnTracks has one row per evaluated (jet, track) pair. Its jet and track indices
203202
// identify the pair independently of the ordering in JetConstituents.
204-
// GNNTrackIndices is a positional jet extension: write one row for every jet in
203+
// GnnTrackIds is a positional jet extension: write one row for every jet in
205204
// Jets order, including an empty array for jets without evaluated tracks. Store
206-
// GNNTrack row indices (e.g. the backbone producer's lastIndex()), not JTrackIds.
207-
#define DECLARE_JET_GNNTRACKINDEX_TABLE(_jet_type_, _description_) \
208-
namespace _jet_type_##gnntrackindextable \
209-
{ \
210-
DECLARE_SOA_INDEX_COLUMN(_jet_type_, jet); \
211-
} \
212-
DECLARE_SOA_TABLE(_jet_type_##GNNTrack, "AOD", _description_, \
213-
o2::soa::Index<>, \
214-
_jet_type_##gnntrackindextable::_jet_type_##Id, \
215-
jetgnnindex::JTrackId, \
216-
jetgnnindex::TrackIndex); \
217-
namespace _jet_type_##gnntrackindices \
218-
{ \
219-
DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL(_jet_type_##GNNTrack, tracksGnn, int32_t, _jet_type_##GNNTrack, ""); \
220-
} \
221-
DECLARE_SOA_TABLE(_jet_type_##GNNTrackIndices, "AOD", _description_ "I", \
222-
_jet_type_##gnntrackindices::_jet_type_##GNNTrackIds);
205+
// GnnTracks row indices (e.g. the backbone producer's lastIndex()), not JTrackIds.
206+
#define DECLARE_JET_GNNTRACKINDEX_TABLE(_jet_type_, _description_) \
207+
namespace _jet_type_##gnntrackindextable \
208+
{ \
209+
DECLARE_SOA_INDEX_COLUMN(_jet_type_, jet); \
210+
} \
211+
DECLARE_SOA_TABLE(_jet_type_##GnnTracks, "AOD", _description_, \
212+
o2::soa::Index<>, \
213+
_jet_type_##gnntrackindextable::_jet_type_##Id, \
214+
jetgnnindex::JTrackId, \
215+
jetgnnindex::TrackIndex); \
216+
namespace _jet_type_##gnntrackindices \
217+
{ \
218+
DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL(_jet_type_##GnnTrack, tracksGnn, int32_t, _jet_type_##GnnTracks, ""); \
219+
} \
220+
DECLARE_SOA_TABLE(_jet_type_##GnnTrackIds, "AOD", _description_ "I", \
221+
_jet_type_##gnntrackindices::_jet_type_##GnnTrackIds);
223222

224223
// #define DECLARE_JET_GNNEDGEINDEX_TABLE(_jet_type_, _description_) \
225-
// namespace _jet_type_##gnnedgeindextable \
226-
// { \
227-
// DECLARE_SOA_INDEX_COLUMN(_jet_type_, jet); \
228-
// } \
229-
// DECLARE_SOA_TABLE(_jet_type_##GNNEdge, "AOD", _description_, \
230-
// o2::soa::Index<>, \
231-
// _jet_type_##gnnedgeindextable::_jet_type_##Id, \
232-
// jetgnnindex::EdgeIndex, \
233-
// jetgnnindex::TrackIds<jetgnnindex::EdgeIndex>); \
234-
// namespace _jet_type_##gnnedgeindices \
235-
// { \
236-
// DECLARE_SOA_ARRAY_INDEX_COLUMN(_jet_type_##GNNEdge, edges); \
237-
// } \
238-
// DECLARE_SOA_TABLE(_jet_type_##GNNEdgeIndices, "AOD", _description_ "I", \
239-
// _jet_type_##gnnedgeindices::_jet_type_##GNNEdgeIds);
224+
namespace _jet_type_##gnnedgeindextable \
225+
{ \
226+
DECLARE_SOA_INDEX_COLUMN(_jet_type_, jet); \
227+
} \
228+
DECLARE_SOA_TABLE(_jet_type_##GnnEdges, "AOD", _description_, \
229+
o2::soa::Index<>, \
230+
_jet_type_##gnnedgeindextable::_jet_type_##Id, \
231+
jetgnnindex::EdgeIndex, \
232+
jetgnnindex::TrackIds<jetgnnindex::EdgeIndex>); \
233+
namespace _jet_type_##gnnedgeindices \
234+
{ \
235+
DECLARE_SOA_ARRAY_INDEX_COLUMN(_jet_type_##GnnEdge, edges); \
236+
} \
237+
DECLARE_SOA_TABLE(_jet_type_##GnnEdgeIds, "AOD", _description_ "I", \
238+
_jet_type_##gnnedgeindices::_jet_type_##GnnEdgeIds);
240239

241240
// In-jet track/edge index backbone tables (own the in-jet ordering + the back-reference to the original track)
242241
DECLARE_JET_GNNTRACKINDEX_TABLE(ChargedJet, "CJGNT");
@@ -248,38 +247,27 @@ DECLARE_JET_GNNTRACKINDEX_TABLE(ChargedMCDetectorLevelJet, "CDJGNT");
248247
// #undef DECLARE_JET_GNNEDGEINDEX_TABLE
249248

250249
// Truth labels from vertexClustering() (MC detector level only). Thin tables: join positionally with
251-
// ChargedMCDetectorLevelJetGNNTrack / ChargedMCDetectorLevelJetGNNEdge (same producer loop, same row order).
250+
// ChargedMCDetectorLevelJetGnnTracks / ChargedMCDetectorLevelJetGnnEdges (same producer loop, same row order).
251+
// TrackOriginDef must have exactly one row per GnnTracks backbone row, aligned
252+
// with GnnTrackOriginTags after GNN input selection/truncation. It is not a JTracks
253+
// extension: a JTrack used by multiple jets has a truth row for each backbone row.
254+
// tracksGnnIds() addresses this shared row position through a join containing
255+
// the backbone, the origin definition and the prediction scores.
256+
// VertexIndexDef uses the same GnnTracks row order and keeps the full-jet vertex
257+
// labels, without renumbering after GNN track selection/truncation.
252258
DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetTrackOriginDef, "AOD", "CDJTOD", jetgnnlabel::Origin);
253-
DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetVertexDef, "AOD", "CDJTVD", jetgnnlabel::VertexIndex);
259+
DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetVertexIndexDef, "AOD", "CDJTVD", jetgnnlabel::VertexIndex);
254260
// DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetEdgeDef, "AOD", "CDJEDF", jetgnnlabel::SameVertex);
255261

256262
// GNN track-origin outputs, data and MC detector level. Thin tables: write one
257-
// score vector per GNNTrack row, in the same row order as the backbone table.
258-
// The vector contains classes for this track, not scores for multiple tracks.
259-
DECLARE_SOA_TABLE(ChargedJetTrackTags, "AOD", "CJTRKT", jetgnnscore::OriginScores);
260-
DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetTrackTags, "AOD", "CDJTRKT", jetgnnscore::OriginScores);
263+
// scalar score per GnnTracks row, in the same row order as the backbone table.
264+
DECLARE_SOA_TABLE(ChargedJetGnnTrackOriginTags, "AOD", "CJTRKT", jetgnnscore::ScoreML);
265+
DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetGnnTrackOriginTags, "AOD", "CDJTRKT", jetgnnscore::ScoreML);
261266

262267
// GNN ScoreML outputs, edge level, data and MC detector level. Thin tables: join positionally with
263-
// ChargedJetGNNEdge / ChargedMCDetectorLevelJetGNNEdge.
264-
// DECLARE_SOA_TABLE(ChargedJetEdgeTags, "AOD", "CJEDGT", jetgnnscore::ScoreML);
265-
// DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetEdgeTags, "AOD", "CDJEDGT", jetgnnscore::ScoreML);
266-
267-
// Prediction joins do not require MC truth tables. Subscribe to the prediction
268-
// join and JTracks in the consuming task to bind both index levels:
269-
// for (auto const& prediction : jet.tracksGnn_as<ChargedJetGNNTrackPredictions>()) {
270-
// auto track = prediction.track_as<JTracks>();
271-
// auto scores = prediction.originScores();
272-
// }
273-
using ChargedJetGNNTrackPredictions = o2::soa::Join<ChargedJetGNNTrack, ChargedJetTrackTags>;
274-
using ChargedMCDetectorLevelJetGNNTrackPredictions = o2::soa::Join<ChargedMCDetectorLevelJetGNNTrack, ChargedMCDetectorLevelJetTrackTags>;
275-
using ChargedJetsWithGNNTrackOrigins = o2::soa::Join<ChargedJets, ChargedJetConstituents, ChargedJetGNNTrackIndices>;
276-
using ChargedMCDetectorLevelJetsWithGNNTrackOrigins = o2::soa::Join<ChargedMCDetectorLevelJets, ChargedMCDetectorLevelJetConstituents, ChargedMCDetectorLevelJetGNNTrackIndices>;
277-
278-
// Convenience joins including truth labels when available (MC detector level).
279-
using ChargedJetGNNTrackLabels = ChargedJetGNNTrackPredictions;
280-
// using ChargedJetGNNEdgeLabels = o2::soa::Join<ChargedJetGNNEdge, ChargedJetEdgeTags>;
281-
using ChargedMCDetectorLevelJetGNNTrackLabels = o2::soa::Join<ChargedMCDetectorLevelJetGNNTrack, ChargedMCDetectorLevelJetTrackOriginDef, ChargedMCDetectorLevelJetVertexDef, ChargedMCDetectorLevelJetTrackTags>;
282-
// using ChargedMCDetectorLevelJetGNNEdgeLabels = o2::soa::Join<ChargedMCDetectorLevelJetGNNEdge, ChargedMCDetectorLevelJetEdgeDef, ChargedMCDetectorLevelJetEdgeTags>;
268+
// ChargedJetGnnEdges / ChargedMCDetectorLevelJetGnnEdges.
269+
// DECLARE_SOA_TABLE(ChargedJetGnnEdgeTags, "AOD", "CJEDGT", jetgnnscore::ScoreML);
270+
// DECLARE_SOA_TABLE(ChargedMCDetectorLevelJetGnnEdgeTags, "AOD", "CDJEDGT", jetgnnscore::ScoreML);
283271

284272
} // namespace o2::aod
285273

‎PWGJE/TableProducer/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ o2physics_add_dpl_workflow(jet-taggerhf
8686
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore
8787
COMPONENT_NAME Analysis)
8888

89+
o2physics_add_dpl_workflow(jet-gnn-definition
90+
SOURCES jetGnnDefinition.cxx
91+
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore
92+
COMPONENT_NAME Analysis)
93+
8994
o2physics_add_dpl_workflow(jet-gnn-inference
9095
SOURCES jetGnnInference.cxx
9196
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore

0 commit comments

Comments
 (0)