#ifndef root_container_hpp #define root_container_hpp #include #include #include #include "TROOT.h" #include "TFile.h" #include "TCanvas.h" #include "TGraph.h" #include "TH1.h" #include "TH2.h" #include "TMVA/Factory.h" #include "TMVA/DataLoader.h" #include "TMVA/DataSetInfo.h" #include "filval/container.hpp" namespace fv::root::util{ /** * Save a TObject. The TObject will typically be a Histogram or Graph object, * but can really be any TObject. The SaveOption can be used to specify how to * save the file. */ void save_as(TObject* container, const std::string& fname, const SaveOption& option = SaveOption::PNG) { auto save_img = [](TObject* container, const std::string& fname){ TCanvas* c1 = new TCanvas("c1"); container->Draw(); c1->Draw(); c1->SaveAs(fname.c_str()); delete c1; }; auto save_bin = [](TObject* container){ INFO("Saving object: " << container->GetName() << " into file " << gDirectory->GetName()); container->Write(container->GetName(), TObject::kOverwrite); }; switch(option){ case PNG: save_img(container, fname+".png"); break; case PDF: save_img(container, fname+".pdf"); break; case ROOT: save_bin(container); break; default: break; } } /** * Saves an STL container into a ROOT file. ROOT knows how to serialize STL * containers, but it needs the *name* of the type of the container, eg. * std::map to be able to do this. In order to generate this name at * run-time, the fv::util::get_type_name function uses RTTI to get type info * and use it to look up the proper name. * * For nexted containers, it is necessary to generate the CLING dictionaries * for each type at compile time to enable serialization. To do this, add the * type definition into the LinkDef.hpp header file. */ void save_as_stl(void* container, const std::string& type_name, const std::string& obj_name, const SaveOption& option = SaveOption::PNG) { switch(option){ case PNG: INFO("Cannot save STL container " << type_name <<" as png"); break; case PDF: INFO("Cannot save STL container " << type_name <<" as pdf"); break; case ROOT: /* DEBUG("Writing object \"" << obj_name << "\" of type \"" << type_name << "\"\n"); */ gDirectory->WriteObjectAny(container, type_name.c_str(), obj_name.c_str()); break; default: break; } } } namespace fv::root { struct TH1Params{ std::string label_x; int nbins; double low; double high; std::string label_y; static TH1Params lookup(const std::string&& param_key){ auto hist_params = fv::util::the_config->get("hist-params"); if(!hist_params[param_key]){ CRITICAL("Key \"" << param_key << "\" does not exist under hist-params in supplied config file. Add it!", true); } else{ auto params = hist_params[param_key]; return TH1Params({params["label_x"].as(), params["nbins"].as(), params["low"].as(), params["high"].as(), params["label_y"].as() }); } } }; template class _ContainerTH1 : public Container{ private: void _fill(){ if (this->container == nullptr){ if (this->value == nullptr){ CRITICAL("Container: \"" << this->get_name() << "\" has a null Value object. " << "Probably built with imcompatible type",-1); } this->container = new TH1D(this->get_name().c_str(), this->title.c_str(), params.nbins, params.low, params.high); this->container->SetXTitle(params.label_x.c_str()); this->container->SetYTitle(params.label_y.c_str()); } _do_fill(); } protected: std::string title; TH1Params params; virtual void _do_fill() = 0; public: explicit _ContainerTH1(const std::string& name, Value* value, const std::string& title, const TH1Params& params) :Container(name, value), title(title), params(params) { } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { util::save_as(this->get_container(), fname, option); } }; template class ContainerTH1 : public _ContainerTH1{ using _ContainerTH1::_ContainerTH1; void _do_fill(){ this->container->Fill(this->value->get_value()); } public: GenContainer* clone_as(const std::string& new_name){ return new ContainerTH1(new_name, this->value, this->title, this->params); } }; template class ContainerTH1Many : public _ContainerTH1>{ using _ContainerTH1>::_ContainerTH1; void _do_fill(){ for(const V &x : this->value->get_value()) this->container->Fill(x); } public: GenContainer* clone_as(const std::string& new_name){ return new ContainerTH1Many(new_name, this->value, this->title, this->params); } }; struct TH2Params{ std::string label_x; int nbins_x; double low_x; double high_x; std::string label_y; int nbins_y; double low_y; double high_y; static TH2Params lookup(const std::string&& param_key){ auto hist_params = fv::util::the_config->get("hist-params"); if(!hist_params[param_key]){ CRITICAL("Key \"" << param_key << "\" does not exist under hist-params in supplied config file. Add it!", true); } else{ auto params = hist_params[param_key]; return TH2Params({params["label_x"].as(), params["nbins_x"].as(), params["low_x"].as(), params["high_x"].as(), params["label_y"].as(), params["nbins_y"].as(), params["low_y"].as(), params["high_y"].as() }); } } }; template class _ContainerTH2 : public Container>{ private: void _fill(){ if (this->container == nullptr){ if (this->value == nullptr){ CRITICAL("Container: \"" << this->get_name() << "\" has a null Value object. " << "Probably built with imcompatible type",-1); } this->container = new TH2D(this->get_name().c_str(), this->title.c_str(), params.nbins_x, params.low_x, params.high_x, params.nbins_y, params.low_y, params.high_y); this->container->SetXTitle(params.label_x.c_str()); this->container->SetYTitle(params.label_y.c_str()); } _do_fill(this->value->get_value()); } protected: std::string title; TH2Params params; virtual void _do_fill(const std::pair& val) = 0; public: explicit _ContainerTH2(const std::string& name, Value>* value, const std::string& title, TH2Params params) :Container>(name, value), title(title), params(params) { } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { util::save_as(this->get_container(), fname, option); } }; template class ContainerTH2 : public _ContainerTH2{ using _ContainerTH2::_ContainerTH2; void _do_fill(const std::pair& val){ this->container->Fill(val.first,val.second); } public: GenContainer* clone_as(const std::string& new_name){ return new ContainerTH2(new_name, this->value, this->title, this->params); } }; template class ContainerTH2Many : public _ContainerTH2>{ using _ContainerTH2>::_ContainerTH2; void _do_fill(const std::pair,std::vector>& val){ int min_size = std::min(val.first.size(), val.second.size()); for(int i=0; icontainer->Fill(val.first[i],val.second[i]); } public: GenContainer* clone_as(const std::string& new_name){ return new ContainerTH2Many(new_name, this->value, this->title, this->params); } }; template class ContainerTGraph : public Container>{ private: std::vector x_data; std::vector y_data; std::string title; bool data_modified; void _fill(){ auto val = this->value->get_value(); x_data.push_back(val.first); y_data.push_back(val.second); data_modified = true; } public: ContainerTGraph(const std::string& name, const std::string& title, Value>* value) :Container>(name, value), data_modified(false){ this->container = new TGraph(); } TGraph* get_container(){ if (data_modified){ delete this->container; this->container = new TGraph(x_data.size(), x_data.data(), y_data.data()); this->container->SetName(this->get_name().c_str()); this->container->SetTitle(title.c_str()); data_modified = false; } return this->container; } GenContainer* clone_as(const std::string& new_name){ return new ContainerTGraph(new_name, this->title, this->value); } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { util::save_as(get_container(), fname, option); } }; template class Vector : public Container,V>{ private: void _fill(){ this->container->push_back(this->value->get_value()); } public: Vector(const std::string& name, Value* value) :Container,V>(name, value){ this->container = new std::vector; } GenContainer* clone_as(const std::string& new_name){ return new Vector(new_name, this->value); } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { std::string type_name = "std::vector<"+fv::util::get_type_name(typeid(V))+">"; util::save_as_stl(this->get_container(), type_name, this->get_name(), option); } }; template class VectorMany : public Container,std::vector>{ private: void _fill(){ for(const V& val: this->value->get_value()) this->container->push_back(val); } public: VectorMany(const std::string& name, Value>* value) :Container,std::vector>(name, value){ this->container = new std::vector; } GenContainer* clone_as(const std::string& new_name){ return new VectorMany(new_name, this->value); } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { std::string type_name = "std::vector<"+fv::util::get_type_name(typeid(V))+">"; util::save_as_stl(this->get_container(), type_name, this->get_name(), option); } }; template class _Counter : public Container,V>{ public: explicit _Counter(const std::string& name, Value* value) :Container,V>(name, value) { this->container = new std::map; } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { std::string type_name = "std::map<"+fv::util::get_type_name(typeid(D))+",int>"; util::save_as_stl(this->get_container(), type_name, this->get_name(), option); } }; /** * A Counter that keeps a mapping of the number of occurances of each input * value. */ template class Counter : public _Counter{ using _Counter::_Counter; void _fill(){ (*this->container)[this->value->get_value()]++; } public: GenContainer* clone_as(const std::string& new_name){ return new Counter(new_name, this->value); } }; /** * Same as Counter but accepts multiple values per fill. */ template class CounterMany : public _Counter,V>{ using _Counter,V>::_Counter; void _fill(){ for(V& val : this->value->get_value()) (*this->container)[val]++; } public: GenContainer* clone_as(const std::string& new_name){ return new CounterMany(new_name, this->value); } }; class PassCount : public Container{ private: void _fill() { if(this->value->get_value()) (*this->container)++; } public: PassCount(const std::string& name, Value* value) :Container(name, value){ this->container = new int(0); } GenContainer* clone_as(const std::string& new_name){ return new PassCount(new_name, this->value); } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { //ROOT(hilariously) cannot serialize basic data types, we wrap this //in a vector. std::vector v({*this->get_container()}); util::save_as_stl(&v, "std::vector", this->get_name(), option); } }; template class EfficiencyContainer : public Container>{ private: std::function* selector; // Selects whether object is up for consideration std::function* predicate; // Says whether the object passes the efficiency criteria std::function* get_val; // Returns a floating point value from the object that is actually // used in the histogram bool write_num_den; TH1F num; TH1F den; TH1F eff; TH1Params params; void _fill(){ for (auto& obj : this->value->get_value()) { if (selector == nullptr or (*selector)(obj)) { float val = (*get_val)(obj); den.Fill(val); if ((*predicate)(obj)) { num.Fill(val); } } } } public: EfficiencyContainer(const std::string& name, Value>* value, TH1Params params, std::function* selector, std::function* predicate, std::function* get_val, bool write_num_den=false) :Container>(name, value), num{(name+"_num").c_str(), (name+"_num").c_str(), params.nbins, params.low, params.high}, den{(name+"_den").c_str(), (name+"_den").c_str(), params.nbins, params.low, params.high}, eff{name.c_str(), name.c_str(), params.nbins, params.low, params.high}, selector(selector), predicate(predicate), get_val(get_val), params(params), write_num_den(write_num_den) { num.SetXTitle(params.label_x.c_str()); num.SetYTitle(params.label_y.c_str()); den.SetXTitle(params.label_x.c_str()); den.SetYTitle(params.label_y.c_str()); eff.SetXTitle(params.label_x.c_str()); eff.SetYTitle(params.label_y.c_str()); this->container = &eff; } TH1F* get_container() { eff.Sumw2(); eff.Divide(&num, &den, 1, 1, "B"); return this->container; } GenContainer* clone_as(const std::string& new_name){ return new EfficiencyContainer(new_name, this->value, this->params, selector, predicate, get_val); } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { util::save_as(this->get_container(), fname, option); if (write_num_den) { util::save_as(&num, fname, option); util::save_as(&den, fname, option); } } }; template class MVA : public Container::type>{ private: std::vector> methods; std::string cut; std::string opt; void _fill(){ std::tuple t; typename MVAData::type& event = this->value->get_value(); bool is_training, is_signal; double weight; std::tie(is_training, is_signal, weight, t) = event; std::vector v = t2v(t); if (is_signal){ if (is_training){ this->container->AddSignalTrainingEvent(v, weight); } else { this->container->AddSignalTestEvent(v, weight); } } else { if (is_training){ this->container->AddBackgroundTrainingEvent(v, weight); } else { this->container->AddBackgroundTestEvent(v, weight); } } } public: MVA(const std::string& name, MVAData* value, const std::string& cut = "", const std::string& opt = "") :Container::type>(name, value), cut(cut), opt(opt) { this->container = new TMVA::DataLoader(name); for (std::pair& p : value->get_label_types()){ this->container->AddVariable(p.first, p.second); } } void add_method(const std::string& method_name, const std::string& method_params) { methods.push_back(std::make_pair(method_name, method_params)); } GenContainer* clone_as(const std::string& new_name){ auto mva = new MVA(new_name, (MVAData*)this->value, this->cut, this->opt); mva->methods = methods; return mva; } void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) { TFile* outputFile = gDirectory->GetFile(); this->container->PrepareTrainingAndTestTree(cut.c_str(), opt.c_str()); TMVA::Factory *factory = new TMVA::Factory("TMVAClassification", outputFile, "!V:!Silent:Color:DrawProgressBar:Transformations=I;D;P;G,D:AnalysisType=Classification"); TMVA::Types& types = TMVA::Types::Instance(); for(auto& p : methods){ std::string method_name, method_params; std::tie(method_name, method_params) = p; TMVA::Types::EMVA method_type = types.GetMethodType(method_name); factory->BookMethod(this->container, method_type, method_name, method_params); } // Train MVAs using the set of training events factory->TrainAllMethods(); // Evaluate all MVAs using the set of test events factory->TestAllMethods(); // Evaluate and compare performance of all configured MVAs factory->EvaluateAllMethods(); delete factory; } }; } #endif // root_container_hpp