Quite a mess

This commit is contained in:
Joshua Coles 2023-03-06 12:14:21 +00:00
parent 15c2edde67
commit c707a20a75
9 changed files with 178 additions and 33 deletions

1
Cargo.lock generated
View File

@ -2723,6 +2723,7 @@ dependencies = [
"num-integer",
"rand",
"serde",
"serde_json",
]
[[package]]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
// }
// }
// }