use std::collections::{HashMap, VecDeque};
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};
use crate::traits::{FitnessFn, GeneT};
pub struct FitnessCache {
map: HashMap<u64, f64>,
order: VecDeque<u64>,
capacity: usize,
hits: u64,
misses: u64,
}
impl FitnessCache {
pub fn new(capacity: usize) -> Self {
FitnessCache {
map: HashMap::with_capacity(capacity),
order: VecDeque::with_capacity(capacity),
capacity,
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, key: u64) -> Option<f64> {
if let Some(&value) = self.map.get(&key) {
self.hits += 1;
self.order.retain(|&k| k != key);
self.order.push_back(key);
Some(value)
} else {
self.misses += 1;
None
}
}
pub fn put(&mut self, key: u64, value: f64) {
if let Some(existing) = self.map.get_mut(&key) {
*existing = value;
self.order.retain(|&k| k != key);
self.order.push_back(key);
return;
}
if self.map.len() >= self.capacity {
if let Some(evicted) = self.order.pop_front() {
self.map.remove(&evicted);
}
}
self.map.insert(key, value);
self.order.push_back(key);
}
pub fn hits(&self) -> u64 {
self.hits
}
pub fn misses(&self) -> u64 {
self.misses
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}
pub fn hash_dna<G: Debug>(dna: &[G]) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for gene in dna {
let repr = format!("{:?}", gene);
repr.hash(&mut hasher);
}
hasher.finish()
}
pub fn wrap_with_cache<G>(
fitness_fn: Arc<FitnessFn<G>>,
cache_size: usize,
) -> Arc<FitnessFn<G>>
where
G: GeneT + Debug + 'static,
{
let cache = Arc::new(Mutex::new(FitnessCache::new(cache_size)));
Arc::new(move |dna: &[G]| {
let key = hash_dna(dna);
{
let mut cache = cache.lock().expect("fitness cache lock poisoned");
if let Some(fitness) = cache.get(key) {
return fitness;
}
}
let fitness = fitness_fn(dna);
{
let mut cache = cache.lock().expect("fitness cache lock poisoned");
cache.put(key, fitness);
}
fitness
})
}