store agents in grids, not model

This commit is contained in:
Simon Gardling 2021-04-01 09:28:43 -04:00
parent 4e688c3fa7
commit 953c540263
3 changed files with 90 additions and 71 deletions

View File

@ -1,4 +1,7 @@
use crate::blur::Blur;
use crate::{
blur::Blur,
model::Agent,
};
use rand::{distributions::Uniform, Rng};
@ -88,6 +91,7 @@ pub struct Grid {
// Scratch space for the blur operation.
buf: Vec<f32>,
blur: Blur,
pub agents: Vec<Agent>
}
impl Clone for Grid {
@ -99,13 +103,14 @@ impl Clone for Grid {
data: self.data.clone(),
buf: self.buf.clone(),
blur: self.blur.clone(),
agents: self.agents.clone(),
}
}
}
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) -> 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.");
}
@ -119,6 +124,7 @@ impl Grid {
config: PopulationConfig::new(rng),
buf: vec![0.0; width * height],
blur: Blur::new(width),
agents,
}
}

View File

@ -2,15 +2,15 @@ use physarum::model;
fn main() {
// # of iterations to go through
let n_iterations = 2048;
let n_iterations = 254;
// Size of grid and pictures
// let (width, height) = (256, 256);
let (width, height) = (1024, 1024);
let (width, height) = (256, 256);
// let (width, height) = (1024, 1024);
// # of agents
let n_particles = 1 << 24;
// let n_particles = 1 << 16;
// let n_particles = 1 << 24;
let n_particles = 1 << 16;
println!("n_particles: {}", n_particles);
let diffusivity = 1;

View File

@ -14,7 +14,7 @@ use std::{f32::consts::TAU, path::Path, time::Instant};
// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
#[derive(Debug)]
struct Agent {
pub struct Agent {
x: f32,
y: f32,
angle: f32,
@ -34,6 +34,60 @@ impl Agent {
i,
}
}
#[inline]
pub fn tick(&mut self, grid: &Grid) {
let (width, height) = (grid.width, grid.height);
let PopulationConfig {
sensor_distance,
sensor_angle,
rotation_angle,
step_distance,
..
} = grid.config;
let xc = self.x + fastapprox::faster::cos(self.angle) * sensor_distance;
let yc = self.y + fastapprox::faster::sin(self.angle) * sensor_distance;
let agent_add_sens = self.angle + sensor_angle;
let agent_sub_sens = self.angle - sensor_angle;
let xl = self.x + fastapprox::faster::cos(agent_sub_sens) * sensor_distance;
let yl = self.y + fastapprox::faster::sin(agent_sub_sens) * sensor_distance;
let xr = self.x + fastapprox::faster::cos(agent_add_sens) * sensor_distance;
let yr = self.y + fastapprox::faster::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 = grid.get_buf(xc, yc);
let left = grid.get_buf(xl, yl);
let right = grid.get_buf(xr, yr);
// Rotate and move logic
let mut rng = rand::thread_rng();
let mut direction: f32 = 0.0;
if (center > left) && (center > right) {
direction = 0.0;
} else if (center < left) && (center < right) {
direction = *[-1.0, 1.0].choose(&mut rng).unwrap();
} else if left < right {
direction = 1.0;
} else if right < left {
direction = -1.0;
}
let delta_angle = rotation_angle * direction;
self.angle = wrap(self.angle + delta_angle, TAU);
self.x = wrap(
self.x + step_distance * fastapprox::faster::cos(self.angle),
width as f32,
);
self.y = wrap(
self.y + step_distance * fastapprox::faster::sin(self.angle),
height as f32,
);
}
}
impl Clone for Agent {
@ -61,7 +115,7 @@ impl PartialEq for Agent {
// Top-level simulation class.
pub struct Model {
// Physarum agents.
agents: Vec<Agent>,
// agents: Vec<Agent>,
// The grid they move on.
grids: Vec<Grid>,
@ -125,13 +179,15 @@ impl Model {
}
}
let mut grids: Vec<Grid> = Vec::new();
for _ in (0..n_populations) {
let agents = (0..particles_per_grid)
.map(|i| Agent::new(width, height, i / particles_per_grid, &mut rng, i)).collect();
grids.push(Grid::new(width, height, &mut rng, agents));
}
Model {
agents: (0..(n_particles-1))
.map(|i| Agent::new(width, height, i / particles_per_grid, &mut rng, i))
.collect(),
grids: (0..n_populations)
.map(|_| Grid::new(width, height, &mut rng))
.collect(),
grids,
attraction_table,
diffusivity,
iteration: 0,
@ -157,6 +213,7 @@ impl Model {
let mut time_per_agent_list: Vec<f64> = Vec::new();
let mut time_per_step_list: Vec<f64> = Vec::new();
let agents_num: usize = self.grids.iter().map(|grid| grid.agents.len()).sum();
for i in 0..steps {
if debug {
println!("Starting tick for all agents...")
@ -165,67 +222,23 @@ impl Model {
// Combine grids
let grids = &mut self.grids;
combine(grids, &self.attraction_table);
let grids_immutable_1 = &grids.clone();
let agents_tick_time = Instant::now();
// Tick agents
self.agents.par_iter_mut().for_each(|agent| {
let grid = &grids[agent.population_id];
let (width, height) = (grid.width, grid.height);
let PopulationConfig {
sensor_distance,
sensor_angle,
rotation_angle,
step_distance,
..
} = grid.config;
let mut rng = rand::thread_rng();
let mut direction: f32 = 0.0;
let agent_add_sens = agent.angle + sensor_angle;
let agent_sub_sens = agent.angle - sensor_angle;
let xl = agent.x + fastapprox::faster::cos(agent_sub_sens) * sensor_distance;
let yl = agent.y + fastapprox::faster::sin(agent_sub_sens) * sensor_distance;
let left = grid.get_buf(xl, yl);
let xr = agent.x + fastapprox::faster::cos(agent_add_sens) * sensor_distance;
let yr = agent.y + fastapprox::faster::sin(agent_add_sens) * sensor_distance;
let right = grid.get_buf(xr, yr);
let xc = agent.x + fastapprox::faster::cos(agent.angle) * sensor_distance;
let yc = agent.y + fastapprox::faster::sin(agent.angle) * sensor_distance;
let center = grid.get_buf(xc, yc);
// println!("{} {} {}", right, left, center);
// Rotate and move logic
if (center > left) && (center > right) {
direction = 0.0;
} else if (center < left) && (center < right) {
direction = *[-1.0, 1.0].choose(&mut rng).unwrap();
} else if left < right {
direction = 1.0;
} else if right < left {
direction = -1.0;
}
let delta_angle = rotation_angle * direction;
agent.angle = wrap(agent.angle + delta_angle, TAU);
agent.x = wrap(
agent.x + step_distance * fastapprox::faster::cos(agent.angle),
width as f32,
);
agent.y = wrap(
agent.y + step_distance * fastapprox::faster::sin(agent.angle),
height as f32,
);
});
for grid in grids.iter_mut() {
grid.agents.par_iter_mut().for_each(|agent| {
agent.tick(&grids_immutable_1[agent.population_id]);
});
}
// Deposit
for agent in self.agents.iter() {
self.grids[agent.population_id].deposit(agent.x, agent.y);
let grids_immutable_2 = &grids.clone();
for grid in grids_immutable_2.iter() {
for agent in grid.agents.iter() {
self.grids[agent.population_id].deposit(agent.x, agent.y);
}
}
// Diffuse + Decay
@ -237,7 +250,7 @@ impl Model {
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) / (self.agents.len() as f64);
let ms_per_agent: f64 = (agents_tick_elapsed as f64) / (agents_num as f64);
time_per_agent_list.push(ms_per_agent);
time_per_step_list.push(agents_tick_elapsed);