Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RModel.cxx
Go to the documentation of this file.
1#include <limits>
2#include <algorithm>
3#include <cctype>
4#include <memory>
5#include <string>
6#include <cstdlib>
7
8#ifdef SOFIE_SUPPORT_ROOT_BINARY
9#include "TFile.h"
10#endif
11
12#include "TMVA/RModel.hxx"
13#include "TMVA/SOFIE_common.hxx"
14
16
17namespace {
18
19const std::string SP = " ";
20
21void ReplaceAll(std::string &str, const std::string &from, const std::string &to)
22{
23 size_t pos = 0;
24 while ((pos = str.find(from, pos)) != std::string::npos) {
25 str.replace(pos, from.length(), to);
26 pos += to.length();
27 }
28}
29
30bool IsIdentifierChar(char c)
31{
32 return std::isalnum(static_cast<unsigned char>(c)) || c == '_';
33}
34
35// Returns true if s is a valid C++ identifier (can be used as a variable name).
36// Dim::param can be either a plain name (e.g. "W") or a computed expression
37// (e.g. "((W+-3)/2+1)"); only the former can be used as a C++ variable name.
38bool IsIdentifier(const std::string &s)
39{
40 if (s.empty() || std::isdigit(static_cast<unsigned char>(s[0])))
41 return false;
42 for (char c : s)
45 return true;
46}
47
48// Get the data member name corresponding to a tensor with a given name.
49std::string TensorMember(std::string const &name)
50{
51 return "tensor_" + name;
52}
53
54} // namespace
55
56std::underlying_type_t<Options> operator|(Options opA, Options opB) {
57 return static_cast<std::underlying_type_t<Options>>(opA) | static_cast<std::underlying_type_t<Options>>(opB);
58}
59std::underlying_type_t<Options> operator|(std::underlying_type_t<Options> opA, Options opB) {
60 return opA | static_cast<std::underlying_type_t<Options>>(opB);
61}
62
63
64std::vector<size_t> RModel::GetTensorShape(const std::string & name) const {
65 auto f = fReadyInputTensorInfos.find(name);
66 if (f != fReadyInputTensorInfos.end()) {
67 return f->second.shape;
68 }
69 auto f2 = fInitializedTensors.find(name);
70 if (f2 != fInitializedTensors.end()) {
71 return f2->second.shape();
72 }
73 auto f3 = fInputTensorInfos.find(name);
74 if (f3 != fInputTensorInfos.end()) {
75 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] is an input tensor with unspecified dimension parameter");
76 }
77 auto f4 = fIntermediateTensorInfos.find(name);
78 if (f4 != fIntermediateTensorInfos.end()) {
79 return f4->second.shape;
80 }
81 // case of shape tensors
82 auto f5 = fShapeTensors.find(name);
83 if (f5 != fShapeTensors.end()) {
84 // shape is vector of size 1 with size of shape values or just a scalar
85 if (f5->second.second) // check scalar flag
86 return std::vector<size_t>{};
87 else
88 return std::vector<size_t>{f5->second.first.size()};
89 }
90
92 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] is a dynamic tensor. Use GetDynamicTensorShape instead of GetTensorShape");
93
96
97 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the shape is requested is not found");
98}
99
100std::vector<Dim> RModel::GetDimTensorShape(const std::string & name) const {
101 if (auto f = fDynamicTensorInfos.find(name); f != fDynamicTensorInfos.end()) {
102 return f->second.shape;
103 }
104 if (auto f = fInputTensorInfos.find(name); f != fInputTensorInfos.end()) {
105 return f->second.shape;
106 }
107 // in case is not a dynamic tensor convert normal shape to Dim one
108 // for this we need to return the vector by value
110}
111std::vector<Dim> RModel::GetDynamicTensorShape(const std::string & name) const {
112 if (auto f = fDynamicTensorInfos.find(name); f != fDynamicTensorInfos.end()) {
113 return f->second.shape;
114 }
115 if (auto f = fInputTensorInfos.find(name); f != fInputTensorInfos.end()) {
116 return f->second.shape;
117 }
118 // throw error if shape is not dynamic
119 if (!IsDynamicTensor(name))
120 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the shape is requested is not dynamic");
121
122 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the shape is requested is not found");
123}
124
126 auto f = fReadyInputTensorInfos.find(name);
127 if (f != fReadyInputTensorInfos.end()) {
128 return f->second.type;
129 }
130 auto f2 = fInitializedTensors.find(name);
131 if (f2 != fInitializedTensors.end()) {
132 return f2->second.type();
133 }
134 auto f3 = fInputTensorInfos.find(name);
135 if (f3 != fInputTensorInfos.end()) {
136 return f3->second.type;
137 }
138 auto f4 = fIntermediateTensorInfos.find(name);
139 if (f4 != fIntermediateTensorInfos.end()) {
140 return f4->second.type;
141 }
142 auto f5 = fDynamicTensorInfos.find(name);
143 if (f5 != fDynamicTensorInfos.end()){
144 return f5->second.type;
145 }
146 // case of shape tensor type is INT64
147 if (fShapeTensors.find(name) != fShapeTensors.end()){
148 return ETensorType::INT64;
149 }
150
153
154 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the type is requested is not found, model name: " + fName);
155}
156
157bool RModel::CheckIfTensorAlreadyExist(std::string tensor_name) {
158 if (fReadyInputTensorInfos.find(tensor_name) != fReadyInputTensorInfos.end()) return true;
159 if (fInputTensorInfos.find(tensor_name) != fInputTensorInfos.end()) return true;
160 if (fInitializedTensors.find(tensor_name) != fInitializedTensors.end()) return true;
161 if (fIntermediateTensorInfos.find(tensor_name) != fIntermediateTensorInfos.end()) return true;
162 if (fDynamicTensorInfos.find(tensor_name) != fDynamicTensorInfos.end()) return true;
163 if (fShapeTensors.find(tensor_name) != fShapeTensors.end()) return true;
165 return false;
166}
167
168void RModel::AddInputTensorInfo(std::string input_name, ETensorType type, std::vector<Dim> shape) {
171 throw std::runtime_error("TMVA-SOFIE: input tensor with name " + input_name + " already exists \n");
172 }
173
174 InputTensorInfo inputInfo { type, shape };
176}
177
178void RModel::AddInputTensorInfo(std::string input_name, ETensorType type, std::vector<size_t> shape) {
181 throw std::runtime_error("TMVA-SOFIE: input tensor with name " + input_name + " already exists \n");
182 }
183 TensorInfo inputInfo { type, shape };
185}
186
190
191void RModel::AddOperator(std::unique_ptr<ROperator> op, int order_execution)
192{
193 AddBlasRoutines(op->GetBlasRoutines());
194 auto libs = op->GetStdLibs();
195 auto op_input_tensors = op->GetOpInputTensors();
196 for (auto &stdlib : libs) {
198 }
199 if (order_execution >= 0) {
200 fOperators.insert(fOperators.begin() + order_execution, std::move(op));
201 } else {
202 fOperators.push_back(std::move(op));
203 order_execution = fOperators.size() - 1;
204 }
205
206 // storing the last usage of tensors which are input to the operator
207 // (excluding tensors which are inputs to the model or the initialized (weights) tensors)
208 // We call this function during parsing so we don't have yet initialized the operators
209 for (size_t index = 0; index < op_input_tensors.size(); index++) {
211 std::find(fInputTensorNames.begin(), fInputTensorNames.end(),
213
215 if (Verbose())
216 std::cout << "adding order execution for " << op_input_tensors[index] << " order " << order_execution
217 << std::endl;
218 }
219 }
220}
221
222void RModel::AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape, std::shared_ptr<void> data) {
223 tensor_name = UTILITY::Clean_name(tensor_name);
224 //NB: own data
225 if (CheckIfTensorAlreadyExist(tensor_name)) {
226 throw std::runtime_error("TMVA-SOFIE: initialized tensor with name " + tensor_name + " already exists \n");
227 }
229 fInitializedTensors[tensor_name] = new_tensor;
230}
231
232void RModel::AddInitializedTensor(const std::string &tensor_name, ETensorType tensor_type,
233 const std::vector<std::size_t> &shape, void *raw_data)
234{
235 size_t size = ConvertShapeToLength(shape);
236 auto itemsize = GetTypeSize(tensor_type);
237 std::shared_ptr<void> data(malloc(size * itemsize), free);
238 std::memcpy(data.get(), raw_data, size * itemsize);
239 AddInitializedTensor(tensor_name, tensor_type, shape, data);
240}
241
242void RModel::AddConstantTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape, std::shared_ptr<void> data) {
243 tensor_name = UTILITY::Clean_name(tensor_name);
244 //NB: own data
245 if (CheckIfTensorAlreadyExist(tensor_name)) {
246 throw std::runtime_error("TMVA-SOFIE: constant tensor with name " + tensor_name + " already exists \n");
247 }
248 InitializedTensor new_tensor {type, shape, data, true}; // add here flag to specify is a constant tensor
249 fInitializedTensors[tensor_name] = new_tensor;
250}
251
252void RModel::AddShapeTensor(const std::string & name, const std::vector<Dim> & shape_values, bool scalar){
253 auto tensor_name = UTILITY::Clean_name(name);
254 if (fShapeTensors.count(tensor_name) != 0) {
255 throw std::runtime_error("TMVA-SOFIE: shape tensor with name " + tensor_name + " already exists \n");
256 }
257 fShapeTensors[tensor_name] = std::make_pair(shape_values, scalar);
258}
259
260void RModel::AddAliasTensor(const std::string & name, const std::string & origin){
261 // add an alias tensor to origin
262 auto tensor_name = UTILITY::Clean_name(name);
264 if (fAliasTensors.count(tensor_name) != 0) {
265 throw std::runtime_error("TMVA-SOFIE: alias tensor with name " + tensor_name + " already exists \n");
266 }
267 fAliasTensors[tensor_name] = origin_name;
268}
269
270bool RModel::IsShapeTensor(const std::string & tensor_name) const {
271 return fShapeTensors.count(tensor_name) != 0;
272}
273
274bool RModel::IsAliasTensor(const std::string & tensor_name) const {
275 return fAliasTensors.count(tensor_name) != 0;
276}
277
278const std::vector<Dim> & RModel::GetShapeTensorValues(const std::string & tensor_name) const {
279 //if (!IsShapeTensor(tensor_name) ) return std::vector<Dim>{};
280 return fShapeTensors.at(tensor_name).first;
281}
282
283bool RModel::IsInitializedTensor(const std::string& tensorName) const {
284 std::string name = UTILITY::Clean_name(tensorName);
285 return fInitializedTensors.find(name) != fInitializedTensors.end();
286}
287bool RModel::IsConstantTensor(const std::string& tensorName) const {
288 // a constant tensor is an initialized tensor but has the constant flag set
289 std::string name = UTILITY::Clean_name(tensorName);
290 auto itr = fInitializedTensors.find(name);
291 if (itr == fInitializedTensors.end()) return false;
292 return itr->second.IsConstantTensor();
293}
294
295// dynamic tensors include also Dim input tensors
296bool RModel::IsDynamicTensor(const std::string& tensorName) const {
297 std::string name = UTILITY::Clean_name(tensorName);
299 return (ret) ? true : IsDimInputTensor(tensorName);
300}
301bool RModel::IsDimInputTensor(const std::string& tensorName) const {
302 std::string name = UTILITY::Clean_name(tensorName);
303 return fInputTensorInfos.find(name) != fInputTensorInfos.end();
304}
305bool RModel::IsReadyInputTensor(const std::string& tensorName) const {
306 std::string name = UTILITY::Clean_name(tensorName);
308}
309
310// generic addition of a tensor
311void RModel::AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<Dim> dim_shape) {
313 if (!int_shape.empty())
314 AddIntermediateTensor(tensor_name, type, int_shape);
315 else
316 AddDynamicTensor(tensor_name, type, dim_shape);
317}
318
319void RModel::AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape) {
320 tensor_name = UTILITY::Clean_name(tensor_name);
321 if (CheckIfTensorAlreadyExist(tensor_name)) {
322 throw std::runtime_error("TMVA-SOFIE: intermediate tensor with name " + tensor_name + " already exists \n");
323 }
324 TensorInfo new_tensor {type, shape};
326}
327
328void RModel::AddDynamicTensor(std::string tensor_name, ETensorType type, std::vector<Dim> shape){
329 tensor_name = UTILITY::Clean_name(tensor_name);
330 if (CheckIfTensorAlreadyExist(tensor_name)){
331 throw std::runtime_error("TMVA-SOFIE: intermediate tensor with name " + tensor_name + " already exists \n");
332 }
334 fDynamicTensorInfos[tensor_name] = new_tensor;
335 // store shape parameter if not existing
336 for (auto &d : shape) {
337 if (d.isParam) {
338 if (d.dim != size_t(-1)) {
339 AddShapeParam(d.param, d.dim);
340 }
341 }
342 }
343}
344
345void RModel::AddShapeParam(const std::string & param, size_t default_value) {
346 if (fShapeParams.count(param) == 0) {
347 fShapeParams[param] = std::to_string(default_value);
348 // add also in the vector list (used to keep the order)
349 fDimShapeNames.push_back(param);
350 }
351}
352
354 fOutputTensorNames.clear();
355 for(auto& it : outputtensornames) {
356 fOutputTensorNames.emplace_back(UTILITY::Clean_name(it));
357 }
358}
359
360void RModel::UpdateOutputTensorList(std::vector<std::string> curr_output_tensors, std::vector<std::string> new_output_tensors) {
361 for(auto& it:curr_output_tensors) {
362 fOutputTensorNames.erase(std::remove(fOutputTensorNames.begin(), fOutputTensorNames.end(), it), fOutputTensorNames.end());
363 }
365}
366
367void RModel::UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape, std::shared_ptr<void> data) {
368 tensor_name = UTILITY::Clean_name(tensor_name);
369 if (!CheckIfTensorAlreadyExist(tensor_name)) {
370 throw std::runtime_error("TMVA-SOFIE: tensor " + tensor_name + " not found when trying to update it");
371 }
373 fInitializedTensors[tensor_name] = new_tensor;
374}
375
376std::shared_ptr<void> RModel::GetInitializedTensorData(std::string tensor_name) {
377 auto f = fInitializedTensors.find(tensor_name);
378 if (f == fInitializedTensors.end()) {
379 throw std::runtime_error("TMVA-SOFIE: tensor " + tensor_name + " not found when trying to get its data");
380 } else {
381 return f->second.sharedptr();
382 }
383}
384
385void RModel::SetNotWritableInitializedTensor(const std::string & tensor_name) {
386 auto t = fInitializedTensors.find(tensor_name);
387 if (t == fInitializedTensors.end()) {
388 throw std::runtime_error("TMVA-SOFIE: initialized tensor " + tensor_name + " not found when trying to get its info");
389 }
390 t->second.SetNotWritable();
391 }
392
393std::string RModel::AllocateIntermediateMemory(std::span<const std::string_view> op_output_tensors)
394{
395 std::stringstream code;
396
397 if (fVerbose) {
398 std::cout << "Total chunks allocated\n";
400 std::cout << "..... chunk " << chunk->first << " size " << chunk->second.tensor_size << " " << chunk->second.tensor_name << std::endl;
401 }
402 }
403
404 auto declareIntermediateTensor = [this, &code](std::string const &name, size_t size, size_t location) {
405 std::string typeName = ConvertTypeToString(GetTensorType(name));
406 code << "\n // Allocating memory for intermediate tensor " << name << " with size " << size << " bytes";
407 code << "\n"
408 << typeName << "* " << TensorMember(name) << " = reinterpret_cast<" << typeName
409 << "*>(fIntermediateMemoryPool.data() + " << location << ");\n";
410 };
411
412 if (fVerbose) std::cout << "*** AllocateIntermediateMemory: Loop on op output tensors\n";
413 // order output tensors by size
414 std::vector<TensorMemoryInfo> ordered_output_tensors;
415
416 for (auto &it : op_output_tensors) {
417 auto name = std::string(it);
420 continue;
421
422 // case of alias tensor
423 if (IsAliasTensor(name)) {
424 continue;
425 }
426
428 // important fill the pair in the ordered output tensors with the string view and not the string
429 TensorMemoryInfo tmi = {it, tensor_size};
430 ordered_output_tensors.push_back(tmi);
431 }
433 [](const TensorMemoryInfo &a, const TensorMemoryInfo &b) { return a.tensor_size > b.tensor_size; });
434
435 for (auto &it : ordered_output_tensors) {
436 bool allocated = false;
437 std::string name = std::string{it.tensor_name};
438 size_t tensor_size = it.tensor_size;
439 if (fVerbose)
440 std::cout << "output tensor " << name << " size " << tensor_size << std::endl;
441
444
445 if (fVerbose) std::cout << ".. available chunk " << chunk->first << " with size = " << chunk->second;
446 // check if available memory chunks can accommodate the tensor
447 if (chunk->second >= tensor_size) {
448 // need to use here string_view (i.e it.tensor_name)
449 // split returns the new chunk with size of new tensor. The free chunk is before the used one
450 auto new_chunk = fIntermediateMemoryInfo.total_stack[chunk->first].split(it.tensor_name, tensor_size);
451 auto new_chunk_location = chunk->first + chunk->second - tensor_size;
453
455 chunk->second -= tensor_size;
456
457 allocated = true;
458
459 if (fVerbose) std::cout << " is re-used and split in a new of size " << new_chunk.tensor_size << " at " << new_chunk_location;
460
461 if (chunk->second == 0) {
462 if (fVerbose) std::cout << " and deleted since size matches";
464 }
465 if (fVerbose) std::cout << std::endl;
466 break;
467 } else if (chunk->first == fIntermediateMemoryInfo.available_stack.rbegin()->first &&
468 fIntermediateMemoryInfo.total_stack.rbegin()->first == chunk->first) {
469 // case last available chunk is the last in the memory, we can increase that one
470 fIntermediateMemoryInfo.total_stack[chunk->first] = {it.tensor_name, tensor_size};
471 declareIntermediateTensor(name, tensor_size, chunk->first);
473 allocated = true;
474 if (fVerbose) std::cout << " is extended with a bigger one of size " << tensor_size << std::endl;
475 break;
476 }
477 ++chunk;
478 if (fVerbose) std::cout << std::endl;
479 }
480
481 if (!allocated) {
483 ? 0
484 : fIntermediateMemoryInfo.total_stack.rbegin()->first +
485 fIntermediateMemoryInfo.total_stack.rbegin()->second.tensor_size;
486
488
490
491 if (fVerbose) std::cout << "no chunk available - add in total stack a new chunk with size of tensor and idx : " << chunk_idx
492 << std::endl;
493 }
494 }
495 return code.str();
496}
497
498void RModel::CheckAndFlushIntermediateMemory(std::span<const std::string_view> op_input_tensors, const size_t& op_idx){
499 if (fVerbose) std::cout << "*** CheckAndFlushIntermediateMemory: Loop on input tensors for op " << op_idx << "\n";
500 //print available chunks
501 if (fVerbose) std::cout << "available chunks before freeing them : \n";
504 if (fVerbose) std::cout << "-- free chunk " << chunk->first << " size = " << chunk->second << std::endl;
505 }
506 for (auto &iv : op_input_tensors) {
507 // last occurrence of the tensor is reached => flush it from memory
508 if (fVerbose) std::cout << ".. input tensors : " << iv;
509
510 // for alias tensors replace name with its alias
511 std::string it{iv}; // convert view to string
512 if (IsAliasTensor(it))
513 it = fAliasTensors[it];
515 if (fVerbose) std::cout << " flash condition is met - looping on chunks to find matching one \n";
516 for (auto chunk = fIntermediateMemoryInfo.total_stack.begin();
518 if (fVerbose) std::cout << "--- chunk " << chunk->first << " , " << chunk->second.tensor_name << " size " << chunk->second.tensor_size;
519 if (chunk->second.tensor_name == it) {
520 if (fVerbose) std::cout << " -- Found chunk corresponding to input tensor: " << chunk->first;
521 // check if nearby chunks in available memory can coalesce
523 chunk->first); // smallest element greater than the flushed chunk idx
526 : std::prev(first_greater); // largest element smaller than the flushed chunk idx
527
528 // check if the next stack entry is actually adjacent in memory
529
531 last_smaller->first + last_smaller->second == chunk->first) {
532 // merge chunk with previous one
533 last_smaller->second += chunk->second.tensor_size;
535 if (fVerbose) std::cout << " is adjacent in memory with previous one - merge ";
537 last_smaller->first + last_smaller->second == first_greater->first) {
538 // merge also with following one
539 last_smaller->second += first_greater->second;
542 // delete merged one in available stack and in total stack
545 if (fVerbose) std::cout << " merge also with following that is free ";
546 }
548 if (fVerbose) std::cout << std::endl;
549 break;
551 chunk->first + chunk->second.tensor_size == first_greater->first) {
552 // merge with first greater
553 if (fVerbose) std::cout << " is adjacent in memory with following one - merge \n";
554 // cannot modify idx of first_greter. Insert a new one and delete previous one
555 size_t new_size = chunk->second.tensor_size + first_greater->second;
556 size_t first_greater_idx = first_greater->first;
558 // cannot use anymore first_greater
563 } else {
564 fIntermediateMemoryInfo.available_stack.insert({chunk->first, chunk->second.tensor_size});
565 if (fVerbose) std::cout << " insert in the available stack the chunk with size " << chunk->second.tensor_size << std::endl;
566 }
567 chunk->second.tensor_name = "free";
568 break;
569 }
570 }
571 } else {
572 if (fVerbose) std::cout << std::endl;
573 }
574 }
575}
576
577void RModel::Initialize(int batchSize, bool verbose) {
578 std::map<std::string, size_t> inputParams;
579 if (batchSize > 0) {
580 inputParams["input_size"] = batchSize;
581 inputParams["batch_size"] = batchSize;
582 inputParams["bs"] = batchSize;
583 }
584 Initialize(inputParams, verbose);
586}
587void RModel::Initialize(const std::map<std::string, size_t> & inputParams, bool verbose) {
588
589 fVerbose = int(verbose);
590
591 if (fIsInitialized) {
592 if (verbose)
593 std::cout << "Model is already initialized - skip initialization " << std::endl;
594 return;
595 }
597 fDynamicTensorInfos.clear();
598
599
600 // loop on inputs and see if shape can be full specified
601 // if the batch size is provided it can be used to specify the full shape
602 // Add the full specified tensors in fReadyInputTensors collection
603 auto originalInputTensorInfos = fInputTensorInfos; // need to copy because we may delete elements
604 for (auto &input : originalInputTensorInfos) {
605 if (verbose) std::cout << "looking at the tensor " << input.first << std::endl;
606 // if a parameter (e.g. batch_size) is specified use for converting parametric shape in defined one
607 if (!inputParams.empty()) {
608 for (auto &d : input.second.shape) {
609 if (d.isParam) {
610 std::string pname = d.param;
611 if (pname == input.first + "_size") pname = "input_size";
612 auto itr = inputParams.find(pname);
613 if (itr != inputParams.end() ) {
614 d = Dim{ itr->second };
615 if (verbose)
616 std::cout << "Tensor: " << input.first << " - fix parametric shape " << itr->first << " to " << itr->second << std::endl;
617 }
618 }
619 }
620 }
621 // see if shape now is fully defined
622 auto shape = ConvertShapeToInt(input.second.shape);
623 if (verbose)
624 std::cout << "converting input shape for " << input.first << " " << ConvertShapeToString(shape) << " from "
625 << ConvertDimShapeToString(input.second.shape) << std::endl;
626 if (!shape.empty()) {
627 // case shape is defined (not parametric) we add the tensor in the fReadyInputTensorInfos map and
628 // we remove the tensor from the fInputTensorInfo where th eold parametric shape was stored
629 fInputTensorInfos.erase(input.first);
630 // add to the ready input tensor information the new fixed shape
631 AddInputTensorInfo(input.first, input.second.type, shape);
632 // check consistency
634 }
635 // store the parameters of the input tensors
636 else {
637 // store the found parametric shape parameters
638 for (auto &d : input.second.shape) {
639 if (d.isParam) {
640 if (fShapeParams.count(d.param) == 0) {
641 fDimShapeNames.push_back(d.param);
642 fShapeParams[d.param] = std::to_string(d.dim);
643 }
644 }
645 }
646 }
647 }
648
649 if (verbose) {
652 }
653
654 // Go through model and initialize each operator
655 int i = 0;
656
657 std::vector<size_t> temp_available_stack; // vector stores individual chunks of available memory that maybe reused
658
659 // Build set of initialized tensors consumed by at least one runtime operator (need for later)
660 std::unordered_set<std::string> runtimeInitializedInputs;
661 for(size_t op_idx = 0; op_idx < fOperators.size(); ++op_idx){
662 if (verbose) {
663 auto& r = *fOperators[op_idx].get();
664 std::cout << "Initializing operator " << i << " " << typeid(r).name() << std::endl;
665 }
666 fOperators[op_idx]->Initialize(*this);
667 for(auto &it:fOperators[op_idx]->GetOpOutputTensors()){
668 std::string name = std::string{it};
669 // check if tensor is not an initialized or output tensor and it is not already in the list
671 std::find(fOutputTensorNames.begin(), fOutputTensorNames.end(), name) == fOutputTensorNames.end() &&
673 {
675 }
676 }
677 // loop for non-constant operators and flag the inputs which are initialized tensors to make sure they are writable
678 if (!fOperators[op_idx]->IsOutputConstant()) {
679 for (auto &it : fOperators[op_idx]->GetOpInputTensors()) {
680 std::string name = std::string{it};
681 if (fInitializedTensors.find(name) != fInitializedTensors.end()) {
683 }
684 }
685 }
686
687 i++;
688 }
689
690 // loop on initialized tensors and make the integers as constant to be
691 // not written in a weight file and check if the tensors flagged as not writable are really not writable,
692 // i.e. are not used by non constant operators
693 for (auto &it : fInitializedTensors) {
694 // check if not-writable tensors are really not writable, i.e. are not used by non constant operators
695 if (it.second.IsNotWritable() && runtimeInitializedInputs.find(it.first) != runtimeInitializedInputs.end()) {
696 it.second.SetWritable();
697 if (verbose) {
698 std::cout << "Initialized tensor " << it.first << " is flagged as not writable but is used by non constant operators, set it as writable \n";
699 }
700 }
701 // if the tensor is an integer we can flag it as constant since it will not be written in a weight file and it is considered equivalent as being created from a Constant operator
702 // only FLOAT tensors are written in a weight file
703 if (it.second.type() != ETensorType::FLOAT) {
704 it.second.SetConstant();
705 }
706 }
707
708 // check if there are initialized tensors to write in a weight file
709 if (fUseWeightFile) {
710 bool modelHasWeights = false;
711 for (auto &it : fInitializedTensors) {
712 if (it.second.IsWeightTensor()) {
713 modelHasWeights = true;
714 break;
715 }
716 }
717 if (!modelHasWeights)
718 fUseWeightFile = false;
719 }
720
721 // update fIntermediateTensorFrequencyLookup for alias tensors
722 for (auto & it : fAliasTensors) {
726 else {
727 // take the largest one
729 }
730 }
731
732 fIsInitialized = true;
733}
734
735void RModel::InitializeSubGraph(std::shared_ptr<RModel> graph) {
736 // add the subgraph to the list
737 fSubGraphs.push_back(graph);
738 //this needs to be done before initializing
739 graph->fParentGraph = this;
740 graph->fIsSubGraph = true;
741
742 graph->Initialize(fBatchSize, fVerbose);
743 // set the same options as parent model
744 graph->fWeightFile = fWeightFile;
745 graph->fUseWeightFile = fUseWeightFile;
746 graph->fUseSession = fUseSession;
747 // add needed blas routines and libs
748 std::vector<std::string> blasRoutines;
749 for (auto & e : graph->fNeededBlasRoutines)
750 blasRoutines.push_back(e);
752 for (auto e : graph->fNeededStdLib)
754 // helper functions used by the subgraph must be emitted in the top-level
755 // header, so propagate them to the parent model
756 for (auto const &h : graph->GetNeededHelperFunctions())
758
759 // add parent input tensors to current graph
760 for (auto & name : fInputTensorNames)
761 graph->fInputTensorNames.emplace_back(name);
762
763 // clean graph name
764 graph->fName = UTILITY::Clean_name(graph->fName);
765
766}
767
768// Function to generate the code for declaring and initializing constant tensors
769// This is for tensors which are not part of weight files and can be created from the Constant operator
770template <typename T>
771std::string GenerateConstantTensorCode(const std::pair<std::string, InitializedTensor> &t)
772{
773 std::stringstream strs;
774 std::string type = ConvertTypeToString(t.second.type());
775 size_t length = ConvertShapeToLength(t.second.shape());
776 // avoid using stack sizes for constant tensors to reduce compilation time
777 // also for weights which can be broadcasted do not use stack but allocate as a std::vector
778 bool allocateOnStack = (length > 100 || t.second.IsWeightTensor()) ? false : true;
779
780 const T *data = t.second.data<T>();
781
782 // and check if all values are the same
783 bool sameData = false;
784
785 // for non stack allocation check if data are the same
786 if (!allocateOnStack && length > 1) {
787 size_t idx = 1;
788 do {
789 sameData = (data[idx] == data[idx - 1]);
790 idx++;
791 } while (sameData && idx < length);
792 }
793 if (allocateOnStack) {
794 strs << type << " fTensor_" << t.first << "[" << length << "] = " << ConvertValuesToString(length, data) << ";\n";
795 strs << type << " * " << TensorMember(t.first) << " = fTensor_" + t.first + ";\n";
796 } else {
797 strs << "std::vector<" << type << "> fTensor_" << t.first << " = ";
798 if (sameData)
799 strs << "std::vector<" << type << ">(" << length << ", " << ConvertValToString(data[0]) << ");\n";
800 else {
802 }
803 strs << type << " * " << TensorMember(t.first) << " = fTensor_" + t.first + ".data();\n";
804 }
805 return strs.str();
806}
807
809{
810 if (!fInitializedTensors.empty())
811 fGC += "// initialized (weights and constant) tensors\n";
812
813 // here are constant tensor or initialized ones which are not weights (e.g. int64_t tensors )
814 for (auto &i : fInitializedTensors) {
815 if (i.second.IsNotWritable()) continue;
816 size_t length = ConvertShapeToLength(i.second.shape());
817 if (!fUseWeightFile || i.second.IsConstantTensor() || !i.second.IsWeightTensor() || i.second.type() != ETensorType::FLOAT ) {
818 if (i.second.type() == ETensorType::FLOAT) {
819 // check if NaN of Inf are inside tensor data
820 bool hasInfOrNaN = false;
821 const float *data = i.second.data<float>();
822 for (size_t idx = 0; idx < length; idx++) {
823 if (std::is_floating_point<float>::value) {
824 if (std::isinf(data[idx]) || std::isnan(data[idx])) {
825 hasInfOrNaN = true;
826 break;
827 }
828 }
829 }
830 if (hasInfOrNaN)
831 AddNeededStdLib("limits");
833 fConstantTensorSize += length * sizeof(float);
834 } else if (i.second.type() == ETensorType::INT64) {
836 fConstantTensorSize += length * sizeof(int64_t);
837 } else if (i.second.type() == ETensorType::INT32) {
839 fConstantTensorSize += length * sizeof(int32_t);
840 } else if (i.second.type() == ETensorType::BOOL || i.second.type() == ETensorType::UINT8 ) {
842 fConstantTensorSize += length * sizeof(uint8_t);
843 }
844
845
846 } else {
847 // case of tensors which are read from a file
848 if (i.second.type() == ETensorType::FLOAT) {
849 fGC += "std::vector<float> fTensor_" + i.first + " = std::vector<float>(" + std::to_string(length) + ");\n";
850 fGC += "float * " + TensorMember(i.first) + " = fTensor_" + i.first + ".data();\n";
851 fWeightsTensorSize += length * sizeof(float);
852 }
853 }
854 }
855}
856
858 if (fIntermediateMemoryInfo.total_stack.empty()) return;
859 fGC += "\n//--- Allocating session memory pool to be used for allocating intermediate tensors\n";
860
861 // char memory block is allocated since char takes 1 byte, thus easier to allocate tensors
862 // of other data types
864 const size_t memPoolSize = totalStack.rbegin()->first + totalStack.rbegin()->second.tensor_size;
865 fGC += "std::vector<char> fIntermediateMemoryPool = std::vector<char>(" + std::to_string(memPoolSize) + ");\n\n";
866}
867
869 if (!fIntermediateTensorInfos.empty()) {
870 std::string tensor_declaration_block = "";
871 for (auto &i : fIntermediateTensorInfos) {
872 bool is_alias = (IsAliasTensor(i.first));
873 if (i.second.type == ETensorType::BOOL && !is_alias) {
874 tensor_declaration_block += "std::vector<std::uint8_t> fTensor_" + i.first + " = std::vector<std::uint8_t>(" + std::to_string(ConvertShapeToLength(i.second.shape)) + ");\n";
875 tensor_declaration_block += "std::uint8_t * " + TensorMember(i.first) + " = fTensor_" + i.first + ".data();\n";
876 continue;
877 }
879 bool not_in_freq_map =
882 (std::find(fOutputTensorNames.begin(), fOutputTensorNames.end(), i.first) == fOutputTensorNames.end());
883
885 size_t length = ConvertShapeToLength(i.second.shape);
886
887 if (i.second.type == ETensorType::FLOAT) {
888 tensor_declaration_block += "std::vector<float> fTensor_" + i.first + " = std::vector<float>(" + std::to_string(length) + ");\n";
889 tensor_declaration_block += "float * " + TensorMember(i.first) + " = fTensor_" + i.first + ".data();\n";
891 }
892 else if (i.second.type == ETensorType::DOUBLE) {
893 tensor_declaration_block += "std::vector<double> fTensor_" + i.first + " = std::vector<double>(" + std::to_string(length) + ");\n";
894 tensor_declaration_block += "double * " + TensorMember(i.first) + " = fTensor_" + i.first + ".data();\n";
896 }
897 else if (i.second.type == ETensorType::INT64) {
898 tensor_declaration_block += "std::vector<int64_t> fTensor_" + i.first + " = std::vector<int64_t>(" + std::to_string(length) + ");\n";
899 tensor_declaration_block += "int64_t * " + TensorMember(i.first) + " = fTensor_" + i.first + ".data();\n";
901 }
902 }
903 if (is_alias) {
904 tensor_declaration_block += ConvertTypeToString(i.second.type) + " * " + TensorMember(i.first) + " = nullptr;\n";
905 }
906
907 }
908
909 if (tensor_declaration_block.length()) {
910 fGC += "\n//--- declare and allocate the intermediate tensors\n" + tensor_declaration_block;
911 }
912 }
913 // add also the dynamic tensors (only declarations, allocation will be done later)
914 if (!fDynamicTensorInfos.empty()) {
915 fGC += "//--- declare the dynamic tensors\n";
916 for (auto &i : fDynamicTensorInfos) {
917 fGC += ConvertTypeToString(i.second.type) + " * " + TensorMember(i.first) + " = nullptr;\n";
918 }
919 fGC += "//--- dynamic tensors pool\n";
920 fGC += "std::vector<char> fDynamicMemoryPool;\n";
921 }
922}
923
924// generate code for specific operator declarations to be defined in the Session class
926 std::string strcode;
927 for (auto & op : fOperators) {
928 strcode += op->GenerateDeclCode();
929 }
930 if (strcode.empty()) return;
931 fGC += "\n//---- operator declarations \n";
932 fGC += strcode;
933 fGC += "\n";
934}
935
937{
938 // generate code for allocating dynamic tensors using the greedy memory allocations
939 if (fDynamicTensorInfos.empty())
940 return;
941
942 if (fVerbose) {
943 std::cout << "generating code for dynamic tensor management" << std::endl;
945 }
946
947 // the generated code uses the TensorLifeInfo / OrganizeMemory inference helpers
948 AddNeededHelperFunction("DynamicMemory");
949
950 std::stringstream out;
951 out << "// dynamic tensor memory management\n";
952 out << SP << "std::vector<TensorLifeInfo> dynamicTensorInfos;\n";
953 out << SP << "dynamicTensorInfos.reserve(" << fDynamicTensorInfos.size() << ");\n";
954
955 // loop on all the operators to find begin/end life of the tensors
956 int op_index = 0;
957 std::vector<std::pair<std::string, ETensorType>> tensors;
958 tensors.reserve(fDynamicTensorInfos.size());
959 for (auto & op : fOperators) {
960 // loop on output tensors -
961 for (auto &it : op->GetOpOutputTensors()) {
962 if (fVerbose) {
963 auto op_ptr = op.get();
964 std::cout << "Looping on operator " << op_index << " " << typeid(*op_ptr).name() << std::endl;
965 }
966 // check if is a dynamic tensor and not an alias tensor or output tensor
967 std::string name = std::string(it);
969 && std::find(fOutputTensorNames.begin(), fOutputTensorNames.end(), name) == fOutputTensorNames.end()) {
971 auto type = GetTensorType(name);
972 size_t type_size = GetTypeSize(type);
973 int begin = op_index;
974 int end = fOperators.size();
975 // look for end
978 end = it_lookup->second + 1; // end is last time used + 1
979 // // some tensors (like xcol in convolutions) are just used within the operators
980 // if (end == 0 && begin > 0) end = begin+1;
981
982 if (begin> end) {
983 std::cout << "op " << op_index << "tensor_" << name << " begin " << begin << " " << " end " << end << std::endl;
984 throw std::runtime_error("TMVA-SOFIE: RModel::GenerateDynamicTensorInfo: tensor_" + name + " has end before begin");
985 }
986
987 // write in code
988 out << SP << "dynamicTensorInfos.push_back( {" << begin << ", " << end << ", " << type_size << "* (" << tensor_size << ") });"
989 << " // tensor_" << name << std::endl;
990 tensors.push_back({name,type});
991 }
992 }
993 op_index++; // increment operator index
994 }
995 out << "\n" << SP << "auto memory_result = OrganizeMemory(dynamicTensorInfos);\n\n";
996 out << "// allocating now the memory\n";
997 out << SP << "fDynamicMemoryPool = std::vector<char>(memory_result.total_bytes);\n";
998 out << SP << "int idx = 0;\n";
999 for (auto & it : tensors) {
1000 out << SP << "tensor_" << it.first << " = reinterpret_cast<" << ConvertTypeToString(it.second) << " *>(fDynamicMemoryPool.data() + memory_result.offsets[idx++]);\n";
1001 }
1002 // check that all dynamic tensors are covered
1003 bool missingTensor = false;
1004 for (auto &i : fDynamicTensorInfos) {
1005 if (IsAliasTensor(i.first)) continue;
1006 if (std::find(fOutputTensorNames.begin(), fOutputTensorNames.end(), i.first) != fOutputTensorNames.end()) continue;
1007 if (std::find(tensors.begin(), tensors.end(), std::pair<std::string,ETensorType>{i.first, i.second.type}) == tensors.end()) {
1008 std::cout << "Dynamic tensors " << i.first << " is not in list of operator input/output " << std::endl;
1009 missingTensor = true;
1010 }
1011 }
1012 if (missingTensor)
1013 throw std::runtime_error("TMVA-SOFIE: RModel::GenerateDynamicTensorInfo - some tensors are not in input/output list");
1014
1015 fGC += out.str();
1016}
1017
1018/// Check if a given parameter is used for the shape of an input tensor.
1019bool RModel::IsInputTensorShapeParam(std::string const &paramName) const
1020{
1021 for (auto &name : fInputTensorNames) {
1022 if (IsDimInputTensor(name)) {
1023 auto shape = GetDynamicTensorShape(name);
1024 for (auto &d : shape) {
1025 if (d.param == paramName)
1026 return true;
1027 }
1028 }
1029 }
1030 return false;
1031}
1032
1033/// Collects all identifiers starting with "tensor_" in the input code,
1034/// provided that the occurrence is not immediately preceded by a
1035/// character that is valid in a C++ identifier. Excludes input and output tensor names.
1036/// Returns a deduplicated std::vector<std::string>.
1037std::vector<std::string> RModel::CollectTensorMemberNames(const std::string &input)
1038{
1039 const std::string target = "tensor_";
1040
1041 std::vector<std::string> result;
1042
1043 for (size_t i = 0; i < input.size();) {
1044
1045 bool doCollect = false;
1046
1047 if (i + target.size() <= input.size() && input.compare(i, target.size(), target) == 0 &&
1048 (i == 0 || !IsIdentifierChar(input[i - 1]))) {
1049
1050 doCollect = true;
1051
1052 std::size_t j = i + target.size();
1053
1054 // Extend to full identifier
1055 while (j < input.size() && IsIdentifierChar(input[j]))
1056 ++j;
1057
1058 std::string fullName = input.substr(i, j - i);
1059
1060 // Exclude input tensor names
1061 for (std::string const &name : fInputTensorNames) {
1062 if (fullName == target + name) {
1063 doCollect = false;
1064 break;
1065 }
1066 }
1067
1068 // Exclude output tensor names
1069 if (doCollect) {
1070 for (std::string const &name : fOutputTensorNames) {
1071 if (fullName == target + name) {
1072 doCollect = false;
1073 break;
1074 }
1075 }
1076 }
1077
1078 if (doCollect) {
1079 result.push_back(fullName);
1080 }
1081
1082 i = j; // advance past the identifier
1083 } else {
1084 ++i;
1085 }
1086 }
1087
1088 // Deduplicate (order not preserved)
1089 std::sort(result.begin(), result.end());
1090 result.erase(std::unique(result.begin(), result.end()), result.end());
1091
1092 return result;
1093}
1094
1096 // generate the infer signature given the inputs: eg. "float * tensor1, float * tensor2"
1097 // if (decl = false) generate only calling signature (tensor1,tensor2,....)
1098 std::string rGC;
1099 std::unordered_map<std::string, int> inputParams;
1100 int i_input = 0;
1101 for (auto &name : fInputTensorNames) {
1102 // if is a dynamic tensor pass initial parameters
1103 if (IsDimInputTensor(name)) {
1104 auto shape = GetDynamicTensorShape(name);
1105 for (auto &d : shape) {
1106 std::string pName = d.param;
1107 // need to check if the input parameters is already existing in another input tensor
1108 if (d.isParam && inputParams.count(pName) == 0) {
1109 if (isdecl) rGC += "size_t ";
1110 rGC += d.param + ",";
1112 }
1113 }
1114 }
1115 if (isdecl) {
1117 if (type == "other")
1118 throw std::runtime_error("TMVA-SOFIE: input tensor " + name +
1119 " is of a data type which is not yet supported.");
1120 rGC += type + " const* ";
1121 }
1122 rGC += "tensor_" + name + ",";
1123 i_input++;
1124 }
1125
1126 if (fInputTensorNames.size() > 0) rGC.pop_back();// remove last ","
1127 return rGC;
1128}
1129
1130namespace {
1131
1132std::string typeForOutput(ETensorType t) {
1133 // The std::vector<bool> is a special type that is not wrapping continuous memory.
1134 // We don't want to use it as a return type.
1136 return ConvertTypeToString(t);
1137}
1138
1139std::string memberNameForDimShape(std::string name)
1140{
1141 if (!name.empty()) {
1142 name[0] = std::toupper(static_cast<unsigned char>(name[0]));
1143 }
1144 name = "f" + name;
1145 return name;
1146}
1147
1148}
1149
1151{
1152 size_t outputSize = fOutputTensorNames.size();
1153 // assume output types are all the same
1154
1155 bool sameOutputTypes = true;
1156 std::string inferReturnType; // type return by infer function
1158 fGC += "\n\n";
1159 if (outputSize == 1) {
1160 fGC += "std::vector<" + typeForOutput(eFirstOutputType) + ">";
1161 } else {
1162 // if all output types are the same we return an std::vector - otherwise a tuple
1163 for (std::string const &name : fOutputTensorNames) {
1165 sameOutputTypes = false;
1166 }
1167 if (sameOutputTypes)
1168 fGC += "std::vector<std::vector<" + typeForOutput(eFirstOutputType) + ">>";
1169 else {
1170 inferReturnType = "std::tuple<";
1171 for (size_t i = 0; i < outputSize; i++) {
1172 inferReturnType += "std::vector<" + typeForOutput(GetTensorType(fOutputTensorNames[i])) + ">";
1173 if (i < outputSize - 1)
1174 inferReturnType += ",";
1175 }
1176 inferReturnType += ">";
1178 }
1179 }
1180
1181 fGC += " infer(" + GenerateInferSignature() + "){\n";
1182
1183 std::string doInferArgs = GenerateInferSignature(false);
1184 if (!doInferArgs.empty())
1185 doInferArgs += ",";
1186 for (std::string const &name : fOutputTensorNames) {
1187 bool isDynamic = fDynamicTensorInfos.count(name) > 0;
1188 std::string n;
1189 if(!isDynamic) {
1190 n = std::to_string(ConvertShapeToLength(GetTensorShape(name)));
1191 } else {
1193 // Use the session member (fXxx) when any dim is a runtime-computed identifier
1194 // (e.g. NonZero count). For expression-type dims derived from input shapes
1195 // (e.g. "((W+-3)/2+1)"), use the expression directly.
1196 // for input shape parameters we don't need to use the session member since it is passed as argument to the infer function and it is not a runtime computed value
1197 bool hasRuntimeParam = false;
1198 for (auto const &dim : GetDynamicTensorShape(name)) {
1199 if (dim.isParam && IsIdentifier(dim.param) && !IsInputTensorShapeParam(dim.param))
1200 hasRuntimeParam = true;
1201 }
1203 }
1204 std::string outputName = "output_tensor_" + name;
1205 fGC += SP + "std::vector<" + typeForOutput(GetTensorType(name)) + " > " + outputName + "(" + n + ");\n";
1206 doInferArgs += " " + outputName + ".data(),";
1207 if(isDynamic) {
1208 for (auto const &dim : GetDynamicTensorShape(name)) {
1209 if (dim.isParam && !IsInputTensorShapeParam(dim.param) && IsIdentifier(dim.param)) {
1210 fGC += SP + "size_t " + dim.param + " = 0;\n";
1211 doInferArgs += " " + dim.param + ",";
1212 }
1213 }
1214 }
1215 }
1216 if (!doInferArgs.empty())
1217 doInferArgs.back() = ' ';
1218
1219 // verifying if the dynamic parameters are within allowed range
1220 std::unordered_set<std::string> input_params_checked;
1221 std::string dynamic_parameters_check = "";
1222 for (auto &name : fInputTensorNames) {
1223 if (IsDimInputTensor(name)) {
1224 auto shape = GetDynamicTensorShape(name);
1225 for (auto &d : shape) {
1226 std::string pName = d.param;
1227 if (d.isParam && input_params_checked.count(pName) == 0) {
1228 std::string memberName = memberNameForDimShape(d.param);
1229 dynamic_parameters_check += d.param + " > " + memberName + " || ";
1231 fGC += SP + "if (" + d.param + " > " + memberName + ") {\n";
1232 fGC += SP + SP + "throw std::runtime_error(\"TMVA-SOFIE: dynamic input tensor shape parameter " +
1233 d.param + " exceeds the initialized maximum allowed shape.\");\n";
1234 fGC += SP + "}\n";
1235 }
1236 }
1237 }
1238 }
1239
1240 if (fUseSession) {
1241 fGC += SP + "doInfer(*this, " + doInferArgs + ");\n";
1242 } else {
1243 fGC += SP + "doInfer(" + doInferArgs + ");\n";
1244 }
1245
1246 // If the output tensors have dynamic sizes, now is the time to set them
1247 for (std::string const &name : fOutputTensorNames) {
1248 bool isDynamic = fDynamicTensorInfos.count(name) > 0;
1249 if (isDynamic) {
1250 std::string outputName = "output_tensor_" + name;
1251 auto tensor_size = ConvertDimShapeToLength(GetDimTensorShape(name));
1252 fGC += SP + outputName + ".resize(" + tensor_size + ");\n";
1253 }
1254 }
1255
1256 fGC += SP + "return {";
1257 for (size_t i = 0; i < fOutputTensorNames.size(); i++) {
1258 fGC += "output_tensor_" + fOutputTensorNames[i];
1259 if (i < fOutputTensorNames.size() - 1)
1260 fGC += ",";
1261 }
1262 fGC += "};\n";
1263 fGC += "}\n"; // end of infer function scope
1264}
1265
1267{
1268 std::string sessionName = !fIsSubGraph ? "Session" : "Session_" + fName;
1269
1270 if (fUseSession && !fIsGNNComponent) {
1271 // forward declare session struct
1272 fGC += "struct " + sessionName + ";\n";
1273 }
1274
1275 // Determine the signature of the actual inference function
1277 if (!doInferSignature.empty())
1278 doInferSignature += ", ";
1279 for (auto const &name : fOutputTensorNames) {
1280 bool isDynamic = fDynamicTensorInfos.count(name) > 0;
1281 doInferSignature += typeForOutput(GetTensorType(name)) + " *tensor_" + name + ",";
1282 if(isDynamic) {
1283 for (auto const &dim : GetDynamicTensorShape(name)) {
1284 if (dim.isParam && !IsInputTensorShapeParam(dim.param) && IsIdentifier(dim.param))
1285 doInferSignature += " size_t &" + dim.param + "_output,";
1286 }
1287 }
1288 }
1289 doInferSignature.back() = ' ';
1290
1291 if (fUseSession) {
1292 doInferSignature = sessionName + " const &session, " + doInferSignature;
1293 }
1294
1295 doInferSignature = "inline void doInfer(" + doInferSignature + ")";
1296
1297 if (!fIsGNNComponent) {
1298 // forward declare inference implementation
1299 fGC += doInferSignature + ";\n";
1300 }
1301
1302 // define the Session struct (for GNN this is generated in RModel_GNN)
1303 if (fUseSession && !fIsGNNComponent) {
1304 fGC += "struct " + sessionName + " {\n";
1305 }
1306
1307 // generate code for declaring the initialized tensors
1309
1311 // evaluate total intermediate memory and position intermediate tensor addresses
1312 std::string intermediate_memory_alloc_string = "";
1313 intermediate_memory_alloc_string += "\n// --- Positioning intermediate tensor memory --";
1314 for (size_t op_idx = 0; op_idx < fOperators.size(); ++op_idx) {
1315 if (fVerbose) {
1316 auto op = fOperators[op_idx].get();
1317 std::cout << "\n******************\n analyzing input/output operator " << op_idx << " "
1318 << typeid(*op).name() << std::endl;
1319 }
1322 }
1323
1324 // to check remaining unused fragments after memory allocation (lesser the better)
1325 // for (const auto &it: fIntermediateMemoryInfo.available_stack){
1326 // std::cout<<"chunk_idx: "<<it.first<<", chunk_size: "<<it.second<<"\n";
1327 // }
1328
1329 // generate the memory pool to be used by intermediate tensors
1331
1332 // position intermediate tensors
1334 }
1335
1336 // generate the declaring the intermediate tensors
1338 // generate code for declarations of some specific operators
1340
1341 // storing the parameters for future checking to avoid mismatches
1342 if (!fDimShapeNames.empty()) {
1343 fGC += "\n// dynamic shape parameters\n";
1345 std::sort(dimShapeNames.begin(), dimShapeNames.end());
1346 for (const auto &p : dimShapeNames) {
1347 fGC += "size_t " + memberNameForDimShape(p) + ";\n";
1348 }
1349 }
1350
1351 // add subgraph session
1352 if (!fSubGraphs.empty()) fGC += "// subgraph sessions\n";
1353 for (auto & graph : fSubGraphs) {
1354 fGC += "Session_" + graph->fName + " fSession_" + graph->fName + ";\n";
1355 }
1356
1357 // Generate code for Session constructor
1358 if (fUseSession) {
1359 // add here specific operator code that needs to define session data members
1360 fGC += "\n";
1361 for (size_t id = 0; id < fOperators.size(); id++) {
1362 std::string opName = std::to_string(id);
1363 fGC += fOperators[id]->GenerateSessionMembersCode(opName);
1364 }
1365 fGC += "\n";
1366 // here add initialization and reading of weight tensors
1367 if (fUseWeightFile) {
1368 std::string fileName = fName;
1370 fileName += ".dat";
1371 }
1373 fileName += ".root";
1374 }
1375 fGC += sessionName + "(std::string filename =\"" + fileName + "\"";
1376 } else {
1377 // no need to pass weight file since it is not used
1378 // keep passing a string for compatibility
1379 fGC += sessionName + "(std::string = \"\"";
1380 }
1381 // add initialization of shape parameters
1382 // assume all parameters are of type size_t
1383 if (!fDimShapeNames.empty()) {
1384 // need to use same order as in infer function not alphabetical one
1385 for (auto &p : fDimShapeNames) {
1386 fGC += ",\n";
1387 fGC += " size_t " + p + " = " + fShapeParams[p];
1388 }
1389 }
1390 fGC += ") {\n";
1391
1392 // initializing dynamic parameters
1393 if (!fDimShapeNames.empty()) {
1394 fGC += "\n\n";
1395 std::sort(fDimShapeNames.begin(), fDimShapeNames.end());
1396 for (const auto &p : fDimShapeNames) {
1397 fGC += " " + memberNameForDimShape(p) + " = " + p + ";\n";
1398 }
1399 }
1400 // add some extra code needed for initialization of dynamic parameters
1402
1403 if (fUseWeightFile) {
1404 fGC += "\n//--- reading weights from file\n";
1406 fGC += "\n";
1407 // fUseWeightFile = fUseWeightFile;
1408 }
1409
1410 // now we have passed the parameters we can allocate the dynamic tensors
1412
1413 // add here initialization code for operator
1414 for (size_t id = 0; id < fOperators.size(); id++) {
1415 fGC += fOperators[id]->GenerateInitCode();
1416 }
1417
1418 fGC += "}\n\n";
1419
1420 // Used to build the tangent Session objects needed to differentiate the
1421 // generated code with Clad: the derivatives of the (constant) weights
1422 // are zero, but a default-constructed Session holds the actual values.
1423 fGC += "// Set all weight and constant tensors to zero. This is useful to create\n"
1424 "// the tangent Session objects needed to differentiate the generated code\n"
1425 "// with Clad.\n"
1426 "void SetWeightsToZero() {\n";
1427 for (auto &i : fInitializedTensors) {
1428 // IsNotWritable tensors have no emitted fTensor_ member, and integer
1429 // tensors are structural (indices, shapes) that carry no tangent.
1430 if (i.second.IsNotWritable() ||
1431 (i.second.type() != ETensorType::FLOAT && i.second.type() != ETensorType::DOUBLE))
1432 continue;
1433 fGC += " for (auto &v : fTensor_" + i.first + ") v = 0;\n";
1434 }
1435 for (auto &graph : fSubGraphs) {
1436 fGC += " fSession_" + graph->fName + ".SetWeightsToZero();\n";
1437 }
1438 fGC += "}\n\n";
1439 }
1440
1441 // generate the inference overload that returns an output struct
1443
1444 // end of session
1445 if (fUseSession && !fIsGNNComponent) {
1446 fGC += "}; // end of Session\n\n";
1447
1449 }
1450
1451 fGC += doInferSignature + " {\n";
1452 fGC += "\n";
1453
1454 // generate the inference code
1455 if (fVerbose)
1456 std::cout << "Generating main inference code for " << fName << std::endl;
1457
1458 if (fOutputTensorNames.size() == 0)
1459 throw std::runtime_error("TMVA-SOFIE: output size=0 are not supported");
1460
1461 std::string allOperatorCode;
1462
1463 for (size_t op_idx = 0; op_idx < fOperators.size(); ++op_idx) {
1464 if (fVerbose)
1465 std::cout << "Generating code for operator .... " << op_idx << std::endl;
1466 std::string operatorCode = fOperators[op_idx]->Generate(std::to_string(op_idx));
1468 }
1469
1470 // If the generated code users members of the session struct, use the
1471 // local variable name that we're using for the session:
1472 ReplaceAll(allOperatorCode, "this->", "session.");
1473
1474 if (fUseSession && !fIsGNNComponent) {
1475 // Collect all "tensor_*" data members that are not input or output tensors
1477 for (auto const& name: tensorMemberNames) {
1478 fGC += " auto &" + name + " = session." + name + ";\n";
1479 }
1480 fGC += "\n";
1481 }
1482
1484
1485 for (auto const& name: fOutputTensorNames) {
1486 bool isDynamic = fDynamicTensorInfos.count(name) > 0;
1487 if(isDynamic) {
1488 for (auto const &dim : GetDynamicTensorShape(name)) {
1489 if (dim.isParam && !IsInputTensorShapeParam(dim.param) && IsIdentifier(dim.param))
1490 fGC += " " + dim.param + "_output = " + dim.param + ";\n";
1491 }
1492 }
1493 if(IsConstantTensor(name)) {
1494 std::string t = "session.tensor_" + name;
1496 fGC += " std::copy(" + t + ", " + t + " + " + std::to_string(length) + ", tensor_" + name + ");\n";
1497 }
1498 }
1499 fGC += "\n";
1500
1501 fGC += "}\n";
1502}
1503
1504void RModel::Generate(std::underlying_type_t<Options> options, int batchSize, long pos, bool verbose)
1505{
1506 fVerbose = verbose;
1507 fBatchSize = batchSize;
1508 fReadPos = pos;
1509
1510 // session flag is used in operator initialize
1511 if (static_cast<std::underlying_type_t<Options>>(Options::kNoSession) & options) {
1512 fUseSession = false;
1514 }
1515 if (static_cast<std::underlying_type_t<Options>>(Options::kNoWeightFile) & options) {
1516 fUseWeightFile = false;
1518 }
1519 if (static_cast<std::underlying_type_t<Options>>(Options::kRootBinaryWeightFile) & options) {
1520 fUseWeightFile = true;
1522 }
1523 if (fUseWeightFile && !fUseSession) {
1524 throw std::runtime_error(
1525 "TMVA-SOFIE: RModel::Generate: cannot use a separate weight file without generating a Session class");
1526 }
1527
1528 if (static_cast<std::underlying_type_t<Options>>(Options::kGNN) & options)
1529 fIsGNN = true;
1530 if (static_cast<std::underlying_type_t<Options>>(Options::kGNNComponent) & options)
1531 fIsGNNComponent = true;
1532
1533 // initialize the model including all operators and sub-graphs
1534 Initialize(batchSize, verbose);
1535
1536 // if having dynamic tensor we need to have a Session
1537 if (!fDynamicTensorInfos.empty()) {
1538 fUseSession = true;
1539 if (verbose)
1540 std::cout << "Warning: Force having a Session since model has dynamic tensors " << std::endl;
1541 }
1542
1543 std::string hgname;
1544 if (!fIsGNNComponent && !fIsSubGraph) {
1545 fGC.clear();
1547 }
1548
1549 // generate first code for the subgraphs
1550 for (auto &graph : fSubGraphs) {
1551 if (fVerbose)
1552 std::cout << "generate session code for subgraph " << graph->fName << std::endl;
1553 graph->GenerateSessionCode();
1554 fGC += graph->fGC;
1555 }
1556
1557 if (fVerbose)
1558 std::cout << "generate Main session code - model " << fName << std::endl;
1559
1560 // generate main session code
1562
1563 if (!fIsGNNComponent && !fIsSubGraph) {
1564 fGC += ("} //TMVA_SOFIE_" + fName + "\n");
1565 fGC += "\n#endif // " + hgname + "\n";
1566 // dump the standalone definitions of the helper functions this model uses
1567 // so that the generated header does not depend on TMVA/SOFIE_common.hxx
1569 }
1570}
1571
1573 // generate the code to read initialized tensors from a text data file
1575 // check if there are tensors to write
1576
1577 if (!fUseWeightFile) return;
1578
1579 fGC += " std::ifstream f;\n";
1580 fGC += " f.open(filename);\n";
1581 fGC += " if (!f.is_open()) {\n";
1582 fGC += " throw std::runtime_error(\"tmva-sofie failed to open file \" + filename + \" for input weights\");\n";
1583 fGC += " }\n";
1584
1585 if(fIsGNNComponent) {
1586 fGC += " f.seekg(" + std::to_string(pos) + ");\n";
1587 }
1588
1589 // ReadTensorFromStream is emitted as a standalone helper in the header
1590 AddNeededHelperFunction("ReadTensorFromStream");
1591
1592 // loop on tensors and parse the file
1593 for (auto& i: fInitializedTensors) {
1594 // skip Constant and shape tensors (not written in a file)
1595 if (!i.second.IsWeightTensor()) continue;
1596 std::string tensor_name = "tensor_" + i.first;
1597 if (i.second.type() == ETensorType::FLOAT) {
1598 std::string length = std::to_string(ConvertShapeToLength(i.second.shape()));
1599 fGC += " ReadTensorFromStream(f, " + tensor_name + ", \"" + tensor_name + "\", " + length + ");\n";
1600 } else {
1601 throw std::runtime_error("tmva-sofie tensor " + tensor_name + " with type " + ConvertTypeToString(i.second.type()) + " cannot be read from a file");
1602 }
1603 }
1604 fGC += " f.close();\n";
1605 }
1606
1607 // generate the code to read initialized tensors from a ROOT data file
1609#ifdef SOFIE_SUPPORT_ROOT_BINARY
1610 fGC += " {\n";
1611 fGC += " std::unique_ptr<TFile> rootFile(TFile::Open(filename.c_str(), \"READ\"));\n";
1612 fGC += " if (!rootFile->IsOpen()) {\n";
1613 fGC += " throw std::runtime_error(\"tmva-sofie failed to open ROOT file for input weights\");\n";
1614 fGC += " }\n";
1615
1616 std::string dirName = fName + "_weights";
1617 fGC += " if (!rootFile->GetKey(\"" + dirName + "\")) {\n";
1618 fGC += " throw std::runtime_error(\"tmva-sofie failed to open ROOT directory for input weights\");\n";
1619 fGC += " }\n";
1620
1621 for (auto &i : fInitializedTensors) {
1622 // skip Constant and shape tensors
1623 if (!i.second.IsWeightTensor()) continue;
1624 fGC += " {\n";
1625 std::string tensor_name = "tensor_" + i.first;
1626 if (i.second.type() == ETensorType::FLOAT) {
1627 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<float>*>(rootFile->Get(\"";
1628 fGC += dirName + "/" + tensor_name + "\"));\n";
1629 } else if (i.second.type() == ETensorType::DOUBLE) {
1630 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<double>*>(rootFile->Get(\"";
1631 fGC += dirName + + "/" + tensor_name + "\"));\n";
1632 } else if (i.second.type() == ETensorType::INT64) {
1633 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<int64_t>*>(rootFile->Get(\"";
1634 fGC += dirName + "/" + tensor_name + "\"));\n";
1635 } else {
1636 throw std::runtime_error("tmva-sofie tensor " + tensor_name + " with type " + ConvertTypeToString(i.second.type()) + " cannot be read from a ROOT file");
1637 }
1638 fGC += " }\n";
1639 }
1640 fGC += " }\n";
1641#else
1642 throw std::runtime_error("SOFIE was not built with ROOT file support.");
1643#endif // SOFIE_SUPPORT_ROOT_BINARY
1644 }
1645}
1646
1648 // Determine the file extension based on the weight file type
1649 std::string fileExtension;
1650 switch (fWeightFile) {
1652 fileExtension = ".dat";
1653 break;
1655 fileExtension = ".root";
1656 break;
1658 fileExtension = ".dat";
1659 break;
1660 }
1661
1662 // If filename is empty, use the model name as the base filename
1663 if (filename.empty()) {
1665 }
1666
1667 // Write the initialized tensors to the file
1669#ifdef SOFIE_SUPPORT_ROOT_BINARY
1670 if(fIsGNNComponent || fIsGNN) {
1671 throw std::runtime_error("SOFIE-GNN yet not supports writing to a ROOT file.");
1672 }
1673 std::unique_ptr<TFile> outputFile(TFile::Open(filename.c_str(), "UPDATE"));
1674
1675 std::string dirName = fName + "_weights";
1676 // check if directory exists, in case delete to replace with new one
1677 if (outputFile->GetKey(dirName.c_str()))
1678 outputFile->rmdir(dirName.c_str());
1679
1680 auto outputDir = outputFile->mkdir(dirName.c_str());
1681
1682 for (const auto& item : fInitializedTensors) {
1683 // skip Constant tensors and tensors which are not writable (e.g. shape tensors)
1684 if (!item.second.IsWeightTensor()) continue;
1685 std::string tensorName = "tensor_" + item.first;
1686 size_t length = 1;
1687 length = ConvertShapeToLength(item.second.shape());
1688 if(item.second.type() == ETensorType::FLOAT) {
1689 const float* data = item.second.data<float>();
1690 std::vector<float> tensorDataVector(data, data + length);
1691 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<float>", tensorName.c_str());
1692 }
1693 else if(item.second.type() == ETensorType::DOUBLE) {
1694 const double* data = item.second.data<double>();
1695 std::vector<double> tensorDataVector(data, data + length);
1696 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<double>", tensorName.c_str());
1697 }
1698 else if(item.second.type() == ETensorType::INT64) {
1699 const int64_t* data = item.second.data<int64_t>();
1700 std::vector<int64_t> tensorDataVector(data, data + length);
1701 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<int64_t>", tensorName.c_str());
1702 }
1703 else {
1704 throw std::runtime_error("tmva-sofie tensor " + tensorName + " with type " + ConvertTypeToString(item.second.type()) +
1705 " cannot be written to a ROOT file");
1706 }
1707 }
1708 outputFile->Write(filename.c_str());
1709
1710 // this needs to be changed, similar to the text file
1711 return -1;
1712
1713#else
1714 throw std::runtime_error("SOFIE was not built with ROOT file support.");
1715#endif // SOFIE_SUPPORT_ROOT_BINARY
1716 } else if (fWeightFile == WeightFileType::Text) {
1717 std::ofstream f;
1718 if(fIsGNNComponent) {
1719 // appending all GNN components into the same file
1720 f.open(filename, std::ios::app);
1721 } else {
1722 f.open(filename);
1723 }
1724 if (!f.is_open())
1725 throw
1726 std::runtime_error("tmva-sofie failed to open file " + filename + " for tensor weight data");
1727 for (auto& i: fInitializedTensors) {
1728 // skip Constant tensors and not writable tensors (e.g. shape tensors)
1729 if (!i.second.IsWeightTensor()) {
1730 continue;
1731 }
1732 size_t length = ConvertShapeToLength(i.second.shape());
1733 std::string tensor_name = "tensor_" + i.first;
1734 f << tensor_name << " " << length << "\n";
1735 if (i.second.type() == ETensorType::FLOAT) {
1736 const float * data = i.second.data<float>();
1737 for (size_t idx = 0; idx < length; idx++) {
1738 // round to zero sub-normal values
1739 float value = data[idx];
1740 if (value != 0. && std::abs(value) < std::numeric_limits<float>::min() ) value = 0;
1741 // handle non-finite values explicitly
1742 if (std::isinf(value))
1743 f << (value > 0 ? "inf" : "-inf");
1744 else if (std::isnan(value))
1745 f << "nan";
1746 else
1747 f << std::setprecision(std::numeric_limits<float>::max_digits10) << value;
1748 f << ( (idx < length-1) ? " " : "\n" );
1749 }
1750 }
1751 else {
1752 throw std::runtime_error("tmva-sofie tensor " + tensor_name + " with type " + ConvertTypeToString(i.second.type()) + " cannot be written to a file");
1753 }
1754 if (f.fail())
1755 throw std::runtime_error("tmva-sofie failed to write tensor data to file for " + tensor_name);
1756 }
1757 long curr_pos = f.tellp();
1758 f.close();
1759 return curr_pos;
1760 } else {
1761 return -1;
1762 }
1763}
1764
1766 std::cout << "Summary of model " << GetName() << std::endl;
1767 for(size_t op_idx = 0; op_idx < fOperators.size(); ++op_idx){
1768 auto& r = *fOperators[op_idx].get();
1769 std::string raw_name = typeid(r).name();
1770 // look for ROperator_NAME
1771 std::string name = raw_name.substr(raw_name.find("ROperator_")+10, raw_name.size());
1772 std::cout << op_idx << " " << name << " : ";
1773 for (auto & t_in : r.GetOpInputTensors()) std::cout << t_in << " ";
1774 std::cout << " ----> ";
1775 for (auto & t_out : r.GetOpOutputTensors()) std::cout << t_out << " ";
1776 std::cout << std::endl;
1777 }
1778}
1779
1780/// To emit the dimensions of the input tensors as a data member of a session,
1781/// which is helpful when validating the inference inputs.
1783{
1784 fGC += "\n// Input tensor dimensions\n";
1785 // SingleDim / TensorDims / makeDims are emitted as standalone helpers
1786 AddNeededHelperFunction("InputTensorDims");
1787 bool hasDynamicInputTensors = false;
1788
1789 for (std::size_t iInput = 0; iInput < fInputTensorNames.size(); ++iInput) {
1790 auto const &name = fInputTensorNames[iInput];
1791 if (IsDimInputTensor(name)) {
1793 }
1794 std::vector<Dim> shape = GetDimTensorShape(name);
1795 fGC += "constexpr std::array<SingleDim, " + std::to_string(shape.size()) + "> dim_" + name + "{";
1796 for (std::size_t iDim = 0; iDim < shape.size(); ++iDim) {
1797 auto const &dim = shape[iDim];
1798 if (dim.isParam) {
1799 fGC += "SingleDim{\"" + dim.GetVal() + "\"}";
1800 } else {
1801 fGC += "SingleDim{" + dim.GetVal() + "}";
1802 }
1803 if (iDim != shape.size() - 1) {
1804 fGC += ", ";
1805 }
1806 }
1807 fGC += "};\n";
1808 }
1809 fGC += "\nconstexpr std::array<TensorDims, " + std::to_string(fInputTensorNames.size()) + "> inputTensorDims{\n";
1810 for (std::size_t iInput = 0; iInput < fInputTensorNames.size(); ++iInput) {
1811 auto const &name = fInputTensorNames[iInput];
1812 fGC += SP + "makeDims(dim_" + name + ")";
1813 if (iInput == fInputTensorNames.size() - 1) {
1814 fGC += "\n";
1815 } else {
1816 fGC += ",\n";
1817 }
1818 }
1819 fGC += "};\n";
1820
1821 fGC +=
1822 "\nconstexpr bool hasDynamicInputTensors{" + std::string{hasDynamicInputTensors ? "true" : "false"} + "};\n\n";
1823
1824 fGC += "\n// Output tensor dimensions\n";
1825 bool hasDynamicOutputTensors = false;
1826 for (std::size_t iOutput = 0; iOutput < fOutputTensorNames.size(); ++iOutput) {
1827 auto const &name = fOutputTensorNames[iOutput];
1828 if (IsDynamicTensor(name)) {
1830 }
1831 std::vector<Dim> shape = GetDimTensorShape(name);
1832 fGC += "constexpr std::array<SingleDim, " + std::to_string(shape.size()) + "> dim_" + name + "{";
1833 for (std::size_t iDim = 0; iDim < shape.size(); ++iDim) {
1834 auto const &dim = shape[iDim];
1835 if (dim.isParam) {
1836 fGC += "SingleDim{\"" + dim.GetVal() + "\"}";
1837 } else {
1838 fGC += "SingleDim{" + dim.GetVal() + "}";
1839 }
1840 if (iDim != shape.size() - 1) {
1841 fGC += ", ";
1842 }
1843 }
1844 fGC += "};\n";
1845 }
1846 fGC += "\nconstexpr std::array<TensorDims, " + std::to_string(fOutputTensorNames.size()) + "> outputTensorDims{\n";
1847 for (std::size_t iOutput = 0; iOutput < fOutputTensorNames.size(); ++iOutput) {
1848 auto const &name = fOutputTensorNames[iOutput];
1849 fGC += SP + "makeDims(dim_" + name + ")";
1850 if (iOutput == fOutputTensorNames.size() - 1) {
1851 fGC += "\n";
1852 } else {
1853 fGC += ",\n";
1854 }
1855 }
1856 fGC += "};\n";
1857 fGC +=
1858 "\nconstexpr bool hasDynamicOutputTensors{" + std::string{hasDynamicOutputTensors ? "true" : "false"} + "};\n\n";
1859}
1860
1862 std::cout << "Model requires following inputs:\n";
1863 for (auto& inputInfo: fInputTensorInfos) {
1864 std::cout << "Parametrised Tensor name: " << inputInfo.first << "\t";
1865 std::cout << "type: " << ConvertTypeToString(inputInfo.second.type) << "\t";
1866 std::cout << "shape: [";
1867 for (size_t i = 0; i < inputInfo.second.shape.size(); i++) {
1868 if (inputInfo.second.shape[i].isParam) {
1869 std::cout << inputInfo.second.shape[i].param;
1870 } else {
1871 std::cout << inputInfo.second.shape[i].dim ;
1872 }
1873 if (i < inputInfo.second.shape.size() - 1) std::cout << ",";
1874 }
1875 std::cout << "]" << std::endl;
1876 }
1877
1878 for (auto& inputInfo: fReadyInputTensorInfos) {
1879 std::cout << "Fully Specified Tensor name: " << inputInfo.first << "\t";
1880 std::cout << "type: " << ConvertTypeToString(inputInfo.second.type) << "\t";
1881 std::cout << "shape: [";
1882 for (size_t i = 0; i < inputInfo.second.shape.size(); i++) {
1883 std::cout << inputInfo.second.shape[i];
1884 if (i < inputInfo.second.shape.size() - 1) std::cout << ",";
1885 }
1886 std::cout << "]" << std::endl;
1887 }
1888 std::cout << "\n";
1889}
1890
1892 std::cout << "Model initialized the following tensors:\n";
1893 for (auto& it: fInitializedTensors) {
1894 std::cout << "Tensor name: \"" << it.first << "\"\t";
1895 std::cout << "type: " << ConvertTypeToString(it.second.type()) << "\t";
1896 std::cout << "shape: [";
1897 for (size_t i = 0; i < it.second.shape().size(); i++) {
1898 std::cout << it.second.shape()[i];
1899 if (i < it.second.shape().size() - 1) std::cout << ",";
1900 }
1901 std::cout << "]";
1902 if (it.second.IsConstantTensor()) std::cout << " (Constant)";
1903 if (it.second.IsNotWritable()) std::cout << " (Not Writable)";
1904 std::cout << std::endl;
1905 }
1906 std::cout << "\n";
1907}
1908
1910 std::cout << "Model specify the following intermediate tensors:\n";
1911 for (auto& it: fIntermediateTensorInfos) {
1912 std::cout << "Tensor name: \"" << it.first << "\"\t";
1913 std::cout << "type: " << ConvertTypeToString(it.second.type) << "\t";
1914 std::cout << "shape: [";
1915 for (size_t i = 0; i < it.second.shape.size(); i++) {
1916 std::cout << it.second.shape[i];
1917 if (i < it.second.shape.size() - 1) std::cout << ",";
1918 }
1919 std::cout << "]" << std::endl;
1920 }
1921 std::cout << "\n";
1922}
1923
1925 std::cout << "Model specify the following dynamic tensors:\n";
1926 for (auto& it: fDynamicTensorInfos) {
1927 std::cout << "Tensor name: \"" << it.first << "\"\t";
1928 std::cout << "type: " << ConvertTypeToString(it.second.type) << "\t";
1929 std::cout << "shape: [";
1930 for (size_t i = 0; i < it.second.shape.size(); i++) {
1931 std::cout << it.second.shape[i].GetVal();
1932 if (i < it.second.shape.size() - 1) std::cout << ",";
1933 }
1934 std::cout << "]" << std::endl;
1935 }
1936 std::cout << "\n";
1937}
1938
1940 std::cout << "Model specify the following output tensors:\n";
1941 for (auto& it: fOutputTensorNames) {
1942 std::cout << "Tensor name: \"" << it << "\"\t";
1943 try {
1944 auto shape = GetDimTensorShape(it);
1945 std::cout << "with shape: " << ConvertDimShapeToString(shape) << std::endl;
1946 } catch (...) {
1947 std::cout << "with shape not yet defined" << std::endl;
1948 }
1949 }
1950 std::cout << "\n";
1951}
1952
1954 auto it = fInitializedTensors.find(name);
1955 if (it == fInitializedTensors.end()) {
1956 std::cout << "Tensor " << name << " not found in model's initialized tensor list" << std::endl;
1957 return;
1958 }
1959
1960 std::cout << "Tensor name: " << it->first << "\t";
1961 std::cout << "type: " << ConvertTypeToString(it->second.type()) << "\t";
1962 int length =1;
1963 std::cout << "shape: [";
1964 for (size_t i = 0; i < it->second.shape().size(); i++) {
1965 std::cout << it->second.shape()[i];
1966 length *= it->second.shape()[i];
1967 if (i < it->second.shape().size() - 1) std::cout << ",";
1968 }
1969 std::cout << "]" << std::endl;
1970 bool ellipsis = true;
1971 if (n_print > length) {
1972 n_print = length;
1973 ellipsis = false;
1974 }
1975
1976 std::cout << "data: [" << std::endl;
1977 if (it->second.type() == ETensorType::FLOAT) {
1978 auto converted_data = it->second.data<float>();
1979 for (int i =0; i < n_print; i++) {
1980 std::cout << converted_data[i];
1981 if (i < n_print - 1) std::cout << " ,";
1982 }
1983 }
1984 if (ellipsis) std::cout << ", ...";
1985 std::cout << "]" << std::endl;
1986
1987}
1988
1989void RModel::OutputGenerated(std::string filename, bool append) {
1990
1992
1993 // write weights in a text file
1994 if (fUseWeightFile) {
1995 if (!filename.empty()) {
1996 size_t pos = filename.find(".hxx");
1998 filename.replace(pos, 4, ".dat");
2000 filename = filename.erase(pos, 4);
2001 filename += ".root";
2002 }
2003 } else {
2004 filename = fName;
2005 filename += fWeightFile == WeightFileType::Text ? ".dat" : ".root";
2006 }
2008 }
2009}
2010
2011} // namespace SOFIE::Experimental::TMVA
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:148
const_iterator begin() const
const_iterator end() const
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:3797
void GenerateHeaderInfo(std::string &hgname)
void AddNeededHelperFunction(std::string name)
void OutputGenerated(std::string filename="", bool append=false)
const std::string & GetName() const
void AddBlasRoutines(std::vector< std::string > routines)
void AddNeededStdLib(std::string libname)
void AddShapeParam(const std::string &name, size_t def_value=0)
Definition RModel.cxx:345
std::vector< size_t > GetTensorShape(const std::string &name) const
Definition RModel.cxx:64
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:100
std::unordered_map< std::string, DynamicTensorInfo > fDynamicTensorInfos
Definition RModel.hxx:31
bool IsDynamicTensor(const std::string &name) const
Definition RModel.cxx:296
void AddAliasTensor(const std::string &tensor_name, const std::string &orig_tensor_name)
Definition RModel.cxx:260
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:311
std::string GenerateInferSignature(bool isdecl=true)
Definition RModel.cxx:1095
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:157
std::vector< std::unique_ptr< ROperator > > fOperators
Definition RModel.hxx:39
void GenerateRequiredInputTensorInfo()
To emit the dimensions of the input tensors as a data member of a session, which is helpful when vali...
Definition RModel.cxx:1782
void OutputGenerated(std::string filename="", bool append=false)
Definition RModel.cxx:1989
std::unordered_map< std::string, std::string > fAliasTensors
Definition RModel.hxx:34
void AddInputTensorInfo(std::string input_name, ETensorType type, std::vector< Dim > shape)
Definition RModel.cxx:168
std::unordered_map< std::string, TensorInfo > fIntermediateTensorInfos
Definition RModel.hxx:30
void AddOutputTensorNameList(std::vector< std::string > output_tensor_names)
Definition RModel.cxx:353
std::unordered_map< std::string, TensorInfo > fReadyInputTensorInfos
Definition RModel.hxx:28
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:242
void AddDynamicTensor(std::string tensor_name, ETensorType type, std::vector< Dim > shape)
Definition RModel.cxx:328
std::vector< std::string > fDimShapeNames
Definition RModel.hxx:35
void AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:222
std::unordered_map< std::string_view, size_t > fIntermediateTensorFrequencyLookup
! lookup table for intermediate tensor frequency (transient)
Definition RModel.hxx:46
void AddInputTensorName(std::string name)
Definition RModel.cxx:187
std::vector< std::string > fOutputTensorNames
Definition RModel.hxx:36
bool IsDimInputTensor(const std::string &name) const
Definition RModel.cxx:301
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
Definition RModel.cxx:270
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:283
bool IsAliasTensor(const std::string &name) const
check if a tensor is a alias tensor
Definition RModel.cxx:274
void CheckAndFlushIntermediateMemory(std::span< const std::string_view > op_output_tensors, const size_t &op_idx)
Definition RModel.cxx:498
void AddOperator(std::unique_ptr< ROperator > op, int order_execution=-1)
Definition RModel.cxx:191
void HeadInitializedTensors(std::string name, int n_print=50)
Definition RModel.cxx:1953
bool IsConstantTensor(const std::string &name) const
Definition RModel.cxx:287
void Initialize(int batchSize=-1, bool verbose=false)
Definition RModel.cxx:577
long WriteInitializedTensorsToFile(std::string filename="")
Definition RModel.cxx:1647
OptimizationLevel fOptimizationLevel
Definition RModel.hxx:25
void Generate(std::underlying_type_t< Options > options, int batchSize=-1, long pos=0, bool verbose=false)
Definition RModel.cxx:1504
std::vector< std::string > CollectTensorMemberNames(const std::string &input)
Collects all identifiers starting with "tensor_" in the input code, provided that the occurrence is n...
Definition RModel.cxx:1037
std::vector< Dim > GetDynamicTensorShape(const std::string &name) const
Definition RModel.cxx:111
std::unordered_map< std::string, InputTensorInfo > fInputTensorInfos
Definition RModel.hxx:27
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:376
MemoryPoolInfo fIntermediateMemoryInfo
! intermediate memory info (transient)
Definition RModel.hxx:45
std::string AllocateIntermediateMemory(std::span< const std::string_view > op_output_tensors)
Definition RModel.cxx:393
std::unordered_map< std::string, std::pair< std::vector< Dim >, bool > > fShapeTensors
Definition RModel.hxx:32
void InitializeSubGraph(std::shared_ptr< RModel > graph)
Definition RModel.cxx:735
std::unordered_map< std::string, std::string > fShapeParams
Definition RModel.hxx:33
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:385
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:125
std::vector< std::string > fInputTensorNames
Definition RModel.hxx:37
std::unordered_map< std::string, InitializedTensor > fInitializedTensors
Definition RModel.hxx:29
void UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:367
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:278
std::vector< std::shared_ptr< RModel > > fSubGraphs
! sub-graph models (transient)
Definition RModel.hxx:41
bool IsReadyInputTensor(const std::string &name) const
Definition RModel.cxx:305
void UpdateOutputTensorList(std::vector< std::string > curr_output_tensor, std::vector< std::string > modify_output_tensor)
Definition RModel.cxx:360
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:252
bool IsInputTensorShapeParam(std::string const &name) const
Check if a given parameter is used for the shape of an input tensor.
Definition RModel.cxx:1019
const Int_t n
Definition legend1.C:16
void ReplaceAll(std::string &str, const std::string &from, const std::string &to, bool recurse=false)
std::string Clean_name(std::string input_tensor_name)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::string ConvertValuesToString(size_t n, const T *data, size_t maxprint=-1)
std::vector< Dim > ConvertShapeToDim(const std::vector< size_t > &shape)
Convert shape from integer format to dynamic one (based on Dim)
constexpr size_t GetTypeSize(ETensorType type)
std::string GenerateConstantTensorCode(const std::pair< std::string, InitializedTensor > &t)
Definition RModel.cxx:771
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertTypeToString(ETensorType type)
std::underlying_type_t< Options > operator|(Options opA, Options opB)
Definition RModel.cxx:56
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
std::string ConvertValToString(T value)
std::map< size_t, TensorMemoryInfo > total_stack
std::map< size_t, size_t > available_stack