ソースを参照

Adds static name lookup for values.

  * Also begins adding in doxygen compatible doc strings
Caleb Fangmeier 8 年 前
コミット
12b4ce9185
共有4 個のファイルを変更した353 個の追加79 個の削除を含む
  1. 2 2
      container.hpp
  2. 18 17
      dataset.hpp
  3. 15 10
      filter.hpp
  4. 318 50
      value.hpp

+ 2 - 2
container.hpp

@@ -9,14 +9,14 @@ class GenContainer{
     private:
         std::string name;
         std::string desc;
-        std::vector<GenFilter*> filters;
+        std::vector<Filter*> filters;
     protected:
         virtual void _fill() = 0;
     public:
         GenContainer(const std::string name)
           :name(name){ }
         void add_filter(GenValue* filter){
-            filters.push_back(dynamic_cast<GenFilter*>(filter));
+            filters.push_back(dynamic_cast<Filter*>(filter));
         }
         void fill(){
             for (auto filter : filters){

+ 18 - 17
dataset.hpp

@@ -1,36 +1,37 @@
 #ifndef dataset_hpp
 #define dataset_hpp
+#include <iostream>
 #include "value.hpp"
 #include "container.hpp"
 
 namespace filval{
 class DataSet{
+    private:
+        void summary(){
+            GenValue::summary();
+        }
     protected:
-        ValueSet values;
         ContainerSet containers;
         virtual bool load_next() = 0;
+        virtual int get_events() = 0;
+        virtual int get_current_event() = 0;
     public:
         void process(){
+            int events, current_event;
+            summary();
+            events = get_events();
+            std::cout << std::endl;
             while( load_next() ){
+                current_event = get_current_event();
+                std::cout << "\rprocessing event: " << current_event+1 << "/" << events << std::flush;
                 GenValue::reset();
-                for(auto con : containers)
+                for(auto con : containers){
+                    /* std::cout << std::endl << "Filling container: " << con.first; */
                     con.second->fill();
+                }
+                /* std::cout << std::endl; */
             }
-        }
-
-        /* template <typename T> */
-        /* virtual T* get_field(const std::string& field_name) = 0; */
-
-        void add_value(GenValue *value, const std::string& value_name ){
-            /* Adds a value to the dataset's list of known value objects. Note
-             * that all new values are automatically kept track of by
-             * GenValue::values so this is only needed if one wants to recall
-             * the value by name.
-             */
-            values[value_name] = value;
-        }
-        GenValue* get_value(std::string value_name){
-            return values.at(value_name);
+            std::cout << " Finished!" << std::endl;
         }
 
         void add_container(GenContainer *container){

+ 15 - 10
filter.hpp

@@ -9,28 +9,33 @@
  */
 namespace filval {
 
-class GenFilter : public DerivedValue<bool>{};
-
-class Filter : public GenFilter{
+class Filter : public DerivedValue<bool>{
     private:
         std::function<bool()> filter_function;
         void update_value(){
             value = filter_function();
         }
     public:
-        Filter(std::function<bool()> filter_function)
-          :filter_function(filter_function){ }
+        Filter(const std::string& name, std::function<bool()> filter_function)
+          :DerivedValue<bool>(name),
+           filter_function(filter_function){ }
 
         Filter* operator*(Filter *f){
-            return new Filter([this, f](){return this->get_value() && f->get_value();});
+            auto new_name = this->get_name() + "&&" + f->get_name();
+            return new Filter(new_name, [this, f](){return this->get_value() && f->get_value();});
         }
 
         Filter* operator+(Filter *f){
-            return new Filter([this, f](){return this->get_value() || f->get_value();});
+            auto new_name = this->get_name() + "||" + f->get_name();
+            return new Filter(new_name, [this, f](){return this->get_value() || f->get_value();});
         }
 
         Filter* operator!(){
-            return new Filter([this](){return !this->get_value();});
+            std::cout << std::string("!") << std::endl;
+            std::cout << this << this->get_name() << std::endl;
+            auto new_name = std::string("!(") + this->get_name() + std::string(")");
+            std::cout << new_name << std::endl;
+            return new Filter(new_name, [this](){return !this->get_value();});
         }
 };
 
@@ -38,8 +43,8 @@ template <typename T>
 class RangeFilter : public Filter{
     private:
     public:
-        RangeFilter(Value<T>* test_value, T range_low, T range_high):
-          Filter([test_value, range_low, range_high]{
+        RangeFilter(const std::string name, Value<T>* test_value, T range_low, T range_high):
+          Filter(name, [test_value, range_low, range_high]{
                   T val = test_value->get_value();
                   return (val >= range_low) && (val < range_high);
                   }){ }

+ 318 - 50
value.hpp

@@ -1,3 +1,44 @@
+/**
+ * @file
+ * @author  Caleb Fangmeier <caleb@fangmeier.tech>
+ * @version 0.1
+ *
+ * @section LICENSE
+ *
+ *
+ * MIT License
+ *
+ * Copyright (c) 2017 Caleb Fangmeier
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ * This header defines a set of generic classes that wrap up "values". In
+ * essence, a Value<T> object is just something that contains a value of type T
+ * and can provide it when requested. The usefulness stems from composing
+ * values together with calculations. This enables very clear dependency
+ * mapping and a way to know clearly how every value was arrived at. This could
+ * be used to, for example, automatically generate commentary for plots that
+ * explain the exect calculation used to create it. Or easily making a series
+ * of plots contrasting different values that have been composed slightly
+ * differently.
+ */
 #ifndef value_hpp
 #define value_hpp
 #include <iostream>
@@ -9,45 +50,104 @@
 #include <initializer_list>
 #include <functional>
 
+/**
+ * The namespace containing all filval classes and functions.
+ */
 namespace filval{
 
+/** 
+ * A type-agnostic value.
+ * It is necessary to create a type-agnostic parent class to Value so that
+ * it is possible to handle collections of them. GenValue also provides the
+ * rest of the type-independent interface to Value.
+ */
 class GenValue{
     private:
-        inline static std::vector<GenValue*> values;
+        /**
+         * The name of the value.
+         * This is used to allow for dynamic lookup of
+         * values based on their name via GenValue::get_value.
+         */
+        std::string name;
+    protected:
+        /**
+         * Mark the internal value as invalid. This is needed for DerivedValue
+         * to force a recalculation of the internal value when a new
+         * observation is loaded into memory. It is called automatically for
+         * all GenValue objects when reset is called.
+         */
         virtual void _reset() = 0;
+        /**
+         * A static mapping containing all created Value objects.
+         * Every value object must have a unique name, and this name is used as
+         * a key in values to that object. This is used to enable more dynamic
+         * creation of objects as well as avoiding the uneccesary passing of
+         * pointers.
+         */
+        inline static std::map<const std::string, GenValue*> values;
     public:
-        GenValue(){
-            values.push_back(this);
+        GenValue(const std::string& name)
+          :name(name) {
+            values[name] = this;
+        }
+        const std::string& get_name(){
+            return name;
         }
         static void reset(){
             for (auto val : values){
-                val->_reset();
+                val.second->_reset();
+            }
+        }
+        static GenValue* get_value(const std::string& name){
+            return values.at(name);
+        }
+        static void summary(){
+            std::cout << "The following values have been created: " << std::endl;
+            for (auto value : values){
+                std::cout << "\t\"" << value.first << "\" at address " << value.second << std::endl;
             }
         }
 };
 typedef std::map<std::string, GenValue*> ValueSet;
 
 
+/**
+ * A generic value.
+ * In order to facilitate run-time creation of analysis routines, it is
+ * necessary to have some ability to get and store *values*. Values can either
+ * be directly taken from some original data source (i.e. ObservedValue), or
+ * they can be a function of some other set of values (i.e. DerivedValue). They
+ * template class T of Value<T> is the type of thing that is returned upon
+ * calling get_value().
+ */
 template <typename T>
 class Value : public GenValue{
     public:
-        Value(): GenValue(){}
+        Value(const std::string& name)
+          :GenValue(name){ }
+        /** Calculate, if necessary, and return the value held by this object.
+         */
         virtual T& get_value() = 0;
 };
 
 
+/**
+ * A generic, observed, value.
+ * An ObservedValue is the interface to your dataset. Upon creation, an
+ * ObservedValue is given a pointer to an object of type T. When an observation
+ * is loaded into memory, the value at the location referenced by that pointer
+ * must be updated with the associated data from that observation. This is the
+ * responsibility of whatever DataSet implementation is being used. This object
+ * then will read that data and return it when requested.
+ */
 template <typename T>
 class ObservedValue : public Value<T>{
-    /* For "observed" values, there is nothing to calculate since this is
-     * merely a wrapper around a field in the observation. A pointer to the
-     * value is kept and it's value is read when requested.
-     */
     private:
         T *val_ref;
         void _reset(){ }
     public:
-        ObservedValue(T* val_ref)
-          :Value<T>(),
+        ObservedValue(const std::string& name, T* val_ref)
+          :Value<T>(name),
            val_ref(val_ref){ }
         T& get_value(){
             return *val_ref;
@@ -55,14 +155,23 @@ class ObservedValue : public Value<T>{
 };
 
 
+/**
+ * A generic, derived, value.
+ * A DerivedValue is generally defined as some function of other Value objects.
+ * For example, a Pair is a function of two other Value objects that makes a
+ * pair of them. Note that these other Value objects are free to be either
+ * ObservedValues or other DerivedValues. 
+ *
+ * It is desireable from a performance standpoint that each DerivedValue be
+ * calculated no more than once per observation. Therefore, when a get_value is
+ * called on a DerivedValue, it first checks whether the value that it holds is
+ * **valid**, meaning it has already been calculated for this observation. If
+ * so, it simply returns the value. If not, the update_value function is called
+ * to calculate the value. and then the newly calculated value is marked as
+ * valid and returned.
+ */
 template <typename T>
 class DerivedValue : public Value<T>{
-    /* A "derived" value is the result of some sort of calculation. Since it is
-     * desireable for the calculation to occur at most a single time for each
-     * observation, the result of the calculation is stored in the object. be
-     * sure that "reset" is called between processing observations to force a
-     * re-calculation.
-     */
     private:
         void _reset(){
             value_valid = false;
@@ -71,9 +180,19 @@ class DerivedValue : public Value<T>{
         T value;
         bool value_valid;
 
+        /**
+         * Updates the internal value.
+         * This function should be overridden by any child class to do the
+         * actual work of updating value based on whatever rules the class
+         * chooses. Normally, this consists of geting the values from some
+         * associated Value objects, doing some calculation on them, and
+         * storing the result in value.
+         */
         virtual void update_value() = 0;
     public:
-        DerivedValue() :Value<T>(), value_valid(false) { }
+        DerivedValue(const std::string& name)
+          :Value<T>(name),
+           value_valid(false) { }
 
         T& get_value(){
             if (!value_valid){
@@ -84,6 +203,17 @@ class DerivedValue : public Value<T>{
         }
 };
 
+
+/**
+ * A std::vector wrapper around a C-style array.
+ * In order to make some of the higher-level Value types easier to work with,
+ * it is a good idea to wrap all arrays in the original data source with
+ * std::vector objects. To do this, it is necessary to supply both a Value
+ * object containing the array itself as well as another Value object
+ * containing the size of that array. Currently, update_value will simply copy
+ * the contents of the array into the interally held vector.
+ * \todo avoid an unneccessary copy and set the vectors data directly.
+ */
 template <typename T>
 class WrapperVector : public DerivedValue<std::vector<T> >{
     private:
@@ -100,14 +230,19 @@ class WrapperVector : public DerivedValue<std::vector<T> >{
         }
 
     public:
-        WrapperVector(Value<int>* _size, Value<T*>* _data)
-          :DerivedValue<std::vector<T> >(),
+        WrapperVector(const std::string& name, Value<int>* _size, Value<T*>* _data)
+          :DerivedValue<std::vector<T> >(name),
            size(_size), data(_data){ }
-        WrapperVector(ValueSet *values, const std::string &label_size, const std::string &label_data)
-          :WrapperVector(dynamic_cast<Value<int>*>(values->at(label_size)),
-                         dynamic_cast<Value<T*>*>(values->at(label_data))) { }
+
+        WrapperVector(const std::string& name, const std::string &label_size, const std::string &label_data)
+          :WrapperVector(name,
+                         dynamic_cast<Value<int>*>(GenValue::values.at(label_size)),
+                         dynamic_cast<Value<T*>*>(GenValue::values.at(label_data))) { }
 };
 
+/**
+ * Creates a std::pair type from a two other Value objects.
+ */
 template <typename T1, typename T2>
 class Pair : public DerivedValue<std::pair<T1, T2> >{
     protected:
@@ -117,14 +252,28 @@ class Pair : public DerivedValue<std::pair<T1, T2> >{
             this->value.second = value_pair.second->get_value();
         }
     public:
-        Pair(Value<T1> *value1, Value<T2> *value2)
-          :DerivedValue<std::pair<T1, T2> >(),
+        Pair(const std::string name, Value<T1> *value1, Value<T2> *value2)
+          :DerivedValue<std::pair<T1, T2> >(name),
            value_pair(value1, value2){ }
-        Pair(ValueSet *values, const std::string &label1, const std::string &label2)
-          :Pair((Value<T1>*) values->at(label1),
-                (Value<T1>*) values->at(label2)){ }
+        Pair(const std::string& name, const std::string& label1, const std::string& label2)
+          :Pair(name,
+                dynamic_cast<Value<T1>*>(GenValue::values.at(label1)),
+                dynamic_cast<Value<T1>*>(GenValue::values.at(label2))){ }
 };
 
+/**
+ * Takes a set of four Value<std::vector<T> > objects and a function of four Ts
+ * and returns a std::vector<R>. This is used in, for instance, calculating the
+ * energy of a set of particles when one has separate arrays containing pt,
+ * eta, phi, and mass. These arrays are first wrapped up in VectorWrappers and
+ * then passes along with a function to calculate the energy into a ZipMapFour.
+ * The result of this calculation is a new vector containing the energy for
+ * each particle. Note that if the input vectors are not all the same size,
+ * calculations are only performed up to the size of the shortest.
+ * \see MiniTreeDataSet
+ * \todo find way to implement for arbitrary number(and possibly type) of
+ * vector inputs.
+ */
 template <typename R, typename T>
 class ZipMapFour : public DerivedValue<std::vector<R> >{
     private:
@@ -149,24 +298,29 @@ class ZipMapFour : public DerivedValue<std::vector<R> >{
         }
 
     public:
-        ZipMapFour(std::function<R(T, T, T, T)> f,
+        ZipMapFour(const std::string& name, std::function<R(T, T, T, T)> f,
                    Value<std::vector<T> >* v1, Value<std::vector<T> >* v2,
                    Value<std::vector<T> >* v3, Value<std::vector<T> >* v4)
-          :DerivedValue<std::vector<R> >(),
+          :DerivedValue<std::vector<R> >(name),
            f(f), v1(v1), v2(v2), v3(v3), v4(v4) { }
-        ZipMapFour(std::function<R(T, T, T, T)> f,
-                   ValueSet *values,
-                   const std::string &label1,
-                   const std::string &label2,
-                   const std::string &label3,
-                   const std::string &label4)
-          :ZipMapFour(f,
-                      dynamic_cast<Value<std::vector<T> >*>(values->at(label1)),
-                      dynamic_cast<Value<std::vector<T> >*>(values->at(label2)),
-                      dynamic_cast<Value<std::vector<T> >*>(values->at(label3)),
-                      dynamic_cast<Value<std::vector<T> >*>(values->at(label4))){ }
+
+        ZipMapFour(const std::string& name,
+                   std::function<R(T, T, T, T)> f,
+                   const std::string &label1, const std::string &label2,
+                   const std::string &label3, const std::string &label4)
+          :ZipMapFour(name, f,
+                      dynamic_cast<Value<std::vector<T> >*>(GenValue::values.at(label1)),
+                      dynamic_cast<Value<std::vector<T> >*>(GenValue::values.at(label2)),
+                      dynamic_cast<Value<std::vector<T> >*>(GenValue::values.at(label3)),
+                      dynamic_cast<Value<std::vector<T> >*>(GenValue::values.at(label4))){ }
 };
 
+/**
+ * Reduce a Value of type vector<T> to just a T.
+ * This is useful functionality to model, for instance, calculating the maximum
+ * element of a vector, or a the mean. See child classes for specific
+ * implementations.
+ */
 template <typename T>
 class Reduce : public DerivedValue<T>{
     private:
@@ -176,11 +330,115 @@ class Reduce : public DerivedValue<T>{
             this->value = reduce(v->get_value());
         }
     public:
-        Reduce(std::function<T(std::vector<T>)> reduce, Value<std::vector<T> >* v)
-          :DerivedValue<T>(),
+        Reduce(const std::string& name, std::function<T(std::vector<T>)> reduce, Value<std::vector<T> >* v)
+          :DerivedValue<T>(name),
            reduce(reduce), v(v) { }
+
+        Reduce(const std::string& name, std::function<T(std::vector<T>)> reduce, const std::string& v_name)
+          :Reduce(name, reduce, dynamic_cast<Value<std::vector<T> >*>(GenValue::get_value(v_name))) { }
 };
 
+/**
+ * Find and return the maximum value of a vector.
+ */
+template <typename T>
+class Max : public Reduce<T>{
+    public:
+        Max(const std::string& name, const std::string& v_name)
+          :Reduce<T>(name, [](std::vector<T> vec){ return std::max(vec.begin(), vec.end());}, v_name) { }
+};
+
+/**
+ * Find and return the minimum value of a vector.
+ */
+template <typename T>
+class Min : public Reduce<T>{
+    public:
+        Min(const std::string& name, const std::string& v_name)
+          :Reduce<T>(name, [](std::vector<T> vec){ return std::min(vec.begin(), vec.end());}, v_name) { }
+};
+
+/**
+ * Calculate the mean value of a vector.
+ */
+template <typename T>
+class Mean : public Reduce<T>{
+    public:
+        Mean(const std::string& name, const std::string& v_name)
+          :Reduce<T>(name,
+                     [](std::vector<T> vec){
+                        int n = 0; T sum = 0;
+                        for (T e : vec){ n++; sum += e; }
+                        return n>0 ? sum / n : 0; },
+                     v_name) { }
+};
+
+/**
+ * Extract the element at a specific index from a vector. 
+ */
+template <typename T>
+class ElementOf : public Reduce<T>{
+    public:
+        ElementOf(const std::string& name, Value<int>* index, const std::string& v_name)
+          :Reduce<T>(name, [index](std::vector<T> vec){return vec[index->get_value()];}, v_name) { }
+        ElementOf(const std::string& name, int index, const std::string& v_name)
+          :Reduce<T>(name, [index](std::vector<T> vec){return vec[index];}, v_name) { }
+};
+
+/**
+ * Similar to Reduce, but returns a pair of a T and an int.
+ * This is useful if you need to know where in the vector exists the element
+ * being returned.
+ */
+template <typename T>
+class ReduceIndex : public DerivedValue<std::pair<T, int> >{
+    private:
+        std::function<std::pair<T,int>(std::vector<T>)> reduce;
+        Value<std::vector<T> >* v;
+        void update_value(){
+            this->value = reduce(v->get_value());
+        }
+    public:
+        ReduceIndex(const std::string& name, std::function<std::pair<T,int>(std::vector<T>)> reduce, Value<std::vector<T> >* v)
+          :DerivedValue<T>(name),
+           reduce(reduce), v(v) { }
+
+        ReduceIndex(const std::string& name, std::function<std::pair<T,int>(std::vector<T>)> reduce, const std::string& v_name)
+          :ReduceIndex(name, reduce, dynamic_cast<Value<std::vector<T> >*>(GenValue::get_value(v_name))) { }
+};
+
+/**
+ * Find and return the maximum value of a vector and its index.
+ */
+template <typename T>
+class MaxIndex : public ReduceIndex<T>{
+    public:
+        MaxIndex(const std::string& name, const std::string& v_name)
+          :ReduceIndex<T>(name,
+                          [](std::vector<T> vec){ 
+                              auto elptr = std::max_element(vec.begin(), vec.end());
+                              return std::pair<T,int>(*elptr, int(elptr-vec.begin()));},
+                          v_name) { }
+};
+
+/**
+ * Find and return the minimum value of a vector and its index.
+ */
+template <typename T>
+class MinIndex : public ReduceIndex<T>{
+    public:
+        MinIndex(const std::string& name, const std::string& v_name)
+          :ReduceIndex<T>(name,
+                          [](std::vector<T> vec){ 
+                              auto elptr = std::min_element(vec.begin(), vec.end());
+                              return std::pair<T,int>(*elptr, int(elptr-vec.begin()));},
+                          v_name) { }
+};
+
+
+/**
+ * A variadic 
+ */
 template <typename R, typename... T>
 class MultiFunc : public DerivedValue<R>{
     private:
@@ -192,24 +450,33 @@ class MultiFunc : public DerivedValue<R>{
         }
 
     public:
-        MultiFunc(std::function<R(std::tuple<T...>)> f, T... varargs)
-          :f(f),
+        MultiFunc(const std::string& name, std::function<R(std::tuple<T...>)> f, T... varargs)
+          :DerivedValue<R>(name),
+           f(f),
            value_tuple(varargs...){ }
 };
 
+/**
+ * A generic value owning only a function object.
+ * All necessary values upon which this value depends must be bound to the
+ * function object.
+ */
 template <typename T>
 class BoundValue : public DerivedValue<T>{
-    /* A "bound" value has it's dependencies bound into a function object. */
     protected:
         std::function<T()> f;
         void update_value(){
             this->value = f();
         }
     public:
-        BoundValue(std::function<T()> f)
-          :f(f) { }
+        BoundValue(const std::string& name, std::function<T()> f)
+          :DerivedValue<T>(name),
+           f(f) { }
 };
 
+/**
+ * A Value which always returns the same value, supplied in the constructor.
+ */
 template <typename T>
 class ConstantValue : public DerivedValue<T>{
     protected:
@@ -218,8 +485,9 @@ class ConstantValue : public DerivedValue<T>{
             this->value = const_value;
         }
     public:
-        ConstantValue(T const_value)
-            :const_value(const_value) { }
+        ConstantValue(const std::string& name, T const_value)
+            :DerivedValue<T>(name),
+             const_value(const_value) { }
 };
 }
 #endif // value_hpp