Belle II Software development
GRLNeuro Class Reference

Class to represent the GRL Neuro. More...

#include <GRLNeuro.h>

Classes

struct  Parameters
 Struct to keep neurotrigger parameters. More...
 

Public Member Functions

 GRLNeuro ()
 Default constructor.
 
virtual ~GRLNeuro ()
 Default destructor.
 
void initialize (const Parameters &p)
 Set parameters and get some network independent parameters.
 
GRLMLP & operator[] (unsigned index)
 return reference to a neural network
 
const GRLMLP & operator[] (unsigned index) const
 return const reference to a neural network
 
unsigned nSectors () const
 return number of neural networks
 
void save (const std::string &filename, const std::string &arrayname="MLPs")
 Save MLPs to file.
 
bool load (unsigned isector, const std::string &wfilename, const std::string &bfilename)
 Load MLPs from file.
 
bool load (unsigned isector, std::vector< float > warray, std::vector< float > barray)
 
std::vector< float > runMLP (unsigned isector, const std::vector< float > &input)
 Run an expert MLP.
 

Private Attributes

std::vector< GRLMLP > m_MLPs = {}
 List of networks.
 

Detailed Description

Class to represent the GRL Neuro.

The Neurotrigger consists of one or several Multi Layer Perceptrons. The input values are calculated from ECLTRG cluster and a 2D track estimate. The output is a scaled estimate of the judgement.

See also
GRLNeuro Modules:
GRLTrainerModule for preparing training data and training,
GRLNeuro for loading trained networks and using them.

Definition at line 34 of file GRLNeuro.h.

Constructor & Destructor Documentation

◆ GRLNeuro()

GRLNeuro ( )
inline

Default constructor.

Definition at line 107 of file GRLNeuro.h.

107{}

◆ ~GRLNeuro()

virtual ~GRLNeuro ( )
inlinevirtual

Default destructor.

Definition at line 110 of file GRLNeuro.h.

110{}

Member Function Documentation

◆ initialize()

void initialize ( const Parameters & p)

Set parameters and get some network independent parameters.

Definition at line 251 of file GRLNeuro.cc.

252{
253 using std::vector;
254
255 // ------------------------------------------------------------------
256 // Basic parameter validation (fatal in initialize)
257 // ------------------------------------------------------------------
258
259 if (p.nHidden.size() != 1 && p.nHidden.size() != p.nMLP) {
260 B2FATAL("Number of nHidden lists should be 1 or " << p.nMLP);
261 }
262
263 if (p.outputScale.size() != 1 && p.outputScale.size() != p.nMLP) {
264 B2FATAL("Number of outputScale lists should be 1 or " << p.nMLP);
265 }
266
267 unsigned short nTarget = static_cast<unsigned short>(p.targetresult);
268 if (nTarget < 1) {
269 B2FATAL("No outputs! Turn on targetresult.");
270 }
271
272 for (unsigned iScale = 0; iScale < p.outputScale.size(); ++iScale) {
273 if (p.outputScale.at(iScale).size() != 2 * nTarget) {
274 B2FATAL("outputScale should be exactly " << 2 * nTarget << " values");
275 }
276 }
277
278 // ------------------------------------------------------------------
279 // Comprehensive parameter-size validation
280 // ------------------------------------------------------------------
281
282 auto check_size = [&](const auto & v, const char* name) {
283 const size_t s = v.size();
284 if (s != 1 && s != static_cast<size_t>(p.nMLP)) {
285 B2FATAL(std::string(name) + " size (" + std::to_string(s)
286 + ") != 1 and != nMLP (" + std::to_string(p.nMLP) + ")");
287 }
288 };
289
290 // sector / structure parameters
291 check_size(p.i_cdc_sector, "i_cdc_sector");
292 check_size(p.i_ecl_sector, "i_ecl_sector");
293 check_size(p.nHidden, "nHidden");
294 check_size(p.outputScale, "outputScale");
295
296 // bias
297 check_size(p.total_bit_bias, "total_bit_bias");
298 check_size(p.int_bit_bias, "int_bit_bias");
299 check_size(p.is_signed_bias, "is_signed_bias");
300 check_size(p.rounding_bias, "rounding_bias");
301 check_size(p.saturation_bias, "saturation_bias");
302
303 // accumulator
304 check_size(p.total_bit_accum, "total_bit_accum");
305 check_size(p.int_bit_accum, "int_bit_accum");
306 check_size(p.is_signed_accum, "is_signed_accum");
307 check_size(p.rounding_accum, "rounding_accum");
308 check_size(p.saturation_accum, "saturation_accum");
309
310 // weights
311 check_size(p.total_bit_weight, "total_bit_weight");
312 check_size(p.int_bit_weight, "int_bit_weight");
313 check_size(p.is_signed_weight, "is_signed_weight");
314 check_size(p.rounding_weight, "rounding_weight");
315 check_size(p.saturation_weight, "saturation_weight");
316
317 // relu
318 check_size(p.total_bit_relu, "total_bit_relu");
319 check_size(p.int_bit_relu, "int_bit_relu");
320 check_size(p.is_signed_relu, "is_signed_relu");
321 check_size(p.rounding_relu, "rounding_relu");
322 check_size(p.saturation_relu, "saturation_relu");
323
324 // generic
325 check_size(p.total_bit, "total_bit");
326 check_size(p.int_bit, "int_bit");
327 check_size(p.is_signed, "is_signed");
328 check_size(p.rounding, "rounding");
329 check_size(p.saturation, "saturation");
330
331 // inputs
332 check_size(p.W_input, "W_input");
333 check_size(p.I_input, "I_input");
334 check_size(p.W_input_layer, "W_input_layer");
335 check_size(p.I_input_layer, "I_input_layer");
336
337 // ------------------------------------------------------------------
338 // Initialize MLPs
339 // ------------------------------------------------------------------
340
341 m_MLPs.clear();
342 m_MLPs.reserve(p.nMLP);
343
344 for (unsigned iMLP = 0; iMLP < p.nMLP; ++iMLP) {
345
346 // helper: broadcast if size == 1
347 auto pick = [&](const auto & container) -> const auto& {
348 return (container.size() == 1) ? container.at(0) : container.at(iMLP);
349 };
350
351 unsigned short i_cdc = static_cast<unsigned short>(pick(p.i_cdc_sector));
352 unsigned short i_ecl = static_cast<unsigned short>(pick(p.i_ecl_sector));
353 unsigned short nInput = static_cast<unsigned short>(i_cdc + i_ecl);
354
355 // nHidden: vector<vector<float>>
356 const vector<float>& nhidden = pick(p.nHidden);
357 vector<unsigned short> nNodes = { nInput };
358
359 for (unsigned iHid = 0; iHid < nhidden.size(); ++iHid) {
360 if (p.multiplyHidden) {
361 nNodes.push_back(static_cast<unsigned short>(nhidden.at(iHid) * nNodes.at(0)));
362 } else {
363 nNodes.push_back(static_cast<unsigned short>(nhidden.at(iHid)));
364 }
365 }
366
367 nNodes.push_back(nTarget);
368 unsigned short targetVars = static_cast<unsigned short>(p.targetresult);
369
370 const vector<float>& outputScale = pick(p.outputScale);
371
372 GRLMLP grlmlp_temp(nNodes, targetVars, outputScale);
373
374 // bias
375 grlmlp_temp.set_total_bit_bias(pick(p.total_bit_bias));
376 grlmlp_temp.set_int_bit_bias(pick(p.int_bit_bias));
377 grlmlp_temp.set_is_signed_bias(pick(p.is_signed_bias));
378 grlmlp_temp.set_rounding_bias(pick(p.rounding_bias));
379 grlmlp_temp.set_saturation_bias(pick(p.saturation_bias));
380
381 // accumulator
382 grlmlp_temp.set_total_bit_accum(pick(p.total_bit_accum));
383 grlmlp_temp.set_int_bit_accum(pick(p.int_bit_accum));
384 grlmlp_temp.set_is_signed_accum(pick(p.is_signed_accum));
385 grlmlp_temp.set_rounding_accum(pick(p.rounding_accum));
386 grlmlp_temp.set_saturation_accum(pick(p.saturation_accum));
387
388 // weights
389 grlmlp_temp.set_total_bit_weight(pick(p.total_bit_weight));
390 grlmlp_temp.set_int_bit_weight(pick(p.int_bit_weight));
391 grlmlp_temp.set_is_signed_weight(pick(p.is_signed_weight));
392 grlmlp_temp.set_rounding_weight(pick(p.rounding_weight));
393 grlmlp_temp.set_saturation_weight(pick(p.saturation_weight));
394
395 // relu
396 grlmlp_temp.set_total_bit_relu(pick(p.total_bit_relu));
397 grlmlp_temp.set_int_bit_relu(pick(p.int_bit_relu));
398 grlmlp_temp.set_is_signed_relu(pick(p.is_signed_relu));
399 grlmlp_temp.set_rounding_relu(pick(p.rounding_relu));
400 grlmlp_temp.set_saturation_relu(pick(p.saturation_relu));
401
402 // generic
403 grlmlp_temp.set_total_bit(pick(p.total_bit));
404 grlmlp_temp.set_int_bit(pick(p.int_bit));
405 grlmlp_temp.set_is_signed(pick(p.is_signed));
406 grlmlp_temp.set_rounding(pick(p.rounding));
407 grlmlp_temp.set_saturation(pick(p.saturation));
408
409 // inputs
410 grlmlp_temp.set_W_input(pick(p.W_input));
411 grlmlp_temp.set_I_input(pick(p.I_input));
412 // input layer quantization parameters
413 grlmlp_temp.set_W_input_layer(pick(p.W_input_layer));
414 grlmlp_temp.set_I_input_layer(pick(p.I_input_layer));
415
416 m_MLPs.push_back(std::move(grlmlp_temp));
417 }
418}
std::vector< GRLMLP > m_MLPs
List of networks.
Definition GRLNeuro.h:144

◆ load() [1/2]

bool load ( unsigned isector,
const std::string & wfilename,
const std::string & bfilename )

Load MLPs from file.

Parameters
isectorindex of the MLP
wfilenamename of the TFile to read from
bfilenamename of the TObjArray holding the MLPs in the file
Returns
true if the MLPs were loaded correctly

Definition at line 569 of file GRLNeuro.cc.

570{
571 if (weightfilename.size() < 1) {
572 B2ERROR("Could not load Neurotrigger weights from database!");
573 return false;
574 } else if (biasfilename.size() < 1) {
575 B2ERROR("Could not load Neurotrigger bias from database!");
576 return false;
577 } else {
578 std::ifstream wfile(weightfilename);
579 if (!wfile.is_open()) {
580 B2WARNING("Could not open file " << weightfilename);
581 return false;
582 } else {
583 std::ifstream bfile(biasfilename);
584 if (!bfile.is_open()) {
585 B2WARNING("Could not open file " << biasfilename);
586 return false;
587 } else {
588 GRLMLP& expert = m_MLPs.at(isector);
589 std::vector<float> warray;
590 std::vector<float> barray;
591 warray.clear();
592 barray.clear();
593
594 float element;
595 while (wfile >> element) {
596 warray.push_back(element);
597 }
598 while (bfile >> element) {
599 barray.push_back(element);
600 }
601
602 if (warray.size() != expert.n_weights_cal()) {
603 B2ERROR("Number of weights is not equal to registered architecture!");
604 return false;
605 } else expert.set_weights(warray);
606 if (barray.size() != expert.n_bias_cal()) {
607 B2ERROR("Number of bias is not equal to registered architecture!");
608 return false;
609 }
610
611 expert.set_weights(warray);
612 expert.set_bias(barray);
613 return true;
614 }
615 }
616 }
617}

◆ load() [2/2]

bool load ( unsigned isector,
std::vector< float > warray,
std::vector< float > barray )

Definition at line 619 of file GRLNeuro.cc.

620{
621 GRLMLP& expert = m_MLPs.at(isector);
622 expert.set_weights(warray);
623 expert.set_bias(barray);
624 return true;
625}

◆ nSectors()

unsigned nSectors ( ) const
inline

return number of neural networks

Definition at line 120 of file GRLNeuro.h.

120{ return m_MLPs.size(); }

◆ operator[]() [1/2]

GRLMLP & operator[] ( unsigned index)
inline

return reference to a neural network

Definition at line 116 of file GRLNeuro.h.

116{ return m_MLPs[index]; }

◆ operator[]() [2/2]

const GRLMLP & operator[] ( unsigned index) const
inline

return const reference to a neural network

Definition at line 118 of file GRLNeuro.h.

118{ return m_MLPs[index]; }

◆ runMLP()

std::vector< float > runMLP ( unsigned isector,
const std::vector< float > & input )

Run an expert MLP.

Parameters
isectorindex of the MLP
inputvector of input values
Returns
output values (classifier) - all 10 outputs for firmware compatibility

Definition at line 423 of file GRLNeuro.cc.

424{
425 const GRLMLP& expert = m_MLPs.at(isector);
426 vector<float> weights = expert.get_weights();
427 vector<float> bias = expert.get_bias();
428 vector<int> total_bit_bias = expert.get_total_bit_bias();
429 vector<int> int_bit_bias = expert.get_int_bit_bias();
430 vector<bool> is_signed_bias = expert.get_is_signed_bias();
431 vector<int> rounding_bias = expert.get_rounding_bias();
432 vector<int> saturation_bias = expert.get_saturation_bias();
433 vector<int> total_bit_accum = expert.get_total_bit_accum();
434 vector<int> int_bit_accum = expert.get_int_bit_accum();
435 vector<bool> is_signed_accum = expert.get_is_signed_accum();
436 vector<int> rounding_accum = expert.get_rounding_accum();
437 vector<int> saturation_accum = expert.get_saturation_accum();
438 vector<int> total_bit_weight = expert.get_total_bit_weight();
439 vector<int> int_bit_weight = expert.get_int_bit_weight();
440 vector<bool> is_signed_weight = expert.get_is_signed_weight();
441 vector<int> rounding_weight = expert.get_rounding_weight();
442 vector<int> saturation_weight = expert.get_saturation_weight();
443 vector<int> total_bit_relu = expert.get_total_bit_relu();
444 vector<int> int_bit_relu = expert.get_int_bit_relu();
445 vector<bool> is_signed_relu = expert.get_is_signed_relu();
446 vector<int> rounding_relu = expert.get_rounding_relu();
447 vector<int> saturation_relu = expert.get_saturation_relu();
448 vector<int> total_bit = expert.get_total_bit();
449 vector<int> int_bit = expert.get_int_bit();
450 vector<bool> is_signed = expert.get_is_signed();
451 vector<int> rounding = expert.get_rounding();
452 vector<int> saturation = expert.get_saturation();
453 vector<vector<int>> W_input = expert.get_W_input();
454 vector<vector<int>> I_input = expert.get_I_input();
455 // Input layer quantization parameters
456 vector<vector<int>> W_input_layer = expert.get_W_input_layer();
457 vector<vector<int>> I_input_layer = expert.get_I_input_layer();
458
459 // Use actual input
460 vector<float> layerinput = input;
461
462 // Input layer quantization: firmware uses TWO steps:
463 // Step 1: float -> input_layer_t (ap_fixed<13,13>) with AP_RND, AP_SAT_SYM
464 // Step 2: input_layer_t -> dense_0_iq_t (per-input ap_fixed<W,I>) with AP_RND, AP_WRAP
465
466 // First, quantize raw input to input_layer_t (ap_fixed<13,13>)
467 // Firmware: ap_fixed<13,13> with AP_TRN (signed: floor toward -inf), AP_WRAP (defaults)
468 for (size_t i = 0; i < layerinput.size(); ++i) {
469 // AP_TRN for signed = floor (toward -inf), AP_WRAP
470 layerinput.at(i) = sim_ap_fixed(layerinput.at(i), 13, 13, true, 3, 2,
471 0); // signed, quant_mode=3 (floor), overflow_mode=2 (AP_WRAP)
472 }
473
474 // Use the first layer's W/I for input quantization (dense_0_iq)
475 const auto& w_layer = W_input_layer.at(0);
476 const auto& i_layer = I_input_layer.at(0);
477
478 // Step 2: dense_0_iq quantization with per-input W, I
479 for (size_t i = 0; i < layerinput.size(); ++i) {
480 // dense_0_iq quantization: signed, quant_mode=1 (AP_RND), overflow_mode=2 (AP_WRAP)
481 layerinput.at(i) = sim_ap_fixed(layerinput.at(i), w_layer.at(i), i_layer.at(i), true, 1, 2, 0);
482 }
483
484 //hidden layer and output layer
485 vector<float> layeroutput = {};
486 unsigned num_layers = expert.get_number_of_layers();
487
488 unsigned num_total_neurons = 0;
489 unsigned iw = 0;
490 for (unsigned iw_layer = 0; iw_layer < num_layers - 1; iw_layer++) {
491 //read bias
492 unsigned num_neurons = expert.get_number_of_nodes_layer(iw_layer + 1);
493 layeroutput.clear();
494 layeroutput.assign(num_neurons, 0.);
495 layeroutput.shrink_to_fit();
496
497
498 for (unsigned io = 0; io < num_neurons; ++io) {
499 float bias_raw = bias.at(io + num_total_neurons);
500 float bias_fixed = sim_ap_fixed(bias_raw, total_bit_bias.at(iw_layer), int_bit_bias.at(iw_layer), is_signed_bias.at(iw_layer),
501 rounding_bias.at(iw_layer), saturation_bias.at(iw_layer), 0);
502 float bias_contrib = sim_ap_fixed(bias_fixed, total_bit_accum.at(iw_layer), int_bit_accum.at(iw_layer),
503 is_signed_accum.at(iw_layer),
504 rounding_accum.at(iw_layer), saturation_accum.at(iw_layer), 0);
505 layeroutput.at(io) = bias_contrib;
506 }
507 num_total_neurons += num_neurons;
508
509 //input*weight
510 unsigned num_inputs = layerinput.size();
511
512 for (unsigned ii = 0; ii < num_inputs; ++ii) {
513 float input_val = layerinput.at(ii);
514 for (unsigned io = 0; io < num_neurons; ++io) {
515 float weight_raw = weights.at(iw);
516 float weight_fixed = sim_ap_fixed(weight_raw, total_bit_weight.at(iw_layer), int_bit_weight.at(iw_layer),
517 is_signed_weight.at(iw_layer),
518 rounding_weight.at(iw_layer), saturation_weight.at(iw_layer), 0);
519 float product = input_val * weight_fixed;
520 float contrib = sim_ap_fixed(product, total_bit_accum.at(iw_layer), int_bit_accum.at(iw_layer), is_signed_accum.at(iw_layer),
521 rounding_accum.at(iw_layer), saturation_accum.at(iw_layer), 0);
522 layeroutput.at(io) = sim_ap_fixed(layeroutput.at(io) + contrib, total_bit_accum.at(iw_layer), int_bit_accum.at(iw_layer),
523 is_signed_accum.at(iw_layer), rounding_accum.at(iw_layer), saturation_accum.at(iw_layer), 0);
524 ++iw;
525 }
526 }
527 // input*weight done
528
529 if (iw_layer < num_layers - 2) {
530 //relu
531 for (unsigned io = 0; io < num_neurons; ++io) {
532 float fixed_val = sim_ap_fixed(layeroutput.at(io), total_bit.at(iw_layer), int_bit.at(iw_layer), is_signed.at(iw_layer), 0,
533 saturation.at(iw_layer), 0);
534 float relu_val = (fixed_val > 0) ? fixed_val : 0;
535 layeroutput.at(io) = sim_ap_fixed(relu_val, total_bit_relu.at(iw_layer), int_bit_relu.at(iw_layer), is_signed_relu.at(iw_layer),
536 rounding_relu.at(iw_layer), saturation_relu.at(iw_layer), 0);
537 }
538 //input to next layer
539 layerinput.clear();
540 layerinput.assign(num_neurons, 0);
541 layerinput.shrink_to_fit();
542
543 for (unsigned i = 0; i < num_neurons; ++i) {
544 // firmware: use ap_fixed with parameters from W_input/I_input
545 // quant_mode=1 (AP_RND), overflow_mode=2 (AP_WRAP)
546 // W=0 means this input is zeroed (sparse quantization)
547 if (W_input.at(iw_layer).at(i) == 0) {
548 layerinput.at(i) = 0.0f;
549 } else {
550 const bool iq_is_signed = (iw_layer != 3); // dense_4_iq is ap_ufixed
551 layerinput.at(i) = sim_ap_fixed(layeroutput.at(i), W_input.at(iw_layer).at(i), I_input.at(iw_layer).at(i), iq_is_signed, 1, 2, 0);
552 }
553 }
554 } else {
555 // Final output layer - return all 10 outputs
556 // firmware: result_t is ap_fixed<19,10> with default AP_TRN (signed floor), AP_WRAP
557 vector<float> final_output(10, 0.0f);
558 for (unsigned io = 0; io < 10; ++io) {
559 final_output.at(io) = sim_ap_fixed(layeroutput.at(io), 19, 10, true, 3, 2, 0);
560 }
561 return final_output;
562 }
563 }
564
565 return vector<float>(10, 0.0f);
566}

◆ save()

void save ( const std::string & filename,
const std::string & arrayname = "MLPs" )

Save MLPs to file.

Parameters
filenamename of the TFile to write to
arraynamename of the TObjArray holding the MLPs in the file

Member Data Documentation

◆ m_MLPs

std::vector<GRLMLP> m_MLPs = {}
private

List of networks.

Definition at line 144 of file GRLNeuro.h.

144{};

The documentation for this class was generated from the following files: