Quite a mess
This commit is contained in:
parent
15c2edde67
commit
c707a20a75
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2723,6 +2723,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -38,6 +38,7 @@ num-integer = "0.1.45"
|
||||
rand = { version = "0.8.5", features = ["default", "small_rng"] }
|
||||
csv = "1.1"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.24.3"
|
||||
|
||||
@ -1,21 +1,52 @@
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use crate::system::model::DLASystem;
|
||||
use crate::system::{GriddedPosition, Position, Storage};
|
||||
use crate::system::{Position, Storage};
|
||||
use crate::system::spaces::grid::{Pos2D, VectorStorage};
|
||||
use crate::system::spaces::hexagonal::HexPosition;
|
||||
use crate::system::spaces::nd::{NDPosition, NDVectorStorage};
|
||||
use crate::system::spawner::{Spawner, UniformSpawner};
|
||||
use crate::system::sticker::{ProbabilisticSticking, SimpleSticking, Sticker};
|
||||
use crate::system::walker::{LocalRandomWalker, Walker};
|
||||
|
||||
pub fn execute<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>>(sys: &mut DLASystem<R, P, S, W, Sp, St>, csv_path: &Path) {
|
||||
pub fn drive_system<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>>(sys: &mut DLASystem<R, P, S, W, Sp, St>, max_frames: Option<usize>) {
|
||||
while sys.running {
|
||||
sys.update();
|
||||
|
||||
match max_frames {
|
||||
Some(max_frames) if max_frames <= sys.frame => {
|
||||
sys.running = false;
|
||||
eprintln!("System halted as it ran to {frame} frames (max_frames = {max_frames}) and did not complete", frame = sys.frame)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sys.export_data(csv_path)
|
||||
.expect("Failed to write");
|
||||
pub fn write_csv<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>>(sys: &DLASystem<R, P, S, W, Sp, St>, csv_path: &Path) {
|
||||
let mut wtr = csv::Writer::from_path(csv_path)
|
||||
.expect("Failed to open file");
|
||||
|
||||
// CSVs can only store the raw positions
|
||||
let positions: Vec<&P> = sys.history
|
||||
.iter()
|
||||
.map(|line| &line.position)
|
||||
.collect();
|
||||
|
||||
wtr.serialize(positions)
|
||||
.unwrap();
|
||||
|
||||
wtr.flush()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn write_json<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>>(sys: &DLASystem<R, P, S, W, Sp, St>, output_path: &Path) {
|
||||
let file = File::create(output_path).expect("Failed to open file");
|
||||
|
||||
serde_json::to_writer(file, &sys.history)
|
||||
.expect("Failed to write json");
|
||||
}
|
||||
|
||||
pub fn initial_config(seed: u64, max_particles: usize) -> DLASystem<SmallRng, Pos2D, VectorStorage, LocalRandomWalker, UniformSpawner, SimpleSticking> {
|
||||
@ -50,3 +81,14 @@ pub fn three_dimensional(seed: u64, max_particles: usize, stick_probability: f32
|
||||
max_particles,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn hex_grid(seed: u64, max_particles: usize, stick_probability: f32) -> DLASystem<SmallRng, HexPosition, VectorStorage, LocalRandomWalker, UniformSpawner, ProbabilisticSticking> {
|
||||
DLASystem::new(
|
||||
SmallRng::seed_from_u64(seed),
|
||||
VectorStorage::new(1600),
|
||||
LocalRandomWalker,
|
||||
UniformSpawner,
|
||||
ProbabilisticSticking { stick_probability },
|
||||
max_particles,
|
||||
)
|
||||
}
|
||||
|
||||
49
src/main.rs
49
src/main.rs
@ -1,24 +1,34 @@
|
||||
#![feature(array_zip)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod system;
|
||||
mod example_systems;
|
||||
|
||||
use clap::Parser;
|
||||
use crate::example_systems::{execute, stick_probability, three_dimensional};
|
||||
use crate::system::model::DLASystem;
|
||||
use crate::example_systems::{drive_system, stick_probability, three_dimensional, write_csv, write_json};
|
||||
|
||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||
enum PreConfiguredSystem {
|
||||
Grid2,
|
||||
Grid3,
|
||||
Hex
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
#[arg(short, long, default_value_t = 2)]
|
||||
dim: u32,
|
||||
#[arg(value_enum, long, default_value_t = PreConfiguredSystem::Grid2)]
|
||||
mode: PreConfiguredSystem,
|
||||
|
||||
#[arg(short, long)]
|
||||
max_frames: Option<usize>,
|
||||
|
||||
seed: u64,
|
||||
max_particles: usize,
|
||||
stick_probability: f32,
|
||||
csv_path: PathBuf,
|
||||
output: PathBuf,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -26,27 +36,40 @@ fn main() {
|
||||
|
||||
println!("Running: {:?}", cli);
|
||||
|
||||
match cli.dim {
|
||||
2 => {
|
||||
match cli.mode {
|
||||
PreConfiguredSystem::Grid2 => {
|
||||
let mut sys = stick_probability(
|
||||
cli.seed,
|
||||
cli.max_particles,
|
||||
cli.stick_probability,
|
||||
);
|
||||
|
||||
execute(&mut sys, &cli.csv_path);
|
||||
},
|
||||
drive_system(&mut sys, cli.max_frames);
|
||||
write_csv(&mut sys, &cli.output);
|
||||
}
|
||||
|
||||
3 => {
|
||||
PreConfiguredSystem::Grid3 => {
|
||||
let mut sys = three_dimensional(
|
||||
cli.seed,
|
||||
cli.max_particles,
|
||||
cli.stick_probability,
|
||||
);
|
||||
|
||||
execute(&mut sys, &cli.csv_path);
|
||||
},
|
||||
drive_system(&mut sys, cli.max_frames);
|
||||
|
||||
n => panic!("Model not compiled with {n} dimensional support")
|
||||
write_json(&mut sys, &cli.output);
|
||||
}
|
||||
|
||||
PreConfiguredSystem::Hex => {
|
||||
let mut sys = three_dimensional(
|
||||
cli.seed,
|
||||
cli.max_particles,
|
||||
cli.stick_probability,
|
||||
);
|
||||
|
||||
drive_system(&mut sys, cli.max_frames);
|
||||
|
||||
write_json(&mut sys, &cli.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,45 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use rand::prelude::*;
|
||||
use serde::Serialize;
|
||||
use crate::system::{GriddedPosition, Position, Storage};
|
||||
use crate::system::spawner::Spawner;
|
||||
use crate::system::sticker::Sticker;
|
||||
use crate::system::walker::Walker;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct HistoryLine<P: Position> {
|
||||
pub frame: usize,
|
||||
pub cluster_radius: f32,
|
||||
pub fd: f32,
|
||||
pub position: P,
|
||||
}
|
||||
|
||||
pub struct DLASystem<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>> {
|
||||
rng: R,
|
||||
|
||||
/*
|
||||
* These object encapsulate the behaviour of our particular model.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The space, along with the chosen position choose the embedding space that the cluster grows
|
||||
* in.
|
||||
*/
|
||||
space: S,
|
||||
|
||||
/*
|
||||
* Walkers allow us to choose between different particle movement behaviours, eg random or
|
||||
* biased walker
|
||||
*/
|
||||
walker: W,
|
||||
spawner: Sp,
|
||||
sticker: St,
|
||||
|
||||
pub frame: usize,
|
||||
pub running: bool,
|
||||
pub history: Vec<HistoryLine<P>>,
|
||||
|
||||
max_particles: usize,
|
||||
particles: Vec<P>,
|
||||
active_particle: Option<P>,
|
||||
@ -36,7 +62,10 @@ impl<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Stick
|
||||
|
||||
space,
|
||||
walker,
|
||||
|
||||
frame: 0,
|
||||
particles: vec![],
|
||||
history: vec![],
|
||||
active_particle: None,
|
||||
|
||||
add_ratio: 1.2,
|
||||
@ -53,6 +82,7 @@ impl<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Stick
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.frame += 1;
|
||||
if self.active_particle.is_some() {
|
||||
self.move_particle();
|
||||
} else if self.particles.len() < self.max_particles {
|
||||
@ -106,16 +136,7 @@ impl<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Stick
|
||||
self.kill_circle = self.kill_ratio * self.add_circle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_data(&self, path: &Path) -> io::Result<()> {
|
||||
let mut wtr = csv::Writer::from_path(path)?;
|
||||
|
||||
for particle in &self.particles {
|
||||
wtr.serialize(particle)?;
|
||||
}
|
||||
|
||||
wtr.flush()?;
|
||||
Ok(())
|
||||
self.history.push(HistoryLine { frame: self.frame, position: p0.clone(), cluster_radius: self.cluster_radius, fd: (self.particles.len() as f32).ln() / self.cluster_radius.ln() });
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ use num_integer::Integer;
|
||||
use rand::Rng;
|
||||
use crate::system::{GriddedPosition, Position, Storage};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::system::spaces::hexagonal::HexPosition;
|
||||
|
||||
pub struct VectorStorage {
|
||||
backing: Vec<bool>,
|
||||
@ -91,3 +92,13 @@ impl Storage<Pos2D> for VectorStorage {
|
||||
self.backing[position.linear_index(self.grid_size)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage<HexPosition> for VectorStorage {
|
||||
fn at(&self, position: &HexPosition) -> bool {
|
||||
return self.backing[position.linear_index(self.grid_size)];
|
||||
}
|
||||
|
||||
fn deposit(&mut self, position: &HexPosition) {
|
||||
self.backing[position.linear_index(self.grid_size)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,8 +32,10 @@ impl GriddedPosition for HexPosition {
|
||||
}
|
||||
|
||||
fn linear_index(&self, grid_size: u32) -> usize {
|
||||
todo!()
|
||||
// ((self.q + grid_size / 2) + grid_size * self.r) as usize
|
||||
let q = (self.q + (grid_size as i32 / 2)) as usize;
|
||||
let r = (self.r + (grid_size as i32 / 2)) as usize;
|
||||
|
||||
r * (grid_size as usize) + q
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +51,9 @@ impl Position for HexPosition {
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: [f32; Self::DIM]) -> Self {
|
||||
todo!()
|
||||
let q = (1.0f32/3.0f32).sqrt() * cartesian[0] - 1.0/3.0 * cartesian[1];
|
||||
let r = 2.0/3.0 * cartesian[1];
|
||||
|
||||
Self { q: q as i32, r: r as i32 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use std::ops::Add;
|
||||
use num_integer::Integer;
|
||||
use rand::Rng;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct};
|
||||
use serde::ser::SerializeStruct;
|
||||
use crate::system::{GriddedPosition, Position};
|
||||
use crate::system::Storage;
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ use std::f32::consts::PI;
|
||||
use rand::Rng;
|
||||
use crate::system::Position;
|
||||
use crate::system::spaces::grid::Pos2D;
|
||||
use crate::system::spaces::hexagonal::HexPosition;
|
||||
use crate::system::spaces::nd::NDPosition;
|
||||
|
||||
pub trait Spawner<P: Position> {
|
||||
@ -19,11 +20,51 @@ impl Spawner<Pos2D> for UniformSpawner {
|
||||
}
|
||||
}
|
||||
|
||||
impl Spawner<HexPosition> for UniformSpawner {
|
||||
fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> HexPosition {
|
||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||
let (x, y) = (radius * theta.cos(), radius * theta.sin());
|
||||
|
||||
HexPosition::from_cartesian([x, y])
|
||||
}
|
||||
}
|
||||
|
||||
impl Spawner<NDPosition<3>> for UniformSpawner {
|
||||
fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> NDPosition<3> {
|
||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||
let phi = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||
let (x, y, z) = (radius * theta.cos(), radius * theta.sin(), radius * theta.sin());
|
||||
|
||||
let (x, y, z) = (
|
||||
radius * theta.sin() * phi.cos(),
|
||||
radius * theta.sin() * phi.sin(),
|
||||
radius * theta.cos()
|
||||
);
|
||||
|
||||
NDPosition::<3>::from_cartesian([x, y, z])
|
||||
}
|
||||
}
|
||||
|
||||
// Does not work due to const generics issue, for now specialise for each dimension needed.
|
||||
// pub struct NDUniformSpawner<const DIM: usize>;
|
||||
//
|
||||
// impl<const DIM: usize> Spawner<NDPosition<DIM>> for NDUniformSpawner<DIM> {
|
||||
// fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> NDPosition<DIM> {
|
||||
// let mut a: [f32; DIM] = [0f32; DIM];
|
||||
//
|
||||
// for i in 0..DIM {
|
||||
// a[i] = rng.gen_range(0f32..1f32);
|
||||
// }
|
||||
//
|
||||
// let norm = a.iter().sum::<f32>()
|
||||
// .sqrt();
|
||||
//
|
||||
// for i in 0..DIM {
|
||||
// a[i] = a[i] * radius / norm;
|
||||
// }
|
||||
//
|
||||
// unsafe {
|
||||
// let b = transmute(a);
|
||||
// NDPosition::from_cartesian(b)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user