8#ifdef SOFIE_SUPPORT_ROOT_BINARY
19const std::string
SP =
" ";
21void ReplaceAll(std::string &str,
const std::string &from,
const std::string &to)
24 while ((pos = str.find(from, pos)) != std::string::npos) {
25 str.replace(pos, from.length(), to);
30bool IsIdentifierChar(
char c)
32 return std::isalnum(
static_cast<unsigned char>(
c)) ||
c ==
'_';
38bool IsIdentifier(
const std::string &s)
40 if (s.empty() || std::isdigit(
static_cast<unsigned char>(s[0])))
43 if (!IsIdentifierChar(
c))
49std::string TensorMember(std::string
const &
name)
51 return "tensor_" +
name;
57 return static_cast<std::underlying_type_t<Options>
>(opA) |
static_cast<std::underlying_type_t<Options>
>(opB);
59std::underlying_type_t<Options>
operator|(std::underlying_type_t<Options> opA,
Options opB) {
60 return opA |
static_cast<std::underlying_type_t<Options>
>(opB);
67 return f->second.shape;
71 return f2->second.shape();
75 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] is an input tensor with unspecified dimension parameter");
79 return f4->second.shape;
85 if (f5->second.second)
86 return std::vector<size_t>{};
88 return std::vector<size_t>{f5->second.first.size()};
92 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] is a dynamic tensor. Use GetDynamicTensorShape instead of GetTensorShape");
97 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] for which the shape is requested is not found");
102 return f->second.shape;
105 return f->second.shape;
113 return f->second.shape;
116 return f->second.shape;
120 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] for which the shape is requested is not dynamic");
122 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] for which the shape is requested is not found");
128 return f->second.type;
132 return f2->second.type();
136 return f3->second.type;
140 return f4->second.type;
144 return f5->second.type;
154 throw std::runtime_error(
"TMVA SOFIE tensor [" +
name +
"] for which the type is requested is not found, model name: " +
fName);
171 throw std::runtime_error(
"TMVA-SOFIE: input tensor with name " + input_name +
" already exists \n");
181 throw std::runtime_error(
"TMVA-SOFIE: input tensor with name " + input_name +
" already exists \n");
193 auto libs = op->GetStdLibs();
194 auto op_input_tensors = op->GetOpInputTensors();
195 for (
auto& stdlib : libs) {
198 if (order_execution >= 0) {
208 for(
size_t index = 0; index<op_input_tensors.size() &&
222 throw std::runtime_error(
"TMVA-SOFIE: initialized tensor with name " + tensor_name +
" already exists \n");
232 throw std::runtime_error(
"TMVA-SOFIE: constant tensor with name " + tensor_name +
" already exists \n");
241 throw std::runtime_error(
"TMVA-SOFIE: shape tensor with name " + tensor_name +
" already exists \n");
243 fShapeTensors[tensor_name] = std::make_pair(shape_values, scalar);
251 throw std::runtime_error(
"TMVA-SOFIE: alias tensor with name " + tensor_name +
" already exists \n");
278 return itr->second.IsConstantTensor();
299 if (!int_shape.empty())
308 throw std::runtime_error(
"TMVA-SOFIE: intermediate tensor with name " + tensor_name +
" already exists \n");
317 throw std::runtime_error(
"TMVA-SOFIE: intermediate tensor with name " + tensor_name +
" already exists \n");
322 for (
auto &
d : shape) {
324 if (
d.dim !=
size_t(-1)) {
341 for(
auto& it : outputtensornames) {
347 for(
auto& it:curr_output_tensors) {
356 throw std::runtime_error(
"TMVA-SOFIE: tensor " + tensor_name +
" not found when trying to update it");
365 throw std::runtime_error(
"TMVA-SOFIE: tensor " + tensor_name +
" not found when trying to get its data");
367 return f->second.sharedptr();
374 throw std::runtime_error(
"TMVA-SOFIE: initialized tensor " + tensor_name +
" not found when trying to get its info");
376 t->second.SetNotWritable();
381 std::stringstream code;
384 std::cout <<
"Total chunks allocated\n";
386 std::cout <<
"..... chunk " << chunk->first <<
" size " << chunk->second.tensor_size <<
" " << chunk->second.tensor_name << std::endl;
390 auto declareIntermediateTensor = [
this, &code](std::string
const &
name,
size_t size,
size_t location) {
392 code <<
"\n // Allocating memory for intermediate tensor " <<
name <<
" with size " <<
size <<
" bytes";
394 << typeName <<
"* " << TensorMember(
name) <<
" = reinterpret_cast<" << typeName
395 <<
"*>(fIntermediateMemoryPool.data() + " << location <<
");\n";
398 if (
fVerbose) std::cout <<
"*** AllocateIntermediateMemory: Loop on op output tensors\n";
400 std::vector<TensorMemoryInfo> ordered_output_tensors;
402 for (
auto &it : op_output_tensors) {
403 auto name = std::string(it);
416 ordered_output_tensors.push_back(tmi);
418 std::sort(ordered_output_tensors.begin(), ordered_output_tensors.end(),
421 for (
auto &it : ordered_output_tensors) {
422 bool allocated =
false;
423 std::string
name = std::string{it.tensor_name};
424 size_t tensor_size = it.tensor_size;
426 std::cout <<
"output tensor " <<
name <<
" size " << tensor_size << std::endl;
431 if (
fVerbose) std::cout <<
".. available chunk " << chunk->first <<
" with size = " << chunk->second;
433 if (chunk->second >= tensor_size) {
437 auto new_chunk_location = chunk->first + chunk->second - tensor_size;
440 declareIntermediateTensor(
name, tensor_size, new_chunk_location);
441 chunk->second -= tensor_size;
445 if (
fVerbose) std::cout <<
" is re-used and split in a new of size " << new_chunk.tensor_size <<
" at " << new_chunk_location;
447 if (chunk->second == 0) {
448 if (
fVerbose) std::cout <<
" and deleted since size matches";
451 if (
fVerbose) std::cout << std::endl;
457 declareIntermediateTensor(
name, tensor_size, chunk->first);
460 if (
fVerbose) std::cout <<
" is extended with a bigger one of size " << tensor_size << std::endl;
464 if (
fVerbose) std::cout << std::endl;
475 declareIntermediateTensor(
name, tensor_size, chunk_idx);
477 if (
fVerbose) std::cout <<
"no chunk available - add in total stack a new chunk with size of tensor and idx : " << chunk_idx
485 if (
fVerbose) std::cout <<
"*** CheckAndFlushIntermediateMemory: Loop on input tensors for op " << op_idx <<
"\n";
487 if (
fVerbose) std::cout <<
"available chunks before freeing them : \n";
490 if (
fVerbose) std::cout <<
"-- free chunk " << chunk->first <<
" size = " << chunk->second << std::endl;
492 for (
auto &iv : op_input_tensors) {
494 if (
fVerbose) std::cout <<
".. input tensors : " << iv;
501 if (
fVerbose) std::cout <<
" flash condition is met - looping on chunks to find matching one \n";
504 if (
fVerbose) std::cout <<
"--- chunk " << chunk->first <<
" , " << chunk->second.tensor_name <<
" size " << chunk->second.tensor_size;
505 if (chunk->second.tensor_name == it) {
506 if (
fVerbose) std::cout <<
" -- Found chunk corresponding to input tensor: " << chunk->first;
512 : std::prev(first_greater);
517 last_smaller->first + last_smaller->second == chunk->first) {
519 last_smaller->second += chunk->second.tensor_size;
521 if (
fVerbose) std::cout <<
" is adjacent in memory with previous one - merge ";
523 last_smaller->first + last_smaller->second == first_greater->first) {
525 last_smaller->second += first_greater->second;
531 if (
fVerbose) std::cout <<
" merge also with following that is free ";
534 if (
fVerbose) std::cout << std::endl;
537 chunk->first + chunk->second.tensor_size == first_greater->first) {
539 if (
fVerbose) std::cout <<
" is adjacent in memory with following one - merge \n";
541 size_t new_size = chunk->second.tensor_size + first_greater->second;
542 size_t first_greater_idx = first_greater->first;
551 if (
fVerbose) std::cout <<
" insert in the available stack the chunk with size " << chunk->second.tensor_size << std::endl;
553 chunk->second.tensor_name =
"free";
558 if (
fVerbose) std::cout << std::endl;
564 std::map<std::string, size_t> inputParams;
566 inputParams[
"input_size"] = batchSize;
567 inputParams[
"batch_size"] = batchSize;
568 inputParams[
"bs"] = batchSize;
579 std::cout <<
"Model is already initialized - skip initialization " << std::endl;
590 for (
auto &input : originalInputTensorInfos) {
591 if (verbose) std::cout <<
"looking at the tensor " << input.first << std::endl;
593 if (!inputParams.empty()) {
594 for (
auto &
d : input.second.shape) {
596 std::string pname =
d.param;
597 if (pname == input.first +
"_size") pname =
"input_size";
598 auto itr = inputParams.find(pname);
599 if (itr != inputParams.end() ) {
600 d =
Dim{ itr->second };
602 std::cout <<
"Tensor: " << input.first <<
" - fix parametric shape " << itr->first <<
" to " << itr->second << std::endl;
610 std::cout <<
"converting input shape for " << input.first <<
" " <<
ConvertShapeToString(shape) <<
" from "
612 if (!shape.empty()) {
624 for (
auto &
d : input.second.shape) {
643 std::vector<size_t> temp_available_stack;
646 std::unordered_set<std::string> runtimeInitializedInputs;
647 for(
size_t op_idx = 0; op_idx <
fOperators.size(); ++op_idx){
650 std::cout <<
"Initializing operator " << i <<
" " <<
typeid(
r).
name() << std::endl;
653 for(
auto &it:
fOperators[op_idx]->GetOpOutputTensors()){
654 std::string
name = std::string{it};
664 if (!
fOperators[op_idx]->IsOutputConstant()) {
665 for (
auto &it :
fOperators[op_idx]->GetOpInputTensors()) {
666 std::string
name = std::string{it};
668 runtimeInitializedInputs.insert(
name);
681 if (it.second.IsNotWritable() && runtimeInitializedInputs.find(it.first) != runtimeInitializedInputs.end()) {
682 it.second.SetWritable();
684 std::cout <<
"Initialized tensor " << it.first <<
" is flagged as not writable but is used by non constant operators, set it as writable \n";
690 it.second.SetConstant();
696 bool modelHasWeights =
false;
698 if (it.second.IsWeightTensor()) {
699 modelHasWeights =
true;
703 if (!modelHasWeights)
725 graph->fParentGraph =
this;
726 graph->fIsSubGraph =
true;
734 std::vector<std::string> blasRoutines;
735 for (
auto &
e : graph->fNeededBlasRoutines)
736 blasRoutines.push_back(
e);
738 for (
auto e : graph->fNeededStdLib)
743 graph->fInputTensorNames.emplace_back(
name);
755 std::stringstream strs;
760 bool allocateOnStack = (length > 100 || t.second.IsWeightTensor()) ? false :
true;
762 const T *data = t.second.data<T>();
765 bool sameData =
false;
767 if (!allocateOnStack && length > 1) {
770 sameData = (data[idx] == data[idx - 1]);
772 }
while (sameData && idx < length);
774 if (allocateOnStack) {
775 strs << type <<
" fTensor_" << t.first <<
"[" << length <<
"] = " <<
ConvertValuesToString(length, data) <<
";\n";
776 strs << type <<
" * " << TensorMember(t.first) <<
" = fTensor_" + t.first +
";\n";
778 strs <<
"std::vector<" << type <<
"> fTensor_" << t.first <<
" = ";
780 strs <<
"std::vector<" << type <<
">(" << length <<
", " <<
ConvertValToString(data[0]) <<
");\n";
784 strs << type <<
" * " << TensorMember(t.first) <<
" = fTensor_" + t.first +
".data();\n";
792 fGC +=
"// initialized (weights and constant) tensors\n";
796 if (i.second.IsNotWritable())
continue;
817 fGC +=
"std::vector<float> fTensor_" + i.first +
" = std::vector<float>(" + std::to_string(length) +
");\n";
818 fGC +=
"float * " + TensorMember(i.first) +
" = fTensor_" + i.first +
".data();\n";
827 fGC +=
"\n//--- Allocating session memory pool to be used for allocating intermediate tensors\n";
832 const size_t memPoolSize = totalStack.rbegin()->first + totalStack.rbegin()->second.tensor_size;
833 fGC +=
"std::vector<char> fIntermediateMemoryPool = std::vector<char>(" + std::to_string(memPoolSize) +
");\n\n";
838 std::string tensor_declaration_block =
"";
842 tensor_declaration_block +=
"std::vector<std::uint8_t> fTensor_" + i.first +
" = std::vector<std::uint8_t>(" + std::to_string(
ConvertShapeToLength(i.second.shape)) +
");\n";
843 tensor_declaration_block +=
"std::uint8_t * " + TensorMember(i.first) +
" = fTensor_" + i.first +
".data();\n";
847 bool not_in_freq_map =
849 bool not_in_output_names =
852 if (((not_in_freq_map && not_in_output_names) || (!not_in_freq_map && !is_extended && not_in_output_names) ) && !is_alias) {
856 tensor_declaration_block +=
"std::vector<float> fTensor_" + i.first +
" = std::vector<float>(" + std::to_string(length) +
");\n";
857 tensor_declaration_block +=
"float * " + TensorMember(i.first) +
" = fTensor_" + i.first +
".data();\n";
861 tensor_declaration_block +=
"std::vector<double> fTensor_" + i.first +
" = std::vector<double>(" + std::to_string(length) +
");\n";
862 tensor_declaration_block +=
"double * " + TensorMember(i.first) +
" = fTensor_" + i.first +
".data();\n";
866 tensor_declaration_block +=
"std::vector<int64_t> fTensor_" + i.first +
" = std::vector<int64_t>(" + std::to_string(length) +
");\n";
867 tensor_declaration_block +=
"int64_t * " + TensorMember(i.first) +
" = fTensor_" + i.first +
".data();\n";
872 tensor_declaration_block +=
ConvertTypeToString(i.second.type) +
" * " + TensorMember(i.first) +
" = nullptr;\n";
877 if (tensor_declaration_block.length()) {
878 fGC +=
"\n//--- declare and allocate the intermediate tensors\n" + tensor_declaration_block;
883 fGC +=
"//--- declare the dynamic tensors\n";
887 fGC +=
"//--- dynamic tensors pool\n";
888 fGC +=
"std::vector<char> fDynamicMemoryPool;\n";
896 strcode += op->GenerateDeclCode();
898 if (strcode.empty())
return;
899 fGC +=
"\n//---- operator declarations \n";
911 std::cout <<
"generating code for dynamic tensor management" << std::endl;
915 std::stringstream out;
916 out <<
"// dynamic tensor memory management\n";
917 out <<
SP <<
"std::vector<TMVA::Experimental::SOFIE::TensorLifeInfo> dynamicTensorInfos;\n";
922 std::vector<std::pair<std::string, ETensorType>> tensors;
926 for (
auto &it : op->GetOpOutputTensors()) {
928 auto op_ptr = op.get();
929 std::cout <<
"Looping on operator " << op_index <<
" " <<
typeid(*op_ptr).name() << std::endl;
932 std::string
name = std::string(it);
938 int begin = op_index;
943 end = it_lookup->second + 1;
948 std::cout <<
"op " << op_index <<
"tensor_" <<
name <<
" begin " << begin <<
" " <<
" end " << end << std::endl;
949 throw std::runtime_error(
"TMVA-SOFIE: RModel::GenerateDynamicTensorInfo: tensor_" +
name +
" has end before begin");
953 out <<
SP <<
"dynamicTensorInfos.push_back( {" << begin <<
", " << end <<
", " << type_size <<
"* (" << tensor_size <<
") });"
954 <<
" // tensor_" <<
name << std::endl;
955 tensors.push_back({
name,type});
960 out <<
"\n" <<
SP <<
"auto memory_result = OrganizeMemory(dynamicTensorInfos);\n\n";
961 out <<
"// allocating now the memory\n";
962 out <<
SP <<
"fDynamicMemoryPool = std::vector<char>(memory_result.total_bytes);\n";
963 out <<
SP <<
"int idx = 0;\n";
964 for (
auto & it : tensors) {
965 out <<
SP <<
"tensor_" << it.first <<
" = reinterpret_cast<" <<
ConvertTypeToString(it.second) <<
" *>(fDynamicMemoryPool.data() + memory_result.offsets[idx++]);\n";
968 bool missingTensor =
false;
972 if (std::find(tensors.begin(), tensors.end(), std::pair<std::string,ETensorType>{i.first, i.second.type}) == tensors.end()) {
973 std::cout <<
"Dynamic tensors " << i.first <<
" is not in list of operator input/output " << std::endl;
974 missingTensor =
true;
978 throw std::runtime_error(
"TMVA-SOFIE: RModel::GenerateDynamicTensorInfo - some tensors are not in input/output list");
989 for (
auto &
d : shape) {
990 if (
d.param == paramName)
1004 const std::string target =
"tensor_";
1006 std::vector<std::string> result;
1008 for (
size_t i = 0; i < input.size();) {
1010 bool doCollect =
false;
1012 if (i + target.size() <= input.size() && input.compare(i, target.size(), target) == 0 &&
1013 (i == 0 || !IsIdentifierChar(input[i - 1]))) {
1017 std::size_t j = i + target.size();
1020 while (j < input.size() && IsIdentifierChar(input[j]))
1023 std::string fullName = input.substr(i, j - i);
1027 if (fullName == target +
name) {
1036 if (fullName == target +
name) {
1044 result.push_back(fullName);
1054 std::sort(result.begin(), result.end());
1055 result.erase(std::unique(result.begin(), result.end()), result.end());
1064 std::unordered_map<std::string, int> inputParams;
1070 for (
auto &
d : shape) {
1071 std::string pName =
d.param;
1073 if (
d.isParam && inputParams.count(pName) == 0) {
1074 if (isdecl) rGC +=
"size_t ";
1075 rGC +=
d.param +
",";
1076 inputParams[pName] = i_input;
1082 if (type ==
"other")
1083 throw std::runtime_error(
"TMVA-SOFIE: input tensor " +
name +
1084 " is of a data type which is not yet supported.");
1085 rGC += type +
" const* ";
1087 rGC +=
"tensor_" +
name +
",";
1104std::string memberNameForDimShape(std::string
name)
1106 if (!
name.empty()) {
1107 name[0] = std::toupper(
static_cast<unsigned char>(
name[0]));
1120 bool sameOutputTypes =
true;
1121 std::string inferReturnType;
1124 if (outputSize == 1) {
1125 fGC +=
"std::vector<" + typeForOutput(eFirstOutputType) +
">";
1130 sameOutputTypes =
false;
1132 if (sameOutputTypes)
1133 fGC +=
"std::vector<std::vector<" + typeForOutput(eFirstOutputType) +
">>";
1135 inferReturnType =
"std::tuple<";
1136 for (
size_t i = 0; i < outputSize; i++) {
1138 if (i < outputSize - 1)
1139 inferReturnType +=
",";
1141 inferReturnType +=
">";
1142 fGC += inferReturnType;
1149 if (!doInferArgs.empty())
1161 bool hasRuntimeParam =
false;
1164 hasRuntimeParam =
true;
1166 n = hasRuntimeParam ? memberNameForDimShape(dimLen) : dimLen;
1168 std::string outputName =
"output_tensor_" +
name;
1170 doInferArgs +=
" " + outputName +
".data(),";
1174 fGC +=
SP +
"size_t " + dim.param +
" = 0;\n";
1175 doInferArgs +=
" " + dim.param +
",";
1180 if (!doInferArgs.empty())
1181 doInferArgs.back() =
' ';
1184 std::unordered_set<std::string> input_params_checked;
1185 std::string dynamic_parameters_check =
"";
1189 for (
auto &
d : shape) {
1190 std::string pName =
d.param;
1191 if (
d.isParam && input_params_checked.count(pName) == 0) {
1192 std::string memberName = memberNameForDimShape(
d.param);
1193 dynamic_parameters_check +=
d.param +
" > " + memberName +
" || ";
1194 input_params_checked.insert(pName);
1195 fGC +=
SP +
"if (" +
d.param +
" > " + memberName +
") {\n";
1196 fGC +=
SP +
SP +
"throw std::runtime_error(\"TMVA-SOFIE: dynamic input tensor shape parameter " +
1197 d.param +
" exceeds the initialized maximum allowed shape.\");\n";
1205 fGC +=
SP +
"doInfer(*this, " + doInferArgs +
");\n";
1207 fGC +=
SP +
"doInfer(" + doInferArgs +
");\n";
1214 std::string outputName =
"output_tensor_" +
name;
1216 fGC +=
SP + outputName +
".resize(" + tensor_size +
");\n";
1220 fGC +=
SP +
"return {";
1236 fGC +=
"struct " + sessionName +
";\n";
1241 if (!doInferSignature.empty())
1242 doInferSignature +=
", ";
1249 doInferSignature +=
" size_t &" + dim.param +
"_output,";
1253 doInferSignature.back() =
' ';
1256 doInferSignature = sessionName +
" const &session, " + doInferSignature;
1259 doInferSignature =
"inline void doInfer(" + doInferSignature +
")";
1263 fGC += doInferSignature +
";\n";
1268 fGC +=
"struct " + sessionName +
" {\n";
1276 std::string intermediate_memory_alloc_string =
"";
1277 intermediate_memory_alloc_string +=
"\n// --- Positioning intermediate tensor memory --";
1278 for (
size_t op_idx = 0; op_idx <
fOperators.size(); ++op_idx) {
1281 std::cout <<
"\n******************\n analyzing input/output operator " << op_idx <<
" "
1282 <<
typeid(*op).name() << std::endl;
1297 fGC += intermediate_memory_alloc_string;
1310 fGC +=
"size_t " + memberNameForDimShape(p) +
";\n";
1317 fGC +=
"Session_" + graph->fName +
" fSession_" + graph->fName +
";\n";
1324 for (
size_t id = 0;
id <
fOperators.size();
id++) {
1325 std::string opName = std::to_string(
id);
1331 std::string fileName =
fName;
1336 fileName +=
".root";
1338 fGC += sessionName +
"(std::string filename =\"" + fileName +
"\"";
1342 fGC += sessionName +
"(std::string = \"\"";
1361 fGC +=
" " + memberNameForDimShape(p) +
" = " + p +
";\n";
1366 fGC +=
"\n//--- reading weights from file\n";
1376 for (
size_t id = 0;
id <
fOperators.size();
id++) {
1388 fGC +=
"}; // end of Session\n\n";
1393 fGC += doInferSignature +
" {\n";
1398 std::cout <<
"Generating main inference code for " <<
fName << std::endl;
1401 throw std::runtime_error(
"TMVA-SOFIE: output size=0 are not supported");
1403 std::string allOperatorCode;
1405 for (
size_t op_idx = 0; op_idx <
fOperators.size(); ++op_idx) {
1407 std::cout <<
"Generating code for operator .... " << op_idx << std::endl;
1408 std::string operatorCode =
fOperators[op_idx]->Generate(std::to_string(op_idx));
1409 allOperatorCode += operatorCode;
1414 ReplaceAll(allOperatorCode,
"this->",
"session.");
1419 for (
auto const&
name: tensorMemberNames) {
1420 fGC +=
" auto &" +
name +
" = session." +
name +
";\n";
1425 fGC += allOperatorCode;
1432 fGC +=
" " + dim.param +
"_output = " + dim.param +
";\n";
1436 std::string t =
"session.tensor_" +
name;
1438 fGC +=
" std::copy(" + t +
", " + t +
" + " + std::to_string(length) +
", tensor_" +
name +
");\n";
1446void RModel::Generate(std::underlying_type_t<Options> options,
int batchSize,
long pos,
bool verbose)
1466 throw std::runtime_error(
1467 "TMVA-SOFIE: RModel::Generate: cannot use a separate weight file without generating a Session class");
1470 if (
static_cast<std::underlying_type_t<Options>
>(
Options::kGNN) & options)
1482 std::cout <<
"Warning: Force having a Session since model has dynamic tensors " << std::endl;
1494 std::cout <<
"generate session code for subgraph " << graph->fName << std::endl;
1495 graph->GenerateSessionCode();
1500 std::cout <<
"generate Main session code - model " <<
fName << std::endl;
1506 fGC += (
"} //TMVA_SOFIE_" +
fName +
"\n");
1507 fGC +=
"\n#endif // " + hgname +
"\n";
1518 fGC +=
" std::ifstream f;\n";
1519 fGC +=
" f.open(filename);\n";
1520 fGC +=
" if (!f.is_open()) {\n";
1521 fGC +=
" throw std::runtime_error(\"tmva-sofie failed to open file \" + filename + \" for input weights\");\n";
1525 fGC +=
" f.seekg(" + std::to_string(pos) +
");\n";
1528 fGC +=
" using TMVA::Experimental::SOFIE::ReadTensorFromStream;\n";
1533 if (!i.second.IsWeightTensor())
continue;
1534 std::string tensor_name =
"tensor_" + i.first;
1537 fGC +=
" ReadTensorFromStream(f, " + tensor_name +
", \"" + tensor_name +
"\", " + length +
");\n";
1539 throw std::runtime_error(
"tmva-sofie tensor " + tensor_name +
" with type " +
ConvertTypeToString(i.second.type()) +
" cannot be read from a file");
1542 fGC +=
" f.close();\n";
1547#ifdef SOFIE_SUPPORT_ROOT_BINARY
1549 fGC +=
" std::unique_ptr<TFile> rootFile(TFile::Open(filename.c_str(), \"READ\"));\n";
1550 fGC +=
" if (!rootFile->IsOpen()) {\n";
1551 fGC +=
" throw std::runtime_error(\"tmva-sofie failed to open ROOT file for input weights\");\n";
1554 std::string dirName =
fName +
"_weights";
1555 fGC +=
" if (!rootFile->GetKey(\"" + dirName +
"\")) {\n";
1556 fGC +=
" throw std::runtime_error(\"tmva-sofie failed to open ROOT directory for input weights\");\n";
1561 if (!i.second.IsWeightTensor())
continue;
1563 std::string tensor_name =
"tensor_" + i.first;
1565 fGC +=
" fTensor_" + i.first +
" = *reinterpret_cast<std::vector<float>*>(rootFile->Get(\"";
1566 fGC += dirName +
"/" + tensor_name +
"\"));\n";
1568 fGC +=
" fTensor_" + i.first +
" = *reinterpret_cast<std::vector<double>*>(rootFile->Get(\"";
1569 fGC += dirName + +
"/" + tensor_name +
"\"));\n";
1571 fGC +=
" fTensor_" + i.first +
" = *reinterpret_cast<std::vector<int64_t>*>(rootFile->Get(\"";
1572 fGC += dirName +
"/" + tensor_name +
"\"));\n";
1574 throw std::runtime_error(
"tmva-sofie tensor " + tensor_name +
" with type " +
ConvertTypeToString(i.second.type()) +
" cannot be read from a ROOT file");
1580 throw std::runtime_error(
"SOFIE was not built with ROOT file support.");
1587 std::string fileExtension;
1590 fileExtension =
".dat";
1593 fileExtension =
".root";
1596 fileExtension =
".dat";
1601 if (filename.empty()) {
1607#ifdef SOFIE_SUPPORT_ROOT_BINARY
1609 throw std::runtime_error(
"SOFIE-GNN yet not supports writing to a ROOT file.");
1611 std::unique_ptr<TFile> outputFile(
TFile::Open(filename.c_str(),
"UPDATE"));
1613 std::string dirName =
fName +
"_weights";
1615 if (outputFile->GetKey(dirName.c_str()))
1616 outputFile->rmdir(dirName.c_str());
1618 auto outputDir = outputFile->mkdir(dirName.c_str());
1622 if (!item.second.IsWeightTensor())
continue;
1623 std::string tensorName =
"tensor_" + item.first;
1627 const float* data = item.second.data<
float>();
1628 std::vector<float> tensorDataVector(data, data + length);
1629 outputDir->WriteObjectAny(&tensorDataVector,
"std::vector<float>", tensorName.c_str());
1632 const double* data = item.second.data<
double>();
1633 std::vector<double> tensorDataVector(data, data + length);
1634 outputDir->WriteObjectAny(&tensorDataVector,
"std::vector<double>", tensorName.c_str());
1637 const int64_t* data = item.second.data<int64_t>();
1638 std::vector<int64_t> tensorDataVector(data, data + length);
1639 outputDir->WriteObjectAny(&tensorDataVector,
"std::vector<int64_t>", tensorName.c_str());
1642 throw std::runtime_error(
"tmva-sofie tensor " + tensorName +
" with type " +
ConvertTypeToString(item.second.type()) +
1643 " cannot be written to a ROOT file");
1646 outputFile->Write(filename.c_str());
1652 throw std::runtime_error(
"SOFIE was not built with ROOT file support.");
1658 f.open(filename, std::ios::app);
1664 std::runtime_error(
"tmva-sofie failed to open file " + filename +
" for tensor weight data");
1667 if (!i.second.IsWeightTensor()) {
1671 std::string tensor_name =
"tensor_" + i.first;
1672 f << tensor_name <<
" " << length <<
"\n";
1674 const float * data = i.second.data<
float>();
1675 for (
size_t idx = 0; idx < length; idx++) {
1677 float value = data[idx];
1678 if (value != 0. && std::abs(value) < std::numeric_limits<float>::min() ) value = 0;
1680 if (std::isinf(value))
1681 f << (value > 0 ?
"inf" :
"-inf");
1682 else if (std::isnan(value))
1685 f << std::setprecision(std::numeric_limits<float>::max_digits10) << value;
1686 f << ( (idx < length-1) ?
" " :
"\n" );
1690 throw std::runtime_error(
"tmva-sofie tensor " + tensor_name +
" with type " +
ConvertTypeToString(i.second.type()) +
" cannot be written to a file");
1693 throw std::runtime_error(
"tmva-sofie failed to write tensor data to file for " + tensor_name);
1695 long curr_pos =
f.tellp();
1704 std::cout <<
"Summary of model " <<
GetName() << std::endl;
1705 for(
size_t op_idx = 0; op_idx <
fOperators.size(); ++op_idx){
1707 std::string raw_name =
typeid(
r).
name();
1709 std::string
name = raw_name.substr(raw_name.find(
"ROperator_")+10, raw_name.size());
1710 std::cout << op_idx <<
" " <<
name <<
" : ";
1711 for (
auto & t_in :
r.GetOpInputTensors()) std::cout << t_in <<
" ";
1712 std::cout <<
" ----> ";
1713 for (
auto & t_out :
r.GetOpOutputTensors()) std::cout << t_out <<
" ";
1714 std::cout << std::endl;
1722 fGC +=
"\n// Input tensor dimensions\n";
1723 fGC +=
"using TMVA::Experimental::SOFIE::SingleDim;\n";
1724 fGC +=
"using TMVA::Experimental::SOFIE::TensorDims;\n";
1725 fGC +=
"using TMVA::Experimental::SOFIE::makeDims;\n\n";
1726 bool hasDynamicInputTensors =
false;
1731 hasDynamicInputTensors =
true;
1734 fGC +=
"constexpr std::array<SingleDim, " + std::to_string(shape.size()) +
"> dim_" +
name +
"{";
1735 for (std::size_t iDim = 0; iDim < shape.size(); ++iDim) {
1736 auto const &dim = shape[iDim];
1738 fGC +=
"SingleDim{\"" + dim.GetVal() +
"\"}";
1740 fGC +=
"SingleDim{" + dim.GetVal() +
"}";
1742 if (iDim != shape.size() - 1) {
1748 fGC +=
"\nconstexpr std::array<TensorDims, " + std::to_string(
fInputTensorNames.size()) +
"> inputTensorDims{\n";
1751 fGC +=
SP +
"makeDims(dim_" +
name +
")";
1761 "\nconstexpr bool hasDynamicInputTensors{" + std::string{hasDynamicInputTensors ?
"true" :
"false"} +
"};\n\n";
1765 std::cout <<
"Model requires following inputs:\n";
1767 std::cout <<
"Parametrised Tensor name: " << inputInfo.first <<
"\t";
1769 std::cout <<
"shape: [";
1770 for (
size_t i = 0; i < inputInfo.second.shape.size(); i++) {
1771 if (inputInfo.second.shape[i].isParam) {
1772 std::cout << inputInfo.second.shape[i].param;
1774 std::cout << inputInfo.second.shape[i].dim ;
1776 if (i < inputInfo.second.shape.size() - 1) std::cout <<
",";
1778 std::cout <<
"]" << std::endl;
1782 std::cout <<
"Fully Specified Tensor name: " << inputInfo.first <<
"\t";
1784 std::cout <<
"shape: [";
1785 for (
size_t i = 0; i < inputInfo.second.shape.size(); i++) {
1786 std::cout << inputInfo.second.shape[i];
1787 if (i < inputInfo.second.shape.size() - 1) std::cout <<
",";
1789 std::cout <<
"]" << std::endl;
1795 std::cout <<
"Model initialized the following tensors:\n";
1797 std::cout <<
"Tensor name: \"" << it.first <<
"\"\t";
1799 std::cout <<
"shape: [";
1800 for (
size_t i = 0; i < it.second.shape().
size(); i++) {
1801 std::cout << it.second.shape()[i];
1802 if (i < it.second.shape().size() - 1) std::cout <<
",";
1805 if (it.second.IsConstantTensor()) std::cout <<
" (Constant)";
1806 if (it.second.IsNotWritable()) std::cout <<
" (Not Writable)";
1807 std::cout << std::endl;
1813 std::cout <<
"Model specify the following intermediate tensors:\n";
1815 std::cout <<
"Tensor name: \"" << it.first <<
"\"\t";
1817 std::cout <<
"shape: [";
1818 for (
size_t i = 0; i < it.second.shape.size(); i++) {
1819 std::cout << it.second.shape[i];
1820 if (i < it.second.shape.size() - 1) std::cout <<
",";
1822 std::cout <<
"]" << std::endl;
1828 std::cout <<
"Model specify the following dynamic tensors:\n";
1830 std::cout <<
"Tensor name: \"" << it.first <<
"\"\t";
1832 std::cout <<
"shape: [";
1833 for (
size_t i = 0; i < it.second.shape.size(); i++) {
1834 std::cout << it.second.shape[i].GetVal();
1835 if (i < it.second.shape.size() - 1) std::cout <<
",";
1837 std::cout <<
"]" << std::endl;
1843 std::cout <<
"Model specify the following output tensors:\n";
1845 std::cout <<
"Tensor name: \"" << it <<
"\"\t";
1850 std::cout <<
"with shape not yet defined" << std::endl;
1859 std::cout <<
"Tensor " <<
name <<
" not found in model's initialized tensor list" << std::endl;
1863 std::cout <<
"Tensor name: " << it->first <<
"\t";
1866 std::cout <<
"shape: [";
1867 for (
size_t i = 0; i < it->second.shape().
size(); i++) {
1868 std::cout << it->second.shape()[i];
1869 length *= it->second.shape()[i];
1870 if (i < it->second.shape().size() - 1) std::cout <<
",";
1872 std::cout <<
"]" << std::endl;
1873 bool ellipsis =
true;
1874 if (n_print > length) {
1879 std::cout <<
"data: [" << std::endl;
1881 auto converted_data = it->second.data<
float>();
1882 for (
int i =0; i < n_print; i++) {
1883 std::cout << converted_data[i];
1884 if (i < n_print - 1) std::cout <<
" ,";
1887 if (ellipsis) std::cout <<
", ...";
1888 std::cout <<
"]" << std::endl;
1898 if (!filename.empty()) {
1899 size_t pos = filename.find(
".hxx");
1901 filename.replace(pos, 4,
".dat");
1903 filename = filename.erase(pos, 4);
1904 filename +=
".root";
1918 i.second.CastPersistentToShared();
1923 i.second.CastSharedToPersistent();
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Buffer base class used for serializing objects.
Int_t ReadBuffer(TBuffer &b, void *pointer, Int_t version, UInt_t start, UInt_t count)
Function called by the Streamer functions to deserialize information from buffer b into object at p.
Int_t WriteBuffer(TBuffer &b, void *pointer, const char *info="")
Function called by the Streamer functions to serialize object at p to buffer b.
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.
void GenerateHeaderInfo(std::string &hgname)
void OutputGenerated(std::string filename="", bool append=false)
const std::string & GetName() const
WeightFileType fWeightFile
void AddBlasRoutines(std::vector< std::string > routines)
void AddNeededStdLib(std::string libname)
void AddShapeParam(const std::string &name, size_t def_value=0)
std::vector< size_t > GetTensorShape(const std::string &name) const
void PrintIntermediateTensors() const
std::vector< Dim > GetDimTensorShape(const std::string &name) const
std::unordered_map< std::string, DynamicTensorInfo > fDynamicTensorInfos
bool IsDynamicTensor(const std::string &name) const
void AddAliasTensor(const std::string &tensor_name, const std::string &orig_tensor_name)
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
void GenerateIntermediateTensorInfo()
std::string GenerateInferSignature(bool isdecl=true)
void GenerateOperatorDeclarations()
size_t fWeightsTensorSize
bool CheckIfTensorAlreadyExist(std::string tensor_name)
std::vector< std::unique_ptr< ROperator > > fOperators
void GenerateRequiredInputTensorInfo()
To emit the dimensions of the input tensors as a data member of a session, which is helpful when vali...
void OutputGenerated(std::string filename="", bool append=false)
std::unordered_map< std::string, std::string > fAliasTensors
void AddInputTensorInfo(std::string input_name, ETensorType type, std::vector< Dim > shape)
std::unordered_map< std::string, TensorInfo > fIntermediateTensorInfos
void AddOutputTensorNameList(std::vector< std::string > output_tensor_names)
std::unordered_map< std::string, TensorInfo > fReadyInputTensorInfos
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
void AddDynamicTensor(std::string tensor_name, ETensorType type, std::vector< Dim > shape)
std::vector< std::string > fDimShapeNames
void AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
std::unordered_map< std::string_view, size_t > fIntermediateTensorFrequencyLookup
! lookup table for intermediate tensor frequency (transient)
void AddInputTensorName(std::string name)
std::vector< std::string > fOutputTensorNames
void PrintRequiredInputTensors() const
void GenerateSessionCode()
bool IsDimInputTensor(const std::string &name) const
void GenerateDynamicTensorInfo()
void PrintDynamicTensors() const
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
bool IsInitializedTensor(const std::string &name) const
bool IsAliasTensor(const std::string &name) const
check if a tensor is a alias tensor
size_t fConstantTensorSize
void CheckAndFlushIntermediateMemory(std::span< const std::string_view > op_output_tensors, const size_t &op_idx)
void AddOperator(std::unique_ptr< ROperator > op, int order_execution=-1)
void PrintOutputTensors() const
void HeadInitializedTensors(std::string name, int n_print=50)
bool IsConstantTensor(const std::string &name) const
void Initialize(int batchSize=-1, bool verbose=false)
long WriteInitializedTensorsToFile(std::string filename="")
OptimizationLevel fOptimizationLevel
void Generate(std::underlying_type_t< Options > options, int batchSize=-1, long pos=0, bool verbose=false)
void PrintSummary() const
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...
std::vector< Dim > GetDynamicTensorShape(const std::string &name) const
void PrintInitializedTensors() const
std::unordered_map< std::string, InputTensorInfo > fInputTensorInfos
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
MemoryPoolInfo fIntermediateMemoryInfo
! intermediate memory info (transient)
void GenerateIntermediateMemoryPool()
void ReadInitializedTensorsFromFile(long)
std::string AllocateIntermediateMemory(std::span< const std::string_view > op_output_tensors)
std::unordered_map< std::string, std::pair< std::vector< Dim >, bool > > fShapeTensors
void InitializeSubGraph(std::shared_ptr< RModel > graph)
std::unordered_map< std::string, std::string > fShapeParams
void SetNotWritableInitializedTensor(const std::string &tensor_name)
ETensorType GetTensorType(std::string name) const
void GenerateInitializedTensorInfo()
std::vector< std::string > fInputTensorNames
std::unordered_map< std::string, InitializedTensor > fInitializedTensors
void UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
std::vector< std::shared_ptr< RModel > > fSubGraphs
! sub-graph models (transient)
bool IsReadyInputTensor(const std::string &name) const
void UpdateOutputTensorList(std::vector< std::string > curr_output_tensor, std::vector< std::string > modify_output_tensor)
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
bool IsInputTensorShapeParam(std::string const &name) const
Check if a given parameter is used for the shape of an input tensor.
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)
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)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
std::string ConvertValToString(T value)