<commit_before>extern crate neat;
extern crate rand;
extern crate graph_neighbor_matching;
extern crate graph_io_gml;
extern crate closed01;
extern crate petgraph;
extern crate cppn;
extern crate asexp;
#[macro_use] extern crate log;

mod common;
mod config;

use neat::population::{Population, Unrated, Runner};
use neat::genomes::acyclic_network::{Genome, GlobalCache, GlobalInnovationCache, Mater, ElementStrategy};
use neat::fitness::Fitness;
use graph_neighbor_matching::graph::GraphBuilder;
use rand::Rng;
use std::marker::PhantomData;
use common::{load_graph, Neuron, convert_neuron_from_str, GraphSimilarity, NodeCount};
use cppn::cppn::{Cppn, CppnNode};
use cppn::bipolar::BipolarActivationFunction;
use cppn::substrate::Substrate;
use cppn::position::Position2d;
use neat::weight::{Weight, WeightRange};

type Node = CppnNode<BipolarActivationFunction>;


/// Distribute n points equally within the interval [left, right]

struct DistributeIntervalIter {
    n: usize,
    i: usize,
    left: f64,
    right: f64
}

impl DistributeIntervalIter {
    fn new(n: usize, left: f64, right: f64) -> Self {
        assert!(left <= right);
        DistributeIntervalIter {
            n: n,
            i: 0,
            left: left,
            right: right,
        }
    }
}

impl Iterator for DistributeIntervalIter {
    type Item = f64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.n {
            return None;
        }

        debug_assert!(self.n > 0);

        let width = self.right - self.left;
        let step = width / self.n as f64; 
        let start = self.left + (step / 2.0);

        let new = start + (self.i as f64) * step;
        self.i += 1;

        return Some(new);
    }
}

#[test]
fn test_distribute_interval() {
    let mut iter = DistributeIntervalIter::new(0, -1.0, 1.0);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(1, -1.0, 1.0);
    assert_eq!(Some(0.0), iter.next());
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(3, -1.0, 1.0);
    assert_eq!(-66, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(0, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(66, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(4, -1.0, 1.0);
    assert_eq!(-75, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(-25, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(25, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(75, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(5, -1.0, 1.0);
    assert_eq!(-80, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(-40, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(0, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(40, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(80, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());
}

#[derive(Debug)]
struct FitnessEvaluator {
    sim: GraphSimilarity,
    node_count: NodeCount,
}

impl FitnessEvaluator {
    // A larger fitness means "better"
    fn fitness(&self, genome: &Genome<Node>) -> f32 {
        let mut substrate = Substrate::new();

        let mut y_iter = DistributeIntervalIter::new(3, -1.0, 1.0); // 3 layers (Input, Hidden, Output)

        // Inputs
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(self.node_count.inputs, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Input);
            }
        }

        // Hidden
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(self.node_count.hidden, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Hidden);
            }
        }

        // Outputs
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(self.node_count.outputs, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Output);
            }
        }

        let mut cppn = Cppn::new(genome.network());

        // now develop the cppn. the result is a graph
        let mut builder = GraphBuilder::new();
        for (i, node) in substrate.nodes().iter().enumerate() {
            let _ = builder.add_node(i, node.node_type.clone());
        }
        for link in substrate.iter_links(&mut cppn, None) {
            if link.weight >= 0.0 && link.weight <= 1.0 {
                builder.add_edge(link.source_idx,
                                 link.target_idx,
                                 closed01::Closed01::new(link.weight as f32));
            }
        }
        let graph = builder.graph();

        self.sim.fitness(graph)
    }
}

struct ES;

impl ElementStrategy<Node> for ES {<commit_msg>Factor code out into genome_to_graph()<commit_after>extern crate neat;
extern crate rand;
extern crate graph_neighbor_matching;
extern crate graph_io_gml;
extern crate closed01;
extern crate petgraph;
extern crate cppn;
extern crate asexp;
#[macro_use] extern crate log;

mod common;
mod config;

use neat::population::{Population, Unrated, Runner};
use neat::genomes::acyclic_network::{Genome, GlobalCache, GlobalInnovationCache, Mater, ElementStrategy};
use neat::fitness::Fitness;
use graph_neighbor_matching::graph::{GraphBuilder, OwnedGraph};
use rand::Rng;
use std::marker::PhantomData;
use common::{load_graph, Neuron, convert_neuron_from_str, GraphSimilarity, NodeCount};
use cppn::cppn::{Cppn, CppnNode};
use cppn::bipolar::BipolarActivationFunction;
use cppn::substrate::Substrate;
use cppn::position::Position2d;
use neat::weight::{Weight, WeightRange};

type Node = CppnNode<BipolarActivationFunction>;


/// Distribute n points equally within the interval [left, right]

struct DistributeIntervalIter {
    n: usize,
    i: usize,
    left: f64,
    right: f64
}

impl DistributeIntervalIter {
    fn new(n: usize, left: f64, right: f64) -> Self {
        assert!(left <= right);
        DistributeIntervalIter {
            n: n,
            i: 0,
            left: left,
            right: right,
        }
    }
}

impl Iterator for DistributeIntervalIter {
    type Item = f64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.n {
            return None;
        }

        debug_assert!(self.n > 0);

        let width = self.right - self.left;
        let step = width / self.n as f64; 
        let start = self.left + (step / 2.0);

        let new = start + (self.i as f64) * step;
        self.i += 1;

        return Some(new);
    }
}

#[test]
fn test_distribute_interval() {
    let mut iter = DistributeIntervalIter::new(0, -1.0, 1.0);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(1, -1.0, 1.0);
    assert_eq!(Some(0.0), iter.next());
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(3, -1.0, 1.0);
    assert_eq!(-66, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(0, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(66, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(4, -1.0, 1.0);
    assert_eq!(-75, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(-25, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(25, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(75, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());

    let mut iter = DistributeIntervalIter::new(5, -1.0, 1.0);
    assert_eq!(-80, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(-40, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(0, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(40, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(80, (iter.next().unwrap() * 100.0) as usize);
    assert_eq!(None, iter.next());
}

fn genome_to_graph(genome: &Genome<Node>, node_count: &NodeCount) -> OwnedGraph<Neuron> {
        let mut substrate = Substrate::new();

        let mut y_iter = DistributeIntervalIter::new(3, -1.0, 1.0); // 3 layers (Input, Hidden, Output)

        // Inputs
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(node_count.inputs, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Input);
            }
        }

        // Hidden
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(node_count.hidden, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Hidden);
            }
        }

        // Outputs
        {
            let y = y_iter.next().unwrap();
            for x in DistributeIntervalIter::new(node_count.outputs, -1.0, 1.0) {
                substrate.add_node(Position2d::new(x, y), Neuron::Output);
            }
        }

        let mut cppn = Cppn::new(genome.network());

        // now develop the cppn. the result is a graph
        let mut builder = GraphBuilder::new();
        for (i, node) in substrate.nodes().iter().enumerate() {
            let _ = builder.add_node(i, node.node_type.clone());
        }
        for link in substrate.iter_links(&mut cppn, None) {
            if link.weight >= 0.0 && link.weight <= 1.0 {
                builder.add_edge(link.source_idx,
                                 link.target_idx,
                                 closed01::Closed01::new(link.weight as f32));
            }
        }
        return builder.graph();
}

#[derive(Debug)]
struct FitnessEvaluator {
    sim: GraphSimilarity,
    node_count: NodeCount,
}

impl FitnessEvaluator {
    // A larger fitness means "better"
    fn fitness(&self, genome: &Genome<Node>) -> f32 {
        self.sim.fitness(genome_to_graph(genome, &self.node_count))
    }
}

struct ES;

impl ElementStrategy<Node> for ES {
{'fraction_non_alphanumeric': 0.15254913698493466, 'fraction_numerical': 0.030330240446971964, 'mean_word_length': 2.5915442493729848, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1962554', 'n_tokens_mistral': 3562, 'n_tokens_neox': 3381, 'n_words': 779}