Belle II Software  release-05-01-25
GRLNeuro.cc
1 #include <framework/logging/Logger.h>
2 #include <trg/grl/GRLNeuro.h>
3 #include <trg/grl/dataobjects/GRLMLP.h>
4 
5 #include <cdc/geometry/CDCGeometryPar.h>
6 #include <framework/gearbox/Const.h>
7 #include <framework/gearbox/Unit.h>
8 #include <framework/datastore/StoreObjPtr.h>
9 #include <framework/datastore/StoreArray.h>
10 #include <trg/cdc/dbobjects/CDCTriggerNeuroConfig.h>
11 #include <trg/cdc/dataobjects/CDCTriggerTrack.h>
12 #include <string>
13 #include <cmath>
14 #include <TFile.h>
15 
16 using namespace Belle2;
17 using namespace CDC;
18 using namespace std;
19 
20 void
22 {
23  // check parameters
24  bool okay = true;
25  // ensure that length of lists matches number of sectors
26  if (p.nHidden.size() != 1 && p.nHidden.size() != p.nMLP) {
27  B2ERROR("Number of nHidden lists should be 1 or " << p.nMLP);
28  okay = false;
29  }
30  if (p.outputScale.size() != 1 && p.outputScale.size() != p.nMLP) {
31  B2ERROR("Number of outputScale lists should be 1 or " << p.nMLP);
32  okay = false;
33  }
34  // ensure that number of target nodes is valid
35  unsigned short nTarget = int(p.targetresult);
36  if (nTarget < 1) {
37  B2ERROR("No outputs! Turn on targetresult.");
38  okay = false;
39  }
40  for (unsigned iScale = 0; iScale < p.outputScale.size(); ++iScale) {
41  if (p.outputScale[iScale].size() != 2 * nTarget) {
42  B2ERROR("outputScale should be exactly " << 2 * nTarget << " values");
43  okay = false;
44  }
45  }
46 
47  if (!okay) return;
48 
49  // initialize MLPs
50  m_MLPs.clear();
51  for (unsigned iMLP = 0; iMLP < p.nMLP; ++iMLP) {
53  //vector<unsigned> indices = getRangeIndices(p, iMLP);
54  //get number of nodes for each layer
55  //unsigned short nInput = 2*p.i_cdc_sector[iMLP] + 3*p.i_ecl_sector[iMLP];
56  unsigned short nInput = p.i_cdc_sector[iMLP] + p.i_ecl_sector[iMLP];
57  vector<float> nHidden = (p.nHidden.size() == 1) ? p.nHidden[0] : p.nHidden[iMLP];
58  vector<unsigned short> nNodes = {nInput};
59  for (unsigned iHid = 0; iHid < nHidden.size(); ++iHid) {
60  if (p.multiplyHidden) {
61  nNodes.push_back(nHidden[iHid] * nNodes[0]);
62  } else {
63  nNodes.push_back(nHidden[iHid]);
64  }
65  }
66  nNodes.push_back(nTarget);
67  unsigned short targetVars = int(p.targetresult);
68  //get scaling values
69  vector<float> outputScale = (p.outputScale.size() == 1) ? p.outputScale[0] : p.outputScale[iMLP];
70  //create new MLP
71  m_MLPs.push_back(GRLMLP(nNodes, targetVars, outputScale));
72  }
73  // load some values from the geometry that will be needed for the input
74 }
75 
76 //vector<unsigned>
77 //GRLNeuro::getRangeIndices(const Parameters& p, unsigned isector)
78 //{
79 // std::vector<unsigned> indices = {0, 0, 0, 0};
80 // if (p.phiRange.size() * p.invptRange.size() * p.thetaRange.size() * p.SLpattern.size() == p.nMLP) {
81 // indices[0] = isector % p.phiRange.size();
82 // indices[1] = (isector / p.phiRange.size()) % p.invptRange.size();
83 // indices[2] = (isector / (p.phiRange.size() * p.invptRange.size())) % p.thetaRange.size();
84 // indices[3] = isector / (p.phiRange.size() * p.invptRange.size() * p.thetaRange.size());
85 // } else {
86 // indices[0] = (p.phiRange.size() == 1) ? 0 : isector;
87 // indices[1] = (p.invptRange.size() == 1) ? 0 : isector;
88 // indices[2] = (p.thetaRange.size() == 1) ? 0 : isector;
89 // indices[3] = (p.SLpattern.size() == 1) ? 0 : isector;
90 // }
91 // return indices;
92 //}
93 
94 //void
95 //GRLNeuro::initializeCollections(string hitCollectionName, string eventTimeName, std::string et_option)
96 //{
97 // m_segmentHits.isRequired(hitCollectionName);
98 // if (!((et_option == "fastestpriority") || (et_option == "zero") || (et_option == "fastest2d"))) {
99 // m_eventTime.isRequired(eventTimeName);
100 // }
101 // m_hitCollectionName = hitCollectionName;
102 //}
103 
104 
105 vector<float>
106 GRLNeuro::runMLP(unsigned isector, const vector<float>& input)
107 {
108  const GRLMLP& expert = m_MLPs[isector];
109  vector<float> weights = expert.getWeights();
110  vector<float> layerinput = input;
111  vector<float> layeroutput = {};
112  unsigned iw = 0;
113  for (unsigned il = 1; il < expert.nLayers(); ++il) {
114  //add bias input
115  layerinput.push_back(1.);
116  //prepare output
117  layeroutput.clear();
118  layeroutput.assign(expert.nNodesLayer(il), 0.);
119  //loop over outputs
120  for (unsigned io = 0; io < layeroutput.size(); ++io) {
121  //loop over inputs
122  for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
123  layeroutput[io] += layerinput[ii] * weights[iw++];
124  }
125  //apply activation function
126  layeroutput[io] = tanh(layeroutput[io] / 2.);
127  }
128  //output is new input
129  layerinput = layeroutput;
130  }
131  return layeroutput;
132 }
133 
134 vector<float>
135 GRLNeuro::runMLPFix(unsigned isector, const vector<float>& input)
136 {
137  unsigned precisionInput = m_precision[3];
138  unsigned precisionWeights = m_precision[4];
139  unsigned precisionLUT = m_precision[5];
140  unsigned precisionTanh = m_precision[3];
141  unsigned dp = precisionInput + precisionWeights - precisionLUT;
142 
143  const GRLMLP& expert = m_MLPs[isector];
144  // transform inputs to fixed point (cut off to input precision)
145  vector<long> inputFix(input.size(), 0);
146  for (unsigned ii = 0; ii < input.size(); ++ii) {
147  inputFix[ii] = long(input[ii] * (1 << precisionInput));
148  }
149  // transform weights to fixed point (round to weight precision)
150  vector<float> weights = expert.getWeights();
151  vector<long> weightsFix(weights.size(), 0);
152  for (unsigned iw = 0; iw < weights.size(); ++iw) {
153  weightsFix[iw] = long(round(weights[iw] * (1 << precisionWeights)));
154  }
155  // maximum input value for the tanh LUT
156  unsigned xMax = unsigned(ceil(atanh(1. - 1. / (1 << (precisionTanh + 1))) *
157  (1 << (precisionLUT + 1))));
158 
159  // run MLP
160  vector<long> layerinput = inputFix;
161  vector<long> layeroutput = {};
162  unsigned iw = 0;
163  for (unsigned il = 1; il < expert.nLayers(); ++il) {
164  // add bias input
165  layerinput.push_back(1 << precisionInput);
166  // prepare output
167  layeroutput.clear();
168  layeroutput.assign(expert.nNodesLayer(il), 0);
169  // loop over outputs
170  for (unsigned io = 0; io < layeroutput.size(); ++io) {
171  // loop over inputs
172  for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
173  layeroutput[io] += layerinput[ii] * weightsFix[iw++];
174  }
175  // apply activation function -> LUT, calculated on the fly here
176  unsigned long bin = abs(layeroutput[io]) >> dp;
177  // correction to get symmetrical rounding errors
178  float x = (bin + 0.5 - 1. / (1 << (dp + 1))) / (1 << precisionLUT);
179  long tanhLUT = (bin < xMax) ? long(round(tanh(x / 2.) * (1 << precisionTanh))) : (1 << precisionTanh);
180  layeroutput[io] = (layeroutput[io] < 0) ? -tanhLUT : tanhLUT;
181  }
182  // output is new input
183  layerinput = layeroutput;
184  }
185 
186  // transform output back to float before unscaling
187  vector<float> output(layeroutput.size(), 0.);
188  for (unsigned io = 0; io < output.size(); ++io) {
189  output[io] = layeroutput[io] / float(1 << precisionTanh);
190  }
191  return output;
192 }
193 
194 void
195 GRLNeuro::save(const string& filename, const string& arrayname)
196 {
197  B2INFO("Saving networks to file " << filename << ", array " << arrayname);
198  TFile datafile(filename.c_str(), "UPDATE");
199  TObjArray* MLPs = new TObjArray(m_MLPs.size());
200  for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
201  MLPs->Add(&m_MLPs[isector]);
202  }
203  MLPs->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
204  datafile.Close();
205  MLPs->Clear();
206  delete MLPs;
207 }
208 
209 //bool
210 //GRLNeuro::load(const string& filename, const string& arrayname)
211 //{
212 // if (filename.size() < 1) {
213 // m_MLPs.clear();
214 // m_MLPs = m_cdctriggerneuroconfig->getMLPs();
215 // if (m_MLPs.size() == 0) {
216 // B2ERROR("Could not load Neurotrigger weights from database!");
217 // return false;
218 // }
219 // B2DEBUG(2, "Loaded Neurotrigger MLP weights from database: " + m_cdctriggerneuroconfig->getNNName());
220 // B2DEBUG(100, "loaded " << m_MLPs.size() << " networks from database");
221 // // load some values from the geometry that will be needed for the input
222 // setConstants();
223 // return true;
224 // } else {
225 // TFile datafile(filename.c_str(), "READ");
226 // if (!datafile.IsOpen()) {
227 // B2WARNING("Could not open file " << filename);
228 // return false;
229 // }
230 //
231 // TObjArray* MLPs = (TObjArray*)datafile.Get(arrayname.c_str());
232 // if (!MLPs) {
233 // datafile.Close();
234 // B2WARNING("File " << filename << " does not contain key " << arrayname);
235 // return false;
236 // }
237 // m_MLPs.clear();
238 // for (int isector = 0; isector < MLPs->GetEntriesFast(); ++isector) {
239 // GRLMLP* expert = dynamic_cast<GRLMLP*>(MLPs->At(isector));
240 // if (expert) m_MLPs.push_back(*expert);
241 // else B2WARNING("Wrong type " << MLPs->At(isector)->ClassName() << ", ignoring this entry.");
242 // }
243 // MLPs->Clear();
244 // delete MLPs;
245 // datafile.Close();
246 // B2DEBUG(100, "loaded " << m_MLPs.size() << " networks");
247 //
248 // // load some values from the geometry that will be needed for the input
249 // setConstants();
250 //
251 // return true;
252 // }
253 //}
Belle2::GRLNeuro::runMLP
std::vector< float > runMLP(unsigned isector, const std::vector< float > &input)
Run an expert MLP.
Definition: GRLNeuro.cc:106
Belle2::GRLMLP
Class to keep all parameters of an expert MLP for the neuro trigger.
Definition: GRLMLP.h:13
Belle2::GRLNeuro::runMLPFix
std::vector< float > runMLPFix(unsigned isector, const std::vector< float > &input)
Run an expert MLP with fixed point arithmetic.
Definition: GRLNeuro.cc:135
Belle2::GRLNeuro::Parameters
Struct to keep neurotrigger parameters.
Definition: GRLNeuro.h:33
Belle2::GRLNeuro::save
void save(const std::string &filename, const std::string &arrayname="MLPs")
Save MLPs to file.
Definition: GRLNeuro.cc:195
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::GRLNeuro::initialize
void initialize(const Parameters &p)
Set parameters and get some network independent parameters.
Definition: GRLNeuro.cc:21