Belle II Software  release-08-01-10
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 
30 using namespace Belle2;
31 using namespace CDC;
32 using namespace std;
33 
34 void
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 * 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 
259 vector<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 
279 void
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 
296 void
297 NeuroTrigger::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 
306 void
307 NeuroTrigger::initializeCollections(string hitCollectionName)
308 {
309  m_segmentHits.isRequired(hitCollectionName);
310  m_hitCollectionName = hitCollectionName;
311 }
312 vector<int>
313 NeuroTrigger::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 }
337 vector<int>
338 NeuroTrigger::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 
364 int
365 NeuroTrigger::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 
401 void
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 
417 void
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 
447 double
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 
460 int
461 NeuroTrigger::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 
484 void
485 NeuroTrigger::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 
628 unsigned long
629 NeuroTrigger::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 }
679 unsigned 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 
723 unsigned long
724 NeuroTrigger::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  // get priority time
742  int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
743  if (t < 0) {
744  t = 0;
745  } else if (t > expert.getTMax()) {
746  t = expert.getTMax();
747  }
748  double relId = getRelId(*axialHits[ihit]);
749 
750  if (expert.isRelevant(relId, iSL)) {
751  if (nHits[iSL] < expert.getMaxHitsPerSL()) {
752  hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
753  ++nHits[iSL];
754  }
755  B2DEBUG(250, "hit in SL " << iSL);
756  } else {
757  B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
758  }
759  }
760  if (!neurotrackinputmode) {
761  // loop over stereo hits
762  for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
763  unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
764  // skip axial hits
765  if (iSL % 2 == 0) continue;
766  // get priority time
767  int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
768  if (t < 0) {
769  t = 0;
770  } else if (t > expert.getTMax()) {
771  t = expert.getTMax();
772  }
773  double relId = getRelId(*m_segmentHits[ihit]);
774  if (expert.isRelevant(relId, iSL)) {
775  if (nHits[iSL] < expert.getMaxHitsPerSL()) {
776  hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
777  ++nHits[iSL];
778  }
779  B2DEBUG(250, "hit in SL " << iSL);
780  } else {
781  B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
782  }
783  }
784  }
785  B2DEBUG(250, "hitPattern " << hitPattern);
786  return hitPattern & expert.getSLpatternMask();
787 }
788 
789 vector<unsigned>
790 NeuroTrigger::selectHitsHWSim(unsigned isector, const CDCTriggerTrack& track)
791 {
792  const CDCTriggerMLP& expert = m_MLPs[isector];
793  vector<unsigned> selectedHitIds = {};
794  // prepare vectors to keep best drift times, left/right and selected hit IDs
795  vector<int> tMin;
796  tMin.assign(expert.nNodesLayer(0), expert.getTMax());
797  vector<bool> LRknown;
798  LRknown.assign(expert.nNodesLayer(0), false);
799  vector<int> hitIds;
800  hitIds.assign(expert.nNodesLayer(0), -1);
801  vector<unsigned> nHits;
802  nHits.assign(9, 0);
803 
804  // loop over all hits related to input track
806  track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
807  B2DEBUG(250, "start hit loop over all related hits");
812  for (unsigned ihit = 0; ihit < allHits.size(); ++ihit) {
813  // skip hits with negative relation weight (not selected in finder)
814  if (allHits.weight(ihit) < 0) continue;
815  unsigned short iSL = allHits[ihit]->getISuperLayer();
816  if (expert.getSLpatternUnmasked() != 0 &&
817  !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
818  B2DEBUG(250, "skipping hit in SL " << iSL);
819  continue;
820  }
821  // get priority time and apply time window cut
822  int t = (m_hasT0) ? allHits[ihit]->priorityTime() - m_T0 : 0;
823  if (t < 0) {
824  t = 0;
825  } else if (t > expert.getTMax()) {
826  t = expert.getTMax();
827  }
828  double relId = getRelId(*allHits[ihit]);
829  if (expert.isRelevant(relId, iSL)) {
830  // get reference hit (worst of existing hits)
831  unsigned short iRef = iSL;
832  if (expert.getMaxHitsPerSL() > 1) {
833  if (nHits[iSL] < expert.getMaxHitsPerSL() &&
834  (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
835  iRef += 9 * nHits[iSL];
836  ++nHits[iSL];
837  } else {
838  for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
839  if ((LRknown[iRef] && !LRknown[compare]) ||
840  (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
841  iRef = compare;
842  }
843  }
844  }
845  // choose best hit (LR known before LR unknown, then shortest drift time)
846  bool useHit = false;
847  if (LRknown[iRef]) {
848  useHit = (allHits[ihit]->LRknown() && t <= tMin[iRef]);
849  } else {
850  useHit = (allHits[ihit]->LRknown() || t <= tMin[iRef]);
851  }
852  B2DEBUG(250, "relevant wire SL " << iSL << " LR " << allHits[ihit]->getLeftRight()
853  << " t " << t << " iRef " << iRef << " useHit " << useHit);
854  if (useHit) {
855  // keep drift time and LR
856  LRknown[iRef] = allHits[ihit]->LRknown();
857  tMin[iRef] = t;
858  hitIds[iRef] = allHits[ihit]->getArrayIndex();
859  }
860  }
861  }
862  // save selected hit Ids
863  for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
864  if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
865  }
866  return selectedHitIds;
867 }
868 vector<unsigned>
869 NeuroTrigger::selectHits(unsigned isector, const CDCTriggerTrack& track,
870  bool returnAllRelevant)
871 {
872  const CDCTriggerMLP& expert = m_MLPs[isector];
873  vector<unsigned> selectedHitIds = {};
874  // prepare vectors to keep best drift times, left/right and selected hit IDs
875  vector<int> tMin;
876  tMin.assign(expert.nNodesLayer(0), expert.getTMax());
877  vector<bool> LRknown;
878  LRknown.assign(expert.nNodesLayer(0), false);
879  vector<int> hitIds;
880  hitIds.assign(expert.nNodesLayer(0), -1);
881  vector<unsigned> nHits;
882  nHits.assign(9, 0);
883 
884  // loop over axial hits related to input track
886  track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
887  B2DEBUG(250, "start axial hit loop");
888  for (unsigned ihit = 0; ihit < axialHits.size(); ++ihit) {
889  // skip hits with negative relation weight (not selected in finder)
890  if (axialHits.weight(ihit) < 0) continue;
891  unsigned short iSL = axialHits[ihit]->getISuperLayer();
892  // skip stereo hits (should not be related to track, but check anyway)
893  if (iSL % 2 == 1) continue;
894  if (expert.getSLpatternUnmasked() != 0 &&
895  !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
896  B2DEBUG(250, "skipping hit in SL " << iSL);
897  continue;
898  }
899  // get priority time and apply time window cut
900 
901  int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
902  if (t < 0) {
903  t = 0;
904  } else if (t > expert.getTMax()) {
905  t = expert.getTMax();
906  }
907  double relId = getRelId(*axialHits[ihit]);
908  if (expert.isRelevant(relId, iSL)) {
909  // get reference hit (worst of existing hits)
910  unsigned short iRef = iSL;
911  if (expert.getMaxHitsPerSL() > 1) {
912  if (nHits[iSL] < expert.getMaxHitsPerSL() &&
913  (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
914  iRef += 9 * nHits[iSL];
915  ++nHits[iSL];
916  } else {
917  for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
918  if ((LRknown[iRef] && !LRknown[compare]) ||
919  (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
920  iRef = compare;
921  }
922  }
923  }
924  // choose best hit (LR known before LR unknown, then shortest drift time)
925  bool useHit = false;
926  if (LRknown[iRef]) {
927  useHit = (axialHits[ihit]->LRknown() && t <= tMin[iRef]);
928  } else {
929  useHit = (axialHits[ihit]->LRknown() || t <= tMin[iRef]);
930  }
931  B2DEBUG(250, "relevant wire SL " << iSL << " LR " << axialHits[ihit]->getLeftRight()
932  << " t " << t << " iRef " << iRef << " useHit " << useHit);
933  if (useHit) {
934  // keep drift time and LR
935  LRknown[iRef] = axialHits[ihit]->LRknown();
936  tMin[iRef] = t;
937  hitIds[iRef] = axialHits[ihit]->getArrayIndex();
938  }
939  if (returnAllRelevant) selectedHitIds.push_back(axialHits[ihit]->getArrayIndex());
940  }
941  }
942 
943  // loop over stereo hits, choosing up to MaxHitsPerSL per superlayer
944  B2DEBUG(250, "start stereo hit loop");
945  for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
946  unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
947  // skip axial hits
948  if (iSL % 2 == 0) continue;
949  if (expert.getSLpatternUnmasked() != 0 &&
950  !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
951  B2DEBUG(250, "skipping hit in SL " << iSL);
952  continue;
953  }
954  // get priority time and apply time window cut
955  int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
956  if (t < 0) {
957  t = 0;
958  } else if (t > expert.getTMax()) {
959  t = expert.getTMax();
960  }
961  double relId = getRelId(*m_segmentHits[ihit]);
962  if (expert.isRelevant(relId, iSL)) {
963  // get reference hit (worst of existing hits)
964  unsigned short iRef = iSL;
965  if (expert.getMaxHitsPerSL() > 1) {
966  if (nHits[iSL] < expert.getMaxHitsPerSL() &&
967  (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
968  iRef += 9 * nHits[iSL];
969  ++nHits[iSL];
970  } else {
971  for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
972  if ((LRknown[iRef] && !LRknown[compare]) ||
973  (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
974  iRef = compare;
975  }
976  }
977  }
978  // choose best hit (LR known before LR unknown, then shortest drift time)
979  bool useHit = false;
980  if (LRknown[iRef]) {
981  useHit = (m_segmentHits[ihit]->LRknown() && t <= tMin[iRef]);
982  } else {
983  useHit = (m_segmentHits[ihit]->LRknown() || t <= tMin[iRef]);
984  }
985  B2DEBUG(250, "relevant wire SL " << iSL << " LR " << m_segmentHits[ihit]->getLeftRight()
986  << " t " << t << " iRef " << iRef << " useHit " << useHit);
987  if (useHit) {
988  // keep drift time and LR
989  LRknown[iRef] = m_segmentHits[ihit]->LRknown();
990  tMin[iRef] = t;
991  hitIds[iRef] = ihit;
992  }
993  if (returnAllRelevant) selectedHitIds.push_back(ihit);
994  }
995  }
996 
997  // save selected hit Ids
998  if (!returnAllRelevant) {
999  for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
1000  if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
1001  }
1002  }
1003  return selectedHitIds;
1004 }
1005 
1006 vector<float>
1007 NeuroTrigger::getInputVector(unsigned isector, const vector<unsigned>& hitIds)
1008 {
1009  const CDCTriggerMLP& expert = m_MLPs[isector];
1010  // prepare empty input vector and vectors to keep best drift times
1011  vector<float> inputVector;
1012  inputVector.assign(expert.nNodesLayer(0), 0.);
1013  // convert hits to input values
1014  vector<unsigned> nHits;
1015  nHits.assign(9, 0);
1016  for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1017  int ihit = hitIds[ii];
1018  unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1019  unsigned short iRef = iSL + 9 * nHits[iSL];
1020  ++nHits[iSL];
1021  int priot = m_segmentHits[ihit]->priorityTime();
1022  int t = (m_hasT0) ? priot - m_T0 : 0;
1023  if (t < 0) {
1024  t = 0;
1025  } else if (t > expert.getTMax()) {
1026  t = expert.getTMax();
1027  }
1028  int LR = m_segmentHits[ihit]->getLeftRight();
1029  double relId = getRelId(*m_segmentHits[ihit]);
1030  int priority = m_segmentHits[ihit]->getPriorityPosition();
1031  // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1032  inputVector[3 * iRef] = expert.scaleId(relId, iSL);
1033  float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1034  inputVector[3 * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1035  inputVector[3 * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1036  }
1037  return inputVector;
1038 }
1039 
1040 vector<float>
1041 NeuroTrigger::runMLP(unsigned isector, const vector<float>& input)
1042 {
1043  const CDCTriggerMLP& expert = m_MLPs[isector];
1044  vector<float> weights = expert.getWeights();
1045  vector<float> layerinput = input;
1046  vector<float> layeroutput = {};
1047  unsigned iw = 0;
1048  for (unsigned il = 1; il < expert.nLayers(); ++il) {
1049  //add bias input
1050  layerinput.push_back(1.);
1051  //prepare output
1052  layeroutput.clear();
1053  layeroutput.assign(expert.nNodesLayer(il), 0.);
1054  //loop over outputs
1055  for (unsigned io = 0; io < layeroutput.size(); ++io) {
1056  //loop over inputs
1057  for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1058  layeroutput[io] += layerinput[ii] * weights[iw++];
1059  }
1060  //apply activation function
1061  layeroutput[io] = tanh(layeroutput[io] / 2.);
1062  }
1063  //output is new input
1064  layerinput = layeroutput;
1065  }
1066  return expert.unscaleTarget(layeroutput);
1067 }
1068 
1069 vector<float>
1070 NeuroTrigger::runMLPFix(unsigned isector, vector<float> input)
1071 {
1072  unsigned precisionInput = m_precision[3];
1073  unsigned precisionWeights = m_precision[4];
1074  unsigned precisionLUT = m_precision[5];
1075  unsigned precisionTanh = m_precision[3];
1076  unsigned dp = precisionInput + precisionWeights - precisionLUT;
1077 
1078  const CDCTriggerMLP& expert = m_MLPs[isector];
1079  // transform inputs to fixed point (cut off to input precision)
1080  vector<long> inputFix(input.size(), 0);
1081  for (unsigned ii = 0; ii < input.size(); ++ii) {
1082  inputFix[ii] = long(input[ii] * (1 << precisionInput));
1083  }
1084  // transform weights to fixed point (round to weight precision)
1085  vector<float> weights = expert.getWeights();
1086  vector<long> weightsFix(weights.size(), 0);
1087  for (unsigned iw = 0; iw < weights.size(); ++iw) {
1088  weightsFix[iw] = long(round(weights[iw] * (1 << precisionWeights)));
1089  }
1090  // maximum input value for the tanh LUT
1091  unsigned xMax = unsigned(ceil(atanh(1. - 1. / (1 << (precisionTanh + 1))) *
1092  (1 << (precisionLUT + 1))));
1093 
1094  // run MLP
1095  vector<long> layerinput = inputFix;
1096  vector<long> layeroutput = {};
1097  unsigned iw = 0;
1098  for (unsigned il = 1; il < expert.nLayers(); ++il) {
1099  // add bias input
1100  layerinput.push_back(1 << precisionInput);
1101  // prepare output
1102  layeroutput.clear();
1103  layeroutput.assign(expert.nNodesLayer(il), 0);
1104  // loop over outputs
1105  for (unsigned io = 0; io < layeroutput.size(); ++io) {
1106  // loop over inputs
1107  for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1108  layeroutput[io] += layerinput[ii] * weightsFix[iw++];
1109  }
1110  // apply activation function -> LUT, calculated on the fly here
1111  unsigned long bin = abs(layeroutput[io]) >> dp;
1112  // correction to get symmetrical rounding errors
1113  float x = (bin + 0.5 - 1. / (1 << (dp + 1))) / (1 << precisionLUT);
1114  long tanhLUT = (bin < xMax) ? long(round(tanh(x / 2.) * (1 << precisionTanh))) : (1 << precisionTanh);
1115  layeroutput[io] = (layeroutput[io] < 0) ? -tanhLUT : tanhLUT;
1116  }
1117  // output is new input
1118  layerinput = layeroutput;
1119  }
1120 
1121  // transform output back to float before unscaling
1122  vector<float> output(layeroutput.size(), 0.);
1123  for (unsigned io = 0; io < output.size(); ++io) {
1124  output[io] = layeroutput[io] / float(1 << precisionTanh);
1125  }
1126  return expert.unscaleTarget(output);
1127 }
1128 
1129 void
1130 NeuroTrigger::save(const string& filename, const string& arrayname)
1131 {
1132  B2INFO("Saving networks to file " << filename << ", array " << arrayname);
1133  TFile datafile(filename.c_str(), "UPDATE");
1134  TObjArray* MLPs = new TObjArray(m_MLPs.size());
1135  for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
1136  MLPs->Add(&m_MLPs[isector]);
1137  }
1138  MLPs->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
1139  datafile.Close();
1140  MLPs->Clear();
1141  delete MLPs;
1142 }
1143 bool NeuroTrigger::loadIDHist(const std::string& filename)
1144 {
1145  std::ifstream gzipfile(filename, ios_base::in | ios_base::binary);
1146  boost::iostreams::filtering_istream arrayStream;
1147  arrayStream.push(boost::iostreams::gzip_decompressor());
1148  arrayStream.push(gzipfile);
1150  if (gzipfile.is_open()) {
1151  while (arrayStream >> hline) {
1152  for (unsigned i = 0; i < 18; ++i) {
1153  m_MLPs[hline.exPert].relevantID[i] = hline.relID[i];
1154  }
1155  }
1156  } else { return false;}
1157  return true;
1158 }
1159 
1160 
1161 bool
1162 NeuroTrigger::load(const string& filename, const string& arrayname)
1163 {
1164  if (filename.size() < 1) {
1165  m_MLPs.clear();
1166  m_MLPs = m_cdctriggerneuroconfig->getMLPs();
1167  if (m_MLPs.size() == 0) {
1168  B2ERROR("Could not load Neurotrigger weights from database!");
1169  return false;
1170  }
1171  B2INFO("Loaded Neurotrigger MLP weights from database: " + m_cdctriggerneuroconfig->getNNName());
1172  B2DEBUG(100, "loaded " << m_MLPs.size() << " networks from database");
1173  // load some values from the geometry that will be needed for the input
1174  setConstants();
1175  return true;
1176  } else {
1177  TFile datafile(filename.c_str(), "READ");
1178  if (!datafile.IsOpen()) {
1179  B2WARNING("Could not open file " << filename);
1180  return false;
1181  }
1182 
1183  TObjArray* MLPs = (TObjArray*)datafile.Get(arrayname.c_str());
1184  if (!MLPs) {
1185  datafile.Close();
1186  B2WARNING("File " << filename << " does not contain key " << arrayname);
1187  return false;
1188  }
1189  m_MLPs.clear();
1190  for (int isector = 0; isector < MLPs->GetEntriesFast(); ++isector) {
1191  CDCTriggerMLP* expert = dynamic_cast<CDCTriggerMLP*>(MLPs->At(isector));
1192  if (expert) m_MLPs.push_back(*expert);
1193  else B2WARNING("Wrong type " << MLPs->At(isector)->ClassName() << ", ignoring this entry.");
1194  }
1195  MLPs->Clear();
1196  delete MLPs;
1197  datafile.Close();
1198  B2DEBUG(100, "loaded " << m_MLPs.size() << " networks");
1199 
1200  B2INFO("Loaded Neurotrigger MLP weights from file: " + filename);
1201  // load some values from the geometry that will be needed for the input
1202  setConstants();
1203 
1204  return true;
1205  }
1206 }
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 getRelId(const CDCTriggerSegmentHit &hit)
Calculate phi position of a hit relative to 2D track (scaled to number of wires).
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.
int selectMLPbyPattern(std::vector< int > &MLPs, unsigned long pattern, const bool neurotrackinputmode)
Select one MLP from a list of sector indices.
unsigned long getInputPattern(unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
Calculate input pattern for MLP.
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 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
std::vector< float > runMLP(unsigned isector, const std::vector< float > &input)
Run an expert MLP.
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.
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.