public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return(false);
            }

            if (obj.GetType() != typeof(ConnectionEndpointsStruct))
            {
                return(false);
            }

            ConnectionEndpointsStruct ces = (ConnectionEndpointsStruct)obj;

            return((sourceNeuronId == ces.sourceNeuronId) && (targetNeuronId == ces.targetNeuronId));
        }
        /// <summary>
        /// Adds a connection to the list that will eventually be copied into a child of this genome during sexual reproduction.
        /// A helper function that is only called by CreateOffspring_Sexual_ProcessCorrelationItem().
        /// </summary>
        /// <param name="connectionGene">Specifies the connection to add to this genome.</param>
        /// <param name="overwriteExisting">If there is already a connection from the same source to the same target,
        /// that connection is replaced when overwriteExisting is true and remains (no change is made) when overwriteExisting is false.</param>
		private void CreateOffspring_Sexual_AddGene(ConnectionGene connectionGene, bool overwriteExisting)
		{
			ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(
																connectionGene.SourceNeuronId, 
																connectionGene.TargetNeuronId);

			// Check if a matching gene has already been added.
			object oIdx = newConnectionGeneTable[connectionKey];
			if(oIdx==null)
			{	// No matching gene has been added.
				// Register this new gene with the newConnectionGeneTable - store its index within newConnectionGeneList.
				newConnectionGeneTable[connectionKey] = newConnectionGeneList.Count;

				// Add the gene to the list.
				newConnectionGeneList.Add(connectionGene);
			}
			else if(overwriteExisting)
			{
				// Overwrite the existing matching gene with this one. In fact only the weight value differs between two
				// matching connection genes, so just overwrite the existing genes weight value.
				
				// Remember that we stored the gene's index in newConnectionGeneTable. So use it here.
				newConnectionGeneList[(int)oIdx].Weight = connectionGene.Weight;
			}
		}
		private void RemoveSimpleNeuron(uint neuronId, EvolutionAlgorithm ea)
		{
			// Create new connections that connect all of the incoming and outgoing neurons
			// that currently exist for the simple neuron. 
			NeuronConnectionLookup lookup = (NeuronConnectionLookup)neuronConnectionLookupTable[neuronId];
			foreach(ConnectionGene incomingConnection in lookup.incomingList)
			{
				foreach(ConnectionGene outgoingConnection in lookup.outgoingList)
				{
					if(TestForExistingConnection(incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId))
					{	// Connection already exists.
						continue;
					}

					// Test for matching connection within NewConnectionGeneTable.
					ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(incomingConnection.SourceNeuronId, 
																							outgoingConnection.TargetNeuronId);
					ConnectionGene existingConnection = (ConnectionGene)ea.NewConnectionGeneTable[connectionKey];
					ConnectionGene newConnectionGene;
					if(existingConnection==null)
					{	// No matching connection found. Create a connection with a new ID.
						newConnectionGene = new ConnectionGene(ea.NextInnovationId,
							incomingConnection.SourceNeuronId,
							outgoingConnection.TargetNeuronId,
							(Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange/2.0);

						// Register the new ID with NewConnectionGeneTable.
						ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene);
	
						// Add the new gene to the genome.
						connectionGeneList.Add(newConnectionGene);
					}
					else
					{	// Matching connection found. Re-use its ID.
						newConnectionGene = new ConnectionGene(existingConnection.InnovationId,
							incomingConnection.SourceNeuronId,
							outgoingConnection.TargetNeuronId,
							(Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange/2.0);

						// Add the new gene to the genome. Use InsertIntoPosition() to ensure we don't break the sort 
						// order of the connection genes.
						connectionGeneList.InsertIntoPosition(newConnectionGene);
					}

					
				}
			}

			// Delete the old connections.
			foreach(ConnectionGene incomingConnection in lookup.incomingList)
				connectionGeneList.Remove(incomingConnection);

			foreach(ConnectionGene outgoingConnection in lookup.outgoingList)
			{	
				// Filter out recurrent connections - they will have already been 
				// deleted in the loop through 'lookup.incomingList'.
				if(outgoingConnection.TargetNeuronId != neuronId)
					connectionGeneList.Remove(outgoingConnection);
			}

			// Delete the simple neuron - it no longer has any connections to or from it.
			neuronGeneList.Remove(neuronId);
		}
		private void Mutate_AddConnection(EvolutionAlgorithm ea)
		{
			// We are always guaranteed to have enough neurons to form connections - because the input/output neurons are
			// fixed. Any domain that doesn't require input/outputs is a bit nonsensical!

			// Make a fixed number of attempts at finding a suitable connection to add. 
			
			if(neuronGeneList.Count>1)
			{	// At least 2 neurons, so we have a chance at creating a connection.

				for(int attempts=0; attempts<5; attempts++)
				{
					// Select candidate source and target neurons. Any neuron can be used as the source. Input neurons 
					// should not be used as a target
					int srcNeuronIdx; 
					int tgtNeuronIdx;
				
					/* Here's some code for adding connections that attempts to avoid any recursive conenctions
					 * within a network by only linking to neurons with innovation id's greater than the source neuron.
					 * Unfortunately this doesn't work because new neurons with large innovations ID's are inserted 
					 * randomly through a network's topology! Hence this code remains here in readyness to be resurrected
					 * as part of some future work to support feedforward nets.
//					if(ea.NeatParameters.feedForwardOnly)
//					{
//						/* We can ensure that all networks are feedforward only by only adding feedforward connections here.
//						 * Feed forward connections fall into one of the following categories.  All references to indexes 
//						 * are indexes within this genome's neuronGeneList:
//						 * 1) Source neuron is an input or hidden node, target is an output node.
//						 * 2) Source is an input or hidden node, target is a hidden node with an index greater than the source node's index.
//						 * 3) Source is an output node, target is an output node with an index greater than the source node's index.
//						 * 
//						 * These rules are easier to understand if you understand how the different types if neuron are arranged within
//						 * the neuronGeneList array. Neurons are arranged in the following order:
//						 * 
//						 * 1) A single bias neuron is always first.
//						 * 2) Experiment specific input neurons.
//						 * 3) Output neurons.
//						 * 4) Hidden neurons.
//						 * 
//						 * The quantity and innovationID of all neurons within the first 3 categories remains fixed throughout the life
//						 * of an experiment, hence we always know where to find the bias, input and output nodes. The number of hidden nodes
//						 * can vary as ne nodes are created, pruned away or perhaps dropped during crossover, however they are always arranged
//						 * newest to oldest, or in other words sorted by innovation idea, lowest ID first. 
//						 * 
//						 * If output neurons were at the end of the list with hidden nodes in the middle then generating feedforward 
//						 * connections would be as easy as selecting a target neuron with a higher index than the source neuron. However, that
//						 * type of arrangement is not conducive to the operation of other routines, hence this routine is a little bit more
//						 * complicated as a result.
//						 */
//					
//						// Ok, for a source neuron we can pick any neuron except the last output neuron.
//						int neuronIdxCount = neuronGeneList.Count;
//						int neuronIdxBound = neuronIdxCount-1;
//
//						// Generate count-1 possibilities and avoid the last output neuron's idx.
//						srcNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * neuronIdxBound);
//						if(srcNeuronIdx>inputBiasOutputNeuronCountMinus2) srcNeuronIdx++;
//						
//
//						// Now generate a target idx depending on what type of neuron srcNeuronIdx is pointing to.
//						if(srcNeuronIdx<inputAndBiasNeuronCount)
//						{	// Source is a bias or input neuron. Target can be any output or hidden neuron.
//							tgtNeuronIdx = inputAndBiasNeuronCount + (int)Math.Floor(Utilities.NextDouble() * (neuronIdxCount-inputAndBiasNeuronCount));
//						}
//						else if(srcNeuronIdx<inputBiasOutputNeuronCount)
//						{	// Source is an output neuron, but not the last output neuron. Target can be any output neuron with an index
//							// greater than srcNeuronIdx.
//							tgtNeuronIdx = (inputAndBiasNeuronCount+1) + (int)Math.Floor(Utilities.NextDouble() * ((inputBiasOutputNeuronCount-1)-srcNeuronIdx));
//						}
//						else 
//						{	// Source is a hidden neuron. Target can be any hidden neuron after srcNeuronIdx or any output neuron.
//							tgtNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * ((neuronIdxBound-srcNeuronIdx)+outputNeuronCount));
//
//							if(tgtNeuronIdx<outputNeuronCount)
//							{	// Map to an output neuron idx.
//								tgtNeuronIdx += inputAndBiasNeuronCount;
//							}
//							else
//							{
//								// Map to one of the hidden neurons after srcNeuronIdx.
//								tgtNeuronIdx = (tgtNeuronIdx-outputNeuronCount)+srcNeuronIdx+1;
//							}
//						}
//					}

//					// Source neuron can by any neuron. Target neuron is any neuron except input neurons.
//					srcNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * neuronGeneList.Count);
//					tgtNeuronIdx = inputAndBiasNeuronCount + (int)Math.Floor(Utilities.NextDouble() * (neuronGeneList.Count-inputAndBiasNeuronCount));
//
//                  NeuronGene sourceNeuron = neuronGeneList[srcNeuronIdx];
//                  NeuronGene targetNeuron = neuronGeneList[tgtNeuronIdx];

                    // Find all potential inputs, or quit if there are not enough. 
                    // Neurons cannot be inputs if they are dummy input nodes of a module.
                    NeuronGeneList potentialInputs = new NeuronGeneList();
                    foreach (NeuronGene n in neuronGeneList) {
                        if (!(n.ActivationFunction is ModuleInputNeuron)) {
                            potentialInputs.Add(n);
                        }
                    }
                    if (potentialInputs.Count < 1)
                        return;

                    // Find all potential outputs, or quit if there are not enough.
                    // Neurons cannot be outputs if they are dummy input or output nodes of a module, or network input or bias nodes.
                    NeuronGeneList potentialOutputs = new NeuronGeneList();
                    foreach (NeuronGene n in neuronGeneList) {
                        if (n.NeuronType != NeuronType.Bias && n.NeuronType != NeuronType.Input
                                && !(n.ActivationFunction is ModuleInputNeuron)
                                && !(n.ActivationFunction is ModuleOutputNeuron)) {
                            potentialOutputs.Add(n);
                        }
                    }
                    if (potentialOutputs.Count < 1)
                        return;

                    NeuronGene sourceNeuron = potentialInputs[Utilities.Next(potentialInputs.Count)];
                    NeuronGene targetNeuron = potentialOutputs[Utilities.Next(potentialOutputs.Count)];

					// Check if a connection already exists between these two neurons.
					uint sourceId = sourceNeuron.InnovationId;
					uint targetId = targetNeuron.InnovationId;

					if(!TestForExistingConnection(sourceId, targetId))
					{
						// Check if a matching mutation has already occured on another genome. 
						// If so then re-use the connection ID.
						ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId);
						ConnectionGene existingConnection = (ConnectionGene)ea.NewConnectionGeneTable[connectionKey];
						ConnectionGene newConnectionGene;
						if(existingConnection==null)
						{	// Create a new connection with a new ID and add it to the Genome.
							newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId,
								(Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange/4.0) - ea.NeatParameters.connectionWeightRange/8.0);

							// Register the new connection with NewConnectionGeneTable.
							ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

							// Add the new gene to this genome. We have a new ID so we can safely append the gene to the end 
							// of the list without risk of breaking the innovation ID order.
							connectionGeneList.Add(newConnectionGene);
						}
						else
						{	// Create a new connection, re-using the ID from existingConnection, and add it to the Genome.
							newConnectionGene = new ConnectionGene(existingConnection.InnovationId, sourceId, targetId,
								(Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange/4.0) - ea.NeatParameters.connectionWeightRange/8.0);

							// Add the new gene to this genome. We are re-using an ID so we must ensure the connection gene is
							// inserted into the correct position (sorted by innovation ID).
							connectionGeneList.InsertIntoPosition(newConnectionGene);
						}
					
						return;
					}
				}
			}

			// We couldn't find a valid connection to create. Instead of doing nothing lets perform connection
			// weight mutation.
			Mutate_ConnectionWeights(ea);
		}
        private void Mutate_AddModule(EvolutionAlgorithm ea)
        {
            // Find all potential inputs, or quit if there are not enough. 
            // Neurons cannot be inputs if they are dummy input nodes created for another module.
            NeuronGeneList potentialInputs = new NeuronGeneList();
            foreach (NeuronGene n in neuronGeneList) {
                if (!(n.ActivationFunction is ModuleInputNeuron)) {
                    potentialInputs.Add(n);
                }
            }
            if (potentialInputs.Count < 1)
                return;

            // Find all potential outputs, or quit if there are not enough.
            // Neurons cannot be outputs if they are dummy input or output nodes created for another module, or network input or bias nodes.
            NeuronGeneList potentialOutputs = new NeuronGeneList();
            foreach (NeuronGene n in neuronGeneList) {
                if (n.NeuronType != NeuronType.Bias && n.NeuronType != NeuronType.Input
                        && !(n.ActivationFunction is ModuleInputNeuron)
                        && !(n.ActivationFunction is ModuleOutputNeuron)) {
                    potentialOutputs.Add(n);
                }
            }
            if (potentialOutputs.Count < 1)
                return;

            // Pick a new function for the new module.
            IModule func = ModuleFactory.GetRandom();

            // Choose inputs uniformly at random, with replacement.
            // Create dummy neurons to represent the module's inputs.
            // Create connections between the input nodes and the dummy neurons.
            IActivationFunction inputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleInputNeuron");
            List<uint> inputDummies = new List<uint>(func.InputCount);
            for (int i = 0; i < func.InputCount; i++) {
                NeuronGene newNeuronGene = new NeuronGene(ea.NextInnovationId, NeuronType.Hidden, inputFunction);
                neuronGeneList.Add(newNeuronGene);

                uint sourceId = potentialInputs[Utilities.Next(potentialInputs.Count)].InnovationId;
                uint targetId = newNeuronGene.InnovationId;

                inputDummies.Add(targetId);

                // Create a new connection with a new ID and add it to the Genome.
                ConnectionGene newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId,
                    (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange / 2.0);

                // Register the new connection with NewConnectionGeneTable.
                ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId);
                ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

                // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end 
                // of the list without risk of breaking the innovation ID order.
                connectionGeneList.Add(newConnectionGene);
            }

            // Choose outputs uniformly at random, with replacement.
            // Create dummy neurons to represent the module's outputs.
            // Create connections between the output nodes and the dummy neurons.
            IActivationFunction outputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleOutputNeuron");
            List<uint> outputDummies = new List<uint>(func.OutputCount);
            for (int i = 0; i < func.OutputCount; i++) {
                NeuronGene newNeuronGene = new NeuronGene(ea.NextInnovationId, NeuronType.Hidden, outputFunction);
                neuronGeneList.Add(newNeuronGene);

                uint sourceId = newNeuronGene.InnovationId;
                uint targetId = potentialOutputs[Utilities.Next(potentialOutputs.Count)].InnovationId;

                outputDummies.Add(sourceId);

                // Create a new connection with a new ID and add it to the Genome.
                ConnectionGene newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId,
                    (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange / 2.0);

                // Register the new connection with NewConnectionGeneTable.
                ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId);
                ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

                // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end 
                // of the list without risk of breaking the innovation ID order.
                connectionGeneList.Add(newConnectionGene);
            }

            // Pick a new ID for the new module and create it.
            // Modules do not participate in history comparisons, so we will always create a new innovation ID.
            // We can change this here if it becomes a problem.
            ModuleGene newModule = new ModuleGene(ea.NextInnovationId, func, inputDummies, outputDummies);
            moduleGeneList.Add(newModule);
        }
		/// <summary>
		/// Search for connections with the same end-points throughout the whole population and 
		/// ensure that like connections have the same innovation ID.
		/// </summary>
		private void MatchConnectionIds()
		{
			Hashtable connectionIdTable = new Hashtable();

			int genomeBound=pop.GenomeList.Count;
			for(int genomeIdx=0; genomeIdx<genomeBound; genomeIdx++)
			{
				NeatGenome.NeatGenome genome = (NeatGenome.NeatGenome)pop.GenomeList[genomeIdx];

				int connectionGeneBound = genome.ConnectionGeneList.Count;
				for(int connectionGeneIdx=0; connectionGeneIdx<connectionGeneBound; connectionGeneIdx++)
				{
					ConnectionGene connectionGene = genome.ConnectionGeneList[connectionGeneIdx];

					ConnectionEndpointsStruct ces = new ConnectionEndpointsStruct();
					ces.sourceNeuronId = connectionGene.SourceNeuronId;
					ces.targetNeuronId = connectionGene.TargetNeuronId;
					
					Object existingConnectionIdObject = connectionIdTable[ces];
					if(existingConnectionIdObject==null)
					{	// No connection withthe same end-points has been registered yet, so 
						// add it to the table.
						connectionIdTable.Add(ces, connectionGene.InnovationId);
					}
					else
					{	// This connection is already registered. Give our latest connection 
						// the same innovation ID as the one in the table.
						connectionGene.InnovationId = (uint)existingConnectionIdObject;
					}
				}

				// The connection genes in this genome may now be out of order. Therefore we must ensure 
				// they are sorted before we continue.
				genome.ConnectionGeneList.SortByInnovationId();
			}
		}
Exemple #7
0
        private void RemoveSimpleNeuron(long neuronId, NeatParameters neatParameters, IdGenerator idGen, Hashtable NewConnectionGeneTable)
        {
            WINManager win = WINManager.SharedWIN;
            //WINNode node;
            WINConnection connection;
            // Create new connections that connect all of the incoming and outgoing neurons
            // that currently exist for the simple neuron.
            NeuronConnectionLookup lookup = (NeuronConnectionLookup)neuronConnectionLookupTable[neuronId];
            foreach(ConnectionGene incomingConnection in lookup.incomingList)
            {
                foreach(ConnectionGene outgoingConnection in lookup.outgoingList)
                {
                    if(TestForExistingConnection(incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId))
                    {	// Connection already exists.
                        continue;
                    }

                    // Test for matching connection within NewConnectionGeneTable.
                    ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(incomingConnection.SourceNeuronId,
                                                                                            outgoingConnection.TargetNeuronId);
                    ConnectionGene existingConnection = (ConnectionGene)NewConnectionGeneTable[connectionKey];
                    ConnectionGene newConnectionGene;
                    if(existingConnection==null)
                    {

                        // No matching connection found. Create a connection with a new ID.

                        //create our newest conncetion, noting the source,target and weight
                        connection = win.createWINConnection(
               WINConnection.ConnectionWithProperties(incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId), idGen);

                        // Create a new connection with a new ID and add it to the Genome.
                        newConnectionGene = new ConnectionGene(connection.UniqueID, connection.SourceID, connection.TargetID,
                             (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange / 2.0
                             );

                        //newConnectionGene = new ConnectionGene(idGen.NextInnovationId,
                        //    incomingConnection.SourceNeuronId,
                        //    outgoingConnection.TargetNeuronId,
                        //    (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange/2.0);

                        // Register the new ID with NewConnectionGeneTable.
                        NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

                        // Add the new gene to the genome.
                        connectionGeneList.Add(newConnectionGene);
                    }
                    else
                    {
                        //WIN should acknowledge change in individual here

                        // Matching connection found. Re-use its ID.
                        newConnectionGene = new ConnectionGene(existingConnection.InnovationId,
                            incomingConnection.SourceNeuronId,
                            outgoingConnection.TargetNeuronId,
                            (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange/2.0);

                        // Add the new gene to the genome. Use InsertIntoPosition() to ensure we don't break the sort
                        // order of the connection genes.
                        connectionGeneList.InsertIntoPosition(newConnectionGene);
                    }

                }
            }

            // Delete the old connections.
            foreach(ConnectionGene incomingConnection in lookup.incomingList)
                connectionGeneList.Remove(incomingConnection);

            foreach(ConnectionGene outgoingConnection in lookup.outgoingList)
            {
                // Filter out recurrent connections - they will have already been
                // deleted in the loop through 'lookup.incomingList'.
                if(outgoingConnection.TargetNeuronId != neuronId)
                    connectionGeneList.Remove(outgoingConnection);
            }

            // Delete the simple neuron - it no longer has any connections to or from it.
            neuronGeneList.Remove(neuronId);
        }
Exemple #8
0
        private void Mutate_AddModule(NeatParameters neatParameters, IdGenerator idGen, Hashtable NewConnectionGeneTable)
        {
            // Find all potential inputs, or quit if there are not enough.
            // Neurons cannot be inputs if they are dummy input nodes created for another module.
            NeuronGeneList potentialInputs = new NeuronGeneList();
            foreach (NeuronGene n in neuronGeneList) {
                if (!(n.ActivationFunction is ModuleInputNeuron)) {
                    potentialInputs.Add(n);
                }
            }
            if (potentialInputs.Count < 1)
                return;

            // Find all potential outputs, or quit if there are not enough.
            // Neurons cannot be outputs if they are dummy input or output nodes created for another module, or network input or bias nodes.
            NeuronGeneList potentialOutputs = new NeuronGeneList();
            foreach (NeuronGene n in neuronGeneList) {
                if (n.NeuronType != NeuronType.Bias && n.NeuronType != NeuronType.Input
                        && !(n.ActivationFunction is ModuleInputNeuron)
                        && !(n.ActivationFunction is ModuleOutputNeuron)) {
                    potentialOutputs.Add(n);
                }
            }
            if (potentialOutputs.Count < 1)
                return;

            // Pick a new function for the new module.
            IModule func = ModuleFactory.GetRandom();

            WINManager win = WINManager.SharedWIN;
            WINNode node;
            WINConnection connection;

            // Choose inputs uniformly at random, with replacement.
            // Create dummy neurons to represent the module's inputs.
            // Create connections between the input nodes and the dummy neurons.
            IActivationFunction inputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleInputNeuron");
            List<long> inputDummies = new List<long>(func.InputCount);
            for (int i = 0; i < func.InputCount; i++) {

                //we are calling nextinnovationID, this is the place for WIN!
                //in reality, win should know the activation function as well, but that's not currently implemented

                //here we create a new node, and two new connections
                //we don't send in a genomeID here, so we get it set for us!
                //do we need to inform it of the activation function? I think so?
                node = win.createWINNode(new PropertyObject() { { WINNode.SNodeString, NeuronType.Hidden.ToString()}});

                NeuronGene newNeuronGene = new NeuronGene(node.UniqueID, NeuronType.Hidden, inputFunction);
                neuronGeneList.Add(newNeuronGene);

                long sourceId = potentialInputs[Utilities.Next(potentialInputs.Count)].InnovationId;
                long targetId = newNeuronGene.InnovationId;

                inputDummies.Add(targetId);

                //aha! we must call the innovationID again, we check against win

                //we don't send in any uniqueIDs so they are generated for us, and we update our idGen object
                connection = win.createWINConnection(
                    WINConnection.ConnectionWithProperties(sourceId, targetId), idGen);

                // Create a new connection with a new ID and add it to the Genome.
                ConnectionGene newConnectionGene = new ConnectionGene(connection.UniqueID, connection.SourceID, connection.TargetID,
                    (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange / 2.0
                    );

                // Register the new connection with NewConnectionGeneTable.
                ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId);
                NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

                // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end
                // of the list without risk of breaking the innovation ID order.
                connectionGeneList.Add(newConnectionGene);
            }

            // Choose outputs uniformly at random, with replacement.
            // Create dummy neurons to represent the module's outputs.
            // Create connections between the output nodes and the dummy neurons.
            IActivationFunction outputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleOutputNeuron");
            List<long> outputDummies = new List<long>(func.OutputCount);
            for (int i = 0; i < func.OutputCount; i++) {

                node = win.createWINNode(new PropertyObject() { { WINNode.SNodeString, NeuronType.Hidden.ToString() } });

                NeuronGene newNeuronGene = new NeuronGene(node.UniqueID, NeuronType.Hidden, outputFunction);
                neuronGeneList.Add(newNeuronGene);

                long sourceId = newNeuronGene.InnovationId;
                long targetId = potentialOutputs[Utilities.Next(potentialOutputs.Count)].InnovationId;

                outputDummies.Add(sourceId);

                connection = win.createWINConnection(
                   WINConnection.ConnectionWithProperties(sourceId, targetId), idGen);

                // Create a new connection with a new ID and add it to the Genome.
                ConnectionGene newConnectionGene = new ConnectionGene(connection.UniqueID, connection.SourceID, connection.TargetID,
                   (Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange / 2.0
                    );

                    //new ConnectionGene(idGen.NextInnovationId, sourceId, targetId,
                    //(Utilities.NextDouble() * neatParameters.connectionWeightRange) - neatParameters.connectionWeightRange / 2.0);

                // Register the new connection with NewConnectionGeneTable.
                ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId);
                NewConnectionGeneTable.Add(connectionKey, newConnectionGene);

                // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end
                // of the list without risk of breaking the innovation ID order.
                connectionGeneList.Add(newConnectionGene);
            }

            // Pick a new ID for the new module and create it.
            // Modules do not participate in history comparisons, so we will always create a new innovation ID.
            // We can change this here if it becomes a problem.

            //TODO: Paul check win conditions here
            //this is confusing from a WIN perspective, I don't know if I'm going to support modules
            //I can create a generic NextInnovationID object, but don't know if it's worth it

            ModuleGene newModule = new ModuleGene(idGen.NextInnovationId, func, inputDummies, outputDummies);
            moduleGeneList.Add(newModule);
        }