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
7#include "TFile.h"
8
9#include "TMVA/RModel.hxx"
10#include "TMVA/SOFIE_common.hxx"
11
12namespace TMVA {
13namespace Experimental {
14namespace SOFIE {
15
16std::underlying_type_t<Options> operator|(Options opA, Options opB) {
17 return static_cast<std::underlying_type_t<Options>>(opA) | static_cast<std::underlying_type_t<Options>>(opB);
18}
19std::underlying_type_t<Options> operator|(std::underlying_type_t<Options> opA, Options opB) {
20 return opA | static_cast<std::underlying_type_t<Options>>(opB);
21}
22
24 fInputTensorInfos = std::move(other.fInputTensorInfos);
25 fReadyInputTensorInfos = std::move(other.fReadyInputTensorInfos);
26 fOutputTensorNames = other.fOutputTensorNames;
27 fInputTensorNames = other.fInputTensorNames;
28 fOperators = std::move(other.fOperators);
29 fInitializedTensors = std::move(other.fInitializedTensors);
30 fIntermediateTensorInfos = std::move(other.fIntermediateTensorInfos);
31 fName = other.fName;
32 fFileName = other.fFileName;
33 fParseTime = other.fParseTime;
34 fGC = other.fGC;
35 fNeededBlasRoutines = other.fNeededBlasRoutines;
36 fNeededStdLib = other.fNeededStdLib;
37}
38
40 fInputTensorInfos = std::move(other.fInputTensorInfos);
41 fReadyInputTensorInfos = std::move(other.fReadyInputTensorInfos);
42 fOutputTensorNames = other.fOutputTensorNames;
43 fInputTensorNames = other.fInputTensorNames;
44 fOperators = std::move(other.fOperators);
45 fInitializedTensors = std::move(other.fInitializedTensors);
46 fIntermediateTensorInfos = std::move(other.fIntermediateTensorInfos);
47 fName = other.fName;
48 fFileName = other.fFileName;
49 fParseTime = other.fParseTime;
50 fGC = other.fGC;
51 fNeededBlasRoutines = other.fNeededBlasRoutines;
52 fNeededStdLib = other.fNeededStdLib;
53 return *this;
54}
55
56const std::vector<size_t>& RModel::GetTensorShape(std::string name) {
57 auto f = fReadyInputTensorInfos.find(name);
58 if (f != fReadyInputTensorInfos.end()) {
59 return f->second.shape;
60 }
61 auto f2 = fInitializedTensors.find(name);
62 if (f2 != fInitializedTensors.end()) {
63 return f2->second.shape();
64 }
65 auto f3 = fInputTensorInfos.find(name);
66 if (f3 != fInputTensorInfos.end()) {
67 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] is an input tensor with unspecified dimension parameter");
68 }
69 auto f4 = fIntermediateTensorInfos.find(name);
70 if (f4 != fIntermediateTensorInfos.end()) {
71 return f4->second.shape;
72 }
74 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] is a dynamic tensor. Use GetDynamicTensorShape instead of GetTensorShape");
75
76 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the shape is requested is not found");
77}
78
79std::vector<Dim> RModel::GetDynamicTensorShape(std::string name) {
80 if (auto f = fDynamicTensorInfos.find(name); f != fDynamicTensorInfos.end()) {
81 return f->second.shape;
82 }
83 if (auto f = fInputTensorInfos.find(name); f != fInputTensorInfos.end()) {
84 return f->second.shape;
85 }
86 // in case is not a dynamic tensor convert normal shape to Dim one
87 // for this we need to return the vector by value
89}
90
92 auto f = fReadyInputTensorInfos.find(name);
93 if (f != fReadyInputTensorInfos.end()) {
94 return f->second.type;
95 }
96 auto f2 = fInitializedTensors.find(name);
97 if (f2 != fInitializedTensors.end()) {
98 return f2->second.type();
99 }
100 auto f3 = fInputTensorInfos.find(name);
101 if (f3 != fInputTensorInfos.end()) {
102 return f3->second.type;
103 }
104 auto f4 = fIntermediateTensorInfos.find(name);
105 if (f4 != fIntermediateTensorInfos.end()) {
106 return f4->second.type;
107 }
108 auto f5 = fDynamicTensorInfos.find(name);
109 if (f5 != fDynamicTensorInfos.end()){
110 return f5->second.type;
111 }
112
113 throw std::runtime_error("TMVA SOFIE tensor [" + name + "] for which the type is requested is not found");
114}
115
116bool RModel::CheckIfTensorAlreadyExist(std::string tensor_name) {
117 if (fReadyInputTensorInfos.find(tensor_name) != fReadyInputTensorInfos.end()) return true;
118 if (fInputTensorInfos.find(tensor_name) != fInputTensorInfos.end()) return true;
119 if (fInitializedTensors.find(tensor_name) != fInitializedTensors.end()) return true;
120 if (fIntermediateTensorInfos.find(tensor_name) != fIntermediateTensorInfos.end()) return true;
121 if (fDynamicTensorInfos.find(tensor_name) != fDynamicTensorInfos.end()) return true;
122 return false;
123}
124
125void RModel::AddInputTensorInfo(std::string input_name, ETensorType type, std::vector<Dim> shape) {
126 input_name = UTILITY::Clean_name(input_name);
127 if (CheckIfTensorAlreadyExist(input_name)) {
128 throw std::runtime_error("TMVA-SOFIE: input tensor with name " + input_name + " already exists \n");
129 }
130
131 InputTensorInfo inputInfo { type, shape };
132 fInputTensorInfos[input_name] = inputInfo;
133}
134
135void RModel::AddInputTensorInfo(std::string input_name, ETensorType type, std::vector<size_t> shape) {
136 input_name = UTILITY::Clean_name(input_name);
137 if (CheckIfTensorAlreadyExist(input_name)) {
138 throw std::runtime_error("TMVA-SOFIE: input tensor with name " + input_name + " already exists \n");
139 }
140 TensorInfo inputInfo { type, shape };
141 fReadyInputTensorInfos[input_name] = inputInfo;
142}
143
144void RModel::AddInputTensorName(std::string input_name) {
145 fInputTensorNames.push_back(UTILITY::Clean_name(input_name));
146}
147
148void RModel::AddOperator(std::unique_ptr<ROperator> op, int order_execution) {
149 AddBlasRoutines(op->GetBlasRoutines());
150 auto libs = op->GetStdLibs();
151 for (auto& stdlib : libs) {
152 AddNeededStdLib(stdlib);
153 }
154 if (order_execution >= 0) {
155 fOperators.insert(fOperators.begin() + order_execution, std::move(op));
156 } else {
157 fOperators.push_back(std::move(op));
158 }
159}
160
161void RModel::AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape, std::shared_ptr<void> data) {
162 tensor_name = UTILITY::Clean_name(tensor_name);
163 //NB: own data
164 if (CheckIfTensorAlreadyExist(tensor_name)) {
165 throw std::runtime_error("TMVA-SOFIE: initialized tensor with name " + tensor_name + " already exists \n");
166 }
167 InitializedTensor new_tensor {type, shape, data};
168 fInitializedTensors[tensor_name] = new_tensor;
169
170}
171
172bool RModel::IsInitializedTensor(const std::string& tensorName) const {
173 std::string name = UTILITY::Clean_name(tensorName);
174 return fInitializedTensors.find(name) != fInitializedTensors.end();
175}
176
177bool RModel::IsDynamicTensor(const std::string& tensorName) const {
178 std::string name = UTILITY::Clean_name(tensorName);
179 return fDynamicTensorInfos.find(name) != fDynamicTensorInfos.end();
180}
181bool RModel::IsInputTensor(const std::string& tensorName) const {
182 std::string name = UTILITY::Clean_name(tensorName);
183 return fInputTensorInfos.find(name) != fInputTensorInfos.end();
184}
185
186// generic addition of a tensor
187void RModel::AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<Dim> dim_shape) {
188 auto int_shape = ConvertShapeToInt(dim_shape);
189 if (!int_shape.empty())
190 AddIntermediateTensor(tensor_name, type, int_shape);
191 else
192 AddDynamicTensor(tensor_name, type, dim_shape);
193}
194
195void RModel::AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape) {
196 tensor_name = UTILITY::Clean_name(tensor_name);
197 if (CheckIfTensorAlreadyExist(tensor_name)) {
198 throw std::runtime_error("TMVA-SOFIE: intermediate tensor with name " + tensor_name + " already exists \n");
199 }
200 TensorInfo new_tensor {type, shape};
201 fIntermediateTensorInfos[tensor_name] = new_tensor;
202}
203
204void RModel::AddDynamicTensor(std::string tensor_name, ETensorType type, std::vector<Dim> shape){
205 tensor_name = UTILITY::Clean_name(tensor_name);
206 if (CheckIfTensorAlreadyExist(tensor_name)){
207 throw std::runtime_error("TMVA-SOFIE: intermediate tensor with name " + tensor_name + " already exists \n");
208 }
209 DynamicTensorInfo new_tensor {type, shape};
210 fDynamicTensorInfos[tensor_name] = new_tensor;
211 // store shape parameter if not existing
212 for (auto &d : shape) {
213 if (d.isParam) {
214 if (fShapeParams.count(d.param) == 0) {
215 // case parameter is an expression of some other existing parameter, no need to
216 // register it
217 if (d.dim != size_t(-1)) {
218 fShapeParams[d.param] = std::to_string(d.dim);
219 }
220 }
221 }
222 }
223}
224
225void RModel::AddOutputTensorNameList(std::vector<std::string> outputtensornames) {
226 fOutputTensorNames.clear();
227 for(auto& it : outputtensornames) {
229 }
230}
231
232void RModel::UpdateOutputTensorList(std::vector<std::string> curr_output_tensors, std::vector<std::string> new_output_tensors) {
233 for(auto& it:curr_output_tensors) {
234 fOutputTensorNames.erase(std::remove(fOutputTensorNames.begin(), fOutputTensorNames.end(), it), fOutputTensorNames.end());
235 }
236 fOutputTensorNames.insert(fOutputTensorNames.end(), new_output_tensors.begin(), new_output_tensors.end());
237}
238
239void RModel::UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape, std::shared_ptr<void> data) {
240 tensor_name = UTILITY::Clean_name(tensor_name);
241 if (!CheckIfTensorAlreadyExist(tensor_name)) {
242 throw std::runtime_error("TMVA-SOFIE: tensor " + tensor_name + " not found when trying to update it");
243 }
244 InitializedTensor new_tensor {type, shape, data};
245 fInitializedTensors[tensor_name] = new_tensor;
246}
247
248std::shared_ptr<void> RModel::GetInitializedTensorData(std::string tensor_name) {
249 auto f = fInitializedTensors.find(tensor_name);
250 if (f == fInitializedTensors.end()) {
251 throw std::runtime_error("TMVA-SOFIE: tensor " + tensor_name + " not found when trying to get its data");
252 } else {
253 return f->second.sharedptr();
254 }
255}
256
257void RModel::Initialize(int batchSize, bool verbose) {
258
260 fDynamicTensorInfos.clear();
261
262 // loop on inputs and see if shape can be full specified
263 // if the batch size is provided it can be used to specify the full shape
264 // Add the full specified tensors in fReadyInputTensors collection
265 auto originalInputTensorInfos = fInputTensorInfos; // need to copy because we may delete elements
266 for (auto &input : originalInputTensorInfos) {
267 if (verbose) std::cout << "looking at the tensor " << input.first << std::endl;
268 // if a batch size is provided convert batch size
269 // assume is parametrized as "bs" or "batch_size"
270 if (batchSize > 0) {
271 // std::vector<Dim> shape;
272 // shape.reserve(input.second.shape.size());
273 // assume first parameter is teh batch size
274 if (!input.second.shape.empty()) {
275 auto & d0 = input.second.shape[0];
276 if (d0.isParam) {
277 if (verbose) std::cout << "Fix the batch size to " << batchSize << std::endl;
278 d0 = Dim{static_cast<size_t>(batchSize)};
279 }
280 else { // look for cases that a bs or bath_size is specified in tensor shape
281 for (auto &d : input.second.shape) {
282 if (d.isParam && (d.param == "bs" || d.param == "batch_size")) {
283 d = Dim{static_cast<size_t>(batchSize)};
284 if (verbose) std::cout << "Input shape has bs or batch_size as names. Fix the batch size to " << batchSize << std::endl;
285 }
286 }
287 }
288 }
289 }
290 auto shape = ConvertShapeToInt(input.second.shape);
291 if (!shape.empty()) {
292 // remove from the tensor info old dynamic shape
293 fInputTensorInfos.erase(input.first);
294 // add to the ready input tensor information the new fixed shape
295 AddInputTensorInfo(input.first, input.second.type, shape);
296 }
297 // store the parameters of the input tensors
298 else {
299 // store the found parametric shape parameters
300 for (auto &d : input.second.shape) {
301 if (d.isParam)
302 fShapeParams[d.param] = std::to_string(d.dim);
303 }
304 }
305 }
306
307 if (verbose) {
310 }
311
312 // check if there are initialized tensors to write in a weight file
313 // support for the time being only weight of FLOAT type
314 if (fUseWeightFile) {
315 bool modelHasWeights = false;
316 for (auto &i : fInitializedTensors) {
317 if (i.second.type() == ETensorType::FLOAT) {
318 modelHasWeights = true;
319 break;
320 }
321 }
322 if (!modelHasWeights)
323 fUseWeightFile = false;
324 }
325 // Go through model and initialize each operator
326 int i = 0;
327 for (auto &op : fOperators) {
328 if (verbose) {
329 auto& r = *op.get();
330 std::cout << "Initializing operator " << i << " " << typeid(r).name() << std::endl;
331 }
332 op->Initialize(*this);
333 i++;
334 }
335}
336
338 for (auto& i: fInitializedTensors) {
339 if (i.second.type() == ETensorType::FLOAT) {
340 size_t length = 1;
341 for (auto & dim: i.second.shape()) {
342 length *= dim;
343 }
344 if (!fUseWeightFile) {
345 fGC += "float tensor_" + i.first + "[" + std::to_string(length) + "] = {";
346 float const *data = i.second.data<float>();
347 std::stringstream floats;
348 for (size_t idx = 0; idx < length-1; idx++) {
349 floats << std::setprecision(std::numeric_limits<float>::max_digits10) << data[idx] << ", ";
350 }
351 floats << std::setprecision(std::numeric_limits<float>::max_digits10) << data[length-1];
352 fGC += floats.str();
353 fGC += "};\n";
354 }
355 else {
356 fGC += "std::vector<float> fTensor_" + i.first + " = std::vector<float>(" + std::to_string(length) + ");\n";
357 fGC += "float * tensor_" + i.first + " = fTensor_" + i.first + ".data();\n";
358 }
359
360 }
361 }
362}
363
365 if (!fIntermediateTensorInfos.empty()) {
366 fGC += "\n//--- declare and allocate the intermediate tensors\n";
367 for (auto &i : fIntermediateTensorInfos) {
368 size_t length = ConvertShapeToLength(i.second.shape);
369 if (i.second.type == ETensorType::FLOAT) {
370 fGC += "std::vector<float> fTensor_" + i.first + " = std::vector<float>(" + std::to_string(length) + ");\n";
371 fGC += "float * tensor_" + i.first + " = fTensor_" + i.first + ".data();\n";
372 }
373 if (i.second.type == ETensorType::DOUBLE) {
374 fGC += "std::vector<double> fTensor_" + i.first + " = std::vector<double>(" + std::to_string(length) + ");\n";
375 fGC += "double * tensor_" + i.first + " = fTensor_" + i.first + ".data();\n";
376 }
377 if (i.second.type == ETensorType::INT64) {
378 fGC += "std::vector<int64_t> fTensor_" + i.first + " = std::vector<int64_t>(" + std::to_string(length) + ");\n";
379 fGC += "int64_t * tensor_" + i.first + " = fTensor_" + i.first + ".data();\n";
380 }
381 if (i.second.type == ETensorType::BOOL) {
382 fGC += "std::vector<bool> fTensor_" + i.first + " = std::vector<bool>(" + std::to_string(length) + ");\n";
383 // don't allocate pointer since boolean vector don't have the .data() member
384 }
385 }
386 }
387 // add also the dynamic tensors (only declarations, allocation will be done later)
388 if (!fDynamicTensorInfos.empty()) {
389 fGC += "//--- declare the dynamic tensors\n";
390 for (auto &i : fDynamicTensorInfos) {
391 if (i.second.type == ETensorType::FLOAT) {
392 fGC += "std::vector<float> fTensor_" + i.first + ";\n";
393 fGC += "float * tensor_" + i.first + " = nullptr;\n";
394 } else if (i.second.type == ETensorType::DOUBLE) {
395 fGC += "std::vector<double> fTensor_" + i.first + ";\n";
396 fGC += "double * tensor_" + i.first + " = nullptr;\n";
397 } else if (i.second.type == ETensorType::INT64) {
398 fGC += "std::vector<int64_t> fTensor_" + i.first + ";\n";
399 fGC += "int64_t * tensor_" + i.first + " = nullptr;\n";
400 }
401 }
402 }
403}
404
406 fGC += "//---- allocate the intermediate dynamic tensors\n";
407 std::stringstream out;
408 for (auto & i: fDynamicTensorInfos) {
409 auto length = ConvertDynamicShapeToLength(i.second.shape);
410 out << SP << "if (" << length << " > 0) {\n";
411 out << SP << SP << "fTensor_" << i.first << ".resize(" << length << ");\n";
412 out << SP << SP << "tensor_" << i.first << " = fTensor_" << i.first << ".data();\n";
413 out << SP << "}\n";
414 }
415 fGC += out.str();
416}
417
419
420 size_t outputSize = fOutputTensorNames.size();
421 // assume output types are all the same
422 if (outputSize == 0)
423 throw std::runtime_error("TMVA-SOFIE: output size=0 are not supported");
424
425 std::string outputType;
426 ETensorType eOutputType;
427 eOutputType = GetTensorType(fOutputTensorNames[0]);
428 outputType = ConvertTypeToString(eOutputType);
429 if (outputSize == 1) {
430 fGC += "std::vector<" + outputType + "> ";
431 } else {
432 // we assume all output types are the same
433 for (size_t i = 1; i < outputSize; i++) {
434 if (GetTensorType(fOutputTensorNames[i]) != eOutputType)
435 throw std::runtime_error("TMVA-SOFIE: different output tensor types are not supported");
436 }
437 fGC += "std::vector<std::vector<" + outputType + ">> ";
438 }
439
440 fGC += "infer(";
441
442 std::unordered_map<std::string, int> inputParams;
443 int i_input = 0;
444 for (auto &name : fInputTensorNames) {
445 // if is a dynamic tensor pass initial parameters
446 if (IsInputTensor(name)) {
447 auto shape = GetDynamicTensorShape(name);
448 for (auto &d : shape) {
449 std::string pName = d.param;
450 // need to check if the input parameters is already existing in another input tensor
451 if (d.isParam && inputParams.count(pName) == 0) {
452 fGC += "size_t " + d.param + ",";
453 inputParams[pName] = i_input;
454 }
455 }
456 }
457 switch (GetTensorType(name)) {
458 case ETensorType::FLOAT: {
459 fGC += "float* tensor_" + name + ",";
460 break;
461 }
462 case ETensorType::INT32: {
463 fGC += "int32_t* tensor_" + name + ",";
464 break;
465 }
466 case ETensorType::INT64: {
467 fGC += "int64_t* tensor_" + name + ",";
468 break;
469 }
470 case ETensorType::DOUBLE: {
471 fGC += "double* tensor_" + name + ",";
472 break;
473 }
474 case ETensorType::BOOL: {
475 fGC += "bool* tensor_" + name + ",";
476 break;
477 }
478 default: {
479 throw std::runtime_error("TMVA-SOFIE: input tensor " + name +
480 " is of a data type which is not yet supported.");
481 }
482 }
483 i_input++;
484 }
485
486 fGC.pop_back(); // remove last ","
487 fGC += "){\n";
488
489 for (size_t id = 0; id < fOperators.size(); id++) {
490 fGC += (fOperators[id]->Generate(std::to_string(id)));
491 }
492
493 if (outputSize == 1) {
494 std::string tensorName = fOutputTensorNames[0];
495 if (fIntermediateTensorInfos.count(tensorName) > 0) {
496 // need to check is size is the same(don't want to return a vector with larger size)
497 // in that case better to copy
498 fGC += SP + "return fTensor_" + tensorName + ";\n";
499 } else {
500 // include also dynamic tensors since the vectors can be allocated with a size larger than their output
501 // we need a special handling for bool type allocated as vector<bool>
502 auto outputLength = ConvertDynamicShapeToLength(GetDynamicTensorShape(tensorName));
503 if (IsDynamicTensor(tensorName) && eOutputType == ETensorType::BOOL) {
504 fGC += SP + "std::vector<bool> ret (fTensor_" + tensorName + ".begin(), fTensor_" + tensorName +
505 ".begin() + " + outputLength + ");\n";
506 } else {
507 fGC += SP + "std::vector<" + outputType + "> ret (tensor_" + tensorName + ", tensor_" + tensorName + " + " +
508 outputLength + ");\n";
509 }
510 fGC += SP + "return ret;\n";
511 }
512 } else {
513 // here we assume all outputs have same type
514 fGC += SP + "std::vector<std::vector<" + outputType + ">> ret({";
515 for (size_t i = 0; i < outputSize; i++) {
516 std::string tensorName = fOutputTensorNames[i];
517 if (!tensorName.empty()) {
518 if (fIntermediateTensorInfos.count(tensorName) > 0) {
519 fGC += "fTensor_" + tensorName;
520 } else {
521 auto outputLength = ConvertDynamicShapeToLength(GetDynamicTensorShape(tensorName));
522 if (IsDynamicTensor(tensorName) && eOutputType == ETensorType::BOOL) {
523 fGC += "std::vector<bool>(fTensor_" + tensorName + ".begin(), fTensor_" + tensorName + ".begin() + " +
524 outputLength + ");\n";
525 } else {
526 fGC += "std::vector<" + outputType + ">(tensor_" + tensorName + ", tensor_" + tensorName + " + " +
527 outputLength + ")";
528 }
529 }
530 if (i < outputSize - 1)
531 fGC += ",";
532 } else {
533 fGC += "{}";
534 }
535 }
536 fGC += "});\n";
537 fGC += SP + "return ret;\n";
538 }
539 fGC += "}\n";
540}
541
542void RModel::Generate(std::underlying_type_t<Options> options, int batchSize, long pos) {
543 // session flag is used in operator initialize
544 if (static_cast<std::underlying_type_t<Options>>(Options::kNoSession) & options) {
545 fUseSession = false;
547 }
548 if (static_cast<std::underlying_type_t<Options>>(Options::kNoWeightFile) & options) {
549 fUseWeightFile = false;
551 }
552 if (static_cast<std::underlying_type_t<Options>>(Options::kRootBinaryWeightFile) & options) {
553 fUseWeightFile = true;
555 }
556 if (fUseWeightFile && !fUseSession) {
557 throw
558 std::runtime_error("TMVA-SOFIE: RModel::Generate: cannot use a separate weight file without generating a Session class");
559 }
560
561 if (static_cast<std::underlying_type_t<Options>>(Options::kGNN) & options)
562 fIsGNN = true;
563 if (static_cast<std::underlying_type_t<Options>>(Options::kGNNComponent) & options)
564 fIsGNNComponent = true;
565
566 Initialize(batchSize);
567 std::string hgname;
568 if(!fIsGNNComponent) {
569 fGC.clear();
570 GenerateHeaderInfo(hgname);
571 if (fUseSession) {
572 fGC += "struct Session {\n";
573 }
574 }
575
578
579 if (fUseSession) {
580 // add here specific operator code that needs to define session data members
581 fGC += "\n";
582 for (size_t id = 0; id < fOperators.size(); id++) {
583 std::string opName = std::to_string(id);
584 fGC += fOperators[id]->GenerateSessionMembersCode(opName);
585 }
586 fGC += "\n";
587 // here add initialization and reading of weight tensors
588 if (fUseWeightFile) {
589 std::string fileName = fName;
591 fileName += ".dat";
592 }
594 fileName += ".root";
595 }
596 fGC += "Session(std::string filename =\"" + fileName + "\"";
597 } else {
598 // no need to pass weight file since it is not used
599 // keep passing a string for compatibility
600 fGC += "Session(std::string = \"\"";
601 }
602 // add initialization of shape parameters
603 // assume all parameters are of type size_t
604 if (!fShapeParams.empty()) {
605 for (auto & p : fShapeParams) {
606 fGC += ",\n";
607 fGC += " size_t " + p.first + " = " + p.second;
608 }
609 }
610 fGC += ") {\n";
611
612 if (fUseWeightFile) {
613 fGC += "\n//--- reading weights from file\n";
615 fGC += "\n";
616 //fUseWeightFile = fUseWeightFile;
617 }
618
619 // now we have passed the parameters we can allocate the dynamic tensors
621
622 // add here initialization code for operator
623 for (size_t id = 0; id < fOperators.size() ; id++) {
624 fGC += fOperators[id]->GenerateInitCode();
625 }
626
627 fGC += "}\n\n";
628 }
629
631
632 if(!fIsGNNComponent) {
633 if (fUseSession) {
634 fGC += "};\n";
635 }
636 fGC += ("} //TMVA_SOFIE_" + fName + "\n");
637 fGC += "\n#endif // " + hgname + "\n";
638 }
639}
640
642 // generate the code to read initialized tensors from a text data file
644 if (fInitializedTensors.empty()) return;
645
646 fGC += " std::ifstream f;\n";
647 fGC += " f.open(filename);\n";
648 fGC += " if (!f.is_open()) {\n";
649 fGC += " throw std::runtime_error(\"tmva-sofie failed to open file \" + filename + \" for input weights\");\n";
650 fGC += " }\n";
651
652 if(fIsGNNComponent) {
653 fGC += " f.seekg(" + std::to_string(pos) + ");\n";
654 }
655
656 fGC += " std::string tensor_name;\n";
657 fGC += " size_t length;\n";
658
659 // loop on tensors and parse the file
660 for (auto& i: fInitializedTensors) {
661 if (i.second.type() == ETensorType::FLOAT) {
662 size_t length = 1;
663 length = ConvertShapeToLength(i.second.shape());
664 std::string tensor_name = "tensor_" + i.first;
665 std::string slength = std::to_string(length);
666 fGC += " f >> tensor_name >> length;\n";
667 fGC += " if (tensor_name != \"" + tensor_name + "\" ) {\n";
668 fGC += " std::string err_msg = \"TMVA-SOFIE failed to read the correct tensor name; expected name is " +
669 tensor_name + " , read \" + tensor_name;\n";
670 fGC += " throw std::runtime_error(err_msg);\n";
671 fGC += " }\n";
672 fGC += " if (length != " + slength + ") {\n";
673 fGC += " std::string err_msg = \"TMVA-SOFIE failed to read the correct tensor size; expected size is " +
674 slength + " , read \" + std::to_string(length) ;\n";
675 fGC += " throw std::runtime_error(err_msg);\n";
676 fGC += " }\n";
677 fGC += " for (size_t i = 0; i < length; ++i)\n";
678 fGC += " f >> " + tensor_name + "[i];\n";
679 }
680 }
681 fGC += " f.close();\n";
682 }
683
684 // generate the code to read initialized tensors from a ROOT data file
686 fGC += " {\n";
687 fGC += " std::unique_ptr<TFile> rootFile(TFile::Open(filename.c_str(), \"READ\"));\n";
688 fGC += " if (!rootFile->IsOpen()) {\n";
689 fGC += " throw std::runtime_error(\"tmva-sofie failed to open ROOT file for input weights\");\n";
690 fGC += " }\n";
691
692 std::string dirName = fName + "_weights";
693 fGC += " if (!rootFile->GetKey(\"" + dirName + "\")) {\n";
694 fGC += " throw std::runtime_error(\"tmva-sofie failed to open ROOT directory for input weights\");\n";
695 fGC += " }\n";
696
697 for (auto &i : fInitializedTensors) {
698 fGC += " {\n";
699 std::string tensor_name = "tensor_" + i.first;
700 if (i.second.type() == ETensorType::FLOAT) {
701 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<float>*>(rootFile->Get(\"";
702 fGC += dirName + "/" + tensor_name + "\"));\n";
703 } else if (i.second.type() == ETensorType::DOUBLE) {
704 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<double>*>(rootFile->Get(\"";
705 fGC += dirName + + "/" + tensor_name + "\"));\n";
706 } else if (i.second.type() == ETensorType::INT64) {
707 fGC += " fTensor_" + i.first + " = *reinterpret_cast<std::vector<int64_t>*>(rootFile->Get(\"";
708 fGC += dirName + "/" + tensor_name + "\"));\n";
709 }
710 fGC += " }\n";
711 }
712 fGC += " }\n";
713 }
714}
715
717 // Determine the file extension based on the weight file type
718 std::string fileExtension;
719 switch (fWeightFile) {
721 fileExtension = ".dat";
722 break;
724 fileExtension = ".root";
725 break;
727 fileExtension = ".dat";
728 break;
729 }
730
731 // If filename is empty, use the model name as the base filename
732 if (filename.empty()) {
733 filename = fFileName + fileExtension;
734 }
735
736 // Write the initialized tensors to the file
738 if(fIsGNNComponent || fIsGNN) {
739 throw std::runtime_error("SOFIE-GNN yet not supports writing to a ROOT file.");
740 }
741 std::unique_ptr<TFile> outputFile(TFile::Open(filename.c_str(), "UPDATE"));
742
743 std::string dirName = fName + "_weights";
744 // check if directory exists, in case delete to replace with new one
745 if (outputFile->GetKey(dirName.c_str()))
746 outputFile->rmdir(dirName.c_str());
747
748 auto outputDir = outputFile->mkdir(dirName.c_str());
749
750 for (const auto& item : fInitializedTensors) {
751 std::string tensorName = "tensor_" + item.first;
752 size_t length = 1;
753 length = ConvertShapeToLength(item.second.shape());
754 if(item.second.type() == ETensorType::FLOAT) {
755 const float* data = item.second.data<float>();
756 std::vector<float> tensorDataVector(data, data + length);
757 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<float>", tensorName.c_str());
758 }
759 else if(item.second.type() == ETensorType::DOUBLE) {
760 const double* data = item.second.data<double>();
761 std::vector<double> tensorDataVector(data, data + length);
762 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<double>", tensorName.c_str());
763 }
764 else if(item.second.type() == ETensorType::INT64) {
765 const int64_t* data = item.second.data<int64_t>();
766 std::vector<int64_t> tensorDataVector(data, data + length);
767 outputDir->WriteObjectAny(&tensorDataVector, "std::vector<int64_t>", tensorName.c_str());
768 }
769 }
770 outputFile->Write(filename.c_str());
771
772 // this needs to be changed, similar to the text file
773 return -1;
774
775 } else if (fWeightFile == WeightFileType::Text) {
776 std::ofstream f;
777 if(fIsGNNComponent) {
778 // appending all GNN components into the same file
779 f.open(filename, std::ios::app);
780 } else {
781 f.open(filename);
782 }
783 if (!f.is_open())
784 throw
785 std::runtime_error("tmva-sofie failed to open file " + filename + " for tensor weight data");
786 for (auto& i: fInitializedTensors) {
787 if (i.second.type() == ETensorType::FLOAT) {
788 size_t length = 1;
789 for (auto &dim : i.second.shape()) {
790 length *= dim;
791 }
792 std::string tensor_name = "tensor_" + i.first;
793 f << tensor_name << " " << length << "\n";
794 const float * data = i.second.data<float>();
795 for (size_t idx = 0; idx < length - 1; idx++) {
796 f << std::setprecision(std::numeric_limits<float>::max_digits10) << data[idx] << " ";
797 }
798 f << std::setprecision(std::numeric_limits<float>::max_digits10) << data[length - 1];
799 f << "\n";
800 }
801 }
802 long curr_pos = f.tellp();
803 f.close();
804 return curr_pos;
805 } else {
806 return -1;
807 }
808}
809
811 std::cout << "Model requires following inputs:\n";
812 for (auto& inputInfo: fInputTensorInfos) {
813 std::cout << "Parameterised Tensor name: " << inputInfo.first << "\t";
814 std::cout << "type: " << ConvertTypeToString(inputInfo.second.type) << "\t";
815 std::cout << "shape: [";
816 for (size_t i = 0; i < inputInfo.second.shape.size(); i++) {
817 if (inputInfo.second.shape[i].isParam) {
818 std::cout << inputInfo.second.shape[i].param;
819 } else {
820 std::cout << inputInfo.second.shape[i].dim ;
821 }
822 if (i < inputInfo.second.shape.size() - 1) std::cout << ",";
823 }
824 std::cout << "]" << std::endl;
825 }
826
827 for (auto& inputInfo: fReadyInputTensorInfos) {
828 std::cout << "Fully Specified Tensor name: " << inputInfo.first << "\t";
829 std::cout << "type: " << ConvertTypeToString(inputInfo.second.type) << "\t";
830 std::cout << "shape: [";
831 for (size_t i = 0; i < inputInfo.second.shape.size(); i++) {
832 std::cout << inputInfo.second.shape[i];
833 if (i < inputInfo.second.shape.size() - 1) std::cout << ",";
834 }
835 std::cout << "]" << std::endl;
836 }
837 std::cout << "\n";
838}
839
841 std::cout << "Model initialized the following tensors:\n";
842 for (auto& it: fInitializedTensors) {
843 std::cout << "Tensor name: \"" << it.first << "\"\t";
844 std::cout << "type: " << ConvertTypeToString(it.second.type()) << "\t";
845 std::cout << "shape: [";
846 for (size_t i = 0; i < it.second.shape().size(); i++) {
847 std::cout << it.second.shape()[i];
848 if (i < it.second.shape().size() - 1) std::cout << ",";
849 }
850 std::cout << "]" << std::endl;
851 }
852 std::cout << "\n";
853}
854
856 std::cout << "Model specify the following intermediate tensors:\n";
857 for (auto& it: fIntermediateTensorInfos) {
858 std::cout << "Tensor name: \"" << it.first << "\"\t";
859 std::cout << "type: " << ConvertTypeToString(it.second.type) << "\t";
860 std::cout << "shape: [";
861 for (size_t i = 0; i < it.second.shape.size(); i++) {
862 std::cout << it.second.shape[i];
863 if (i < it.second.shape.size() - 1) std::cout << ",";
864 }
865 std::cout << "]" << std::endl;
866 }
867 std::cout << "\n";
868}
869
871 std::cout << "Model specify the following dynamic tensors:\n";
872 for (auto& it: fDynamicTensorInfos) {
873 std::cout << "Tensor name: \"" << it.first << "\"\t";
874 std::cout << "type: " << ConvertTypeToString(it.second.type) << "\t";
875 std::cout << "shape: [";
876 for (size_t i = 0; i < it.second.shape.size(); i++) {
877 std::cout << it.second.shape[i].GetVal();
878 if (i < it.second.shape.size() - 1) std::cout << ",";
879 }
880 std::cout << "]" << std::endl;
881 }
882 std::cout << "\n";
883}
884
886 std::cout << "Model specify the following output tensors:\n";
887 for (auto& it: fOutputTensorNames) {
888 std::cout << "Tensor name: \"" << it << "\"\t";
889 if (!IsDynamicTensor(it))
890 std::cout << "shape: " << ConvertShapeToString(GetTensorShape(it)) << std::endl;
891 else
892 std::cout << "shape: " << ConvertDynamicShapeToString(GetDynamicTensorShape(it)) << std::endl;
893 }
894 std::cout << "\n";
895}
896
897void RModel::HeadInitializedTensors(std::string name, int n_print) {
898 auto it = fInitializedTensors.find(name);
899 if (it == fInitializedTensors.end()) {
900 std::cout << "Tensor " << name << " not found in model's initialized tensor list" << std::endl;
901 return;
902 }
903
904 std::cout << "Tensor name: " << it->first << "\t";
905 std::cout << "type: " << ConvertTypeToString(it->second.type()) << "\t";
906 int length =1;
907 std::cout << "shape: [";
908 for (size_t i = 0; i < it->second.shape().size(); i++) {
909 std::cout << it->second.shape()[i];
910 length *= it->second.shape()[i];
911 if (i < it->second.shape().size() - 1) std::cout << ",";
912 }
913 std::cout << "]" << std::endl;
914 bool ellipsis = true;
915 if (n_print > length) {
916 n_print = length;
917 ellipsis = false;
918 }
919
920 std::cout << "data: [" << std::endl;
921 if (it->second.type() == ETensorType::FLOAT) {
922 auto converted_data = it->second.data<float>();
923 for (int i =0; i < n_print; i++) {
924 std::cout << converted_data[i];
925 if (i < n_print - 1) std::cout << " ,";
926 }
927 }
928 if (ellipsis) std::cout << ", ...";
929 std::cout << "]" << std::endl;
930
931}
932
933void RModel::OutputGenerated(std::string filename, bool append) {
934
936
937 // write weights in a text file
938 if (fUseWeightFile) {
939 if (!filename.empty()) {
940 size_t pos = filename.find(".hxx");
942 filename.replace(pos, 4, ".dat");
944 filename = filename.erase(pos, 4);
945 filename += ".root";
946 }
947 } else {
948 filename = fName;
949 filename += fWeightFile == WeightFileType::Text ? ".dat" : ".root";
950 }
952 }
953}
954
955void RModel::Streamer(TBuffer &R__b) {
956 if (R__b.IsReading()) {
957 RModel::Class()->ReadBuffer(R__b, this);
958 for(auto i=RModel::fInitializedTensors.begin(); i!=RModel::fInitializedTensors.end(); ++i) {
959 i->second.CastPersistentToShared();
960 }
961 }
962 else {
963 for(auto i=RModel::fInitializedTensors.begin(); i!=RModel::fInitializedTensors.end(); ++i) {
964 i->second.CastSharedToPersistent();
965 }
966 RModel::Class()->WriteBuffer(R__b, this);
967 }
968}
969
970}//SOFIE
971}//Experimental
972}//TMVA
#define d(i)
Definition RSha256.hxx:102
#define f(i)
Definition RSha256.hxx:104
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
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 data
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 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 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:110
Buffer base class used for serializing objects.
Definition TBuffer.h:43
Bool_t IsReading() const
Definition TBuffer.h:86
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:4089
void GenerateHeaderInfo(std::string &hgname)
std::unordered_set< std::string > fNeededBlasRoutines
void OutputGenerated(std::string filename="", bool append=false)
std::unordered_set< std::string > fNeededStdLib
void AddBlasRoutines(std::vector< std::string > routines)
void AddNeededStdLib(std::string libname)
const ETensorType & GetTensorType(std::string name)
Definition RModel.cxx:91
std::unordered_map< std::string, DynamicTensorInfo > fDynamicTensorInfos
Definition RModel.hxx:20
bool IsDynamicTensor(const std::string &name) const
Definition RModel.cxx:177
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:187
std::vector< Dim > GetDynamicTensorShape(std::string name)
Definition RModel.cxx:79
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:116
std::vector< std::unique_ptr< ROperator > > fOperators
Definition RModel.hxx:26
void OutputGenerated(std::string filename="", bool append=false)
Definition RModel.cxx:933
void AddInputTensorInfo(std::string input_name, ETensorType type, std::vector< Dim > shape)
Definition RModel.cxx:125
std::unordered_map< std::string, TensorInfo > fIntermediateTensorInfos
Definition RModel.hxx:19
void AddOutputTensorNameList(std::vector< std::string > output_tensor_names)
Definition RModel.cxx:225
std::unordered_map< std::string, TensorInfo > fReadyInputTensorInfos
Definition RModel.hxx:17
void AddDynamicTensor(std::string tensor_name, ETensorType type, std::vector< Dim > shape)
Definition RModel.cxx:204
void AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:161
RModel & operator=(RModel &&other)
Definition RModel.cxx:39
void AddInputTensorName(std::string name)
Definition RModel.cxx:144
std::vector< std::string > fOutputTensorNames
Definition RModel.hxx:23
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:172
void AddOperator(std::unique_ptr< ROperator > op, int order_execution=-1)
Definition RModel.cxx:148
RModel()=default
Default constructor.
void HeadInitializedTensors(std::string name, int n_print=50)
Definition RModel.cxx:897
void Initialize(int batchSize=-1, bool verbose=false)
Definition RModel.cxx:257
const std::vector< size_t > & GetTensorShape(std::string name)
Definition RModel.cxx:56
bool IsInputTensor(const std::string &name) const
Definition RModel.cxx:181
long WriteInitializedTensorsToFile(std::string filename="")
Definition RModel.cxx:716
void Generate(std::underlying_type_t< Options > options, int batchSize=-1, long pos=0)
Definition RModel.cxx:542
std::unordered_map< std::string, InputTensorInfo > fInputTensorInfos
Definition RModel.hxx:16
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:248
std::unordered_map< std::string, std::string > fShapeParams
Definition RModel.hxx:22
std::vector< std::string > fInputTensorNames
Definition RModel.hxx:24
std::unordered_map< std::string, InitializedTensor > fInitializedTensors
Definition RModel.hxx:18
void UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:239
void UpdateOutputTensorList(std::vector< std::string > curr_output_tensor, std::vector< std::string > modify_output_tensor)
Definition RModel.cxx:232
std::string Clean_name(std::string input_tensor_name)
std::vector< Dim > ConvertShapeToDim(std::vector< size_t > shape)
Convert shape from integer format to dynamic one (based on Dim)
std::string ConvertDynamicShapeToLength(std::vector< Dim > shape)
std::string ConvertShapeToString(std::vector< size_t > shape)
std::string ConvertTypeToString(ETensorType type)
std::string ConvertDynamicShapeToString(std::vector< Dim > shape)
std::underlying_type_t< Options > operator|(Options opA, Options opB)
Definition RModel.cxx:16
std::vector< size_t > ConvertShapeToInt(std::vector< Dim > shape)
Convert shape based on Dim to integer format.
std::size_t ConvertShapeToLength(std::vector< size_t > shape)
create variable transformations