Belle II Software development
NeuroTrigger.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#include <framework/logging/Logger.h>
9#include <trg/cdc/NeuroTrigger.h>
10#include <trg/cdc/NeuroTriggerParameters.h>
11#include <trg/cdc/dataobjects/CDCTriggerMLPData.h>
12#include <cdc/geometry/CDCGeometryPar.h>
13#include <framework/gearbox/Const.h>
14#include <framework/gearbox/Unit.h>
15#include <framework/datastore/StoreObjPtr.h>
16#include <framework/datastore/StoreArray.h>
17#include <trg/cdc/dbobjects/CDCTriggerNeuroConfig.h>
18#include <trg/cdc/dataobjects/CDCTriggerTrack.h>
19#include <string>
20#include <cmath>
21#include <TFile.h>
22#include "boost/iostreams/filter/gzip.hpp"
23#include "boost/iostreams/filtering_streambuf.hpp"
24#include "boost/iostreams/filtering_stream.hpp"
25#include "boost/multi_array.hpp"
26#define BOOST_MULTI_ARRAY_NO_GENERATORS
27
28
29
30using namespace Belle2;
31using namespace CDC;
32using namespace std;
33
34void
36{
37 // check parameters
38 bool okay = true;
39 // ensure that length of lists matches number of sectors
40 if (p.nHidden.size() != 1 && p.nHidden.size() != p.nMLP) {
41 B2ERROR("Number of nHidden lists should be 1 or " << p.nMLP);
42 okay = false;
43 }
44 if (p.outputScale.size() != 1 && p.outputScale.size() != p.nMLP) {
45 B2ERROR("Number of outputScale lists should be 1 or " << p.nMLP);
46 okay = false;
47 }
48 bool rangeProduct = (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size() * p.SLpattern.size() == p.nMLP);
49 if (!rangeProduct) {
50 if (p.phiRangeUse.size() != 1 && p.phiRangeUse.size() != p.nMLP) {
51 B2ERROR("Number of phiRangeUse.lists should be 1 or " << p.nMLP);
52 okay = false;
53 }
54 if (p.invptRangeUse.size() != 1 && p.invptRangeUse.size() != p.nMLP) {
55 B2ERROR("Number of invptRangeUse.lists should be 1 or " << p.nMLP);
56 okay = false;
57 }
58 if (p.thetaRangeUse.size() != 1 && p.thetaRangeUse.size() != p.nMLP) {
59 B2ERROR("Number of thetaRangeUse.lists should be 1 or " << p.nMLP);
60 okay = false;
61 }
62 if (p.SLpattern.size() != 1 && p.SLpattern.size() != p.nMLP) {
63 B2ERROR("Number of SLpattern lists should be 1 or " << p.nMLP);
64 okay = false;
65 }
66 }
67 // ensure that length of maxHitsPerSL and SLpatternMask lists matches SLpattern
68 if (p.maxHitsPerSL.size() != 1 && p.maxHitsPerSL.size() != p.SLpattern.size()) {
69 B2ERROR("Number of maxHitsPerSL lists should be 1 or " << p.SLpattern.size());
70 okay = false;
71 }
72 if (p.SLpatternMask.size() != 1 && p.SLpatternMask.size() != p.SLpattern.size()) {
73 B2ERROR("Number of SLpatternMask lists should be 1 or " << p.SLpattern.size());
74 okay = false;
75 }
76 // ensure that number of target nodes is valid
77 if (p.targetZ.isSet() || p.targetTheta.isSet()) {
78 unsigned short nTarget = int(p.targetZ) + int(p.targetTheta);
79 if (nTarget < 1) {
80 B2ERROR("No outputs! Turn on either targetZ or targetTheta.");
81 okay = false;
82 }
83 }
84 // ensure that sector ranges are valid
85 for (unsigned iPhi = 0; iPhi < p.phiRangeUse.size(); ++iPhi) {
86 if (p.phiRangeUse[iPhi].size() != 2) {
87 B2ERROR("phiRangeUse should be exactly 2 values");
88 okay = false;
89 continue;
90 }
91 if (p.phiRangeUse[iPhi][0] >= p.phiRangeUse[iPhi][1]) {
92 B2ERROR("phiRangeUse should be smaller than phiRangeUse");
93 okay = false;
94 }
95 if (p.phiRangeUse[iPhi][0] < -360. || p.phiRangeUse[iPhi][1] > 360. ||
96 (p.phiRangeUse[iPhi][1] - p.phiRangeUse[iPhi][0]) > 360.) {
97 B2ERROR("phiRangeUse should be in [-360, 360], with maximal width of 360");
98 okay = false;
99 }
100 }
101 for (unsigned iPt = 0; iPt < p.invptRangeUse.size(); ++iPt) {
102 if (p.invptRangeUse[iPt].size() != 2) {
103 B2ERROR("invptRangeUse should be exactly 2 values");
104 okay = false;
105 }
106 if (p.invptRangeUse[iPt][0] >= p.invptRangeUse[iPt][1]) {
107 B2ERROR("invptRangeUse should be smaller than invptRangeUse");
108 okay = false;
109 }
110 }
111 for (unsigned iTheta = 0; iTheta < p.thetaRangeUse.size(); ++iTheta) {
112 if (p.thetaRangeUse[iTheta].size() != 2) {
113 B2ERROR("thetaRangeUse should be exactly 2 values");
114 okay = false;
115 continue;
116 }
117 if (p.thetaRangeUse[iTheta][0] >= p.thetaRangeUse[iTheta][1]) {
118 B2ERROR("thetaRangeUse should be smaller than thetaRangeUse");
119 okay = false;
120 }
121 if (p.thetaRangeUse[iTheta][0] < 0. || p.thetaRangeUse[iTheta][1] > 180.) {
122 B2ERROR("thetaRangeUse should be in [0, 180]");
123 okay = false;
124 }
125 }
126 int nTarget = (int) p.targetZ + (int) p.targetTheta;
127 if (p.outputScale.size() == p.nMLP || p.outputScale.size() == 1) {
128 for (unsigned iScale = 0; iScale < p.outputScale.size(); ++iScale) {
129 if (p.outputScale[iScale].size() != 2 * static_cast<unsigned int>(nTarget)) {
130 B2ERROR("outputScale should be exactly " << 2 * nTarget << " values");
131 okay = false;
132 }
133 }
134 } else {
135 B2ERROR("the size of outputscale should be 1 or match the number of experts");
136 }
137 // ensure that train sectors are valid
138 if (p.phiRangeUse.size() != p.phiRangeTrain.size()) {
139 B2ERROR("Number of phiRangeUse.lists and phiRangeTrain lists should be equal.");
140 okay = false;
141 } else {
142 for (unsigned iPhi = 0; iPhi < p.phiRangeUse.size(); ++iPhi) {
143 if (p.phiRangeTrain[iPhi].size() != 2) {
144 B2ERROR("phiRangeTrain should be exactly 2 values.");
145 okay = false;
146 } else if (p.phiRangeTrain[iPhi][0] > p.phiRangeUse[iPhi][0] ||
147 p.phiRangeTrain[iPhi][1] < p.phiRangeUse[iPhi][1]) {
148 B2ERROR("phiRangeTrain should be wider than phiRangeUse.or equal.");
149 okay = false;
150 }
151 }
152 }
153 if (p.invptRangeUse.size() != p.invptRangeTrain.size()) {
154 B2ERROR("Number of invptRangeUse.lists and invptRangeTrain lists should be equal.");
155 okay = false;
156 } else {
157 for (unsigned iPt = 0; iPt < p.invptRangeUse.size(); ++iPt) {
158 if (p.invptRangeTrain[iPt].size() != 2) {
159 B2ERROR("invptRangeTrain should be exactly 2 values.");
160 okay = false;
161 } else if (p.invptRangeTrain[iPt][0] > p.invptRangeUse[iPt][0] ||
162 p.invptRangeTrain[iPt][1] < p.invptRangeUse[iPt][1]) {
163 B2ERROR("invptRangeTrain should be wider than invptRangeUse.or equal.");
164 okay = false;
165 }
166 }
167 }
168 if (p.thetaRangeUse.size() != p.thetaRangeTrain.size()) {
169 B2ERROR("Number of thetaRangeUse.lists and thetaRangeTrain lists should be equal.");
170 okay = false;
171 } else {
172 for (unsigned iTheta = 0; iTheta < p.thetaRangeUse.size(); ++iTheta) {
173 if (p.thetaRangeTrain[iTheta].size() != 2) {
174 B2ERROR("thetaRangeTrain should be exactly 2 values.");
175 okay = false;
176 } else if (p.thetaRangeTrain[iTheta][0] > p.thetaRangeUse[iTheta][0] ||
177 p.thetaRangeTrain[iTheta][1] < p.thetaRangeUse[iTheta][1]) {
178 B2ERROR("thetaRangeTrain should be wider than thetaRangeUse.or equal.");
179 okay = false;
180 }
181 }
182 }
183
184 if (!okay) return;
185
186 // initialize MLPs
187 m_MLPs.clear();
188 for (unsigned iMLP = 0; iMLP < p.nMLP; ++iMLP) {
189 //get indices for sector parameters
190 //this is important for cases, where we have experts specialized on different geometrical sectors as well as the pattern mask. since they are all in one array, we need the specific index of the expert. E.g. p.maxhitspersl cloud look like: [<expert-trained-on-slpattern0+thetabigger90>,<expert-trained-on-slpattern1+thetabigger90>,<expert-trained-on-slpattern0+thetasmaller90>,<expert-trained-on-slpattern1+thetasmaller90>]
191 vector<unsigned> indices = getRangeIndices(p, iMLP);
192 //get number of nodes for each layer
193 unsigned short maxHits = (p.maxHitsPerSL.size() == 1) ? p.maxHitsPerSL[0] : p.maxHitsPerSL[indices[3]];
194 unsigned long SLpattern = p.SLpattern[indices[3]];
195 unsigned long SLpatternMask = (p.SLpatternMask.size() == 1) ? p.SLpatternMask[0] : p.SLpatternMask[indices[3]];
196 unsigned short nInput = 27 * maxHits;
197 vector<NNTParam<float>> nHidden = (p.nHidden.size() == 1) ? p.nHidden[0] : p.nHidden[iMLP];
198 vector<unsigned short> nNodes = {nInput};
199 for (unsigned iHid = 0; iHid < nHidden.size(); ++iHid) {
200 if (p.multiplyHidden) {
201 nNodes.push_back(nHidden[iHid] * nNodes[0]);
202 } else {
203 nNodes.push_back(nHidden[iHid]);
204 }
205 }
206 nNodes.push_back(nTarget);
207 unsigned short targetVars = int(p.targetZ) + (int(p.targetTheta) << 1);
208 // the parameters stored in the parameterset are not advanced enough to be vectors, they can only be single data types. the workaround was to make every variable contained in the (nested) vector an NNTParam. for the further use, those have to be converted to float vecors, which is done by the tcastvector function.
209 vector<float> phiRangeUse = p.tcastvector<float>(p.phiRangeUse)[indices[0]];
210 vector<float> invptRangeUse = p.tcastvector<float>(p.invptRangeUse)[indices[1]];
211 vector<float> thetaRangeUse = p.tcastvector<float>(p.thetaRangeUse)[indices[2]];
212 vector<float> phiRangeTrain = p.tcastvector<float>(p.phiRangeTrain)[indices[0]];
213 vector<float> invptRangeTrain = p.tcastvector<float>(p.invptRangeTrain)[indices[1]];
214 vector<float> thetaRangeTrain = p.tcastvector<float>(p.thetaRangeTrain)[indices[2]];
215 B2DEBUG(50, "Ranges for sector " << iMLP
216 << ": phiRange [" << phiRangeUse[0] << ", " << phiRangeUse[1]
217 << "], invptRange [" << invptRangeUse[0] << ", " << invptRangeUse[1]
218 << "], thetaRange [" << thetaRangeUse[0] << ", " << thetaRangeUse[1]
219 << "], SLpattern " << SLpattern);
220 //get scaling values
221 vector<float> outputScale = (p.outputScale.size() == 1) ? p.tcastvector<float>(p.outputScale)[0] : p.tcastvector<float>
222 (p.outputScale)[iMLP];
223 //convert phi and theta from degree to radian
224 phiRangeUse[0] *= Unit::deg;
225 phiRangeUse[1] *= Unit::deg;
226 thetaRangeUse[0] *= Unit::deg;
227 thetaRangeUse[1] *= Unit::deg;
228 phiRangeTrain[0] *= Unit::deg;
229 phiRangeTrain[1] *= Unit::deg;
230 thetaRangeTrain[0] *= Unit::deg;
231 thetaRangeTrain[1] *= Unit::deg;
232 if (p.targetTheta) {
233 outputScale[2 * int(p.targetZ)] *= Unit::deg;
234 outputScale[2 * int(p.targetZ) + 1] *= Unit::deg;
235 }
236 //create new MLP
237 m_MLPs.push_back(CDCTriggerMLP(nNodes, targetVars, outputScale,
238 phiRangeUse, invptRangeUse, thetaRangeUse,
239 phiRangeTrain, invptRangeTrain, thetaRangeTrain,
240 maxHits, SLpattern, SLpatternMask, p.tMax,
241 p.et_option()));
242 }
243
244 if (p.IDRanges.size() == p.nMLP) {
245 for (auto exp : p.IDRanges) {
246 // first entry is the expert number, after that follow the idranges for all the superlayers
247 std::vector<float> irange = {exp.begin() + 1, exp.end()};
248 m_MLPs[static_cast<int>(exp[0])].setRelID(irange);
249 }
250 } else if (p.IDRanges.size() == 0) {
251 B2WARNING("idranges have not been initialized yet, did you forget it?");
252 } else {
253 B2ERROR("number of idranges should match the number of experts!");
254 }
255 // load some values from the geometry that will be needed for the input
256 setConstants();
257}
258
259vector<unsigned>
261{
262 // the indices can be used for both rangeuse and rangetrain, because the size of those arrays should be the same (it is checked in the initialize function).
263 std::vector<unsigned> indices = {0, 0, 0, 0};
264 if (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size() * p.SLpattern.size() == p.nMLP) {
265 indices[0] = isector % p.phiRangeUse.size();
266 indices[1] = (isector / p.phiRangeUse.size()) % p.invptRangeUse.size();
267 indices[2] = (isector / (p.phiRangeUse.size() * p.invptRangeUse.size())) % p.thetaRangeUse.size();
268 indices[3] = isector / (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size());
269 } else {
270 indices[0] = (p.phiRangeUse.size() == 1) ? 0 : isector;
271 indices[1] = (p.invptRangeUse.size() == 1) ? 0 : isector;
272 indices[2] = (p.thetaRangeUse.size() == 1) ? 0 : isector;
273 indices[3] = (p.SLpattern.size() == 1) ? 0 : isector;
274 }
275 return indices;
276}
277
278
279void
281{
283 int layerId = 3;
284 int nTS = 0;
285 for (int iSL = 0; iSL < 9; ++iSL) {
286 m_TSoffset[iSL] = nTS;
287 nTS += cdc.nWiresInLayer(layerId);
288 m_TSoffset[iSL + 1] = nTS;
289 for (int priority = 0; priority < 2; ++ priority) {
290 m_radius[iSL][priority] = cdc.senseWireR(layerId + priority);
291 }
292 layerId += (iSL > 0 ? 6 : 7);
293 }
294}
295
296void
297NeuroTrigger::initializeCollections(string hitCollectionName, string eventTimeName, const std::string& et_option)
298{
299 m_segmentHits.isRequired(hitCollectionName);
300 if (!((et_option == "fastestpriority") || (et_option == "etfhwin") || (et_option == "zero") || (et_option == "fastest2d"))) {
301 m_eventTime.isRequired(eventTimeName);
302 }
303 m_hitCollectionName = hitCollectionName;
304}
305
306void
307NeuroTrigger::initializeCollections(string hitCollectionName)
308{
309 m_segmentHits.isRequired(hitCollectionName);
310 m_hitCollectionName = hitCollectionName;
311}
312vector<int>
313NeuroTrigger::selectMLPsTrain(float phi0, float invpt, float theta)
314{
315 vector<int> indices = {};
316
317 if (m_MLPs.size() == 0) {
318 B2WARNING("Trying to select MLP before initializing MLPs.");
319 return indices;
320 }
321
322 // find all matching sectors
323 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
324 if (m_MLPs[isector].inPhiRangeTrain(phi0) && m_MLPs[isector].inInvptRangeTrain(invpt)
325 && m_MLPs[isector].inThetaRangeTrain(theta)) {
326 indices.push_back(isector);
327 }
328 }
329
330 if (indices.size() == 0) {
331 B2DEBUG(150, "Track does not match any sector.");
332 B2DEBUG(150, "invpt=" << invpt << ", phi=" << phi0 * 180. / M_PI << ", theta=" << theta * 180. / M_PI);
333 }
334
335 return indices;
336}
337vector<int>
338NeuroTrigger::selectMLPs(float phi0, float invpt, float theta)
339{
340 vector<int> indices = {};
341
342 if (m_MLPs.size() == 0) {
343 B2WARNING("Trying to select MLP before initializing MLPs.");
344 return indices;
345 }
346
347 // find all matching sectors
348 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
349 if (m_MLPs[isector].inPhiRangeUse(phi0) && m_MLPs[isector].inInvptRangeUse(invpt)
350 && m_MLPs[isector].inThetaRangeUse(theta)) {
351 indices.push_back(isector);
352 }
353 }
354
355 if (indices.size() == 0) {
356 B2DEBUG(150, "Track does not match any sector.");
357 B2DEBUG(150, "invpt=" << invpt << ", phi=" << phi0 * 180. / M_PI << ", theta=" << theta * 180. / M_PI);
358 }
359
360 return indices;
361}
362
363
364int
365NeuroTrigger::selectMLPbyPattern(std::vector<int>& MLPs, unsigned long pattern, const bool neurotrackinputmode)
366{
367 if (MLPs.size() == 0) {
368 return -1;
369 }
370
371 int bestIndex = -1;
372 for (unsigned i = 0; i < MLPs.size(); ++i) {
373 int isector = MLPs[i];
374 unsigned long sectorPattern = m_MLPs[isector].getSLpattern();
375 B2DEBUG(250, "hitPattern " << pattern << " sectorPattern " << sectorPattern);
376 // no hit pattern restriction -> keep looking for exact match
377 if (sectorPattern == 0) {
378 B2DEBUG(250, "found match for general sector");
379 bestIndex = isector;
380 }
381 // exact match -> keep this sector and stop searching
382 if (pattern == sectorPattern) {
383 B2DEBUG(250, "found match for hit pattern " << pattern);
384 bestIndex = isector;
385 break;
386 }
387 }
388
389 if (bestIndex < 0) {
390 if (neurotrackinputmode) {
391 B2DEBUG(150, "No sector found to match pattern, using sector 0" << pattern << ".");
392 bestIndex = 0;
393 } else {
394 B2DEBUG(150, "No sector found to match pattern " << pattern << ".");
395 }
396 }
397 return bestIndex;
398}
399
400
401void
403{
404 double omega = track.getOmega(); // signed track curvature
405 for (int iSL = 0; iSL < 9; ++iSL) {
406 for (int priority = 0; priority < 2; ++priority) {
407 m_alpha[iSL][priority] = (m_radius[iSL][priority] * abs(omega) < 2.) ?
408 asin(m_radius[iSL][priority] * omega / 2.) :
409 M_PI_2;
410 m_idRef[iSL][priority] = remainder(((track.getPhi0() - m_alpha[iSL][priority]) *
411 (m_TSoffset[iSL + 1] - m_TSoffset[iSL]) / 2. / M_PI),
412 (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
413 }
414 }
415}
416
417void
419{
420 //unsigned precisionPhi = m_precision[0];
421 //unsigned precisionAlpha = m_precision[0];
422 unsigned precisionPhiAlpha = m_precision[0];
423 unsigned precisionScale = m_precision[1];
424 unsigned precisionId = m_precision[2];
425
426 double omega = track.getOmega();
427 long phi = round(track.getPhi0() * (1 << precisionPhiAlpha));
428
429 for (int iSL = 0; iSL < 9; ++iSL) {
430 for (int priority = 0; priority < 2; ++priority) {
431 // LUT, calculated on the fly here
432 m_alpha[iSL][priority] =
433 (m_radius[iSL][priority] * abs(omega) < 2) ?
434 round(asin(m_radius[iSL][priority] * omega / 2) * (1 << precisionPhiAlpha)) :
435 round(M_PI_2 * (1 << precisionPhiAlpha));
436 long dphi = phi - (long(m_alpha[iSL][priority]));
437 m_idRef[iSL][priority] =
438 long(dphi * round((m_TSoffset[iSL + 1] - m_TSoffset[iSL]) / 2. / M_PI * (1 << precisionScale)) /
439 (long(1) << (precisionPhiAlpha + precisionScale - precisionId)));
440 // unscale after rounding
441 m_alpha[iSL][priority] /= (1 << precisionPhiAlpha);
442 m_idRef[iSL][priority] /= (1 << precisionId);
443 }
444 }
445}
446
447double
449{
450 int iSL = hit.getISuperLayer();
451 int priority = hit.getPriorityPosition();
452 // (((priority >> 1) & 1) - (priority & 1)) is 0, -1, 1, 0 for priority = 0, 1, 2, 3
453 double relId = hit.getSegmentID() + 0.5 * (((priority >> 1) & 1) - (priority & 1))
454 - m_TSoffset[iSL] - m_idRef[iSL][int(priority < 3)];
455 relId = remainder(relId, (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
456 return relId;
457}
458
459
460int
461NeuroTrigger::getLowestTime(unsigned isector, RelationVector<CDCTriggerSegmentHit> Hits, bool onlyAxials = false)
462{
463 int tlow = 9999;
464 B2DEBUG(200, "looping over axials:");
465 for (unsigned ihit = 0; ihit < Hits.size(); ++ihit) {
466 // skip hits with negative relation weight (not selected in finder)
467 if (Hits.weight(ihit) < 0) continue;
468 unsigned short iSL = Hits[ihit]->getISuperLayer();
469 if (iSL % 2 == 1 && onlyAxials) {continue;}
470 // get shortest time of relevant hits
471 B2DEBUG(200, " check drifttime: SL" + std::to_string(iSL) + ",ID = " + std::to_string(Hits[ihit]->getSegmentID()) + ", t = " +
472 std::to_string(Hits[ihit]->priorityTime()));
473 double relId = getRelId(*Hits[ihit]);
474 if (m_MLPs[isector].isRelevant(relId, iSL) &&
475 Hits[ihit]->priorityTime() < tlow) {
476 tlow = Hits[ihit]->priorityTime();
477 B2DEBUG(200, " new tlow: " << std::to_string(tlow));
478 }
479 }
480 return tlow;
481}
482
483
484void
485NeuroTrigger::getEventTime(unsigned isector, const CDCTriggerTrack& track, std::string et_option, const bool neuroinputmode = false)
486{
487
488 if (et_option != m_MLPs[isector].get_et_option()) {
489 B2DEBUG(20, "Used event time option is different to the one set in the MLP"
490 << LogVar("et_option", et_option) << LogVar("isector", isector)
491 << LogVar("et_option_mlp", m_MLPs[isector].get_et_option()));
492 }
493 if (et_option == "fastestpriority") {
494 B2DEBUG(200, "et_option is 'fastestpriority'");
495 m_T0 = 9999;
496 // find shortest time of related and relevant axial hits
498 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
499 m_T0 = getLowestTime(isector, Hits, false);
500 if (m_T0 < 9999) {
501 m_hasT0 = true;
502 } else {
503 m_T0 = 0;
504 m_hasT0 = false;
505 }
506
507 } else if (et_option == "fastest2d") {
508 m_T0 = 9999;
509 // find shortest time of related and relevant axial hits
511 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
512 m_T0 = getLowestTime(isector, Hits, true);
513 if (m_T0 < 9999) {
514 m_hasT0 = true;
515 } else {
516 m_T0 = 0;
517 m_hasT0 = false;
518 }
519 } else if (et_option == "zero") {
520 m_hasT0 = true;
521 m_T0 = 0;
522 } else if (et_option == "etf_only") {
523 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
524 if (hasT0) {
525 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
526 m_hasT0 = true;
527 } else {
528 B2ERROR("No ETF output, but forced to use ETF!");
529 m_hasT0 = false;
530 m_T0 = 0;
531 }
532 } else if (et_option == "etf_or_fastestpriority") {
533 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
534 if (hasT0) {
535 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
536 m_hasT0 = true;
537 } else {
538 getEventTime(isector, track, "fastestpriority", neuroinputmode);
551 }
552 } else if (et_option == "min_etf_fastestpriority") {
553 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
554 int T0_etf = 9999;
555 if (hasT0) {
556 T0_etf = m_eventTime->getBinnedEventT0(Const::CDC);
557 m_hasT0 = true;
558 }
559 getEventTime(isector, track, "fastestpriority", neuroinputmode);
560 if (m_T0 > T0_etf) {
561 m_T0 = T0_etf;
562 }
563 } else if (et_option == "etf_or_fastest2d") {
564 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
565 if (hasT0) {
566 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
567 m_hasT0 = true;
568 } else {
569 getEventTime(isector, track, "fastest2d", neuroinputmode);
582 }
583 } else if (et_option == "etf_or_zero") {
584 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
585 if (hasT0) {
586 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
587 m_hasT0 = true;
588 } else {
589 m_hasT0 = true;
590 m_T0 = 0;
591 }
592 } else if (et_option == "etfcc") {
593 if (track.getHasETFTime()) {
594 m_T0 = track.getETF_unpacked();
595 m_hasT0 = true;
596 } else {
597 m_T0 = 0;
598 m_hasT0 = false;
599 }
600
601 } else if (et_option == "etfcc_or_zero") {
602 if (track.getHasETFTime()) {
603 m_T0 = track.getETF_unpacked();
604 m_hasT0 = true;
605 } else {
606 m_T0 = 0;
607 m_hasT0 = true;
608 }
609
610 } else if (et_option == "etfcc_or_fastestpriority") {
611 if (track.getHasETFTime()) {
612 m_T0 = track.getETF_unpacked();
613 m_hasT0 = true;
614 } else {
615 getEventTime(isector, track, "fastestpriority", neuroinputmode);
616 }
617
618 } else if (et_option == "etfhwin") {
619 m_T0 = track.getETF_recalced();
620 m_hasT0 = true;
621
622 } else {
623 B2ERROR("No valid parameter for et_option (" << et_option << " )!");
624 }
625
626}
627
628unsigned long
629NeuroTrigger::getPureDriftThreshold(unsigned isector, const CDCTriggerTrack& track, const bool neurotrackinputmode)
630{
631 const CDCTriggerMLP& expert = m_MLPs[isector];
632 unsigned long driftth = 0;
633 vector<unsigned> nHits;
634 nHits.assign(9, 0);
635 // loop over axial hits related to input track
637 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
638 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
639 // skip hits with negative relation weight (not selected in finder)
640 if (axialHits.weight(ihit) < 0) {
641 continue;
642 }
643 unsigned short iSL = axialHits[ihit]->getISuperLayer();
644 // // skip stereo hits (should not be related to track, but check anyway)
645 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
646 // get priority time
647 int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
648 double relId = getRelId(*axialHits[ihit]);
649 if (t < 0 || t > expert.getTMax()) {
650 if (expert.isRelevant(relId, iSL)) {
651 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
652 driftth |= 1 << (8 - iSL + 9 * nHits[iSL]);
653 ++nHits[iSL];
654 }
655 }
656 }
657 }
658 if (!neurotrackinputmode) {
659 // loop over stereo hits
660 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
661 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
662 // skip axial hits
663 if (iSL % 2 == 0) continue;
664 // get priority time
665 int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
666 double relId = getRelId(*m_segmentHits[ihit]);
667 if (t < 0 || t > expert.getTMax()) {
668 if (expert.isRelevant(relId, iSL)) {
669 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
670 driftth |= 1 << (8 - iSL + 9 * nHits[iSL]);
671 ++nHits[iSL];
672 }
673 }
674 }
675 }
676 }
677 return driftth;
678}
679unsigned long NeuroTrigger::getCompleteHitPattern(unsigned isector, const CDCTriggerTrack& track, const bool neurotrackinputmode)
680{
681 const CDCTriggerMLP& expert = m_MLPs[isector];
682 unsigned long chitPattern = 0;
683 vector<unsigned> nHits;
684 nHits.assign(9, 0);
685 // loop over axial hits related to input track
687 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
688 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
689 // skip hits with negative relation weight (not selected in finder)
690 if (axialHits.weight(ihit) < 0) {
691 continue;
692 }
693 unsigned short iSL = axialHits[ihit]->getISuperLayer();
694 // // skip stereo hits (should not be related to track, but check anyway)
695 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
696 double relId = getRelId(*axialHits[ihit]);
697 if (expert.isRelevant(relId, iSL)) {
698 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
699 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
700 ++nHits[iSL];
701 }
702 }
703 }
704 if (!neurotrackinputmode) {
705 // loop over stereo hits
706 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
707 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
708 // skip axial hits
709 if (iSL % 2 == 0) continue;
710 // get priority time
711 double relId = getRelId(*m_segmentHits[ihit]);
712 if (expert.isRelevant(relId, iSL)) {
713 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
714 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
715 ++nHits[iSL];
716 }
717 }
718 }
719 }
720 return chitPattern;
721}
722
723unsigned long
724NeuroTrigger::getInputPattern(unsigned isector, const CDCTriggerTrack& track, const bool neurotrackinputmode)
725{
726 const CDCTriggerMLP& expert = m_MLPs[isector];
727 unsigned long hitPattern = 0;
728 vector<unsigned> nHits;
729 nHits.assign(9, 0);
730 // loop over axial hits related to input track
732 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
733 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
734 // skip hits with negative relation weight (not selected in finder)
735 if (axialHits.weight(ihit) < 0) {
736 continue;
737 }
738 unsigned short iSL = axialHits[ihit]->getISuperLayer();
739 // // skip stereo hits (should not be related to track, but check anyway)
740 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
741 double relId = getRelId(*axialHits[ihit]);
742
743 if (expert.isRelevant(relId, iSL)) {
744 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
745 hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
746 ++nHits[iSL];
747 }
748 B2DEBUG(250, "hit in SL " << iSL);
749 } else {
750 B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
751 }
752 }
753 if (!neurotrackinputmode) {
754 // loop over stereo hits
755 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
756 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
757 // skip axial hits
758 if (iSL % 2 == 0) continue;
759 double relId = getRelId(*m_segmentHits[ihit]);
760 if (expert.isRelevant(relId, iSL)) {
761 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
762 hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
763 ++nHits[iSL];
764 }
765 B2DEBUG(250, "hit in SL " << iSL);
766 } else {
767 B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
768 }
769 }
770 }
771 B2DEBUG(250, "hitPattern " << hitPattern);
772 return hitPattern & expert.getSLpatternMask();
773}
774
775vector<unsigned>
776NeuroTrigger::selectHitsHWSim(unsigned isector, const CDCTriggerTrack& track)
777{
778 const CDCTriggerMLP& expert = m_MLPs[isector];
779 vector<unsigned> selectedHitIds = {};
780 // prepare vectors to keep best drift times, left/right and selected hit IDs
781 vector<int> tMin;
782 tMin.assign(expert.nNodesLayer(0), expert.getTMax());
783 vector<bool> LRknown;
784 LRknown.assign(expert.nNodesLayer(0), false);
785 vector<int> hitIds;
786 hitIds.assign(expert.nNodesLayer(0), -1);
787 vector<unsigned> nHits;
788 nHits.assign(9, 0);
789
790 // loop over all hits related to input track
792 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
793 B2DEBUG(250, "start hit loop over all related hits");
798 for (unsigned ihit = 0; ihit < allHits.size(); ++ihit) {
799 // skip hits with negative relation weight (not selected in finder)
800 if (allHits.weight(ihit) < 0) continue;
801 unsigned short iSL = allHits[ihit]->getISuperLayer();
802 if (expert.getSLpatternUnmasked() != 0 &&
803 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
804 B2DEBUG(250, "skipping hit in SL " << iSL);
805 continue;
806 }
807 // get priority time and apply time window cut
808 int t = (m_hasT0) ? allHits[ihit]->priorityTime() - m_T0 : 0;
809 if (t < 0) {
810 t = 0;
811 } else if (t > expert.getTMax()) {
812 t = expert.getTMax();
813 }
814 double relId = getRelId(*allHits[ihit]);
815 if (expert.isRelevant(relId, iSL)) {
816 // get reference hit (worst of existing hits)
817 unsigned short iRef = iSL;
818 if (expert.getMaxHitsPerSL() > 1) {
819 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
820 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
821 iRef += 9 * nHits[iSL];
822 ++nHits[iSL];
823 } else {
824 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
825 if ((LRknown[iRef] && !LRknown[compare]) ||
826 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
827 iRef = compare;
828 }
829 }
830 }
831 // choose best hit (LR known before LR unknown, then shortest drift time)
832 bool useHit = false;
833 if (LRknown[iRef]) {
834 useHit = (allHits[ihit]->LRknown() && t <= tMin[iRef]);
835 } else {
836 useHit = (allHits[ihit]->LRknown() || t <= tMin[iRef]);
837 }
838 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << allHits[ihit]->getLeftRight()
839 << " t " << t << " iRef " << iRef << " useHit " << useHit);
840 if (useHit) {
841 // keep drift time and LR
842 LRknown[iRef] = allHits[ihit]->LRknown();
843 tMin[iRef] = t;
844 hitIds[iRef] = allHits[ihit]->getArrayIndex();
845 }
846 }
847 }
848 // save selected hit Ids
849 for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
850 if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
851 }
852 return selectedHitIds;
853}
854vector<unsigned>
855NeuroTrigger::selectHits(unsigned isector, const CDCTriggerTrack& track,
856 bool returnAllRelevant)
857{
858 const CDCTriggerMLP& expert = m_MLPs[isector];
859 vector<unsigned> selectedHitIds = {};
860 // prepare vectors to keep best drift times, left/right and selected hit IDs
861 vector<int> tMin;
862 tMin.assign(expert.nNodesLayer(0), expert.getTMax());
863 vector<bool> LRknown;
864 LRknown.assign(expert.nNodesLayer(0), false);
865 vector<int> hitIds;
866 hitIds.assign(expert.nNodesLayer(0), -1);
867 vector<unsigned> nHits;
868 nHits.assign(9, 0);
869
870 // loop over axial hits related to input track
872 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
873 B2DEBUG(250, "start axial hit loop");
874 for (unsigned ihit = 0; ihit < axialHits.size(); ++ihit) {
875 // skip hits with negative relation weight (not selected in finder)
876 if (axialHits.weight(ihit) < 0) continue;
877 unsigned short iSL = axialHits[ihit]->getISuperLayer();
878 // skip stereo hits (should not be related to track, but check anyway)
879 if (iSL % 2 == 1) continue;
880 if (expert.getSLpatternUnmasked() != 0 &&
881 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
882 B2DEBUG(250, "skipping hit in SL " << iSL);
883 continue;
884 }
885 // get priority time and apply time window cut
886
887 int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
888 if (t < 0) {
889 t = 0;
890 } else if (t > expert.getTMax()) {
891 t = expert.getTMax();
892 }
893 double relId = getRelId(*axialHits[ihit]);
894 if (expert.isRelevant(relId, iSL)) {
895 // get reference hit (worst of existing hits)
896 unsigned short iRef = iSL;
897 if (expert.getMaxHitsPerSL() > 1) {
898 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
899 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
900 iRef += 9 * nHits[iSL];
901 ++nHits[iSL];
902 } else {
903 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
904 if ((LRknown[iRef] && !LRknown[compare]) ||
905 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
906 iRef = compare;
907 }
908 }
909 }
910 // choose best hit (LR known before LR unknown, then shortest drift time)
911 bool useHit = false;
912 if (LRknown[iRef]) {
913 useHit = (axialHits[ihit]->LRknown() && t <= tMin[iRef]);
914 } else {
915 useHit = (axialHits[ihit]->LRknown() || t <= tMin[iRef]);
916 }
917 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << axialHits[ihit]->getLeftRight()
918 << " t " << t << " iRef " << iRef << " useHit " << useHit);
919 if (useHit) {
920 // keep drift time and LR
921 LRknown[iRef] = axialHits[ihit]->LRknown();
922 tMin[iRef] = t;
923 hitIds[iRef] = axialHits[ihit]->getArrayIndex();
924 }
925 if (returnAllRelevant) selectedHitIds.push_back(axialHits[ihit]->getArrayIndex());
926 }
927 }
928
929 // loop over stereo hits, choosing up to MaxHitsPerSL per superlayer
930 B2DEBUG(250, "start stereo hit loop");
931 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
932 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
933 // skip axial hits
934 if (iSL % 2 == 0) continue;
935 if (expert.getSLpatternUnmasked() != 0 &&
936 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
937 B2DEBUG(250, "skipping hit in SL " << iSL);
938 continue;
939 }
940 // get priority time and apply time window cut
941 int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
942 if (t < 0) {
943 t = 0;
944 } else if (t > expert.getTMax()) {
945 t = expert.getTMax();
946 }
947 double relId = getRelId(*m_segmentHits[ihit]);
948 if (expert.isRelevant(relId, iSL)) {
949 // get reference hit (worst of existing hits)
950 unsigned short iRef = iSL;
951 if (expert.getMaxHitsPerSL() > 1) {
952 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
953 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
954 iRef += 9 * nHits[iSL];
955 ++nHits[iSL];
956 } else {
957 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
958 if ((LRknown[iRef] && !LRknown[compare]) ||
959 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
960 iRef = compare;
961 }
962 }
963 }
964 // choose best hit (LR known before LR unknown, then shortest drift time)
965 bool useHit = false;
966 if (LRknown[iRef]) {
967 useHit = (m_segmentHits[ihit]->LRknown() && t <= tMin[iRef]);
968 } else {
969 useHit = (m_segmentHits[ihit]->LRknown() || t <= tMin[iRef]);
970 }
971 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << m_segmentHits[ihit]->getLeftRight()
972 << " t " << t << " iRef " << iRef << " useHit " << useHit);
973 if (useHit) {
974 // keep drift time and LR
975 LRknown[iRef] = m_segmentHits[ihit]->LRknown();
976 tMin[iRef] = t;
977 hitIds[iRef] = ihit;
978 }
979 if (returnAllRelevant) selectedHitIds.push_back(ihit);
980 }
981 }
982
983 // save selected hit Ids
984 if (!returnAllRelevant) {
985 for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
986 if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
987 }
988 }
989 return selectedHitIds;
990}
991
992vector<float>
993NeuroTrigger::getInputVector(unsigned isector, const vector<unsigned>& hitIds)
994{
995 const CDCTriggerMLP& expert = m_MLPs[isector];
996 // prepare empty input vector and vectors to keep best drift times
997 vector<float> inputVector;
998 inputVector.assign(expert.nNodesLayer(0), 0.);
999 // convert hits to input values
1000 vector<unsigned> nHits;
1001 nHits.assign(9, 0);
1002 for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1003 int ihit = hitIds[ii];
1004 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1005 unsigned short iRef = iSL + 9 * nHits[iSL];
1006 ++nHits[iSL];
1007 int priot = m_segmentHits[ihit]->priorityTime();
1008 int t = (m_hasT0) ? priot - m_T0 : 0;
1009 if (t < 0) {
1010 t = 0;
1011 } else if (t > expert.getTMax()) {
1012 t = expert.getTMax();
1013 }
1014 int LR = m_segmentHits[ihit]->getLeftRight();
1015 double relId = getRelId(*m_segmentHits[ihit]);
1016 int priority = m_segmentHits[ihit]->getPriorityPosition();
1017 // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1018 inputVector[3 * iRef] = expert.scaleId(relId, iSL);
1019 float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1020 inputVector[3 * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1021 inputVector[3 * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1022 }
1023 return inputVector;
1024}
1025
1026vector<float>
1027NeuroTrigger::runMLP(unsigned isector, const vector<float>& input)
1028{
1029 const CDCTriggerMLP& expert = m_MLPs[isector];
1030 vector<float> weights = expert.getWeights();
1031 vector<float> layerinput = input;
1032 vector<float> layeroutput = {};
1033 unsigned iw = 0;
1034 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1035 //add bias input
1036 layerinput.push_back(1.);
1037 //prepare output
1038 layeroutput.clear();
1039 layeroutput.assign(expert.nNodesLayer(il), 0.);
1040 //loop over outputs
1041 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1042 //loop over inputs
1043 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1044 layeroutput[io] += layerinput[ii] * weights[iw++];
1045 }
1046 //apply activation function
1047 layeroutput[io] = tanh(layeroutput[io] / 2.);
1048 }
1049 //output is new input
1050 layerinput = layeroutput;
1051 }
1052 return expert.unscaleTarget(layeroutput);
1053}
1054
1055vector<float>
1056NeuroTrigger::runMLPFix(unsigned isector, vector<float> input)
1057{
1058 unsigned precisionInput = m_precision[3];
1059 unsigned precisionWeights = m_precision[4];
1060 unsigned precisionLUT = m_precision[5];
1061 unsigned precisionTanh = m_precision[3];
1062 unsigned dp = precisionInput + precisionWeights - precisionLUT;
1063
1064 const CDCTriggerMLP& expert = m_MLPs[isector];
1065 // transform inputs to fixed point (cut off to input precision)
1066 vector<long> inputFix(input.size(), 0);
1067 for (unsigned ii = 0; ii < input.size(); ++ii) {
1068 inputFix[ii] = long(input[ii] * (1 << precisionInput));
1069 }
1070 // transform weights to fixed point (round to weight precision)
1071 vector<float> weights = expert.getWeights();
1072 vector<long> weightsFix(weights.size(), 0);
1073 for (unsigned iw = 0; iw < weights.size(); ++iw) {
1074 weightsFix[iw] = long(round(weights[iw] * (1 << precisionWeights)));
1075 }
1076 // maximum input value for the tanh LUT
1077 unsigned xMax = unsigned(ceil(atanh(1. - 1. / (1 << (precisionTanh + 1))) *
1078 (1 << (precisionLUT + 1))));
1079
1080 // run MLP
1081 vector<long> layerinput = inputFix;
1082 vector<long> layeroutput = {};
1083 unsigned iw = 0;
1084 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1085 // add bias input
1086 layerinput.push_back(1 << precisionInput);
1087 // prepare output
1088 layeroutput.clear();
1089 layeroutput.assign(expert.nNodesLayer(il), 0);
1090 // loop over outputs
1091 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1092 // loop over inputs
1093 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1094 layeroutput[io] += layerinput[ii] * weightsFix[iw++];
1095 }
1096 // apply activation function -> LUT, calculated on the fly here
1097 unsigned long bin = abs(layeroutput[io]) >> dp;
1098 // correction to get symmetrical rounding errors
1099 float x = (bin + 0.5 - 1. / (1 << (dp + 1))) / (1 << precisionLUT);
1100 long tanhLUT = (bin < xMax) ? long(round(tanh(x / 2.) * (1 << precisionTanh))) : (1 << precisionTanh);
1101 layeroutput[io] = (layeroutput[io] < 0) ? -tanhLUT : tanhLUT;
1102 }
1103 // output is new input
1104 layerinput = layeroutput;
1105 }
1106
1107 // transform output back to float before unscaling
1108 vector<float> output(layeroutput.size(), 0.);
1109 for (unsigned io = 0; io < output.size(); ++io) {
1110 output[io] = layeroutput[io] / float(1 << precisionTanh);
1111 }
1112 return expert.unscaleTarget(output);
1113}
1114
1115void
1116NeuroTrigger::save(const string& filename, const string& arrayname)
1117{
1118 B2INFO("Saving networks to file " << filename << ", array " << arrayname);
1119 TFile datafile(filename.c_str(), "UPDATE");
1120 TObjArray* MLPs = new TObjArray(m_MLPs.size());
1121 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
1122 MLPs->Add(&m_MLPs[isector]);
1123 }
1124 MLPs->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
1125 datafile.Close();
1126 MLPs->Clear();
1127 delete MLPs;
1128}
1129bool NeuroTrigger::loadIDHist(const std::string& filename)
1130{
1131 std::ifstream gzipfile(filename, ios_base::in | ios_base::binary);
1132 boost::iostreams::filtering_istream arrayStream;
1133 arrayStream.push(boost::iostreams::gzip_decompressor());
1134 arrayStream.push(gzipfile);
1136 if (gzipfile.is_open()) {
1137 while (arrayStream >> hline) {
1138 for (unsigned i = 0; i < 18; ++i) {
1139 m_MLPs[hline.exPert].relevantID[i] = hline.relID[i];
1140 }
1141 }
1142 } else { return false;}
1143 return true;
1144}
1145
1146
1147bool
1148NeuroTrigger::load(const string& filename, const string& arrayname)
1149{
1150 if (filename.size() < 1) {
1151 m_MLPs.clear();
1152 m_MLPs = m_cdctriggerneuroconfig->getMLPs();
1153 if (m_MLPs.size() == 0) {
1154 B2ERROR("Could not load Neurotrigger weights from database!");
1155 return false;
1156 }
1157 B2INFO("Loaded Neurotrigger MLP weights from database: " + m_cdctriggerneuroconfig->getNNName());
1158 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks from database");
1159 // load some values from the geometry that will be needed for the input
1160 setConstants();
1161 return true;
1162 } else {
1163 TFile datafile(filename.c_str(), "READ");
1164 if (!datafile.IsOpen()) {
1165 B2WARNING("Could not open file " << filename);
1166 return false;
1167 }
1168
1169 TObjArray* MLPs = (TObjArray*)datafile.Get(arrayname.c_str());
1170 if (!MLPs) {
1171 datafile.Close();
1172 B2WARNING("File " << filename << " does not contain key " << arrayname);
1173 return false;
1174 }
1175 m_MLPs.clear();
1176 for (int isector = 0; isector < MLPs->GetEntriesFast(); ++isector) {
1177 CDCTriggerMLP* expert = dynamic_cast<CDCTriggerMLP*>(MLPs->At(isector));
1178 if (expert) m_MLPs.push_back(*expert);
1179 else B2WARNING("Wrong type " << MLPs->At(isector)->ClassName() << ", ignoring this entry.");
1180 }
1181 MLPs->Clear();
1182 delete MLPs;
1183 datafile.Close();
1184 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks");
1185
1186 B2INFO("Loaded Neurotrigger MLP weights from file: " + filename);
1187 // load some values from the geometry that will be needed for the input
1188 setConstants();
1189
1190 return true;
1191 }
1192}
bool hasBinnedEventT0(const Const::EDetector detector) const
Check if one of the detectors in the given set has a binned t0 estimation.
int getBinnedEventT0(const Const::EDetector detector) const
Return the stored binned event t0 for the given detector or 0 if nothing stored.
Class to keep all parameters of an expert MLP for the neuro trigger.
Definition: CDCTriggerMLP.h:20
Combination of several CDCHits to a track segment hit for the trigger.
Track created by the CDC trigger.
The Class for CDC Geometry Parameters.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
Class to represent a complete set to describe a Neurotrigger.
unsigned long getPureDriftThreshold(unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
Get the drift threshold bits, where the time of the TS was outside of the accepted time window and th...
double m_alpha[9][2]
2D crossing angle of current track
Definition: NeuroTrigger.h:301
std::vector< CDCTriggerMLP > m_MLPs
List of networks.
Definition: NeuroTrigger.h:293
double getRelId(const CDCTriggerSegmentHit &hit)
Calculate phi position of a hit relative to 2D track (scaled to number of wires).
DBObjPtr< CDCTriggerNeuroConfig > m_cdctriggerneuroconfig
get NNT payload from database.
Definition: NeuroTrigger.h:322
void initialize(const Parameters &p)
Set parameters and get some network independent parameters.
std::vector< unsigned > getRangeIndices(const NeuroTriggerParameters &p, unsigned isector)
Get indices for sector ranges in parameter lists.
std::vector< int > selectMLPs(float phi0, float invpt, float theta)
Select all matching expert MLPs based on the given track parameters.
void updateTrack(const CDCTriggerTrack &track)
Calculate 2D phi position and arclength for the given track and store them.
bool load(const std::string &filename, const std::string &arrayname="MLPs")
Load MLPs from file.
std::vector< unsigned > selectHits(unsigned isector, const CDCTriggerTrack &track, bool returnAllRelevant=false)
Select best hits for each super layer.
std::vector< float > runMLPFix(unsigned isector, std::vector< float > input)
Run an expert MLP with fixed point arithmetic.
void initializeCollections(std::string hitCollectionName, std::string eventTimeName, const std::string &et_option)
set the hit collection and event time to required and store the hit collection name
std::vector< float > getInputVector(unsigned isector, const std::vector< unsigned > &hitIds)
Calculate input values for MLP.
StoreArray< CDCTriggerSegmentHit > m_segmentHits
StoreArray containing the input track segment hits.
Definition: NeuroTrigger.h:316
int selectMLPbyPattern(std::vector< int > &MLPs, unsigned long pattern, const bool neurotrackinputmode)
Select one MLP from a list of sector indices.
std::vector< unsigned > m_precision
Fixed point precision in bit after radix point.
Definition: NeuroTrigger.h:313
std::string get_et_option()
Return value of m_et_option.
Definition: NeuroTrigger.h:229
unsigned long getInputPattern(unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
Calculate input pattern for MLP.
int m_T0
Event time of current event / track.
Definition: NeuroTrigger.h:303
void save(const std::string &filename, const std::string &arrayname="MLPs")
Save MLPs to file.
void updateTrackFix(const CDCTriggerTrack &track)
Calculate 2D phi position and arclength for the given track and store them.
std::vector< unsigned > selectHitsHWSim(unsigned isector, const CDCTriggerTrack &track)
Select hits for each super layer from the ones related to input track.
bool loadIDHist(const std::string &filename)
function to load idhist from file
std::vector< int > selectMLPsTrain(float phi0, float invpt, float theta)
Select all matching expert MLPs based on the given track parameters.
void setConstants()
Loads parameters from the geometry and precalculates some constants that will be needed.
unsigned m_TSoffset[10]
Number of track segments up to super layer.
Definition: NeuroTrigger.h:297
double m_radius[9][2]
Radius of the CDC layers with priority wires (2 per super layer)
Definition: NeuroTrigger.h:295
StoreObjPtr< BinnedEventT0 > m_eventTime
StoreObjPtr containing the event time.
Definition: NeuroTrigger.h:318
unsigned long getCompleteHitPattern(unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
Get complete hit pattern of neurotrack.
void getEventTime(unsigned isector, const CDCTriggerTrack &track, std::string et_option, const bool)
Read out the event time and store it.
int getLowestTime(unsigned isector, RelationVector< CDCTriggerSegmentHit > Hits, bool onlyAxials)
helper function to get the fastest priority time of given ts array
double m_idRef[9][2]
2D phi position of current track scaled to number of wires
Definition: NeuroTrigger.h:299
std::string m_hitCollectionName
Name of the StoreArray containing the input track segment hits.
Definition: NeuroTrigger.h:320
std::vector< float > runMLP(unsigned isector, const std::vector< float > &input)
Run an expert MLP.
bool m_hasT0
Flag to show if stored event time is valid.
Definition: NeuroTrigger.h:305
Class for type safe access to objects that are referred to in relations.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
bool isValid() const
Check whether the object was created.
Definition: StoreObjPtr.h:111
static const double deg
degree to radians
Definition: Unit.h:109
Class to store variables with their name which were sent to the logging service.
Abstract base class for different kinds of events.
STL namespace.