unit testing neural network operations
This commit is contained in:
parent
5670649227
commit
be02823c4c
1 changed files with 563 additions and 43 deletions
|
@ -408,6 +408,11 @@ fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec<
|
|||
// Now we need to copy the connections from the original networks to the new network
|
||||
// We can do this by referencing our connections array, it will contain the original id's of the neurons
|
||||
// and their new id as well as their layer. We can iterate one layer at a time and copy the connections
|
||||
|
||||
let primary_shape = primary.get_layer_sizes();
|
||||
let secondary_shape = secondary.get_layer_sizes();
|
||||
debug!("Primary shape: {:?}", primary_shape);
|
||||
debug!("Secondary shape: {:?}", secondary_shape);
|
||||
|
||||
// Start by iterating layer by later
|
||||
let primary_connections = primary.get_connections();
|
||||
|
@ -423,16 +428,87 @@ fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec<
|
|||
for (previous_neuron_id, _, _, previous_new_id) in previous_layer_connections.iter() {
|
||||
// First we use primary to and check the correct connections array to see if the connection exists
|
||||
// If it does, we add it to the new network
|
||||
let connection = if *is_primary {
|
||||
primary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id)
|
||||
let mut connection ;
|
||||
let mut found_in_primary = false;
|
||||
if *is_primary {
|
||||
connection = primary_connections.iter()
|
||||
.find(|connection| {
|
||||
let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape);
|
||||
let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape);
|
||||
|
||||
// If both neurons have a Some value
|
||||
if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) {
|
||||
from_neuron == *previous_neuron_id && to_neuron == *neuron_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if let None = connection {
|
||||
connection = secondary_connections.iter()
|
||||
.find(|connection| {
|
||||
let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape);
|
||||
let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape);
|
||||
|
||||
// If both neurons have a Some value
|
||||
if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) {
|
||||
from_neuron == *previous_neuron_id && to_neuron == *neuron_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
} else {
|
||||
found_in_primary = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
secondary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id)
|
||||
connection = secondary_connections.iter()
|
||||
.find(|connection| {
|
||||
let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape);
|
||||
let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape);
|
||||
|
||||
// If both neurons have a Some value
|
||||
if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) {
|
||||
from_neuron == *previous_neuron_id && to_neuron == *neuron_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if let None = connection {
|
||||
connection = primary_connections.iter()
|
||||
.find(|connection| {
|
||||
let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape);
|
||||
let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape);
|
||||
|
||||
// If both neurons have a Some value
|
||||
if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) {
|
||||
from_neuron == *previous_neuron_id && to_neuron == *neuron_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
} else {
|
||||
found_in_primary = true;
|
||||
}
|
||||
};
|
||||
|
||||
// If the connection exists, we need to add it to the new network
|
||||
if let Some(connection) = connection {
|
||||
new_fann.set_weight(*previous_new_id, *new_id, connection.weight);
|
||||
if *is_primary {
|
||||
let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape);
|
||||
let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape);
|
||||
debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id);
|
||||
} else {
|
||||
let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape);
|
||||
let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape);
|
||||
debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id);
|
||||
}
|
||||
let translated_from = to_bias_network_id(previous_new_id, &new_shape);
|
||||
let translated_to = to_bias_network_id(new_id, &new_shape);
|
||||
new_fann.set_weight(translated_from, translated_to, connection.weight);
|
||||
} else {
|
||||
debug!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -448,6 +524,9 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
// - The resulting neuron id in the new network which will be calculated after the fact
|
||||
let mut new_neurons = Vec::new();
|
||||
let mut current_layer = 0;
|
||||
// keep track of the last layer that we inserted a neuron into for each network
|
||||
let mut primary_last_layer = 0;
|
||||
let mut secondary_last_layer = 0;
|
||||
let mut is_primary = true;
|
||||
for (i, &segment) in segments.iter().enumerate() {
|
||||
// If it's the first slice, copy neurons from the primary network up to the cut_point
|
||||
|
@ -458,6 +537,12 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
current_layer += 1;
|
||||
}
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
if is_primary {
|
||||
primary_last_layer = current_layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = current_layer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
@ -475,6 +560,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
// Equal
|
||||
if layer == ¤t_layer {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = current_layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = current_layer;
|
||||
}
|
||||
}
|
||||
// Earlier
|
||||
else if layer < ¤t_layer {
|
||||
|
@ -483,15 +575,31 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
// As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual
|
||||
let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::<Vec<_>>();
|
||||
// get max id from that layer
|
||||
let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id);
|
||||
let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0)));
|
||||
if let Some(highest_id) = highest_id {
|
||||
if highest_id.1 == is_primary {
|
||||
let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::<Vec<_>>();
|
||||
for (neuron_id, _) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id < neuron_id && l == layer).collect::<Vec<_>>();
|
||||
for (neuron_id, layer) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, *layer, 0));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = *layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = *layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_neurons.push((*neuron_id, is_primary, *layer, 0));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = *layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = *layer;
|
||||
}
|
||||
}
|
||||
// Later
|
||||
else if layer > ¤t_layer {
|
||||
|
@ -504,6 +612,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::<Vec<_>>();
|
||||
for (neuron_id, _) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = current_layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = current_layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -516,6 +631,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::<Vec<_>>();
|
||||
for (neuron_id, _) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = current_layer;
|
||||
}
|
||||
else {
|
||||
secondary_last_layer = current_layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -549,17 +671,22 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
break;
|
||||
}
|
||||
else if *neuron_id == &segments.last().unwrap().1 + 1 {
|
||||
let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::<Vec<_>>();
|
||||
let target_layer = if is_primary { primary_last_layer } else { secondary_last_layer };
|
||||
let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| *l >= target_layer && l <= layer).collect::<Vec<_>>();
|
||||
// get max neuron from with both
|
||||
// The highest layer
|
||||
// get max id from that layer
|
||||
let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id);
|
||||
let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0)));
|
||||
if let Some(highest_id) = highest_id {
|
||||
if highest_id.1 == is_primary {
|
||||
let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::<Vec<_>>();
|
||||
for (neuron_id, _) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, *layer, 0));
|
||||
let neurons_to_add = target_neurons.iter().filter(|(id, _)| id > &highest_id.0 && id < neuron_id).collect::<Vec<_>>();
|
||||
for (neuron_id, l) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, *l, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_neurons.push((*neuron_id, is_primary, *layer, 0));
|
||||
}
|
||||
else {
|
||||
new_neurons.push((*neuron_id, is_primary, *layer, 0));
|
||||
|
@ -606,11 +733,64 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32
|
|||
}
|
||||
|
||||
fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> {
|
||||
shape.iter().enumerate().flat_map(|(i, &size)| {
|
||||
(0..size).enumerate().map(move |(j, _)| (j as u32, i))
|
||||
}).collect()
|
||||
let mut result = Vec::new();
|
||||
let mut global_index = 0; // Keep a global index that does not reset
|
||||
|
||||
for (layer_index, &neurons) in shape.iter().enumerate() {
|
||||
for _ in 0..neurons {
|
||||
result.push((global_index, layer_index));
|
||||
global_index += 1; // Increment global index for each neuron
|
||||
}
|
||||
// global_index += 1; // Skip index for bias neuron at the end of each layer
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
fn to_bias_network_id(id: &u32, shape: &Vec<u32>) -> u32 {
|
||||
// The given id comes from a network without a bias neuron at the end of every layer
|
||||
// We need to translate this id to the id in the network with bias neurons
|
||||
let mut translated_id = 0;
|
||||
for (layer_index, &neurons) in shape.iter().enumerate() {
|
||||
for _ in 0..neurons {
|
||||
if &translated_id == id {
|
||||
return translated_id + layer_index as u32;
|
||||
}
|
||||
translated_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If the id is not found, return the id
|
||||
translated_id
|
||||
}
|
||||
|
||||
fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option<u32> {
|
||||
let mut bias_count = 0; // Count of bias neurons encountered up to the current ID
|
||||
let mut total_neurons = 0; // Total count of neurons (excluding bias neurons) processed
|
||||
|
||||
for &neurons in shape {
|
||||
let layer_end = total_neurons + neurons; // End of the current layer, excluding the bias neuron
|
||||
if id < layer_end {
|
||||
// ID is within the current layer (excluding the bias neuron)
|
||||
return Some(id - bias_count);
|
||||
}
|
||||
if id == layer_end {
|
||||
// ID matches the position where a bias neuron would be
|
||||
return None;
|
||||
}
|
||||
|
||||
// Update counts after considering the current layer
|
||||
total_neurons += neurons + 1; // Move to the next layer, accounting for the bias neuron
|
||||
bias_count += 1; // Increment bias count as we've moved past where a bias neuron would be
|
||||
}
|
||||
|
||||
// If the ID is beyond the range of all neurons (including bias), it's treated as invalid
|
||||
// Adjust this behavior based on your application's needs
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result<f32, io::Error> {
|
||||
let mut attempts = 0;
|
||||
|
||||
|
@ -659,33 +839,9 @@ mod tests {
|
|||
// Assign
|
||||
let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)];
|
||||
|
||||
let primary_network = vec![
|
||||
// Input layer
|
||||
(0, 0), (1, 0), (2, 0), (3, 0),
|
||||
// Hidden layer 1
|
||||
(4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1),
|
||||
// Hidden layer 2
|
||||
(12, 2), (13, 2), (14, 2), (15, 2), (16, 2), (17, 2),
|
||||
// Output layer
|
||||
(18, 3), (19, 3), (20, 3), (21, 3),
|
||||
];
|
||||
let primary_network = generate_neuron_datastructure(&vec![4, 8, 6, 4]);
|
||||
|
||||
let secondary_network = vec![
|
||||
// Input layer
|
||||
(0, 0), (1, 0), (2, 0), (3, 0),
|
||||
// Hidden layer 1
|
||||
(4, 1), (5, 1), (6, 1),
|
||||
// Hidden layer 2
|
||||
(7, 2), (8, 2), (9, 2),
|
||||
// Hiden Layer 3
|
||||
(10, 3), (11, 3), (12, 3),
|
||||
// Hidden Layer 4
|
||||
(13, 4), (14, 4), (15, 4),
|
||||
// Hiden Layer 5
|
||||
(16, 5), (17, 5), (18, 5),
|
||||
// Output layer
|
||||
(19, 6), (20, 6), (21, 6), (22, 6),
|
||||
];
|
||||
let secondary_network = generate_neuron_datastructure(&vec![4, 3, 3, 3, 3, 3, 4]);
|
||||
|
||||
// Act
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone());
|
||||
|
@ -735,6 +891,370 @@ mod tests {
|
|||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Testing with a different segment
|
||||
// Assign
|
||||
let segments = vec![(0, 4), (5, 14), (15, 15), (16, 16)];
|
||||
|
||||
// Act
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 3
|
||||
(4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6),
|
||||
// Hidden Layer 2: Expect 6
|
||||
(7, false, 2, 7), (8, false, 2, 8), (9, false, 2, 9), (15, true, 2, 10), (16, true, 2, 11), (17, true, 2, 12),
|
||||
// Hidden Layer 3: Expect 3
|
||||
(10, false, 3, 13), (11, false, 3, 14), (12, false, 3, 15),
|
||||
// Hidden Layer 4: Expect 3
|
||||
(13, false, 4, 16), (14, false, 4, 17), (15, false, 4, 18),
|
||||
// Output Layer: Expect 4
|
||||
(18, true, 5, 19), (19, true, 5, 20), (20, true, 5, 21), (21, true, 5, 22),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Swapping order
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 8
|
||||
(4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), (11, false, 1, 11),
|
||||
// Hidden Layer 2: Expect 5
|
||||
(12, false, 2, 12), (13, false, 2, 13), (14, false, 2, 14), (15, false, 2, 15), (16, false, 2, 16),
|
||||
// Hidden Layer 3: Expect 3
|
||||
(13, true, 3, 17), (14, true, 3, 18), (15, true, 3, 19),
|
||||
// Hidden Layer 4: Expect 3
|
||||
(16, true, 4, 20), (17, true, 4, 21), (18, true, 4, 22),
|
||||
// Output Layer: Expect 4
|
||||
(19, true, 5, 23), (20, true, 5, 24), (21, true, 5, 25), (22, true, 5, 26),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Testing with a different segment
|
||||
// Assign
|
||||
let segments = vec![(0, 7), (8, 9), (10, 10), (11, 12)];
|
||||
|
||||
// Act
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 7
|
||||
(4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10),
|
||||
// Hidden Layer 2: Expect 8
|
||||
(7, false, 2, 11), (8, false, 2, 12), (9, false, 2, 13), (13, true, 2, 14), (14, true, 2, 15), (15, true, 2, 16), (16, true, 2, 17), (17, true, 2, 18),
|
||||
// Hidden Layer 3: Expect 3
|
||||
(10, false, 3, 19), (11, false, 3, 20), (12, false, 3, 21),
|
||||
// Output Layer: Expect 4
|
||||
(18, true, 4, 22), (19, true, 4, 23), (20, true, 4, 24), (21, true, 4, 25),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Swapping order
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 7
|
||||
(4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (8, false, 1, 7), (9, false, 1, 8), (10, false, 1, 9), (11, false, 1, 10),
|
||||
// Hidden Layer 2: Expect 4
|
||||
(7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), (12, false, 2, 14),
|
||||
// Hidden Layer 3: Expect 3
|
||||
(10, true, 3, 15), (11, true, 3, 16), (12, true, 3, 17),
|
||||
// Hidden Layer 4: Expect 3
|
||||
(13, true, 4, 18), (14, true, 4, 19), (15, true, 4, 20),
|
||||
// Hidden Layer 5: Expect 3
|
||||
(16, true, 5, 21), (17, true, 5, 22), (18, true, 5, 23),
|
||||
// Output Layer: Expect 4
|
||||
(19, true, 6, 24), (20, true, 6, 25), (21, true, 6, 26), (22, true, 6, 27),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Testing networks with the same size
|
||||
// Assign
|
||||
let segments = vec![(0, 3), (4, 6), (7, 8), (9, 11)];
|
||||
|
||||
let primary_network = generate_neuron_datastructure(&vec![4, 3, 4, 5, 4]);
|
||||
|
||||
vec![
|
||||
// Input layer
|
||||
(0, 0), (1, 0), (2, 0), (3, 0),
|
||||
// Hidden layer 1: 3 neurons
|
||||
(4, 1), (5, 1), (6, 1),
|
||||
// Hidden Layer 2: 4 neurons
|
||||
(7, 2), (8, 2), (9, 2), (10, 2),
|
||||
// Hidden Layer 3: 5 neurons
|
||||
(11, 3), (12, 3), (13, 3), (14, 3), (15, 3),
|
||||
// Output layer
|
||||
(16, 4), (17, 4), (18, 4), (19, 4),
|
||||
];
|
||||
|
||||
let secondary_network = primary_network.clone();
|
||||
|
||||
// Act
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 3
|
||||
(4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6),
|
||||
// Hidden Layer 2: Expect 4
|
||||
(7, true, 2, 7), (8, true, 2, 8), (9, false, 2, 9), (10, false, 2, 10),
|
||||
// Hidden Layer 3: Expect 5
|
||||
(11, false, 3, 11), (12, true, 3, 12), (13, true, 3, 13), (14, true, 3, 14), (15, true, 3, 15),
|
||||
// Output Layer: Expect 4
|
||||
(16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
|
||||
// Testing with different segment
|
||||
let segments = vec![(0, 5), (6, 6), (7, 11), (12, 13)];
|
||||
|
||||
// Act
|
||||
let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone());
|
||||
|
||||
// Expected Result Set
|
||||
let expected: HashSet<(u32, bool, usize, u32)> = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 3
|
||||
(4, true, 1, 4), (5, true, 1, 5), (6, false, 1, 6),
|
||||
// Hidden Layer 2: Expect 4
|
||||
(7, true, 2, 7), (8, true, 2, 8), (9, true, 2, 9), (10, true, 2, 10),
|
||||
// Hidden Layer 3: Expect 5
|
||||
(11, true, 3, 11), (12, false, 3, 12), (13, false, 3, 13), (14, true, 3, 14), (15, true, 3, 15),
|
||||
// Output Layer: Expect 4
|
||||
(16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19),
|
||||
].into_iter().collect();
|
||||
|
||||
// print result before comparison
|
||||
for r in result.iter() {
|
||||
println!("{:?}", r);
|
||||
}
|
||||
|
||||
// Convert Result to HashSet for Comparison
|
||||
let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result_set, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_neuron_datastructure_test() {
|
||||
// Assign
|
||||
let shape = vec![4, 3, 5, 4];
|
||||
|
||||
// Act
|
||||
let result = generate_neuron_datastructure(shape.as_slice());
|
||||
|
||||
// Expected Result
|
||||
let expected: Vec<(u32, usize)> = vec![
|
||||
(0, 0), (1, 0), (2, 0), (3, 0),
|
||||
(4, 1), (5, 1), (6, 1),
|
||||
(7, 2), (8, 2), (9, 2), (10, 2), (11, 2),
|
||||
(12, 3), (13, 3), (14, 3), (15, 3),
|
||||
];
|
||||
|
||||
// Assert
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translate_neuron_id_test() {
|
||||
// Assign
|
||||
let shape = vec![4, 3, 5, 4];
|
||||
|
||||
let expected = vec![
|
||||
// (input, expected output)
|
||||
(0, 0), (1, 1), (2, 2), (3, 3),
|
||||
(4, 5), (5, 6), (6, 7),
|
||||
(7, 9), (8, 10), (9, 11), (10, 12), (11, 13),
|
||||
(12, 15), (13, 16), (14, 17), (15, 18),
|
||||
];
|
||||
|
||||
// Act
|
||||
for (input, expected_output) in expected {
|
||||
let result = to_bias_network_id(&input, &shape);
|
||||
// Assert
|
||||
assert_eq!(result, expected_output);
|
||||
|
||||
// Go the other direction too
|
||||
let result = to_non_bias_network_id(expected_output, &shape);
|
||||
|
||||
// Assert
|
||||
if let Some(result) = result {
|
||||
assert_eq!(result, input);
|
||||
}
|
||||
else {
|
||||
assert!(false, "Expected Some, got None");
|
||||
}
|
||||
}
|
||||
|
||||
// Validate bias neuron values
|
||||
let bias_neurons = vec![4, 8, 14, 19];
|
||||
|
||||
for &bias_neuron in bias_neurons.iter() {
|
||||
let result = to_non_bias_network_id(bias_neuron, &shape);
|
||||
|
||||
// Assert
|
||||
assert!(result.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn consolidate_old_connections_test() -> Result<(), Box<dyn std::error::Error>>{
|
||||
// Assign
|
||||
let primary_shape = vec![4, 8, 6, 4];
|
||||
let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4];
|
||||
|
||||
let mut primary_fann = Fann::new(&primary_shape)?;
|
||||
let mut secondary_fann = Fann::new(&secondary_shape)?;
|
||||
|
||||
let mut primary_connections = primary_fann.get_connections();
|
||||
for connection in primary_connections.iter_mut() {
|
||||
connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32;
|
||||
}
|
||||
primary_fann.set_connections(&primary_connections);
|
||||
|
||||
let mut secondary_connections = secondary_fann.get_connections();
|
||||
for connection in secondary_connections.iter_mut() {
|
||||
connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32;
|
||||
connection.weight = connection.weight * -1.0;
|
||||
}
|
||||
secondary_fann.set_connections(&secondary_connections);
|
||||
|
||||
let new_neurons = vec![
|
||||
// Input layer: Expect 4
|
||||
(0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3),
|
||||
// Hidden Layer 1: Expect 8
|
||||
(4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11),
|
||||
// Hidden Layer 2: Expect 9
|
||||
(7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20),
|
||||
// Output Layer: Expect 4
|
||||
(18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24),
|
||||
];
|
||||
let new_shape = vec![4, 8, 9, 4];
|
||||
let mut new_fann = Fann::new(&[4, 8, 9, 4])?;
|
||||
// Initialize weights to 0
|
||||
let mut new_connections = new_fann.get_connections();
|
||||
for connection in new_connections.iter_mut() {
|
||||
connection.weight = 0.0;
|
||||
}
|
||||
new_fann.set_connections(&new_connections);
|
||||
|
||||
// Act
|
||||
consolidate_old_connections(&primary_fann, &secondary_fann, new_shape, new_neurons, &mut new_fann);
|
||||
|
||||
// Bias neurons
|
||||
// Layer 1: 4
|
||||
// Layer 2: 13
|
||||
// Layer 3: 23
|
||||
let expected_connections = vec![
|
||||
// (from_neuron, to_neuron, weight)
|
||||
// Hidden Layer 1 (5-12)
|
||||
(0, 5, -5.0), (1, 5, -105.0), (2, 5, -205.0), (3, 5, -305.0),
|
||||
(0, 6, -6.0), (1, 6, -106.0), (2, 6, -206.0), (3, 6, -306.0),
|
||||
(0, 7, -7.0), (1, 7, -107.0), (2, 7, -207.0), (3, 7, -307.0),
|
||||
(0, 8, 8.0), (1, 8, 108.0), (2, 8, 208.0), (3, 8, 308.0),
|
||||
(0, 9, 9.0), (1, 9, 109.0), (2, 9, 209.0), (3, 9, 309.0),
|
||||
(0, 10, 10.0), (1, 10, 110.0), (2, 10, 210.0), (3, 10, 310.0),
|
||||
(0, 11, 11.0), (1, 11, 111.0), (2, 11, 211.0), (3, 11, 311.0),
|
||||
(0, 12, 12.0), (1, 12, 112.0), (2, 12, 212.0), (3, 12, 312.0),
|
||||
// Hidden Layer 2 (14-22)
|
||||
(5, 14, -509.0), (6, 14, -609.0), (7, 14, -709.0), (8, 14, 0.0), (9, 14, 0.0), (10, 14, 0.0), (11, 14, 0.0), (12, 14, 0.0),
|
||||
(5, 15, -510.0), (6, 15, -610.0), (7, 15, -710.0), (8, 15, 0.0), (9, 15, 0.0), (10, 15, 0.0), (11, 15, 0.0), (12, 15, 0.0),
|
||||
(5, 16, -511.0), (6, 16, -611.0), (7, 16, -711.0), (8, 16, 0.0), (9, 16, 0.0), (10, 16, 0.0), (11, 16, 0.0), (12, 16, 0.0),
|
||||
(5, 17, 514.0), (6, 17, 614.0), (7, 17, 714.0), (8, 17, 814.0), (9, 17, 914.0), (10, 17, 1014.0), (11, 17, 1114.0), (12, 17, 1214.0),
|
||||
(5, 18, 515.0), (6, 18, 615.0), (7, 18, 715.0), (8, 18, 815.0), (9, 18, 915.0), (10, 18, 1015.0), (11, 18, 1115.0), (12, 18, 1215.0),
|
||||
(5, 19, 516.0), (6, 19, 616.0), (7, 19, 716.0), (8, 19, 816.0), (9, 19, 916.0), (10, 19, 1016.0), (11, 19, 1116.0), (12, 19, 1216.0),
|
||||
(5, 20, 517.0), (6, 20, 617.0), (7, 20, 717.0), (8, 20, 817.0), (9, 20, 917.0), (10, 20, 1017.0), (11, 20, 1117.0), (12, 20, 1217.0),
|
||||
(5, 21, 518.0), (6, 21, 618.0), (7, 21, 718.0), (8, 21, 818.0), (9, 21, 918.0), (10, 21, 1018.0), (11, 21, 1118.0), (12, 21, 1218.0),
|
||||
(5, 22, 519.0), (6, 22, 619.0), (7, 22, 719.0), (8, 22, 819.0), (9, 22, 919.0), (10, 22, 1019.0), (11, 22, 1119.0), (12, 22, 1219.0),
|
||||
// Output layer (24-27)
|
||||
(14, 24, 0.0), (15, 24, 0.0), (16, 24, 0.0), (17, 24, 1421.0), (18, 24, 1521.0), (19, 24, 1621.0), (20, 24, 1721.0), (21, 24, 1821.0), (22, 24, 1921.0),
|
||||
(14, 25, 0.0), (15, 25, 0.0), (16, 25, 0.0), (17, 25, 1422.0), (18, 25, 1522.0), (19, 25, 1622.0), (20, 25, 1722.0), (21, 25, 1822.0), (22, 25, 1922.0),
|
||||
(14, 26, 0.0), (15, 26, 0.0), (16, 26, 0.0), (17, 26, 1423.0), (18, 26, 1523.0), (19, 26, 1623.0), (20, 26, 1723.0), (21, 26, 1823.0), (22, 26, 1923.0),
|
||||
(14, 27, 0.0), (15, 27, 0.0), (16, 27, 0.0), (17, 27, 1424.0), (18, 27, 1524.0), (19, 27, 1624.0), (20, 27, 1724.0), (21, 27, 1824.0), (22, 27, 1924.0),
|
||||
];
|
||||
|
||||
for connection in new_fann.get_connections().iter() {
|
||||
println!("{:?}", connection);
|
||||
}
|
||||
|
||||
// Assert
|
||||
// Compare each connection to the expected connection
|
||||
let new_connections = new_fann.get_connections();
|
||||
for connection in expected_connections.iter() {
|
||||
let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1);
|
||||
if let Some(matching_connection) = matching_connection {
|
||||
assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection);
|
||||
} else {
|
||||
assert!(false, "Connection not found: {:?}", connection);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue