store agents in grids, not model
This commit is contained in:
parent
4e688c3fa7
commit
953c540263
10
src/grid.rs
10
src/grid.rs
@ -1,4 +1,7 @@
|
|||||||
use crate::blur::Blur;
|
use crate::{
|
||||||
|
blur::Blur,
|
||||||
|
model::Agent,
|
||||||
|
};
|
||||||
|
|
||||||
use rand::{distributions::Uniform, Rng};
|
use rand::{distributions::Uniform, Rng};
|
||||||
|
|
||||||
@ -88,6 +91,7 @@ pub struct Grid {
|
|||||||
// Scratch space for the blur operation.
|
// Scratch space for the blur operation.
|
||||||
buf: Vec<f32>,
|
buf: Vec<f32>,
|
||||||
blur: Blur,
|
blur: Blur,
|
||||||
|
pub agents: Vec<Agent>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Grid {
|
impl Clone for Grid {
|
||||||
@ -99,13 +103,14 @@ impl Clone for Grid {
|
|||||||
data: self.data.clone(),
|
data: self.data.clone(),
|
||||||
buf: self.buf.clone(),
|
buf: self.buf.clone(),
|
||||||
blur: self.blur.clone(),
|
blur: self.blur.clone(),
|
||||||
|
agents: self.agents.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
// Create a new grid filled with random floats in the [0.0..1.0) range.
|
// 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() {
|
if !width.is_power_of_two() || !height.is_power_of_two() {
|
||||||
panic!("Grid dimensions must be a power of two.");
|
panic!("Grid dimensions must be a power of two.");
|
||||||
}
|
}
|
||||||
@ -119,6 +124,7 @@ impl Grid {
|
|||||||
config: PopulationConfig::new(rng),
|
config: PopulationConfig::new(rng),
|
||||||
buf: vec![0.0; width * height],
|
buf: vec![0.0; width * height],
|
||||||
blur: Blur::new(width),
|
blur: Blur::new(width),
|
||||||
|
agents,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/main.rs
10
src/main.rs
@ -2,15 +2,15 @@ use physarum::model;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// # of iterations to go through
|
// # of iterations to go through
|
||||||
let n_iterations = 2048;
|
let n_iterations = 254;
|
||||||
|
|
||||||
// Size of grid and pictures
|
// Size of grid and pictures
|
||||||
// let (width, height) = (256, 256);
|
let (width, height) = (256, 256);
|
||||||
let (width, height) = (1024, 1024);
|
// let (width, height) = (1024, 1024);
|
||||||
|
|
||||||
// # of agents
|
// # of agents
|
||||||
let n_particles = 1 << 24;
|
// let n_particles = 1 << 24;
|
||||||
// let n_particles = 1 << 16;
|
let n_particles = 1 << 16;
|
||||||
println!("n_particles: {}", n_particles);
|
println!("n_particles: {}", n_particles);
|
||||||
|
|
||||||
let diffusivity = 1;
|
let diffusivity = 1;
|
||||||
|
|||||||
137
src/model.rs
137
src/model.rs
@ -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.
|
// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Agent {
|
pub struct Agent {
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
@ -34,6 +34,60 @@ impl Agent {
|
|||||||
i,
|
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 {
|
impl Clone for Agent {
|
||||||
@ -61,7 +115,7 @@ impl PartialEq for Agent {
|
|||||||
// Top-level simulation class.
|
// Top-level simulation class.
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
// Physarum agents.
|
// Physarum agents.
|
||||||
agents: Vec<Agent>,
|
// agents: Vec<Agent>,
|
||||||
|
|
||||||
// The grid they move on.
|
// The grid they move on.
|
||||||
grids: Vec<Grid>,
|
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 {
|
Model {
|
||||||
agents: (0..(n_particles-1))
|
grids,
|
||||||
.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(),
|
|
||||||
attraction_table,
|
attraction_table,
|
||||||
diffusivity,
|
diffusivity,
|
||||||
iteration: 0,
|
iteration: 0,
|
||||||
@ -157,6 +213,7 @@ impl Model {
|
|||||||
let mut time_per_agent_list: Vec<f64> = Vec::new();
|
let mut time_per_agent_list: Vec<f64> = Vec::new();
|
||||||
let mut time_per_step_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 {
|
for i in 0..steps {
|
||||||
if debug {
|
if debug {
|
||||||
println!("Starting tick for all agents...")
|
println!("Starting tick for all agents...")
|
||||||
@ -165,68 +222,24 @@ impl Model {
|
|||||||
// Combine grids
|
// Combine grids
|
||||||
let grids = &mut self.grids;
|
let grids = &mut self.grids;
|
||||||
combine(grids, &self.attraction_table);
|
combine(grids, &self.attraction_table);
|
||||||
|
let grids_immutable_1 = &grids.clone();
|
||||||
|
|
||||||
let agents_tick_time = Instant::now();
|
let agents_tick_time = Instant::now();
|
||||||
|
|
||||||
// Tick agents
|
// Tick agents
|
||||||
self.agents.par_iter_mut().for_each(|agent| {
|
for grid in grids.iter_mut() {
|
||||||
let grid = &grids[agent.population_id];
|
grid.agents.par_iter_mut().for_each(|agent| {
|
||||||
let (width, height) = (grid.width, grid.height);
|
agent.tick(&grids_immutable_1[agent.population_id]);
|
||||||
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,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Deposit
|
// Deposit
|
||||||
for agent in self.agents.iter() {
|
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);
|
self.grids[agent.population_id].deposit(agent.x, agent.y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Diffuse + Decay
|
// Diffuse + Decay
|
||||||
let diffusivity = self.diffusivity;
|
let diffusivity = self.diffusivity;
|
||||||
@ -237,7 +250,7 @@ impl Model {
|
|||||||
self.save_image_data();
|
self.save_image_data();
|
||||||
|
|
||||||
let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64;
|
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_agent_list.push(ms_per_agent);
|
||||||
time_per_step_list.push(agents_tick_elapsed);
|
time_per_step_list.push(agents_tick_elapsed);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user