Empirical
memo_function.h
Go to the documentation of this file.
1 
11 #ifndef EMP_MEMO_FUNCTIONS_H
12 #define EMP_MEMO_FUNCTIONS_H
13 
14 #include <unordered_map>
15 
16 #include "../base/assert.h"
17 #include "../meta/meta.h"
18 
19 #include "tuple_utils.h"
20 
21 namespace emp {
22 
26  template <class T> class memo_function; // Not defined.
27 
28  // Single argument functions don't need a tuple...
29  template <class R, class ARG>
30  class memo_function<R(ARG)> {
31  public:
32  using size_t = std::size_t;
33  using return_t = R;
34  using index_t = std::decay_t<ARG>;
35  using fun_t = std::function<R(ARG)>;
37 
38  private:
39  mutable std::unordered_map<index_t, return_t> cache_map;
40  fun_t fun;
41 
42  public:
43  template <typename T>
44  memo_function(T && fun_info) : cache_map(), fun(std::forward<T>(fun_info)) { ; }
45  memo_function(const this_t &) = default;
46  memo_function(this_t &&) = default;
47  memo_function() : cache_map(), fun() { ; }
48 
50  this_t & operator=(const this_t &) = default;
51 
53  this_t & operator=(this_t &&) = default;
54 
56  this_t & operator=(const fun_t & _f) { cache_map.clear(); fun=_f; return *this; }
57 
59  this_t & operator=(fun_t && _f) { cache_map.clear(); fun=std::move(_f); return *this; }
60  template <typename T>
61 
63  this_t & operator=(T && arg) { cache_map.clear(); fun = std::forward<T>(arg); return *this; }
64 
66  size_t size() const { return cache_map.size(); }
67 
69  bool Has(const ARG & k) const { return cache_map.find(k) != cache_map.end(); }
70 
72  void Clear() { cache_map.clear(); }
73 
75  void Erase(const ARG & k) { cache_map.erase(k); }
76 
78  template <class KEY>
79  return_t operator()(KEY && k) const {
80  emp_assert(fun);
81  auto cache_it = cache_map.find(k);
82  if (cache_it != cache_map.end()) return cache_it->second;
83  const return_t result = fun(k);
84  return cache_map.emplace(std::forward<KEY>(k), result).first->second;
85  }
86 
88  operator bool() const { return (bool) fun; }
89 
91  operator std::function<R(ARG)>() {
92  return [this](const ARG & arg){ return operator()(arg); };
93  }
94 
96  std::function<R(ARG)> to_function() {
97  return [this](const ARG & arg){ return operator()(arg); };
98  }
99  };
100 
101  // Specialization for when we have more than one argument... we need to convert inputs
102  // to a tuple to make this work.
103  template <class R, class A1, class A2, class... EXTRA>
104  class memo_function<R(A1,A2,EXTRA...)> {
105  public:
106  using size_t = std::size_t;
107  using return_t = R;
108  using fun_t = std::function<R(A1,A2,EXTRA...)>;
109  using hash_t = emp::TupleHash<A1,A2,EXTRA...>;
110  using this_t = memo_function<R(A1,A2,EXTRA...)>;
111  using tuple_t = std::tuple<std::decay_t<A1>,std::decay_t<A2>,std::decay_t<EXTRA>...>;
112 
113  private:
114  std::unordered_map<tuple_t, return_t, hash_t> cache_map;
115  fun_t fun;
116 
117  public:
118  template <typename... Ts>
119  memo_function(Ts &&... args) : cache_map(), fun(std::forward<Ts>(args)...) { ; }
120  memo_function(const memo_function &) = default;
121  memo_function(memo_function &&) = default;
122 
123  this_t & operator=(const this_t &) = default;
124  this_t & operator=(this_t &&) = default;
125  this_t & operator=(const fun_t & _f) { cache_map.clear(); fun=_f; return *this; }
126  this_t & operator=(fun_t && _f) { cache_map.clear(); fun=std::move(_f); return *this; }
127  template <typename T>
128  this_t & operator=(T && arg) { cache_map.clear(); fun = std::forward<T>(arg); return *this; }
129 
130  size_t size() const { return cache_map.size(); }
131 
132  inline static size_t Hash(const A1 & k1, const A2 & k2, const EXTRA &... k_extra) {
133  return CombineHash(k1,k2,k_extra...);
134  }
135  bool Has(const A1 & k1, const A2 & k2, const EXTRA &... k_extra) const {
136  return cache_map.find(std::make_tuple(k1,k2,k_extra...)) != cache_map.end();
137  }
138  void Clear() { cache_map.clear(); }
139  void Erase(const A1 & k1, const A2 & k2, const EXTRA &... k_extra) {
140  cache_map.erase(std::make_tuple(k1,k2,k_extra...));
141  }
142 
143  return_t operator()(const A1 & k1, const A2 & k2, const EXTRA &... k_extra) {
144  emp_assert(fun); // Function must be specified with Get() -or- already set.
145  auto cache_it = cache_map.find(std::make_tuple(k1,k2,k_extra...));
146  if (cache_it != cache_map.end()) return cache_it->second;
147  return cache_map.emplace(std::make_tuple(k1,k2,k_extra...),
148  fun(k1, k2, k_extra...)).first->second;
149  }
150 
151  operator bool() const { return (bool) fun; }
152 
153  // A memo_function can be converted to a regular std::function for function calls.
154  operator std::function<R(A1,A2,EXTRA...)>() const {
155  return [this](A1 k1, A2 k2, EXTRA... k_extra) {
156  return operator()(k1, k2, k_extra...);
157  };
158  }
159  std::function<R(A1,A2,EXTRA...)> to_function() const {
160  return [this](A1 k1, A2 k2, EXTRA... k_extra) {
161  return operator()(k1, k2, k_extra...);
162  };
163  }
164  };
165 
166  // Single argument functions don't need a tuple...
167  template <class R>
168  class memo_function<R()> {
169  public:
170  using size_t = std::size_t;
171  using return_t = R;
172  using index_t = void;
173  using fun_t = std::function<R()>;
175 
176  private:
177  return_t cached_value;
178  bool has_cache;
179  fun_t fun;
180 
181  public:
182  template <typename T>
183  memo_function(T && fun_info) : cached_value(), has_cache(false), fun(std::forward<T>(fun_info)) { ; }
184  memo_function(const this_t &) = default;
185  memo_function(this_t &&) = default;
186  memo_function() : has_cache(false) { ; }
187 
188  this_t & operator=(const this_t &) = default;
189  this_t & operator=(this_t &&) = default;
190  this_t & operator=(const fun_t & _f) { has_cache=false; fun=_f; return *this; }
191  this_t & operator=(fun_t && _f) { has_cache=false; fun=std::move(_f); return *this; }
192  template <typename T>
193  this_t & operator=(T && arg) { has_cache=false; fun = std::forward<T>(arg); return *this; }
194 
195  size_t size() const { return (size_t) has_cache; }
196 
197  bool Has() const { return has_cache; }
198  void Clear() { has_cache=false; }
199  void Erase() { has_cache=false; }
200 
202  emp_assert(fun);
203  if (has_cache == false) { cached_value = fun(); has_cache = true; }
204  return cached_value;
205  }
206 
207  operator bool() { return (bool) fun; }
208 
209  // A memo_function can be converted to a regular std::function for function calls.
210  operator std::function<R()>() {
211  return [this](){ return operator()(); };
212  }
213  std::function<R()> to_function() {
214  return [this](){ return operator()(); };
215  }
216  };
217 
218 }
219 
220 #endif
size_t size() const
Definition: memo_function.h:195
std::function< R()> fun_t
Definition: memo_function.h:173
memo_function(Ts &&...args)
Definition: memo_function.h:119
return_t operator()()
Definition: memo_function.h:201
void index_t
Definition: memo_function.h:172
std::function< R()> to_function()
Definition: memo_function.h:213
bool Has(const ARG &k) const
Test if a certain input has been cached.
Definition: memo_function.h:69
this_t & operator=(fun_t &&_f)
Definition: memo_function.h:126
Setup tuples to be able to be used in hash tables.
Definition: tuple_utils.h:51
Definition: BitVector.h:785
std::function< R(ARG)> fun_t
Definition: memo_function.h:35
std::size_t CombineHash(const T &x)
Definition: meta.h:207
this_t & operator=(const fun_t &_f)
Definition: memo_function.h:190
void Erase(const ARG &k)
Erase a specific entry from the cache.
Definition: memo_function.h:75
size_t size() const
How many values have been cached?
Definition: memo_function.h:66
size_t size() const
Definition: memo_function.h:130
bool Has() const
Definition: memo_function.h:197
Definition: memo_function.h:30
this_t & operator=(const fun_t &_f)
Set a new std::function of the appropriate type.
Definition: memo_function.h:56
this_t & operator=(fun_t &&_f)
Definition: memo_function.h:191
void Erase(const A1 &k1, const A2 &k2, const EXTRA &...k_extra)
Definition: memo_function.h:139
return_t operator()(KEY &&k) const
Call the memo_function.
Definition: memo_function.h:79
void Clear()
Clear out the cache.
Definition: memo_function.h:72
std::tuple< std::decay_t< A1 >, std::decay_t< A2 >, std::decay_t< EXTRA >... > tuple_t
Definition: memo_function.h:111
Definition: memo_function.h:104
this_t & operator=(fun_t &&_f)
Move to here an std::function of the appropriate type.
Definition: memo_function.h:59
void Erase()
Definition: memo_function.h:199
this_t & operator=(T &&arg)
A universal copy/move for other combinations that work with std::function.
Definition: memo_function.h:63
R return_t
Definition: memo_function.h:107
std::decay_t< ARG > index_t
Definition: memo_function.h:34
R return_t
Definition: memo_function.h:171
memo_function()
Definition: memo_function.h:186
Functions to simplify the use of std::tuple.
R return_t
Definition: memo_function.h:33
void Clear()
Definition: memo_function.h:198
memo_function(T &&fun_info)
Definition: memo_function.h:183
Definition: memo_function.h:26
If we are in emscripten, make sure to include the header.
Definition: array.h:37
std::function< R(A1, A2, EXTRA...)> to_function() const
Definition: memo_function.h:159
std::function< R(ARG)> to_function()
Convert a memo_function to a regular std::function for function calls.
Definition: memo_function.h:96
#define emp_assert(...)
Definition: assert.h:199
bool Has(const A1 &k1, const A2 &k2, const EXTRA &...k_extra) const
Definition: memo_function.h:135
Definition: memo_function.h:168
void Clear()
Definition: memo_function.h:138
this_t & operator=(T &&arg)
Definition: memo_function.h:193
memo_function()
Definition: memo_function.h:47
std::function< R(A1, A2, EXTRA...)> fun_t
Definition: memo_function.h:108
static size_t Hash(const A1 &k1, const A2 &k2, const EXTRA &...k_extra)
Definition: memo_function.h:132
return_t operator()(const A1 &k1, const A2 &k2, const EXTRA &...k_extra)
Definition: memo_function.h:143
memo_function(T &&fun_info)
Definition: memo_function.h:44
this_t & operator=(const fun_t &_f)
Definition: memo_function.h:125
this_t & operator=(T &&arg)
Definition: memo_function.h:128