Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Clip.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_CLIP
2#define TMVA_SOFIE_ROPERATOR_CLIP
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <limits>
9#include <sstream>
10#include <string>
11#include <vector>
12
13namespace TMVA {
14namespace Experimental {
15namespace SOFIE {
16
17// ---------------------------------------------------------------------------
18// ROperator_Clip
19//
20// ONNX spec: Y = max(min_val, min(max_val, X)) element-wise
21//
22// The min and max bounds are optional in the ONNX spec:
23// - if fNMin is empty → no lower clipping (effectively -inf)
24// - if fNMax is empty → no upper clipping (effectively +inf)
25//
26// Bounds can be provided either as:
27// (a) initializer / constant tensors (scalar, shape []),
28// (b) runtime input tensors (resolved at Generate time),
29// (c) compile-time float literals (via the fMin / fMax attributes).
30//
31// The implementation follows the Selu operator style exactly:
32// - static shape stored in fShape
33// - dynamic shape stored in fDimShape
34// - a flat loop over all elements in Generate()
35// ---------------------------------------------------------------------------
36
37template <typename T>
39private:
40
41 // Tensor names
42 std::string fNX; // input data
43 std::string fNY; // output
44 std::string fNMin; // optional: tensor name for min bound
45 std::string fNMax; // optional: tensor name for max bound
46
47
48 // Static shape (non-dynamic path, mirrors Selu)
49 std::vector<size_t> fShape;
50
51 // Dynamic shape (Dim-aware, for dynamic input tensors)
52 std::vector<Dim> fDimShape;
53 bool fIsDynamic = false;
54
55 // Compile-time bound values — used when bounds are constant tensors
56 // Initialised to the ONNX defaults (no clipping)
57 T fMin = std::numeric_limits<T>::lowest(); // -inf equivalent
58 T fMax = std::numeric_limits<T>::max(); // +inf equivalent
59
60 // Flags indicating whether each bound is:
61 // - absent (no input provided)
62 // - a constant resolved at Initialize time
63 // - a runtime tensor that must be read in the generated code
64 bool fHasMin = false;
65 bool fHasMax = false;
66 bool fMinIsConstant = false;
67 bool fMaxIsConstant = false;
68
69public:
70
72
73 // Constructor for the common case where bounds are tensor inputs
74 // (follows ONNX node input order: X, min, max)
75 ROperator_Clip(std::string nameX,
76 std::string nameY,
77 std::string nameMin = "",
78 std::string nameMax = "")
79 : fNX (UTILITY::Clean_name(nameX)),
80 fNY (UTILITY::Clean_name(nameY)),
81 fNMin(nameMin.empty() ? "" : UTILITY::Clean_name(nameMin)),
82 fNMax(nameMax.empty() ? "" : UTILITY::Clean_name(nameMax))
83 {
85 if (!fNMin.empty()) fInputTensorNames.push_back(fNMin);
86 if (!fNMax.empty()) fInputTensorNames.push_back(fNMax);
88 }
89
90 // Convenience constructor when bounds are known scalars at model-build time
91 ROperator_Clip(std::string nameX,
92 std::string nameY,
93 T minVal,
94 T maxVal)
95 : fNX (UTILITY::Clean_name(nameX)),
96 fNY (UTILITY::Clean_name(nameY)),
97 fMin(minVal), fMax(maxVal),
100 {
103 }
104
105
106 // -----------------------------------------------------------------------
107 void Initialize(RModel& model) override
108 {
109 // ---- validate main input ------------------------------------------
110 if (!model.CheckIfTensorAlreadyExist(fNX))
111 throw std::runtime_error(
112 "TMVA SOFIE Clip Op Input Tensor " + fNX + " is not found in model");
113
114 // ---- collect shape (static or dynamic, mirrors BasicBinary) -------
115 if (model.IsDynamicTensor(fNX)) {
116 fIsDynamic = true;
118 } else {
119 fShape = model.GetTensorShape(fNX);
121 }
122
123 // ---- resolve min bound --------------------------------------------
124 if (!fNMin.empty() && model.CheckIfTensorAlreadyExist(fNMin)) {
125 fHasMin = true;
126 if (model.IsInitializedTensor(fNMin)) {
127 // constant scalar tensor — read value now
128 auto data = static_cast<T*>(model.GetInitializedTensorData(fNMin).get());
129 fMin = data[0];
130 fMinIsConstant = true;
132 }
133 // else: runtime input — will be dereferenced in generated code
134 }
135
136 // ---- resolve max bound --------------------------------------------
137 if (!fNMax.empty() && model.CheckIfTensorAlreadyExist(fNMax)) {
138 fHasMax = true;
139 if (model.IsInitializedTensor(fNMax)) {
140 auto data = static_cast<T*>(model.GetInitializedTensorData(fNMax).get());
141 fMax = data[0];
142 fMaxIsConstant = true;
144 }
145 }
146
147 // ---- register output tensor ---------------------------------------
148 if (fIsDynamic)
150 else
152
153 if (model.Verbose()) {
154 std::cout << "Clip : " << fNX << " "
156 if (fHasMin)
157 std::cout << " min=" << (fMinIsConstant
158 ? std::to_string(fMin) : fNMin + "(runtime)");
159 if (fHasMax)
160 std::cout << " max=" << (fMaxIsConstant
161 ? std::to_string(fMax) : fNMax + "(runtime)");
162 std::cout << " --> " << fNY << "\n";
163 }
164
165 // only needs <algorithm> and <limits> — no cmath
166 model.AddNeededStdLib("algorithm");
167 model.AddNeededStdLib("limits");
168 }
169
170
171 // -----------------------------------------------------------------------
172 // Generate
173 // -----------------------------------------------------------------------
174 std::string Generate(std::string OpName) override
175 {
176 OpName = "op_" + OpName;
177
178 if (fShape.empty() && fDimShape.empty())
179 throw std::runtime_error(
180 "TMVA SOFIE Operator Clip called to Generate without being initialized first");
181
182 std::stringstream out;
183 out << SP << "\n//------ CLIP " << OpName << "\n";
184
185 // ---- build the length expression (static or dynamic) -------------
187
188 // ---- build min/max expressions for the generated code ------------
189 //
190 // Priority:
191 // 1. compile-time constant value → emit literal
192 // 2. runtime input tensor → emit tensor_<name>[0] (scalar)
193 // 3. not provided → emit numeric_limits extreme
194 //
195 std::string minExpr, maxExpr;
196
197 if (fMinIsConstant) {
199 } else if (fHasMin) {
200 minExpr = "tensor_" + fNMin + "[0]"; // scalar input tensor
201 } else {
202 // No lower bound — use lowest representable value
203 minExpr = "std::numeric_limits<" + TensorType<T>::Name()
204 + ">::lowest()";
205 }
206
207 if (fMaxIsConstant) {
209 } else if (fHasMax) {
210 maxExpr = "tensor_" + fNMax + "[0]";
211 } else {
212 // No upper bound — use max representable value
213 maxExpr = "std::numeric_limits<" + TensorType<T>::Name()
214 + ">::max()";
215 }
216
217 auto tensorValue = [](const std::string & name, const std::string & index) {
218 std::stringstream s;
219 s << "tensor_" << name << "[" << index << "]";
220 return s.str();
221 };
222
223 // ---- flat element loop (identical structure to Selu) -------------
224 out << SP << "for (int id = 0; id < " << length << " ; id++) {\n";
225 std::string firstExpr = fHasMax ? "std::min(" + maxExpr + ", " + tensorValue(fNX, "id") + ")" : tensorValue(fNX, "id");
226 std::string secondExpr = fHasMin ? "std::max(" + minExpr + ", " + firstExpr + ")" : firstExpr;
227 out << SP << SP << tensorValue(fNY, "id") << " = " << secondExpr << ";\n";
228 out << SP << "}\n";
229
230 return out.str();
231 }
232
233
234private:
235
236 // Helper: convert a T value to string with enough precision
237 std::string ToStringHighPrec(T val) const {
238 std::ostringstream ss;
239 ss << std::setprecision(std::numeric_limits<T>::max_digits10) << val;
240 // add dot if missing
241 if (ss.str().find(".") == std::string::npos) ss << ".";
242 // append 'f' suffix for float literals so generated code compiles
243 // cleanly without implicit double→float conversion warnings
244 if (std::is_same<T, float>::value) ss << "f";
245 return ss.str();
246 }
247};
248
249} // namespace SOFIE
250} // namespace Experimental
251} // namespace TMVA
252
253#endif // TMVA_SOFIE_ROPERATOR_CLIP
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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 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
char name[80]
Definition TGX11.cxx:146
void AddNeededStdLib(std::string libname)
std::vector< size_t > GetTensorShape(const std::string &name) const
Definition RModel.cxx:51
bool IsDynamicTensor(const std::string &name) const
Definition RModel.cxx:269
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:284
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:144
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:256
std::vector< Dim > GetDynamicTensorShape(const std::string &name) const
Definition RModel.cxx:98
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:350
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:359
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:112
ROperator_Clip(std::string nameX, std::string nameY, T minVal, T maxVal)
std::string Generate(std::string OpName) override
void Initialize(RModel &model) override
ROperator_Clip(std::string nameX, std::string nameY, std::string nameMin="", std::string nameMax="")
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:47
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:42
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:48
std::vector< Dim > ConvertShapeToDim(const std::vector< size_t > &shape)
Convert shape from integer format to dynamic one (based on Dim)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
create variable transformations