Compare commits

...

10 Commits

Author SHA1 Message Date
1a3b137e95 update README.md 2025-03-24 16:29:17 -04:00
f0d4e883f6 cargo fmt 2025-03-24 16:27:11 -04:00
3a9940dfba grid: fix test 2025-03-24 16:27:02 -04:00
cb6e2e8133 remove criterion 2025-03-24 16:23:42 -04:00
0c5ba3154a unlock indicatif version 2025-03-24 16:23:42 -04:00
1b6cbce8b6 remove more unused stuff 2025-03-24 16:23:41 -04:00
13fa44ee12 update dependencies and cargo.toml 2025-03-24 16:23:41 -04:00
8e3944d5df clippy 2025-03-24 16:23:40 -04:00
7acf12a325 update note 2025-03-24 16:23:12 -04:00
c097446df4 Revert "implement sin and cos table"
This reverts commit 650edff95c.
2025-03-24 16:23:07 -04:00
15 changed files with 530 additions and 735 deletions

713
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,33 +2,19 @@
name = "physarum"
version = "0.1.0"
authors = ["Simon Gardling <titaniumtown@gmail.com>", "mindv0rtex <mindv0rtex@users.noreply.github.com>"]
edition = "2018"
edition = "2021"
[dependencies]
image = "0.23.14"
indicatif = {version = "0.15.0", features = ["rayon"]}
image = "0.23"
indicatif = { version = "0.15", features = [ "rayon" ] }
itertools = "0.10"
rand = "0.8.3"
rand_distr = "0.4.0"
rayon = {git = "https://github.com/rayon-rs/rayon.git"}
fastapprox = "0.3.0"
[dev-dependencies]
criterion = "0.3.4"
[patch.crates-io]
rayon = {git = "https://github.com/rayon-rs/rayon.git"} #fixes dependency issues
[profile.dev]
codegen-units = 1
opt-level = 3
target-cpu = "native"
lto = "fat"
debug = true
rand = "0.8"
rand_distr = "0.4"
rayon = "1.10"
fastapprox = "0.3"
[profile.release]
codegen-units = 1
opt-level = 3
target-cpu = "native"
lto = "fat"
debug = false

View File

@@ -15,3 +15,6 @@
- 0.000018658ms (2.56% slower)
- fast_approx::faster::sin + fast_approx::faster::cos
- 0.000015878ms (14.57% faster)
## sin and cos lookup table:
- With lookup tables: 0.000044201500713825226ms
- Without lookup tables: 0.000043705105781555176ms`

View File

@@ -1,14 +1,3 @@
![Physarum distribution](assets/example.gif)
# Physarum simulation
I recently came across [this repo](https://github.com/fogleman/physarum) that uses Go to implement an extended version of the Physarum transport networks model, first described in:
> Jones, J. (2010). Characteristics of pattern formation and evolution in approximations of physarum transport networks. Artificial Life, 16(2), 127-153. https://doi.org/10.1162/artl.2010.16.2.16202
I was amazed by the images this model produces, so I decided to figure out how it works and reimplement it in Rust for no other reason than to have some fun and practice some Rust.
# Physarum Simulation
The simulation consists of a 2D grid that stores information about the spatial distribution of some chemotactic sensory stimuli. This is called the _trail_ layer in the original paper. The stimuli attract discrete agents that are meant to represent a particle of Physarum plasmodium gel-sol structure. Each agent knows its position and orientation on the said 2D grid. They are attracted to the stimuli. They have sensors to tell them which way they should turn and move to reach the location with the highest level of the thing that attracts them. As the agents move, they leave a trail of the same stimuli behind, effectively signaling other agents where they should follow. The trail layer is also subjected to a simple diffusion-decay operator after every agent got their change to move to a new location. This image (shamelessly taken from [here](https://sagejenson.com/physarum)) shows all the steps that happen during one iteration of the simulation:
![Physarum simulation steps](assets/physarum-steps.jpg)
based on: https://github.com/yan-zaretskiy/physarum

View File

@@ -1 +0,0 @@
nightly

View File

@@ -1,3 +0,0 @@
unstable_features = true
imports_granularity = "Crate"
wrap_comments = false

View File

@@ -1,11 +1,8 @@
use crate::{
util::wrap,
buffer::Buf,
};
use crate::{buffer::Buf, util::wrap};
use fastapprox::faster::{cos, sin};
use rand::{seq::SliceRandom, Rng};
use std::f32::consts::TAU;
use fastapprox::faster::{cos, sin};
use std::fmt::{Display, Formatter};
// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
@@ -30,7 +27,13 @@ impl Display for Agent {
impl Agent {
// Construct a new agent with random parameters.
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, id: usize, rng: &mut R, i: usize) -> Self {
pub fn new<R: Rng + ?Sized>(
width: usize,
height: usize,
id: usize,
rng: &mut R,
i: usize,
) -> Self {
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
Agent {
x: x * width as f32,
@@ -41,44 +44,28 @@ impl Agent {
}
}
pub fn get_from_table(i: f32, table: &Vec<f32>) -> f32 {
let interval: f32 = 1.0/0.01;
let i_proc = (i * interval).round()+(360.0*interval);
let output = table[i_proc as usize];
return output;
}
// Tick an agent
#[inline]
pub fn tick(&mut self, buf: &Buf, sensor_distance: f32, sensor_angle: f32, rotation_angle: f32, step_distance: f32, width: usize, height: usize, sin_table: &Vec<f32>, cos_table: &Vec<f32>) {
/*
pub fn tick(
&mut self,
buf: &Buf,
sensor_distance: f32,
sensor_angle: f32,
rotation_angle: f32,
step_distance: f32,
width: usize,
height: usize,
) {
let xc = self.x + cos(self.angle) * sensor_distance;
let yc = self.y + sin(self.angle) * sensor_distance;
*/
// /*
let xc = self.x + Self::get_from_table(self.angle, &cos_table) * sensor_distance;
let yc = self.y + Self::get_from_table(self.angle, &sin_table) * sensor_distance;
// */
let agent_add_sens = self.angle + sensor_angle;
let agent_sub_sens = self.angle - sensor_angle;
// /*
let xl = self.x + Self::get_from_table(agent_sub_sens, &cos_table) * sensor_distance;
let yl = self.y + Self::get_from_table(agent_sub_sens, &sin_table) * sensor_distance;
let xr = self.x + Self::get_from_table(agent_add_sens, &cos_table) * sensor_distance;
let yr = self.y + Self::get_from_table(agent_add_sens, &sin_table) * sensor_distance;
// */
/*
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
let xr = self.x + cos(agent_add_sens) * sensor_distance;
let yr = self.y + sin(agent_add_sens) * sensor_distance;
*/
// We sense from the buffer because this is where we previously combined data from all the grid.
let center = buf.get_buf(xc, yc);
@@ -102,16 +89,8 @@ impl Agent {
let delta_angle = rotation_angle * direction;
self.angle = wrap(self.angle + delta_angle, TAU);
self.x = wrap(
// self.x + step_distance * cos(self.angle),
self.x + step_distance * Self::get_from_table(self.angle, &cos_table),
width as f32,
);
self.y = wrap(
// self.y + step_distance * sin(self.angle),
self.y + step_distance * Self::get_from_table(self.angle, &sin_table),
height as f32,
);
self.x = wrap(self.x + step_distance * cos(self.angle), width as f32);
self.y = wrap(self.y + step_distance * sin(self.angle), height as f32);
}
}

View File

@@ -37,7 +37,7 @@ impl Blur {
}
// Approximate 1D Gaussian filter of standard deviation sigma with N box filter passes. Each element in the output array contains the radius of the box filter for the corresponding pass.
fn boxes_for_gaussian<const N: usize>(sigma: f32) -> ([usize; N]) {
fn boxes_for_gaussian<const N: usize>(sigma: f32) -> [usize; N] {
let w_ideal = (12.0 * sigma * sigma / N as f32 + 1.0).sqrt();
let mut w = w_ideal as usize;
w -= 1 - (w & 1);
@@ -148,15 +148,69 @@ mod tests {
// ndimage.uniform_filter(a, size=3, mode='wrap') # 2D blur
let mut src: Vec<f32> = vec![
0.32352856, 0.06571674, 0.01939427, 0.06352045, 0.708_527, 0.617_221_7, 0.16638431,
0.628_400_74, 0.554_893_9, 0.240_076_77, 0.325_009_94, 0.08515139, 0.679_840_9, 0.6975669,
0.736_234_25, 0.55053085, 0.692_227_66, 0.22727048, 0.13594262, 0.10002105, 0.16099514,
0.07719103, 0.23984282, 0.9083058, 0.642_227_4, 0.968_934_2, 0.74662715, 0.715_620_1,
0.736_546_5, 0.70610344, 0.221_011_18, 0.755_721_87, 0.691_958_84, 0.837_414, 0.27583158,
0.572_570_5, 0.681_606, 0.392_373_38, 0.33524343, 0.893_968_34, 0.602_969_35, 0.171_301_13,
0.1733834, 0.771_278_2, 0.99537134, 0.915_049_6, 0.493_121_1, 0.430_352_03, 0.70297265,
0.367_341_8, 0.4551964, 0.471_043_14, 0.603_747_8, 0.738_726_85, 0.5630592, 0.974_402_25,
0.633_682_85, 0.841_092_94, 0.24447136, 0.750384, 0.16893725, 0.542_256_65, 0.435_607_82,
0.32352856,
0.06571674,
0.01939427,
0.06352045,
0.708_527,
0.617_221_7,
0.16638431,
0.628_400_74,
0.554_893_9,
0.240_076_77,
0.325_009_94,
0.08515139,
0.679_840_9,
0.6975669,
0.736_234_25,
0.55053085,
0.692_227_66,
0.22727048,
0.13594262,
0.10002105,
0.16099514,
0.07719103,
0.23984282,
0.9083058,
0.642_227_4,
0.968_934_2,
0.74662715,
0.715_620_1,
0.736_546_5,
0.70610344,
0.221_011_18,
0.755_721_87,
0.691_958_84,
0.837_414,
0.27583158,
0.572_570_5,
0.681_606,
0.392_373_38,
0.33524343,
0.893_968_34,
0.602_969_35,
0.171_301_13,
0.1733834,
0.771_278_2,
0.99537134,
0.915_049_6,
0.493_121_1,
0.430_352_03,
0.70297265,
0.367_341_8,
0.4551964,
0.471_043_14,
0.603_747_8,
0.738_726_85,
0.5630592,
0.974_402_25,
0.633_682_85,
0.841_092_94,
0.24447136,
0.750384,
0.16893725,
0.542_256_65,
0.435_607_82,
0.414_971_23,
];
let (width, height) = (8, 8);
@@ -165,15 +219,69 @@ mod tests {
blur.box_blur_h(&src, &mut dst, width, 1);
let mut sol: Vec<f32> = vec![
0.339_215_37, 0.136_213_18, 0.04954382, 0.263_813_9, 0.46308973, 0.497_377_7, 0.470_668_94,
0.372_771_2, 0.448_500_5, 0.373_326_87, 0.21674603, 0.363_334_1, 0.48751974, 0.70454735,
0.661_444, 0.613_886_36, 0.609_268, 0.351_813_58, 0.15441138, 0.1323196, 0.11273574,
0.159343, 0.40844655, 0.613_458_75, 0.788_961_2, 0.785_929_56, 0.810_393_8, 0.732_931_26,
0.719_423_35, 0.554_553_7, 0.560_945_5, 0.539_653_5, 0.807_780_4, 0.601_734_8, 0.561_938_7,
0.510_002_7, 0.548_849_94, 0.46974093, 0.540_528_4, 0.640_390_2, 0.40154082, 0.315_884_62,
0.371_987_58, 0.646_677_6, 0.893_899_74, 0.801_180_66, 0.612_840_9, 0.508_814_16, 0.681_572_2,
0.508_503_6, 0.431_193_77, 0.509_995_76, 0.604_505_9, 0.635_177_9, 0.758_729_4, 0.746_811_33,
0.629_915_65, 0.573_082_4, 0.611_982_76, 0.38793087, 0.48719263, 0.38226724, 0.464_278_58,
0.339_215_37,
0.136_213_18,
0.04954382,
0.263_813_9,
0.46308973,
0.497_377_7,
0.470_668_94,
0.372_771_2,
0.448_500_5,
0.373_326_87,
0.21674603,
0.363_334_1,
0.48751974,
0.70454735,
0.661_444,
0.613_886_36,
0.609_268,
0.351_813_58,
0.15441138,
0.1323196,
0.11273574,
0.159343,
0.40844655,
0.613_458_75,
0.788_961_2,
0.785_929_56,
0.810_393_8,
0.732_931_26,
0.719_423_35,
0.554_553_7,
0.560_945_5,
0.539_653_5,
0.807_780_4,
0.601_734_8,
0.561_938_7,
0.510_002_7,
0.548_849_94,
0.46974093,
0.540_528_4,
0.640_390_2,
0.40154082,
0.315_884_62,
0.371_987_58,
0.646_677_6,
0.893_899_74,
0.801_180_66,
0.612_840_9,
0.508_814_16,
0.681_572_2,
0.508_503_6,
0.431_193_77,
0.509_995_76,
0.604_505_9,
0.635_177_9,
0.758_729_4,
0.746_811_33,
0.629_915_65,
0.573_082_4,
0.611_982_76,
0.38793087,
0.48719263,
0.38226724,
0.464_278_58,
0.494_753_96,
];
for (v1, v2) in dst.iter().zip(sol) {
@@ -182,15 +290,69 @@ mod tests {
blur.box_blur_v(&src, &mut dst, width, height, 1, 1.0);
sol = vec![
0.504_035_1, 0.382_295_5, 0.19629186, 0.299_685_27, 0.519_101_74, 0.619_015_1, 0.446_075_47,
0.531_300_96, 0.523_550_03, 0.177688, 0.16011561, 0.08289763, 0.516_454_34, 0.46399322,
0.38082045, 0.695_745_8, 0.629_783_03, 0.47876048, 0.402_526_56, 0.300_264_18, 0.5257942,
0.49362046, 0.3990294, 0.738_186_2, 0.675_471_3, 0.677_872_9, 0.386_133_8, 0.46273723,
0.526_382_57, 0.391_889_3, 0.265_365_8, 0.852_665_36, 0.645_718_5, 0.659_216_46, 0.39861405,
0.686_489_6, 0.804_508, 0.671_175_5, 0.349_791_88, 0.693_347_4, 0.665_966_9, 0.458_685_64,
0.30147046, 0.604_963_96, 0.760_241_7, 0.682_05, 0.463_807_9, 0.766_240_9, 0.6465416,
0.459_911_97, 0.291_017_06, 0.664_235_1, 0.589_352_13, 0.732_011, 0.497_262_72, 0.606_575_13,
0.553_394_7, 0.42471716, 0.23968734, 0.428_315_88, 0.493_737_34, 0.632_735_1, 0.388_350_46,
0.504_035_1,
0.382_295_5,
0.19629186,
0.299_685_27,
0.519_101_74,
0.619_015_1,
0.446_075_47,
0.531_300_96,
0.523_550_03,
0.177688,
0.16011561,
0.08289763,
0.516_454_34,
0.46399322,
0.38082045,
0.695_745_8,
0.629_783_03,
0.47876048,
0.402_526_56,
0.300_264_18,
0.5257942,
0.49362046,
0.3990294,
0.738_186_2,
0.675_471_3,
0.677_872_9,
0.386_133_8,
0.46273723,
0.526_382_57,
0.391_889_3,
0.265_365_8,
0.852_665_36,
0.645_718_5,
0.659_216_46,
0.39861405,
0.686_489_6,
0.804_508,
0.671_175_5,
0.349_791_88,
0.693_347_4,
0.665_966_9,
0.458_685_64,
0.30147046,
0.604_963_96,
0.760_241_7,
0.682_05,
0.463_807_9,
0.766_240_9,
0.6465416,
0.459_911_97,
0.291_017_06,
0.664_235_1,
0.589_352_13,
0.732_011,
0.497_262_72,
0.606_575_13,
0.553_394_7,
0.42471716,
0.23968734,
0.428_315_88,
0.493_737_34,
0.632_735_1,
0.388_350_46,
0.672_591_45,
];
for (v1, v2) in dst.iter().zip(sol) {
@@ -199,15 +361,69 @@ mod tests {
blur.box_blur(&mut src, &mut dst, width, height, 1, 1.0);
sol = vec![
0.472_543_84, 0.36087415, 0.29275754, 0.338_359_62, 0.47926736, 0.528_064_1, 0.5321305,
0.493_803_83, 0.465_661_3, 0.287_117_9, 0.140_233_76, 0.253_155_86, 0.3544484, 0.453_756,
0.513_519_8, 0.5333721, 0.615_576_57, 0.503_69, 0.393_850_42, 0.40952832, 0.43989295,
0.472_814_68, 0.543_612, 0.588_999_5, 0.735_336_54, 0.579826, 0.508_914_65, 0.458_417_86,
0.460_336_36, 0.39454588, 0.503_306_8, 0.597_834_17, 0.666_094_1, 0.567_849_7, 0.581_440_03,
0.62987053, 0.720_724_34, 0.608_491_8, 0.571_438_25, 0.562_952_64, 0.630_297_84, 0.475_374_34,
0.455_04, 0.5555587, 0.682_418_5, 0.635_366_5, 0.63736624, 0.632_005_2, 0.571_009_6,
0.465_823_53, 0.471_721_38, 0.5148681, 0.661_866_07, 0.606_208_6, 0.611_949_6, 0.583_459_8,
0.550_234_44, 0.405_933_05, 0.364_240_14, 0.38724685, 0.518_262_74, 0.504_940_9, 0.564_559,
0.472_543_84,
0.36087415,
0.29275754,
0.338_359_62,
0.47926736,
0.528_064_1,
0.5321305,
0.493_803_83,
0.465_661_3,
0.287_117_9,
0.140_233_76,
0.253_155_86,
0.3544484,
0.453_756,
0.513_519_8,
0.5333721,
0.615_576_57,
0.503_69,
0.393_850_42,
0.40952832,
0.43989295,
0.472_814_68,
0.543_612,
0.588_999_5,
0.735_336_54,
0.579826,
0.508_914_65,
0.458_417_86,
0.460_336_36,
0.39454588,
0.503_306_8,
0.597_834_17,
0.666_094_1,
0.567_849_7,
0.581_440_03,
0.62987053,
0.720_724_34,
0.608_491_8,
0.571_438_25,
0.562_952_64,
0.630_297_84,
0.475_374_34,
0.455_04,
0.5555587,
0.682_418_5,
0.635_366_5,
0.63736624,
0.632_005_2,
0.571_009_6,
0.465_823_53,
0.471_721_38,
0.5148681,
0.661_866_07,
0.606_208_6,
0.611_949_6,
0.583_459_8,
0.550_234_44,
0.405_933_05,
0.364_240_14,
0.38724685,
0.518_262_74,
0.504_940_9,
0.564_559,
0.538_112_16,
];
for (v1, v2) in src.iter().zip(sol) {

View File

@@ -17,11 +17,7 @@ impl Clone for Buf {
impl Buf {
pub fn new(width: usize, height: usize, buf: Vec<f32>) -> Self {
Buf {
width,
height,
buf,
}
Buf { width, height, buf }
}
// Truncate x and y and return a corresponding index into the data slice.

View File

@@ -1,12 +1,8 @@
use crate::{
blur::Blur,
agent::Agent,
buffer::Buf,
};
use crate::{agent::Agent, blur::Blur, buffer::Buf};
use rand::{distributions::Uniform, Rng};
use std::fmt::{Display, Formatter};
use rayon::{iter::ParallelIterator, prelude::*};
use std::fmt::{Display, Formatter};
// A population configuration.
#[derive(Debug)]
@@ -93,7 +89,7 @@ pub struct Grid {
// pub buf: Vec<f32>,
pub buf: Buf,
pub blur: Blur,
pub agents: Vec<Agent>
pub agents: Vec<Agent>,
}
impl Clone for Grid {
@@ -112,7 +108,12 @@ impl Clone for Grid {
impl Grid {
// Create a new grid filled with random floats in the [0.0..1.0) range.
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, rng: &mut R, agents: Vec<Agent>) -> Self {
pub fn new<R: Rng + ?Sized>(
width: usize,
height: usize,
rng: &mut R,
agents: Vec<Agent>,
) -> Self {
if !width.is_power_of_two() || !height.is_power_of_two() {
panic!("Grid dimensions must be a power of two.");
}
@@ -164,7 +165,7 @@ impl Grid {
}
#[inline]
pub fn tick(&mut self, sin_table: Vec<f32>, cos_table: Vec<f32>) {
pub fn tick(&mut self) {
let (width, height) = (self.width, self.height);
let PopulationConfig {
sensor_distance,
@@ -177,10 +178,15 @@ impl Grid {
let buf = self.buf.clone();
self.agents.par_iter_mut().for_each(|agent| {
agent.tick(&buf,
sensor_distance, sensor_angle,
rotation_angle, step_distance,
width, height, &sin_table, &cos_table);
agent.tick(
&buf,
sensor_distance,
sensor_angle,
rotation_angle,
step_distance,
width,
height,
);
});
self.deposit_all();
}
@@ -258,6 +264,5 @@ mod tests {
assert_eq!(grid.index(2.5, 0.6), 2);
assert_eq!(grid.index(2.5, 1.6), 10);
assert_eq!(grid.index(7.9, 7.9), 63);
assert_eq!(grid.index(-0.5, -0.6), 0);
}
}

View File

@@ -9,7 +9,6 @@ pub struct ThinGridData {
pub data: Vec<f32>,
}
impl Clone for ThinGridData {
fn clone(&self) -> ThinGridData {
ThinGridData {
@@ -32,9 +31,10 @@ impl ThinGridData {
#[allow(dead_code)]
pub fn new_from_grid_vec(in_grids: Vec<Grid>) -> Vec<Self> {
return in_grids.iter().map(|grid|{
Self::new_from_grid(grid)
}).collect();
in_grids
.iter()
.map(|grid| Self::new_from_grid(grid))
.collect()
}
// from grid.rs (needed in image gen)
@@ -60,12 +60,12 @@ impl ThinGridData {
pub fn size_of(&self) -> usize {
let mut output: usize = 0;
output = output + std::mem::size_of_val(&self.width);
output = output + std::mem::size_of_val(&self.height);
output += std::mem::size_of_val(&self.width);
output += std::mem::size_of_val(&self.height);
for i in self.data.iter() {
output = output + std::mem::size_of_val(&i);
output += std::mem::size_of_val(&i);
}
return output;
output
}
}
@@ -97,12 +97,12 @@ impl ImgData {
pub fn size_of(&self) -> usize {
let mut output: usize = 0;
output = output + std::mem::size_of_val(&self.iteration);
output = output + std::mem::size_of_val(&self.palette);
output += std::mem::size_of_val(&self.iteration);
output += std::mem::size_of_val(&self.palette);
for grid in self.grids.iter() {
output = output + grid.size_of();
output += grid.size_of();
}
return output;
output
}
#[inline]

View File

@@ -1,8 +1,8 @@
mod agent;
mod blur;
mod buffer;
mod grid;
mod imgdata; // for storing image data
pub mod model;
mod palette;
mod util; // for math things
mod agent;
mod buffer;

View File

@@ -2,14 +2,14 @@ use physarum::model;
fn main() {
// # of iterations to go through
let n_iterations = 128;
let n_iterations = 1024;
// let n_iterations = 2048;
// let n_iterations = 1 << 14;
// Size of grid and pictures
let (width, height) = (256, 256);
// let (width, height) = (256, 256);
// let (width, height) = (512, 512);
// let (width, height) = (1024, 1024);
let (width, height) = (1024, 1024);
// # of agents
// let n_particles = 1 << 10;

View File

@@ -1,8 +1,8 @@
use crate::{
agent::Agent,
grid::{combine, Grid},
imgdata::{ImgData, ThinGridData},
palette::{random_palette, Palette},
agent::Agent,
};
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
@@ -10,7 +10,6 @@ use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
use rand_distr::{Distribution, Normal};
use rayon::{iter::ParallelIterator, prelude::*};
use std::{path::Path, time::Instant};
use fastapprox::faster::{cos, sin};
// Top-level simulation class.
pub struct Model {
@@ -79,9 +78,8 @@ impl Model {
let mut grids: Vec<Grid> = Vec::new();
for pop in 0..n_populations {
let agents = (0..particles_per_grid)
.map(|i| {
Agent::new(width, height, pop, &mut rng, i)
}).collect();
.map(|i| Agent::new(width, height, pop, &mut rng, i))
.collect();
grids.push(Grid::new(width, height, &mut rng, agents));
}
@@ -95,32 +93,6 @@ impl Model {
}
}
pub fn gen_table(opt: i32) -> Vec<f32> {
let mut output: Vec<f32> = Vec::new();
let max: f32 = 360.0;
let min: f32 = -360.0;
let interval: f32 = 0.01;
let mut i: f32;
let mut tmp: f32 = 0.0;
for i1 in ((min/interval)as i32)..((max/interval)as i32) {
i = (i1 as f32)*interval;
if opt == 0 {
tmp = sin(i);
} else if opt == 1 {
tmp = cos(i);
}
output.push(tmp);
}
println!("{}", output.len());
return output;
}
// Simulates `steps` # of steps
#[inline]
pub fn run(&mut self, steps: usize) {
@@ -139,10 +111,6 @@ impl Model {
let mut time_per_step_list: Vec<f64> = Vec::new();
let agents_num: usize = self.grids.iter().map(|grid| grid.agents.len()).sum();
let sin_table1 = Self::gen_table(0);
let cos_table1 = Self::gen_table(1);
let sin_table = sin_table1.clone();
let cos_table = cos_table1.clone();
for i in 0..steps {
if debug {
println!("Starting tick for all agents...")
@@ -156,14 +124,14 @@ impl Model {
// Tick agents
let diffusivity = self.diffusivity;
self.grids.par_iter_mut().for_each(|grid| {
grid.tick(sin_table.clone(), cos_table.clone());
grid.tick();
grid.diffuse(diffusivity); // Diffuse + Decay
});
self.save_image_data();
let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64;
let ms_per_agent: f64 = (agents_tick_elapsed as f64) / (agents_num as f64);
let ms_per_agent: f64 = agents_tick_elapsed / (agents_num as f64);
time_per_agent_list.push(ms_per_agent);
time_per_step_list.push(agents_tick_elapsed);
@@ -180,9 +148,9 @@ impl Model {
pb.finish();
let avg_per_step: f64 =
time_per_step_list.iter().sum::<f64>() as f64 / time_per_step_list.len() as f64;
time_per_step_list.iter().sum::<f64>() / time_per_step_list.len() as f64;
let avg_per_agent: f64 =
time_per_agent_list.iter().sum::<f64>() as f64 / time_per_agent_list.len() as f64;
time_per_agent_list.iter().sum::<f64>() / time_per_agent_list.len() as f64;
println!(
"Average time per step: {}ms\nAverage time per agent: {}ms",
avg_per_step, avg_per_agent
@@ -190,7 +158,7 @@ impl Model {
}
fn size_of_imgdata_vec(&self) -> usize {
return (self.img_data_vec[0].size_of() as usize) * (self.img_data_vec.len() as usize);
self.img_data_vec[0].size_of() * self.img_data_vec.len()
}
fn save_image_data(&mut self) {
@@ -203,7 +171,11 @@ impl Model {
let max_mb = 6000;
if mb >= max_mb {
println!("ram usage is over {} MB (and len of {}), flushing to disk\n", max_mb, self.img_data_vec.len());
println!(
"ram usage is over {} MB (and len of {}), flushing to disk\n",
max_mb,
self.img_data_vec.len()
);
self.render_all_imgdata();
self.flush_image_data();
}
@@ -231,7 +203,7 @@ impl Model {
pb.finish();
*/
(&self.img_data_vec)
self.img_data_vec
.par_iter()
.progress_with(pb)
.for_each(|img| {

View File

@@ -15,7 +15,7 @@ pub fn random_palette() -> Palette {
const fn hex_to_color(c: usize) -> image::Rgb<u8> {
let r = (c >> 16) & 0xff;
let g = (c >> 8) & 0xff;
let b = (c >> 0) & 0xff;
let b = c & 0xff;
image::Rgb::<u8>([r as u8, g as u8, b as u8])
}