Merge pull request 'Migrating data to separate repository' (#2) from dootcamp-visualizations into main

Reviewed-on: #2
This commit is contained in:
tepichord 2025-09-05 12:38:39 -07:00
commit 3cc08a4b2a
11 changed files with 2 additions and 4369 deletions

View file

@ -1,171 +0,0 @@
# Re-importing necessary libraries
import json
import matplotlib.pyplot as plt
from collections import defaultdict
import numpy as np
# Simplified JSON data for demonstration
with open('gemla/round4.json', 'r') as file:
simplified_json_data = json.load(file)
target_node_id = '523f8250-3101-4586-90a1-127ffa6d73d9'
# Function to traverse the tree to find a node id
def traverse_left_nodes(node):
if node is None:
return []
left_node = node.get("left")
if left_node is None:
return [node]
return [node] + traverse_left_nodes(left_node)
# Function to traverse the tree to find a node id
def traverse_right_nodes(node):
if node is None:
return []
right_node = node.get("right")
left_node = node.get("left")
if right_node is None and left_node is None:
return []
elif right_node and left_node:
return [right_node] + traverse_right_nodes(left_node)
return []
# Getting the left graph
left_nodes = traverse_left_nodes(simplified_json_data[0])
left_nodes.reverse()
# print(node)
# Print properties available on the first node
node = left_nodes[0]
# print(node["val"].keys())
scores = []
for node in left_nodes:
# print(node)
# print(f'Node ID: {node["val"]["id"]}')
# print(f'Node scores length: {len(node["val"]["node"]["scores"])}')
if node["val"]["node"]:
node_scores = node["val"]["node"]["scores"]
if node_scores:
for score in node_scores:
scores.append(score)
# print(scores)
scores_values = [list(score_set.values()) for score_set in scores]
# Set up the figure for plotting on the same graph
fig, ax = plt.subplots(figsize=(10, 6))
# Generate a boxplot for each set of scores on the same graph
boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))])
# Set figure name to node id
ax.set_xscale('symlog', linthresh=1.0)
# Labeling
ax.set_xlabel(f'Scores - Main Line')
ax.set_ylabel('Score Sets')
ax.yaxis.grid(True) # Add horizontal grid lines for clarity
# Set y-axis labels to be visible
ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))])
# Getting most recent right graph
right_nodes = traverse_right_nodes(simplified_json_data[0])
if len(right_nodes) != 0:
target_node_id = None
target_node = None
if target_node_id:
for node in right_nodes:
if node["val"]["id"] == target_node_id:
target_node = node
break
else:
target_node = right_nodes[0]
scores = target_node["val"]["node"]["scores"]
scores_values = [list(score_set.values()) for score_set in scores]
# Set up the figure for plotting on the same graph
fig, ax = plt.subplots(figsize=(10, 6))
# Generate a boxplot for each set of scores on the same graph
boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))])
ax.set_xscale('symlog', linthresh=1.0)
# Labeling
ax.set_xlabel(f'Scores: {target_node['val']['id']}')
ax.set_ylabel('Score Sets')
ax.yaxis.grid(True) # Add horizontal grid lines for clarity
# Set y-axis labels to be visible
ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))])
# Find the highest scoring sets combining all scores and generations
scores = []
for node in left_nodes:
if node["val"]["node"]:
node_scores = node["val"]["node"]["scores"]
translated_node_scores = []
if node_scores:
for i in range(len(node_scores)):
for (individual, score) in node_scores[i].items():
translated_node_scores.append((node["val"]["id"], i, score))
scores.append(translated_node_scores)
# Add scores from the right nodes
if len(right_nodes) != 0:
for node in right_nodes:
if node["val"]["node"]:
node_scores = node["val"]["node"]["scores"]
translated_node_scores = []
if node_scores:
for i in range(len(node_scores)):
for (individual, score) in node_scores[i].items():
translated_node_scores.append((node["val"]["id"], i, score))
scores.append(translated_node_scores)
# Organize scores by individual and then by generation
individual_generation_scores = defaultdict(lambda: defaultdict(list))
for sublist in scores:
for id, generation, score in sublist:
individual_generation_scores[id][generation].append(score)
# Calculate Q3 for each individual's generation
individual_generation_q3 = {}
for id, generations in individual_generation_scores.items():
for gen, scores in generations.items():
individual_generation_q3[(id, gen)] = np.percentile(scores, 75)
# Sort by Q3 value, highest first, and select the top 20
top_20_individual_generations = sorted(individual_generation_q3, key=individual_generation_q3.get, reverse=True)[:40]
# Prepare scores for the top 20 for plotting
top_20_scores = [individual_generation_scores[id][gen] for id, gen in top_20_individual_generations]
# Adjust labels for clarity, indicating both the individual ID and generation
labels = [f'{id[:8]}... Gen {gen}' for id, gen in top_20_individual_generations]
# Generate box and whisker plots for the top 20 individual generations
fig, ax = plt.subplots(figsize=(12, 10))
ax.boxplot(top_20_scores, vert=False, patch_artist=True, labels=labels)
ax.set_xscale('symlog', linthresh=1.0)
ax.set_xlabel('Scores')
ax.set_ylabel('Individual Generation')
ax.set_title('Top 20 Individual Generations by Q3 Value')
ax.yaxis.grid(True) # Add horizontal grid lines for clarity
# Display the plot
plt.show()

View file

@ -1,9 +0,0 @@
[package]
name = "extract_fann_data"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fann = "0.1.8"

View file

@ -1,11 +0,0 @@
fn main() {
// Replace this with the path to the directory containing `fann.lib`
let lib_dir = "F://vandomej/Downloads/vcpkg/packages/fann_x64-windows/lib";
println!("cargo:rustc-link-search=native={}", lib_dir);
println!("cargo:rustc-link-lib=static=fann");
// Use `dylib=fann` instead of `static=fann` if you're linking dynamically
// If there are any additional directories where the compiler can find header files, you can specify them like this:
// println!("cargo:include={}", path_to_include_directory);
}

View file

@ -1,38 +0,0 @@
extern crate fann;
use fann::Fann;
use std::os::raw::c_uint;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <network_file>", args[0]);
std::process::exit(1);
}
let network_file = &args[1];
match Fann::from_file(network_file) {
Ok(ann) => {
// Output layer sizes
let layer_sizes = ann.get_layer_sizes();
let bias_counts = ann.get_bias_counts();
println!("Layers:");
for (layer_size, bias_count) in layer_sizes.iter().zip(bias_counts.iter()) {
println!("{} {}", layer_size, bias_count);
}
// Output connections
println!("Connections:");
let connections = ann.get_connections();
for connection in connections {
println!("{} {} {}", connection.from_neuron, connection.to_neuron, connection.weight);
}
},
Err(err) => {
eprintln!("Error loading network from file {}: {}", network_file, err);
std::process::exit(1);
}
}
}

View file

@ -3,12 +3,11 @@ extern crate gemla;
#[macro_use]
extern crate log;
mod fighter_nn;
mod test_state;
use anyhow::Result;
use clap::Parser;
use fighter_nn::FighterNN;
use test_state::TestState;
use file_linked::constants::data_format::DataFormat;
use gemla::{
core::{Gemla, GemlaConfig},
@ -45,7 +44,7 @@ fn main() -> Result<()> {
.block_on(async {
let args = Args::parse(); // Assuming Args::parse() doesn't need to be async
let mut gemla = log_error(
Gemla::<FighterNN>::new(
Gemla::<TestState>::new(
&PathBuf::from(args.file),
GemlaConfig { overwrite: false },
DataFormat::Json,

View file

@ -1,79 +0,0 @@
use std::sync::Arc;
use serde::ser::SerializeTuple;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use tokio::sync::Semaphore;
const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 50;
const VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT: usize = 1;
#[derive(Debug, Clone)]
pub struct FighterContext {
pub shared_semaphore: Arc<Semaphore>,
pub visible_simulations: Arc<Semaphore>,
}
impl Default for FighterContext {
fn default() -> Self {
FighterContext {
shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)),
visible_simulations: Arc::new(Semaphore::new(VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT)),
}
}
}
// Custom serialization to just output the concurrency limit.
impl Serialize for FighterContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Assuming the semaphore's available permits represent the concurrency limit.
// This part is tricky since Semaphore does not expose its initial permits.
// You might need to store the concurrency limit as a separate field if this assumption doesn't hold.
let concurrency_limit = SHARED_SEMAPHORE_CONCURRENCY_LIMIT;
let visible_concurrency_limit = VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT;
// serializer.serialize_u64(concurrency_limit as u64)
// Serialize the concurrency limit as a tuple
let mut state = serializer.serialize_tuple(2)?;
state.serialize_element(&concurrency_limit)?;
state.serialize_element(&visible_concurrency_limit)?;
state.end()
}
}
// Custom deserialization to reconstruct the FighterContext from a concurrency limit.
impl<'de> Deserialize<'de> for FighterContext {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Deserialize the tuple
let (_, _) = <(usize, usize)>::deserialize(deserializer)?;
Ok(FighterContext {
shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)),
visible_simulations: Arc::new(Semaphore::new(VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT)),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialization() {
let context = FighterContext::default();
let serialized = serde_json::to_string(&context).unwrap();
let deserialized: FighterContext = serde_json::from_str(&serialized).unwrap();
assert_eq!(
context.shared_semaphore.available_permits(),
deserialized.shared_semaphore.available_permits()
);
assert_eq!(
context.visible_simulations.available_permits(),
deserialized.visible_simulations.available_permits()
);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,380 +0,0 @@
# Re-importing necessary libraries
import json
import matplotlib.pyplot as plt
from collections import defaultdict
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import matplotlib.ticker as ticker
# Simplified JSON data for demonstration
with open('gemla/round4.json', 'r') as file:
simplified_json_data = json.load(file)
# Function to traverse the tree to find a node id
def traverse_right_nodes(node):
if node is None:
return []
right_node = node.get("right")
left_node = node.get("left")
if right_node is None and left_node is None:
return []
elif right_node and left_node:
return [right_node] + traverse_right_nodes(left_node)
return []
# Getting most recent right graph
right_nodes = traverse_right_nodes(simplified_json_data[0])
# Heatmaps
# Data structure to store mutation rates, generations, and scores
mutation_rate_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by mutation rate and generation
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
minor_mutation_rate = node_val["minor_mutation_rate"]
generation = node_val["generation"]
# Ensure each score is associated with the correct generation
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
mutation_rate_data[minor_mutation_rate][gen_index].append(score)
# Prepare data for heatmap
max_generation = max(max(gens.keys()) for gens in mutation_rate_data.values())
heatmap_data = np.full((len(mutation_rate_data), max_generation + 1), np.nan)
# Populate the heatmap data with average scores
mutation_rates = sorted(mutation_rate_data.keys())
for i, mutation_rate in enumerate(mutation_rates):
for generation in range(max_generation + 1):
scores = mutation_rate_data[mutation_rate][generation]
if scores: # Check if there are scores for this generation
heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the heatmap
df_heatmap = pd.DataFrame(
data=heatmap_data,
index=mutation_rates,
columns=range(max_generation + 1)
)
# Data structure to store major mutation rates, generations, and scores
major_mutation_rate_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by major mutation rate and generation
# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
major_mutation_rate = node_val["major_mutation_rate"]
generation = node_val["generation"]
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
major_mutation_rate_data[major_mutation_rate][gen_index].append(score)
# Prepare the heatmap data for major_mutation_rate similar to minor_mutation_rate
major_heatmap_data = np.full((len(major_mutation_rate_data), max_generation + 1), np.nan)
major_mutation_rates = sorted(major_mutation_rate_data.keys())
for i, major_rate in enumerate(major_mutation_rates):
for generation in range(max_generation + 1):
scores = major_mutation_rate_data[major_rate][generation]
if scores: # Check if there are scores for this generation
major_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_major_heatmap = pd.DataFrame(
data=major_heatmap_data,
index=major_mutation_rates,
columns=range(max_generation + 1)
)
# crossbreed_segments
# Data structure to store major mutation rates, generations, and scores
crossbreed_segments_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by major mutation rate and generation
# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
crossbreed_segments = node_val["crossbreed_segments"]
generation = node_val["generation"]
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
crossbreed_segments_data[crossbreed_segments][gen_index].append(score)
# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate
crossbreed_heatmap_data = np.full((len(crossbreed_segments_data), max_generation + 1), np.nan)
crossbreed_segments = sorted(crossbreed_segments_data.keys())
for i, crossbreed_segment in enumerate(crossbreed_segments):
for generation in range(max_generation + 1):
scores = crossbreed_segments_data[crossbreed_segment][generation]
if scores: # Check if there are scores for this generation
crossbreed_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_crossbreed_heatmap = pd.DataFrame(
data=crossbreed_heatmap_data,
index=crossbreed_segments,
columns=range(max_generation + 1)
)
# mutation_weight_range
# Data structure to store major mutation rates, generations, and scores
mutation_weight_range_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by major mutation rate and generation
# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
mutation_weight_range = node_val["mutation_weight_range"]
positive_extent = mutation_weight_range["end"]
negative_extent = -mutation_weight_range["start"]
mutation_weight_range = (positive_extent + negative_extent) / 2
generation = node_val["generation"]
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
mutation_weight_range_data[mutation_weight_range][gen_index].append(score)
# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate
mutation_weight_range_heatmap_data = np.full((len(mutation_weight_range_data), max_generation + 1), np.nan)
mutation_weight_ranges = sorted(mutation_weight_range_data.keys())
for i, mutation_weight_range in enumerate(mutation_weight_ranges):
for generation in range(max_generation + 1):
scores = mutation_weight_range_data[mutation_weight_range][generation]
if scores: # Check if there are scores for this generation
mutation_weight_range_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_mutation_weight_range_heatmap = pd.DataFrame(
data=mutation_weight_range_heatmap_data,
index=mutation_weight_ranges,
columns=range(max_generation + 1)
)
# weight_initialization_range
# Data structure to store major mutation rates, generations, and scores
weight_initialization_range_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by major mutation rate and generation
# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
weight_initialization_range = node_val["weight_initialization_range"]
positive_extent = weight_initialization_range["end"]
negative_extent = -weight_initialization_range["start"]
weight_initialization_range = (positive_extent + negative_extent) / 2
generation = node_val["generation"]
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
weight_initialization_range_data[weight_initialization_range][gen_index].append(score)
# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate
weight_initialization_range_heatmap_data = np.full((len(weight_initialization_range_data), max_generation + 1), np.nan)
weight_initialization_ranges = sorted(weight_initialization_range_data.keys())
for i, weight_initialization_range in enumerate(weight_initialization_ranges):
for generation in range(max_generation + 1):
scores = weight_initialization_range_data[weight_initialization_range][generation]
if scores: # Check if there are scores for this generation
weight_initialization_range_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_weight_initialization_range_heatmap = pd.DataFrame(
data=weight_initialization_range_heatmap_data,
index=weight_initialization_ranges,
columns=range(max_generation + 1)
)
# weight_initialization_range_skew
# Data structure to store major mutation rates, generations, and scores
weight_initialization_range_skew_data = defaultdict(lambda: defaultdict(list))
# Populate the dictionary with scores indexed by major mutation rate and generation
# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
weight_initialization_range = node_val["weight_initialization_range"]
positive_extent = weight_initialization_range["end"]
negative_extent = -weight_initialization_range["start"]
weight_initialization_range_skew = (positive_extent - negative_extent) / 2
generation = node_val["generation"]
for gen_index, score_list in enumerate(scores):
for score in score_list.values():
weight_initialization_range_skew_data[weight_initialization_range_skew][gen_index].append(score)
# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate
weight_initialization_range_skew_heatmap_data = np.full((len(weight_initialization_range_skew_data), max_generation + 1), np.nan)
weight_initialization_range_skews = sorted(weight_initialization_range_skew_data.keys())
for i, weight_initialization_range_skew in enumerate(weight_initialization_range_skews):
for generation in range(max_generation + 1):
scores = weight_initialization_range_skew_data[weight_initialization_range_skew][generation]
if scores: # Check if there are scores for this generation
weight_initialization_range_skew_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_weight_initialization_range_skew_heatmap = pd.DataFrame(
data=weight_initialization_range_skew_heatmap_data,
index=weight_initialization_range_skews,
columns=range(max_generation + 1)
)
# Analyze number of neurons correlation to score
# We can get the number of neurons via node_val["nn_shapes"] which contains an array of maps
# Each map has a key for the individual id and a value which is an array of integers representing the number of neurons in each layer
# We can use the individual id to get the score from the scores array
# We then generate a density map of the number of neurons vs the score
neuron_number_score_data = defaultdict(lambda: defaultdict(list))
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
nn_shapes = node_val["nn_shapes"]
# Both scores and nn_shapes are arrays where score is 1 less in length than nn_shapes (each index corresponds to a generation)
for gen_index, score in enumerate(scores):
for individual_id, nn_shape in nn_shapes[gen_index].items():
neuron_number = sum(nn_shape)
# check if score has a value for the individual id
if individual_id not in score:
continue
neuron_number_score_data[neuron_number][gen_index].append(score[individual_id])
# prepare the density map data
neuron_number_score_heatmap_data = np.full((len(neuron_number_score_data), max_generation + 1), np.nan)
neuron_numbers = sorted(neuron_number_score_data.keys())
for i, neuron_number in enumerate(neuron_numbers):
for generation in range(max_generation + 1):
scores = neuron_number_score_data[neuron_number][generation]
if scores: # Check if there are scores for this generation
neuron_number_score_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_neuron_number_score_heatmap = pd.DataFrame(
data=neuron_number_score_heatmap_data,
index=neuron_numbers,
columns=range(max_generation + 1)
)
# Analyze number of layers correlation to score
nn_layers_score_data = defaultdict(lambda: defaultdict(list))
for node in right_nodes:
node_val = node["val"]["node"]
if node_val:
scores = node_val["scores"]
nn_shapes = node_val["nn_shapes"]
# Both scores and nn_shapes are arrays where score is 1 less in length than nn_shapes (each index corresponds to a generation)
for gen_index, score in enumerate(scores):
for individual_id, nn_shape in nn_shapes[gen_index].items():
layer_number = len(nn_shape)
# check if score has a value for the individual id
if individual_id not in score:
continue
nn_layers_score_data[layer_number][gen_index].append(score[individual_id])
# prepare the density map data
nn_layers_score_heatmap_data = np.full((len(nn_layers_score_data), max_generation + 1), np.nan)
nn_layers = sorted(nn_layers_score_data.keys())
for i, nn_layer in enumerate(nn_layers):
for generation in range(max_generation + 1):
scores = nn_layers_score_data[nn_layer][generation]
if scores: # Check if there are scores for this generation
nn_layers_score_heatmap_data[i, generation] = np.mean(scores)
# Creating a DataFrame for the major mutation rate heatmap
df_nn_layers_score_heatmap = pd.DataFrame(
data=nn_layers_score_heatmap_data,
index=nn_layers,
columns=range(max_generation + 1)
)
# print("Format: ", custom_formatter(0.123498761234, 0))
# Creating subplots
fig, axs = plt.subplots(2, 2, figsize=(20, 14)) # Creates a 3x2 grid of subplots
# Plotting the minor mutation rate heatmap
sns.heatmap(df_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[0, 0])
# axs[0, 0].set_title('Minor Mutation Rate')
axs[0, 0].set_xlabel('Minor Mutation Rate')
axs[0, 0].set_ylabel('Generation')
axs[0, 0].invert_yaxis()
# Plotting the major mutation rate heatmap
sns.heatmap(df_major_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[0, 1])
# axs[0, 1].set_title('Major Mutation Rate')
axs[0, 1].set_xlabel('Major Mutation Rate')
axs[0, 1].invert_yaxis()
# Plotting the crossbreed_segments heatmap
sns.heatmap(df_crossbreed_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[1, 0])
# axs[1, 0].set_title('Crossbreed Segments')
axs[1, 0].set_xlabel('Crossbreed Segments')
axs[1, 0].set_ylabel('Generation')
axs[1, 0].invert_yaxis()
# Plotting the mutation_weight_range heatmap
sns.heatmap(df_mutation_weight_range_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[1, 1])
# axs[1, 1].set_title('Mutation Weight Range')
axs[1, 1].set_xlabel('Mutation Weight Range')
axs[1, 1].invert_yaxis()
fig3, axs3 = plt.subplots(1, 2, figsize=(20, 14)) # Creates a 3x2 grid of subplots
# Plotting the weight_initialization_range heatmap
sns.heatmap(df_weight_initialization_range_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs3[0])
# axs[2, 0].set_title('Weight Initialization Range')
axs3[0].set_xlabel('Weight Initialization Range')
axs3[0].set_ylabel('Generation')
axs3[0].invert_yaxis()
# Plotting the weight_initialization_range_skew heatmap
sns.heatmap(df_weight_initialization_range_skew_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs3[1])
# axs[2, 1].set_title('Weight Initialization Range Skew')
axs3[1].set_xlabel('Weight Initialization Range Skew')
axs3[1].set_ylabel('Generation')
axs3[1].invert_yaxis()
# Creating a new window for the scatter plots
fig2, axs2 = plt.subplots(2, 1, figsize=(20, 14)) # Creates a 2x1 grid of subplots
# Plotting the neuron number vs score heatmap
sns.heatmap(df_neuron_number_score_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs2[1])
# axs[3, 1].set_title('Neuron Number vs. Score')
axs2[1].set_xlabel('Neuron Number')
axs2[1].set_ylabel('Generation')
axs2[1].invert_yaxis()
# Plotting the number of layers vs score heatmap
sns.heatmap(df_nn_layers_score_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs2[0])
# axs[3, 1].set_title('Number of Layers vs. Score')
axs2[0].set_xlabel('Number of Layers')
axs2[0].set_ylabel('Generation')
axs2[0].invert_yaxis()
# Display the plot
plt.tight_layout() # Adjusts the subplots to fit into the figure area.
plt.show()

View file

@ -1,118 +0,0 @@
import matplotlib.pyplot as plt
import networkx as nx
import subprocess
import tkinter as tk
from tkinter import filedialog
def select_file():
root = tk.Tk()
root.withdraw() # Hide the main window
file_path = filedialog.askopenfilename(
initialdir="/", # Set the initial directory to search for files
title="Select file",
filetypes=(("Net files", "*.net"), ("All files", "*.*"))
)
return file_path
def get_fann_data(network_file):
# Adjust the path to the Rust executable as needed
result = subprocess.run(['./extract_fann_data/target/debug/extract_fann_data.exe', network_file], capture_output=True, text=True)
if result.returncode != 0:
print("Error:", result.stderr)
return None, None
layer_sizes = []
connections = []
parsing_connections = False
for line in result.stdout.splitlines():
if line.startswith("Layers:"):
continue
elif line.startswith("Connections:"):
parsing_connections = True
continue
if parsing_connections:
from_neuron, to_neuron, weight = map(float, line.split())
connections.append((int(from_neuron), int(to_neuron), weight))
else:
layer_size, bias_count = map(int, line.split())
layer_sizes.append((layer_size, bias_count))
return layer_sizes, connections
def visualize_fann_network(network_file):
# Get network data
layer_sizes, connections = get_fann_data(network_file)
if layer_sizes is None or connections is None:
return # Error handling in get_fann_data should provide error output
# Create a directed graph
G = nx.DiGraph()
# Positions dictionary to hold the position of each neuron
pos = {}
node_count = 0
x_spacing = 1.0
y_spacing = 1.0
# Calculate the maximum layer size for proper spacing
max_layer_size = max(size for size, bias in layer_sizes)
# Build nodes and position them layer by layer from left to right
for layer_index, (layer_size, bias_count) in enumerate(layer_sizes):
y_positions = list(range(-layer_size-bias_count+1, 1, 1)) # Center-align vertically
y_positions = [y * (max_layer_size / (layer_size + bias_count)) * y_spacing for y in y_positions] # Adjust spacing
for neuron_index in range(layer_size + bias_count): # Include bias neurons
node_label = f"L{layer_index}N{neuron_index}"
G.add_node(node_count, label=node_label)
pos[node_count] = (layer_index * x_spacing, y_positions[neuron_index % len(y_positions)])
node_count += 1
# Add connections to the graph
for from_neuron, to_neuron, weight in connections:
G.add_edge(from_neuron, to_neuron, weight=weight)
max_weight = max(abs(weight) for _, _, weight in connections)
print(f"Max weight: {max_weight}")
# Draw nodes
nx.draw_networkx_nodes(G, pos, node_color='skyblue', node_size=200)
nx.draw_networkx_labels(G, pos, font_size=7)
# Custom function for edge properties
def adjust_properties(weight):
# if weight > 0:
# print("Weight:", weight)
color = 'green' if weight > 0 else 'red'
alpha = min((abs(weight) / max_weight) ** 3, 1)
# print(f"Color: {color}, Alpha: {alpha}")
return color, alpha
# Draw edges with custom properties
for u, v, d in G.edges(data=True):
color, alpha = adjust_properties(d['weight'])
nx.draw_networkx_edges(G, pos, edgelist=[(u, v)], edge_color=color, alpha=alpha, width=1.5, arrows=False)
# Show plot
plt.title('FANN Network Visualization')
plt.axis('off') # Turn off the axis
plt.show()
# Path to the FANN network file
fann_path = 'F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_4f2be613-ab26-4384-9a65-450e043984ea\\6\\4f2be613-ab26-4384-9a65-450e043984ea_fighter_nn_0.net'
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_fc294503-7b2a-40f8-be59-ccc486eb3f79\\0\\fc294503-7b2a-40f8-be59-ccc486eb3f79_fighter_nn_0.net"
# fann_path = 'F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_99c30a7f-40ab-4faf-b16a-b44703fdb6cd\\0\\99c30a7f-40ab-4faf-b16a-b44703fdb6cd_fighter_nn_0.net'
# Has a 4 layer network
# # Generation 1
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\1\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net"
# # Generation 5
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\5\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net"
# # Generation 10
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\10\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net"
# # Generation 20
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\20\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net"
# # Generation 32
# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\32\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net"
fann_path = select_file()
visualize_fann_network(fann_path)

View file

@ -1,104 +0,0 @@
# Re-importing necessary libraries
import json
import matplotlib.pyplot as plt
import networkx as nx
import random
def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
if not nx.is_tree(G):
raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
root = next(iter(nx.topological_sort(G)))
else:
root = random.choice(list(G.nodes))
def _hierarchy_pos(G, root, width=2., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None):
if pos is None:
pos = {root: (xcenter, vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
children = list(G.successors(root)) # Use successors to get children for DiGraph
if not isinstance(G, nx.DiGraph):
if parent is not None:
children.remove(parent)
if len(children) != 0:
dx = width / len(children)
nextx = xcenter - width / 2 - dx / 2
for child in children:
nextx += dx
pos = _hierarchy_pos(G, child, width=dx*2.0, vert_gap=vert_gap,
vert_loc=vert_loc - vert_gap, xcenter=nextx,
pos=pos, parent=root)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
# Simplified JSON data for demonstration
with open('gemla/round4.json', 'r') as file:
simplified_json_data = json.load(file)
# Function to traverse the tree and create a graph
def traverse(node, graph, parent=None):
if node is None:
return
node_id = node["val"]["id"]
if "node" in node["val"] and node["val"]["node"]:
scores = node["val"]["node"]["scores"]
generations = node["val"]["node"]["generation"]
population_size = node["val"]["node"]["population_size"]
# Prepare to track the highest score across all generations and the corresponding individual
overall_max_score = float('-inf')
overall_max_score_individual = None
overall_max_score_gen = None
for gen, gen_scores in enumerate(scores):
if gen_scores: # Ensure the dictionary is not empty
# Find the max score and the individual for this generation
max_score_for_gen = max(gen_scores.values())
individual_with_max_score_for_gen = max(gen_scores, key=gen_scores.get)
# if max_score_for_gen > overall_max_score:
overall_max_score = max_score_for_gen
overall_max_score_individual = individual_with_max_score_for_gen
overall_max_score_gen = gen
# print debug statement
# print(f"Node {node_id}: Max score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})")
# print(f"Left: {node.get('left')}, Right: {node.get('right')}")
label = f"{node_id}\nGenerations: {generations}, Population: {population_size}\nMax score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen + 1 if overall_max_score_gen is not None else 'N/A'})"
else:
label = node_id
graph.add_node(node_id, label=label)
if parent:
graph.add_edge(parent, node_id)
traverse(node.get("left"), graph, parent=node_id)
traverse(node.get("right"), graph, parent=node_id)
# Create a directed graph
G = nx.DiGraph()
# Populate the graph
traverse(simplified_json_data[0], G)
# Find the root node (a node with no incoming edges)
root_candidates = [node for node, indeg in G.in_degree() if indeg == 0]
if root_candidates:
root_node = root_candidates[0] # Assuming there's only one root candidate
else:
root_node = None # This should ideally never happen in a properly structured tree
# Use the determined root node for hierarchy_pos
if root_node is not None:
pos = hierarchy_pos(G, root=root_node)
labels = nx.get_node_attributes(G, 'label')
nx.draw(G, pos, labels=labels, with_labels=True, arrows=True)
plt.show()
else:
print("No root node found. Cannot draw the tree.")