Skip to main content

npc_maker/
evo.rs

1//! Evolutionary algorithms and supporting tools.
2
3use mate_selection::MateSelection;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fs::File;
7use std::io::{BufRead, BufReader, BufWriter, Error, Read, Write};
8use std::path::{Path, PathBuf};
9use std::sync::{Arc, Mutex, Once, OnceLock};
10
11/// Generate a universally unique name. This will never return the same name twice.
12fn uuid4() -> String {
13    let uuid: u128 = rand::random();
14    format!("{uuid:032X}")
15}
16
17/// Container for a distinct life-form and all of its associated data.
18#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
19pub struct Individual {
20    /// Name or UUID of this individual.
21    pub name: String,
22
23    /// Number of individuals who died before this one,
24    /// or None if this individual has not yet died.
25    #[serde(default)]
26    pub ascension: Option<u64>,
27
28    /// Name of the environment that this individual lives in.
29    #[serde(default)]
30    pub environment: String,
31
32    /// Name of the population that this individual belongs to.
33    #[serde(default)]
34    pub population: String,
35
36    /// Name or UUID of this individual's species.
37    /// Mating may be restricted to individuals of the same species.
38    #[serde(default)]
39    pub species: String,
40
41    /// Command line invocation of the controller program.
42    #[serde(default)]
43    pub controller: Vec<String>,
44
45    /// Genetic parameters for this individual.
46    #[serde(skip)]
47    pub genome: OnceLock<Arc<[u8]>>,
48
49    /// The environmental info dictionary. The environment updates this information.
50    #[serde(default)]
51    pub telemetry: HashMap<String, String>,
52
53    /// The epigenetic info dictionary. The controller updates this information.
54    #[serde(default)]
55    pub epigenome: HashMap<String, String>,
56
57    /// Reproductive fitness of this individual, as assessed by the environment.
58    #[serde(default)]
59    pub score: Option<String>,
60
61    /// Number of cohorts that passed before this individual was born.
62    #[serde(default)]
63    pub generation: u64,
64
65    /// The names of this individual's parents.
66    #[serde(default)]
67    pub parents: Vec<String>,
68
69    /// The names of this individual's children.
70    #[serde(default)]
71    pub children: Vec<String>,
72
73    /// Time of birth, as a UTC timestamp, or an empty string if this individual
74    /// has not yet been born.
75    #[serde(default)]
76    pub birth_date: String,
77
78    /// Time of death, as a UTC timestamp, or an empty string if this individual
79    /// has not yet died.
80    #[serde(default)]
81    pub death_date: String,
82
83    /// Custom / unofficial fields that are saved with the individual.
84    #[serde(flatten)]
85    pub extra: HashMap<String, serde_json::Value>,
86
87    /// The file path this individual was loaded from or saved to, or None
88    /// if this individual has not touched the file system.
89    #[serde(skip)]
90    pub path: Option<PathBuf>,
91}
92
93impl Individual {
94    /// Create a new individual.
95    pub fn new(environment: &str, population: &str, controller: &[&str], genome: Box<[u8]>) -> Individual {
96        Individual {
97            name: uuid4(),
98            ascension: None,
99            environment: environment.to_string(),
100            population: population.to_string(),
101            species: uuid4(),
102            controller: controller.iter().map(|arg| arg.to_string()).collect(),
103            genome: OnceLock::from(Arc::from(genome)),
104            telemetry: HashMap::new(),
105            epigenome: HashMap::new(),
106            score: None,
107            generation: 0,
108            parents: Vec::new(),
109            children: Vec::new(),
110            birth_date: String::new(),
111            death_date: String::new(),
112            extra: HashMap::new(),
113            path: None,
114        }
115    }
116
117    /// Get the genetic parameters for this individual. This loads the genome
118    /// from file if necessary.
119    pub fn genome(&self) -> Arc<[u8]> {
120        self.genome
121            .get_or_init(|| {
122                let path = self.path.as_ref().expect("missing genome");
123                // Safe to unwrap this file access, because we already accessed
124                // this file at least once since the start of this program run.
125                let mut file = BufReader::new(File::open(path).unwrap());
126                file.skip_until(b'\0').unwrap();
127                let mut data = vec![];
128                file.read_to_end(&mut data).unwrap();
129                Arc::from(data)
130            })
131            .clone()
132    }
133
134    /// Asexually reproduce an individual.
135    pub fn asex(&mut self, child_genome: &[u8]) -> Individual {
136        let individual = Individual {
137            name: uuid4(),
138            ascension: None,
139            environment: self.environment.clone(),
140            population: self.population.clone(),
141            species: self.species.clone(),
142            controller: self.controller.clone(),
143            genome: OnceLock::from(Arc::from(child_genome)),
144            telemetry: HashMap::new(),
145            epigenome: HashMap::new(),
146            score: None,
147            generation: self.generation + 1,
148            parents: vec![self.name.clone()],
149            children: vec![],
150            birth_date: String::new(),
151            death_date: String::new(),
152            extra: HashMap::new(),
153            path: None,
154        };
155        self.children.push(individual.name.clone());
156        individual
157    }
158
159    /// Sexually reproduce two individuals.
160    pub fn sex(&mut self, other: &mut Individual, child_genome: &[u8]) -> Individual {
161        let individual = Individual {
162            name: uuid4(),
163            ascension: None,
164            environment: self.environment.clone(),
165            population: self.population.clone(),
166            species: self.species.clone(),
167            controller: self.controller.clone(),
168            genome: OnceLock::from(Arc::from(child_genome)),
169            telemetry: HashMap::new(),
170            epigenome: HashMap::new(),
171            score: None,
172            generation: self.generation.max(other.generation) + 1,
173            parents: vec![self.name.clone(), other.name.clone()],
174            children: vec![],
175            birth_date: String::new(),
176            death_date: String::new(),
177            extra: HashMap::new(),
178            path: None,
179        };
180        self.children.push(individual.name.clone());
181        other.children.push(individual.name.clone());
182        individual
183    }
184
185    fn file_name(&self) -> String {
186        format!("{}.indiv", self.name)
187    }
188
189    /// Save an individual to a file.
190    ///
191    /// Argument path is the directory to save in. Optional, use empty string
192    /// for temporary file. The filename will be the individual's name with the
193    /// ".indiv" file extension.
194    ///
195    /// Returns the file path of the saved individual.
196    pub fn save(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
197        let mut path: PathBuf = path.as_ref().into();
198        // Fill in default path.
199        if path.to_str() == Some("") {
200            if let Some(save_file) = self.path.as_ref() {
201                path = save_file.parent().unwrap().into();
202            } else {
203                path = std::env::temp_dir();
204            }
205        }
206        // Load the genome from file before modifying the file system.
207        self.genome();
208        // Make the directory in case this is the first individual to be saved to it.
209        if !path.exists() {
210            std::fs::create_dir(&path)?;
211        }
212        // Make paths to temporary buffer and final file locations.
213        let file_name = self.file_name();
214        let mut temp = std::env::temp_dir();
215        temp.push(format!("{file_name}.tmp"));
216        path.push(file_name);
217        //
218        let file = File::create(&temp)?;
219        let mut buf = BufWriter::new(file);
220        serde_json::to_writer(&mut buf, self).unwrap();
221        buf.write_all(b"\0")?;
222        buf.write_all(&self.genome())?;
223        let file = buf.into_inner()?; // flush the buffer
224        file.sync_all()?; // push to disk
225        std::fs::rename(&temp, &path)?; // move file into place
226        self.path = Some(path);
227        self.genome.take(); // free the genome
228        Ok(self.path.as_ref().unwrap())
229    }
230
231    /// Load a previously saved individual.
232    pub fn load(path: impl AsRef<Path>) -> Result<Individual, Error> {
233        let path = path.as_ref().to_path_buf();
234        let mut file = BufReader::new(File::open(&path)?);
235        let mut text = vec![];
236        file.read_until(b'\0', &mut text)?;
237        text.pop_if(|&mut char| char == b'\0');
238        let mut individual: Individual = serde_json::from_slice(&text)?;
239        individual.path = Some(path);
240        Ok(individual)
241    }
242
243    /// Get all individuals saved in a directory.
244    pub fn load_dir(path: impl AsRef<Path>) -> Result<Vec<Individual>, Error> {
245        let path = path.as_ref();
246        if !path.exists() {
247            return Ok(vec![]);
248        }
249        let mut individuals = vec![];
250        for file in path.read_dir()? {
251            let file = file?;
252            if !file.file_type()?.is_file() {
253                continue;
254            }
255            let file_name = file.file_name();
256            let Some(file_name) = file_name.to_str() else {
257                continue;
258            };
259            if file_name.ends_with(".indiv") {
260                individuals.push(Individual::load(file.path())?);
261            }
262        }
263        Ok(individuals)
264    }
265
266    /// Remove this individual and its associated save file.
267    pub fn delete(self) -> Result<(), Error> {
268        if let Some(path) = self.path {
269            std::fs::remove_file(path)?;
270        }
271        Ok(())
272    }
273
274    /// Delete the individual if this is the last reference to it.
275    pub fn drop(this: Arc<Mutex<Self>>) -> Result<(), Error> {
276        if let Some(mutex) = Arc::into_inner(this) {
277            let individual = mutex.into_inner().unwrap();
278            individual.delete()?;
279        }
280        Ok(())
281    }
282}
283
284/// Controls how a population replaces its members once it's full.
285#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
286pub enum Replacement {
287    /*
288    /// Do not add or remove members.
289    Frozen,
290    */
291    /// Do not replace members. The population grows without bounds.
292    Unbounded,
293
294    /// Replace members at random.
295    Random,
296
297    /// Replace the oldest member.
298    Oldest,
299
300    /// Replace the lowest scoring member.
301    Worst,
302
303    /// Replace generations entirely and all at once.
304    Generation,
305}
306
307/// Evolutionary algorithms choose which parent to reproduce with this
308/// user-supplied function.
309///
310/// The first argument is the current mating population.
311///
312/// The second argument is the requested number of children to be spawned.
313///
314/// Returns a list of parent groups, where each parent group is a list of
315/// parents to be mated together. The caller should take the following action
316/// depending on how many unique parents are in a group.
317///
318/// | Parents | Action |
319/// | --- | --- |
320/// | 0 | Use the initial genetic material |
321/// | 1 | Asexually reproduce the parent |
322/// | 2 | Sexually reproduce the parents |
323/// | 3+ | Unspecified |
324///
325pub type Selection = dyn Fn(&[Arc<Mutex<Individual>>], usize) -> Vec<Vec<Arc<Mutex<Individual>>>> + Send + Sync;
326
327/// Individuals may have custom scores functions with this type signature.
328///
329/// By default the npc_maker will parse the individual's score field into a single
330/// floating point number, with a default of `-inf` for missing or invalid scores.
331pub type Score = dyn Fn(&Individual) -> f64 + Send + Sync;
332
333const DEFAULT_SCORE: f64 = f64::NEG_INFINITY;
334
335fn default_score(individual: &Individual) -> f64 {
336    if let Some(score) = &individual.score {
337        score.parse().unwrap_or(DEFAULT_SCORE)
338    } else {
339        DEFAULT_SCORE
340    }
341}
342
343fn compare_scores(score_fn: &Score) -> impl Fn(&Arc<Mutex<Individual>>, &Arc<Mutex<Individual>>) -> std::cmp::Ordering {
344    move |a, b| {
345        let a_score = score_fn(&a.lock().unwrap());
346        let b_score = score_fn(&b.lock().unwrap());
347        match (a_score.is_nan(), b_score.is_nan()) {
348            (false, false) => a_score.total_cmp(&b_score),
349            (true, true) => a_score.total_cmp(&b_score),
350            (false, true) => std::cmp::Ordering::Greater,
351            (true, false) => std::cmp::Ordering::Less,
352        }
353        .reverse()
354    }
355}
356
357// TOOD: Consider introducing a mutex lock into the population so that
358// Evolution.spawn() and Evolution.death() are immutable. This would allow finer
359// grained locking: move the file-system tasks out of the mutex-locked critical
360// section.
361//
362// Pseudo Code:
363//
364// fn spawn(&self)
365//      1) lock mutex
366//      2) refill parents queue if empty
367//      3) pop & return parent group
368//      4) release mutex
369//
370// fn death(&self, individual)
371//      1) individual.save()
372//      2) lock mutex
373//      3) reckon the population
374//      4) release mutex
375//      5) delete individuals
376//
377
378/// Container for an evolving population of individuals.
379///
380/// Features:
381/// * Several strategies for replacing individuals with new ones,
382/// * Persistence by saving to file,
383/// * A Leaderboard and Hall of Fame.
384pub struct Evolution {
385    path: PathBuf,
386
387    replacement: Replacement,
388
389    selection: Arc<Selection>,
390
391    score: Arc<Score>,
392
393    population_size: usize,
394
395    leaderboard_size: usize,
396
397    hall_of_fame_size: usize,
398
399    ascension: u64,
400
401    generation: u64,
402
403    members: Vec<Arc<Mutex<Individual>>>,
404
405    waiting: Vec<Arc<Mutex<Individual>>>,
406
407    leaderboard: Vec<Arc<Mutex<Individual>>>,
408
409    hall_of_fame: Vec<Arc<Mutex<Individual>>>,
410
411    parents: Vec<Vec<Arc<Mutex<Individual>>>>,
412}
413
414#[derive(Serialize, Deserialize)]
415struct EvolutionMetadata {
416    ascension: u64,
417    generation: u64,
418    members: Vec<String>,
419    waiting: Vec<String>,
420    leaderboard: Vec<String>,
421    hall_of_fame: Vec<String>,
422}
423
424impl Evolution {
425    /// Argument `path` is a directory where this will save the population to.
426    /// If path is an empty string, a temporary directory will be created.
427    ///
428    /// Argument `replacement` controls how new members are added once the size of
429    /// the population reaches the population_size argument.
430    ///
431    /// Argument `selection` controls which individuals are allowed to mate and
432    /// with whom.
433    ///
434    /// Argument `score` is an optional custom scoring function.
435    ///
436    /// Argument `population_size` controls the total size of the mating
437    /// population.
438    ///
439    /// Argument `leaderboard_size` is the number of the best scoring individuals
440    /// to save in perpetuity. Set to zero to disable the leaderboard.
441    ///
442    /// Argument `hall_of_fame_size` is the number of individuals from each
443    /// generation to induct in to the hall of fame. Set to zero to disable the
444    /// hall of fame.
445    pub fn new(
446        path: impl AsRef<Path>,
447        replacement: Option<Replacement>,
448        selection: Option<Arc<Selection>>,
449        score: Option<Arc<Score>>,
450        population_size: usize,
451        leaderboard_size: usize,
452        hall_of_fame_size: usize,
453    ) -> Result<Evolution, Error> {
454        let mut path = path.as_ref().to_path_buf();
455        // Fill in empty path with temp dir.
456        if path.to_str() == Some("") {
457            path = std::env::temp_dir();
458            path.push(format!("pop{:x}", rand::random_range(0..u64::MAX)));
459        }
460        if !path.exists() {
461            std::fs::create_dir(&path)?;
462        }
463        //
464        let score = score.unwrap_or_else(|| Arc::new(default_score));
465        //
466        let selection = selection.unwrap_or_else(|| {
467            const MEDIAN_PERCENT: f64 = 0.1;
468            let median = (population_size as f64 * MEDIAN_PERCENT).round() as usize;
469            let score_fn = score.clone();
470            Arc::new(move |population, spawn| {
471                let rng = &mut rand::rng();
472                let scores: Vec<f64> = population
473                    .iter()
474                    .map(|individual| score_fn(&individual.lock().unwrap()))
475                    .collect();
476                let index = mate_selection::RankedExponential(median).pairs(rng, spawn, scores);
477                index
478                    .iter()
479                    .map(|parents| parents.iter().map(|&i| population[i].clone()).collect())
480                    .collect()
481            })
482        });
483        let mut this = Evolution {
484            path,
485            replacement: replacement.unwrap_or(Replacement::Generation),
486            selection,
487            score,
488            population_size,
489            leaderboard_size,
490            hall_of_fame_size,
491            ascension: 0,
492            generation: 0,
493            members: Default::default(),
494            waiting: Default::default(),
495            leaderboard: Default::default(),
496            hall_of_fame: Default::default(),
497            parents: vec![],
498        };
499        this.load()?;
500        Ok(this)
501    }
502    fn save(&self) -> Result<(), Error> {
503        let get_name = |indiv: &Arc<Mutex<Individual>>| indiv.lock().unwrap().name.clone();
504        let metadata = EvolutionMetadata {
505            ascension: self.ascension,
506            generation: self.generation,
507            members: self.members.iter().map(get_name).collect(),
508            waiting: self.waiting.iter().map(get_name).collect(),
509            leaderboard: self.leaderboard.iter().map(get_name).collect(),
510            hall_of_fame: self.hall_of_fame.iter().map(get_name).collect(),
511        };
512        let path = self.get_metadata_path();
513        std::fs::write(path, serde_json::to_vec(&metadata).unwrap())?;
514        Ok(())
515    }
516    fn load(&mut self) -> Result<(), Error> {
517        let path = self.get_metadata_path();
518        if !path.exists() {
519            return Ok(());
520        }
521        let metadata: EvolutionMetadata = serde_json::from_slice(&std::fs::read(&path)?).unwrap();
522        self.ascension = metadata.ascension;
523        self.generation = metadata.generation;
524        //
525        let individuals: HashMap<String, Arc<Mutex<Individual>>> = Individual::load_dir(&self.path)?
526            .into_iter()
527            .map(|individual| (individual.name.to_string(), Arc::from(Mutex::from(individual))))
528            .collect();
529        let lookup = |individual: &String| individuals.get(individual).unwrap().clone();
530        self.members = metadata.members.iter().map(lookup).collect();
531        self.waiting = metadata.waiting.iter().map(lookup).collect();
532        self.leaderboard = metadata.leaderboard.iter().map(lookup).collect();
533        self.hall_of_fame = metadata.hall_of_fame.iter().map(lookup).collect();
534        // Sort the historical data to enforce invariants.
535        self.leaderboard.sort_by(compare_scores(self.score.as_ref()));
536        self.hall_of_fame
537            .sort_by_key(|x| x.lock().unwrap().ascension.unwrap_or(u64::MAX));
538        Ok(())
539    }
540    /// Get the `path` argument or a temporary directory.
541    pub fn get_path(&self) -> &Path {
542        &self.path
543    }
544    fn get_metadata_path(&self) -> PathBuf {
545        self.path.join("population.json")
546    }
547    /// Get the `replacement` argument.
548    pub fn get_replacement(&self) -> Replacement {
549        self.replacement
550    }
551    /// Get the `population_size` argument.
552    pub fn get_population_size(&self) -> usize {
553        self.population_size
554    }
555    /// Get the total number of individuals that have died.
556    pub fn get_ascension(&self) -> u64 {
557        self.ascension
558    }
559    /// Get the number of cohorts of `population_size` that have died.
560    pub fn get_generation(&self) -> u64 {
561        self.generation
562    }
563    /// Get the current members of the population.
564    ///
565    /// All modifications must be saved to file using `individual.save("")`
566    pub fn get_members(&self) -> &[Arc<Mutex<Individual>>] {
567        &self.members
568    }
569    /// Get the highest scoring individuals ever recorded. This is sorted
570    /// descending by score, so that `leaderboard[0]` is the best individual.
571    pub fn get_leaderboard(&self) -> &[Arc<Mutex<Individual>>] {
572        &self.leaderboard
573    }
574    /// Get the highest scoring individuals from each generation. This is sorted
575    /// by ascension, so that `hall_of_fame[0]` is the oldest.
576    pub fn get_hall_of_fame(&self) -> &[Arc<Mutex<Individual>>] {
577        &self.hall_of_fame
578    }
579    /// Get a list of parents to be mated together to produce a child.
580    pub fn spawn(&mut self) -> Vec<Arc<Mutex<Individual>>> {
581        // Refill parents buffer.
582        if self.parents.is_empty() {
583            let num_pairs = if self.get_replacement() == Replacement::Generation {
584                self.get_population_size()
585            } else {
586                1
587            };
588            let members = self.get_members();
589            assert!(!members.is_empty(), "population is empty");
590            self.parents.extend_from_slice(&(*self.selection)(members, num_pairs));
591        }
592        let mut parents = self.parents.pop().unwrap();
593        // Deduplicate the parents list.
594        parents.sort_unstable_by_key(Arc::as_ptr);
595        parents.dedup_by_key(|parent| Arc::as_ptr(parent));
596        parents
597    }
598    /// Add a new individual to this population.
599    pub fn death(&mut self, mut individual: Individual) -> Result<(), Error> {
600        debug_assert!(individual.ascension.is_none());
601        individual.ascension = Some(self.ascension);
602        self.ascension += 1;
603        //
604        individual.save(&self.path)?;
605        let individual = Arc::from(Mutex::from(individual));
606        // Make room in the current members list for another individual.
607        match self.replacement {
608            Replacement::Unbounded => {}
609            Replacement::Generation => {}
610            Replacement::Random => {
611                while !self.members.is_empty() && self.members.len() >= self.population_size {
612                    let random_index = rand::random_range(0..self.members.len());
613                    let random_individual = self.members.swap_remove(random_index);
614                    Individual::drop(random_individual)?;
615                }
616            }
617            Replacement::Worst => {
618                let compare_scores = compare_scores(self.score.as_ref());
619                while !self.members.is_empty() && self.members.len() >= self.population_size {
620                    let (worst_index, _worst_individual) = self
621                        .members
622                        .iter()
623                        .enumerate()
624                        .min_by(|a, b| compare_scores(a.1, b.1))
625                        .unwrap();
626                    let worst_individual = self.members.swap_remove(worst_index);
627                    Individual::drop(worst_individual)?;
628                }
629            }
630            Replacement::Oldest => {
631                while !self.members.is_empty() && self.members.len() >= self.population_size {
632                    let (oldest_index, _oldest_individual) = self
633                        .members
634                        .iter()
635                        .enumerate()
636                        .min_by_key(|(_index, individual)| individual.lock().unwrap().ascension)
637                        .unwrap();
638                    let oldest_individual = self.members.swap_remove(oldest_index);
639                    Individual::drop(oldest_individual)?;
640                }
641            }
642        }
643        // Save the individual into the current generation.
644        match self.replacement {
645            Replacement::Unbounded | Replacement::Random | Replacement::Worst | Replacement::Oldest => {
646                self.members.push(individual.clone());
647            }
648            Replacement::Generation => {}
649        }
650        // Stage the individual for the next generation and bookkeeping.
651        self.waiting.push(individual.clone());
652        if self.waiting.len() >= self.population_size {
653            self.rollover()?;
654        }
655        Ok(())
656    }
657    /// Force the next generation to replace the current generation, even if the
658    /// next generation has not reached the `population_size`. This is useful for
659    /// seeding a population with initial genetic material and then making
660    /// the seed material immediately available by calling this method.
661    pub fn rollover(&mut self) -> Result<(), Error> {
662        self.rollover_leaderboard()?;
663        self.rollover_hall_of_fame()?;
664        self.rollover_generation()?;
665        self.save()?;
666        Ok(())
667    }
668    fn rollover_leaderboard(&mut self) -> Result<(), Error> {
669        if self.leaderboard_size == 0 || self.waiting.is_empty() {
670            return Ok(());
671        }
672        let min_score = if self.leaderboard.len() >= self.leaderboard_size {
673            let individual = self.leaderboard.last().unwrap();
674            (*self.score)(&individual.lock().unwrap())
675        } else {
676            f64::NEG_INFINITY
677        };
678        // Sort together the existing leaderboard and the new contenders.
679        self.leaderboard.extend(
680            self.waiting
681                .iter()
682                .filter(|individual| (*self.score)(&individual.lock().unwrap()) > min_score)
683                .cloned(),
684        );
685        // Use stable sort to preserve ascension ordering.
686        self.leaderboard.sort_by(compare_scores(self.score.as_ref()));
687        // Remove low performing individuals from the leaderboard directory.
688        if self.leaderboard.len() > self.leaderboard_size {
689            for individual in self.leaderboard.drain(self.leaderboard_size..) {
690                Individual::drop(individual)?;
691            }
692        }
693        Ok(())
694    }
695    fn rollover_hall_of_fame(&mut self) -> Result<(), Error> {
696        if self.hall_of_fame_size == 0 || self.waiting.is_empty() {
697            return Ok(());
698        }
699        // Find the highest scoring individuals in the new generation.
700        let n = self.hall_of_fame_size.min(self.waiting.len() - 1);
701        // This should be a stable sort but std does not support it.
702        self.waiting
703            .select_nth_unstable_by(n, compare_scores(self.score.as_ref()));
704        let winners = &mut self.waiting[..n];
705        winners.sort_unstable_by_key(|individual| individual.lock().unwrap().ascension);
706        self.hall_of_fame.extend_from_slice(winners);
707        Ok(())
708    }
709    fn rollover_generation(&mut self) -> Result<(), Error> {
710        self.generation += 1;
711        // Move the next generation into place.
712        if self.replacement == Replacement::Generation {
713            std::mem::swap(&mut self.members, &mut self.waiting);
714        }
715        // Discard the old generation.
716        for individual in self.waiting.drain(..) {
717            Individual::drop(individual)?;
718        }
719        Ok(())
720    }
721}
722
723///
724pub struct EvolutionProgram {
725    pub genetics: Vec<String>,
726    pub ctrl: Vec<String>,
727    pub path: PathBuf,
728    pub replacement: Option<Replacement>,
729    pub selection: Option<Arc<Selection>>,
730    pub score: Option<Arc<Score>>,
731    pub population_size: usize,
732    pub leaderboard_size: usize,
733    pub hall_of_fame_size: usize,
734    pub threads: usize,
735    pub env: PathBuf,
736    pub settings: HashMap<String, String>,
737    pub rollover: Arc<dyn Fn(&Evolution) -> bool + Send + Sync>,
738}
739
740use super::env::{Environment, EnvironmentSpec, Message};
741use process_anywhere::{Computer, Process};
742
743impl EvolutionProgram {
744    pub fn main(self) -> Result<(), Error> {
745        let mut evo = Evolution::new(
746            self.path.clone(),
747            self.replacement,
748            self.selection.clone(),
749            self.score.clone(),
750            self.population_size,
751            self.leaderboard_size,
752            self.hall_of_fame_size,
753        )?;
754        let mut genetic_workers = vec![];
755        let local_host = Arc::new(Computer::new_local());
756        for _ in 0..self.threads {
757            genetic_workers.push(local_host.clone().exec(&self.genetics).unwrap());
758        }
759        for (index, worker) in genetic_workers.iter_mut().enumerate() {
760            let (mut seed, _phenome) = Self::recv(worker).unwrap();
761            if index == 0 {
762                for _ in 0..self.population_size {
763                    let mut seed = seed.asex(&seed.genome());
764                    seed.score = Some("0".to_string());
765                    evo.death(seed)?;
766                }
767            }
768        }
769        let evo = Arc::new(Mutex::new(evo));
770        let env_spec = Arc::new(EnvironmentSpec::new(&self.env));
771        let mut env_workers = vec![];
772        let exit = Arc::new(Once::new());
773        for mut genetic_worker in genetic_workers {
774            let env_spec = env_spec.clone();
775            let ctrl = self.ctrl.clone();
776            let settings = self.settings.clone();
777            let evo = evo.clone();
778            let local_host = local_host.clone();
779            let exit = exit.clone();
780            let worker = std::thread::spawn(move || {
781                let mut env = Environment::new(local_host, env_spec, false.into(), settings, None);
782                while !exit.is_completed() {
783                    let msg = loop {
784                        if let Some(msg) = env.poll().unwrap() {
785                            break msg;
786                        }
787                        std::thread::sleep(std::time::Duration::from_millis(10));
788                    };
789                    match msg {
790                        Message::Spawn { .. } => {
791                            let mut parents = evo.lock().unwrap().spawn();
792                            if parents.len() == 1 {
793                                parents.push(parents[0].clone());
794                            }
795                            assert!(parents.len() == 2);
796                            for individual in &parents {
797                                let mut individual = individual.lock().unwrap();
798                                let path = if let Some(path) = &individual.path {
799                                    path
800                                } else {
801                                    individual.save("").unwrap()
802                                };
803                                genetic_worker.send_line(&format!("{}", path.display())).unwrap();
804                            }
805                            let (mut indiv, phenome) = Self::recv(&mut genetic_worker).unwrap();
806                            indiv.controller = ctrl.clone();
807                            for individual in parents {
808                                Individual::drop(individual).unwrap();
809                            }
810                            env.birth(indiv, &phenome);
811                        }
812                        Message::Death { individual } => {
813                            evo.lock().unwrap().death(*individual).unwrap();
814                        }
815                        _ => {}
816                    }
817                }
818            });
819            env_workers.push(worker);
820        }
821        let mut generation = 0;
822        loop {
823            let current_generation = evo.lock().unwrap().get_generation();
824            if current_generation > generation {
825                generation = current_generation;
826                {
827                    // let evo = evo.lock().unwrap();
828                    if !(self.rollover)(&evo.clone().lock().unwrap()) {
829                        break;
830                    }
831                }
832            }
833            std::thread::sleep(std::time::Duration::from_millis(100));
834        }
835        exit.call_once(|| {});
836        for thread in env_workers {
837            thread.join().unwrap();
838        }
839        Ok(())
840    }
841
842    /// Receive a spawned individual from the genetic module.
843    fn recv(worker: &mut Process) -> Result<(Individual, Box<[u8]>), process_anywhere::Error> {
844        if let Ok(err_msg) = worker.error_bytes() {
845            std::io::stderr().lock().write_all(&err_msg).unwrap();
846        }
847        let result = Self::recv_inner(worker);
848        if let Ok(err_msg) = worker.error_bytes() {
849            std::io::stderr().lock().write_all(&err_msg).unwrap();
850        }
851        result
852    }
853
854    fn recv_inner(worker: &mut Process) -> Result<(Individual, Box<[u8]>), process_anywhere::Error> {
855        let path = worker.block_line()?;
856        let size = worker.block_line()?;
857        let size: usize = size.parse().unwrap();
858        let phenome = worker.block_bytes(size)?;
859        let individual = Individual::load(path).unwrap();
860        Ok((individual, phenome))
861    }
862}
863
864#[cfg(test)]
865mod tests {
866    use super::*;
867
868    #[test]
869    fn uuid4_len() {
870        for _ in 0..100 {
871            assert_eq!(uuid4().len(), 32);
872        }
873    }
874    #[test]
875    fn uuid4_unique() {
876        use std::collections::HashSet;
877        let unique = 1000;
878        assert_eq!((0..unique).map(|_| uuid4()).collect::<HashSet<String>>().len(), unique);
879    }
880    #[test]
881    fn indiv_save_load() {
882        let mut indiv1 = Individual::new("foo", "bar", &["ctrl", "prog"], Box::new(*b"beepboop"));
883        indiv1.extra.insert("X".into(), "Y".into());
884        let path = dbg!(indiv1.save(std::env::temp_dir())).unwrap();
885        let indiv2 = Individual::load(path).unwrap();
886        indiv1.genome();
887        indiv2.genome();
888        assert_eq!(indiv1, indiv2);
889    }
890    #[test]
891    fn pop_save_load() {
892        let mut pop1 = Evolution::new("", None, None, None, 10, 3, 2).unwrap();
893
894        for _ in 0..30 {
895            let mut genome = Box::new(*b"beepboop");
896            rand::fill(&mut genome[..]);
897            let mut indiv = Individual::new("foo", "bar", &["ctrl", "prog"], genome);
898            indiv.score = Some(rand::random::<f64>().to_string());
899            pop1.death(indiv).unwrap();
900        }
901        pop1.save().unwrap();
902        let pop2 = Evolution::new(pop1.get_path(), None, None, None, 10, 3, 2).unwrap();
903        assert_eq!(pop1.get_path(), pop2.get_path());
904        assert_eq!(pop1.get_ascension(), pop2.get_ascension());
905        assert_eq!(pop1.get_generation(), pop2.get_generation());
906        fn cmp_indiv(individuals: &[Arc<Mutex<Individual>>]) -> Vec<(Option<PathBuf>, Arc<[u8]>)> {
907            let mut stubs = individuals
908                .iter()
909                .map(|stub| {
910                    let stub = stub.lock().unwrap();
911                    (stub.path.clone(), stub.genome())
912                })
913                .collect::<Vec<(Option<PathBuf>, Arc<[u8]>)>>();
914            stubs.sort();
915            stubs
916        }
917        assert_eq!(cmp_indiv(pop1.get_members()), cmp_indiv(pop2.get_members()));
918        assert_eq!(cmp_indiv(pop1.get_leaderboard()), cmp_indiv(pop2.get_leaderboard()));
919        assert_eq!(cmp_indiv(pop1.get_hall_of_fame()), cmp_indiv(pop2.get_hall_of_fame()));
920        assert_eq!(pop2.get_members().len(), 10);
921        assert_eq!(pop2.get_leaderboard().len(), 3);
922        assert_eq!(pop2.get_hall_of_fame().len(), 6);
923    }
924    #[test]
925    fn compare_scores() {
926        use rand::seq::SliceRandom;
927        let rng = &mut rand::rng();
928        // Make some individuals with random scores.
929        let mut individuals = vec![];
930        for index in 0..30 {
931            let mut indiv = Individual::new("foo", "bar", &["ctrl", "prog"], Box::new(*b""));
932            indiv.score = Some(index.to_string());
933            individuals.push(Arc::new(Mutex::new(indiv)));
934        }
935        for index in 5..10 {
936            individuals[index].lock().unwrap().score = Some("corrupt".to_string());
937        }
938        for index in 10..15 {
939            individuals[index].lock().unwrap().score = Some(f64::NAN.to_string());
940        }
941        //
942        individuals.shuffle(rng);
943        individuals.sort_by(super::compare_scores(&default_score));
944        //
945        for index in 0..20 {
946            let score = default_score(&individuals[index].lock().unwrap());
947            assert!(dbg!(score) >= 0.0);
948        }
949    }
950    #[test]
951    fn evo() {
952        fn new_genome() -> Box<[u8]> {
953            rand::random_iter::<u8>()
954                .take(10)
955                .collect::<Vec<u8>>()
956                .into_boxed_slice()
957        }
958        fn mate_fn(a: &[u8], b: &[u8]) -> Box<[u8]> {
959            let n = a.len();
960            let crossover = rand::random_range(0..n);
961            let mut c = vec![];
962            c.extend_from_slice(&a[0..crossover]);
963            c.extend_from_slice(&b[crossover..n]);
964            mutate_fn(&mut c);
965            c.into_boxed_slice()
966        }
967        fn mutate_fn(x: &mut [u8]) {
968            if rand::random_bool(0.5) {
969                x[rand::random_range(0..x.len())] = rand::random();
970            }
971        }
972        fn eval(a: &[u8], b: &[u8]) -> String {
973            let abs_dif = a.iter().zip(b).map(|(&x, &y)| (x as f64 - y as f64).abs()).sum::<f64>();
974            (-abs_dif).to_string()
975        }
976        let target_genome = new_genome();
977        let seed_genome = new_genome();
978        let seed_score = eval(&seed_genome, &target_genome);
979        let mut seed = Individual::new("", "", &[], seed_genome);
980        seed.score = Some(seed_score);
981        let mut evo = Evolution::new("", None, None, None, 100, 3, 1).unwrap();
982        evo.death(seed).unwrap();
983        evo.rollover().unwrap();
984        while evo.get_generation() < 20 {
985            let mut parents = evo.spawn();
986            let mut child = if parents.len() == 1 {
987                let mom = parents.pop().unwrap();
988                let mut mom = mom.lock().unwrap();
989                let mut genome: Vec<u8> = mom.genome().iter().cloned().collect();
990                mutate_fn(&mut genome);
991                Individual::asex(&mut mom, &genome)
992            } else if parents.len() == 2 {
993                let mom = parents.pop().unwrap();
994                let dad = parents.pop().unwrap();
995                let mut mom = mom.lock().unwrap();
996                let mut dad = dad.lock().unwrap();
997                let genome = mate_fn(&mom.genome(), &dad.genome());
998                Individual::sex(&mut mom, &mut dad, &genome)
999            } else {
1000                panic!()
1001            };
1002            child.score = Some(eval(&child.genome(), &target_genome));
1003            evo.death(child).unwrap();
1004        }
1005        for indiv in evo.get_hall_of_fame() {
1006            println!("{}", indiv.lock().unwrap().score.as_ref().unwrap());
1007        }
1008        let best = evo.get_leaderboard()[0].clone();
1009        assert!(best.lock().unwrap().score.as_ref().unwrap().parse::<f64>().unwrap() > -100.0);
1010    }
1011}