Compare commits
2 commits
6725ab3feb
...
3cc08a4b2a
Author | SHA1 | Date | |
---|---|---|---|
3cc08a4b2a | |||
![]() |
c5aecf07a9 |
11 changed files with 2 additions and 4369 deletions
171
analyze_data.py
171
analyze_data.py
|
@ -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()
|
||||
|
|
@ -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"
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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()
|
|
@ -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)
|
|
@ -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.")
|
Loading…
Add table
Reference in a new issue