1use 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
11fn uuid4() -> String {
13 let uuid: u128 = rand::random();
14 format!("{uuid:032X}")
15}
16
17#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
19pub struct Individual {
20 pub name: String,
22
23 #[serde(default)]
26 pub ascension: Option<u64>,
27
28 #[serde(default)]
30 pub environment: String,
31
32 #[serde(default)]
34 pub population: String,
35
36 #[serde(default)]
39 pub species: String,
40
41 #[serde(default)]
43 pub controller: Vec<String>,
44
45 #[serde(skip)]
47 pub genome: OnceLock<Arc<[u8]>>,
48
49 #[serde(default)]
51 pub telemetry: HashMap<String, String>,
52
53 #[serde(default)]
55 pub epigenome: HashMap<String, String>,
56
57 #[serde(default)]
59 pub score: Option<String>,
60
61 #[serde(default)]
63 pub generation: u64,
64
65 #[serde(default)]
67 pub parents: Vec<String>,
68
69 #[serde(default)]
71 pub children: Vec<String>,
72
73 #[serde(default)]
76 pub birth_date: String,
77
78 #[serde(default)]
81 pub death_date: String,
82
83 #[serde(flatten)]
85 pub extra: HashMap<String, serde_json::Value>,
86
87 #[serde(skip)]
90 pub path: Option<PathBuf>,
91}
92
93impl Individual {
94 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 pub fn genome(&self) -> Arc<[u8]> {
120 self.genome
121 .get_or_init(|| {
122 let path = self.path.as_ref().expect("missing genome");
123 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 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 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 pub fn save(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
197 let mut path: PathBuf = path.as_ref().into();
198 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 self.genome();
208 if !path.exists() {
210 std::fs::create_dir(&path)?;
211 }
212 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 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()?; file.sync_all()?; std::fs::rename(&temp, &path)?; self.path = Some(path);
227 self.genome.take(); Ok(self.path.as_ref().unwrap())
229 }
230
231 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 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 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 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#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
286pub enum Replacement {
287 Unbounded,
293
294 Random,
296
297 Oldest,
299
300 Worst,
302
303 Generation,
305}
306
307pub type Selection = dyn Fn(&[Arc<Mutex<Individual>>], usize) -> Vec<Vec<Arc<Mutex<Individual>>>> + Send + Sync;
326
327pub 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
357pub 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 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 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 let score = score.unwrap_or_else(|| Arc::new(default_score));
465 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 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 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 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 pub fn get_replacement(&self) -> Replacement {
549 self.replacement
550 }
551 pub fn get_population_size(&self) -> usize {
553 self.population_size
554 }
555 pub fn get_ascension(&self) -> u64 {
557 self.ascension
558 }
559 pub fn get_generation(&self) -> u64 {
561 self.generation
562 }
563 pub fn get_members(&self) -> &[Arc<Mutex<Individual>>] {
567 &self.members
568 }
569 pub fn get_leaderboard(&self) -> &[Arc<Mutex<Individual>>] {
572 &self.leaderboard
573 }
574 pub fn get_hall_of_fame(&self) -> &[Arc<Mutex<Individual>>] {
577 &self.hall_of_fame
578 }
579 pub fn spawn(&mut self) -> Vec<Arc<Mutex<Individual>>> {
581 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 parents.sort_unstable_by_key(Arc::as_ptr);
595 parents.dedup_by_key(|parent| Arc::as_ptr(parent));
596 parents
597 }
598 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 individual.save(&self.path)?;
605 let individual = Arc::from(Mutex::from(individual));
606 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 match self.replacement {
645 Replacement::Unbounded | Replacement::Random | Replacement::Worst | Replacement::Oldest => {
646 self.members.push(individual.clone());
647 }
648 Replacement::Generation => {}
649 }
650 self.waiting.push(individual.clone());
652 if self.waiting.len() >= self.population_size {
653 self.rollover()?;
654 }
655 Ok(())
656 }
657 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 self.leaderboard.extend(
680 self.waiting
681 .iter()
682 .filter(|individual| (*self.score)(&individual.lock().unwrap()) > min_score)
683 .cloned(),
684 );
685 self.leaderboard.sort_by(compare_scores(self.score.as_ref()));
687 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 let n = self.hall_of_fame_size.min(self.waiting.len() - 1);
701 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 if self.replacement == Replacement::Generation {
713 std::mem::swap(&mut self.members, &mut self.waiting);
714 }
715 for individual in self.waiting.drain(..) {
717 Individual::drop(individual)?;
718 }
719 Ok(())
720 }
721}
722
723pub 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 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 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 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 individuals.shuffle(rng);
943 individuals.sort_by(super::compare_scores(&default_score));
944 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}