Skip to content

Commit 979189c

Browse files
committed
[tmva][sofie] Add in RModel managing of Shape tensors types
Include in RMOdel a new type of tensor, shape tensors , where their values (a shape) is stored in RModel as vector of Dims (so shape can be parametrised) and a shape of rank 1 or 0 ( in case of scalars). Shapes which are fully known can instead be stored as constant tensors This allows to treat and manage the chain of operator treting shapes when the shapes are express as parameters (are dynamic). We then include then these functions in RModel: - AddShapeTensor given name and shape values (vector of Dim) and also a boolean flag specifying if tensor is a scalar. In this case the shape of teh shape tensor is a scalar (tensor with rank 0). - GetShapeTensorValues given name and retuirning vector of Dims - IsShapeTensor gievn the name We also include in RTensor a convenient template function GetTensorData, working for both constant tensors and ShapeTensors. In this commit we apply the needed fixes to use Shape Tensor in order to parge the ATLAS gnn model
1 parent fd69457 commit 979189c

18 files changed

+489
-138
lines changed

math/mathcore/inc/Fit/FitUtil.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ namespace FitUtil {
238238
// Figure out the size of the SIMD vectors.
239239
constexpr static int vecSize = sizeof(ROOT::Double_v) / sizeof(double);
240240
double xBuffer[vecSize];
241-
std::vector<ROOT::Double_v> xx(fDim);
241+
ROOT::Double_v xx[fDim];
242242
for (unsigned int i = 0; i < fDim; ++i) {
243243
// The Load() function reads multiple values from the pointed-to
244244
// memory into xx. This is why we have to copy the input values from
@@ -250,7 +250,7 @@ namespace FitUtil {
250250
}
251251
vecCore::Load<ROOT::Double_v>(xx[i], xBuffer);
252252
}
253-
auto res = (*f)(xx.data(), p);
253+
auto res = (*f)(xx, p);
254254
return vecCore::Get<ROOT::Double_v>(res, 0);
255255
}
256256

tmva/sofie/inc/TMVA/RModel.hxx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ private:
2525
std::unordered_map<std::string, InitializedTensor> fInitializedTensors;
2626
std::unordered_map<std::string, TensorInfo> fIntermediateTensorInfos;
2727
std::unordered_map<std::string, DynamicTensorInfo> fDynamicTensorInfos;
28+
std::unordered_map<std::string, std::pair<std::vector<Dim>, bool>> fShapeTensors; // constant tensors describing a shape
2829
std::unordered_map<std::string, std::string> fShapeParams; // parameters defining the dynamic shape (e.g. batch size), store also its default value
2930
std::vector<std::string> fDimShapeNames; // parameter names used to define the shapes
3031
std::vector<std::string> fOutputTensorNames;
3132
std::vector<std::string> fInputTensorNames; // input tensor names using ONNX order
3233

34+
35+
3336
std::vector<std::unique_ptr<ROperator>> fOperators;
3437

3538
std::vector<std::shared_ptr<RModel>> fSubGraphs; ///<! sub-graph models (transient)
@@ -52,11 +55,14 @@ public:
5255

5356
int Verbose() const { return fVerbose;}
5457

55-
const std::vector<size_t> & GetTensorShape(const std::string & name) const;
58+
std::vector<size_t> GetTensorShape(const std::string & name) const;
5659
std::vector<Dim> GetDimTensorShape(const std::string & name) const;
5760
std::vector<Dim> GetDynamicTensorShape(const std::string & name) const ;
5861

59-
const ETensorType &GetTensorType(std::string name) const;
62+
// get the values for the tensor representing a shape
63+
const std::vector<Dim> & GetShapeTensorValues(const std::string & tensor_name) const;
64+
65+
ETensorType GetTensorType(std::string name) const;
6066

6167

6268
bool CheckIfTensorAlreadyExist(std::string tensor_name);
@@ -73,6 +79,7 @@ public:
7379
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape,
7480
std::shared_ptr<void> data);
7581

82+
7683
template<class T>
7784
void AddConstantTensor(const std::string & name, const std::vector<size_t> & shape, const T * data) {
7885
size_t length = ConvertShapeToLength(shape);
@@ -99,6 +106,9 @@ public:
99106
AddInitializedTensor(tensor_name, GetTemplatedType(T()), shape, data);
100107
}
101108

109+
void AddShapeTensor(const std::string & name, const std::vector<Dim> & shapeValues, bool scalar = false);
110+
111+
102112
// add and initialize subgraph to the model
103113
void InitializeSubGraph(std::shared_ptr<RModel> graph);
104114

@@ -115,6 +125,8 @@ public:
115125
bool IsDimInputTensor(const std::string &name) const;
116126
// check if tensor is a fully specified input tensor
117127
bool IsReadyInputTensor(const std::string &name) const;
128+
/// check if a tensor is a shape tensor
129+
bool IsShapeTensor(const std::string & name) const;
118130

119131
// Add intermediate tensor
120132
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<Dim> dim_shape);
@@ -131,6 +143,9 @@ public:
131143
std::shared_ptr<void> data);
132144
std::shared_ptr<void> GetInitializedTensorData(std::string tensor_name);
133145

146+
template<class T>
147+
std::vector<T> GetTensorData(const std::string & name);
148+
134149
void Initialize(int batchSize = -1, bool verbose = false);
135150
void Initialize(const std::map<std::string,size_t> & inputParams, bool verbose = false);
136151

@@ -204,6 +219,23 @@ public:
204219
ClassDefNV(RModel, 3);
205220
};
206221

222+
// need to implement here templated member functions and its specialization
223+
224+
225+
template<class T>
226+
inline std::vector<T> RModel::GetTensorData(const std::string & name) {
227+
if (!IsInitializedTensor(name)) return std::vector<T>{};
228+
T * data = static_cast<T*>(GetInitializedTensorData(name).get());
229+
size_t size = ConvertShapeToLength(GetTensorShape(name));
230+
return std::vector<T>(data, data+size);
231+
}
232+
233+
template<>
234+
inline std::vector<Dim> RModel::GetTensorData<Dim>(const std::string & name) {
235+
if (!IsShapeTensor(name)) return std::vector<Dim>{};
236+
return GetShapeTensorValues(name);
237+
}
238+
207239
} // namespace SOFIE
208240
} // namespace Experimental
209241
} // namespace TMVA

tmva/sofie/inc/TMVA/ROperator.hxx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ protected:
4242
const std::string SP = " "; ///< space used to correctly indent the generated C++ code
4343
bool fUseSession = false; ///< flag to identify if using the session class
4444
bool fIsOutputConstant = false; ///< flag to identify if operator has a constant output (no need to generate code)
45-
45+
bool fIsOutputParamShape = false; ///< flag to identify of the output represents a parametric shape (can be knwon at compile time)
46+
4647
mutable std::vector<std::string_view> fInputTensorNames;
4748
mutable std::vector<std::string_view> fOutputTensorNames;
4849

@@ -54,7 +55,7 @@ public:
5455
std::span<const std::string_view> GetOpOutputTensors() const {
5556
return fOutputTensorNames;
5657
}
57-
58+
5859
};
5960

6061

tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,10 @@ public:
113113
fShapeB = model.GetTensorShape(fNB);
114114
fDimShapeB = ConvertShapeToDim(fShapeB);
115115
}
116-
std::cout << BinaryOperatorTrait<T, Op>::Name() << " ";
117-
if (dynamicInputs & 1)
118-
std::cout << fNA << " is dynamic " << ConvertShapeToString(fDimShapeA) << " ";
119-
if (dynamicInputs & 2)
120-
std::cout << fNB << " is dynamic " << ConvertShapeToString(fDimShapeB) << " ";
116+
if (dynamicInputs & 1 && model.Verbose() )
117+
std::cout << BinaryOperatorTrait<T, Op>::Name() << " : input " << fNA << " is dynamic " << ConvertShapeToString(fDimShapeA) << " ";
118+
if (dynamicInputs & 2 && model.Verbose())
119+
std::cout << BinaryOperatorTrait<T, Op>::Name() << " : input " << fNB << " is dynamic " << ConvertShapeToString(fDimShapeB) << " ";
121120
std::cout << std::endl;
122121
// check if need to broadcast at initialization time if shapes are known and different
123122
// (we could broadcast the tensor tensor to maximum values of dynamic shapes - to be done)
@@ -126,7 +125,6 @@ public:
126125
auto ret = UTILITY::MultidirectionalBroadcastShape(fShapeA, fShapeB);
127126
fBroadcastFlag = ret.first;
128127
fShapeY = ret.second;
129-
std::cout << BinaryOperatorTrait<T, Op>::Name() << " : checking for defined shapes " << fBroadcastFlag << " " << ConvertShapeToString(fShapeY) << std::endl;
130128
bool broadcast = ret.first > 0;
131129
if (broadcast) {
132130
// Y is the common shape of A and B
@@ -212,7 +210,6 @@ public:
212210
auto ret = UTILITY::MultidirectionalBroadcastShape(fDimShapeA, fDimShapeB);
213211
fBroadcastFlag = ret.first;
214212
fDimShapeY = ret.second;
215-
std::cout << BinaryOperatorTrait<T, Op>::Name() << " : checking for Dim shapes " << fBroadcastFlag << " " << ConvertShapeToString(fDimShapeY) << std::endl;
216213
// case of all parametric shapes and MultiDirectionalBroadcastShape return the max of the 2
217214
// need to do before we declare the output tensor shape and the broadcasted ones
218215
if (ret.first & 4) {

tmva/sofie/inc/TMVA/ROperator_Comparision.hxx

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,95 @@ public:
157157
fShapeY = fShapeX1;
158158
}
159159
// case of constant tensors
160-
if (model.IsInitializedTensor(fNX1) && model.IsInitializedTensor(fNX2) ) {
160+
T * data1 = nullptr;
161+
T * data2 = nullptr;
162+
std::vector<Dim> shapeData1;
163+
std::vector<Dim> shapeData2;
164+
size_t length = ConvertShapeToLength(fShapeY);
165+
bool * outData = new bool[length];
166+
if (model.IsInitializedTensor(fNX1)) {
167+
data1 = static_cast<T *>(model.GetInitializedTensorData(fNX1).get());
168+
} else if (model.IsShapeTensor(fNX1)) {
169+
shapeData1 = model.GetShapeTensorValues(fNX1);
170+
}
171+
if (model.IsInitializedTensor(fNX2)) {
172+
data2 = static_cast<T *>(model.GetInitializedTensorData(fNX2).get());
173+
} else if (model.IsShapeTensor(fNX2)) {
174+
shapeData2 = model.GetShapeTensorValues(fNX2);
175+
}
176+
if (data1 && data2) {
161177
fIsOutputConstant = true;
162-
auto data1 = static_cast<T *>(model.GetInitializedTensorData(fNX1).get());
163-
auto data2 = static_cast<T *>(model.GetInitializedTensorData(fNX2).get());
164-
size_t length = ConvertShapeToLength(fShapeY);
165-
bool * outData = new bool[length];
166178
for (size_t i = 0; i < length; i++)
167179
outData[i] = ComparisionTrait<T,Op>::Result(data1[i], data2[i]);
168180
model.AddConstantTensor(fNY, fShapeY, outData);
169181
if (model.Verbose())
170182
std::cout << ComparisionTrait<T,Op>::Name() << " op ---> " << fNY << " " << ConvertShapeToString(fShapeY) << " : "
171183
<< ConvertValuesToString(length,outData) << std::endl;
172-
delete [] outData;
173-
} else {
184+
} else if ((data1 || !shapeData1.empty()) && (data2 || !shapeData2.empty())) {
185+
fIsOutputConstant = true;
186+
if (data1 && !data2) {
187+
// data 1 is constant and data2 is shape
188+
for (size_t i = 0; i < length; i++) {
189+
if (shapeData2[i].isParam) {
190+
if (shapeData2[i].dim == size_t(-1) || data1[i] > 0) {
191+
fIsOutputConstant = false;
192+
break;
193+
} else {
194+
// assume a comparison is done with .dim = 0
195+
shapeData2[i].dim = 0;
196+
}
197+
}
198+
outData[i] = ComparisionTrait<T,Op>::Result(data1[i], static_cast<T>(shapeData2[i].dim));
199+
}
200+
} else if (!data1 && data2) {
201+
// data 1 is shape and dat2 is constant
202+
for (size_t i = 0; i < length; i++) {
203+
if (shapeData1[i].isParam) {
204+
if (shapeData1[i].dim == size_t(-1) || data2[i] > 0) {
205+
fIsOutputConstant = false;
206+
break;
207+
} else {
208+
// assume a comparison is done with .dim = 0
209+
shapeData1[i].dim = 0;
210+
}
211+
}
212+
outData[i] = ComparisionTrait<T,Op>::Result(static_cast<T>(shapeData1[i].dim), data2[i]);
213+
}
214+
} else if (!shapeData1.empty() && !shapeData2.empty() ) {
215+
// both data1 and data2 are shape tensors
216+
for (size_t i = 0; i < length; i++) {
217+
if (!shapeData1[i].isParam && !shapeData2[i].isParam) {
218+
outData[i] = ComparisionTrait<T,Op>::Result(shapeData1[i].dim, shapeData2[i].dim);
219+
}
220+
else if (shapeData1[i].isParam && shapeData2[i].isParam) {
221+
if (shapeData1[i].param == shapeData2[i].param)
222+
outData[i] = ComparisionTrait<int,Op>::Result(1,1); // comparison of two equal value
223+
else {
224+
fIsOutputConstant = false;
225+
break;
226+
}
227+
}
228+
else {
229+
fIsOutputConstant = false;
230+
break;
231+
}
232+
}
233+
}
234+
if (fIsOutputConstant) {
235+
model.AddConstantTensor(fNY, fShapeY, outData);
236+
if (model.Verbose())
237+
std::cout << ComparisionTrait<T,Op>::Name() << " op ---> " << fNY << " " << ConvertShapeToString(fShapeY) << " : "
238+
<< ConvertValuesToString(length,outData) << " (constant) " << std::endl;
239+
240+
}
241+
}
242+
delete [] outData;
243+
if (!fIsOutputConstant) {
174244
model.AddIntermediateTensor(fNY, ETensorType::BOOL , fShapeY);
245+
if (model.Verbose())
246+
std::cout << ComparisionTrait<T,Op>::Name() << " op ---> " << fNY << " " << ConvertShapeToString(fShapeY) << std::endl;
175247
}
248+
176249
// check if this is not output operators to add a specific line for definining the tensor_xxx variable
177250
const auto & outputTensorNames = model.GetOutputTensorNames();
178251
fIsModelOutput = false;

tmva/sofie/inc/TMVA/ROperator_Concat.hxx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,18 @@
193193
std::cout << "Output of concat operator has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;
194194

195195
// check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
196+
bool isOutputShape = false;
196197
if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
197198
fIsOutputConstant = true;
199+
isOutputShape = true;
200+
198201
for ( auto & input : fInputs) {
199202
if (!model.IsInitializedTensor(input)) {
200203
fIsOutputConstant = false;
201-
break;
204+
if (!model.IsShapeTensor(input)) {
205+
isOutputShape = false;
206+
break;
207+
}
202208
}
203209
}
204210
if (fIsOutputConstant) {
@@ -217,8 +223,35 @@
217223
model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
218224
if (model.Verbose()) {
219225
std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
220-
<< ConvertValuesToString(outputData) << std::endl;
226+
<< ConvertValuesToString(outputData) << " (constant)" << std::endl;
227+
}
228+
} else if (isOutputShape) {
229+
auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
230+
std::vector<Dim> outputData(ConvertShapeToLength(outputShape));
231+
size_t offset = 0;
232+
for ( auto & input : fInputs) {
233+
std::vector<Dim> inputData;
234+
auto inputShape = model.GetTensorShape(input); // shape is not dynamic
235+
size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
236+
if (model.IsShapeTensor(input))
237+
inputData = model.GetShapeTensorValues(input);
238+
else if (model.IsConstantTensor(input)) {
239+
inputData.resize(inputLength);
240+
auto intData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
241+
for (size_t i = 0; i < inputData.size(); i++)
242+
inputData[i] = Dim{ static_cast<size_t>(intData[i])};
243+
}
244+
std::cout << "concatanating input data " << inputLength << " " << inputData[0] << std::endl;
245+
std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset );
246+
offset += inputLength;
247+
}
248+
// add output tensor
249+
model.AddShapeTensor(fOutput,outputData, false); // cannot be a scalar
250+
if (model.Verbose()) {
251+
std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
252+
<< ConvertShapeToString(outputData) << " (shape)" << std::endl;
221253
}
254+
fIsOutputConstant = true;
222255
}
223256
}
224257
if (!fIsOutputConstant) {

tmva/sofie/inc/TMVA/ROperator_Constant.hxx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ private:
2525
std::vector<T> fValues;
2626
std::string fAttrType;
2727
bool fIsConstantOfShape = false;
28+
bool fIsUndefinedInputShape = false;
2829

2930
public:
3031
ROperator_Constant(){}
@@ -52,6 +53,7 @@ public:
5253
void Initialize(RModel& model) override {
5354
//input must be a graph input, or already initialized intermediate tensor
5455
size_t length = 1;
56+
/// ConstantOfShape-------------
5557
if (!fNX.empty()) {
5658
// case of ConstantOfShape (since no inputs in case of Constant operator)
5759
fIsConstantOfShape = true;
@@ -81,8 +83,13 @@ public:
8183
T value = fValues[0];
8284
fValues = std::vector<T>(length, value);
8385
}
84-
else {
85-
// case of non constant tensors- we need to do at run time
86+
else if (model.IsShapeTensor(fNX)) {
87+
// case tensor values representing output shapes are known
88+
fDimOutputShape = model.GetShapeTensorValues(fNX);
89+
} else {
90+
// case of not known shape tensors- we need to do at run time
91+
// not sure if we ever encounter this case
92+
fIsUndefinedInputShape = true;
8693
fDimShape = model.GetDimTensorShape(fNX);
8794
if (fDimShape.size() > 1 )
8895
throw std::runtime_error("TMVA SOFIE ConstantOfShape Op Input Tensor has invalid shape");
@@ -138,9 +145,11 @@ public:
138145
// generate constant tensor according to input
139146

140147
out << "\n//--------- ConstantOfShape " << opName << " --> " << ConvertShapeToString(fDimOutputShape) << "\n";
141-
// set shape values
142-
for (size_t i = 0; i < fDimOutputShape.size(); i++) {
143-
out << SP << "size_t " << fDimOutputShape[i].param << " = " << "tensor_" << fNX << "[" << i << "];\n";
148+
// set shape values if needed
149+
if (fIsUndefinedInputShape) {
150+
for (size_t i = 0; i < fDimOutputShape.size(); i++) {
151+
out << SP << "size_t " << fDimOutputShape[i].param << " = " << "tensor_" << fNX << "[" << i << "];\n";
152+
}
144153
}
145154
auto length = ConvertDimShapeToLength(fDimOutputShape);
146155
// vector is already allocated- fill with values

0 commit comments

Comments
 (0)