Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/LruCache.cs @ 16053

Last change on this file since 16053 was 16053, checked in by bburlacu, 6 years ago

#2886: Refactor RSquaredEvaluator as a standalone ParameterizedNamedItem which is a parameter of the algorithm. Implement BestSolutionAnalyzer analyzer for quality statistics. Add license headers where missing.

File size: 9.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections;
24using System.Collections.Generic;
25using System.Linq;
26using HeuristicLab.Common;
27using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
28
29namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration {
30  /// <summary>
31  /// A least-recently-used cache stored like a dictionary.
32  /// </summary>
33  /// <typeparam name="TKey">
34  /// The type of the key to the cached item
35  /// </typeparam>
36  /// <typeparam name="TValue">
37  /// The type of the cached item.
38  /// </typeparam>
39  /// <remarks>
40  /// Derived from https://stackoverflow.com/a/3719378/240845
41  /// </remarks>
42  [StorableClass]
43  public class LruCache<TKey, TValue> : DeepCloneable, IDictionary<TKey, TValue> {
44    private Dictionary<TKey, LinkedListNode<LruCacheItem>> cacheMap = new Dictionary<TKey, LinkedListNode<LruCacheItem>>();
45    private LinkedList<LruCacheItem> lruList = new LinkedList<LruCacheItem>();
46    private readonly Action<TValue> dispose;
47
48    [Storable]
49    // since a linked list is not directly serializable we use a list of { key, value, index } tuples
50    // the index is necessary to be able to restore the linked list with the correct order of nodes
51    private List<Tuple<TKey, TValue, int>> cacheItems {
52      get {
53        var indexedNodes = lruList.Select((lruCacheItem, index) => new { lruCacheItem, index }).ToDictionary(x => x.lruCacheItem, x => x.index);
54        return cacheMap.Select(x => Tuple.Create(x.Key, x.Value.Value.Value, indexedNodes[x.Value.Value])).ToList();
55      }
56      set {
57        lruList = new LinkedList<LruCacheItem>();
58        cacheMap = new Dictionary<TKey, LinkedListNode<LruCacheItem>>();
59        foreach (var t in value.OrderBy(x => x.Item3)) {
60          var key = t.Item1;
61          var val = t.Item2;
62          var item = new LruCacheItem(key, val);
63          var node = new LinkedListNode<LruCacheItem>(item);
64          lruList.AddLast(node);
65          cacheMap.Add(key, node);
66        }
67      }
68    }
69
70    /// <summary>
71    /// Initializes a new instance of the <see cref="LruCache{TKey, TValue}"/>
72    /// class.
73    /// </summary>
74    /// <param name="capacity">
75    /// Maximum number of elements to cache.
76    /// </param>
77    /// <param name="dispose">
78    /// When elements cycle out of the cache, disposes them. May be null.
79    /// </param>
80    public LruCache(int capacity, Action<TValue> dispose = null) {
81      this.Capacity = capacity;
82      this.dispose = dispose;
83    }
84
85    [StorableConstructor]
86    protected LruCache(bool deserializing) { }
87
88    protected LruCache(LruCache<TKey, TValue> original, Cloner cloner) : base(original, cloner) {
89      cacheMap = new Dictionary<TKey, LinkedListNode<LruCacheItem>>();
90      lruList = new LinkedList<LruCacheItem>(original.lruList.Select(cloner.Clone));
91
92      var nodeMap = new Dictionary<LruCacheItem, LinkedListNode<LruCacheItem>>();
93      for (var node = lruList.First; node != null; node = node.Next) {
94        nodeMap.Add(node.Value, node);
95      }
96
97      foreach (var t in original.cacheMap) {
98        var node = t.Value;
99        var item = node.Value;
100        var clone = (LruCacheItem)cloner.GetClone(item);
101        cacheMap.Add(t.Key, nodeMap[clone]);
102      }
103      Capacity = original.Capacity;
104    }
105
106    /// <summary>
107    /// Gets the capacity of the cache.
108    /// </summary>
109    [Storable]
110    public int Capacity { get; private set; }
111
112    public ICollection<TKey> Keys => cacheMap.Keys;
113
114    public ICollection<TValue> Values => (ICollection<TValue>)Keys.Select(x => cacheMap[x].Value.Value);
115
116    public int Count => cacheMap.Count;
117
118    public bool IsReadOnly => false;
119
120    public TValue this[TKey key] {
121      get { return Get(key, null); }
122      set { Add(key, value); }
123    }
124
125    /// <summary>Gets the value associated with the specified key.</summary>
126    /// <param name="key">
127    /// The key of the value to get.
128    /// </param>
129    /// <param name="value">
130    /// When this method returns, contains the value associated with the specified
131    /// key, if the key is found; otherwise, the default value for the type of the
132    /// <paramref name="value" /> parameter. This parameter is passed
133    /// uninitialized.
134    /// </param>
135    /// <returns>
136    /// true if the <see cref="T:System.Collections.Generic.Dictionary`2" />
137    /// contains an element with the specified key; otherwise, false.
138    /// </returns>
139    public bool TryGetValue(TKey key, out TValue value) {
140      lock (cacheMap) {
141        LinkedListNode<LruCacheItem> node;
142        if (cacheMap.TryGetValue(key, out node)) {
143          value = node.Value.Value;
144          lruList.Remove(node);
145          lruList.AddLast(node);
146          return true;
147        }
148
149        value = default(TValue);
150        return false;
151      }
152    }
153
154    /// <summary>
155    /// Looks for a value for the matching <paramref name="key"/>. If not found,
156    /// calls <paramref name="valueGenerator"/> to retrieve the value and add it to
157    /// the cache.
158    /// </summary>
159    /// <param name="key">
160    /// The key of the value to look up.
161    /// </param>
162    /// <param name="valueGenerator">
163    /// Generates a value if one isn't found.
164    /// </param>
165    /// <returns>
166    /// The requested value.
167    /// </returns>
168    public TValue Get(TKey key, Func<TValue> valueGenerator) {
169      lock (cacheMap) {
170        LinkedListNode<LruCacheItem> node;
171        TValue value;
172        if (cacheMap.TryGetValue(key, out node)) {
173          value = node.Value.Value;
174          lruList.Remove(node);
175          lruList.AddLast(node);
176        } else {
177          value = valueGenerator();
178          if (cacheMap.Count >= Capacity) {
179            RemoveFirst();
180          }
181
182          LruCacheItem cacheItem = new LruCacheItem(key, value);
183          node = new LinkedListNode<LruCacheItem>(cacheItem);
184          lruList.AddLast(node);
185          cacheMap.Add(key, node);
186        }
187
188        return value;
189      }
190    }
191
192    /// <summary>
193    /// Adds the specified key and value to the dictionary.
194    /// </summary>
195    /// <param name="key">
196    /// The key of the element to add.
197    /// </param>throw new NotImplementedException()
198    /// <param name="value">
199    /// The value of the element to add. The value can be null for reference types.
200    /// </param>
201    public void Add(TKey key, TValue value) {
202      lock (cacheMap) {
203        if (cacheMap.Count >= Capacity) {
204          RemoveFirst();
205        }
206
207        LruCacheItem cacheItem = new LruCacheItem(key, value);
208        LinkedListNode<LruCacheItem> node = new LinkedListNode<LruCacheItem>(cacheItem);
209        lruList.AddLast(node);
210        cacheMap.Add(key, node);
211      }
212    }
213
214    public void Remove(TKey key) {
215      LinkedListNode<LruCacheItem> node;
216      if (cacheMap.TryGetValue(key, out node)) {
217        lruList.Remove(node);
218        cacheMap.Remove(key);
219      }
220      dispose?.Invoke(node.Value.Value);
221    }
222
223    private void RemoveFirst() {
224      // Remove from LRUPriority
225      LinkedListNode<LruCacheItem> node = lruList.First;
226      lruList.RemoveFirst();
227
228      // Remove from cache
229      cacheMap.Remove(node.Value.Key);
230
231      // dispose
232      dispose?.Invoke(node.Value.Value);
233    }
234
235    private class LruCacheItem : DeepCloneable {
236      public LruCacheItem(TKey k, TValue v) {
237        Key = k;
238        Value = v;
239      }
240
241      protected LruCacheItem(LruCacheItem original, Cloner cloner) : base(original, cloner) {
242        Key = original.Key;
243        Value = original.Value;
244      }
245
246      public TKey Key { get; }
247
248      public TValue Value { get; }
249
250      public override IDeepCloneable Clone(Cloner cloner) {
251        return new LruCacheItem(this, cloner);
252      }
253    }
254
255    #region implement DeepCloneable abstract methods
256    public override IDeepCloneable Clone(Cloner cloner) {
257      return new LruCache<TKey, TValue>(this, cloner);
258    }
259
260    public bool ContainsKey(TKey key) {
261      return cacheMap.ContainsKey(key);
262    }
263
264    bool IDictionary<TKey, TValue>.Remove(TKey key) {
265      return cacheMap.Remove(key);
266    }
267
268    public void Add(KeyValuePair<TKey, TValue> item) {
269      Add(item.Key, item.Value);
270    }
271
272    public void Clear() {
273      cacheMap.Clear();
274      lruList.Clear();
275    }
276
277    public bool Contains(KeyValuePair<TKey, TValue> item) {
278      return cacheMap.Select(x => new KeyValuePair<TKey, TValue>(x.Key, x.Value.Value.Value)).Contains(item);
279    }
280
281    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
282      throw new NotImplementedException();
283    }
284
285    public bool Remove(KeyValuePair<TKey, TValue> item) {
286      var ret = cacheMap.ContainsKey(item.Key);
287      Remove(item.Key);
288      return ret;
289    }
290
291    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
292      return cacheMap.Select(x => new KeyValuePair<TKey, TValue>(x.Key, x.Value.Value.Value)).GetEnumerator();
293    }
294
295    IEnumerator IEnumerable.GetEnumerator() {
296      return GetEnumerator();
297    }
298    #endregion
299  }
300}
Note: See TracBrowser for help on using the repository browser.