neat.NEAT.java Source code

Java tutorial

Introduction

Here is the source code for neat.NEAT.java

Source

package neat;

/*
 * Copyright (C) 2011 Istvan Fehervari, Wilfried Elmenreich
 * Original project page: http://www.frevotool.tk
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License Version 3 as published
 * by the Free Software Foundation http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * There is no warranty for this free software. The GPL requires that 
 * modified versions be marked as changed, so that their problems will
 * not be attributed erroneously to authors of previous versions.
 */

import graphics.NeuralNetworkVisualisation;

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import main.FrevoMain;
import neat.activationfunction.ActivationFunction;
import neat.activationfunction.LinearFunction;
import neat.activationfunction.SigmoidFunction;
import neat.activationfunction.TanhFunction;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;

import utils.NESRandom;
import utils.SafeSAX;
import core.AbstractRepresentation;
import core.XMLFieldEntry;

/**
 * <b>NeuroEvolution of Augmenting Topologies</b><br>
 * A neural network whose structure is also evolved.<br>
 * See http://en.wikipedia.org/wiki/Neuroevolution_of_augmenting_topologies<br>
 * <br>
 * Originally developed by Ken Stanley in 2002 while at The University of Texas
 * at Austin
 */
public class NEAT extends AbstractRepresentation {

    private final static int MAX_LINK_ATTEMPTS = 5;

    private NEATChromosome chromosome;

    private NESRandom generator;
    private int input_number;
    private int output_number;

    private Synapse[] connections;
    private Neuron[] neurons;
    private int level = 0;

    private float excessCoeff;
    private float disjointCoeff;
    private float weightCoeff;

    private float max_perturbation;
    private float max_bias_perturbation;

    private float add_link_prob;
    private float add_node_prob;
    private float mutate_node_weight_replaced_prob;
    private float mutate_node_sf_prob;
    private float mutate_link_toggle_prob;
    private float mutate_feature_prob;
    private float mutate_node_bias_prob;
    private float mutate_link_weight_prob;

    private boolean featureSelection;

    private boolean recurrent;

    public NEAT(int inputnumber, int outputnumber, NESRandom random, Hashtable<String, XMLFieldEntry> properties) {
        super(inputnumber, outputnumber, random, properties);

        this.setProperties(properties);
        this.generator = random;
        this.input_number = inputnumber;
        this.output_number = outputnumber;

        loadProperties();

        // construct from innovation base
        chromosome = InnovationDatabase.database().individualFromTemplate();

        updateNetStructure();
    }

    public NEAT(int inputnumber, int outputnumber, NESRandom random, Hashtable<String, XMLFieldEntry> properties,
            NEATChromosome chromosome) {

        super(inputnumber, outputnumber, random, properties);

        this.setProperties(properties);
        this.generator = random;
        this.input_number = inputnumber;
        this.output_number = outputnumber;

        loadProperties();

        // construct from innovation base
        this.chromosome = chromosome;

        updateNetStructure();
    }

    public NEAT(File xmlfile, int popid) {
        // loads properties
        super(xmlfile);

        // get population root node
        Document doc = SafeSAX.read(xmlfile, true);
        Node dpopulations = doc.selectSingleNode("/frevo/population");
        List<?> nets = dpopulations.selectNodes(".//NEAT");
        //   Node pop = (Node) npops.get(popid);

        //List<?> nets = pop.selectNodes("./*");

        // return the one with the highest fitness
        int bestID = 0;
        Node net = (Node) nets.get(bestID);
        String fitnessString = net.valueOf("./@fitness");
        double bestfitness = Double.parseDouble(fitnessString);

        for (int i = 1; i < nets.size(); i++) {
            net = (Node) nets.get(i);
            fitnessString = net.valueOf("./@fitness");
            double fitness = Double.parseDouble(fitnessString);
            if (fitness > bestfitness) {
                bestID = i;
                bestfitness = fitness;
            }
        }

        loadFromXML((Node) nets.get(bestID));

    }

    private void loadProperties() {
        Hashtable<String, XMLFieldEntry> properties = getProperties();
        // load properties
        XMLFieldEntry entry = properties.get("disjoint_coeff");
        disjointCoeff = Float.parseFloat(entry.getValue());

        entry = properties.get("excess_coeff");
        excessCoeff = Float.parseFloat(entry.getValue());

        entry = properties.get("weight_coeff");
        weightCoeff = Float.parseFloat(entry.getValue());

        entry = properties.get("recursive");
        recurrent = Boolean.parseBoolean(entry.getValue());

        entry = properties.get("max_perturbation");
        max_perturbation = Float.parseFloat(entry.getValue());

        entry = properties.get("feature_selection");
        featureSelection = Boolean.parseBoolean(entry.getValue());

        // probabilities

        entry = properties.get("add_link_prob");
        add_link_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("add_node_prob");
        add_node_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_node_weight_replaced_prob");
        mutate_node_weight_replaced_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_link_toggle_prob");
        mutate_link_toggle_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_node_sf_prob");
        mutate_node_sf_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_feature_prob");
        mutate_feature_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_node_bias_prob");
        mutate_node_bias_prob = Float.parseFloat(entry.getValue());

        entry = properties.get("mutate_link_weight_prob");
        mutate_link_weight_prob = Float.parseFloat(entry.getValue());
    }

    /** Initializes the innovation factory, should only run once */
    public static void initialize(int inputnumber, int outputnumber, Hashtable<String, XMLFieldEntry> properties,
            Random rand) {
        if (!isClassInitialized()) {

            InnovationDatabase.reset();

            // initialize the innovation database (single template right now)
            InnovationDatabase.database().createTemplate(inputnumber, outputnumber, properties, rand);

            setClassInitialized(true);
        }
    }

    /**
     * Generates a neural network structure based on the chromosome
     * 
     */
    public void updateNetStructure() {

        ArrayList<NEATGene> genes = chromosome.genes();

        ArrayList<NEATNodeGene> nodes = new ArrayList<NEATNodeGene>();
        ArrayList<NEATLinkGene> links = new ArrayList<NEATLinkGene>();

        for (int i = 0; i < chromosome.size(); i++) {
            if (genes.get(i) instanceof NEATNodeGene) {
                nodes.add((NEATNodeGene) genes.get(i));
            } else if (genes.get(i) instanceof NEATLinkGene) {
                if (((NEATLinkGene) genes.get(i)).isEnabled()) {
                    // only add enabled links to the net structure
                    links.add((NEATLinkGene) genes.get(i));
                }
            }
        }

        this.connections = this.createLinks(links, this.createNeurons(nodes));
        this.assignNeuronDepth(this.outputNeurons(), 0);
    }

    public ArrayList<Neuron> outputNeurons() {
        ArrayList<Neuron> outputNeurons = new ArrayList<Neuron>();

        for (int i = 0; i < this.neurons.length; i++) {
            if (this.neurons[i].neuronType() == NEATNodeGene.OUTPUT) {
                outputNeurons.add(this.neurons[i]);
            }
        }
        return (outputNeurons);
    }

    public ArrayList<Neuron> inputNeurons() {
        ArrayList<Neuron> inputNeurons = new ArrayList<Neuron>();

        for (int i = 0; i < this.neurons.length; i++) {
            if (this.neurons[i].neuronType() == NEATNodeGene.INPUT) {
                inputNeurons.add(this.neurons[i]);
            }
        }
        return (inputNeurons);
    }

    public ArrayList<Neuron> hiddenNeurons() {
        ArrayList<Neuron> hiddenNeurons = new ArrayList<Neuron>();

        for (int i = 0; i < this.neurons.length; i++) {
            if (this.neurons[i].neuronType() == NEATNodeGene.HIDDEN) {
                hiddenNeurons.add(this.neurons[i]);
            }
        }
        return (hiddenNeurons);
    }

    /** Provided lists must be created. */
    public void getNeurons(ArrayList<Neuron> inputs, ArrayList<Neuron> hidden, ArrayList<Neuron> outputs) {
        if (inputs != null)
            inputs.clear();

        if (hidden != null)
            hidden.clear();

        if (outputs != null)
            outputs.clear();

        for (int i = 0; i < this.neurons.length; i++) {
            if (this.neurons[i].neuronType() == NEATNodeGene.INPUT) {
                if (inputs != null)
                    inputs.add(this.neurons[i]);
            } else if (this.neurons[i].neuronType() == NEATNodeGene.OUTPUT) {
                if (outputs != null)
                    outputs.add(this.neurons[i]);
            } else {
                if (hidden != null)
                    hidden.add(this.neurons[i]);
            }
        }
    }

    private void assignNeuronDepth(ArrayList<Neuron> neurons, int depth) {
        int i;
        Neuron neuron;

        for (i = 0; i < neurons.size(); i++) {
            neuron = neurons.get(i);
            if (neuron.neuronType() == NEATNodeGene.OUTPUT) {
                if (neuron.neuronDepth() == -1) {
                    neuron.setNeuronDepth(depth);
                    this.assignNeuronDepth(neuron.sourceNeurons(), depth + 1);
                }
            } else if (neuron.neuronType() == NEATNodeGene.HIDDEN) {
                if (neuron.neuronDepth() == -1) {
                    neuron.setNeuronDepth(depth);
                    this.assignNeuronDepth(neuron.sourceNeurons(), depth + 1);
                }
            } else if (neuron.neuronType() == NEATNodeGene.INPUT) {
                neuron.setNeuronDepth(Integer.MAX_VALUE);
            }
        }
    }

    private Synapse[] createLinks(ArrayList<NEATLinkGene> links, Neuron[] neurons) {
        NEATLinkGene gene;
        Synapse[] synapses = new Synapse[links.size()];

        Neuron from;
        Neuron to;

        for (int i = 0; i < links.size(); i++) {
            gene = links.get(i);
            from = this.findNeuronById(neurons, gene.getFromId());
            to = this.findNeuronById(neurons, gene.getToId());
            to.addSourceNeuron(from);
            synapses[i] = new Synapse(from, to, gene.getWeight());
            synapses[i].setEnabled(gene.isEnabled());
            to.addIncomingSynapse(synapses[i]);
        }

        return (synapses);
    }

    private Neuron[] createNeurons(ArrayList<NEATNodeGene> nodes) {
        this.neurons = new Neuron[nodes.size()];
        NEATNodeGene gene;

        for (int i = 0; i < neurons.length; i++) {
            gene = nodes.get(i);
            this.neurons[i] = new Neuron(this.createActivationFunction(gene), gene.id(), gene.getType());
            this.neurons[i].modifyBias(gene.bias(), 0, true);
        }

        return (neurons);
    }

    private ActivationFunction createActivationFunction(NEATNodeGene gene) {
        ActivationFunction function = null;
        // inputs are passed through
        if (gene.getType() == NEATNodeGene.INPUT) {
            function = new LinearFunction();
        } else if (gene.getType() == NEATNodeGene.OUTPUT) {
            function = new SigmoidFunction(gene.sigmoidFactor());
        } else {
            function = new TanhFunction();
        }

        return (function);
    }

    private Neuron findNeuronById(Neuron[] neurons, int id) {
        boolean found = false;
        Neuron neuron = null;
        int i = 0;

        while (!found) {
            if (neurons[i].id() == id) {
                neuron = neurons[i];
                found = true;
            } else {
                i++;
            }
        }

        return (neuron);
    }

    @Override
    public void exportToFile(File saveFile) {
        // TODO Auto-generated method stub

    }

    @Override
    protected void mutationFunction(float severity, float probability, int method) {

        boolean res = mutateAddNode();

        if (!res)
            res = mutateAddLink();

        if (!res) {
            // no structural changes has occurred then mutate the rest

            if (generator.nextFloat() < mutate_link_weight_prob)
                mutateLinkWeights();
            if (generator.nextFloat() < mutate_node_bias_prob)
                mutateNodeBias();
            if (generator.nextFloat() < mutate_node_sf_prob)
                mutateNodeSigmoidFactor();
            if ((generator.nextFloat() < mutate_link_toggle_prob) && featureSelection) {
                mutateLinkToggle();
            }
            if ((generator.nextFloat() < mutate_feature_prob) && (featureSelection)) {
                mutateFeatureNode();
            }
        }

        this.updateDepthInfo();
        // now update chrome for depth and recurrency legality

        chromosome.updateChromosome(this.ensureLegalLinks(chromosome.genes()));

        updateNetStructure();
    }

    private void mutateLinkToggle() {
        ArrayList<NEATLinkGene> links = new ArrayList<NEATLinkGene>();

        // collect all link trait
        for (NEATGene g : chromosome.genes()) {
            if (g instanceof NEATLinkGene)
                links.add((NEATLinkGene) g);
        }

        // select one randomly
        NEATLinkGene mutatee = links.get(generator.nextInt(links.size()));

        if (this.featureSelection) {
            mutatee.setEnabled(!mutatee.isEnabled());
        }
    }

    private void mutateNodeSigmoidFactor() {
        // select randomly
        ArrayList<NEATNodeGene> nodes = new ArrayList<NEATNodeGene>();

        // collect all nodes
        for (NEATGene g : chromosome.genes()) {
            if (g instanceof NEATNodeGene)
                nodes.add((NEATNodeGene) g);
        }

        // select one randomly
        NEATNodeGene mutatee = nodes.get(generator.nextInt(nodes.size()));

        double newSF = mutatee.sigmoidFactor();

        newSF = mutatee.sigmoidFactor()
                + MathUtils.nextClampedDouble(-max_perturbation, max_perturbation, generator);
        mutatee = new NEATNodeGene(mutatee.getInnovationNumber(), mutatee.id(), newSF, mutatee.getType(),
                mutatee.bias());
        // mutatee.setSigmoidFactor(newSF);
    }

    private void mutateNodeBias() {
        ArrayList<NEATNodeGene> nodes = new ArrayList<NEATNodeGene>();

        // collect all nodes
        for (NEATGene g : chromosome.genes()) {
            if (g instanceof NEATNodeGene)
                nodes.add((NEATNodeGene) g);
        }

        // select one randomly
        NEATNodeGene mutatee = nodes.get(generator.nextInt(nodes.size()));

        double newBias = mutatee.bias();

        newBias += MathUtils.nextClampedDouble(-max_bias_perturbation, max_bias_perturbation, generator);
        // mutatee.setBias(newBias);
        mutatee = new NEATNodeGene(mutatee.getInnovationNumber(), mutatee.id(), mutatee.sigmoidFactor(),
                mutatee.getType(), newBias);

    }

    private void mutateLinkWeights() {
        ArrayList<NEATLinkGene> links = new ArrayList<NEATLinkGene>();

        // collect all link trait
        for (NEATGene g : chromosome.genes()) {
            if (g instanceof NEATLinkGene)
                links.add((NEATLinkGene) g);
        }

        // select one randomly
        NEATLinkGene mutatee = links.get(generator.nextInt(links.size()));

        // change weight
        double newWeight;
        if (generator.nextDouble() < this.mutate_node_weight_replaced_prob) {
            newWeight = MathUtils.nextPlusMinusOne(generator);
        } else {
            newWeight = mutatee.getWeight()
                    + MathUtils.nextClampedDouble(-max_perturbation, max_perturbation, generator);
        }
        mutatee = new NEATLinkGene(mutatee.getInnovationNumber(), mutatee.isEnabled(), mutatee.getFromId(),
                mutatee.getToId(), newWeight);
    }

    /** Prunes out recurrent connections if they are not allowed */
    private ArrayList<NEATGene> ensureLegalLinks(ArrayList<NEATGene> genes) {
        NEATLinkGene link;
        NEATNodeGene from;
        NEATNodeGene to;

        if (this.recurrent)
            return genes;

        ArrayList<NEATGene> newGenes = new ArrayList<NEATGene>();

        // only need to prune if recurrency not allowed

        // only return enabled links
        for (int i = 0; i < genes.size(); i++) {
            if (genes.get(i) instanceof NEATLinkGene) {
                link = (NEATLinkGene) genes.get(i);
                from = this.findNode(link.getFromId(), genes);
                to = this.findNode(link.getToId(), genes);
                if (from.getDepth() > to.getDepth()) {
                    // not recurrent - so keep
                    newGenes.add(genes.get(i));
                }
            } else {
                // add nodes automatically
                newGenes.add(genes.get(i));
            }
        }

        return (newGenes);
    }

    private void updateDepthInfo() {
        // use descriptor's chromo to create net
        ArrayList<NEATNodeGene> nodes = new ArrayList<NEATNodeGene>();
        ArrayList<NEATLinkGene> links = new ArrayList<NEATLinkGene>();

        ArrayList<NEATGene> genes = chromosome.genes();

        for (int i = 0; i < genes.size(); i++) {
            if (genes.get(i) instanceof NEATNodeGene) {
                nodes.add((NEATNodeGene) genes.get(i));
            } else if (genes.get(i) instanceof NEATLinkGene) {
                if (((NEATLinkGene) genes.get(i)).isEnabled()) {
                    // only add enabled links to the net structure
                    links.add((NEATLinkGene) genes.get(i));
                }
            }
        }

        this.assignNeuronDepth(this.findOutputNodes(this.candidateNodes(genes)), 1, chromosome);
    }

    private void assignNeuronDepth(ArrayList<NEATNodeGene> nodeGenes, int depth, NEATChromosome mutated) {

        NEATNodeGene node;

        for (int i = 0; i < nodeGenes.size(); i++) {
            node = nodeGenes.get(i);
            if (node.getType() == NEATNodeGene.OUTPUT) {
                if (depth == 1) {
                    node.setDepth(depth);
                    this.assignNeuronDepth(this.findSourceNodes(node.id(), mutated.genes()), depth + 1, mutated);
                }
            } else if (node.getType() == NEATNodeGene.HIDDEN) {
                if (node.getDepth() == 0) {
                    // we have an unassigned depth
                    node.setDepth(depth);
                    this.assignNeuronDepth(this.findSourceNodes(node.id(), mutated.genes()), depth + 1, mutated);
                }
            } else if (node.getType() == NEATNodeGene.INPUT) {
                node.setDepth(Integer.MAX_VALUE);
            }
        }
    }

    private ArrayList<NEATNodeGene> findOutputNodes(ArrayList<NEATNodeGene> nodes) {
        ArrayList<NEATNodeGene> outputNodes = new ArrayList<NEATNodeGene>();
        NEATNodeGene node;

        for (int i = 0; i < nodes.size(); i++) {
            node = (NEATNodeGene) nodes.get(i);
            if (node.getType() == NEATNodeGene.OUTPUT) {
                outputNodes.add(node);
            }
        }

        return outputNodes;
    }

    private ArrayList<NEATNodeGene> findSourceNodes(int nodeId, ArrayList<NEATGene> genes) {
        ArrayList<NEATLinkGene> links = this.candidateLinks(genes);
        NEATLinkGene link;
        ArrayList<NEATNodeGene> sources = new ArrayList<NEATNodeGene>();

        for (int i = 0; i < links.size(); i++) {
            link = (NEATLinkGene) links.get(i);
            if (nodeId == link.getToId()) {
                // add from Id
                sources.add(this.findNode(link.getFromId(), genes));
            }
        }

        return (sources);
    }

    /** Returns a node from the gene set with the give id */
    private NEATNodeGene findNode(int id, ArrayList<NEATGene> genes) {
        int i = 0;
        NEATGene gene;
        NEATNodeGene node = null;
        boolean found = false;

        while (i < genes.size() && !found) {
            gene = genes.get(i);
            if (gene instanceof NEATNodeGene) {
                node = (NEATNodeGene) genes.get(i);
                if (node.id() == id) {
                    found = true;
                }
            }
            i++;
        }

        return (node);
    }

    /** Tries to add a node on an existing connection */
    private boolean mutateAddNode() {
        if (generator.nextDouble() > this.add_node_prob)
            return false;

        boolean mutated = false;

        ArrayList<NEATLinkGene> nodeLinks;

        NEATLinkGene chosenLink;
        NEATNodeGene newNode;
        NEATLinkGene newLowerLink;
        NEATLinkGene newUpperLink;

        int linkIdx;

        // add a node on an existing enabled connection
        // find an existing connection to intercept
        nodeLinks = this.candidateLinks(chromosome.genes());
        if (nodeLinks.size() > 0) {

            // pick a link randomly
            linkIdx = generator.nextInt(nodeLinks.size());
            chosenLink = (NEATLinkGene) nodeLinks.get(linkIdx);

            // disable old link
            chosenLink.setEnabled(false);
            newNode = InnovationDatabase.database().submitNodeInnovation(chosenLink);
            // newNode.setBias(MathUtils.nextPlusMinusOne());
            newLowerLink = InnovationDatabase.database().submitLinkInnovation(chosenLink.getFromId(), newNode.id());
            newUpperLink = InnovationDatabase.database().submitLinkInnovation(newNode.id(), chosenLink.getToId());

            // set weights according to Stanley et al's NEAT document
            newLowerLink.setWeight(1);
            newUpperLink.setWeight(chosenLink.getWeight());

            chromosome.addGene(newNode);
            chromosome.addGene(newLowerLink);
            chromosome.addGene(newUpperLink);

            mutated = true;
        }

        return mutated;
    }

    /** Returns true if a mutation was successful. */
    private boolean mutateAddLink() {
        boolean mutated = false;
        double linkRandVal = generator.nextDouble();
        NEATNodeGene from;
        NEATNodeGene to;
        int rIdx;
        int i = 0;
        ArrayList<NEATLinkGene> links;
        ArrayList<NEATNodeGene> nodes;

        NEATGene newLink = null;

        if (linkRandVal < this.add_link_prob) {
            nodes = this.candidateNodes(chromosome.genes());
            links = this.candidateLinks(chromosome.genes());
            // find a new available link
            while (newLink == null && i < MAX_LINK_ATTEMPTS) {
                rIdx = generator.nextInt(nodes.size());
                from = ((NEATNodeGene) nodes.get(rIdx));
                rIdx = generator.nextInt(nodes.size());
                to = ((NEATNodeGene) nodes.get(rIdx));

                if (!this.linkIllegal(from, to, links)) {
                    // set it to a random value
                    newLink = InnovationDatabase.database().submitLinkInnovation(from.id(), to.id());
                    ((NEATLinkGene) newLink).setWeight(MathUtils.nextPlusMinusOne(generator));
                    chromosome.addGene(newLink);
                    mutated = true;
                    break;
                }
                i++;
            }
        }

        return mutated;
    }

    private void mutateFeatureNode() {
        // select random feature gene
        ArrayList<NEATFeatureGene> featurenodes = new ArrayList<NEATFeatureGene>();

        // collect all nodes
        for (NEATGene g : chromosome.genes()) {
            if (g instanceof NEATFeatureGene)
                featurenodes.add((NEATFeatureGene) g);
        }

        // select one randomly
        NEATFeatureGene mutatee = featurenodes.get(generator.nextInt(featurenodes.size()));

        mutatee = new NEATFeatureGene(mutatee.getInnovationNumber(), mutatee.geneAsNumber().doubleValue()
                + MathUtils.nextClampedDouble(-max_perturbation, max_perturbation, generator));

    }

    private boolean linkIllegal(NEATNodeGene from, NEATNodeGene to, ArrayList<NEATLinkGene> links) {
        boolean illegal = false;
        int idx = 0;
        NEATLinkGene linkGene;

        if ((to.getType() == NEATNodeGene.INPUT)) {
            illegal = true;
        } else {
            while (!illegal && (idx < links.size())) {
                linkGene = (NEATLinkGene) links.get(idx);

                if ((linkGene.getFromId() == from.id() && linkGene.getToId() == to.id())) {
                    illegal = true;
                }
                idx++;
            }
        }

        return (illegal);
    }

    /** Returns a lost of link genes */
    private ArrayList<NEATLinkGene> candidateLinks(ArrayList<NEATGene> genes) {
        ArrayList<NEATLinkGene> nodeLinks = new ArrayList<NEATLinkGene>();
        NEATGene gene;

        for (int i = 0; i < genes.size(); i++) {
            gene = genes.get(i);
            if (gene instanceof NEATLinkGene) {
                nodeLinks.add((NEATLinkGene) gene);
            }
        }

        return (nodeLinks);
    }

    private ArrayList<NEATNodeGene> candidateNodes(ArrayList<NEATGene> genes) {
        ArrayList<NEATNodeGene> nodes = new ArrayList<NEATNodeGene>();
        NEATGene gene;

        for (int i = 0; i < genes.size(); i++) {
            gene = genes.get(i);
            if (gene instanceof NEATNodeGene) {
                nodes.add((NEATNodeGene) gene);
            }
        }

        return (nodes);
    }

    @Override
    protected void recombinationFunction(AbstractRepresentation other, int method) {
        NEATChromosome best = this.chromosome;
        NEATChromosome worst = ((NEAT) other).chromosome;

        // if fitness equal, shortest chromosome is be best (Cannot check it
        // here)
        if (getFitness() == other.getFitness()) {
            if (((NEAT) other).chromosome.size() < this.chromosome.size()) {
                best = worst;
                worst = this.chromosome;
            }
        }

        ArrayList<NEATGene> bestGenes = best.genes();
        ArrayList<NEATGene> worstGenes = worst.genes();

        boolean childBorn = false;
        ArrayList<NEATGene> childGenes = new ArrayList<NEATGene>();
        int bestIdx = 0;
        int worstIdx = 0;

        while (!childBorn) {
            if (worstIdx >= worstGenes.size()) {
                // copy rest of best
                while (bestIdx < bestGenes.size()) {
                    childGenes.add(bestGenes.get(bestIdx++).cloneGene());
                }
                childBorn = true;
            } else if (bestIdx >= bestGenes.size()) {
                childBorn = true;
            } else if (bestGenes.get(bestIdx).getInnovationNumber() == worstGenes.get(worstIdx)
                    .getInnovationNumber()) {
                // innovations are the same, pick one gene at random
                childGenes.add(generator.nextBoolean() ? bestGenes.get(bestIdx).cloneGene()
                        : worstGenes.get(worstIdx).cloneGene());
                bestIdx++;
                worstIdx++;
            } else if (bestGenes.get(bestIdx).getInnovationNumber() > worstGenes.get(worstIdx)
                    .getInnovationNumber()) {
                // skip disjoint/excess
                worstIdx++;
            } else if (bestGenes.get(bestIdx).getInnovationNumber() < worstGenes.get(worstIdx)
                    .getInnovationNumber()) {
                // add best disjoint/excess
                childGenes.add(bestGenes.get(bestIdx).cloneGene());
                bestIdx++;
            }
        }

        // assign new chromosome
        this.chromosome = createChromosome(childGenes);

        // update this structure
        this.updateNetStructure();

    }

    /** Creates a new chromosome from the genes provided. */
    private NEATChromosome createChromosome(ArrayList<NEATGene> genes) {
        NEATChromosome chromo = new NEATChromosome(genes);

        return chromo;
    }

    @Override
    public int getNumberofMutationFunctions() {
        return 1;
    }

    @Override
    public int getNumberOfRecombinationFunctions() {
        return 1;
    }

    static final boolean useFixedIteratedOutput = true;

    @Override
    public ArrayList<Float> getOutput(ArrayList<Float> input) {

        if (!useFixedIteratedOutput) {

            this.level = 0;
            int i;
            // travel through the graph backwards from each output node
            ArrayList<Neuron> outputNeurons = this.outputNeurons();
            if (outputNeurons.size() == 0) {
                System.err.println("WARNING: No output neurons, no output will be generated.");
            }

            ArrayList<Float> outputs = new ArrayList<Float>(outputNeurons.size());

            this.level = 0;
            for (i = 0; i < outputNeurons.size(); i++) {
                outputs.add(threshold((this.neuronOutput(outputNeurons.get(i), input))));
            }

            return outputs;

        } else {
            // get neurons
            ArrayList<Neuron> inputneurons = new ArrayList<Neuron>();
            ArrayList<Neuron> outputneurons = new ArrayList<Neuron>();
            ArrayList<Neuron> hiddenneurons = new ArrayList<Neuron>();
            getNeurons(inputneurons, hiddenneurons, outputneurons);

            // experimental value
            int ITERATION_NUMBER = 3;

            // calculate all input
            for (int in = 0; in < inputneurons.size(); in++) {
                Neuron neuron = inputneurons.get(in);
                neuron.activate(input.get(neuron.id() - 1));
            }

            for (int i = 0; i < ITERATION_NUMBER; i++) {

                // calculate all hidden
                for (int in = 0; in < hiddenneurons.size(); in++) {
                    Neuron neuron = hiddenneurons.get(in);
                    neuron.activate();
                }

                // calculate all output
                for (int in = 0; in < outputneurons.size(); in++) {
                    Neuron neuron = outputneurons.get(in);
                    neuron.activate();
                }
            }

            ArrayList<Float> outputs = new ArrayList<Float>(outputneurons.size());

            for (int i = 0; i < outputneurons.size(); i++) {
                outputs.add((float) (outputneurons.get(i).lastActivation()));
            }

            return outputs;
        }
    }

    private float threshold(double value) {
        if (value > 1.0)
            return 1.0f;
        else if (value < 0.0) {
            return 0.0f;
        }

        return (float) value;
    }

    public void setGenerator(NESRandom generator) {
        this.generator = generator;
    }

    static int maxlevel = 0;

    private double neuronOutput(Neuron neuron, ArrayList<Float> netInput) {

        double output = 0;
        double[] inputPattern;
        // find its inputs
        ArrayList<Neuron> sourceNodes = neuron.sourceNeurons();
        int i;

        if (this.level > maxlevel) {
            maxlevel = this.level;
            System.out.println("Max recursion level: " + maxlevel);
        }
        this.level++;
        if (neuron.neuronType() == NEATNodeGene.INPUT) {
            inputPattern = new double[1];
            // match the input column to the input node, id's start from 1
            inputPattern[0] = netInput.get(neuron.id() - 1);
        } else {
            inputPattern = new double[sourceNodes.size()];
            for (i = 0; i < sourceNodes.size(); i++) {
                if (neuron.id() == ((Neuron) sourceNodes.get(i)).id()) {
                    // Self Recurrent
                    inputPattern[i] = neuron.lastActivation();
                } else if (neuron.neuronDepth() > ((Neuron) sourceNodes.get(i)).neuronDepth()) {
                    // Recurrent
                    inputPattern[i] = ((Neuron) sourceNodes.get(i)).lastActivation();
                } else {
                    inputPattern[i] = this.neuronOutput((Neuron) sourceNodes.get(i), netInput);
                }
            }
        }
        output = neuron.activate(inputPattern);
        this.level--;
        return (output);
    }

    @Override
    public void reset() {
        for (int i = 0; i < neurons.length; i++) {
            neurons[i].reset();
        }
    }

    @Override
    public double diffTo(AbstractRepresentation o) {
        int disjoints = 0;
        int excess = 0;
        boolean genesToProcess = true;
        int applicantIdx = 0;
        boolean applicantIdxEnded = false;
        int repIdx = 0;
        boolean repIdxEnded = false;
        double avWeightDiff = 0;
        double weightDiffTotal = 0;
        int N;
        double compatabilityScore = Integer.MAX_VALUE;

        NEAT other = (NEAT) o;

        ArrayList<NEATGene> applicantGenes = chromosome.genes();
        ArrayList<NEATGene> repGenes = other.chromosome.genes();

        N = chromosome.size() > other.chromosome.size() ? chromosome.size() : other.chromosome.size();

        int matchinggenes = 0;

        while (genesToProcess) {
            // find disjoints and excess
            if ((applicantGenes.get(applicantIdx)).getInnovationNumber() == ((NEATGene) repGenes.get(repIdx))
                    .getInnovationNumber()) {
                // find average weight diff
                if (applicantGenes.get(applicantIdx) instanceof NEATLinkGene) {
                    weightDiffTotal += Math.abs((((NEATLinkGene) applicantGenes.get(applicantIdx)).getWeight()
                            - ((NEATLinkGene) repGenes.get(repIdx)).getWeight()));
                    matchinggenes++;
                }
                applicantIdx++;
                repIdx++;
            } else if (((NEATGene) applicantGenes.get(applicantIdx))
                    .getInnovationNumber() > ((NEATGene) repGenes.get(repIdx)).getInnovationNumber()) {
                if (repIdx < repGenes.size() && !repIdxEnded) {
                    repIdx++;
                    disjoints++;
                } else {
                    applicantIdx++;
                    excess++;
                }
            } else if (((NEATGene) applicantGenes.get(applicantIdx))
                    .getInnovationNumber() < ((NEATGene) repGenes.get(repIdx)).getInnovationNumber()) {
                if (applicantIdx < applicantGenes.size() && !applicantIdxEnded) {
                    applicantIdx++;
                    disjoints++;
                } else {
                    repIdx++;
                    excess++;
                }
            }

            if (applicantIdx == N || repIdx == N) {
                genesToProcess = false;
            }

            // ensure we don't go out of range
            if (applicantIdx == applicantGenes.size()) {
                applicantIdx %= applicantGenes.size();
                applicantIdxEnded = true;
            } else if (repIdx == repGenes.size()) {
                repIdx %= repGenes.size();
                repIdxEnded = true;
            }
        }

        if (matchinggenes != 0)
            avWeightDiff = Math.abs(weightDiffTotal / matchinggenes);

        compatabilityScore = ((excessCoeff * excess) / N) + ((disjointCoeff * disjoints) / N)
                + weightCoeff * avWeightDiff;

        return (compatabilityScore);
    }

    @Override
    protected AbstractRepresentation cloneFunction() {
        NEAT c = new NEAT(input_number, output_number, new NESRandom(generator.nextLong()), getProperties(),
                this.chromosome.cloneChromosome());

        if (this.isEvaluated()) {
            c.setFitness(getFitness());
        } else {
            c.setEvaluated(false);
        }

        return c;
    }

    @Override
    public void exportToXmlElement(Element element) {
        Element nn = element.addElement("NEAT");

        ArrayList<Neuron> inputneurons = new ArrayList<Neuron>();
        ArrayList<Neuron> outputneurons = new ArrayList<Neuron>();
        ArrayList<Neuron> hiddenneurons = new ArrayList<Neuron>();
        getNeurons(inputneurons, hiddenneurons, outputneurons);

        // export nodes

        nn.addAttribute("input_nodes", String.valueOf(inputneurons.size()));
        nn.addAttribute("output_nodes", String.valueOf(outputneurons.size()));
        nn.addAttribute("hidden_nodes", String.valueOf(hiddenneurons.size()));

        nn.addAttribute("recurrent", String.valueOf(recurrent));

        nn.addAttribute("randomseed", String.valueOf(generator.getSeed()));

        if (this.isEvaluated()) {
            nn.addAttribute("fitness", String.valueOf(this.getFitness()));
        }

        Element genes = nn.addElement("genes");

        Element node;
        for (int i = 0; i < chromosome.genes().size(); i++) {
            NEATGene gene = chromosome.genes().get(i);

            node = genes.addElement("NEATGene");
            gene.exportToXMLElement(node);
        }

    }

    @Override
    public NEAT loadFromXML(Node nd) {
        // Add properties
        loadProperties();

        // set generator
        generator = new NESRandom(FrevoMain.getSeed());

        this.input_number = Integer.parseInt(nd.valueOf("./@input_nodes"));
        this.output_number = Integer.parseInt(nd.valueOf("./@output_nodes"));

        this.recurrent = Boolean.parseBoolean(nd.valueOf("./@recurrent"));

        String fitnessString = nd.valueOf("./@fitness");
        if (!fitnessString.isEmpty()) {
            this.setFitness(Double.parseDouble(fitnessString));
        }

        // load genes
        ArrayList<NEATGene> genes = new ArrayList<NEATGene>();
        Node dgenes = nd.selectSingleNode("./genes");

        @SuppressWarnings("unchecked")
        List<? extends Node> gs = dgenes.selectNodes("./*");
        Iterator<? extends Node> it = gs.iterator();

        while (it.hasNext()) {
            Node n = it.next();
            String name = n.getName();

            if (name.equals("NEATlinkGene"))
                genes.add(new NEATLinkGene(n));
            else if (name.equals("NEATNodeGene"))
                genes.add(new NEATNodeGene(n));
            else
                genes.add(new NEATFeatureGene(n));
        }

        this.chromosome = new NEATChromosome(genes);

        updateNetStructure();

        return this;
    }

    @Override
    public Hashtable<String, String> getDetails() {
        Hashtable<String, String> result = new Hashtable<String, String>();
        visualizeTopology();
        return result;
    }

    @Override
    public String getHash() {
        double sum = 0;
        // add bias
        for (int i = 0; i < neurons.length; i++) {
            sum += neurons[i].bias();
        }
        // add weights
        for (int i = 0; i < connections.length; i++) {
            sum += connections[i].getWeight();
        }

        String res = Double.toString(sum);
        int resint = res.hashCode();
        return Integer.toHexString(resint & 0xFFFFF);
    }

    /**
     * Shows a frame with a visualized structure of the neural network. 
     */
    private void visualizeTopology() {
        NeuralNetworkVisualisation visualizer = new NeuralNetworkVisualisation();

        for (NEATGene gene : chromosome.genes()) {
            if (gene instanceof NEATNodeGene) {
                NEATNodeGene node = (NEATNodeGene) gene;
                if (node.getType() == NEATNodeGene.INPUT) {
                    visualizer.addToInputLayer(node.id());
                } else if (node.getType() == NEATNodeGene.HIDDEN) {
                    visualizer.addToHiddenLayer(node.id());
                } else {
                    visualizer.addToOutputLayer(node.id());
                }
            }
        }

        for (NEATGene gene : chromosome.genes()) {
            if (gene instanceof NEATLinkGene) {
                NEATLinkGene link = (NEATLinkGene) gene;
                if (!link.isEnabled()) {
                    continue;
                }

                try {
                    visualizer.addLink(link.getFromId(), link.getToId(), link.getWeight());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        visualizer.visualize();
    }
}