Struct neuralneat::Genome
source · pub struct Genome { /* private fields */ }
Expand description
A Genome contains a network of Network of Neurons and Genes that when given the same number of inputs the containing Pool was given, will produce a Vec with the same number of outputs that the Pool was given.
Implementations§
source§impl Genome
impl Genome
sourcepub fn active_network(&self) -> DiGraph<Neuron, Gene>
pub fn active_network(&self) -> DiGraph<Neuron, Gene>
Returns only the active part of the Genome’s network. That is to say only Neurons that have an activated value != 0.0 AND are connected to another Neuron with an activated value != 0.0 by an enabled Gene with a weight greater than 0.0 will be included.
sourcepub fn stats(&self) -> GenomeStats
pub fn stats(&self) -> GenomeStats
Returns the GenomeStats at a moment in time. (If you need updated statistics you must call this method each time you need them.)
pub fn get_max_fitness(&self) -> f32
sourcepub fn get_outputs(&self) -> Vec<f32>
pub fn get_outputs(&self) -> Vec<f32>
Returns the activated values of the output nodes in a Vec of the same length as the outputs parameter the Pool was created with.
Examples found in repository?
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the training data structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
let mut trainer = Trainer::new(training_data);
// This function will be called once per Genome per piece of
// TrainingData in each generation, passing the values of the
// output nodes of the Genome as well as the expected result
// from the TrainingData.
trainer.evaluate_fn = adding_fitness_func;
trainer.hidden_activation = linear_activation;
trainer.output_activation = linear_activation;
// Train over the course of 100 generations
trainer.train(&mut gene_pool, 100);
let best_genome = gene_pool.get_best_genome();
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome).unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
sourcepub fn update_fitness(&mut self, new_fitness: f32)
pub fn update_fitness(&mut self, new_fitness: f32)
Update the fitness score of the Genome to new_fitness
.
Examples found in repository?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
sourcepub fn evaluate(
&mut self,
inputs: &Vec<f32>,
hidden_activation_fn: Option<ActivationFn>,
output_activation_fn: Option<ActivationFn>
)
pub fn evaluate( &mut self, inputs: &Vec<f32>, hidden_activation_fn: Option<ActivationFn>, output_activation_fn: Option<ActivationFn> )
“Evaluate” the Genome using the given inputs
as values for the input
Neurons. These values will propagate through the Genome’s network
ultimately updating the activated values of the output Neurons. These
activated values can then be accessed by calling Genome::get_outputs.
Arguments
inputs
- The values to use for the input Neurons.hidden_activation_fn
- If provided, this function will be called to active the raw values of each Neuron in the hidden layer of the network. If not provided, the built-in sigmoid activation function will be used instead.output_activation_fn
- If provided, this function will be called to active the raw values of each Neuron in the output layer of the network. If not provided, the built-in sigmoid activation function will be used instead.
Examples found in repository?
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the training data structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
let mut trainer = Trainer::new(training_data);
// This function will be called once per Genome per piece of
// TrainingData in each generation, passing the values of the
// output nodes of the Genome as well as the expected result
// from the TrainingData.
trainer.evaluate_fn = adding_fitness_func;
trainer.hidden_activation = linear_activation;
trainer.output_activation = linear_activation;
// Train over the course of 100 generations
trainer.train(&mut gene_pool, 100);
let best_genome = gene_pool.get_best_genome();
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome).unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}