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