[lab3] Total algo refactoring

This commit is contained in:
Andrew Golovashevich 2026-02-16 20:57:29 +03:00
parent 50db706436
commit 088cf8d194
8 changed files with 148 additions and 128 deletions

17
lab3/src/algo/ant.rs Normal file
View 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
View File

@ -0,0 +1,6 @@
pub struct AntsSimulationConfig {
pub ferment_weight: f64,
pub heuristic_coefficient: f64,
pub q: f64,
pub r: f64,
}

View File

@ -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;

View File

@ -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,
}
}
};
}
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 finished_ants_count == state.ants.len();
return ants.into_boxed_slice();
}
}

View 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) {}
}

View File

@ -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,

View File

@ -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};

View 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]);
}