9 #ifndef IMPKERNEL_CACHE_H
10 #define IMPKERNEL_CACHE_H
12 #include <IMP/kernel_config.h>
13 #include "internal/cache.h"
18 #include <boost/multi_index_container.hpp>
19 #include <boost/multi_index/sequenced_index.hpp>
20 #include <boost/multi_index/hashed_index.hpp>
21 #include <boost/multi_index/global_fun.hpp>
29 IMPKERNEL_BEGIN_NAMESPACE
38 template <
class Generator,
39 class Checker = std::equal_to<typename Generator::result_type> >
43 mutable bool has_result_;
44 mutable typename Generator::result_type result_;
45 mutable int num_misses_;
46 mutable int num_stats_;
49 typedef typename Generator::result_type Value;
50 typedef typename Generator::argument_type Key;
51 Memoizer(
const Generator &gen,
const Checker &checker = Checker())
57 Generator &access_generator() {
return gen_; }
58 const Generator &get_generator()
const {
return gen_; }
63 const Value &
get()
const {
71 "Wrong result returned from generator");
75 void set(
const Value &v)
const {
80 double get_hit_rate()
const {
81 return 1.0 -
static_cast<double>(num_misses_) / num_stats_;
88 template <
class Generator,
class Checker>
91 typedef typename Generator::argument_type::value_type Key;
92 typedef typename Generator::result_type::value_type Entry;
99 static Key get_0(Entry e) {
return e[0]; }
100 static Key get_1(Entry e) {
return e[1]; }
103 typedef boost::multi_index::global_fun<Entry, Key, &This::get_0> P0Member;
104 typedef boost::multi_index::global_fun<Entry, Key, &This::get_1> P1Member;
105 typedef boost::multi_index::hashed_non_unique<P0Member> Hash0Index;
106 typedef boost::multi_index::hashed_non_unique<P1Member> Hash1Index;
107 typedef boost::multi_index::indexed_by<Hash0Index, Hash1Index> IndexBy;
108 typedef boost::multi_index_container<Entry, IndexBy> Cache;
109 typedef typename boost::multi_index::nth_index<Cache, 0>::type::const_iterator
111 typedef typename boost::multi_index::nth_index<Cache, 1>::type::const_iterator
118 EntryEqual(Key t0, Key t1) : v(t0, t1) {}
120 bool operator()(
const O &o)
const {
121 return v[0] == o[0] && v[1] == o[1];
125 Hash0Iterator
get(Key t0, Key t1)
const {
127 boost::tie(b, e) = cache_.template get<0>().equal_range(t0);
130 Hash0Iterator f = std::find_if(b, e, EntryEqual(t0, t1));
133 return cache_.template get<0>().end();
138 void check_it()
const {
139 #if IMP_HAS_CHECKS >= IMP_INTERNAL
143 << gen_(domain_,
this)
144 <<
" and cleared is " << cleared_);
145 for (Hash0Iterator c = cache_.template get<0>().begin();
146 c != cache_.template get<0>().end(); ++c) {
148 get(c->operator[](1), c->operator[](0)) ==
149 cache_.template get<0>().end(),
150 "Both an entry and its flip are in the table: " << *c <<
": " << cur);
156 for (
unsigned int i = 0; i < cleared_.size(); ++i) {
159 boost::tie(b, e) = cache_.template get<0>().equal_range(cleared_[i]);
161 <<
" was not cleared.");
165 boost::tie(b, e) = cache_.template get<1>().equal_range(cleared_[i]);
167 <<
" was not cleared.");
173 IMP_LOG_VERBOSE(
"Inserting " << nv <<
" into pair memoizer" << std::endl);
175 for (
unsigned int i = 0; i < nv.size(); ++i) {
177 std::find_if(nv.begin(), nv.end(),
178 EntryEqual(nv[i][1], nv[i][0])) == nv.end(),
179 "An entry and its flip are already in list: " << nv);
182 cache_.insert(nv.begin(), nv.end());
184 IMP_LOG_VERBOSE(
"To get " <<
typename Generator::result_type(cache_.begin(),
189 template <
class F,
class It>
190 F do_apply(It b, It e, F f)
const {
191 for (It c = b; c != e; ++c) {
199 const Generator &gen = Generator(),
200 const Checker &check = Checker())
201 : gen_(gen), checker_(check), cleared_(domain), domain_(domain) {
202 IMP_LOG_TERSE(
"Domain for memoizer is " << domain << std::endl);
207 if (!cleared_.empty()) fill_it();
209 return do_apply(cache_.begin(), cache_.end(), f);
215 return do_apply(cache_.begin(), cache_.end(), f);
219 void remove(
const Key &a) {
220 if (std::find(cleared_.begin(), cleared_.end(), a) != cleared_.end()) {
224 cleared_.push_back(a);
227 boost::tie(b, e) = cache_.template get<0>().equal_range(a);
228 cache_.template get<0>().erase(b, e);
232 boost::tie(b, e) = cache_.template get<1>().equal_range(a);
233 cache_.template get<1>().erase(b, e);
236 void insert(
const Entry &e) { cache_.insert(e); }
241 const Generator &get_generator()
const {
return gen_; }
242 Generator &access_generator()
const {
return gen_; }
252 template <
class Generator,
253 class Checker = std::equal_to<typename Generator::result_type> >
256 typedef typename Generator::result_type Value;
257 typedef typename Generator::argument_type Key;
262 unsigned int max_size_;
263 mutable int num_stats_;
264 mutable int num_misses_;
268 KVP(
const Key &k,
const Value &v) : key(k), value(v) {}
270 typedef boost::multi_index::member<KVP, Key, &KVP::key> KeyMember;
271 typedef boost::multi_index::hashed_unique<KeyMember> HashIndex;
272 typedef boost::multi_index::sequenced<> Sequenced;
273 typedef boost::multi_index_container<
274 KVP, boost::multi_index::indexed_by<HashIndex, Sequenced> > Map;
276 typedef typename boost::multi_index::template nth_index<
277 Map, 0>::type::const_iterator LookupIterator;
278 typedef typename boost::multi_index::template nth_index<
279 Map, 1>::type::const_iterator OrderIterator;
280 Value add_value(
const Key &k)
const {
281 Value v = gen_(k, *
this);
282 map_.template get<1>().push_front(KVP(k, v));
283 while (map_.size() > max_size_) {
285 map_.template get<1>().pop_back();
291 LRUCache(
const Generator &gen,
unsigned int size,
292 const Checker checker = Checker())
298 Value
get(
const Key &k)
const {
299 LookupIterator it = map_.template get<0>().find(k);
301 if (it == map_.template get<0>().end()) {
304 Value v = add_value(k);
306 map_.template get<0>().end(),
307 "Failed to insert into cache");
310 map_.template get<1>().relocate(map_.template project<1>(it),
311 map_.template get<1>().begin());
318 map_.template get<0>().find(k) != map_.template get<0>().end(),
319 "Gone, gone I tell you");
323 double get_hit_rate()
const {
324 return 1.0 -
static_cast<double>(num_misses_) / num_stats_;
328 for (OrderIterator it = map_.template get<1>().begin();
329 it != map_.template get<1>().end(); ++it) {
330 ret.push_back(it->key);
334 typedef OrderIterator ContentIterator;
335 ContentIterator contents_begin()
const {
336 return map_.template get<1>().begin();
338 ContentIterator contents_end()
const {
return map_.template get<1>().end(); }
339 void insert(Key k, Value v) {
340 LookupIterator it = map_.template get<0>().find(k);
341 if (it == map_.template get<0>().end()) {
342 map_.template get<1>().push_front(KVP(k, v));
343 while (map_.size() > max_size_) {
344 map_.template get<1>().pop_back();
348 unsigned int size()
const {
return map_.size(); }
349 Generator &access_generator() {
return gen_; }
350 const Generator &get_generator()
const {
return gen_; }
353 IMPKERNEL_END_NAMESPACE
Helper class for writing memoizers.
#define IMP_IF_CHECK(level)
Execute the code block if a certain level checks are on.
#define IMP_FUNCTION_LOG
Beginning logging for a non-member function.
Helper functions for implementing hashes.
#define IMP_LOG_VERBOSE(expr)
Classes to handle static sized arrays of things.
#define IMP_LOG_TERSE(expr)
void set(const Value &v) const
Update the stored result manually.
#define IMP_INTERNAL_CHECK(expr, message)
An assertion to check for internal errors in IMP. An IMP::ErrorException will be thrown.
Logging and error reporting support.
Implement a simple least recently used cache.
A class for storing lists of IMP items.
F apply_to_current_contents(F f)
Helper macros for throwing and handling exceptions.