file src/BDT.cpp
[No description available]
Source code
#include "gambit/cmake/cmake_variables.hpp"
#ifndef EXCLUDE_ROOT
#include "gambit/ColliderBit/BDT.h"
#include "mvautils/NodeImpl.h"
#include "mvautils/ForestLGBM.h"
#include "mvautils/ForestXGBoost.h"
#include "mvautils/ForestTMVA.h"
#include "TMVA/MethodBDT.h"
#include "TMVA/DecisionTree.h"
#include "TTree.h"
#include <stack>
#include <string>
#include <sstream>
#include <set>
#include <memory>
#include <stdexcept>
using namespace MVAUtils;
void NodeTMVA::Print(index_t index) const
{
std::cout << " Variable: " << int(m_var) << ", Cut: " << m_cut
<< " (index = " << index << ")" << std::endl;
}
void NodeLGBMSimple::Print(index_t index) const
{
std::cout << " Variable: " << int(m_var) << ", Cut: " << m_cut
<< " (index = " << index << ")" << std::endl;
}
void NodeLGBM::Print(index_t index) const
{
std::cout << " Variable: " << int(m_var) << ", Cut: " << m_cut << ", DefaultLeft: " << (int)m_default_left
<< " (index = " << index << ")" << std::endl;
}
void NodeXGBoost::Print(index_t index) const
{
std::cout << " Variable: " << int(m_var) << ", Cut: " << m_cut << ", DefaultLeft: " << (int)m_default_left
<< " (index = " << index << ")" << std::endl;
}
namespace{
/* utility functions : to split option (e.g. "creator=lgbm;node=lgbm_simple")
* in a std::map {{"creator", "lgbm"}, {"node", "lgbm_simple"}}
*/
std::string get_default_string_map(const std::map <std::string, std::string> & m,
const std::string& key, const std::string & defval="")
{
std::map<std::string, std::string>::const_iterator it = m.find(key);
if (it == m.end()) { return defval; }
else { return it->second; }
}
std::map<std::string, std::string> parseOptions(std::string raw_options)
{
std::stringstream ss(raw_options);
std::map<std::string, std::string> options;
std::string item;
while (std::getline(ss, item, ';')) {
auto pos = item.find_first_of('=');
const auto right = item.substr(pos+1);
const auto left = item.substr(0, pos);
if (!options.insert(std::make_pair(left, right)).second)
{
throw std::runtime_error(std::string("option ") + left + " duplicated in title of TTree used as input");
}
}
return options;
}
}
/** c-tor from TTree **/
BDT::BDT(::TTree *tree)
{
// at runtime decide which flavour of BDT we need to build
// the information is coming from the title of the TTree
std::map<std::string, std::string> options = parseOptions(tree->GetTitle());
std::string creator = get_default_string_map(options, std::string("creator"));
if (creator == "lgbm")
{
std::string node_type = get_default_string_map (options, std::string("node_type"));
if (node_type == "lgbm") {
m_forest = std::make_unique<ForestLGBM>(tree);
}
else if (node_type == "lgbm_simple") {
m_forest = std::make_unique<ForestLGBMSimple>(tree); // this do not support nan as inputs
}
else
{
throw std::runtime_error("the title of the input tree is misformatted: cannot understand which BDT implementation to use");
}
}
else if (creator == "xgboost")
{
//this do support nan as inputs
m_forest = std::make_unique<ForestXGBoost>(tree);
}
else {
// default for compatibility: old TTree (based on TMVA) don't have a special title
m_forest = std::make_unique<ForestTMVA>(tree);
}
}
BDT::~BDT(){}
unsigned int BDT::GetNTrees() const { return m_forest->GetNTrees(); }
int BDT::GetNVars() const { return m_forest->GetNVars(); }
float BDT::GetOffset() const { return m_forest->GetOffset(); }
/** c-tor from TMVA::MethodBDT **/
BDT::BDT(TMVA::MethodBDT* bdt, bool isRegression, bool useYesNoLeaf)
{
if (!bdt) { throw std::runtime_error("bdt pointer to BDT constructor is null"); }
m_forest = std::make_unique<ForestTMVA>(bdt, isRegression, useYesNoLeaf);
}
/** Return offset + the sum of the response of each tree **/
float BDT::GetResponse(const std::vector<float>& values) const
{
return m_forest->GetResponse(values);
}
/** Return offset + the sum of the response of each tree **/
float BDT::GetResponse(const std::vector<float*>& pointers) const
{
return m_forest->GetResponse(pointers);
}
float BDT::GetClassification(const std::vector<float>& values) const
{
return m_forest->GetClassification(values);
}
float BDT::GetClassification(const std::vector<float*>& pointers) const
{
return m_forest->GetClassification(pointers);
}
float BDT::GetGradBoostMVA(const std::vector<float>& values) const
{
const float sum = m_forest->GetRawResponse(values); // ignores the offset
return 2. / (1 + exp(-2 * sum)) - 1; //output shaping for gradient boosted decision tree (-1,1)
}
float BDT::GetGradBoostMVA(const std::vector<float*>& pointers) const
{
const float sum = m_forest->GetRawResponse(pointers); // ignores the offset
return 2. / (1 + exp(-2 * sum)) - 1; //output shaping for gradient boosted decision tree (-1,1)
}
std::vector<float> BDT::GetMultiResponse(const std::vector<float>& values,
unsigned int numClasses) const
{
return m_forest->GetMultiResponse(values, numClasses);
}
std::vector<float> BDT::GetMultiResponse(const std::vector<float*>& pointers,
unsigned int numClasses) const
{
return m_forest->GetMultiResponse(pointers, numClasses);
}
float BDT::GetTreeResponse(const std::vector<float>& values, MVAUtils::index_t index) const
{
return m_forest->GetTreeResponse(values, index);
}
float BDT::GetTreeResponse(const std::vector<float*>& pointers, MVAUtils::index_t index) const
{
return m_forest->GetTreeResponse(pointers, index);
}
TTree* BDT::WriteTree(TString name) const { return m_forest->WriteTree(name); }
void BDT::PrintForest() const { m_forest->PrintForest(); }
void BDT::PrintTree(unsigned int itree) const { m_forest->PrintTree(itree); }
ForestLGBMSimple::ForestLGBMSimple(TTree* tree) : ForestLGBMBase<NodeLGBMSimple>()
{
m_max_var = 0;
// variables read from the TTree
std::vector<int> *vars = nullptr;
std::vector<float> *values = nullptr;
std::vector<NodeLGBMSimple> nodes;
tree->SetBranchAddress("vars", &vars);
tree->SetBranchAddress("values", &values);
for (int i = 0; i < tree->GetEntries(); ++i)
{
// each entry in the TTree is a decision tree
tree->GetEntry(i);
if (!vars) { throw std::runtime_error("vars pointer is null in ForestLGBMSimple constructor"); }
if (!values) { throw std::runtime_error("values pointers is null in ForestLGBMSimple constructor"); }
if (vars->size() != values->size()) { throw std::runtime_error("inconsistent size for vars and values in ForestLGBMSimple constructor"); }
nodes.clear();
std::vector<MVAUtils::index_t> right = detail::computeRight(*vars);
for (size_t i = 0; i < vars->size(); ++i) {
nodes.emplace_back(vars->at(i), values->at(i), right[i]);
if (vars->at(i) > m_max_var) { m_max_var = vars->at(i); }
}
newTree(nodes);
} // end loop on TTree, all decision tree loaded
delete vars;
delete values;
}
TTree* ForestLGBMSimple::WriteTree(TString name) const
{
TTree *tree = new TTree(name.Data(), "creator=lgbm;node_type=lgbm_simple");
std::vector<int> vars;
std::vector<float> values;
tree->Branch("vars", &vars);
tree->Branch("values", &values);
for (size_t itree = 0; itree < GetNTrees(); ++itree) {
vars.clear();
values.clear();
for(const auto& node : GetTree(itree)) {
vars.push_back(node.GetVar());
values.push_back(node.GetVal());
}
tree->Fill();
}
return tree;
}
void ForestLGBMSimple::PrintForest() const
{
std::cout << "***BDT LGBMSimple: Printing entire forest***" << std::endl;
Forest::PrintForest();
}
ForestLGBM::ForestLGBM(TTree* tree) : ForestLGBMBase<NodeLGBM>()
{
m_max_var = 0;
// variables read from the TTree
std::vector<int> *vars = nullptr;
std::vector<float> *values = nullptr;
std::vector<bool> *default_left = nullptr;
std::vector<NodeLGBM> nodes;
tree->SetBranchAddress("vars", &vars);
tree->SetBranchAddress("values", &values);
tree->SetBranchAddress("default_left", &default_left);
for (int i = 0; i < tree->GetEntries(); ++i)
{
// each entry in the TTree is a decision tree
tree->GetEntry(i);
if (!vars) { throw std::runtime_error("vars pointer is null in ForestLGBM constructor"); }
if (!values) { throw std::runtime_error("values pointers is null in ForestLGBM constructor"); }
if (!default_left) { throw std::runtime_error("default_left pointers is null in ForestLGBM constructor"
); }
if (vars->size() != values->size()) { throw std::runtime_error("inconsistent size for vars and values in ForestLGBM constructor"); }
if (default_left->size() != values->size()) { throw std::runtime_error("inconsistent size for default_left and values in ForestLGBM constructor"); }
nodes.clear();
std::vector<MVAUtils::index_t> right = detail::computeRight(*vars);
for (size_t i = 0; i < vars->size(); ++i) {
nodes.emplace_back(vars->at(i), values->at(i), right[i], default_left->at(i));
if (vars->at(i) > m_max_var) { m_max_var = vars->at(i); }
}
newTree(nodes);
} // end loop on TTree, all decision tree loaded
delete vars;
delete values;
delete default_left;
}
TTree* ForestLGBM::WriteTree(TString name) const
{
TTree *tree = new TTree(name.Data(), "creator=lgbm;node_type=lgbm");
std::vector<int> vars;
std::vector<float> values;
std::vector<bool> default_left;
tree->Branch("vars", &vars);
tree->Branch("values", &values);
tree->Branch("default_left", &default_left);
for (size_t itree = 0; itree < GetNTrees(); ++itree) {
vars.clear();
values.clear();
default_left.clear();
for(const auto& node : GetTree(itree)) {
vars.push_back(node.GetVar());
values.push_back(node.GetVal());
default_left.push_back(node.GetDefaultLeft());
}
tree->Fill();
}
return tree;
}
void ForestLGBM::PrintForest() const
{
std::cout << "***BDT LGBM: Printing entire forest***" << std::endl;
Forest::PrintForest();
}
ForestTMVA::ForestTMVA(TTree* tree) : ForestWeighted<NodeTMVA>()
{
m_max_var = 0;
// variables read from the TTree
std::vector<int> *vars = nullptr;
std::vector<float> *values = nullptr;
float offset; // the offset is the weight
std::vector<NodeTMVA> nodes;
tree->SetBranchAddress("vars", &vars);
tree->SetBranchAddress("values", &values);
tree->SetBranchAddress("offset", &offset);
for (int i = 0; i < tree->GetEntries(); ++i)
{
// each entry in the TTree is a decision tree
tree->GetEntry(i);
if (!vars) { throw std::runtime_error("vars pointer is null in ForestTMVA constructor"); }
if (!values) { throw std::runtime_error("values pointers is null in ForestTMVA constructor"); }
if (vars->size() != values->size()) { throw std::runtime_error("inconsistent size for vars and values in ForestTMVA constructor"); }
nodes.clear();
std::vector<MVAUtils::index_t> right = detail::computeRight(*vars);
for (size_t i = 0; i < vars->size(); ++i) {
nodes.emplace_back(vars->at(i), values->at(i), right[i]);
if (vars->at(i) > m_max_var) { m_max_var = vars->at(i); }
}
newTree(nodes, offset);
} // end loop on TTree, all decision tree loaded
delete vars;
delete values;
}
ForestTMVA::ForestTMVA(TMVA::MethodBDT* bdt, bool isRegression, bool useYesNoLeaf)
{
m_max_var = 0;
std::vector<TMVA::DecisionTree*>::const_iterator it;
for(it = bdt->GetForest().begin(); it != bdt->GetForest().end(); ++it) {
uint index = it - bdt->GetForest().begin();
float weight = 0.;
if(bdt->GetBoostWeights().size() > index) {
weight = bdt->GetBoostWeights()[index];
}
newTree((*it)->GetRoot(), weight, isRegression, useYesNoLeaf);
}
}
/**
* Creates the full tree structure from TMVA::DecisionTree node.
**/
void ForestTMVA::newTree(const TMVA::DecisionTreeNode *node, float weight, bool isRegression, bool useYesNoLeaf
)
{
// index is relative to the current node
std::vector<MVAUtils::index_t> right;
{
// not strictly parent if doing a right node
std::stack<const TMVA::DecisionTreeNode *> parent;
std::stack<MVAUtils::index_t> parentIndex;
parentIndex.push(-1);
parent.push(nullptr);
auto currNode = node;
int i = -1;
while (currNode) {
++i;
right.push_back(-1);
if (!currNode->GetLeft()) {
// a leaf
auto currParent = parent.top();
auto currParentIndex = parentIndex.top();
// if right has not been visited, next will be right
if (currParentIndex >= 0) {
right[currParentIndex] = i + 1 - currParentIndex;
currNode = currParent->GetCutType() ? currParent->GetLeft() : currParent->GetRight();
} else {
currNode = nullptr;
}
parent.pop();
parentIndex.pop();
} else {
// not a leaf
parent.push(currNode);
parentIndex.push(i);
currNode = currNode->GetCutType() ? currNode->GetRight() : currNode->GetLeft();
}
}
}
{
std::stack<const TMVA::DecisionTreeNode *> parent; // not strictly parent if doing a right node
parent.push(nullptr);
auto currNode = node;
int i = -1;
std::vector<NodeTMVA> nodes;
while (currNode) {
++i;
if (!currNode->GetLeft()) {
// a leaf
nodes.emplace_back(-1,
isRegression ?
currNode->GetResponse() : useYesNoLeaf ? currNode->GetNodeType() : currNode->GetPurity(),
right[i]);
auto currParent = parent.top();
// if right has not been visited, next will be right
if (currParent) {
currNode = currParent->GetCutType() ? currParent->GetLeft() : currParent->GetRight();
} else {
currNode = nullptr;
}
parent.pop();
} else {
// not a leaf
parent.push(currNode);
if (currNode->GetSelector() > m_max_var) { m_max_var = currNode->GetSelector(); }
nodes.emplace_back(currNode->GetSelector(), currNode->GetCutValue(), right[i]);
currNode = currNode->GetCutType() ? currNode->GetRight() : currNode->GetLeft();
}
}
newTree(nodes, weight);
}
}
TTree* ForestTMVA::WriteTree(TString name) const
{
TTree *tree = new TTree(name.Data(), "creator=TMVA");
std::vector<int> vars;
std::vector<float> values;
float offset;
tree->Branch("offset", &offset);
tree->Branch("vars", &vars);
tree->Branch("values", &values);
for (size_t itree = 0; itree < GetNTrees(); ++itree) {
vars.clear();
values.clear();
for(const auto& node : GetTree(itree)) {
vars.push_back(node.GetVar());
values.push_back(node.GetVal());
}
offset = GetTreeWeight(itree);
tree->Fill();
}
return tree;
}
float ForestTMVA::GetResponse(const std::vector<float>& values) const {
return GetRawResponse(values) + GetOffset();
}
float ForestTMVA::GetResponse(const std::vector<float*>& pointers) const {
return GetRawResponse(pointers) + GetOffset();
}
float ForestTMVA::GetClassification(const std::vector<float>& values) const
{
float result = GetWeightedResponse(values);
return result / GetSumWeights();
}
float ForestTMVA::GetClassification(const std::vector<float*>& pointers) const
{
float result = GetWeightedResponse(pointers);
return result / GetSumWeights();
}
void ForestTMVA::PrintForest() const
{
std::cout << "***BDT TMVA: Printing entire forest***" << std::endl;
Forest::PrintForest();
}
ForestXGBoost::ForestXGBoost(TTree* tree) : ForestXGBoostBase<NodeXGBoost>()
{
m_max_var = 0;
// variables read from the TTree
std::vector<int> *vars = nullptr;
std::vector<float> *values = nullptr;
std::vector<bool> *default_left = nullptr;
std::vector<NodeXGBoost> nodes;
tree->SetBranchAddress("vars", &vars);
tree->SetBranchAddress("values", &values);
tree->SetBranchAddress("default_left", &default_left);
for (int i = 0; i < tree->GetEntries(); ++i)
{
// each entry in the TTree is a decision tree
tree->GetEntry(i);
if (!vars) { throw std::runtime_error("vars pointer is null in ForestXGBoost constructor"); }
if (!values) { throw std::runtime_error("values pointers is null in ForestXGBoost constructor"); }
if (!default_left) { throw std::runtime_error("default_left pointers is null in ForestXGBoost constructor"); }
if (vars->size() != values->size()) { throw std::runtime_error("inconsistent size for vars and values in ForestXGBoost constructor"); }
if (default_left->size() != values->size()) { throw std::runtime_error("inconsistent size for default_left and values in ForestXGBoost constructor"); }
nodes.clear();
std::vector<MVAUtils::index_t> right = detail::computeRight(*vars);
for (size_t i = 0; i < vars->size(); ++i) {
nodes.emplace_back(vars->at(i), values->at(i), right[i], default_left->at(i));
if (vars->at(i) > m_max_var) { m_max_var = vars->at(i); }
}
newTree(nodes);
} // end loop on TTree, all decision tree loaded
delete vars;
delete values;
delete default_left;
}
TTree* ForestXGBoost::WriteTree(TString name) const
{
TTree *tree = new TTree(name.Data(), "creator=xgboost");
std::vector<int> vars;
std::vector<float> values;
std::vector<bool> default_left;
tree->Branch("vars", &vars);
tree->Branch("values", &values);
tree->Branch("default_left", &default_left);
for (size_t itree = 0; itree < GetNTrees(); ++itree) {
vars.clear();
values.clear();
default_left.clear();
for(const auto& node : GetTree(itree)) {
vars.push_back(node.GetVar());
values.push_back(node.GetVal());
default_left.push_back(node.GetDefaultLeft());
}
tree->Fill();
}
return tree;
}
void ForestXGBoost::PrintForest() const
{
std::cout << "***BDT XGBoost: Printing entire forest***" << std::endl;
Forest::PrintForest();
}
#endif
Updated on 2026-11-03 at 14:50:48 +0000