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",
|
"num-integer",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -38,6 +38,7 @@ num-integer = "0.1.45"
|
|||||||
rand = { version = "0.8.5", features = ["default", "small_rng"] }
|
rand = { version = "0.8.5", features = ["default", "small_rng"] }
|
||||||
csv = "1.1"
|
csv = "1.1"
|
||||||
serde = { version = "1.0.152", features = ["derive"] }
|
serde = { version = "1.0.152", features = ["derive"] }
|
||||||
|
serde_json = "1.0.93"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cbindgen = "0.24.3"
|
cbindgen = "0.24.3"
|
||||||
|
|||||||
@ -1,21 +1,52 @@
|
|||||||
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use rand::rngs::SmallRng;
|
use rand::rngs::SmallRng;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use crate::system::model::DLASystem;
|
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::grid::{Pos2D, VectorStorage};
|
||||||
|
use crate::system::spaces::hexagonal::HexPosition;
|
||||||
use crate::system::spaces::nd::{NDPosition, NDVectorStorage};
|
use crate::system::spaces::nd::{NDPosition, NDVectorStorage};
|
||||||
use crate::system::spawner::{Spawner, UniformSpawner};
|
use crate::system::spawner::{Spawner, UniformSpawner};
|
||||||
use crate::system::sticker::{ProbabilisticSticking, SimpleSticking, Sticker};
|
use crate::system::sticker::{ProbabilisticSticking, SimpleSticking, Sticker};
|
||||||
use crate::system::walker::{LocalRandomWalker, Walker};
|
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 {
|
while sys.running {
|
||||||
sys.update();
|
sys.update();
|
||||||
}
|
|
||||||
|
|
||||||
sys.export_data(csv_path)
|
match max_frames {
|
||||||
.expect("Failed to write");
|
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)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
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,
|
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(array_zip)]
|
||||||
#![feature(generic_const_exprs)]
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
mod example_systems;
|
mod example_systems;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crate::example_systems::{execute, stick_probability, three_dimensional};
|
use crate::example_systems::{drive_system, stick_probability, three_dimensional, write_csv, write_json};
|
||||||
use crate::system::model::DLASystem;
|
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||||
|
enum PreConfiguredSystem {
|
||||||
|
Grid2,
|
||||||
|
Grid3,
|
||||||
|
Hex
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[arg(short, long, default_value_t = 2)]
|
#[arg(value_enum, long, default_value_t = PreConfiguredSystem::Grid2)]
|
||||||
dim: u32,
|
mode: PreConfiguredSystem,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
max_frames: Option<usize>,
|
||||||
|
|
||||||
seed: u64,
|
seed: u64,
|
||||||
max_particles: usize,
|
max_particles: usize,
|
||||||
stick_probability: f32,
|
stick_probability: f32,
|
||||||
csv_path: PathBuf,
|
output: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -26,27 +36,40 @@ fn main() {
|
|||||||
|
|
||||||
println!("Running: {:?}", cli);
|
println!("Running: {:?}", cli);
|
||||||
|
|
||||||
match cli.dim {
|
match cli.mode {
|
||||||
2 => {
|
PreConfiguredSystem::Grid2 => {
|
||||||
let mut sys = stick_probability(
|
let mut sys = stick_probability(
|
||||||
cli.seed,
|
cli.seed,
|
||||||
cli.max_particles,
|
cli.max_particles,
|
||||||
cli.stick_probability,
|
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(
|
let mut sys = three_dimensional(
|
||||||
cli.seed,
|
cli.seed,
|
||||||
cli.max_particles,
|
cli.max_particles,
|
||||||
cli.stick_probability,
|
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::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
use crate::system::{GriddedPosition, Position, Storage};
|
use crate::system::{GriddedPosition, Position, Storage};
|
||||||
use crate::system::spawner::Spawner;
|
use crate::system::spawner::Spawner;
|
||||||
use crate::system::sticker::Sticker;
|
use crate::system::sticker::Sticker;
|
||||||
use crate::system::walker::Walker;
|
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>> {
|
pub struct DLASystem<R: Rng, P: Position, S: Storage<P>, W: Walker<P>, Sp: Spawner<P>, St: Sticker<P>> {
|
||||||
rng: R,
|
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,
|
space: S,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walkers allow us to choose between different particle movement behaviours, eg random or
|
||||||
|
* biased walker
|
||||||
|
*/
|
||||||
walker: W,
|
walker: W,
|
||||||
spawner: Sp,
|
spawner: Sp,
|
||||||
sticker: St,
|
sticker: St,
|
||||||
|
|
||||||
|
pub frame: usize,
|
||||||
pub running: bool,
|
pub running: bool,
|
||||||
|
pub history: Vec<HistoryLine<P>>,
|
||||||
|
|
||||||
max_particles: usize,
|
max_particles: usize,
|
||||||
particles: Vec<P>,
|
particles: Vec<P>,
|
||||||
active_particle: Option<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,
|
space,
|
||||||
walker,
|
walker,
|
||||||
|
|
||||||
|
frame: 0,
|
||||||
particles: vec![],
|
particles: vec![],
|
||||||
|
history: vec![],
|
||||||
active_particle: None,
|
active_particle: None,
|
||||||
|
|
||||||
add_ratio: 1.2,
|
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) {
|
pub fn update(&mut self) {
|
||||||
|
self.frame += 1;
|
||||||
if self.active_particle.is_some() {
|
if self.active_particle.is_some() {
|
||||||
self.move_particle();
|
self.move_particle();
|
||||||
} else if self.particles.len() < self.max_particles {
|
} 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;
|
self.kill_circle = self.kill_ratio * self.add_circle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export_data(&self, path: &Path) -> io::Result<()> {
|
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() });
|
||||||
let mut wtr = csv::Writer::from_path(path)?;
|
|
||||||
|
|
||||||
for particle in &self.particles {
|
|
||||||
wtr.serialize(particle)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
wtr.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use num_integer::Integer;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use crate::system::{GriddedPosition, Position, Storage};
|
use crate::system::{GriddedPosition, Position, Storage};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use crate::system::spaces::hexagonal::HexPosition;
|
||||||
|
|
||||||
pub struct VectorStorage {
|
pub struct VectorStorage {
|
||||||
backing: Vec<bool>,
|
backing: Vec<bool>,
|
||||||
@ -91,3 +92,13 @@ impl Storage<Pos2D> for VectorStorage {
|
|||||||
self.backing[position.linear_index(self.grid_size)] = true;
|
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 {
|
fn linear_index(&self, grid_size: u32) -> usize {
|
||||||
todo!()
|
let q = (self.q + (grid_size as i32 / 2)) as usize;
|
||||||
// ((self.q + grid_size / 2) + grid_size * self.r) 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 {
|
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 num_integer::Integer;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct};
|
use serde::ser::SerializeStruct;
|
||||||
use crate::system::{GriddedPosition, Position};
|
use crate::system::{GriddedPosition, Position};
|
||||||
use crate::system::Storage;
|
use crate::system::Storage;
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use std::f32::consts::PI;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use crate::system::Position;
|
use crate::system::Position;
|
||||||
use crate::system::spaces::grid::Pos2D;
|
use crate::system::spaces::grid::Pos2D;
|
||||||
|
use crate::system::spaces::hexagonal::HexPosition;
|
||||||
use crate::system::spaces::nd::NDPosition;
|
use crate::system::spaces::nd::NDPosition;
|
||||||
|
|
||||||
pub trait Spawner<P: Position> {
|
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 {
|
impl Spawner<NDPosition<3>> for UniformSpawner {
|
||||||
fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> NDPosition<3> {
|
fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> NDPosition<3> {
|
||||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||||
let phi = 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])
|
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