Revising error types
This commit is contained in:
parent
d4af685500
commit
edc2f67897
8 changed files with 153 additions and 132 deletions
|
@ -15,4 +15,6 @@ categories = ["filesystem", "data-structures"]
|
|||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
anyhow = "1.0"
|
|
@ -4,11 +4,24 @@ extern crate serde;
|
|||
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Serialization(serde_json::Error),
|
||||
#[error(transparent)]
|
||||
IO(std::io::Error),
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
/// A wrapper around an object `T` that ties the object to a physical file
|
||||
pub struct FileLinked<T>
|
||||
|
@ -95,9 +108,12 @@ where
|
|||
/// # std::fs::remove_file("./temp").expect("Unable to remove file");
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new(val: T, path: path::PathBuf) -> Result<FileLinked<T>, String> {
|
||||
pub fn new(val: T, path: path::PathBuf) -> Result<FileLinked<T>, Error> {
|
||||
if path.is_file() {
|
||||
return Err(format!("{} is not a valid file path", path.display()));
|
||||
return Err(Error::IO(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
anyhow!("{} is not a valid file path", path.display()),
|
||||
)));
|
||||
}
|
||||
|
||||
let result = FileLinked { val, path };
|
||||
|
@ -107,20 +123,20 @@ where
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
fn write_data(&self) -> Result<(), String> {
|
||||
fn write_data(&self) -> Result<(), Error> {
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&self.path)
|
||||
.map_err(|_| format!("Unable to open path {}", self.path.display()))?;
|
||||
.with_context(|| format!("Unable to open path {}", self.path.display()))?;
|
||||
|
||||
write!(
|
||||
file,
|
||||
"{}",
|
||||
serde_json::to_string(&self.val).map_err(|e| e.to_string())?
|
||||
serde_json::to_string(&self.val).map_err(Error::Serialization)?
|
||||
)
|
||||
.or_else(|_| Err(String::from("Unable to write to file.")))?;
|
||||
.with_context(|| format!("Unable to write to file {}", self.path.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -143,7 +159,7 @@ where
|
|||
/// # pub c: f64
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let test = Test {
|
||||
/// a: 1,
|
||||
/// b: String::from(""),
|
||||
|
@ -164,7 +180,7 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn mutate<U, F: FnOnce(&mut T) -> U>(&mut self, op: F) -> Result<U, String> {
|
||||
pub fn mutate<U, F: FnOnce(&mut T) -> U>(&mut self, op: F) -> Result<U, Error> {
|
||||
let result = op(&mut self.val);
|
||||
|
||||
self.write_data()?;
|
||||
|
@ -189,7 +205,7 @@ where
|
|||
/// # pub c: f64
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let test = Test {
|
||||
/// a: 1,
|
||||
/// b: String::from(""),
|
||||
|
@ -214,7 +230,7 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn replace(&mut self, val: T) -> Result<(), String> {
|
||||
pub fn replace(&mut self, val: T) -> Result<(), Error> {
|
||||
self.val = val;
|
||||
|
||||
self.write_data()
|
||||
|
@ -245,7 +261,7 @@ where
|
|||
/// # pub c: f64
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let test = Test {
|
||||
/// a: 1,
|
||||
/// b: String::from("2"),
|
||||
|
@ -278,27 +294,33 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_file(path: path::PathBuf) -> Result<FileLinked<T>, String> {
|
||||
pub fn from_file(path: path::PathBuf) -> Result<FileLinked<T>, Error> {
|
||||
if !path.is_file() {
|
||||
return Err(format!("{} is not a valid file path", path.display()));
|
||||
return Err(Error::IO(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
anyhow!("{} is not a valid file path", path.display()),
|
||||
)));
|
||||
}
|
||||
|
||||
let metadata = path
|
||||
.metadata()
|
||||
.map_err(|_| format!("Error obtaining metadata for {}", path.display()))?;
|
||||
.with_context(|| format!("Error obtaining metadata for {}", path.display()))?;
|
||||
|
||||
if metadata.is_file() {
|
||||
let file = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&path)
|
||||
.map_err(|_| format!("Unable to open file {}", path.display()))?;
|
||||
.with_context(|| format!("Unable to open file {}", path.display()))?;
|
||||
|
||||
let val = serde_json::from_reader(file)
|
||||
.map_err(|_| String::from("Unable to parse value from file."))?;
|
||||
.with_context(|| String::from("Unable to parse value from file."))?;
|
||||
|
||||
Ok(FileLinked { val, path })
|
||||
} else {
|
||||
Err(format!("{} is not a file.", path.display()))
|
||||
return Err(Error::IO(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
anyhow!("{} is not a file.", path.display()),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +340,7 @@ mod tests {
|
|||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn test_mutate() -> Result<(), String> {
|
||||
fn test_mutate() -> Result<(), Error> {
|
||||
let list = vec![1, 2, 3, 4];
|
||||
let mut file_linked_list = FileLinked::new(list, path::PathBuf::from("test.txt"))?;
|
||||
|
||||
|
|
|
@ -18,4 +18,6 @@ uuid = { version = "0.7", features = ["serde", "v4"] }
|
|||
clap = { version = "~2.27.0", features = ["yaml"] }
|
||||
toml = "0.5.8"
|
||||
regex = "1"
|
||||
file_linked = { version = "0.1.0", path = "../file_linked" }
|
||||
file_linked = { version = "0.1.0", path = "../file_linked" }
|
||||
thiserror = "1.0"
|
||||
anyhow = "1.0"
|
|
@ -2,6 +2,9 @@
|
|||
//!
|
||||
//! [`Bracket`]: crate::bracket::Bracket
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
|
@ -33,19 +36,20 @@ pub trait GeneticNode {
|
|||
///
|
||||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::error::Error;
|
||||
/// #
|
||||
/// struct Node {
|
||||
/// pub fit_score: f64,
|
||||
/// }
|
||||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// fn initialize() -> Result<Box<Self>, String> {
|
||||
/// fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// Ok(Box::new(Node {fit_score: 0.0}))
|
||||
/// }
|
||||
///
|
||||
/// //...
|
||||
/// #
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
|
@ -53,22 +57,22 @@ pub trait GeneticNode {
|
|||
/// # self.fit_score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let node = Node::initialize()?;
|
||||
/// assert_eq!(node.get_fit_score(), 0.0);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn initialize() -> Result<Box<Self>, String>;
|
||||
fn initialize() -> Result<Box<Self>, Error>;
|
||||
|
||||
/// Runs a simulation on the state object for the given number of `iterations` in order to guage it's fitness.
|
||||
/// This will be called for every node in a bracket before evaluating it's fitness against other nodes.
|
||||
|
@ -77,6 +81,7 @@ pub trait GeneticNode {
|
|||
///
|
||||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::error::Error;
|
||||
/// #
|
||||
/// struct Model {
|
||||
/// pub fit_score: f64,
|
||||
|
@ -89,7 +94,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// impl Model {
|
||||
/// fn fit(&mut self, epochs: u64) -> Result<(), String> {
|
||||
/// fn fit(&mut self, epochs: u64) -> Result<(), Error> {
|
||||
/// //...
|
||||
/// # self.fit_score += epochs as f64;
|
||||
/// # Ok(())
|
||||
|
@ -97,13 +102,13 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]}))
|
||||
/// # }
|
||||
/// #
|
||||
/// //...
|
||||
///
|
||||
/// fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// for m in self.models.iter_mut()
|
||||
/// {
|
||||
/// m.fit(iterations)?;
|
||||
|
@ -117,23 +122,23 @@ pub trait GeneticNode {
|
|||
/// # self.models.iter().max_by(|m1, m2| m1.fit_score.partial_cmp(&m2.fit_score).unwrap()).unwrap().fit_score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let mut node = Node::initialize()?;
|
||||
/// node.simulate(5)?;
|
||||
/// assert_eq!(node.get_fit_score(), 5.0);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn simulate(&mut self, iterations: u64) -> Result<(), String>;
|
||||
fn simulate(&mut self, iterations: u64) -> Result<(), Error>;
|
||||
|
||||
/// Returns a fit score associated with the nodes performance.
|
||||
/// This will be used by a bracket in order to determine the most successful child.
|
||||
|
@ -141,6 +146,7 @@ pub trait GeneticNode {
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::error::Error;
|
||||
/// #
|
||||
/// struct Model {
|
||||
/// pub fit_score: f64,
|
||||
|
@ -153,7 +159,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// # impl Model {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
|
||||
/// # //...
|
||||
/// # self.fit_score += epochs as f64;
|
||||
/// # Ok(())
|
||||
|
@ -161,13 +167,13 @@ pub trait GeneticNode {
|
|||
/// # }
|
||||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]}))
|
||||
/// # }
|
||||
/// #
|
||||
/// # //...
|
||||
/// #
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # for m in self.models.iter_mut()
|
||||
/// # {
|
||||
/// # m.fit(iterations)?;
|
||||
|
@ -182,16 +188,16 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// //...
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let mut node = Node::initialize()?;
|
||||
/// node.simulate(5)?;
|
||||
/// assert_eq!(node.get_fit_score(), 5.0);
|
||||
|
@ -205,6 +211,7 @@ pub trait GeneticNode {
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::error::Error;
|
||||
/// #
|
||||
/// struct Model {
|
||||
/// pub fit_score: f64,
|
||||
|
@ -218,7 +225,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// # impl Model {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
|
||||
/// # //...
|
||||
/// # self.fit_score += epochs as f64;
|
||||
/// # Ok(())
|
||||
|
@ -226,7 +233,7 @@ pub trait GeneticNode {
|
|||
/// # }
|
||||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(Node {
|
||||
/// # models: vec![
|
||||
/// # Model { fit_score: 0.0 },
|
||||
|
@ -241,7 +248,7 @@ pub trait GeneticNode {
|
|||
/// #
|
||||
/// # //...
|
||||
/// #
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # for m in self.models.iter_mut() {
|
||||
/// # m.fit(iterations)?;
|
||||
/// # }
|
||||
|
@ -258,7 +265,7 @@ pub trait GeneticNode {
|
|||
/// # .fit_score
|
||||
/// # }
|
||||
/// #
|
||||
/// fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// self.models.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse());
|
||||
/// self.models.truncate(3);
|
||||
/// Ok(())
|
||||
|
@ -266,12 +273,12 @@ pub trait GeneticNode {
|
|||
///
|
||||
/// //...
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let mut node = Node::initialize()?;
|
||||
/// assert_eq!(node.models.len(), 5);
|
||||
///
|
||||
|
@ -283,13 +290,14 @@ pub trait GeneticNode {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn calculate_scores_and_trim(&mut self) -> Result<(), String>;
|
||||
fn calculate_scores_and_trim(&mut self) -> Result<(), Error>;
|
||||
|
||||
/// Mutates members in a population and/or crossbreeds them to produce new offspring.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::error::Error;
|
||||
/// # use std::convert::TryInto;
|
||||
/// #
|
||||
/// struct Model {
|
||||
|
@ -304,7 +312,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// # impl Model {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> {
|
||||
/// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
|
||||
/// # //...
|
||||
/// # self.fit_score += epochs as f64;
|
||||
/// # Ok(())
|
||||
|
@ -320,7 +328,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(Node {
|
||||
/// # models: vec![
|
||||
/// # Model { fit_score: 0.0 },
|
||||
|
@ -333,7 +341,7 @@ pub trait GeneticNode {
|
|||
/// # }))
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # for m in self.models.iter_mut() {
|
||||
/// # m.fit(iterations)?;
|
||||
/// # }
|
||||
|
@ -348,14 +356,14 @@ pub trait GeneticNode {
|
|||
/// # .fit_score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # self.models.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse());
|
||||
/// # self.models.truncate(3);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// //...
|
||||
///
|
||||
/// fn mutate(&mut self) -> Result<(), String> {
|
||||
/// fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// loop {
|
||||
/// if self.models.len() < self.population_size.try_into().unwrap()
|
||||
/// {
|
||||
|
@ -368,7 +376,7 @@ pub trait GeneticNode {
|
|||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let mut node = Node::initialize()?;
|
||||
/// assert_eq!(node.models.len(), 5);
|
||||
///
|
||||
|
@ -383,7 +391,7 @@ pub trait GeneticNode {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn mutate(&mut self) -> Result<(), String>;
|
||||
fn mutate(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as
|
||||
|
@ -410,6 +418,7 @@ where
|
|||
/// ```
|
||||
/// # use gemla::bracket::genetic_node::GeneticNode;
|
||||
/// # use gemla::bracket::genetic_node::GeneticNodeWrapper;
|
||||
/// # use gemla::error::Error;
|
||||
/// # #[derive(Debug)]
|
||||
/// struct Node {
|
||||
/// # pub fit_score: f64,
|
||||
|
@ -418,12 +427,12 @@ where
|
|||
///
|
||||
/// impl GeneticNode for Node {
|
||||
/// //...
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(Node {fit_score: 0.0}))
|
||||
/// # }
|
||||
/// #
|
||||
/// #
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
|
@ -431,22 +440,22 @@ where
|
|||
/// # self.fit_score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), String> {
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// let mut wrapped_node = GeneticNodeWrapper::<Node>::new()?;
|
||||
/// assert_eq!(wrapped_node.data.unwrap().get_fit_score(), 0.0);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new() -> Result<Self, String> {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let mut node = GeneticNodeWrapper {
|
||||
data: None,
|
||||
state: GeneticState::Initialize,
|
||||
|
@ -476,14 +485,14 @@ where
|
|||
/// [`simulate`]: crate::bracket::genetic_node::GeneticNode#tymethod.simulate
|
||||
/// [`calculate_scores_and_trim`]: crate::bracket::genetic_node::GeneticNode#tymethod.calculate_scores_and_trim
|
||||
/// [`mutate`]: crate::bracket::genetic_node::GeneticNode#tymethod.mutate
|
||||
pub fn process_node(&mut self, iterations: u32) -> Result<(), String> {
|
||||
pub fn process_node(&mut self, iterations: u32) -> Result<(), Error> {
|
||||
// Looping through each state transition until the number of iterations have been reached.
|
||||
loop {
|
||||
match (self.state, self.data.as_ref()) {
|
||||
match (self.state, &self.data) {
|
||||
(GeneticState::Initialize, _) => {
|
||||
self.iteration = 0;
|
||||
let new_data =
|
||||
T::initialize().map_err(|e| format!("Error initializing node: {}", e))?;
|
||||
let new_data = T::initialize()
|
||||
.with_context(|| format!("Error initializing node {:?}", self))?;
|
||||
self.data = Some(*new_data);
|
||||
self.state = GeneticState::Simulate;
|
||||
}
|
||||
|
@ -492,7 +501,7 @@ where
|
|||
.as_mut()
|
||||
.unwrap()
|
||||
.simulate(5)
|
||||
.map_err(|e| format!("Error simulating node: {}", e))?;
|
||||
.with_context(|| format!("Error simulating node: {:?}", self))?;
|
||||
self.state = GeneticState::Score;
|
||||
}
|
||||
(GeneticState::Score, Some(_)) => {
|
||||
|
@ -500,7 +509,7 @@ where
|
|||
.as_mut()
|
||||
.unwrap()
|
||||
.calculate_scores_and_trim()
|
||||
.map_err(|e| format!("Error scoring and trimming node: {}", e))?;
|
||||
.with_context(|| format!("Error scoring and trimming node: {:?}", self))?;
|
||||
|
||||
self.state = if self.iteration == iterations {
|
||||
GeneticState::Finish
|
||||
|
@ -513,13 +522,13 @@ where
|
|||
.as_mut()
|
||||
.unwrap()
|
||||
.mutate()
|
||||
.map_err(|e| format!("Error mutating node: {}", e))?;
|
||||
.with_context(|| format!("Error mutating node: {:?}", self))?;
|
||||
self.state = GeneticState::Simulate;
|
||||
}
|
||||
(GeneticState::Finish, Some(_)) => {
|
||||
break;
|
||||
}
|
||||
_ => return Err(format!("Error processing node {:?}", self.data)),
|
||||
_ => panic!("Error processing node {:?}", self.data),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
pub mod genetic_node;
|
||||
|
||||
use super::tree;
|
||||
use crate::error::Error;
|
||||
use crate::tree;
|
||||
|
||||
use file_linked::FileLinked;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::path;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// As the bracket tree increases in height, `IterationScaling` can be used to configure the number of iterations that
|
||||
/// a node runs for.
|
||||
|
@ -19,6 +19,7 @@ use std::str::FromStr;
|
|||
///
|
||||
/// ```
|
||||
/// # use gemla::bracket::*;
|
||||
/// # use gemla::error::Error;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// # use std::fmt;
|
||||
/// # use std::str::FromStr;
|
||||
|
@ -30,14 +31,6 @@ use std::str::FromStr;
|
|||
/// # pub score: f64,
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl FromStr for TestState {
|
||||
/// # type Err = String;
|
||||
/// #
|
||||
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
|
||||
/// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl TestState {
|
||||
/// # fn new(score: f64) -> TestState {
|
||||
/// # TestState { score: score }
|
||||
|
@ -45,7 +38,7 @@ use std::str::FromStr;
|
|||
/// # }
|
||||
/// #
|
||||
/// # impl genetic_node::GeneticNode for TestState {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # self.score += iterations as f64;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
|
@ -54,15 +47,15 @@ use std::str::FromStr;
|
|||
/// # self.score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(TestState { score: 0.0 }))
|
||||
/// # }
|
||||
/// # }
|
||||
|
@ -135,7 +128,7 @@ where
|
|||
|
||||
impl<T> Bracket<T>
|
||||
where
|
||||
T: genetic_node::GeneticNode + FromStr + Default + DeserializeOwned + Serialize + Clone,
|
||||
T: genetic_node::GeneticNode + Default + DeserializeOwned + Serialize + Clone,
|
||||
{
|
||||
/// Initializes a bracket of type `T` storing the contents to `file_path`
|
||||
///
|
||||
|
@ -143,6 +136,7 @@ where
|
|||
/// ```
|
||||
/// # use gemla::bracket::*;
|
||||
/// # use gemla::btree;
|
||||
/// # use gemla::error::Error;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// # use std::fmt;
|
||||
/// # use std::str::FromStr;
|
||||
|
@ -175,7 +169,7 @@ where
|
|||
/// }
|
||||
///
|
||||
/// impl genetic_node::GeneticNode for TestState {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # self.score += iterations as f64;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
|
@ -184,15 +178,15 @@ where
|
|||
/// # self.score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// fn initialize() -> Result<Box<Self>, String> {
|
||||
/// fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// Ok(Box::new(TestState { score: 0.0 }))
|
||||
/// }
|
||||
///
|
||||
|
@ -212,14 +206,14 @@ where
|
|||
/// std::fs::remove_file("./temp").expect("Unable to remove file");
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn initialize(file_path: path::PathBuf) -> Result<FileLinked<Self>, String> {
|
||||
FileLinked::new(
|
||||
pub fn initialize(file_path: path::PathBuf) -> Result<FileLinked<Self>, Error> {
|
||||
Ok(FileLinked::new(
|
||||
Bracket {
|
||||
tree: btree!(*T::initialize()?),
|
||||
iteration_scaling: IterationScaling::default(),
|
||||
},
|
||||
file_path,
|
||||
)
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Given a bracket object, configures it's [`IterationScaling`].
|
||||
|
@ -227,6 +221,7 @@ where
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// # use gemla::bracket::*;
|
||||
/// # use gemla::error::Error;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// # use std::fmt;
|
||||
/// # use std::str::FromStr;
|
||||
|
@ -238,14 +233,6 @@ where
|
|||
/// # pub score: f64,
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl FromStr for TestState {
|
||||
/// # type Err = String;
|
||||
/// #
|
||||
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
|
||||
/// # serde_json::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)
|
||||
|
@ -259,7 +246,7 @@ where
|
|||
/// # }
|
||||
/// #
|
||||
/// # impl genetic_node::GeneticNode for TestState {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # self.score += iterations as f64;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
|
@ -268,15 +255,15 @@ where
|
|||
/// # self.score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(TestState { score: 0.0 }))
|
||||
/// # }
|
||||
/// # }
|
||||
|
@ -300,7 +287,7 @@ where
|
|||
|
||||
// Creates a balanced tree with the given `height` that will be used as a branch of the primary tree.
|
||||
// This additionally simulates and evaluates nodes in the branch as it is built.
|
||||
fn create_new_branch(&self, height: u64) -> Result<tree::Tree<T>, String> {
|
||||
fn create_new_branch(&self, height: u64) -> Result<tree::Tree<T>, Error> {
|
||||
if height == 1 {
|
||||
let mut base_node = btree!(*T::initialize()?);
|
||||
|
||||
|
@ -337,6 +324,7 @@ where
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// # use gemla::bracket::*;
|
||||
/// # use gemla::error::Error;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// # use std::fmt;
|
||||
/// # use std::str::FromStr;
|
||||
|
@ -348,14 +336,6 @@ where
|
|||
/// # pub score: f64,
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl FromStr for TestState {
|
||||
/// # type Err = String;
|
||||
/// #
|
||||
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
|
||||
/// # serde_json::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)
|
||||
|
@ -369,7 +349,7 @@ where
|
|||
/// # }
|
||||
/// #
|
||||
/// # impl genetic_node::GeneticNode for TestState {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
/// # self.score += iterations as f64;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
|
@ -378,15 +358,15 @@ where
|
|||
/// # self.score
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn mutate(&mut self) -> Result<(), String> {
|
||||
/// # fn mutate(&mut self) -> Result<(), Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn initialize() -> Result<Box<Self>, String> {
|
||||
/// # fn initialize() -> Result<Box<Self>, Error> {
|
||||
/// # Ok(Box::new(TestState { score: 0.0 }))
|
||||
/// # }
|
||||
/// # }
|
||||
|
@ -407,7 +387,7 @@ where
|
|||
/// # std::fs::remove_file("./temp").expect("Unable to remove file");
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn run_simulation_step(&mut self) -> Result<&mut Self, String> {
|
||||
pub fn run_simulation_step(&mut self) -> Result<&mut Self, Error> {
|
||||
let new_branch = self.create_new_branch(self.tree.height())?;
|
||||
|
||||
self.tree.val.simulate(match self.iteration_scaling {
|
||||
|
@ -448,7 +428,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl genetic_node::GeneticNode for TestState {
|
||||
fn simulate(&mut self, iterations: u64) -> Result<(), String> {
|
||||
fn simulate(&mut self, iterations: u64) -> Result<(), Error> {
|
||||
self.score += iterations as f64;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -457,15 +437,15 @@ mod tests {
|
|||
self.score
|
||||
}
|
||||
|
||||
fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
|
||||
fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mutate(&mut self) -> Result<(), String> {
|
||||
fn mutate(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize() -> Result<Box<Self>, String> {
|
||||
fn initialize() -> Result<Box<Self>, Error> {
|
||||
Ok(Box::new(TestState { score: 0.0 }))
|
||||
}
|
||||
}
|
||||
|
|
18
gemla/src/error.rs
Normal file
18
gemla/src/error.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
FileLinked(file_linked::Error),
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl From<file_linked::Error> for Error {
|
||||
fn from(error: file_linked::Error) -> Error {
|
||||
match error {
|
||||
file_linked::Error::Other(e) => Error::Other(e),
|
||||
_ => Error::FileLinked(error),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,3 +5,4 @@ extern crate regex;
|
|||
pub mod tree;
|
||||
pub mod bracket;
|
||||
pub mod constants;
|
||||
pub mod error;
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
//! assert_eq!(t.right.unwrap().val, 3);
|
||||
//! ```
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::max;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// An unbalanced binary tree type where each node has an optional left and right child.
|
||||
///
|
||||
|
@ -155,17 +153,6 @@ impl<T: fmt::Debug + Serialize> fmt::Debug for Tree<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> FromStr for Tree<T>
|
||||
where
|
||||
T: FromStr + DeserializeOwned,
|
||||
{
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Add table
Reference in a new issue