Browse Source

rearranges the filval project to make root a sub-directory

Caleb Fangmeier 7 years ago
parent
commit
8a6c0eaf54
7 changed files with 724 additions and 16 deletions
  1. 6 16
      README.md
  2. 14 0
      root/LinkDef.hpp
  3. 63 0
      root/root_api.hpp
  4. 444 0
      root/root_container.hpp
  5. 27 0
      root/root_filter.hpp
  6. 7 0
      root/root_filval.hpp
  7. 163 0
      root/root_value.hpp

+ 6 - 16
README.md

@@ -1,16 +1,6 @@
-A FILter-VALue System
-=====================
-This is a header-only, generic, data analysis system that allows for creating performant generation of *Plots*. *Plots* contain *Values* and can make use of *Filters*.
-*Filters* can also depend of *Values*, and *Values* can depend on other *Values*. A *Dataset* is a generic object that contains a series of observations. The individual observations
-consist of a series of *Observed Values*. One can also define *Derived Values* which are calculated from *Observed Values* or other *Derived Values*. Care is taken automatically 
-so *Derived Values* are calculated at most once per observation.
-
-
-
-```C++
-MyDataSet myDataSet("somefile.root", "tree"); // MyDataSet subclasses DataSet
-TTreeValue<int> countmyDataSet;
-myDataSet.addValue
-
-Hist1D myplot(count, myDataSet, ); // Hist1D subclasses Plot
-```
+ROOT compatability layer for FilVal
+================================================
+See [FilVal](../filval/README.md) for details on FilVal. This layer provides
+container classes wrapping ROOT histograms and Graph objects. It also provides
+the ability to write these containers, as well as a variety of STL containers
+to ROOT files.

+ 14 - 0
root/LinkDef.hpp

@@ -0,0 +1,14 @@
+#ifndef linkdef_hpp
+#define linkdef_hpp
+#include <vector>
+#include <string>
+#include <map>
+
+#ifdef __CLING__
+#pragma link C++ class std::vector<std::vector<int>>+;
+#pragma link C++ class std::vector<std::vector<unsigned int>>+;
+#pragma link C++ class std::vector<std::vector<float>>+;
+#pragma link C++ class std::map<std::string,std::string>+;
+#endif
+
+#endif // linkdev_hpp

+ 63 - 0
root/root_api.hpp

@@ -0,0 +1,63 @@
+#ifndef ROOT_API_HPP
+#define ROOT_API_HPP
+#include <string>
+#include <vector>
+#include <tuple>
+#include "filval/api.hpp"
+#include "filval/root/root_value.hpp"
+namespace fv::root{
+
+    decltype(auto)
+    lorentz_vectors(Value<std::vector<float>>* pt, Value<std::vector<float>>* eta,
+                    Value<std::vector<float>>* phi, Value<std::vector<float>>* mass,
+                    const std::string& alias=""){
+        typedef std::vector<TLorentzVector> type;
+        const std::string& name = root::LorentzVectors::fmt_name(pt, eta, phi, mass);
+        if (check_exists<type>(name))
+            return lookup<type>(name);
+        else
+            return (Value<type>*)new root::LorentzVectors(pt, eta, phi, mass, alias);
+    }
+
+    decltype(auto)
+    lorentz_vectors(const std::string& pt_name, const std::string& eta_name,
+                    const std::string& phi_name, const std::string& mass_name,
+                    const std::string& alias=""){
+        return lorentz_vectors(lookup<std::vector<float>>(pt_name), lookup<std::vector<float>>(eta_name),
+                               lookup<std::vector<float>>(phi_name), lookup<std::vector<float>>(mass_name),
+                               alias);
+    }
+
+    decltype(auto)
+    energies(Value<std::vector<TLorentzVector>>* vectors, const std::string& alias="") {
+        typedef std::vector<float> type;
+        const std::string& name = root::Energies::fmt_name(vectors);
+        if (check_exists<type>(name))
+            return lookup<type>(name);
+        else
+            return (Value<type>*)new root::Energies(vectors, alias);
+    }
+
+    decltype(auto)
+    energies(const std::string& vectors_name, const std::string& alias="") {
+        return energies(lookup<std::vector<TLorentzVector>>(vectors_name), alias);
+    }
+
+    template<typename... DataTypes>
+    decltype(auto)
+    mva_data(Value<bool>* is_training, Value<bool>* is_signal, Value<double>* weight,
+             const std::pair<std::string, Value<DataTypes>*>&&... data_vals) {
+        typedef typename root::MVAData<DataTypes...> mva_type;
+        typedef typename mva_type::type type;
+        std::string alias="";
+        const std::string& name = mva_type::fmt_name(is_training, is_signal, weight,
+                                                     std::forward<const std::pair<std::string,Value<DataTypes>*>>(data_vals)...);
+        if (check_exists<type>(name))
+            return lookup<type>(name);
+        else
+            return (Value<type>*)new mva_type(is_training, is_signal, weight,
+                                                     std::forward<const std::pair<std::string,Value<DataTypes>*>>(data_vals)...);
+    }
+
+}
+#endif // ROOT_API_HPP

+ 444 - 0
root/root_container.hpp

@@ -0,0 +1,444 @@
+#ifndef root_container_hpp
+#define root_container_hpp
+#include <iostream>
+#include <utility>
+#include <map>
+
+#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<int,int> 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 {
+
+template <typename V>
+class _ContainerTH1 : public Container<TH1,V>{
+    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(),
+                                           this->nbins, this->low, this->high);
+                this->container->SetXTitle(label_x.c_str());
+                this->container->SetYTitle(label_y.c_str());
+            }
+            _do_fill();
+        }
+
+    protected:
+        std::string title;
+        std::string label_x;
+        std::string label_y;
+        int nbins;
+        double low;
+        double high;
+
+        virtual void _do_fill() = 0;
+
+    public:
+        explicit _ContainerTH1(const std::string &name, const std::string& title, Value<V>* value,
+                               int nbins, double low, double high,
+                               const std::string& label_x = "",
+                               const std::string& label_y = "")
+          :Container<TH1,V>(name, value),
+           title(title), nbins(nbins), low(low), high(high),
+           label_x(label_x), label_y(label_y) { }
+
+        void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) {
+            util::save_as(this->get_container(), fname, option);
+        }
+};
+
+template <typename V>
+class ContainerTH1 : public _ContainerTH1<V>{
+    using _ContainerTH1<V>::_ContainerTH1;
+    void _do_fill(){
+        this->container->Fill(this->value->get_value());
+    }
+    public:
+        GenContainer* clone_as(const std::string& new_name){
+            return new ContainerTH1<V>(new_name, this->title, this->value, this->nbins, this->low, this->high, this->label_x, this->label_y);
+        }
+};
+
+template <typename V>
+class ContainerTH1Many : public _ContainerTH1<std::vector<V>>{
+    using _ContainerTH1<std::vector<V>>::_ContainerTH1;
+    void _do_fill(){
+        for(V x : this->value->get_value())
+            this->container->Fill(x);
+    }
+    public:
+        GenContainer* clone_as(const std::string& new_name){
+            return new ContainerTH1Many<V>(new_name, this->title, this->value, this->nbins, this->low, this->high, this->label_x, this->label_y);
+        }
+};
+
+
+
+template <typename V>
+class _ContainerTH2 : public Container<TH2,std::pair<V,V>>{
+    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(),
+                                           this->nbins_x, this->low_x, this->high_x,
+                                           this->nbins_y, this->low_y, this->high_y);
+                this->container->SetXTitle(label_x.c_str());
+                this->container->SetYTitle(label_y.c_str());
+            }
+            _do_fill(this->value->get_value());
+        }
+
+    protected:
+        std::string title;
+        std::string label_x;
+        std::string label_y;
+        int nbins_x;
+        int nbins_y;
+        double low_x;
+        double low_y;
+        double high_x;
+        double high_y;
+
+        virtual void _do_fill(std::pair<V,V>& val) = 0;
+
+    public:
+        explicit _ContainerTH2(const std::string& name, const std::string& title,
+                               Value<std::pair<V, V>>* value,
+                               int nbins_x, double low_x, double high_x,
+                               int nbins_y, double low_y, double high_y,
+                               const std::string& label_x = "",
+                               const std::string& label_y = "")
+          :Container<TH2,std::pair<V,V>>(name, value),
+           title(title),
+           nbins_x(nbins_x), low_x(low_x), high_x(high_x),
+           nbins_y(nbins_y), low_y(low_y), high_y(high_y),
+           label_x(label_x), label_y(label_y) { }
+
+        void save_as(const std::string& fname, const SaveOption& option = SaveOption::PNG) {
+            util::save_as(this->get_container(), fname, option);
+        }
+};
+
+template <typename V>
+class ContainerTH2 : public _ContainerTH2<V>{
+    using _ContainerTH2<V>::_ContainerTH2;
+    void _do_fill(std::pair<V,V>& val){
+        this->container->Fill(val.first,val.second);
+    }
+    public:
+        GenContainer* clone_as(const std::string& new_name){
+            return new ContainerTH2<V>(new_name, this->title, this->value, this->nbins_x, this->low_x, this->high_x,
+                                       this->nbins_y, this->low_y, this->high_y, this->label_x, this->label_y);
+        }
+};
+
+template <typename V>
+class ContainerTH2Many : public _ContainerTH2<std::vector<V>>{
+    using _ContainerTH2<std::vector<V>>::_ContainerTH2;
+    void _do_fill(std::pair<std::vector<V>,std::vector<V>>& val){
+        int min_size = std::min(val.first.size(), val.second.size());
+        for(int i=0; i<min_size; i++)
+            this->container->Fill(val.first[i],val.second[i]);
+    }
+    public:
+        GenContainer* clone_as(const std::string& new_name){
+            return new ContainerTH2Many<V>(new_name, this->title, this->value, this->nbins_x, this->low_x, this->high_x,
+                                           this->nbins_y, this->low_y, this->high_y, this->label_x, this->label_y);
+        }
+};
+
+template <typename V>
+class ContainerTGraph : public Container<TGraph,std::pair<V,V>>{
+    private:
+        std::vector<V> x_data;
+        std::vector<V> 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<std::pair<V, V>>* value)
+          :Container<TGraph,std::pair<V,V>>(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<V>(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 <typename V>
+class Vector : public Container<std::vector<V>,V>{
+    private:
+
+        void _fill(){
+            this->container->push_back(this->value->get_value());
+        }
+    public:
+        Vector(const std::string& name, Value<V>* value)
+          :Container<std::vector<V>,V>(name, value){
+            this->container = new std::vector<V>;
+        }
+
+        GenContainer* clone_as(const std::string& new_name){
+            return new Vector<V>(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 <typename V, typename D>
+class _Counter : public Container<std::map<D,int>,V>{
+    public:
+        explicit _Counter(const std::string& name, Value<V>* value)
+          :Container<std::map<D,int>,V>(name, value) {
+            this->container = new std::map<D,int>;
+        }
+
+        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 <typename V>
+class Counter : public _Counter<V,V>{
+    using _Counter<V,V>::_Counter;
+        void _fill(){
+            (*this->container)[this->value->get_value()]++;
+        }
+    public:
+        GenContainer* clone_as(const std::string& new_name){
+            return new Counter<V>(new_name, this->value);
+        }
+};
+
+/**
+ * Same as Counter but accepts multiple values per fill.
+ */
+template <typename V>
+class CounterMany : public _Counter<std::vector<V>,V>{
+    using _Counter<std::vector<V>,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<V>(new_name, this->value);
+        }
+};
+
+class PassCount : public Container<int,bool>{
+    private:
+
+        void _fill(){
+            if(this->value->get_value()){
+                (*this->container)++;
+            }
+        }
+    public:
+        PassCount(const std::string& name, Value<bool>* value)
+          :Container<int,bool>(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<int> v({*this->get_container()});
+            util::save_as_stl(&v, "std::vector<int>", this->get_name(), option);
+        }
+};
+
+
+template <typename... ArgTypes>
+class MVA : public Container<TMVA::DataLoader,typename MVAData<ArgTypes...>::type>{
+    private:
+        std::vector<std::pair<std::string,std::string>> methods;
+
+        std::string cut;
+        std::string opt;
+
+        void _fill(){
+            std::tuple<ArgTypes...> t;
+            typename MVAData<ArgTypes...>::type& event = this->value->get_value();
+            bool is_training, is_signal;
+            double weight;
+            std::tie(is_training, is_signal, weight, t) = event;
+            std::vector<double> v = t2v<double>(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<ArgTypes...>* value, const std::string& cut = "", const std::string& opt = "")
+          :Container<TMVA::DataLoader,typename MVAData<ArgTypes...>::type>(name, value),
+           cut(cut), opt(opt) {
+            this->container = new TMVA::DataLoader(name);
+            for (std::pair<std::string,char>& 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<ArgTypes...>(new_name, (MVAData<ArgTypes...>*)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

+ 27 - 0
root/root_filter.hpp

@@ -0,0 +1,27 @@
+#ifndef root_filter_hpp
+#define root_filter_hpp
+#include "value.hpp"
+#include "TLorentzVector.h"
+
+namespace fv::root{
+
+class MassFilter : public Filter {
+    private:
+        Value<TLorentzVector> *lorentz_vector;
+        Value<double> *mass_cut_low;
+        Value<double> *mass_cut_high;
+    void update_value(){
+        double m = lorentz_vector->get_value().M();
+        value = (m > mass_cut_low) && (m < mass_cut_high);
+    }
+    public:
+        FilterAnd(Value<TLorentzVector> *lorentz_vector,
+                  Value<double> *mass_cut_low,
+                  Value<double> *mass_cut_high)
+          :lorentz_vector(lorentz_vector),
+           mass_cut_low(mass_cut_low),
+           mass_cut_high(mass_cut_high){ }
+};
+
+}
+#endif // root_filter_hpp

+ 7 - 0
root/root_filval.hpp

@@ -0,0 +1,7 @@
+#ifndef root_filval_hpp
+#define root_filval_hpp
+#include "filval/root/root_value.hpp"
+#include "filval/root/root_container.hpp"
+#include "filval/root/root_api.hpp"
+/* #include "dataset.hpp" */
+#endif // root_filval_hpp

+ 163 - 0
root/root_value.hpp

@@ -0,0 +1,163 @@
+#ifndef root_value_hpp
+#define root_value_hpp
+#include "filval/value.hpp"
+#include "TLorentzVector.h"
+
+namespace fv::root{
+
+class LorentzVectors : public DerivedValue<std::vector<TLorentzVector>>{
+    protected:
+        Value<std::vector<float>> *pt_val;
+        Value<std::vector<float>> *eta_val;
+        Value<std::vector<float>> *phi_val;
+        Value<std::vector<float>> *mass_val;
+
+        void update_value(){
+            auto pt = pt_val->get_value();
+            auto eta = eta_val->get_value();
+            auto phi = phi_val->get_value();
+            auto mass = mass_val->get_value();
+            std::vector<int> sizes = {pt.size(), eta.size(), phi.size(), mass.size()};
+            int size = *std::min_element(sizes.begin(), sizes.end());
+            this->value.clear();
+            TLorentzVector lv;
+            for (int i =0; i<size; i++){
+                lv.SetPtEtaPhiM(pt[i], eta[i], phi[i], mass[i]);
+                this->value.push_back(lv);
+            }
+        }
+
+    public:
+        static std::string fmt_name(Value<std::vector<float>>* pt, Value<std::vector<float>>* eta,
+                                    Value<std::vector<float>>* phi, Value<std::vector<float>>* mass){
+            return "lorentz_vectors("+pt->get_name()+"," +eta->get_name()+","+
+                                      phi->get_name()+"," +mass->get_name()+")";
+        }
+
+        LorentzVectors(Value<std::vector<float>>* pt,
+                      Value<std::vector<float>>* eta,
+                      Value<std::vector<float>>* phi,
+                      Value<std::vector<float>>* mass,
+                      const std::string& alias)
+          :DerivedValue<std::vector<TLorentzVector>>(fmt_name(pt,eta,phi,mass), alias),
+           pt_val(pt), eta_val(eta), phi_val(phi), mass_val(mass) { }
+};
+
+class Energies : public DerivedValue<std::vector<float>>{
+    private:
+        Value<std::vector<TLorentzVector>> *vectors;
+        void update_value(){
+            std::vector<TLorentzVector>& vecs = vectors->get_value();
+            this->value.clear();
+            for (auto v : vecs){
+                this->value.push_back(v.Energy());
+            }
+        }
+
+    public:
+        static std::string fmt_name(Value<std::vector<TLorentzVector>> *vectors){
+            return "energies("+vectors->get_name()+")";
+        }
+
+        Energies(Value<std::vector<TLorentzVector>> *vectors,
+                 const std::string& alias)
+          :DerivedValue<std::vector<float>>(fmt_name(vectors), alias),
+           vectors(vectors) { }
+};
+
+
+namespace detail {
+    template<typename Tuple, std::size_t... Is>
+    decltype(auto) tuple_get_values_impl(const Tuple& t, std::index_sequence<Is...>){
+        return std::make_tuple(std::get<1>(std::get<Is>(t))->get_value()...);
+    }
+
+    template<typename Tuple, std::size_t... Is>
+    decltype(auto) tuple_get_labels_impl(const Tuple& t, std::index_sequence<Is...>){
+        return std::make_tuple(std::get<0>(std::get<Is>(t))...);
+    }
+}
+/**
+ * Converts a tuple of pairs of label/Value objects to a tuple of the contained values
+ */
+template<typename... ArgTypes>
+std::tuple<ArgTypes...> tuple_get_values(const std::tuple<std::pair<std::string,Value<ArgTypes>*>...>& t){
+    return detail::tuple_get_values_impl<std::tuple<std::pair<std::string,Value<ArgTypes>*>...>>(t, std::index_sequence_for<ArgTypes...>{});
+}
+
+/**
+ * Converts a tuple of pairs of label/Value objects to a tuple of just the
+ * labels
+ */
+template<typename... ArgTypes>
+decltype(auto) tuple_get_labels(const std::tuple<std::pair<std::string,Value<ArgTypes>*>...>& t){
+    return detail::tuple_get_labels_impl<std::tuple<std::pair<std::string,Value<ArgTypes>*>...>>(t, std::index_sequence_for<ArgTypes...>{});
+}
+
+
+//-----------------------
+
+template <typename T>
+char type_lookup(){
+    return 0;
+}
+template <>
+char type_lookup<int>(){
+    return 'I';
+}
+template <>
+char type_lookup<float>(){
+    return 'F';
+}
+template <>
+char type_lookup<double>(){
+    return 'F';
+}
+
+
+template<typename... DataTypes>
+class MVAData : public DerivedValue<std::tuple<bool,bool,double,std::tuple<DataTypes...>>>{
+    private:
+        std::tuple<std::pair<std::string,Value<DataTypes>*>...> data;
+        Value<bool>* is_training;
+        Value<bool>* is_signal;
+        Value<double>* weight;
+
+        void update_value(){
+            this->value = std::make_tuple(is_training->get_value(), is_signal->get_value(), weight->get_value(),
+                                          tuple_get_values(data));
+        }
+
+        template <typename... Types>
+        static std::vector<std::pair<std::string, char>> get_label_types_impl(std::pair<std::string,Value<Types>*>... pairs){
+            return std::vector<std::pair<std::string, char>>({ std::make_pair(pairs.first,type_lookup<Types>())...});
+        }
+
+
+    public:
+        typedef std::tuple<bool, bool, double, std::tuple<DataTypes...>> type;
+
+
+        std::vector<std::pair<std::string, char>> get_label_types(){
+            return call(get_label_types_impl<DataTypes...>, data);
+        }
+
+        static std::string fmt_name(Value<bool>* is_training, Value<bool>* is_signal, Value<double>* weight,
+                                    const std::pair<std::string, Value<DataTypes>*>&&... data_vals){
+            //TODO: add data names
+            return "mva_data("+is_training->get_name()+","
+                              +is_signal->get_name()+","
+                              +weight->get_name()+")";
+        }
+
+        MVAData(Value<bool>* is_training, Value<bool>* is_signal,
+                Value<double>* weight, const std::pair<std::string, Value<DataTypes>*>&&... data_vals)
+          :DerivedValue<type>(fmt_name(is_training, is_signal, weight,
+                                       std::forward<const std::pair<std::string,Value<DataTypes>*>>(data_vals)...),""),
+           data(std::make_tuple(data_vals...)),
+           is_training(is_training),
+           is_signal(is_signal),
+           weight(weight) { }
+};
+}
+#endif // root_value_hpp