Skip to content
Merged
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
3 changes: 0 additions & 3 deletions tmva/sofie/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTTMVASofie
# target.
NO_GLOB_HEADERS
HEADERS
TMVA/RModel_Base.hxx
TMVA/RModel.hxx
TMVA/SOFIE_common.hxx
TMVA/SOFIEHelpers.hxx
SOURCES
src/RModel_Base.cxx
src/RModel.cxx
src/SOFIE_common.cxx
src/SOFIE_common_helpers.cxx
Expand Down
1 change: 0 additions & 1 deletion tmva/sofie/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ With the default settings, the weights are contained in a separate binary file,
model.Generate(Options::kNoWeightFile);
```

Other such options includes `Options::kNoSession` (for not generating the Session class, and instead keeping the infer function independent).
SOFIE also supports generating inference code with RDataFrame as inputs, refer to the tutorials below for examples.

## Supported ONNX operators
Expand Down
1 change: 0 additions & 1 deletion tmva/sofie/inc/LinkDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#pragma link C++ nestedclass;

#pragma link C++ namespace TMVA::Experimental::SOFIE;
#pragma link C++ class TMVA::Experimental::SOFIE::RModel_Base+;
#pragma link C++ class TMVA::Experimental::SOFIE::RModel+;
#pragma link C++ struct TMVA::Experimental::SOFIE::InitializedTensor+;
#pragma link C++ struct TMVA::Experimental::SOFIE::TensorInfo+;
Expand Down
102 changes: 98 additions & 4 deletions tmva/sofie/inc/TMVA/RModel.hxx
Original file line number Diff line number Diff line change
@@ -1,24 +1,83 @@
#ifndef TMVA_SOFIE_RMODEL
#define TMVA_SOFIE_RMODEL

#include "TMVA/RModel_Base.hxx"
#include "TMVA/SOFIE_common.hxx"

#include "Rtypes.h" // for ClassDefNV

#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <set>
#include <sstream>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace TMVA::Experimental::SOFIE {

enum class Options {
kDefault = 0x0,
kNoWeightFile = 0x2,
kRootBinaryWeightFile = 0x4,
};

// Optimization levels inspired by ONNXRuntime.
// We only get Operator Fusion with the Basic, and
// memory reuse with Extended. kExtended is enabled
// by default
enum class OptimizationLevel {
kBasic = 0x0,
kExtended = 0x1,
};

enum class WeightFileType { None, RootBinary, Text };

std::underlying_type_t<Options> operator|(Options opA, Options opB);
std::underlying_type_t<Options> operator|(std::underlying_type_t<Options> opA, Options opB);

// The ROperator interface is an implementation detail of the code generation
// and deliberately not exposed to the public: only this forward declaration
// is visible here, the full definition (TMVA/ROperator.hxx) is a private
// header of the SOFIE libraries.
class ROperator;

class RModel final : public RModel_Base {
class RModel final {

private:
std::string fFileName; // file name of original model file for identification
std::string fParseTime; // UTC date and time string at parsing

WeightFileType fWeightFile = WeightFileType::Text;

std::unordered_set<std::string> fNeededBlasRoutines;
// Set to true once GenerateHeaderInfo has emitted the extern "C" declaration
// of the BLAS sgemm_ routine (from fNeededBlasRoutines). It lets the
// standalone Gemm_Call helper skip emitting a second, duplicate declaration.
bool fBlasSgemmDeclared = false;

std::unordered_set<std::string> fNeededStdLib = {"vector"};
std::unordered_set<std::string> fCustomOpHeaders;

// Inference helper functions (from SOFIE_common) that the generated code
// needs. Their standalone definitions are emitted into the generated header
// so that it does not depend on including TMVA/SOFIE_common.hxx.
std::set<std::string> fNeededHelperFunctions;

std::string fName = "UnnamedModel";
std::string fGC; // generated code
bool fUseWeightFile = true;

// Placeholder tokens emitted by GenerateHeaderInfo and later replaced by
// EmitHelperFunctionsCode with the actual helper includes / definitions.
// This two-step approach is needed because the full set of required helpers
// is only known once all operators (and sub-graphs) have been generated.
static constexpr const char *kHelperIncludesMarker = "//@SOFIE_HELPER_INCLUDES@\n";
static constexpr const char *kHelperFunctionsMarker = "//@SOFIE_HELPER_FUNCTIONS@\n";

bool fIsInitialized = false;
bool fIsSubGraph = false;
bool fUseVDT = false;
Expand Down Expand Up @@ -68,7 +127,11 @@ public:
https://root.cern/manual/io_custom_classes/#restrictions-on-types-root-io-can-handle
*/
RModel() = default;
RModel(std::string name, std::string parsedtime) : RModel_Base(name, parsedtime) {}
RModel(std::string name, std::string parsedtime) : fFileName(std::move(name)), fParseTime(std::move(parsedtime))
{
fName = fFileName.substr(0, fFileName.rfind("."));
fName = UTILITY::Clean_name(fName);
}

// Defined out of line because ROperator is an incomplete type in this
// header (the definition is a private implementation header).
Expand Down Expand Up @@ -233,13 +296,44 @@ public:
void PrintOutputTensors() const;
void OutputGenerated(std::string filename = "", bool append = false);
void SetFilename(std::string filename) { fName = filename; }
std::string GetFilename() { return fName; }
const std::string &GetName() const { return fName; }

void AddBlasRoutines(std::vector<std::string> routines)
{
for (auto &routine : routines) {
fNeededBlasRoutines.insert(routine);
}
}
void AddNeededStdLib(std::string libname)
{
// if the library is already in the set, insert does nothing, so we don't need to check before inserting
fNeededStdLib.insert(std::move(libname));
}
void AddNeededCustomHeader(std::string filename)
{
fCustomOpHeaders.insert(std::move(filename));
}
// Register an inference helper function that the generated code needs. See
// EmitHelperFunctionsCode for the list of recognised keys.
void AddNeededHelperFunction(std::string name)
{
fNeededHelperFunctions.insert(std::move(name));
}
const std::set<std::string> &GetNeededHelperFunctions() const { return fNeededHelperFunctions; }

void GenerateHeaderInfo(std::string &hgname);
// Replace the helper markers in the generated code with the standalone
// definitions of the helper functions collected in fNeededHelperFunctions.
void EmitHelperFunctionsCode();
void PrintGenerated(std::ostream &os = std::cout) { os << fGC; }
std::string ReturnGenerated() { return fGC; }

void PrintRequiredInputTensors() const;
void PrintInitializedTensors() const;
void PrintDynamicTensors() const;
void HeadInitializedTensors(std::string name, int n_print = 50);

bool UseSession() const { return fUseSession; }
// flag to use vdt for fast math functions (e.g. exp in softmax)
void SetUseVDT(bool on) {
fUseVDT = on;
Expand Down
120 changes: 0 additions & 120 deletions tmva/sofie/inc/TMVA/RModel_Base.hxx

This file was deleted.

1 change: 0 additions & 1 deletion tmva/sofie/inc/TMVA/ROperator.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public:
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 known at compile time)

Expand Down
15 changes: 1 addition & 14 deletions tmva/sofie/inc/TMVA/ROperator_Conv.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ public:
}

void Initialize(RModel& model) override {
fUseSession = model.UseSession();
if (!model.CheckIfTensorAlreadyExist(fNX)) {
throw
std::runtime_error("TMVA SOFIE Conv op Input Tensor " + fNX + " is not found in model");
Expand Down Expand Up @@ -272,7 +271,6 @@ public:
auto shapeDimB = model.GetDimTensorShape(fNB);
bool broadcast_needed = !UTILITY::AreSameShape(shapeDimB, targetShape);
if (broadcast_needed) {
auto original_data = model.GetInitializedTensorData(fNB);
// make bias shape equal to Y shape by adding 1
if (fShapeB.size() < 1)
throw std::runtime_error("TMVA SOFIE Conv op: Bias Tensor has empty shape");
Expand All @@ -283,19 +281,8 @@ public:
ConvertShapeToString(fShapeB));
if (fType != "float")
throw std::runtime_error("TMVA SOFIE Conv op: Broadcasting for non-float type tensors is not supported");
// here is the actual broadcasting
// here is the actual broadcasting (done in the Session constructor via GenerateInitCode)
fBroadcastBias = true;
if (!fUseSession) {
// do here broadcasting
std::vector<size_t> shape(fDim + 1, 1);
shape[0] = fShapeB[0];
auto intTargetShape = ConvertShapeToInt(targetShape);
std::shared_ptr<void> new_data_ptr(
UTILITY::UnidirectionalBroadcast(static_cast<float *>(original_data.get()), shape, intTargetShape),
std::default_delete<float[]>());
model.UpdateInitializedTensor(fNB, model.GetTensorType(fNB), intTargetShape, new_data_ptr);
fShapeB = model.GetTensorShape(fNB);
}
}
}
// output channel size can be parametric and is an expression
Expand Down
Loading
Loading