[lab3] Total algo refactoring
This commit is contained in:
parent
50db706436
commit
088cf8d194
17
lab3/src/algo/ant.rs
Normal file
17
lab3/src/algo/ant.rs
Normal file
@ -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<usize>,
|
||||
pub(super) current_vertex: usize,
|
||||
}
|
||||
|
||||
impl Ant {
|
||||
pub fn new(current_vertex: usize, allowed_vertices: HashSet<usize>) -> Self {
|
||||
return Self {
|
||||
allowed_vertices,
|
||||
current_vertex,
|
||||
};
|
||||
}
|
||||
}
|
||||
6
lab3/src/algo/config.rs
Normal file
6
lab3/src/algo/config.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub struct AntsSimulationConfig {
|
||||
pub ferment_weight: f64,
|
||||
pub heuristic_coefficient: f64,
|
||||
pub q: f64,
|
||||
pub r: f64,
|
||||
}
|
||||
@ -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<EdgeExtraData>;
|
||||
pub type EdgesVec = _BaseEdgesVec<EdgeExtraData>;
|
||||
pub use state::{AntsSimulationConfig, AntsSimulationState, updateState, Ant};
|
||||
pub(crate) use ant::Ant;
|
||||
pub(crate) use config::AntsSimulationConfig;
|
||||
@ -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<usize>,
|
||||
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<usize>) -> 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<EdgeExtraData>,
|
||||
pub ants: Vec<Ant>,
|
||||
}
|
||||
|
||||
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::<Vec<(usize, (usize, AntDirection), f64)>>();
|
||||
|
||||
let mut accumulator: f64 = allowed_outbounds
|
||||
.iter()
|
||||
.fold(0.0, |a, e| a + e.2)
|
||||
.mul(rng.random::<f64>());
|
||||
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,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finished_ants_count == state.ants.len();
|
||||
|
||||
fn _create_ants(graph: &'cfg CompleteGraph<()>, ants_per_vertex: usize) -> Box<[Ant]> {
|
||||
let mut ants = Vec::new();
|
||||
let mut allowed_vertices_set = HashSet::<usize>::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 ants.into_boxed_slice();
|
||||
}
|
||||
}
|
||||
|
||||
60
lab3/src/algo/update_state.rs
Normal file
60
lab3/src/algo/update_state.rs
Normal file
@ -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<usize>,
|
||||
) -> 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) {}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
pub mod graph;
|
||||
pub mod gui;
|
||||
mod update_pending;
|
||||
mod weighted_random;
|
||||
|
||||
pub use update_pending::{UpdatePending, UpdateTarget};
|
||||
pub use weighted_random::{weighted_random, weighted_random_index};
|
||||
|
||||
24
utility/src/weighted_random.rs
Normal file
24
utility/src/weighted_random.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use rand::Rng;
|
||||
|
||||
pub fn weighted_random_index<E>(
|
||||
rng: &mut impl Rng,
|
||||
weights: &[E],
|
||||
getter: impl Fn(&E) -> f64,
|
||||
) -> Option<usize> {
|
||||
let max: f64 = weights.iter().map(|w| getter(w)).sum();
|
||||
let ptr = max * rng.random::<f64>();
|
||||
|
||||
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]);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user