use delegate::delegate;
use fxhash::FxHashMap;
use priority_queue::DoublePriorityQueue;
use crate::circuit::CircuitHash;
use crate::Circuit;
#[derive(Debug, Clone, Default)]
pub struct HugrPQ<P: Ord, C> {
queue: DoublePriorityQueue<u64, P>,
hash_lookup: FxHashMap<u64, Circuit>,
cost_fn: C,
max_size: usize,
}
pub struct Entry<C, P, H> {
pub circ: C,
pub cost: P,
pub hash: H,
}
impl<P: Ord, C> HugrPQ<P, C> {
pub fn new(cost_fn: C, max_size: usize) -> Self {
Self {
queue: DoublePriorityQueue::with_capacity(max_size),
hash_lookup: Default::default(),
cost_fn,
max_size,
}
}
#[allow(unused)]
pub fn peek(&self) -> Option<Entry<&Circuit, &P, u64>> {
let (hash, cost) = self.queue.peek_min()?;
let circ = self.hash_lookup.get(hash)?;
Some(Entry {
circ,
cost,
hash: *hash,
})
}
#[allow(unused)]
pub fn push(&mut self, circ: Circuit)
where
C: Fn(&Circuit) -> P,
{
let hash = circ.circuit_hash(circ.parent()).unwrap();
let cost = (self.cost_fn)(&circ);
self.push_unchecked(circ, hash, cost);
}
pub fn push_unchecked(&mut self, circ: Circuit, hash: u64, cost: P)
where
C: Fn(&Circuit) -> P,
{
if !self.check_accepted(&cost) {
return;
}
if self.len() >= self.max_size {
self.pop_max();
}
self.queue.push(hash, cost);
self.hash_lookup.insert(hash, circ);
}
pub fn pop(&mut self) -> Option<Entry<Circuit, P, u64>> {
let (hash, cost) = self.queue.pop_min()?;
let circ = self.hash_lookup.remove(&hash)?;
Some(Entry { circ, cost, hash })
}
pub fn pop_max(&mut self) -> Option<Entry<Circuit, P, u64>> {
let (hash, cost) = self.queue.pop_max()?;
let circ = self.hash_lookup.remove(&hash)?;
Some(Entry { circ, cost, hash })
}
#[allow(unused)]
pub fn truncate(&mut self, max_size: usize) {
while self.queue.len() > max_size {
let (hash, _) = self.queue.pop_max().unwrap();
self.hash_lookup.remove(&hash);
}
}
#[allow(unused)]
pub fn cost_fn(&self) -> &C {
&self.cost_fn
}
pub fn max_cost(&self) -> Option<&P> {
self.queue.peek_max().map(|(_, cost)| cost)
}
pub fn check_accepted(&self, cost: &P) -> bool {
if self.max_size == 0 {
return false;
}
if self.len() < self.max_size {
return true;
}
cost < self.max_cost().unwrap()
}
pub fn is_full(&self) -> bool {
self.queue.len() >= self.max_size
}
delegate! {
to self.queue {
pub fn len(&self) -> usize;
pub fn is_empty(&self) -> bool;
}
}
}