Add rayon to genetic_algorithm_v1

This commit is contained in:
hal8174 2025-02-18 22:02:31 +01:00
parent 81edc8e67a
commit 3b8865c169
4 changed files with 72 additions and 57 deletions

View file

@ -4,7 +4,7 @@ use factorio_blueprint::abstraction::{Blueprint, Entity};
use factorio_core::{beltoptions::Beltspeed, prelude::*, visualize::Visualize}; use factorio_core::{beltoptions::Beltspeed, prelude::*, visualize::Visualize};
use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock}; use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock};
use factorio_pathfinding::Pathfinder; use factorio_pathfinding::Pathfinder;
use rand::{Rng, seq::IndexedRandom}; use rand::{Rng, SeedableRng, seq::IndexedRandom};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::assembly::assembly_line_2_input; use crate::assembly::assembly_line_2_input;
@ -38,11 +38,11 @@ pub struct FactoryConnection {
pub to: usize, pub to: usize,
} }
pub fn generate_factory<L: Layouter, P: Pathfinder>( pub fn generate_factory<L: Layouter, P: Pathfinder + Sync, R: Rng + SeedableRng + Send + Sync>(
layouter: &L, layouter: &L,
pathfinder: &P, pathfinder: &P,
factory_graph: FactoryGraph, factory_graph: FactoryGraph,
rng: &mut impl Rng, rng: &mut R,
) -> Blueprint { ) -> Blueprint {
let mut blocks = Vec::new(); let mut blocks = Vec::new();
let mut blueprints = Vec::new(); let mut blueprints = Vec::new();

View file

@ -14,3 +14,4 @@ clap = { version = "4.4.8", features = ["derive"] }
miette = { version = "7.2.0", features = ["fancy"] } miette = { version = "7.2.0", features = ["fancy"] }
serde_yaml = "0.9.34" serde_yaml = "0.9.34"
tracing = "0.1.41" tracing = "0.1.41"
rayon = "1.10.0"

View file

@ -1,3 +1,4 @@
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use tracing::{Level, field::Empty, info, span, trace, warn}; use tracing::{Level, field::Empty, info, span, trace, warn};
use crate::{ use crate::{
@ -16,30 +17,39 @@ pub struct GeneticAlgorithm {
} }
impl Layouter for GeneticAlgorithm { impl Layouter for GeneticAlgorithm {
fn layout<R: rand::Rng, P: factorio_pathfinding::Pathfinder>( fn layout<R, P>(
&self, &self,
input: &crate::LayoutInput, input: &crate::LayoutInput,
pathfinder: &P, pathfinder: &P,
rng: &mut R, rng: &mut R,
) -> Option<crate::LayoutResult> { ) -> Option<crate::LayoutResult>
where
R: rand::Rng + rand::SeedableRng + Send + Sync,
P: factorio_pathfinding::Pathfinder + Sync,
{
assert!(self.population_new + self.population_keep <= self.population_size); assert!(self.population_new + self.population_keep <= self.population_size);
let _complete_span = span!(Level::TRACE, "genetic_algorithm_v1").entered(); let _complete_span = span!(Level::TRACE, "genetic_algorithm_v1").entered();
let mut population = Vec::new(); let mut population: Vec<(LayoutResult, usize)>;
let mut rngs: Vec<_> = (0..self.population_size)
.map(|_| R::from_rng(rng))
.collect();
{ {
let _initial_generation_span = span!(Level::TRACE, "initial generation").entered(); let _initial_generation_span = span!(Level::TRACE, "initial generation").entered();
for i in 0..self.population_size { population = (0..self.population_size, &mut rngs)
let _layout_span = span!(Level::TRACE, "layout", i).entered(); .into_par_iter()
loop { .map(|(i, rng)| {
if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { let _layout_span = span!(Level::TRACE, "layout", i).entered();
let score = score(input, &l); loop {
population.push((l, score)); if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) {
break; let score = score(input, &l);
break (l, score);
}
warn!("Unable to generate valid layout");
} }
warn!("Unable to generate valid layout"); })
} .collect();
}
} }
population.sort_by_key(|(_, s)| *s); population.sort_by_key(|(_, s)| *s);
@ -49,51 +59,55 @@ impl Layouter for GeneticAlgorithm {
let generation_span = let generation_span =
span!(Level::TRACE, "generation", generation = g, score = Empty).entered(); span!(Level::TRACE, "generation", generation = g, score = Empty).entered();
{ let (keep, modify) = population.split_at_mut(self.population_keep);
let _mutate_span = span!(Level::TRACE, "mutate").entered();
for i in 0..(self.population_size - self.population_keep - self.population_new) {
let _layout_span = span!(Level::TRACE, "layout", i).entered();
loop {
let parent = &population[i % self.population_keep].0;
if let Some(blocks) = mutate(input, parent, rng) {
let (connections, map) =
path_input_from_blocks_positions(input, parent.size, &blocks);
if let Some(paths) = (
pathfinder.find_paths(factorio_pathfinding::PathInput { (0..self.population_size - self.population_keep),
connections: &connections, &mut rngs,
map, modify,
}) )
{ .into_par_iter()
let r = LayoutResult { .for_each(|(i, rng, m)| {
positions: blocks, if i < self.population_new {
path_result: paths, {
size: parent.size, let _layout_span = span!(Level::TRACE, "new layout", i).entered();
}; loop {
let score = score(input, &r); if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) {
population.push((r, score)); let score = score(input, &l);
break; let _ = std::mem::replace(m, (l, score));
break;
}
warn!("Unable to generate valid layout");
} }
} }
trace!("unsuccesfull mutation"); } else {
} let _layout_span = span!(Level::TRACE, "layout", i).entered();
} loop {
} let parent = &keep[i % keep.len()].0;
if let Some(blocks) = mutate(input, parent, rng) {
let (connections, map) =
path_input_from_blocks_positions(input, parent.size, &blocks);
{ if let Some(paths) =
let _new_span = span!(Level::TRACE, "new").entered(); pathfinder.find_paths(factorio_pathfinding::PathInput {
for i in 0..self.population_new { connections: &connections,
let _layout_span = span!(Level::TRACE, "layout", i).entered(); map,
loop { })
if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { {
let score = score(input, &l); let r = LayoutResult {
population[self.population_size - self.population_new + i] = (l, score); positions: blocks,
break; path_result: paths,
size: parent.size,
};
let score = score(input, &r);
let _ = std::mem::replace(m, (r, score));
break;
}
}
trace!("unsuccesfull mutation");
} }
warn!("Unable to generate valid layout");
} }
} });
}
population.sort_by_key(|(_, s)| *s); population.sort_by_key(|(_, s)| *s);
if population[0].1 < best_result.1 { if population[0].1 < best_result.1 {

View file

@ -5,7 +5,7 @@ use factorio_core::{
visualize::{self, Visualization, Visualize}, visualize::{self, Visualization, Visualize},
}; };
use factorio_pathfinding::Pathfinder; use factorio_pathfinding::Pathfinder;
use rand::Rng; use rand::{Rng, SeedableRng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod genetic_algorithm_v1; pub mod genetic_algorithm_v1;
@ -51,7 +51,7 @@ pub struct LayoutResult {
} }
pub trait Layouter { pub trait Layouter {
fn layout<R: Rng, P: Pathfinder>( fn layout<R: Rng + SeedableRng + Send + Sync, P: Pathfinder + Sync>(
&self, &self,
input: &LayoutInput, input: &LayoutInput,
pathfinder: &P, pathfinder: &P,