/// <summary> /// Create a default minimal genome that describes a NN with the given number of inputs and outputs. /// </summary> /// <returns></returns> public static IGenome CreateGenome(NeatParameters neatParameters, IdGenerator idGenerator, int inputNeuronCount, int outputNeuronCount, float connectionProportion) { IActivationFunction actFunct; NeuronGene neuronGene; // temp variable. NeuronGeneList inputNeuronGeneList = new NeuronGeneList(); // includes bias neuron. NeuronGeneList outputNeuronGeneList = new NeuronGeneList(); NeuronGeneList neuronGeneList = new NeuronGeneList(); ConnectionGeneList connectionGeneList = new ConnectionGeneList(); // IMPORTANT NOTE: The neurons must all be created prior to any connections. That way all of the genomes // will obtain the same innovation ID's for the bias,input and output nodes in the initial population. // Create a single bias neuron. //TODO: DAVID proper activation function change to NULL? actFunct = ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid"); neuronGene = new NeuronGene(idGenerator.NextInnovationId, NeuronType.Bias, actFunct); inputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); // Create input neuron genes. //actFunct = ActivationFunctionFactory.GetRandomActivationFunction(neatParameters); for (int i = 0; i < inputNeuronCount; i++) { //TODO: DAVID proper activation function change to NULL? neuronGene = new NeuronGene(idGenerator.NextInnovationId, NeuronType.Input, actFunct); inputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); } // Create output neuron genes. //actFunct = ActivationFunctionFactory.GetActivationFunction("NullFn"); for (int i = 0; i < outputNeuronCount; i++) { //actFunct = ActivationFunctionFactory.GetRandomActivationFunction(neatParameters); neuronGene = new NeuronGene(idGenerator.NextInnovationId, NeuronType.Output, actFunct); //TODO: DAVID proper activation function //neuronGene = new NeuronGene(idGenerator.NextInnovationId, NeuronType.Output, actFunct); outputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); } // Loop over all possible connections from input to output nodes and create a number of connections based upon // connectionProportion. foreach (NeuronGene targetNeuronGene in outputNeuronGeneList) { foreach (NeuronGene sourceNeuronGene in inputNeuronGeneList) { // Always generate an ID even if we aren't going to use it. This is necessary to ensure connections // between the same neurons always have the same ID throughout the generated population. uint connectionInnovationId = idGenerator.NextInnovationId; if (Utilities.NextDouble() < connectionProportion) { // Ok lets create a connection. connectionGeneList.Add(new ConnectionGene(connectionInnovationId, sourceNeuronGene.InnovationId, targetNeuronGene.InnovationId, (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange / 2.0)); // Weight 0 +-5 } } } // Don't create any hidden nodes at this point. Fundamental to the NEAT way is to start minimally! return(new NeatGenome(idGenerator.NextGenomeId, neuronGeneList, connectionGeneList, inputNeuronCount, outputNeuronCount)); }