diff options
| author | tslil <tslil@posteo.de> | 2026-07-14 20:22:56 +0100 |
|---|---|---|
| committer | tslil <tslil@posteo.de> | 2026-07-14 21:25:04 +0100 |
| commit | fc5789e026473ea82237be847672ecb350e64479 (patch) | |
| tree | 6210659eb424124b55bd67a63397a37931e50b5f /src/learner.rs | |
| parent | 156cbfe2b0383521f540109682d98d35c88d65f8 (diff) | |
license, simplify code, add README, lose "learner" naming
Diffstat (limited to 'src/learner.rs')
| -rw-r--r-- | src/learner.rs | 158 |
1 files changed, 0 insertions, 158 deletions
diff --git a/src/learner.rs b/src/learner.rs deleted file mode 100644 index 942d52e..0000000 --- a/src/learner.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use rand::{self, seq::IndexedRandom}; -use serde::{Deserialize, Serialize}; - -use crate::{config::Config, trajectory::Action}; - -fn compute_weight(past: &HashSet<String>, now: &HashSet<&String>) -> f32 { - let inter = now.iter().filter(|&&p| past.contains(p)).count(); - if inter == 0 { - return 0.0; - } - (inter as f32) / ((now.len() as f32).sqrt() * (past.len() as f32).sqrt()) -} - -fn softmax_sample(items: &[(String, f32)], temperature: f32) -> Option<(String, f32)> { - if items.is_empty() { - return None; - } - let mut rng = rand::rng(); - - let max_val = items - .iter() - .map(|(_, v)| *v) - .fold(f32::NEG_INFINITY, f32::max); - - let exp_values: Vec<_> = items - .iter() - .map(|(k, val)| (k, ((val - max_val) / temperature).exp())) - .collect(); - - let sum: f32 = exp_values.iter().map(|(_, v)| *v).sum(); - - let norm_values: Vec<_> = exp_values.iter().map(|&(k, v)| (k, v / sum)).collect(); - - norm_values - .choose_weighted(&mut rng, |item| item.1) - .map(|p| (p.0.clone(), p.1)) - .ok() -} - -#[derive(Serialize, Deserialize)] -enum Episode { - Continue { - group: HashSet<String>, - avoid: Option<String>, - committed: String, - }, - Escape { - from: HashSet<String>, - to: String, - committed: String, - }, -} - -#[derive(Serialize, Deserialize, Default)] -pub struct Learner { - history: Vec<Episode>, -} - -pub enum Learning { - SkipToMore(Vec<String>, String), - MoreToSkip(Vec<String>, String), -} - -impl Learner { - pub fn learn(&mut self, learning: &Learning) { - let stamp = chrono::Utc::now().format("%+").to_string(); - match learning { - Learning::SkipToMore(trajectory, new) => { - self.history.push(Episode::Escape { - from: trajectory.iter().map(String::clone).collect(), - to: new.clone(), - committed: stamp, - }); - } - Learning::MoreToSkip(trajectory, new) => { - self.history.push(Episode::Continue { - group: trajectory.iter().map(String::clone).collect(), - avoid: Some(new.clone()), - committed: stamp, - }); - } - } - } - - pub fn sample( - &self, - trajectory: &[String], - action: &Action, - candidates: &HashSet<String>, - config: &Config, - ) -> Option<(String, f32)> { - let trajectory: HashSet<_> = trajectory.iter().collect(); - - let candidates: Vec<_> = candidates - .iter() - .filter(|c| !trajectory.contains(c)) - .collect(); - - if candidates.is_empty() { - return None; - } - - let mut items: HashMap<String, f32> = - candidates.into_iter().map(|c| (c.clone(), 0.0)).collect(); - - for episode in &self.history { - match (episode, action) { - (Episode::Escape { from, to, .. }, Action::Skip) => { - let w = compute_weight(from, &trajectory); - if let Some(to_w) = items.get_mut(to) { - *to_w += config.max_weight * w; - } - for f in from { - if let Some(from_weight) = items.get_mut(f) { - *from_weight -= config.mid_weight * w; - } - } - } - (Episode::Escape { from, to, .. }, Action::More) => { - let w = compute_weight(from, &trajectory); - if trajectory.contains(to) { - for f in from { - if let Some(f_w) = items.get_mut(f) { - *f_w -= config.low_weight * w; - } - } - } - } - (Episode::Continue { group, avoid, .. }, Action::More) => { - let w = compute_weight(group, &trajectory); - for g in group { - if let Some(g_w) = items.get_mut(g) { - *g_w += config.max_weight * w; - } - } - if let Some(a) = avoid - && let Some(a_w) = items.get_mut(a) - { - *a_w -= config.low_weight * w; - } - } - (Episode::Continue { group, .. }, Action::Skip) => { - let w = compute_weight(group, &trajectory); - for g in group { - if let Some(v) = items.get_mut(g) { - *v -= config.high_weight * w; - } - } - } - } - } - - let pairs: Vec<_> = items.into_iter().collect(); - softmax_sample(&pairs, config.temperature) - } -} |
