Skip to content
5 changes: 4 additions & 1 deletion tmva/sofie/inc/TMVA/RModel.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ private:
MemoryPoolInfo fIntermediateMemoryInfo; ///<! intermediate memory info (transient)
std::unordered_map<std::string_view, size_t> fIntermediateTensorFrequencyLookup; ///<! lookup table for intermediate tensor frequency (transient)

std::string fExtraCodeForDimShapes; // extra code needed for initialization of dynamic parameters (e.g. number of non zero elements in NonZero operator)
Comment thread
guitargeek marked this conversation as resolved.

public:
/**
Default constructor. Needed to allow serialization of ROOT objects. See
Expand Down Expand Up @@ -108,6 +110,7 @@ public:

void AddShapeTensor(const std::string & name, const std::vector<Dim> & shapeValues, bool scalar = false);

void AddExtraCodeForDimShapes(const std::string & code) { fExtraCodeForDimShapes += code; }

// add and initialize subgraph to the model
void InitializeSubGraph(std::shared_ptr<RModel> graph);
Expand Down Expand Up @@ -239,7 +242,7 @@ public:
bool UseVDT() const { return fUseVDT;}

// Use the ClassDef macro to allow definition of custom streaming
ClassDefNV(RModel, 3);
ClassDefNV(RModel, 4);
};

// need to implement here templated member functions and its specialization
Expand Down
6 changes: 2 additions & 4 deletions tmva/sofie/inc/TMVA/RModel_Base.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,8 @@ public:
}
void AddNeededStdLib(std::string libname)
{
static const std::unordered_set<std::string> allowedStdLib = {"vector", "algorithm", "cmath", "memory", "span"};
if (allowedStdLib.find(libname) != allowedStdLib.end()) {
fNeededStdLib.insert(libname);
}
// if the library is already in the set, insert does nothing, so we don't need to check before inserting
fNeededStdLib.insert(libname);
}
void AddNeededCustomHeader(std::string filename)
{
Expand Down
2 changes: 1 addition & 1 deletion tmva/sofie/inc/TMVA/ROperator.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected:
const std::string SP = " "; ///< space used to correctly indent the generated C++ code
bool fUseSession = false; ///< flag to identify if using the session class
bool fIsOutputConstant = false; ///< flag to identify if operator has a constant output (no need to generate code)
bool fIsOutputParamShape = false; ///< flag to identify of the output represents a parametric shape (can be knwon at compile time)
bool fIsOutputParamShape = false; ///< flag to identify of the output represents a parametric shape (can be known at compile time)

mutable std::vector<std::string_view> fInputTensorNames;
mutable std::vector<std::string_view> fOutputTensorNames;
Expand Down
8 changes: 4 additions & 4 deletions tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ public:
<< ConvertShapeToString(fShapeY) << " : " << ConvertValuesToString(dataY) << std::endl;
}
} else if (((model.IsShapeTensor(fNA) && model.IsShapeTensor(fNB)) ||
(model.IsShapeTensor(fNA) && model.IsConstantTensor(fNB)) ||
(model.IsShapeTensor(fNB) && model.IsConstantTensor(fNA)))
(model.IsShapeTensor(fNA) && model.IsInitializedTensor(fNB)) ||
(model.IsShapeTensor(fNB) && model.IsInitializedTensor(fNA)))
&& (fShapeA.size() <=1 && fShapeB.size() <=1 && model.GetTensorType(fNA) == ETensorType::INT64)) {
// case of shape tensors ( tensors are of rank 0 or 1 )
std::vector<Dim> dimValA;
Expand All @@ -235,9 +235,9 @@ public:
dimValues[i] = Dim{ static_cast<size_t>(data[0])};
}
};
if (model.IsConstantTensor(fNA)) {
if (model.IsInitializedTensor(fNA)) {
convertDataToDim(fNA,fShapeA,dimValA);
} else if (model.IsConstantTensor(fNB)) {
} else if (model.IsInitializedTensor(fNB)) {
convertDataToDim(fNB,fShapeB,dimValB);
}

Expand Down
11 changes: 8 additions & 3 deletions tmva/sofie/inc/TMVA/ROperator_Cast.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ public:
if (!fIsOutputConstant)
model.AddIntermediateTensor(fNY, fType, fShape);
if (model.Verbose()) {
std::cout << "Cast : " << ConvertTypeToString(inputType) << " " << fNX << " -> " << ConvertTypeToString(fType) << " for " << fNY
<< " shape " << ConvertDimShapeToString(fShape);
std::cout << "Cast : " << ConvertTypeToString(inputType) << " " << fNX << " -> " << ConvertTypeToString(fType);
if (fType == ETensorType::BOOL) std::cout << " (converted from BOOL) ";
std::cout << " for " << fNY << " shape " << ConvertDimShapeToString(fShape);
if (fIsOutputConstant) std::cout << " (constant) ";
std::cout << std::endl;
}
Expand All @@ -87,7 +88,11 @@ public:

out << SP << "for (int id = 0; id < " << length << " ; id++){\n";

out << SP << SP << "tensor_" << fNY << "[id] = static_cast<"<< ConvertTypeToString(fType) << ">(tensor_" << fNX << "[id]);\n";
// need to handle bool case separatly since casting to uint8 will not give right result
if (fType == ETensorType::BOOL)
out << SP << SP << "tensor_" << fNY << "[id] = (tensor_" << fNX << "[id] != 0) ? 1 : 0;\n";
else
out << SP << SP << "tensor_" << fNY << "[id] = static_cast<"<< ConvertTypeToString(fType) << ">(tensor_" << fNX << "[id]);\n";

out << SP << "}\n";
return out.str();
Expand Down
174 changes: 112 additions & 62 deletions tmva/sofie/inc/TMVA/ROperator_Concat.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
std::vector<std::string> fInputs;
std::string fOutput;
std::vector<Dim>fOutputShape;
std::vector<Dim> fOutputShapeData; // in case output is a shape tensor we store here the output shape value data (can be parametric)
std::vector<std::vector<Dim>> fInputShapes;

public:
Expand Down Expand Up @@ -170,82 +171,125 @@
}

void Initialize(RModel& model) override {
std::vector<std::vector<size_t>> inputIntShapes;
for (auto &it : fInputs) {
if (model.CheckIfTensorAlreadyExist(it) == false) {
throw std::runtime_error("TMVA SOFIE Concat Op Input Tensor " + it + " is not found in model");
}
fInputShapes.push_back(model.GetDimTensorShape(it));
if (!model.IsDynamicTensor(it)) {
inputIntShapes.push_back(ConvertShapeToInt(fInputShapes.back()));
}
}
if (inputIntShapes.size() == fInputs.size()) {
// if all input shapes are static we can compute output shape at initialization time
auto outputIntShape = ShapeInference(inputIntShapes)[0];
fOutputShape = ConvertShapeToDim(outputIntShape);
if (model.Verbose())
std::cout << "Initialize Concat operator with defined inputs shapes, "
<< "output has shape " << ConvertShapeToString(outputIntShape) << std::endl;

} else {
// if at least one input shape is dynamic we need to compute output shape using the symbolic expression for the dimensions
fOutputShape = ShapeInference(fInputShapes, model);
if (model.Verbose())
std::cout << "Initialize Concat operator with dynamic inputs shapes, "
<< "output has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;
}
fOutputShape = ShapeInference(fInputShapes, model);
if (model.Verbose())
std::cout << "Output of concat operator has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;

// check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
bool isOutputShape = false;
if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
fIsOutputConstant = true;
isOutputShape = true;

for ( auto & input : fInputs) {
if (!model.IsInitializedTensor(input)) {
fIsOutputConstant = false;
if (!model.IsShapeTensor(input)) {
isOutputShape = false;
break;
}
}
// if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
fIsOutputConstant = true;
isOutputShape = true;

for (auto &input : fInputs) {
if (model.IsDynamicTensor(input)) {
fIsOutputConstant = false;
isOutputShape = false;
break;
}
if (fIsOutputConstant) {
auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
size_t offset = 0;
for ( auto & input : fInputs) {
auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
size_t inputLength = ConvertShapeToLength(inputShape);
std::copy(inputData, inputData + inputLength, outputData.begin() + offset );
offset += inputLength;
// the data of the input tensor don't need to be written in the generated code and data file
model.SetNotWritableInitializedTensor(input);
}
model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
if (model.Verbose()) {
std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
<< ConvertValuesToString(outputData) << " (constant)" << std::endl;
}
} else if (isOutputShape) {
auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
std::vector<Dim> outputData(ConvertShapeToLength(outputShape));
size_t offset = 0;
for ( auto & input : fInputs) {
std::vector<Dim> inputData;
auto inputShape = model.GetTensorShape(input); // shape is not dynamic
size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
if (model.IsShapeTensor(input)) {
inputData = model.GetShapeTensorValues(input);
} else if (model.IsInitializedTensor(input)) {
inputData.resize(inputLength);
auto intData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
for (size_t i = 0; i < inputData.size(); i++)
inputData[i] = Dim{ static_cast<size_t>(intData[i])};
}
else {
// this should not happen
throw std::runtime_error("TMVA SOFIE Concat Operator- invalid input type for shape output type");
if (!model.IsInitializedTensor(input)) {
if (model.IsShapeTensor(input)) {
// if it is a shape tensor we can have constant output if the shapes are defined)
auto shapeData = model.GetShapeTensorValues(input);
bool isShapeFullyDefined = ConvertShapeToInt(shapeData).size() == shapeData.size();
if (!isShapeFullyDefined) {
fIsOutputConstant = false;
} else {
// if shape is fully defined we can consider output as constant and we can compute the output
// shape at initialization time
fIsOutputConstant = fIsOutputConstant && true;
}
std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset );
offset += inputLength;
// inputs are then shape tensors and output is a shape tensor
isOutputShape = true;
} else {
// case of standard intermediate tensor
fIsOutputConstant = false;
isOutputShape = false;
break;
}
// add output tensor
model.AddShapeTensor(fOutput,outputData, false); // cannot be a scalar
if (model.Verbose()) {
std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
<< ConvertDimShapeToString(outputData) << " (shape)" << std::endl;
} else {
fIsOutputConstant = fIsOutputConstant && true;
}
}
//}

if (fIsOutputConstant) {
auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
size_t offset = 0;
for (auto &input : fInputs) {
auto inputData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
size_t inputLength = ConvertShapeToLength(inputShape);
std::copy(inputData, inputData + inputLength, outputData.begin() + offset);
offset += inputLength;
// the data of the input tensor don't need to be written in the generated code and data file
model.SetNotWritableInitializedTensor(input);
}
model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
if (model.Verbose()) {
std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
<< ConvertValuesToString(outputData) << " (constant)" << std::endl;
}
} else if (isOutputShape) {
auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
if (outputShape.size() != 1)
throw std::runtime_error("TMVA SOFIE Concat Op - output shape for shape tensor must have rank 1");
// output shape is a rank 1 tensor with size equal to the output rank
std::vector<Dim> outputData(outputShape[0]);
size_t offset = 0;
for (auto &input : fInputs) {
std::vector<Dim> inputData;
auto inputShape = model.GetTensorShape(input); // shape is not dynamic
size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
if (model.IsShapeTensor(input)) {
inputData = model.GetShapeTensorValues(input);
} else if (model.IsInitializedTensor(input)) {
inputData.resize(inputLength);
auto intData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
for (size_t i = 0; i < inputData.size(); i++)
inputData[i] = Dim{static_cast<size_t>(intData[i])};
} else {
// this should not happen
throw std::runtime_error("TMVA SOFIE Concat Operator- invalid tensor input " + input +
" for shape output type");
}
fIsOutputConstant = true;
std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset);
offset += inputLength;
}
// add output tensor
model.AddShapeTensor(fOutput, outputData, false); // cannot be a scalar
fOutputShapeData = outputData;
if (model.Verbose()) {
std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
<< ConvertDimShapeToString(outputData) << " (shape)" << std::endl;
}
fIsOutputParamShape = true;
}
if (!fIsOutputConstant) {
if (!fIsOutputConstant && !fIsOutputParamShape) {
model.AddIntermediateTensor(fOutput, model.GetTensorType(fInputs[0]), fOutputShape);
if (model.Verbose()) {
std::cout << "Concat ---> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << std::endl;
Expand All @@ -260,8 +304,14 @@

if (fIsOutputConstant) return out.str();

if(fOutputShape.empty()){
throw std::runtime_error("TMVA SOFIE Concat called to Generate without being initialized first");
if (fIsOutputParamShape) {
// output is a shape tensor defined by the concatenation of the input shapes
out << "// output is a shape tensor defined by the concatenation of the input shapes\n";
for (int i = 0; i < static_cast<int>(fOutputShape
[0].dim); i++) {
out << SP << "tensor_" << fOutput << "[" << i << "] = " << fOutputShapeData[i] << ";\n";
}
return out.str();
}
// special case when memory is contiguous
bool hasShapeOnes = true;
Expand Down
Loading
Loading