diff --git a/lab4/src/algo/comparation_operators_model.rs b/lab4/src/algo/comparation_operators_model.rs new file mode 100644 index 0000000..3c60de1 --- /dev/null +++ b/lab4/src/algo/comparation_operators_model.rs @@ -0,0 +1,104 @@ +use crate::algo::net::Net; +use rand::Rng; + +struct ComparationOperatorsModel +where + [(); { W * H }]:, +{ + net: Net<{ W * H }, ClassCount>, + pub input: [[bool; W]; H], + etalons: [[[bool; W]; H]; ClassCount], +} + +impl + ComparationOperatorsModel +where + [(); { W * H }]:, +{ + pub fn new(etalons: [[[bool; W]; H]; ClassCount]) -> Self { + return Self { + net: Net::new(), + input: [[false; W]; H], + etalons, + }; + } + + fn set_inputs(net: &mut Net<{ W * H }, ClassCount>, data: &[[bool; W]; H]) { + let mut i = 0; + for y in 0..H { + for x in 0..W { + net.input_data[i] = if data[y][x] { 1.0 } else { 0.0 }; + i += 1; + } + } + } + + fn train_one(&mut self, n: f64, image: usize) { + assert!(image < self.etalons.len()); + + Self::set_inputs(&mut self.net, &self.etalons[image]); + + self.net.compute(); + + let mut expected = [0.0; ClassCount]; + expected[image] = 1.0; + + self.net.fix(&expected, n); + } + + pub fn train_epoch(&mut self, n: f64) { + for i in 0..ClassCount { + self.train_one(n, i) + } + } + + pub fn train(&mut self, n: f64, epochs: usize) -> impl Iterator { + return TrainIterator { + owner: self, + n: n, + epochs_left: epochs, + }; + } + pub fn hidden_layer_size(&self) -> usize { + return self.net.hidden_layer_size(); + } + + pub fn resize_hidden_layer(&mut self, new_size: usize) { + self.net.resize_hidden_layer(new_size); + } + pub fn set_random_weights(&mut self, rng: &mut impl Rng) { + self.net.set_random_weights(rng) + } + + pub fn calculate_predictions(&mut self) -> [f64; ClassCount] { + Self::set_inputs(&mut self.net, &self.input); + self.net.compute(); + return self.net.output_data; + } +} + +struct TrainIterator<'a, const W: usize, const H: usize, const ClassCount: usize> +where + [(); { W * H }]:, +{ + owner: &'a mut ComparationOperatorsModel, + n: f64, + epochs_left: usize, +} + +impl<'a, const W: usize, const H: usize, const ClassCount: usize> Iterator + for TrainIterator<'_, W, H, ClassCount> +where + [(); { W * H }]:, +{ + type Item = (); + + fn next(&mut self) -> Option { + if self.epochs_left == 0 { + return None; + } + + self.owner.train_epoch(self.n); + return Some(()); + } +} diff --git a/lab4/src/algo/images.rs b/lab4/src/algo/images.rs new file mode 100644 index 0000000..e42ad96 --- /dev/null +++ b/lab4/src/algo/images.rs @@ -0,0 +1,114 @@ +use std::array; + +fn image(raw: [&str; H]) -> [[bool; W]; H] { + return raw.map(|r| { + let mut it = r.chars().map(|c| match c { + '+' => true, + '0' => false, + _ => panic!(), + }); + + return array::from_fn(|_| it.next().unwrap()) + }); +} + +pub fn gen_images() -> [[[bool; 7]; 7]; 8] { + return [ + // < + image( + [ + " ", + " * ", + " * ", + " * ", + " * ", + " * ", + " ", + ] + ), + // <= + image( + [ + " * ", + " * ", + " * ", + " * ", + " * * ", + " * ", + " * ", + ] + ), + // > + image( + [ + " ", + " * ", + " * ", + " * ", + " * ", + " * ", + " ", + ] + ), + // >= + image( + [ + " * ", + " * ", + " * ", + " * ", + " * * ", + " * ", + " * ", + ] + ), + // = + image( + [ + " ", + " ", + " ***** ", + " ", + " ***** ", + " ", + " ", + ] + ), + // != + image( + [ + " ", + " * ", + " ***** ", + " * ", + " ***** ", + " * ", + " ", + ] + ), + // ≡ + image( + [ + " ", + " ***** ", + " ", + " ***** ", + " ", + " ***** ", + " ", + ] + ), + // ≈ + image( + [ + " * ", + " * * * ", + " * ", + " ", + " * ", + " * * * ", + " * ", + ] + ), + ] +} \ No newline at end of file diff --git a/lab4/src/algo/mod.rs b/lab4/src/algo/mod.rs index a89eddd..ed722e4 100644 --- a/lab4/src/algo/mod.rs +++ b/lab4/src/algo/mod.rs @@ -1,3 +1,5 @@ mod compute; mod fix; mod net; +mod comparation_operators_model; +mod images; diff --git a/lab4/src/algo/net.rs b/lab4/src/algo/net.rs index 9b1ec8e..d8a668a 100644 --- a/lab4/src/algo/net.rs +++ b/lab4/src/algo/net.rs @@ -1,8 +1,10 @@ +use std::array; use crate::algo::compute::compute; use crate::algo::fix::{apply_error, calc_error}; +use rand::Rng; use std::ops::{Div, Mul}; -struct Net { +pub(super) struct Net { inner_layer_size: usize, pub input_data: [f64; InputLayerSize], i_to_h_weights: Vec<[f64; InputLayerSize]>, @@ -18,6 +20,22 @@ struct Net { impl Net { + pub(crate) fn new() -> Self { + return Self { + inner_layer_size: 1, + input_data: [0.0; InputLayerSize], + i_to_h_weights: Vec::from([[0.0; InputLayerSize]]), + hidden_potentials: Vec::from([0.0]), + hidden_data: Vec::from([0.0]), + h_to_o_weights: array::from_fn(|_| Vec::from([0.0])), + output_potentials: [0.0; OutputLayerSize], + output_data: [0.0; OutputLayerSize], + output_errors: [0.0; OutputLayerSize], + hidden_errors: Vec::from([0.0]), + } + } + + fn _sigmoid(x: f64) -> f64 { return 1.0.div(1.0 + (-x).exp()); } @@ -61,7 +79,7 @@ impl &mut self.h_to_o_weights, self.hidden_data.as_slice(), &self.output_potentials, - Self::_sigmoidDerivative + Self::_sigmoidDerivative, ); apply_error( n, @@ -69,7 +87,36 @@ impl self.i_to_h_weights.as_mut_slice(), &self.input_data, self.hidden_potentials.as_slice(), - Self::_sigmoidDerivative + Self::_sigmoidDerivative, ); } + + pub fn hidden_layer_size(&self) -> usize { + return self.inner_layer_size; + } + + pub fn resize_hidden_layer(&mut self, new_size: usize) { + assert!(new_size > 0); + self.inner_layer_size = new_size; + self.i_to_h_weights.resize(new_size, [0.0; InputLayerSize]); + self.hidden_potentials.resize(new_size, 0.0); + self.hidden_data.resize(new_size, 0.0); + for w in self.h_to_o_weights.iter_mut() { + w.resize(new_size, 0.0); + } + self.hidden_errors.resize(new_size, 0.0); + } + + pub fn set_random_weights(&mut self, rng: &mut impl Rng) { + for ww in self.i_to_h_weights.iter_mut() { + for w in ww.iter_mut() { + *w = rng.random() + } + } + for ww in self.h_to_o_weights.iter_mut() { + for w in ww.iter_mut() { + *w = rng.random() + } + } + } }