Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't add new non-transient class members without updating the class version

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now version 0 is used to flag that RModel is not made persistent


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,8 @@ public:
bool UseVDT() const { return fUseVDT;}

// Use the ClassDef macro to allow definition of custom streaming
ClassDefNV(RModel, 3);
// Use Version 0 since we don't support for time being ROOT I/O streaming of RModel objects
ClassDefNV(RModel, 4);
};

// need to implement here templated member functions and its specialization
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
20 changes: 14 additions & 6 deletions tmva/sofie/inc/TMVA/ROperator_NonZero.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ private:

std::string fNX;
std::string fNY;
std::string fNonZeroParam; // name of the parameter used to store the number of non zero elements when output is not constant
std::vector<Dim> fShapeX;
std::vector<Dim> fShapeY;

Expand Down Expand Up @@ -93,7 +94,15 @@ public:
fShapeY[0] = fShapeX.size();

// identify as -1 since we will declare maximum as size of input
fShapeY[1] = Dim{std::string("v_NonZero_") + fNX, static_cast<size_t>(-1)};
// we will compute at run time the actual number of non zero and rearrange the output vector accordingly
fNonZeroParam = "v_NonZero_" + fNX;
fShapeY[1] = Dim{fNonZeroParam, static_cast<size_t>(-1)};

// declare the parameter for number of non zero elements, used when output is not constant
auto inputLength = ConvertDimShapeToLength(fShapeX);
std::string codeDecl = SP + "size_t " + fNonZeroParam + " = " + inputLength + ";\n";
codeDecl += SP + "fV_NonZero_" + fNX + " = " + fNonZeroParam + ";\n";
model.AddExtraCodeForDimShapes(codeDecl);

model.AddIntermediateTensor(fNY, ETensorType::INT64, fShapeY);
if (model.Verbose()) {
Expand All @@ -104,13 +113,12 @@ public:

std::string GenerateSessionMembersCode(std::string /*opName*/) override {
if (fIsOutputConstant) return "";
// define output value used as max non zero with max size = input shape * N
auto inputLength = ConvertDimShapeToLength(fShapeX);
std::stringstream out;
out << SP << "size_t fV_NonZero_" << fNX << " = " << inputLength << ";\n";
out << SP << "size_t fV_NonZero_" << fNX << " = 0;\n";
return out.str();
}


std::string Generate(std::string opName) override {
if (fIsOutputConstant) {
return "";
Expand All @@ -127,9 +135,9 @@ public:
inputLength = ConvertShapeToLength(intShapeX);

size_t dims = fShapeX.size();
out << "\n//------ NonZero\n";
out << "\n//------ NonZero -> " << ConvertDimShapeToString(fShapeY) << "\n";

std::string vnonzero = "v_NonZero_" + fNX;
std::string vnonzero = fNonZeroParam;

// loop on input indices
out << SP << "size_t offset_" << opName << " = 0;\n";
Expand Down
14 changes: 9 additions & 5 deletions tmva/sofie/inc/TMVA/ROperator_Softmax.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ public:
}
}

std::string Generate(std::string OpName) override {
OpName = "op_" + OpName;
std::string Generate(std::string opName) override {
opName = "op_" + opName;
if (fShape.empty()) {
throw std::runtime_error("TMVA SOFIE Operator Softmax called to Generate without being initialized first");
}
std::stringstream out;
out << "///------- Softmax " << opName << " ---> " << fNY << " "
<< ConvertDimShapeToString(fShape) << "\n" << std::endl;
size_t size = fShape.size();
auto length_str = ConvertDimShapeToLength(fShape);
size_t axis = fAttrAxis < 0 ? size + fAttrAxis : fAttrAxis;
Expand All @@ -85,7 +87,7 @@ public:
num_rows = "(" + length_str + ") / (" + axis_size + ")";
}

out << "\n" << SP << "//------ SOFTMAX - " << size << " " << length_str << " " << axis << "\n";
out << SP << "//----- softmax axis is last one - " << axis << "\n";
out << SP << "for (int i = 0; i < " << num_rows << "; ++i) {\n";
out << SP << SP << "size_t offset = i * " << axis_size << ";\n";
out << SP << SP << fType << " const * x_ptr = &tensor_" << fNX << "[offset];\n";
Expand All @@ -111,14 +113,15 @@ public:
out << SP << "}\n";

} else {
// generic case for any axis
auto stride = UTILITY::ComputeStrideFromShape(fShape);
size_t k = 0;
std::vector<std::string> l(size);
for (size_t i = 0; i < size; i++) {
if (i != axis) {
for (size_t j = 0; j < k; j++) out << SP;
l[i] = std::string("i") + std::to_string(i);
out << "for (int " << l[i] << " = 0; " << l[i] << " < " << fShape[i] << "; " << l[i] << "++) {\n";
out << SP << "for (int " << l[i] << " = 0; " << l[i] << " < " << fShape[i] << "; " << l[i] << "++) {\n";
k++;
}
}
Expand Down Expand Up @@ -167,7 +170,8 @@ public:
out << "for (int i = 0; i < " << fShape[axis] << "; i++) {\n";
for (size_t j = 0; j < size; j++) out << SP;
out << "size_t id = index + i";
if (stride[axis].GetVal() != "1") out << "*(" << stride[axis] << ");\n";
if (stride[axis].GetVal() != "1") out << "*(" << stride[axis] << ")";
out << ";\n";
for (size_t j = 0; j < size; j++) out << SP;
out << "tensor_" << fNY << "[id] /= sum;\n";
if (fLogSoftmax) {
Expand Down
6 changes: 3 additions & 3 deletions tmva/sofie/inc/TMVA/SOFIE_common.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ std::string ConvertValuesToString(size_t n, const T * data, size_t maxprint = -1
if (std::is_floating_point_v<T>)
ret << std::setprecision(std::numeric_limits<T>::max_digits10) << data[i];
else
// cast in case of boolean (int8)
ret << data[i];
ret << std::to_string(data[i]);

if (i < n-1) ret << ", ";
if (i < n-1 && i == maxprint-1) ret << "..... ";
Expand Down Expand Up @@ -779,7 +778,8 @@ inline void Fill(float *output, float value, int size)
std::fill(output, output + size, value);
}

inline void Copy(float *output, float const *input, int size)
template <class T>
inline void Copy(T *output, T const *input, int size)
{
std::copy(input, input + size, output);
}
Expand Down
41 changes: 40 additions & 1 deletion tmva/sofie/src/RModel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@ void RModel::GenerateOutput()
// Use the session member (fXxx) when any dim is a runtime-computed identifier
// (e.g. NonZero count). For expression-type dims derived from input shapes
// (e.g. "((W+-3)/2+1)"), use the expression directly.
// for input shape parameters we don't need to use the session member since it is passed as argument to the infer function and it is not a runtime computed value
bool hasRuntimeParam = false;
for (auto const &dim : GetDynamicTensorShape(name)) {
if (dim.isParam && IsIdentifier(dim.param) && !IsInputTensorShapeParam(dim.param))
Expand Down Expand Up @@ -1304,7 +1305,7 @@ void RModel::GenerateSessionCode()

// storing the parameters for future checking to avoid mismatches
if (!fDimShapeNames.empty()) {
fGC += "\n\n";
fGC += "\n// dynamic shape parameters\n";
std::sort(fDimShapeNames.begin(), fDimShapeNames.end());
for (const auto &p : fDimShapeNames) {
fGC += "size_t " + memberNameForDimShape(p) + ";\n";
Expand Down Expand Up @@ -1361,6 +1362,8 @@ void RModel::GenerateSessionCode()
fGC += " " + memberNameForDimShape(p) + " = " + p + ";\n";
}
}
// add some extra code needed for initialization of dynamic parameters
fGC += fExtraCodeForDimShapes;

if (fUseWeightFile) {
fGC += "\n//--- reading weights from file\n";
Expand Down Expand Up @@ -1759,6 +1762,42 @@ void RModel::GenerateRequiredInputTensorInfo()

fGC +=
"\nconstexpr bool hasDynamicInputTensors{" + std::string{hasDynamicInputTensors ? "true" : "false"} + "};\n\n";

fGC += "\n// Output tensor dimensions\n";
bool hasDynamicOutputTensors = false;
for (std::size_t iOutput = 0; iOutput < fOutputTensorNames.size(); ++iOutput) {
auto const &name = fOutputTensorNames[iOutput];
if (IsDynamicTensor(name)) {
hasDynamicOutputTensors = true;
}
std::vector<Dim> shape = GetDimTensorShape(name);
fGC += "constexpr std::array<SingleDim, " + std::to_string(shape.size()) + "> dim_" + name + "{";
for (std::size_t iDim = 0; iDim < shape.size(); ++iDim) {
auto const &dim = shape[iDim];
if (dim.isParam) {
fGC += "SingleDim{\"" + dim.GetVal() + "\"}";
} else {
fGC += "SingleDim{" + dim.GetVal() + "}";
}
if (iDim != shape.size() - 1) {
fGC += ", ";
}
}
fGC += "};\n";
}
fGC += "\nconstexpr std::array<TensorDims, " + std::to_string(fOutputTensorNames.size()) + "> outputTensorDims{\n";
for (std::size_t iOutput = 0; iOutput < fOutputTensorNames.size(); ++iOutput) {
auto const &name = fOutputTensorNames[iOutput];
fGC += SP + "makeDims(dim_" + name + ")";
if (iOutput == fOutputTensorNames.size() - 1) {
fGC += "\n";
} else {
fGC += ",\n";
}
}
fGC += "};\n";
fGC +=
"\nconstexpr bool hasDynamicOutputTensors{" + std::string{hasDynamicOutputTensors ? "true" : "false"} + "};\n\n";
}

void RModel::PrintRequiredInputTensors() const {
Expand Down
Loading