(C++) MultiApproximator

February 24, 2017 · View on GitHub

 

 

 

 

 

(C++) MultiApproximator

 

STLQt
CreatorLubuntuWindows

 

MultiApproximator is a class to perform an interpolation on data. For example, when the supplied data consists of the coordinats (1,1) and (2,2), it will estimate (the x value of) 1,5 to be (the value y value of) 1,5.

 

MultiApproximator supports multiple identical keys (for example the coordinats (1.0,2.0) and (1.0,3.0)), similar to a std::multi_map. Approximator does not allow this.

Technical facts

 

 

 

 

 

 

./CppMultiApproximator/CppMultiApproximator.pri

 


INCLUDEPATH += \     ../../Classes/CppMultiApproximator SOURCES += \     ../../Classes/CppMultiApproximator/multiapproximator.cpp HEADERS  += \     ../../Classes/CppMultiApproximator/multiapproximator.h OTHER_FILES += \     ../../Classes/CppMultiApproximator/Licence.txt

 

 

 

 

 

./CppMultiApproximator/multiapproximator.h

 


#ifndef MULTIAPPROXIMATOR_H #define MULTIAPPROXIMATOR_H #include <algorithm> #include <cassert> #include <cmath> #include <numeric> #include <stdexcept> #include <map> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #pragma GCC diagnostic pop #include "approximator.h" #include "exceptionnoextrapolation.h" #include "testtimer.h" #include "trace.h" namespace ribi { ///MultiApproximator can estimate a value between (non-unique) keys. ///For example, supply Approximator<X,Y> with (1.0,10) and (2.0,20) ///and it will estimate an X if 1.5 to have a Y of 15. ///If the data used has unique keys only, use Approximator ///for an increase in run-time speed. ///If all keys are added to MultiApproximator, it is possible to spawn ///an Approximator from it. /// ///Key: a type that has operator< ///Value: a type that has operator/(double) ///Container: how to store the std::pair<Key,Value> ///  - boost::container::flat_multimap<Key,Value> ///    : faster for lookup, data adding is slower ///  - std::multimap<Key,Value> ///    : faster adding of data, lookup is slower template <class Key = double, class Value = double, class Container = std::multimap<Key,Value> > struct MultiApproximator {   typedef Key key_type;   typedef Value value_type;   typedef typename Container::const_iterator Iterator;   typedef typename std::pair<Iterator,Iterator> Range;   explicit MultiApproximator(const Container& container = Container()) noexcept;   ///Add a (key,value) pair. Because keys do not need to be unique,   ///this will always succeed   void Add(const Key& key, const Value& value) noexcept;   ///Approximates a value from a key   ///Will throw a std::logic_error if there is no data in the MultiApproximator   ///Will throw ExceptionNoExtrapolation if the key is beyond the range   ///of the keys stored in MultiApproxitor   Value Approximate(const Key& key) const;   ///Obtain the container   const Container& GetContainer() const noexcept{ return m_m; }   ///Get the heighest key value   Key GetMax() const;   ///Get the lowest key value   Key GetMin() const;   ///Obtain the true or averaged value at the key when the key is present   Value GetValue(const Key& key) const;   ///Obtain the version of this class   static std::string GetVersion() noexcept;   ///Obtain the version history of this class   static std::vector<std::string> GetVersionHistory() noexcept;   private:   ///The container to store the std::pair<Key,Value> in   Container m_m;   #ifndef NDEBUG   ///Test this class   static void Test() noexcept;   #endif   //template <class AnyKey, class AnyValue, class AnyContainer>   //friend void MultiApproximator<AnyKey,AnyValue,AnyContainer>::Test();   //friend void MultiApproximator<Key,Value,Container>::Test(); }; ///Convert a std::multimap to a std::map template <class Key, class Value> const std::map<Key,Value> MultimapToMap(   const std::multimap<Key,Value> m) noexcept; ///Convert to a (non-multi)Approximator ///This probably gain runtime speed, when will no data added anymore template <   class Key,   class Value,   class MultiContainer,   class Container   > Approximator<Key,Value,Container> ToApproximator(   const MultiApproximator<Key,Value,MultiContainer>& m) noexcept; template <class Key, class Value, class Container> MultiApproximator<Key,Value,Container>::MultiApproximator(const Container& container) noexcept   : m_m { container } {   static_assert(!std::is_integral<Key>(),     "MultiApproximator will not work on integer keys");   static_assert(!std::is_integral<Value>(),     "MultiApproximator will not work on integer values");   #ifndef NDEBUG   Test();   #endif } template <class Key, class Value, class Container> void MultiApproximator<Key,Value,Container>::Add(const Key& key, const Value& value) noexcept {   m_m.insert(std::make_pair(key,value)); } template <class Key, class Value, class Container> Value MultiApproximator<Key,Value,Container>::Approximate(const Key& key) const {   if (m_m.empty())   {     throw std::logic_error(       "MultiApproximator<Key,Value,Container>::Approximate: "       "cannot approximate without data");   }   //Must the average be calculated?   if (m_m.find(key) != m_m.end()) return GetValue(key);   const Iterator high { m_m.lower_bound(key) };   if (high == m_m.begin() || high == m_m.end())   {     assert(!m_m.empty());     const Key lowest  { (*m_m.begin()).first  };     const Key highest { (*m_m.rbegin()).first };     throw ExceptionNoExtrapolation<Key>(key, lowest, highest);   }   const Iterator low { --Iterator(high) };   assert(low != m_m.end());   assert(high != m_m.end());   const Key d_low  { (*low).first  };   const Key d_high { (*high).first };   assert(d_low < key);   assert(d_high > key);   const double fraction { (key - d_low) / (d_high - d_low) };   assert(fraction >= 0.0);   assert(fraction <= 1.0);   assert(m_m.find(d_low)  != m_m.end());   assert(m_m.find(d_high) != m_m.end());   const Value h_low  { GetValue(d_low)  };   const Value h_high { GetValue(d_high) };   return ((1.0 - fraction)) * h_low + ((0.0 + fraction) * h_high); } template <class Key, class Value, class Container> Key MultiApproximator<Key,Value,Container>::GetMax() const {   assert(!m_m.empty());   return (*m_m.rbegin()).first; } template <class Key, class Value, class Container> Key MultiApproximator<Key,Value,Container>::GetMin() const {   assert(!m_m.empty());   return (*m_m.begin()).first; } template <class Key, class Value, class Container> Value MultiApproximator<Key,Value,Container>::GetValue(const Key& key) const {   assert(m_m.find(key) != m_m.end());   //Must the average be calculated?   const Range r { m_m.equal_range(key) };   assert(r.first != m_m.end());   if (r.first != r.second )   {     //const Value result = std::accumulate(r.first,r.second,Value(0.0), [] )     //  / static_cast<double>( std::distance(r.first,r.second) );     Value sum { 0.0 };     int cnt { 0 };     for (Iterator i { r.first }; i!=r.second; ++i)     {       sum += (*i).second;       ++cnt;     }     const Value result { sum / static_cast<double>(cnt) };     return result;   }   else   {     const Value result { (*r.first).second };     return result;   } } template <class Key, class Value, class Container> std::string MultiApproximator<Key,Value,Container>::GetVersion() noexcept {   return "1.1"; } ///Obtain the version history of this class template <class Key, class Value, class Container> std::vector<std::string> MultiApproximator<Key,Value,Container>::GetVersionHistory() noexcept {   return {     "2013-08-23: version 1.0: initial version",     "2013-08-23: version 1.1: add conversion to an Approximator"   }; } template <class Key, class Value> const std::map<Key,Value> MultimapToMap(   const std::multimap<Key,Value> m) noexcept {   std::map<Key,Value> n;   if (m.empty()) return n;   const auto end = m.end();   for (auto begin = m.begin(); begin != end; )   {     assert(begin != m.end());     const auto key = (*begin).first;     assert(m.find(key) != m.end());     //Must the average be calculated?     const auto r = m.equal_range(key);     assert(r.first != m.end());     if (r.first != r.second )     {       Value sum(0.0);       int cnt = 0;       for (auto i = r.first; i!=r.second; ++i)       {         sum += (*i).second;         ++cnt;       }       const Value result( sum / static_cast<double>(cnt));       n[ key ] = result;     }     else     {       const Value result((*r.first).second);       n[ key ] = result;     }     begin = r.second;   }   return n; } #ifndef NDEBUG template <class Key, class Value, class Container> void MultiApproximator<Key,Value,Container>::Test() noexcept {   {     static bool is_tested{false};     if (is_tested) return;     is_tested = true;   }   const TestTimer test_timer(__func__,__FILE__,1.0);   //GetMin and GetMax   {     MultiApproximator<double,double,std::multimap<double,double> > m;     m.Add(1.0,0.0);     assert(m.GetMin() == 1.0);     assert(m.GetMax() == 1.0);     m.Add(2.0,0.0);     assert(m.GetMin() == 1.0);     assert(m.GetMax() == 2.0);     m.Add(0.0,0.0);     assert(m.GetMin() == 0.0);     assert(m.GetMax() == 2.0);     m.Add(0.5,0.0);     assert(m.GetMin() == 0.0);     assert(m.GetMax() == 2.0);   }   //GetValue   {     MultiApproximator<double,double,std::multimap<double,double> > m;     const double key = 1.0;     const double value1 = 1.0;     m.Add(key,value1);     assert(m.GetValue(key) == value1);     const double value2 = 2.0;     m.Add(key,value2);     assert(m.GetValue(key) == (value1 + value2) / 2.0);     const double value3 = 4.0;     m.Add(key,value3);     assert(m.GetValue(key) == (value1 + value2 + value3) / 3.0);   }   //MultimapToMap   {     {       const std::multimap<int,double> m ( { { 1, 1.0} } );       const std::map<int,double> n(MultimapToMap(m));       assert(n.size() == 1);     }     {       const std::multimap<int,double> m ( { { 1, 1.0}, { 1, 1.0} } );       const std::map<int,double> n(MultimapToMap(m));       const std::map<int,double> e( { { 1, 1.0 } } );       assert(n.size() == 1);       assert(n == e);     }     {       const std::multimap<int,double> m ( { { 1, 1.0}, { 1, 2.0} } );       const std::map<int,double> n(MultimapToMap(m));       const std::map<int,double> e( { { 1, 1.5 } } );       assert(n.size() == 1);       assert(n == e);     }     {       const std::multimap<int,double> m ( { {0, 0.0}, { 1, 1.0}, { 1, 1.0}, {2, 2.0} } );       const std::map<int,double> n(MultimapToMap(m));       const std::map<int,double> e( { {0, 0.0}, { 1, 1.0 }, {2, 2.0} } );       assert(n.size() == 3);       assert(n == e);     }     {       const std::multimap<int,double> m ( { {0, 0.0}, { 1, 1.0}, { 1, 2.0}, {2, 2.0} } );       const std::map<int,double> n(MultimapToMap(m));       const std::map<int,double> e( { {0, 0.0}, { 1, 1.5 }, {2, 2.0} } );       assert(n.size() == 3);       assert(n == e);     }   }   //Test approximation   {     MultiApproximator<double,double,std::multimap<double,double> > m;     m.Add(1.0,10);     m.Add(2.0,20);     assert(m.Approximate(1.5) == 15);     m.Add(4.0,40);     m.Add(4.0,40);     assert(m.Approximate(3.0) == 30);     m.Add(3.0,35);     assert(m.Approximate(3.0) == 35);     m.Add(3.0,45);     assert(m.Approximate(3.0) == 40);   }   TRACE("Completed MultiApproximator::Test successfully"); } #endif template <   class Key,   class Value,   class MultiContainer,   class Container   > Approximator<Key,Value,Container> ToApproximator(   const MultiApproximator<Key,Value,MultiContainer>& multi_approximator) noexcept {   const MultiContainer m = multi_approximator.GetContainer();   const Container n = MultimapToMap(m);   Approximator<Key,Value,Container> a(n);   return a; } } //~namespace ribi #endif // MULTIAPPROXIMATOR_H

 

 

 

 

 

./CppMultiApproximator/multiapproximator.cpp

 


#include "multiapproximator.h"