[lab5] Simulation and visualization
This commit is contained in:
parent
0e735f4db5
commit
dab2d16e38
3
lab5/src/algo/config.rs
Normal file
3
lab5/src/algo/config.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub struct GeneticSimulationConfig {
|
||||
pub mutation_chance: f64,
|
||||
}
|
||||
@ -1,3 +1,7 @@
|
||||
mod product;
|
||||
mod mutate;
|
||||
mod simulation;
|
||||
mod simulation;
|
||||
mod config;
|
||||
|
||||
pub use config::GeneticSimulationConfig;
|
||||
pub use simulation::GeneticSimulationState;
|
||||
@ -0,0 +1,85 @@
|
||||
use crate::algo::config::GeneticSimulationConfig;
|
||||
use crate::algo::mutate::mutate;
|
||||
use crate::algo::product::product;
|
||||
use bgtu_ai_utility::graph::CompleteGraph;
|
||||
use bgtu_ai_utility::weighted_random_index;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
pub struct GeneticSimulationState<'a> {
|
||||
pub config: &'a GeneticSimulationConfig,
|
||||
pub graph: &'a CompleteGraph<()>,
|
||||
pub chromosome_size: usize,
|
||||
pub population: Vec<Vec<usize>>,
|
||||
}
|
||||
|
||||
impl<'a> GeneticSimulationState<'a> {
|
||||
pub fn new(
|
||||
config: &'a GeneticSimulationConfig,
|
||||
graph: &'a CompleteGraph<()>,
|
||||
chromosome_size: usize,
|
||||
population_size: usize,
|
||||
rng: &mut impl Rng,
|
||||
) -> Self {
|
||||
let mut population = vec![vec![0; chromosome_size]; population_size];
|
||||
for chromosome in population.iter_mut() {
|
||||
for (i, gene) in chromosome.iter_mut().enumerate() {
|
||||
*gene = i;
|
||||
}
|
||||
chromosome.shuffle(rng)
|
||||
}
|
||||
|
||||
return Self {
|
||||
config,
|
||||
graph,
|
||||
chromosome_size,
|
||||
population,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next_generation(&mut self, rng: &mut impl Rng) {
|
||||
let mut new_generation = Vec::new();
|
||||
let weights = self.calculate_population_weights(self.population.as_slice());
|
||||
|
||||
for _ in 0..self.population.len() {
|
||||
let mut new_chromosome = vec![0; self.chromosome_size];
|
||||
|
||||
product(
|
||||
&self.population[weighted_random_index(rng, weights.as_slice(), |w| *w).unwrap()],
|
||||
&self.population[weighted_random_index(rng, weights.as_slice(), |w| *w).unwrap()],
|
||||
&mut new_chromosome,
|
||||
rng,
|
||||
);
|
||||
|
||||
new_generation.push(new_chromosome);
|
||||
}
|
||||
|
||||
for h in new_generation.iter_mut() {
|
||||
if rng.random::<f64>() < self.config.mutation_chance {
|
||||
mutate(h.as_mut(), rng);
|
||||
}
|
||||
}
|
||||
|
||||
self.population = new_generation;
|
||||
}
|
||||
|
||||
fn calculate_population_weights(&self, population: &[Vec<usize>]) -> Vec<f64> {
|
||||
return population
|
||||
.iter()
|
||||
.map(|h| {
|
||||
let mut weight = 0.0;
|
||||
h.iter().reduce(|a, b| {
|
||||
match self.graph.vertices[*a]
|
||||
.iter()
|
||||
.find(|e| self.graph.edges[**e].another(*a) == *b)
|
||||
{
|
||||
None => unreachable!(),
|
||||
Some(e) => weight += self.graph.edges[*e].length,
|
||||
};
|
||||
return b;
|
||||
});
|
||||
return 1.0 / weight;
|
||||
})
|
||||
.collect::<Vec<f64>>();
|
||||
}
|
||||
}
|
||||
52
lab5/src/gui/data.rs
Normal file
52
lab5/src/gui/data.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::algo::{GeneticSimulationConfig, GeneticSimulationState};
|
||||
use bgtu_ai_utility::UpdatePending;
|
||||
use bgtu_ai_utility::graph::CompleteGraph;
|
||||
use std::pin::Pin;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub(crate) struct GeneticVisualisationData {
|
||||
pub config: GeneticSimulationConfig,
|
||||
pub graph: CompleteGraph<()>,
|
||||
}
|
||||
|
||||
pub(crate) enum GeneticVisualisationState<'cfg> {
|
||||
Edit {
|
||||
config: &'cfg mut GeneticSimulationConfig,
|
||||
graph: &'cfg mut CompleteGraph<()>,
|
||||
vertex_update: UpdatePending,
|
||||
population_size: usize,
|
||||
generations_count: usize,
|
||||
},
|
||||
Running {
|
||||
simulation: GeneticSimulationState<'cfg>,
|
||||
generations_done: usize,
|
||||
generations_count: usize,
|
||||
vertex_locations: Vec<(f32, f32)>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) struct GeneticVisualisationApp {
|
||||
pub data: Pin<Box<GeneticVisualisationData>>,
|
||||
pub state: GeneticVisualisationState<'static /* actually not but fuck rust */>,
|
||||
}
|
||||
|
||||
impl GeneticVisualisationApp {
|
||||
pub fn new() -> Self {
|
||||
let mut data = Box::pin(GeneticVisualisationData {
|
||||
config: GeneticSimulationConfig {
|
||||
mutation_chance: 0.1,
|
||||
},
|
||||
graph: CompleteGraph::new(),
|
||||
});
|
||||
|
||||
let state = GeneticVisualisationState::Edit {
|
||||
config: unsafe { NonNull::from_mut(&mut data.config).as_mut() },
|
||||
graph: unsafe { NonNull::from_mut(&mut data.graph).as_mut() },
|
||||
population_size: 1,
|
||||
generations_count: 2,
|
||||
vertex_update: UpdatePending::NoChange,
|
||||
};
|
||||
|
||||
return Self { data, state };
|
||||
}
|
||||
}
|
||||
69
lab5/src/gui/graph.rs
Normal file
69
lab5/src/gui/graph.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use crate::algo::GeneticSimulationState;
|
||||
use bgtu_ai_utility::gui::render_graph;
|
||||
use eframe::egui::Ui;
|
||||
use rand::{Rng, rng};
|
||||
|
||||
pub(crate) fn graph_with_controls(
|
||||
ui: &mut Ui,
|
||||
vertex_locations: &mut [(f32, f32)],
|
||||
data: &mut GeneticSimulationState,
|
||||
generations_left: &mut usize,
|
||||
exit: &mut bool,
|
||||
) {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Exit").clicked() {
|
||||
*exit = true;
|
||||
}
|
||||
|
||||
ui.add_enabled_ui(*generations_left > 0, |ui| {
|
||||
if ui.button("Step").clicked() {
|
||||
data.next_generation(&mut rng());
|
||||
*generations_left -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
ui.label("");
|
||||
});
|
||||
draw_ants(ui, vertex_locations, data);
|
||||
if ui.input(|i| i.viewport().close_requested()) {
|
||||
*exit = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn draw_ants(ui: &mut Ui, vertex_locations: &mut [(f32, f32)], data: &GeneticSimulationState) {
|
||||
let cap = data.population.len() as f64;
|
||||
let mut usage = vec![0usize; data.graph.edges.capacity()];
|
||||
for chromosome in data.population.iter() {
|
||||
chromosome.iter().reduce(|a, b| {
|
||||
match data.graph.vertices[*a]
|
||||
.iter()
|
||||
.position(|e| data.graph.edges[*e].another(*a) == *b)
|
||||
{
|
||||
None => unreachable!(),
|
||||
Some(e) => usage[e] += 1,
|
||||
};
|
||||
return b;
|
||||
});
|
||||
}
|
||||
|
||||
render_graph(
|
||||
ui,
|
||||
&data.graph.vertices,
|
||||
vertex_locations,
|
||||
&data.graph.edges,
|
||||
|ei, _| usage[ei] as f64 / cap,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn gen_vertex_locations(rng: &mut impl Rng, count: usize) -> Vec<(f32, f32)> {
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..count {
|
||||
v.push((
|
||||
rng.random::<f32>() * 0.8 + 0.1,
|
||||
rng.random::<f32>() * 0.8 + 0.1,
|
||||
));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
60
lab5/src/gui/input.rs
Normal file
60
lab5/src/gui/input.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use crate::algo::GeneticSimulationConfig;
|
||||
use bgtu_ai_utility::gui::const_mut_switch::{
|
||||
ConstMutSwitchUi, RefType, _ConstMutSwitchUiCallback,
|
||||
};
|
||||
use bgtu_ai_utility::gui::graph_lengths_table::{draw_lengths_table, Graph};
|
||||
use bgtu_ai_utility::UpdatePending;
|
||||
|
||||
|
||||
pub(crate) fn input<Ctx: ConstMutSwitchUi>(
|
||||
ctx: &mut Ctx,
|
||||
mut config: <Ctx::RefType as RefType>::Ref<'_, GeneticSimulationConfig>,
|
||||
graph: &mut impl Graph<RefType = Ctx::RefType>,
|
||||
mut population_size: <Ctx::RefType as RefType>::Ref<'_, usize>,
|
||||
mut generations_count: <Ctx::RefType as RefType>::Ref<'_, usize>,
|
||||
mut vertex_update: <Ctx::RefType as RefType>::Ref<'_, UpdatePending>,
|
||||
mut launch: <Ctx::RefType as RefType>::Ref<'_, bool>,
|
||||
) {
|
||||
ctx.labeled_slider("Population size", 1..=100, 1.0, &mut population_size);
|
||||
|
||||
ctx.space();
|
||||
|
||||
ctx.labeled_slider("Generations", 1..=100, 1.0, &mut generations_count);
|
||||
|
||||
ctx.space();
|
||||
|
||||
ctx.labeled_slider(
|
||||
"Mutation chance",
|
||||
0.0..=1.0,
|
||||
0.001,
|
||||
&mut Ctx::RefType::_get(
|
||||
&mut config,
|
||||
|config| &config.mutation_chance,
|
||||
|config| &mut config.mutation_chance,
|
||||
),
|
||||
);
|
||||
|
||||
ctx.space();
|
||||
|
||||
ctx.space();
|
||||
|
||||
ctx.horizontal(ControlsRow {
|
||||
update: &mut vertex_update,
|
||||
run: &mut launch,
|
||||
});
|
||||
|
||||
draw_lengths_table(ctx, graph, vertex_update)
|
||||
}
|
||||
|
||||
struct ControlsRow<'uu, 'u, 'rr, 'r, RT: RefType> {
|
||||
update: &'uu mut RT::Ref<'u, UpdatePending>,
|
||||
run: &'rr mut RT::Ref<'r, bool>,
|
||||
}
|
||||
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for ControlsRow<'_, '_, '_, '_, RT> {
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType = RT>) {
|
||||
ctx.button("Add vertex", self.update, |d| *d = UpdatePending::Add);
|
||||
ctx.separator();
|
||||
ctx.button("Run", self.run, |d| *d = true);
|
||||
}
|
||||
}
|
||||
7
lab5/src/gui/mod.rs
Normal file
7
lab5/src/gui/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod data;
|
||||
mod input;
|
||||
mod graph;
|
||||
|
||||
pub(crate) use data::{GeneticVisualisationApp, GeneticVisualisationData, GeneticVisualisationState};
|
||||
pub(crate) use input::input;
|
||||
pub(crate) use graph::{gen_vertex_locations, graph_with_controls};
|
||||
262
lab5/src/main.rs
262
lab5/src/main.rs
@ -1,176 +1,128 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
mod algo;
|
||||
mod gui;
|
||||
|
||||
use crate::algo::GeneticSimulationState;
|
||||
use bgtu_ai_utility::UpdatePending;
|
||||
use bgtu_ai_utility::gui::boot_eframe;
|
||||
use bgtu_ai_utility::gui::const_mut_switch::{ConstUI, MutUI};
|
||||
use bgtu_ai_utility::gui::graph_lengths_table::{ConstGraph, MutableGraph};
|
||||
use bgtu_ai_utility::gui::subwindow;
|
||||
use eframe::egui;
|
||||
use eframe::egui::{Frame, Ui};
|
||||
use eframe::emath::Numeric;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::RangeInclusive;
|
||||
use tsp_utility::graph::{EdgesVec, VerticesVec};
|
||||
use tsp_utility::gui::lengths_table::{UpdatePending, draw_lengths_table};
|
||||
use rand::rng;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default().with_inner_size([640.0, 400.0]),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"Ants simulation",
|
||||
options,
|
||||
Box::new(|_cc| Ok(Box::<MyApp>::default())),
|
||||
)
|
||||
return boot_eframe("Genetic simulation", || gui::GeneticVisualisationApp::new());
|
||||
}
|
||||
|
||||
enum ViewState {
|
||||
Stop,
|
||||
Running { lastUpdateTimestamp: u64 },
|
||||
VertexMove { vertexId: usize },
|
||||
}
|
||||
|
||||
enum GlobalState {
|
||||
Edit {},
|
||||
Running { view_state: ViewState },
|
||||
}
|
||||
|
||||
struct MyApp {
|
||||
edges: EdgesVec<()>,
|
||||
vertices: VerticesVec,
|
||||
vertex_update: UpdatePending,
|
||||
population_size: usize,
|
||||
rounds: usize,
|
||||
mutation_chance: f64,
|
||||
state: GlobalState,
|
||||
}
|
||||
|
||||
impl Default for MyApp {
|
||||
fn default() -> Self {
|
||||
return Self {
|
||||
edges: EdgesVec::new(),
|
||||
vertices: VerticesVec::new(),
|
||||
vertex_update: UpdatePending::NoChange,
|
||||
population_size: 2,
|
||||
rounds: 1,
|
||||
mutation_chance: 0.1,
|
||||
state: GlobalState::Edit {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn _slider<T: Numeric>(
|
||||
ui: &mut Ui,
|
||||
name: &str,
|
||||
storage: &mut T,
|
||||
range: RangeInclusive<T>,
|
||||
step: f64,
|
||||
) {
|
||||
let label = ui.label(name);
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.spacing_mut().slider_width = ui.available_width()
|
||||
- ui.spacing().interact_size.x
|
||||
- ui.spacing().button_padding.x * 2.0;
|
||||
ui.add(egui::Slider::new(storage, range).step_by(step))
|
||||
.labelled_by(label.id);
|
||||
});
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
impl eframe::App for gui::GeneticVisualisationApp {
|
||||
fn update(&mut self, ui: &eframe::egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ui, |ui| match self.state {
|
||||
GlobalState::Edit {} => {
|
||||
match self.vertex_update {
|
||||
let state_ptr = NonNull::from_mut(&mut self.state);
|
||||
egui::CentralPanel::default().show(ui, |ui| match &mut self.state {
|
||||
gui::GeneticVisualisationState::Edit {
|
||||
config,
|
||||
graph,
|
||||
vertex_update,
|
||||
population_size,
|
||||
generations_count,
|
||||
} => {
|
||||
match vertex_update {
|
||||
UpdatePending::NoChange => {}
|
||||
UpdatePending::Add => {
|
||||
let new_vi = self.vertices.add(HashSet::new());
|
||||
let mut newEdgesSet = HashSet::new();
|
||||
for (vi, v) in self.vertices.iter_indexed_mut() {
|
||||
if (vi == new_vi) {
|
||||
continue;
|
||||
}
|
||||
let ei = self.edges.add(new_vi, vi, 1.0, ());
|
||||
newEdgesSet.insert(ei);
|
||||
v.insert(ei);
|
||||
}
|
||||
self.vertices[new_vi] = newEdgesSet;
|
||||
self.vertex_update = UpdatePending::NoChange;
|
||||
graph.add_vertex(|| ());
|
||||
*vertex_update = UpdatePending::NoChange;
|
||||
}
|
||||
UpdatePending::Remove(vi) => {
|
||||
let mut eis = Vec::with_capacity(self.vertices[vi].len());
|
||||
for ei in self.vertices[vi].iter() {
|
||||
eis.push(*ei)
|
||||
}
|
||||
for ei in eis {
|
||||
self.vertices[self.edges[ei].another(vi)].remove(&ei);
|
||||
self.edges.remove(ei);
|
||||
}
|
||||
self.vertices.remove(vi);
|
||||
self.vertex_update = UpdatePending::NoChange;
|
||||
graph.remove_vertex(*vi);
|
||||
*vertex_update = UpdatePending::NoChange;
|
||||
}
|
||||
}
|
||||
edit_panel(self, ui)
|
||||
}
|
||||
GlobalState::Running { .. } => {
|
||||
ui.add_enabled_ui(false, |ui| edit_panel(self, ui));
|
||||
ui.ctx().show_viewport_immediate(
|
||||
egui::ViewportId::from_hash_of("Visualisation"),
|
||||
egui::ViewportBuilder::default()
|
||||
.with_title("Visualisation")
|
||||
.with_inner_size([640.0, 480.0])
|
||||
.with_resizable(false),
|
||||
|ui, _| {
|
||||
egui::CentralPanel::default()
|
||||
.frame(Frame::default().inner_margin(0.0))
|
||||
.show(ui, |ui| visualization_panel(self, ui));
|
||||
},
|
||||
|
||||
let mut run = false;
|
||||
gui::input(
|
||||
&mut MutUI { ui },
|
||||
*config,
|
||||
&mut MutableGraph { graph },
|
||||
population_size,
|
||||
generations_count,
|
||||
vertex_update,
|
||||
&mut run,
|
||||
);
|
||||
|
||||
if run {
|
||||
let ns = gui::GeneticVisualisationState::Running {
|
||||
simulation: GeneticSimulationState::new(
|
||||
*config,
|
||||
*graph,
|
||||
graph.vertex_count(),
|
||||
*population_size,
|
||||
&mut rng(),
|
||||
),
|
||||
generations_done: 0,
|
||||
vertex_locations: gui::gen_vertex_locations(
|
||||
&mut rng(),
|
||||
graph.vertices.capacity(),
|
||||
),
|
||||
generations_count: *generations_count,
|
||||
};
|
||||
unsafe {
|
||||
state_ptr.write(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
gui::GeneticVisualisationState::Running {
|
||||
simulation,
|
||||
generations_done,
|
||||
generations_count,
|
||||
vertex_locations,
|
||||
} => {
|
||||
gui::input(
|
||||
&mut ConstUI { ui },
|
||||
&simulation.config,
|
||||
&mut ConstGraph {
|
||||
graph: &simulation.graph,
|
||||
},
|
||||
&simulation.population.len(),
|
||||
generations_count,
|
||||
&UpdatePending::NoChange,
|
||||
&false,
|
||||
);
|
||||
|
||||
let mut exit = false;
|
||||
let mut generations_left = *generations_count - *generations_done;
|
||||
subwindow(
|
||||
ui,
|
||||
"visualisation",
|
||||
"Visualisation",
|
||||
|vb| vb.with_inner_size([640.0, 480.0]),
|
||||
|ui| {
|
||||
gui::graph_with_controls(
|
||||
ui,
|
||||
vertex_locations,
|
||||
simulation,
|
||||
&mut generations_left,
|
||||
&mut exit,
|
||||
)
|
||||
},
|
||||
)
|
||||
.on_close(|| exit = true);
|
||||
*generations_done = *generations_count - generations_left;
|
||||
|
||||
if exit {
|
||||
let ns = gui::GeneticVisualisationState::Edit {
|
||||
config: unsafe { NonNull::from_mut(&mut self.data.config).as_mut() },
|
||||
graph: unsafe { NonNull::from_mut(&mut self.data.graph).as_mut() },
|
||||
vertex_update: UpdatePending::NoChange,
|
||||
population_size: simulation.population.len(),
|
||||
generations_count: *generations_count,
|
||||
};
|
||||
unsafe {
|
||||
state_ptr.write(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_panel(data: &mut MyApp, ui: &mut Ui) {
|
||||
let run: bool;
|
||||
|
||||
_slider(
|
||||
ui,
|
||||
"Population size",
|
||||
&mut data.population_size,
|
||||
2..=1000,
|
||||
1.0,
|
||||
);
|
||||
ui.label("");
|
||||
_slider(ui, "Rounds", &mut data.rounds, 1..=100, 1.0);
|
||||
ui.label("");
|
||||
_slider(
|
||||
ui,
|
||||
"Mutation chance",
|
||||
&mut data.mutation_chance,
|
||||
0.0..=1.0,
|
||||
0.001,
|
||||
);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Add vertex").clicked() {
|
||||
data.vertex_update = UpdatePending::Add;
|
||||
}
|
||||
ui.separator();
|
||||
let run = ui.button("Run").clicked();
|
||||
});
|
||||
|
||||
draw_lengths_table(
|
||||
ui,
|
||||
&mut data.vertices,
|
||||
&mut data.edges,
|
||||
&mut data.vertex_update,
|
||||
)
|
||||
}
|
||||
|
||||
fn visualization_panel(data: &mut MyApp, ui: &mut Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Exit").clicked() {}
|
||||
|
||||
if ui.button("Step").clicked() {}
|
||||
ui.label("");
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user