use super::{BagItemTable, BagNameTable, Distribute, Distributor, NameValue};
use crate::{
entity::{Item, MergeOrder, ShortFloat},
global::Float,
inference::{Budget, BudgetFunctions, BudgetInference},
parameters::{Parameters, DEFAULT_PARAMETERS},
util::ToDisplayAndBrief,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Bag<E: Item> {
distributor: Distributor,
item_map: BagNameTable<E>,
level_map: BagItemTable,
#[serde(flatten)]
parameters: BagParameters,
#[serde(flatten)]
status: BagStatus,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct BagParameters {
capacity: usize,
forget_rate: usize,
#[serde(default = "default::total_level")]
total_level: usize,
#[serde(default = "default::threshold")]
threshold: usize,
#[serde(default = "default::relative_threshold")]
relative_threshold: Float,
#[serde(default = "default::load_factor")]
load_factor: Float,
}
mod default {
use super::BagParameters;
use crate::{global::Float, parameters::DEFAULT_PARAMETERS};
pub const fn total_level() -> usize {
DEFAULT_PARAMETERS.bag_level
}
pub const fn threshold() -> usize {
DEFAULT_PARAMETERS.bag_threshold
}
pub fn relative_threshold() -> Float {
const TOTAL_LEVEL: usize = total_level();
const THRESHOLD: usize = threshold();
BagParameters::calculate_relative_threshold(TOTAL_LEVEL, THRESHOLD)
}
pub const fn load_factor() -> Float {
DEFAULT_PARAMETERS.load_factor
}
}
impl BagParameters {
fn calculate_relative_threshold(total_level: usize, threshold: usize) -> Float {
threshold as Float / total_level as Float
}
fn from_parameters(capacity: usize, forget_rate: usize, parameters: &Parameters) -> Self {
let &Parameters {
bag_level: total_level,
bag_threshold: threshold,
load_factor,
..
} = parameters;
let relative_threshold = Self::calculate_relative_threshold(total_level, threshold);
Self {
capacity,
forget_rate,
total_level,
threshold,
relative_threshold,
load_factor,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
struct BagStatus {
mass: usize,
level_index: usize,
current_level: usize,
current_counter: usize,
}
impl<E: Item> Bag<E> {
pub fn from_parameters(capacity: usize, forget_rate: usize, parameters: &Parameters) -> Self {
Self::with_parameters(BagParameters::from_parameters(
capacity,
forget_rate,
parameters,
))
}
pub fn new(capacity: usize, forget_rate: usize) -> Self {
let parameters = BagParameters::from_parameters(capacity, forget_rate, &DEFAULT_PARAMETERS);
Self::with_parameters(parameters)
}
fn with_parameters(parameters: BagParameters) -> Self {
let mut this = Self {
distributor: Distributor::new(parameters.total_level),
item_map: BagNameTable::default(),
level_map: BagItemTable::default(),
status: BagStatus::default(),
parameters,
};
this.init();
this
}
}
impl<E: Item> Bag<E> {
pub fn capacity(&self) -> usize {
self.parameters.capacity
}
fn mass(&self) -> usize {
self.status.mass
}
pub fn init(&mut self) {
self.level_map = BagItemTable::new(self.parameters.total_level);
for level in 0..self.parameters.total_level {
self.level_map.add_new(level);
}
self.item_map = BagNameTable::new();
self.status = BagStatus {
current_level: self.parameters.total_level - 1,
level_index: self.capacity() % self.parameters.total_level, mass: 0,
current_counter: 0,
}; }
#[inline(always)]
pub fn size(&self) -> usize {
self.item_map.size()
}
pub fn average_priority(&self) -> Float {
if self.size() == 0 {
return 0.01;
}
Float::min(
(self.mass() as Float) / (self.size() * self.parameters.total_level) as Float,
1.0,
)
}
pub fn iter(&self) -> impl Iterator<Item = &E> {
self.item_map.iter_items()
}
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut E> {
self.item_map.iter_items_mut()
}
#[inline(always)]
pub fn contains(&self, item: &E) -> bool {
self.get(item.key()).is_some()
}
#[inline(always)]
#[must_use]
pub fn get(&self, key: &str) -> Option<&E> {
self.item_map.get(key).map(|(e, _)| e)
}
#[inline(always)]
#[must_use]
pub fn get_mut(&mut self, key: &str) -> Option<&mut E> {
self.item_map.get_mut(key).map(|(e, _)| e)
}
pub fn has(&self, key: &str) -> bool {
self.item_map.has(key)
}
#[must_use]
pub fn put_in(&mut self, new_item: E) -> Option<E> {
self.assert_valid();
let new_key = new_item.key().clone();
let level = self.calculate_level_for_item(&new_item);
let old_item = self.item_map.put(&new_key, new_item, level);
if let Some(old) = old_item {
self.item_out_of_base(&old);
let (mut old_item, _) = old;
let new_item = self.get(&new_key).unwrap(); let merge_order = old_item.merge_order(new_item); let new_item = self.get_mut(&new_key).unwrap();
use MergeOrder::*;
match merge_order {
OldToNew => new_item.merge_from(&old_item),
NewToOld => old_item.merge_from(new_item),
}
}
if let Some(overflow_key) = self.item_into_base(&new_key) {
let overflow_item = self.item_map.remove_item(&overflow_key);
self.assert_valid();
match overflow_key == new_key {
true => overflow_item,
false => None, }
} else {
self.assert_valid();
None
}
}
#[must_use]
pub fn put_back(&mut self, mut old_item: E) -> Option<E> {
self.assert_valid();
self.forget(&mut old_item);
self.put_in(old_item)
}
pub fn forget(&self, item: &mut impl Budget) {
let new_priority = item.forget(
self.parameters.forget_rate as Float,
self.parameters.relative_threshold,
);
item.set_priority(ShortFloat::from_float(new_priority));
}
#[must_use]
pub fn take_out(&mut self) -> Option<E> {
self.assert_valid();
if self.item_map.is_empty() {
return None;
}
let level = self.select_next_level_for_take();
let selected_key = self.take_out_first(level);
let selected = selected_key.and_then(|key| self.item_map.remove_item(&key));
self.assert_valid(); selected
}
pub fn peek(&self) -> Option<&String> {
if self.item_map.is_empty() {
return None;
}
let (level, ..) = self.calculate_next_level();
self.peek_first(level)
}
fn select_next_level_for_take(&mut self) -> usize {
(
self.status.current_level,
self.status.level_index,
self.status.current_counter,
) = self.calculate_next_level();
self.status.current_level
}
#[inline]
fn calculate_next_level(&self) -> (usize, usize, usize) {
let BagStatus {
mut current_level,
mut level_index,
mut current_counter,
..
} = self.status;
if self.empty_level(current_level) || current_counter == 0 {
current_level = self.distributor.pick(level_index);
level_index = self.distributor.next(level_index);
while self.empty_level(current_level) {
current_level = self.distributor.pick(level_index);
level_index = self.distributor.next(level_index);
}
current_counter = match current_level < self.parameters.threshold {
true => 1,
false => self.level_map.get(current_level).size(),
};
}
current_counter -= 1;
(current_level, level_index, current_counter)
}
#[must_use]
pub fn pick_out(&mut self, key: &str) -> Option<E> {
let name_value = self.item_map.remove(key)?;
self.item_out_of_base(&name_value);
self.assert_valid();
Some(name_value.0)
}
pub fn empty_level(&self, level: usize) -> bool {
self.level_map.get(level).is_empty()
}
#[doc(alias = "level_from_item")]
fn calculate_level_for_item(&self, item: &E) -> usize {
let fl = item.priority().to_float() * self.parameters.total_level as Float;
let level = (fl.ceil()) as usize; level.saturating_sub(1)
}
fn item_into_base(&mut self, new_key: &str) -> Option<String> {
let new_item = self.get(new_key).expect("不能没有所要获取的值"); let mut old_item = None;
let in_level = self.calculate_level_for_item(new_item);
self.status.mass += in_level + 1;
if self.size() > self.capacity() {
let out_level = (0..self.parameters.total_level)
.find(|level| !self.empty_level(*level))
.unwrap_or(self.parameters.total_level);
if out_level > in_level {
self.status.mass -= in_level + 1; return Some(new_key.to_string()); } else {
old_item = self.take_out_first(out_level);
}
}
self.level_map.get_mut(in_level).add(new_key.to_string());
old_item
}
fn peek_first(&self, level: usize) -> Option<&String> {
self.level_map.get(level).get_first()
}
fn take_out_first(&mut self, level: usize) -> Option<String> {
let selected = self.level_map.get(level).get_first().cloned();
if selected.is_some() {
self.level_map.get_mut(level).remove_first();
self.status.mass -= level + 1;
}
selected
}
fn item_out_of_base(&mut self, (old_item, level): &NameValue<E>) {
self.level_map.remove_element(old_item.key());
self.status.mass -= level + 1;
}
fn debug_display(&self) -> String {
format!(
"level_map: \n{:?}\n\nitem_map: \n{}\n\nbag: \n{}",
self.level_map,
self.item_map.debug_display(),
self.bag_to_display(),
)
}
fn assert_valid(&self) {
if cfg!(debug_assertions) {
self.assert_count_consistent();
self.assert_unique_level_map();
}
}
#[inline(always)]
fn assert_count_consistent(&self) {
let l_count = self.level_map.count();
let n_count = self.size();
assert_eq!(
l_count,
n_count,
"层级映射与物品映射数目不一致: {l_count} != {n_count}\n{}",
self.debug_display()
);
}
#[inline(always)]
fn assert_unique_level_map(&self) {
for (key, _) in self.item_map.iter() {
debug_assert!(
1 == self
.level_map
.iter()
.map(|l| l.iter().filter(|k| *k == key).count())
.sum::<usize>(),
"发现重复元素:{key}\nlevel_map: \n{}",
self.debug_display()
);
}
}
pub fn bag_to_display(&self) -> String {
let mut buf = String::new();
for level in (0..self.parameters.total_level)
.rev()
.filter(|&level| !self.empty_level(level))
{
buf += "\n --- Level ";
buf += &level.to_string();
buf += ":\n ";
let level_size = self.level_map.get(level).size();
for i in 0..level_size {
let key = self.level_map.get(level).get(i);
if let Some(key) = key {
if let Some(item) = self.get(key) {
buf += &item.to_display_brief();
buf += "\n "
} else {
buf += "!!!NONE@{key}!!!\n"
}
}
}
}
buf
}
}
impl<E: Item> ToDisplayAndBrief for Bag<E> {
fn to_display(&self) -> String {
self.bag_to_display()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::global::Float;
use crate::{
entity::{BudgetValue, ShortFloat, Token},
inference::Budget,
ok,
util::{AResult, ToDisplayAndBrief},
};
use nar_dev_utils::{asserts, list};
type ItemV1 = Token;
fn new_item(key: impl Into<String>, p: Float, d: Float, q: Float) -> ItemV1 {
ItemV1::new(key.into(), BudgetValue::from_floats(p, d, q))
}
type Item1 = ItemV1;
type Bag1 = Bag<Item1>;
#[test]
fn single_item() -> AResult {
let mut bag = Bag1::new(1, 1);
dbg!(&bag);
bag.init();
dbg!(&bag);
asserts! {
bag.size() == 0, bag.mass() == 0, bag.empty_level(0) => true, }
let key1 = "item001";
let item1 = new_item(key1, 0.0, 0.0, 0.0); let overflowed = bag.put_in(dbg!(item1.clone()));
asserts! {
overflowed.is_none(), bag.get(key1) == Some(&item1), bag.size() == 1, bag.calculate_level_for_item(&item1) => 0, bag.empty_level(0) => false, bag.mass() == 1, }
dbg!(&bag);
let picked = bag.pick_out(key1).unwrap();
asserts! {
picked == item1, bag.size() == 0, bag.mass() == 0, bag.empty_level(0) => true, }
let overflowed = bag.put_back(picked);
asserts! {
overflowed => None, bag.size() == 1, bag.empty_level(0) => false, bag.mass() == 1, }
let peeked = bag.peek().unwrap().clone();
let mut taken = bag.take_out().unwrap();
asserts! {
taken == item1, peeked == *taken.key(), bag.size() == 0, bag.mass() == 0, bag.empty_level(0) => true, }
taken.budget_mut().set_priority(ShortFloat::ONE);
taken.budget_mut().set_durability(ShortFloat::ONE);
asserts! {
taken.budget_mut().priority() == ShortFloat::ONE,
taken.budget_mut().durability() == ShortFloat::ONE,
}
let overflowed = bag.put_back(taken);
asserts! {
overflowed => None, bag.size() == 1, bag.empty_level(0) => true, bag.empty_level(bag.parameters.total_level-1) => false, bag.mass() == bag.parameters.total_level, }
ok!()
}
#[test]
fn multi_item() -> AResult {
let mut bag = Bag1::new(
DEFAULT_PARAMETERS.concept_bag_size,
DEFAULT_PARAMETERS.concept_forgetting_cycle,
);
bag.init();
dbg!(&bag);
asserts! {
bag.size() == 0, bag.empty_level(0) => true, }
const N: usize = 10;
let key_f = |i| format!("item{:03}", i);
let priority = |i| i as Float / N as Float;
let total_level = bag.parameters.total_level;
let expected_level = |i| {
let level_percent = priority(i) as Float * total_level as Float;
(level_percent.ceil() as usize).saturating_sub(1)
};
let items = list![
{
let key = key_f(i);
let priority = priority(i);
let durability = 0.5;
let quality = 0.5;
let item = new_item(key.clone(), priority, durability, quality);
(key, item)
}
for i in (0..=N)
];
for (i, (key, item)) in items.iter().enumerate() {
let overflowed = bag.put_in(item.clone());
asserts! {
overflowed.is_none(), bag.get(key) == Some(item), bag.size() == i + 1, bag.calculate_level_for_item(item) => expected_level(i), bag.empty_level(expected_level(i)) => false, }
}
println!("初次放入后:{bag:#?}");
let mut picked_items = vec![];
for (i, (key, item)) in items.iter().enumerate() {
let picked = bag.pick_out(key).unwrap();
asserts! {
picked == *item, bag.size() == N - i, bag.empty_level(expected_level(i)) => true, }
picked_items.push(picked);
}
for (i, picked) in picked_items.into_iter().enumerate() {
let overflowed = bag.put_back(picked); asserts! {
overflowed => None, bag.size() == i + 1, }
}
println!("第一次放回后:{bag:#?}");
let mut taken_items = vec![];
for i in 0..=N {
let peeked = bag.peek().unwrap().clone();
let taken = bag.take_out().unwrap(); asserts! {
peeked == *taken.key() bag.size() == N - i, }
taken_items.push(dbg!(taken));
}
for (i, taken) in taken_items.into_iter().enumerate() {
let _ = bag.put_back(taken);
asserts! {
bag.size() == i + 1, }
}
println!("第二次放回后:{bag:#?}");
ok!()
}
#[test]
fn long_term() -> AResult {
const N: usize = 100;
let mut bag = Bag1::new(10, N);
bag.init();
dbg!(&bag);
asserts! {
bag.size() == 0, bag.mass() == 0, }
let key = "item";
let budget_initial = BudgetValue::new(ShortFloat::ONE, ShortFloat::HALF, ShortFloat::ONE);
let item = Item1::new(key, budget_initial);
let overflowed = bag.put_in(dbg!(item.clone()));
asserts! {
overflowed.is_none(), bag.get(key) == Some(&item), bag.size() == 1, bag.mass() >= 1, }
dbg!(&bag);
println!("budget trending from {budget_initial}:");
for _ in 0..N {
let taken = bag.take_out().unwrap();
asserts! {
bag.size() == 0, bag.mass() == 0, };
println!("\t{}", taken.budget());
let overflowed = bag.put_back(taken);
assert_eq!(
overflowed,
None )
}
println!("{}", bag.to_display_long());
ok!()
}
#[test]
fn modified_level_in_bag() -> AResult {
let mut bag = Bag1::new(1, 1);
bag.init();
let key = "item001";
let item = new_item(key, 0.0, 0.0, 0.0); let overflowed = bag.put_in(dbg!(item.clone()));
asserts! {
overflowed.is_none(), bag.get(key) == Some(&item), bag.size() == 1, bag.calculate_level_for_item(&item) => 0, bag.empty_level(0) => false, bag.mass() == 1, }
dbg!(&bag);
let item_mut = bag.get_mut(key).expect("此时袋内必须有物品");
item_mut.set_priority(ShortFloat::ONE);
let picked = bag.pick_out(key).unwrap();
asserts! {
bag.size() == 0, bag.mass() == 0, bag.empty_level(0) => true, }
let overflowed = bag.put_back(picked);
asserts! {
overflowed => None, bag.size() == 1, bag.empty_level(0) => false, bag.mass() == 1, }
let item_mut = bag.get_mut(key).expect("此时袋内必须有物品");
item_mut.set_priority(ShortFloat::HALF);
let taken = bag.take_out().unwrap();
asserts! {
taken.priority() == ShortFloat::HALF,
bag.size() == 0, bag.mass() == 0, bag.empty_level(0) => true, }
ok!()
}
}