diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index c7ee63a..4fa771c 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" uuid = { version = "0.7", features = ["serde", "v4"] } clap = { version = "~2.27.0", features = ["yaml"] } toml = "0.5.8" diff --git a/gemla/src/bracket/genetic_state.rs b/gemla/src/bracket/genetic_state.rs new file mode 100644 index 0000000..ad1dc0f --- /dev/null +++ b/gemla/src/bracket/genetic_state.rs @@ -0,0 +1,7 @@ +pub trait GeneticState { + fn run_simulation(&mut self, iterations: u32); + + fn get_fit_score(&self) -> f64; + + fn initialize() -> Self; +} \ No newline at end of file diff --git a/gemla/src/bracket/mod.rs b/gemla/src/bracket/mod.rs index 169f987..4e7ec8b 100644 --- a/gemla/src/bracket/mod.rs +++ b/gemla/src/bracket/mod.rs @@ -1,75 +1,91 @@ -mod state; +pub mod genetic_state; use super::file_linked::FileLinked; use super::tree; +use std::fmt; use std::str::FromStr; -use uuid::Uuid; +use std::string::ToString; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; -impl tree::Tree { - pub fn run_simulation(&self) { - println!("================================"); - println!("Running simulation for node: {}", self.val); - println!( - "With children {} and {}\n", - tree::Tree::fmt_node(&self.left), - tree::Tree::fmt_node(&self.right) +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "enumType", content = "enumContent")] +pub enum IterationScaling { + Linear(u32) +} + +impl Default for IterationScaling { + fn default() -> Self { + IterationScaling::Linear(1) + } +} + +impl fmt::Display for IterationScaling { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", serde_json::to_string(self).expect("Unable to deserialize IterationScaling struct")) + } +} + + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Bracket { + tree: tree::Tree, + step: u32, + iteration_scaling: IterationScaling +} + +impl fmt::Display for Bracket { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", serde_json::to_string(self).expect("Unable to deserialize Bracket struct")) + } +} + +impl Bracket + where T: genetic_state::GeneticState + ToString + FromStr + Default + fmt::Display + DeserializeOwned + Serialize + Clone +{ + pub fn initialize(file_path: String) -> Result, String> + { + FileLinked::new( + Bracket + { + tree: btree!(T::initialize()), + step: 0, + iteration_scaling: IterationScaling::default() + } + ,file_path) + } + + pub fn iteration_scaling(&mut self, iteration_scaling: IterationScaling) -> &mut Self + { + self.iteration_scaling = iteration_scaling; + self + } + + pub fn run_simulation_step(&mut self) -> &mut Self + { + self.tree.val.run_simulation( + match self.iteration_scaling + { + IterationScaling::Linear(x) => x + } ); - } -} - -// pub struct Bracket { -// tree: tree::Tree, -// directory: String -// } - -/// Constructs a tree with a given height while simultaneously running a simulation on each node. -fn build_tree(h: u32) -> Option> { - // Recursively building a tree and running the simulation after wards to ensure a bottom-up - // execution order. - if h != 0 { - let tree = btree!( - Uuid::new_v4(), - build_tree(h - 1), - build_tree(h - 1)); - tree.run_simulation(); - Some(tree) - } else { - None - } -} - -/// Generates a bracket tree and runs simulation against each node. -/// -/// TODO: Explain reasoning for bracket system against genetic algorithm. -pub fn run_bracket() { - let mut height = 1; - let mut tree = FileLinked::new( - build_tree(height).expect("Error getting result from build_tree"), - "for_tests", - ) - .expect("Unable to create file linked object from tree"); - - // Building tree one node at a time, appending to the top. - loop { - println!("========================================="); - println!("Running bracket..."); - height += 1; - tree.replace(btree!( - Uuid::new_v4(), - Some(tree.readonly().clone()), - build_tree(height) - )) - .expect("Error building up tree node"); - tree.readonly().run_simulation(); - - if height == 3 { - println!("{}\n\n", tree); - let s = format!("{}", tree); - println!("{}\n\n", s); - let tree2: tree::Tree = tree::Tree::from_str(&s).unwrap(); - println!("{}\n\n", tree2); - break; - } + + let mut new_branch = btree!(T::initialize()); + new_branch.val.run_simulation( + match self.iteration_scaling + { + IterationScaling::Linear(x) => x * (self.step + 1) + } + ); + + self.tree = btree!( + self.tree.val.clone(), + Some(self.tree.clone()), + Some(new_branch) + ); + self.step += 1; + + self } } diff --git a/gemla/src/bracket/state.rs b/gemla/src/bracket/state.rs deleted file mode 100644 index 61e3d63..0000000 --- a/gemla/src/bracket/state.rs +++ /dev/null @@ -1,18 +0,0 @@ -// use uuid::Uuid; -// use std::fmt; - -// pub struct State { -// id: Uuid -// } - -// impl fmt::Display for State { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// write!(f, "{}", self.id) -// } -// } - -// pub fn create(id: &Uuid) -> State { -// State { -// id: id.clone() -// } -// } diff --git a/gemla/src/file_linked/mod.rs b/gemla/src/file_linked/mod.rs index 8e38964..3f7320d 100644 --- a/gemla/src/file_linked/mod.rs +++ b/gemla/src/file_linked/mod.rs @@ -21,10 +21,10 @@ impl FileLinked where T: ToString, { - pub fn new(val: T, path: &str) -> Result, String> { + pub fn new(val: T, path: String) -> Result, String> { let result = FileLinked { val, - path: String::from(path), + path, }; result.write_data()?; @@ -32,7 +32,7 @@ where Ok(result) } - pub fn write_data(&self) -> Result<(), String> { + fn write_data(&self) -> Result<(), String> { let mut file = fs::OpenOptions::new() .write(true) .create(true) diff --git a/gemla/src/main.rs b/gemla/src/main.rs index 52e94e1..5bb2899 100644 --- a/gemla/src/main.rs +++ b/gemla/src/main.rs @@ -31,7 +31,6 @@ fn main() { Ok(m) if m.is_dir() => { println!("{} is a valid directory!", directory); println!("Building tree for {}.", directory); - bracket::run_bracket(); } Ok(_) => println!("{} is not a valid directory!", directory), _ => println!("{} does not exist!", directory), diff --git a/gemla/src/tests/bracket.rs b/gemla/src/tests/bracket.rs index 8b13789..cc9b273 100644 --- a/gemla/src/tests/bracket.rs +++ b/gemla/src/tests/bracket.rs @@ -1 +1,93 @@ +use super::super::bracket; +use std::str::FromStr; +use std::string::ToString; +use std::fmt; +use serde::{Deserialize, Serialize}; + +#[derive(Default, Deserialize, Serialize, Clone)] +struct TestState { + pub score: f64 +} + +impl FromStr for TestState +{ + type Err = String; + + fn from_str(s: &str) -> Result + { + toml::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) + } +} + +impl fmt::Display for TestState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.score) + } +} + +impl TestState { + fn new(score: f64) -> TestState { + TestState { score: score } + } +} + +impl bracket::genetic_state::GeneticState for TestState { + fn run_simulation(&mut self, iterations: u32) + { + self.score += iterations as f64; + } + + fn get_fit_score(&self) -> f64 { + self.score + } + + fn initialize() -> Self { + TestState { + score: 0.0 + } + } +} + +#[test] +fn test_new() { + + let bracket = bracket::Bracket::::initialize("./temp".to_string()) + .expect("Bracket failed to initialize"); + + assert_eq!( + format!("{}", bracket), + format!("{{\"tree\":{},\"step\":0,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":1}}}}", + btree!(TestState::new(0.0))) + ); +} + +#[test] +fn test_run() { + let mut bracket = bracket::Bracket::::initialize("./temp".to_string()) + .expect("Bracket failed to initialize"); + + bracket.mutate(|b| drop(b.iteration_scaling(bracket::IterationScaling::Linear(2)))) + .expect("Failed to set iteration scaling"); + + for _ in 0..3 { + bracket.mutate(|b| drop(b.run_simulation_step())) + .expect("Failed to run step"); + } + + assert_eq!( + format!("{}", bracket), + format!("{{\"tree\":{},\"step\":3,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":2}}}}", + btree!( + TestState::new(6.0), + Some(btree!( + TestState::new(6.0), + Some(btree!(TestState::new(4.0), + Some(btree!(TestState::new(2.0))), + Some(btree!(TestState::new(2.0))))), + Some(btree!(TestState::new(4.0))) + )), + Some(btree!(TestState::new(6.0))) + )) + ); +} \ No newline at end of file diff --git a/gemla/src/tests/file_linked.rs b/gemla/src/tests/file_linked.rs index 8d82eaa..b33e075 100644 --- a/gemla/src/tests/file_linked.rs +++ b/gemla/src/tests/file_linked.rs @@ -1,21 +1,20 @@ use super::super::file_linked::FileLinked; -use super::super::tree::Tree; #[test] fn test_mutate() -> Result<(), String> { let tree = btree!(1, Some(btree!(2)), Some(btree!(3, Some(btree!(4)),))); - let mut linked_tree = FileLinked::new(tree, "blah.txt")?; + let mut linked_tree = FileLinked::new(tree, String::from("blah.txt"))?; assert_eq!( format!("{}", linked_tree.readonly()), - "val = 1\n\n[left]\nval = 2\n\n[right]\nval = 3\n\n[right.left]\nval = 4\n" + "{\"val\":1,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}" ); linked_tree.mutate(|v1| v1.val = 10)?; assert_eq!( format!("{}", linked_tree.readonly()), - "val = 10\n\n[left]\nval = 2\n\n[right]\nval = 3\n\n[right.left]\nval = 4\n" + "{\"val\":10,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}" ); linked_tree.mutate(|v1| { @@ -26,7 +25,7 @@ fn test_mutate() -> Result<(), String> { assert_eq!( format!("{}", linked_tree.readonly()), - "val = 10\n\n[left]\nval = 13\n\n[right]\nval = 3\n\n[right.left]\nval = 4\n" + "{\"val\":10,\"left\":{\"val\":13,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}" ); Ok(()) diff --git a/gemla/src/tests/tree.rs b/gemla/src/tests/tree.rs index 6cd77f1..fe4bca7 100644 --- a/gemla/src/tests/tree.rs +++ b/gemla/src/tests/tree.rs @@ -20,7 +20,7 @@ fn test_new() { fn test_fmt() { assert_eq!( format!("{}", btree!("foo", Some(btree!("bar")),),), - "val = \"foo\"\n\n[left]\nval = \"bar\"\n" + "{\"val\":\"foo\",\"left\":{\"val\":\"bar\",\"left\":null,\"right\":null},\"right\":null}" ); } @@ -30,7 +30,7 @@ fn test_fmt_node() { assert_eq!(Tree::fmt_node(&t.left), "16"); assert_eq!( Tree::fmt_node(&Some(Box::new(btree!(btree!("foo"))))), - "val = \"foo\"\n" + "{\"val\":\"foo\",\"left\":null,\"right\":null}" ); assert_eq!(Tree::::fmt_node(&None), "_"); } diff --git a/gemla/src/tree/mod.rs b/gemla/src/tree/mod.rs index 6ff31e2..274c167 100644 --- a/gemla/src/tree/mod.rs +++ b/gemla/src/tree/mod.rs @@ -88,7 +88,7 @@ macro_rules! btree { $crate::tree::Tree::new($val, $l.and_then(|l| Some(Box::new(l))), None) }; ($val:expr) => { - Tree::new($val, None, None) + $crate::tree::Tree::new($val, None, None) }; } @@ -111,7 +111,7 @@ impl Tree { impl fmt::Display for Tree { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let result = toml::to_string(self); + let result = serde_json::to_string(self); match result { Ok(string) => write!(f, "{}", string), @@ -127,6 +127,6 @@ where type Err = String; fn from_str(s: &str) -> Result { - toml::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) + serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) } }