File indexing completed on 2026-08-16 09:21:39
0001 #ifndef TMVA_SOFIE_ROPERATOR_CLIP
0002 #define TMVA_SOFIE_ROPERATOR_CLIP
0003
0004 #include "TMVA/SOFIE_common.hxx"
0005 #include "TMVA/ROperator.hxx"
0006 #include "TMVA/RModel.hxx"
0007
0008 #include <limits>
0009 #include <sstream>
0010 #include <string>
0011 #include <vector>
0012
0013 namespace TMVA {
0014 namespace Experimental {
0015 namespace SOFIE {
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 template <typename T>
0038 class ROperator_Clip final : public ROperator {
0039 private:
0040
0041
0042 std::string fNX;
0043 std::string fNY;
0044 std::string fNMin;
0045 std::string fNMax;
0046
0047
0048
0049 std::vector<size_t> fShape;
0050
0051
0052 std::vector<Dim> fDimShape;
0053 bool fIsDynamic = false;
0054
0055
0056
0057 T fMin = std::numeric_limits<T>::lowest();
0058 T fMax = std::numeric_limits<T>::max();
0059
0060
0061
0062
0063
0064 bool fHasMin = false;
0065 bool fHasMax = false;
0066 bool fMinIsConstant = false;
0067 bool fMaxIsConstant = false;
0068
0069 public:
0070
0071 ROperator_Clip() {}
0072
0073
0074
0075 ROperator_Clip(std::string nameX,
0076 std::string nameY,
0077 std::string nameMin = "",
0078 std::string nameMax = "")
0079 : fNX (UTILITY::Clean_name(nameX)),
0080 fNY (UTILITY::Clean_name(nameY)),
0081 fNMin(nameMin.empty() ? "" : UTILITY::Clean_name(nameMin)),
0082 fNMax(nameMax.empty() ? "" : UTILITY::Clean_name(nameMax))
0083 {
0084 fInputTensorNames = { fNX };
0085 if (!fNMin.empty()) fInputTensorNames.push_back(fNMin);
0086 if (!fNMax.empty()) fInputTensorNames.push_back(fNMax);
0087 fOutputTensorNames = { fNY };
0088 }
0089
0090
0091 ROperator_Clip(std::string nameX,
0092 std::string nameY,
0093 T minVal,
0094 T maxVal)
0095 : fNX (UTILITY::Clean_name(nameX)),
0096 fNY (UTILITY::Clean_name(nameY)),
0097 fMin(minVal), fMax(maxVal),
0098 fHasMin(true), fHasMax(true),
0099 fMinIsConstant(true), fMaxIsConstant(true)
0100 {
0101 fInputTensorNames = { fNX };
0102 fOutputTensorNames = { fNY };
0103 }
0104
0105
0106
0107 void Initialize(RModel& model) override
0108 {
0109
0110 if (!model.CheckIfTensorAlreadyExist(fNX))
0111 throw std::runtime_error(
0112 "TMVA SOFIE Clip Op Input Tensor " + fNX + " is not found in model");
0113
0114
0115 if (model.IsDynamicTensor(fNX)) {
0116 fIsDynamic = true;
0117 fDimShape = model.GetDynamicTensorShape(fNX);
0118 } else {
0119 fShape = model.GetTensorShape(fNX);
0120 fDimShape = ConvertShapeToDim(fShape);
0121 }
0122
0123
0124 if (!fNMin.empty() && model.CheckIfTensorAlreadyExist(fNMin)) {
0125 fHasMin = true;
0126 if (model.IsInitializedTensor(fNMin)) {
0127
0128 auto data = static_cast<T*>(model.GetInitializedTensorData(fNMin).get());
0129 fMin = data[0];
0130 fMinIsConstant = true;
0131 model.SetNotWritableInitializedTensor(fNMin);
0132 }
0133
0134 }
0135
0136
0137 if (!fNMax.empty() && model.CheckIfTensorAlreadyExist(fNMax)) {
0138 fHasMax = true;
0139 if (model.IsInitializedTensor(fNMax)) {
0140 auto data = static_cast<T*>(model.GetInitializedTensorData(fNMax).get());
0141 fMax = data[0];
0142 fMaxIsConstant = true;
0143 model.SetNotWritableInitializedTensor(fNMax);
0144 }
0145 }
0146
0147
0148 if (fIsDynamic)
0149 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fDimShape);
0150 else
0151 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShape);
0152
0153 if (model.Verbose()) {
0154 std::cout << "Clip : " << fNX << " "
0155 << ConvertShapeToString(fShape);
0156 if (fHasMin)
0157 std::cout << " min=" << (fMinIsConstant
0158 ? std::to_string(fMin) : fNMin + "(runtime)");
0159 if (fHasMax)
0160 std::cout << " max=" << (fMaxIsConstant
0161 ? std::to_string(fMax) : fNMax + "(runtime)");
0162 std::cout << " --> " << fNY << "\n";
0163 }
0164
0165
0166 model.AddNeededStdLib("algorithm");
0167 model.AddNeededStdLib("limits");
0168 }
0169
0170
0171
0172
0173
0174 std::string Generate(std::string OpName) override
0175 {
0176 OpName = "op_" + OpName;
0177
0178 if (fShape.empty() && fDimShape.empty())
0179 throw std::runtime_error(
0180 "TMVA SOFIE Operator Clip called to Generate without being initialized first");
0181
0182 std::stringstream out;
0183 out << SP << "\n//------ CLIP " << OpName << "\n";
0184
0185
0186 std::string length = ConvertDimShapeToLength(fDimShape);
0187
0188
0189
0190
0191
0192
0193
0194
0195 std::string minExpr, maxExpr;
0196
0197 if (fMinIsConstant) {
0198 minExpr = ToStringHighPrec(fMin);
0199 } else if (fHasMin) {
0200 minExpr = "tensor_" + fNMin + "[0]";
0201 } else {
0202
0203 minExpr = "std::numeric_limits<" + TensorType<T>::Name()
0204 + ">::lowest()";
0205 }
0206
0207 if (fMaxIsConstant) {
0208 maxExpr = ToStringHighPrec(fMax);
0209 } else if (fHasMax) {
0210 maxExpr = "tensor_" + fNMax + "[0]";
0211 } else {
0212
0213 maxExpr = "std::numeric_limits<" + TensorType<T>::Name()
0214 + ">::max()";
0215 }
0216
0217 auto tensorValue = [](const std::string & name, const std::string & index) {
0218 std::stringstream s;
0219 s << "tensor_" << name << "[" << index << "]";
0220 return s.str();
0221 };
0222
0223
0224 out << SP << "for (int id = 0; id < " << length << " ; id++) {\n";
0225 std::string firstExpr = fHasMax ? "std::min(" + maxExpr + ", " + tensorValue(fNX, "id") + ")" : tensorValue(fNX, "id");
0226 std::string secondExpr = fHasMin ? "std::max(" + minExpr + ", " + firstExpr + ")" : firstExpr;
0227 out << SP << SP << tensorValue(fNY, "id") << " = " << secondExpr << ";\n";
0228 out << SP << "}\n";
0229
0230 return out.str();
0231 }
0232
0233
0234 private:
0235
0236
0237 std::string ToStringHighPrec(T val) const {
0238 std::ostringstream ss;
0239 ss << std::setprecision(std::numeric_limits<T>::max_digits10) << val;
0240
0241 if (ss.str().find(".") == std::string::npos) ss << ".";
0242
0243
0244 if (std::is_same<T, float>::value) ss << "f";
0245 return ss.str();
0246 }
0247 };
0248
0249 }
0250 }
0251 }
0252
0253 #endif