diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index afdec40..a2bd716 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -24,14 +24,17 @@ where file_thread: Option>, } -impl Drop for FileLinked -where T: Serialize +impl Drop for FileLinked +where + T: Serialize, { fn drop(&mut self) { - if self.file_thread.is_some() - { + if self.file_thread.is_some() { let file_thread = self.file_thread.take(); - file_thread.unwrap().join().expect("Error cleaning up file thread for file_linked object"); + file_thread + .unwrap() + .join() + .expect("Error cleaning up file thread for file_linked object"); } } } @@ -71,6 +74,8 @@ where /// assert_eq!(linked_test.readonly().b, String::from("two")); /// assert_eq!(linked_test.readonly().c, 3.0); /// # + /// # drop(linked_test); + /// # /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` @@ -109,6 +114,8 @@ where /// assert_eq!(linked_test.readonly().b, String::from("two")); /// assert_eq!(linked_test.readonly().c, 3.0); /// # + /// # drop(linked_test); + /// # /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` @@ -145,9 +152,8 @@ where match File::open(&self.path) { Ok(_) => { - let handle = std::thread::spawn(move || { - copy(&thread_path, &thread_temp_path).expect("Error copying temp file"); + copy(&thread_path, &thread_temp_path).expect("Unable to copy temp file"); let mut file = File::create(&thread_path).expect("Error creating file handle"); @@ -210,6 +216,8 @@ where /// /// assert_eq!(linked_test.readonly().a, 2); /// # + /// # drop(linked_test); + /// # /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # /// # Ok(()) @@ -261,6 +269,8 @@ where /// /// assert_eq!(linked_test.readonly().a, 2); /// # + /// # drop(linked_test); + /// # /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # /// # Ok(()) @@ -322,6 +332,8 @@ where /// assert_eq!(linked_test.readonly().b, test.b); /// assert_eq!(linked_test.readonly().c, test.c); /// # + /// # drop(linked_test); + /// # /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # /// # Ok(()) @@ -417,6 +429,8 @@ mod tests { "[1, 1, 3, 4, 5]" ); + drop(file_linked_list); + fs::remove_file("test.txt").expect("Unable to remove file"); Ok(()) diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 774cc92..add122d 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -52,11 +52,11 @@ fn main() -> anyhow::Result<()> { &PathBuf::from(file_path), GemlaConfig { generations_per_node: 1, - overwrite: false, + overwrite: true, }, ))?; - log_error(gemla.simulate(100).await)?; + log_error(gemla.simulate(3).await)?; Ok(()) }) diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index ab0d229..2b016f7 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -47,47 +47,61 @@ pub trait GeneticNode { /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as /// well as signal recovery. Transition states are given by [`GeneticState`] #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GeneticNodeWrapper -where - T: Clone, -{ - pub node: Option, +pub struct GeneticNodeWrapper { + node: Option, state: GeneticState, generation: u64, - pub total_generations: u64, + max_generations: u64, id: uuid::Uuid, } -impl GeneticNodeWrapper -where - T: GeneticNode + Debug + Clone, -{ - pub fn get_id(&self) -> uuid::Uuid { - self.id - } - - pub fn new(total_generations: u64) -> Self { +impl Default for GeneticNodeWrapper { + fn default() -> Self { GeneticNodeWrapper { node: None, state: GeneticState::Initialize, generation: 0, - total_generations, + max_generations: 1, id: uuid::Uuid::new_v4(), } } +} - pub fn from(data: T, total_generations: u64, id: uuid::Uuid) -> Self { +impl GeneticNodeWrapper +where + T: GeneticNode + Debug, +{ + pub fn new(max_generations: u64) -> Self { + GeneticNodeWrapper:: { + max_generations, + ..Default::default() + } + } + + pub fn from(data: T, max_generations: u64, id: uuid::Uuid) -> Self { GeneticNodeWrapper { node: Some(data), state: GeneticState::Simulate, generation: 0, - total_generations, + max_generations, id, } } - pub fn state(&self) -> &GeneticState { - &self.state + pub fn as_ref(&self) -> Option<&T> { + self.node.as_ref() + } + + pub fn id(&self) -> uuid::Uuid { + self.id + } + + pub fn max_generations(&self) -> u64 { + self.max_generations + } + + pub fn state(&self) -> GeneticState { + self.state } pub fn process_node(&mut self) -> Result { @@ -103,7 +117,7 @@ where .simulate() .with_context(|| format!("Error simulating node: {:?}", self))?; - self.state = if self.generation >= self.total_generations { + self.state = if self.generation >= self.max_generations { GeneticState::Finish } else { GeneticState::Mutate diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index d31ede2..e5cbd01 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -5,7 +5,6 @@ pub mod genetic_node; use crate::error::Error; use crate::tree::Tree; -use anyhow::anyhow; use file_linked::FileLinked; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; @@ -18,7 +17,7 @@ use std::mem::swap; use std::path::Path; use std::time::Instant; -type SimulationTree = Tree>; +type SimulationTree = Box>>; #[derive(Serialize, Deserialize)] pub struct GemlaConfig { @@ -49,18 +48,14 @@ where { pub fn new(path: &Path, config: GemlaConfig) -> Result { match File::open(path) { - Ok(file) => { - drop(file); - - Ok(Gemla { - data: if config.overwrite { - FileLinked::new((None, config), path)? - } else { - FileLinked::from_file(path)? - }, - threads: std::collections::HashMap::new(), - }) - } + Ok(_) => Ok(Gemla { + data: if config.overwrite { + FileLinked::new((None, config), path)? + } else { + FileLinked::from_file(path)? + }, + threads: std::collections::HashMap::new(), + }), Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { data: FileLinked::new((None, config), path)?, threads: std::collections::HashMap::new(), @@ -70,8 +65,14 @@ where } pub async fn simulate(&mut self, steps: u64) -> Result<(), Error> { - self.data - .mutate(|(d, c)| Gemla::increase_height(d, c, steps))??; + // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed + // in the tree and which nodes have not. + self.data.mutate(|(d, c)| { + let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); + swap(d, &mut tree); + })?; + + // println!("{}", serde_json::to_string(&self.data.readonly().0).expect("")); info!( "Height of simulation tree increased to {}", @@ -79,20 +80,21 @@ where ); loop { - if Gemla::tree_processed(self.data.readonly().0.as_ref().unwrap())? { + if Gemla::is_completed(self.data.readonly().0.as_ref().unwrap()) { self.join_threads().await?; info!("Processed tree"); break; } - let node_to_process = self.find_process_node(); + let node_to_process = + self.get_unprocessed_node(self.data.readonly().0.as_ref().unwrap()); if let Some(node) = node_to_process { - trace!("Adding node to process list {}", node.get_id()); + trace!("Adding node to process list {}", node.id()); self.threads - .insert(node.get_id(), Box::pin(Gemla::process_node(node))); + .insert(node.id(), Box::pin(Gemla::process_node(node))); } else { trace!("No node found to process, joining threads"); @@ -104,7 +106,7 @@ where } async fn join_threads(&mut self) -> Result<(), Error> { - if self.threads.len() > 0 { + if !self.threads.is_empty() { trace!("Joining threads for nodes {:?}", self.threads.keys()); let results = futures::future::join_all(self.threads.values_mut()).await; @@ -114,11 +116,15 @@ where self.threads.clear(); reduced_results.and_then(|r| { - if !self + let failed_nodes = self .data - .mutate(|(d, _)| Gemla::replace_nodes(d.as_mut().unwrap(), r))? - { - warn!("Unable to find nodes to replace in tree") + .mutate(|(d, _)| Gemla::replace_nodes(d.as_mut().unwrap(), r))?; + + if !failed_nodes.is_empty() { + warn!( + "Unable to find {:?} to replace in tree", + failed_nodes.iter().map(|n| n.id()) + ) } self.data @@ -132,46 +138,44 @@ where } fn merge_completed_nodes(tree: &mut SimulationTree) -> Result<(), Error> { - if tree.val.state() == &GeneticState::Initialize { + if tree.val.state() == GeneticState::Initialize { match (&mut tree.left, &mut tree.right) { (Some(l), Some(r)) - if l.val.state() == &GeneticState::Finish - && r.val.state() == &GeneticState::Finish => + if l.val.state() == GeneticState::Finish + && r.val.state() == GeneticState::Finish => { - info!("Merging nodes {} and {}", l.val.get_id(), r.val.get_id()); + info!("Merging nodes {} and {}", l.val.id(), r.val.id()); - let left_node = l.val.node.as_ref().unwrap(); - let right_node = r.val.node.as_ref().unwrap(); + let left_node = l.val.as_ref().unwrap(); + let right_node = r.val.as_ref().unwrap(); let merged_node = GeneticNode::merge(left_node, right_node)?; tree.val = GeneticNodeWrapper::from( *merged_node, - tree.val.total_generations, - tree.val.get_id(), + tree.val.max_generations(), + tree.val.id(), ); } (Some(l), Some(r)) => { Gemla::merge_completed_nodes(l)?; Gemla::merge_completed_nodes(r)?; } - (Some(l), None) if l.val.state() == &GeneticState::Finish => { - trace!("Copying node {}", l.val.get_id()); + (Some(l), None) if l.val.state() == GeneticState::Finish => { + trace!("Copying node {}", l.val.id()); - let left_node = l.val.clone(); tree.val = GeneticNodeWrapper::from( - left_node.node.unwrap(), - tree.val.total_generations, - tree.val.get_id(), + l.val.as_ref().unwrap().clone(), + tree.val.max_generations(), + tree.val.id(), ); } (Some(l), None) => Gemla::merge_completed_nodes(l)?, - (None, Some(r)) if r.val.state() == &GeneticState::Finish => { - trace!("Copying node {}", r.val.get_id()); + (None, Some(r)) if r.val.state() == GeneticState::Finish => { + trace!("Copying node {}", r.val.id()); - let right_node = r.val.clone(); tree.val = GeneticNodeWrapper::from( - right_node.node.unwrap(), - tree.val.total_generations, - tree.val.get_id(), + r.val.as_ref().unwrap().clone(), + tree.val.max_generations(), + tree.val.id(), ); } (None, Some(r)) => Gemla::merge_completed_nodes(r)?, @@ -182,22 +186,20 @@ where Ok(()) } - fn find_process_node_helper(&self, tree: &SimulationTree) -> Option> { - if tree.val.state() != &GeneticState::Finish - && !self.threads.contains_key(&tree.val.get_id()) - { + fn get_unprocessed_node(&self, tree: &SimulationTree) -> Option> { + if tree.val.state() != GeneticState::Finish && !self.threads.contains_key(&tree.val.id()) { match (&tree.left, &tree.right) { (Some(l), Some(r)) - if l.val.state() == &GeneticState::Finish - && r.val.state() == &GeneticState::Finish => + if l.val.state() == GeneticState::Finish + && r.val.state() == GeneticState::Finish => { Some(tree.val.clone()) } (Some(l), Some(r)) => self - .find_process_node_helper(&*l) - .or_else(|| self.find_process_node_helper(&*r)), - (Some(l), None) => self.find_process_node_helper(&*l), - (None, Some(r)) => self.find_process_node_helper(&*r), + .get_unprocessed_node(l) + .or_else(|| self.get_unprocessed_node(r)), + (Some(l), None) => self.get_unprocessed_node(l), + (None, Some(r)) => self.get_unprocessed_node(r), (None, None) => Some(tree.val.clone()), } } else { @@ -205,83 +207,63 @@ where } } - fn find_process_node(&self) -> Option> { - let tree = self.data.readonly().0.as_ref(); - tree.and_then(|t| self.find_process_node_helper(&t)) - } - - fn replace_node( + fn replace_nodes( tree: &mut SimulationTree, - node: GeneticNodeWrapper, - ) -> Option> { - if tree.val.get_id() == node.get_id() { - tree.val = node; - None - } else { - match (&mut tree.left, &mut tree.right) { - (Some(l), Some(r)) => { - Gemla::replace_node(l, node).and_then(|n| Gemla::replace_node(r, n)) - } - (Some(l), None) => Gemla::replace_node(l, node), - (None, Some(r)) => Gemla::replace_node(r, node), - _ => Some(node), - } + mut nodes: Vec>, + ) -> Vec> { + if let Some(i) = nodes.iter().position(|n| n.id() == tree.val.id()) { + tree.val = nodes.remove(i); } - } - fn replace_nodes(tree: &mut SimulationTree, nodes: Vec>) -> bool { - nodes - .into_iter() - .map(|n| Gemla::replace_node(tree, n).is_none()) - .reduce(|a, b| a && b) - .unwrap_or(false) + match (&mut tree.left, &mut tree.right) { + (Some(l), Some(r)) => Gemla::replace_nodes(r, Gemla::replace_nodes(l, nodes)), + (Some(l), None) => Gemla::replace_nodes(l, nodes), + (None, Some(r)) => Gemla::replace_nodes(r, nodes), + _ => nodes, + } } fn increase_height( - tree: &mut Option>, + tree: Option>, config: &GemlaConfig, amount: u64, - ) -> Result<(), Error> { - for _ in 0..amount { - if tree.is_none() { - swap( - tree, - &mut Some(btree!(GeneticNodeWrapper::new(config.generations_per_node))), - ); - } else { - let height = tree.as_mut().unwrap().height() as u64; - let temp = tree.take(); - swap( - tree, - &mut Some(btree!( - GeneticNodeWrapper::new(config.generations_per_node), - temp.unwrap(), - btree!(GeneticNodeWrapper::new( - height * config.generations_per_node - )) - )), - ); - } - } + ) -> Option> { + if amount == 0 { + tree + } else { + let right_branch_height = + tree.as_ref().map(|t| t.height() as u64).unwrap_or(0) + amount - 1; - Ok(()) + Some(Box::new(Tree::new( + GeneticNodeWrapper::new(config.generations_per_node), + Gemla::increase_height(tree, config, amount - 1), + if right_branch_height > 0 { + Some(Box::new(btree!(GeneticNodeWrapper::new( + right_branch_height * config.generations_per_node + )))) + } else { + None + }, + ))) + } } - fn tree_processed(tree: &SimulationTree) -> Result { - if tree.val.state() == &GeneticState::Finish { + fn is_completed(tree: &SimulationTree) -> bool { + if tree.val.state() == GeneticState::Finish { match (&tree.left, &tree.right) { - (Some(l), Some(r)) => Ok(Gemla::tree_processed(l)? && Gemla::tree_processed(r)?), - (None, None) => Ok(true), - _ => Err(Error::Other(anyhow!("unable to process tree {:?}", tree))), + (Some(l), Some(r)) => Gemla::is_completed(l) && Gemla::is_completed(r), + (Some(l), None) => Gemla::is_completed(l), + (None, Some(r)) => Gemla::is_completed(r), + (None, None) => true, } } else { - Ok(false) + false } } async fn process_node(mut node: GeneticNodeWrapper) -> Result, Error> { let node_state_time = Instant::now(); - let node_state = *node.state(); + let node_state = node.state(); node.process_node()?; @@ -289,11 +271,11 @@ where "{:?} completed in {:?} for {}", node_state, node_state_time.elapsed(), - node.get_id() + node.id() ); - if node.state() == &GeneticState::Finish { - info!("Processed node {}", node.get_id()); + if node.state() == GeneticState::Finish { + info!("Processed node {}", node.id()); } Ok(node)