diff --git a/lab3/src/algo/ant.rs b/lab3/src/algo/ant.rs new file mode 100644 index 0000000..f5229cc --- /dev/null +++ b/lab3/src/algo/ant.rs @@ -0,0 +1,17 @@ +use bgtu_ai_utility::graph::VerticesVec; +use std::collections::HashSet; + +#[derive(Clone)] +pub(crate) struct Ant { + pub(super) allowed_vertices: HashSet, + pub(super) current_vertex: usize, +} + +impl Ant { + pub fn new(current_vertex: usize, allowed_vertices: HashSet) -> Self { + return Self { + allowed_vertices, + current_vertex, + }; + } +} diff --git a/lab3/src/algo/config.rs b/lab3/src/algo/config.rs new file mode 100644 index 0000000..7fa1e0f --- /dev/null +++ b/lab3/src/algo/config.rs @@ -0,0 +1,6 @@ +pub struct AntsSimulationConfig { + pub ferment_weight: f64, + pub heuristic_coefficient: f64, + pub q: f64, + pub r: f64, +} \ No newline at end of file diff --git a/lab3/src/algo/mod.rs b/lab3/src/algo/mod.rs index 03355d1..08325d9 100644 --- a/lab3/src/algo/mod.rs +++ b/lab3/src/algo/mod.rs @@ -1,12 +1,10 @@ mod state; +mod ant; +mod config; +mod update_state; pub use bgtu_ai_utility::graph::{VerticesVec}; use bgtu_ai_utility::graph::{Edge as _BaseEdge, EdgesVec as _BaseEdgesVec}; -pub struct EdgeExtraData { - pub(crate) ferment_intensity: f64 -} - -pub type Edge = _BaseEdge; -pub type EdgesVec = _BaseEdgesVec; -pub use state::{AntsSimulationConfig, AntsSimulationState, updateState, Ant}; \ No newline at end of file +pub(crate) use ant::Ant; +pub(crate) use config::AntsSimulationConfig; \ No newline at end of file diff --git a/lab3/src/algo/state.rs b/lab3/src/algo/state.rs index bcf6851..c15390e 100644 --- a/lab3/src/algo/state.rs +++ b/lab3/src/algo/state.rs @@ -1,132 +1,44 @@ -use super::{EdgeExtraData, EdgesVec}; -use rand::Rng; +use super::{Ant, AntsSimulationConfig}; +use bgtu_ai_utility::graph::CompleteGraph; use std::collections::HashSet; -use std::ops::Mul; -use bgtu_ai_utility::graph::{CompleteGraph, VerticesVec}; -#[derive(Clone, Copy)] -pub enum AntDirection { - ToSecond, - ToFirst, +pub struct AntsSimulationState<'cfg> { + pub graph: &'cfg CompleteGraph<()>, + pub cfg: &'cfg AntsSimulationConfig, + pub ants: Box<[Ant]>, + pub ferments: Box<[f64]>, } -#[derive(Clone)] -pub struct Ant { - allowed_vertices: HashSet, - location: AntLocation, -} +impl<'cfg> AntsSimulationState<'cfg> { + pub fn new( + graph: &'cfg CompleteGraph<()>, + cfg: &'cfg AntsSimulationConfig, + ants_per_vertex: usize, + ) -> Self { + let ants = Self::_create_ants(graph, ants_per_vertex); -impl Ant { - pub fn new(vertex: usize, allowed_vertices: HashSet) -> Self { return Self { - allowed_vertices: allowed_vertices, - location: AntLocation::OnVertex { - vertex_index: vertex, - }, + graph, + cfg, + ants: ants, + ferments: vec![0.0; graph.edges.capacity()].into_boxed_slice(), }; } -} -#[derive(Clone)] -pub enum AntLocation { - OnEdge { - edge_index: usize, - offset: u16, - direction: AntDirection, - }, - OnVertex { - vertex_index: usize, - }, -} - -pub struct AntsSimulationState { - pub graph: CompleteGraph, - pub ants: Vec, -} - -pub struct AntsSimulationConfig { - pub ferment_weight: f64, - pub heuristic_coefficient: f64, - pub q: f64, - pub r: f64, -} - -pub fn updateState( - state: &mut AntsSimulationState, - cfg: &AntsSimulationConfig, - rng: &mut impl Rng, -) { - _updateState(state, cfg, rng); - _updateState(state, cfg, rng); -} - -fn _updateState( - state: &mut AntsSimulationState, - cfg: &AntsSimulationConfig, - rng: &mut impl Rng, -) -> bool { - let mut finished_ants_count = 0usize; - for ant in state.ants.iter_mut() { - match &mut ant.location { - AntLocation::OnEdge { - edge_index, - offset, - direction, - } => { - let edge = &mut state.graph.edges[*edge_index]; - edge.extra.ferment_intensity += (cfg.q / edge.length) * cfg.r; - let vertex_index; - match direction { - AntDirection::ToSecond => vertex_index = edge.vertex2_index, - AntDirection::ToFirst => vertex_index = edge.vertex1_index, - } - ant.location = AntLocation::OnVertex { vertex_index } - } - AntLocation::OnVertex { vertex_index } => { - let allowed_outbounds = state.graph.vertices[*vertex_index] - .iter() - .map(|ei| (*ei, &state.graph.edges[*ei])) - .map(|(ei, e)| { - if *vertex_index == e.vertex1_index { - return (ei, e, e.vertex2_index, AntDirection::ToSecond); - } else { - return (ei, e, e.vertex1_index, AntDirection::ToFirst); - } - }) - .filter(|e| ant.allowed_vertices.contains(&e.2)) - .map(|e| { - return ( - e.0, - (e.2, e.3), - e.1.extra.ferment_intensity.powf(cfg.ferment_weight) - * (1.0 / e.1.length).powf(cfg.heuristic_coefficient), - ); - }) - .collect::>(); - - let mut accumulator: f64 = allowed_outbounds - .iter() - .fold(0.0, |a, e| a + e.2) - .mul(rng.random::()); - let target_vertex_index = allowed_outbounds.iter().find(|e| { - accumulator -= e.2; - return accumulator <= 0.0; - }); - - match target_vertex_index { - None => finished_ants_count += 1, - Some((ei, (vi, dir), _)) => { - ant.allowed_vertices.remove(vi); - ant.location = AntLocation::OnEdge { - edge_index: *ei, - offset: 0, - direction: *dir, - } - } - }; - } + fn _create_ants(graph: &'cfg CompleteGraph<()>, ants_per_vertex: usize) -> Box<[Ant]> { + let mut ants = Vec::new(); + let mut allowed_vertices_set = HashSet::::new(); + for (vi, _) in graph.vertices.iter_indexed() { + allowed_vertices_set.insert(vi); + } + for (vi, _) in graph.vertices.iter_indexed() { + allowed_vertices_set.remove(&vi); + for _ in 0..ants_per_vertex { + ants.push(Ant::new(vi, allowed_vertices_set.clone())) + } + allowed_vertices_set.insert(vi); } - } - return finished_ants_count == state.ants.len(); + return ants.into_boxed_slice(); + } } diff --git a/lab3/src/algo/update_state.rs b/lab3/src/algo/update_state.rs new file mode 100644 index 0000000..aea0e79 --- /dev/null +++ b/lab3/src/algo/update_state.rs @@ -0,0 +1,60 @@ +use crate::algo::Ant; +use crate::algo::state::AntsSimulationState; +use bgtu_ai_utility::weighted_random; +use rand::Rng; +use std::collections::HashSet; +use std::ptr; + +impl AntsSimulationState<'_> { + pub fn is_ended(&self) -> bool { + return self.ants.iter().all(|a| a.allowed_vertices.len() == 0); + } + + pub fn update1(&mut self, rng: &mut impl Rng) { + self._move_ants(rng); + self._evaporate_ferments(); + } + + fn _move_ants(&mut self, rng: &mut impl Rng) { + for ant in unsafe { (*ptr::from_mut(&mut self.ants)).iter_mut() } { + let outbound_weights = + self._get_outbounds_weights(ant.current_vertex, &ant.allowed_vertices); + + match weighted_random(rng, &outbound_weights, |e| e.2) { + None => {} + Some((ei, nvi, _)) => { + self.ferments[*ei] += self._calculate_weight(*ei); + ant.current_vertex = *nvi; + } + } + } + } + + fn _get_outbounds_weights( + &self, + location: usize, + allowed_outbounds: &HashSet, + ) -> Vec<(usize, usize, f64)> { + let mut weights = Vec::with_capacity(allowed_outbounds.len()); + for ei in self.graph.vertices[location].iter().map(|ei| *ei) { + let outbound = self.graph.edges[ei].another(location); + if !allowed_outbounds.contains(&outbound) { + continue; + } + weights.push((ei, outbound, self._calculate_weight(ei))) + } + return weights; + } + + fn _calculate_weight(&self, edge: usize) -> f64 { + let a = self.ferments[edge].powf(self.cfg.ferment_weight); + let b = (1.0 / self.graph.edges[edge].length).powf(self.cfg.heuristic_coefficient); + return a * b; + } + + fn _calculate_ferment(&self, edge: usize) -> f64 { + return self.cfg.q; + } + + fn _evaporate_ferments(&mut self) {} +} diff --git a/lab3/src/gui/data.rs b/lab3/src/gui/data.rs index 058c915..48369a3 100644 --- a/lab3/src/gui/data.rs +++ b/lab3/src/gui/data.rs @@ -7,6 +7,7 @@ pub(crate)enum GlobalState { Running, } pub(crate) struct MyApp { + pub graph: CompleteGraph<()>, pub simulation: AntsSimulationState, pub config: AntsSimulationConfig, pub vertex_update: UpdatePending, diff --git a/utility/src/lib.rs b/utility/src/lib.rs index e680279..22e0066 100644 --- a/utility/src/lib.rs +++ b/utility/src/lib.rs @@ -1,5 +1,7 @@ pub mod graph; pub mod gui; mod update_pending; +mod weighted_random; -pub use update_pending::{UpdatePending, UpdateTarget}; \ No newline at end of file +pub use update_pending::{UpdatePending, UpdateTarget}; +pub use weighted_random::{weighted_random, weighted_random_index}; diff --git a/utility/src/weighted_random.rs b/utility/src/weighted_random.rs new file mode 100644 index 0000000..be90b21 --- /dev/null +++ b/utility/src/weighted_random.rs @@ -0,0 +1,24 @@ +use rand::Rng; + +pub fn weighted_random_index( + rng: &mut impl Rng, + weights: &[E], + getter: impl Fn(&E) -> f64, +) -> Option { + let max: f64 = weights.iter().map(|w| getter(w)).sum(); + let ptr = max * rng.random::(); + + let mut accumulator = ptr; + return weights.iter().map(|w| getter(w)).position(|w| { + accumulator -= w; + return accumulator <= 0.0; + }); +} +pub fn weighted_random<'e, E>( + rng: &mut impl Rng, + weights: &'e [E], + getter: impl Fn(&E) -> f64, +) -> Option<&'e E> { + let index = weighted_random_index(rng, weights, getter); + return index.map(|i| &weights[i]); +}