diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 729d234..8aa31c2 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -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::>(); // 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::>(); - 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::>(); + 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::>(); 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::>(); 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::>(); + 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::>(); + // 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::>(); - 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::>(); + 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 { + // 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 { + 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 { 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>{ + // 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(()) } - } \ No newline at end of file