Represents a set of nodes on a substrate. Nodesets are used to represent sets such as the input and output nodes. Hidden nodes can be represented as layers with each layer being represented by a set. This allows connection mapping to be defined between sets.
        /// <summary>
        /// Returns an IEnumerable that yields the mappings/connections defined by the mapping function (from the source nodes to
        /// the target nodes) as a sequence. The alternative of returning a list would require a very long list in extreme scenarios; 
        /// this approach minimizes down memory usage.
        /// </summary>
        public IEnumerable<SubstrateConnection> GenerateConnections(SubstrateNodeSet srcNodeSet, SubstrateNodeSet tgtNodeSet)
        {
            // Test for scenario where srcNodeSet and tgtNodeSet are the same node set, that is, we are creating 
            // connections within a nodeset and therefore we need to test (and honour) _allowLocalRecurrentConnections.
            if(srcNodeSet == tgtNodeSet)
            {
                // Mapping between nodes within a single node set. 

                // Break the inner loop into two. This avoids having to make the local recurrent test connection
                // for every node pair - we only test when we actually have the same node as source and target.
                IList<SubstrateNode> nodeList = srcNodeSet.NodeList;
                int count = nodeList.Count;
                for(int i=0; i<count; i++)
                {
                    // Loop over all nodes prior to the i'th.
                    SubstrateNode srcNode = nodeList[i];
                    for(int j=0; j<i; j++)
                    {
                        if(_testNodePair(srcNode, nodeList[j])) {
                            yield return new SubstrateConnection(srcNode, nodeList[j]);
                        }
                    }

                    //  Test for local recurrent connection.
                    if(_allowLocalRecurrentConnections && _testNodePair(srcNode, srcNode)) {
                        yield return new SubstrateConnection(srcNode, srcNode);
                    }

                    // Loop over all nodes after the i'th.
                    for(int j=i+1; j<count; j++)
                    {
                        if(_testNodePair(srcNode, nodeList[j])) {
                            yield return new SubstrateConnection(srcNode, nodeList[j]);
                        }
                    }
                }
            }
            else
            {   
                // Mapping between nodes in two distinct node sets.
                foreach(SubstrateNode srcNode in srcNodeSet.NodeList)
                {
                    foreach(SubstrateNode tgtNode in tgtNodeSet.NodeList)
                    {
                        if(_testNodePair(srcNode, tgtNode)) {
                            yield return new SubstrateConnection(srcNode, tgtNode);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Creates a substrate to use to create network defintions/the genome decoder.
        /// </summary>
        /// <param name="visualFieldResolution">The visual field's pixel resolution, e.g. 11 means 11x11 pixels.</param>
        /// <param name="lengthCppnInput">Indicates if the CPPNs being decoded have an extra input for specifying connection length.</param>
        public static Substrate CreateSubstrate(int visualFieldResolution, bool lengthCppnInput = false)
        {
            // Create two layer 'sandwich' substrate.
            int pixelCount = visualFieldResolution * visualFieldResolution;
            double pixelSize = 2.0 / visualFieldResolution;
            double originPixelXY = -1 + (pixelSize / 2.0);

            SubstrateNodeSet inputLayer = new SubstrateNodeSet(pixelCount);
            SubstrateNodeSet HiddenLayer = new SubstrateNodeSet(pixelCount);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(pixelCount);

            // Node IDs start at 1. (bias node is always zero).
            uint inputId = 1;
            uint outputId = (uint)(pixelCount + 1);
            uint hiddenId = (uint)(2 * pixelCount + 2);
            double yReal = originPixelXY;

            for (int y = 0; y < visualFieldResolution; y++, yReal += pixelSize)
            {
                double xReal = originPixelXY;
                for (int x = 0; x < visualFieldResolution; x++, xReal += pixelSize, inputId++, outputId++, hiddenId++)
                {
                    //CJR: I leave the thrid dimintion in,cause I can ignore it when I'm adding inputs to the CPPN
                    //but use it to dicate what set of outputs to use
                    inputLayer.NodeList.Add(new SubstrateNode(inputId, new double[] { xReal, yReal, -1.0 }));
                    if ((x % 2 == 0 && y % 2 == 0) || ((x + 1) % 2 == 0 && (y + 1) % 2 == 0))
                    {
                        HiddenLayer.NodeList.Add(new SubstrateNode(hiddenId, new double[] { xReal, yReal, 0 }));
                    }
                    outputLayer.NodeList.Add(new SubstrateNode(outputId, new double[] { xReal, yReal, 1.0 }));
                }
            }

            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(3);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);
            nodeSetList.Add(HiddenLayer);

            //CJR: Fist and second layer must be input/output respectively to be validated by the substrate, so hidden is third
            // Define connection mappings between layers/sets.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(3);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 2, (double?)null));
            nodeSetMappingList.Add(NodeSetMapping.Create(2, 1, (double?)null));

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList, DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance), 0, 0.2, 5, nodeSetMappingList);

            return substrate;
        }
        /// <summary>
        /// Create a genome decoder for the experiment.
        /// </summary>
        protected override IGenomeDecoder<NeatGenome, IBlackBox> CreateHyperNeatGenomeDecoder()
        {
            // Create HyperNEAT network substrate.

            uint nodeId = 1;

            //-- Create input layer nodes.
            var inputLayer = new SubstrateNodeSet(samples.Length);
            for (int v = 0; v < samples.GetLength(0); v++) // variables
            {
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < m; j++)
                    {
                        var x = 2 * (double)i / n - 1;
                        var y = 2 * (double)j / m - 1;
                        var z = -(double)v / samples.GetLength(0);
                        inputLayer.NodeList.Add(new SubstrateNode(nodeId++, new double[] { x, y, z }));
                    }
                }
            }

            //-- Create output layer nodes.
            var outputLayers = new SubstrateNodeSet[nbClusters];
            for (var c = 0; c < nbClusters; c++) // clusters
            {
                outputLayers[c] = new SubstrateNodeSet(n * m);

                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < m; j++)
                    {
                        var x = 2 * (double)i / n - 1;
                        var y = 2 * (double)j / m - 1;
                        var z = c / nbClusters;
                        outputLayers[c].NodeList.Add(new SubstrateNode(nodeId++, new double[] { x, y, z }));
                    }
                }
            }

            // Connect up layers.
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(1 + nbClusters);
            nodeSetList.Add(inputLayer);
            foreach (var layer in outputLayers)
            {
                nodeSetList.Add(layer);
            }


            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(nbClusters);
            for (int i = 0; i < nbClusters; i++)
            {
                var inputLayerIdx = 0;
                var outputLayerIdx = 1 + i;
                nodeSetMappingList.Add(NodeSetMapping.Create(inputLayerIdx, outputLayerIdx, (double?)null));
            }

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList,
                DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance),
                0, 0.2, 5,
                nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, _lengthCppnInput);
            return genomeDecoder;

        }
        /// <summary>
        /// Creates a new HyperNEAT genome decoder that can be used to convert
        /// a genome into a phenome.
        /// </summary>
        public IGenomeDecoder<NeatGenome, IBlackBox> CreateHyperNeatGenomeDecoder()
        {

            var game = Parameters.GameFunction(null, null);
            int xTotal = game.Board.GetLength(0);
            int yTotal = game.Board.GetLength(1);

            // Create an input and an output layer for the HyperNEAT
            // substrate. Each layer corresponds to the game board
            // with 9 squares.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(Parameters.Inputs);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(Parameters.Outputs);

            // Each node in each layer needs a unique ID.
            uint uid = 1;

            // The game board is represented as a 2-dimensional plane.
            // Each square is at [0,...,1] in the x and y axis.
            // Thus, the game board for TicTacToe looks like this:
            //
            //  (0,1)  |  (0.5,1)  | (1,1)
            // (0,0.5) | (0.5,0.5) | (1,0.5)
            //  (0,0)  |  (0.5,0)  | (1,0)
            //
            for (int x = 0; x < xTotal; x++)
                for (int y = 0; y < yTotal; y++)
                    inputLayer.NodeList.Add(new SubstrateNode(uid++, new double[] { x, y }));

            for (int x = 0; x < xTotal; x++)
                for (int y = 0; y < yTotal; y++)
                    outputLayer.NodeList.Add(new SubstrateNode(uid++, new double[] { x, y }));

            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);

            // Define a connection mapping from the input layer to the output layer.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1, (double?)null));

            // Construct the substrate using a plain sigmoid as the phenome's
            // activation function. All weights under 0.2 will not generate
            // connections in the final phenome.
            List<ActivationFunctionInfo> fnList = new List<ActivationFunctionInfo>(5);
            fnList.Add(new ActivationFunctionInfo(0, 0.2, Linear.__DefaultInstance));
            fnList.Add(new ActivationFunctionInfo(1, 0.2, BipolarSigmoid.__DefaultInstance));
            fnList.Add(new ActivationFunctionInfo(2, 0.2, Gaussian.__DefaultInstance));
            fnList.Add(new ActivationFunctionInfo(3, 0.2, Sine.__DefaultInstance));
            fnList.Add(new ActivationFunctionInfo(4, 0.2, PlainSigmoid.__DefaultInstance));
            var activationFnLib = new DefaultActivationFunctionLibrary(fnList);
            Substrate substrate = new Substrate(nodeSetList,
                activationFnLib,
                4, 0.2, 5, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with
            // an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder =
                new HyperNeatDecoder(substrate, 
                                     NetworkActivationScheme.CreateCyclicFixedTimestepsScheme(4),
                                     NetworkActivationScheme.CreateCyclicFixedTimestepsScheme(4),
                                     false);

            return genomeDecoder;
        }
 private void AddNode(SubstrateNodeSet nodeSet, uint id, double x, double y, double z)
 {
     nodeSet.NodeList.Add(new SubstrateNode(id, new double[] {x, y, z}));
 }
        /// <summary>
        /// Create a genome decoder for the experiment.
        /// </summary>
        public IGenomeDecoder<NeatGenome,IBlackBox> CreateGenomeDecoder()
        {
            // Create HyperNEAT network substrate.

        //-- Create input layer nodes.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(13);

            //-- Hip joint inputs.
            // Left hip joint.
            AddNode(inputLayer, 1, -1.0, +1.0, -1.0);  // Angular velocity.
            AddNode(inputLayer, 2, -0.5, +1.0, -1.0);  // Angle.
            
            // Right hip joint.
            AddNode(inputLayer, 3, +0.5, +1.0, -1.0);  // Angle.
            AddNode(inputLayer, 4, +1.0, +1.0, -1.0);  // Angular velocity.

            //-- Knee joint inputs.
            // Left knee joint.
            AddNode(inputLayer, 5, -1.0, -1.0, -1.0);  // Angular velocity.
            AddNode(inputLayer, 6, -0.5, -1.0, -1.0);  // Angle.

            // Right knee joint.
            AddNode(inputLayer, 7, +0.5, -1.0, -1.0);  // Angular velocity.
            AddNode(inputLayer, 8, +1.0, -1.0, -1.0);  // Angle.

            //-- Torso inputs.
            AddNode(inputLayer, 9, 0.0, +1.0, -1.0);    // Torso elevation.
            AddNode(inputLayer, 10, 0.0, +0.75, -1.0);  // Torso angle.
            AddNode(inputLayer, 11, 0.0, +0.5, -1.0);   // Torso angular velocity.
            AddNode(inputLayer, 12, 0.0, +0.25, -1.0);  // Velocity X.
            AddNode(inputLayer, 13, 0.0, 0.0, -1.0);    // Velocity Y.

        //-- Output layer nodes.
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(4);
            AddNode(outputLayer, 14, -1.0, +1.0, +1.0); // Left hip torque.
            AddNode(outputLayer, 15, +1.0, +1.0, +1.0); // Right hip torque.
            AddNode(outputLayer, 16, -1.0, -1.0, +1.0); // Left knee torque.
            AddNode(outputLayer, 17, +1.0, -1.0, +1.0); // Right knee torque.

        //-- Hidden layer nodes.
            SubstrateNodeSet h1Layer = new SubstrateNodeSet(4);
            AddNode(h1Layer, 18, -1.0, +1.0, 0.0); 
            AddNode(h1Layer, 19, +1.0, +1.0, 0.0); 
            AddNode(h1Layer, 20, -1.0, -1.0, 0.0); 
            AddNode(h1Layer, 21, +1.0, -1.0, 0.0); 

        // Connect up layers.
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);
            nodeSetList.Add(h1Layer);

            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1, (double?)null));  // Input -> Output.
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 2, (double?)null));  // Input -> Hidden.
            nodeSetMappingList.Add(NodeSetMapping.Create(2, 1, (double?)null));  // Hidden -> Output.
            nodeSetMappingList.Add(NodeSetMapping.Create(1, 2, (double?)null));  // Output -> Hidden

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList, DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance), 0, 0.2, 5, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome,IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, _lengthCppnInput);
            return genomeDecoder;
        }
        /// <summary>
        /// Creates a genome decoder. We split this code into a separate  method so that it can be re-used by the problem domain visualization code
        /// (it needs to decode genomes to phenomes in order to create a visualization).
        /// </summary>
        /// <param name="visualFieldResolution">The visual field's pixel resolution, e.g. 11 means 11x11 pixels.</param>
        /// <param name="lengthCppnInput">Indicates if the CPPNs being decoded have an extra input for specifying connection length.</param>
        public IGenomeDecoder<NeatGenome,IBlackBox> CreateGenomeDecoder(int visualFieldResolution, bool lengthCppnInput)
        {
            // Create two layer 'sandwich' substrate.
            int pixelCount = visualFieldResolution * visualFieldResolution;
            double pixelSize = BoxesVisualDiscriminationEvaluator.VisualFieldEdgeLength / visualFieldResolution;
            double originPixelXY = -1 + (pixelSize/2.0);

            SubstrateNodeSet inputLayer = new SubstrateNodeSet(pixelCount);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(pixelCount);

            // Node IDs start at 1. (bias node is always zero).
            uint inputId = 1;
            uint outputId = (uint)(pixelCount + 1);
            double yReal = originPixelXY;

            for(int y=0; y<visualFieldResolution; y++, yReal += pixelSize)
            {
                double xReal = originPixelXY;
                for(int x=0; x<visualFieldResolution; x++, xReal += pixelSize, inputId++, outputId++)
                {
                    inputLayer.NodeList.Add(new SubstrateNode(inputId, new double[] {xReal, yReal, -1.0}));
                    outputLayer.NodeList.Add(new SubstrateNode(outputId, new double[] {xReal, yReal, 1.0}));
                }
            }

            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);

            // Define connection mappings between layers/sets.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1,(double?)null));

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList, DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance), 0, 0.2, 5, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome,IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, lengthCppnInput);
            return genomeDecoder;
        }
 /// <summary>
 /// Returns an estimate/hint for the number of connections that would be created between the provided source and target node sets.
 /// </summary>
 public int GetConnectionCountHint(SubstrateNodeSet srcNodeSet, SubstrateNodeSet tgtNodeSet)
 {
     if(null == _maximumConnectionDistance) {
         return srcNodeSet.NodeList.Count * tgtNodeSet.NodeList.Count;
     }
     
     int count = 0;
     foreach(SubstrateConnection conn in GenerateConnections(srcNodeSet, tgtNodeSet)) {
         count++;
     }
     return count;
 }
        /// <summary>
        /// Creates a new HyperNEAT genome decoder that can be used to convert a genome into a phenome.
        /// </summary>
        public IGenomeDecoder<NeatGenome, IBlackBox> CreateGenomeDecoder()
        {
            // Create an input and an output layer for the HyperNEAT
            // substrate. Each layer corresponds to the game board
            // with 9 squares.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(9);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(9);

            // Each node in each layer needs a unique ID.
            // The input nodes use ID range [1,9] and
            // the output nodes use [10,18].
            uint inputId = 1, outputId = 10;

            // The game board is represented as a 2-dimensional plane.
            // Each square is at -1, 0, or 1 in the x and y axis.
            // Thus, the game board looks like this:
            //
            // (-1,1)  | (0,1)   | (1,1)
            // (-1,0)  | (0,0)   | (1,0)
            // (-1,-1) | (0,-1)  | (1,-1)
            //
            // Note that the NeatPlayer class orders the board from
            // left to right, then top to bottom. So we need to create
            // nodes with the following IDs:
            //
            //    1    |    2    |   3
            //    4    |    5    |   6
            //    7    |    8    |   9
            //
            for(int x = -1; x <= 1; x++)
                for (int y = 1; y >= -1; y--, inputId++, outputId++)
                {
                    inputLayer.NodeList.Add(new SubstrateNode(inputId, new double[] { x, y }));
                    outputLayer.NodeList.Add(new SubstrateNode(outputId, new double[] { x, y }));
                }

            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);

            // Define a connection mapping from the input layer to the output layer.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1, (double?)null));

            // Construct the substrate using a steepened sigmoid as the phenome's
            // activation function. All weights under 0.2 will not generate
            // connections in the final phenome.
            Substrate substrate = new Substrate(nodeSetList,
                DefaultActivationFunctionLibrary.CreateLibraryCppn(),
                0, 0.2, 5, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with
            // an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder =
                new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, false);

            return genomeDecoder;
        }
        /// <summary>
        /// Create a genome decoder for the experiment.
        /// </summary>
        protected override IGenomeDecoder<NeatGenome, IBlackBox> CreateHyperNeatGenomeDecoder()
        {
            // Create HyperNEAT network substrate.

            uint nodeId = 1;

            //-- Create input layer nodes.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(_dataset.InputCount);
            for (var i = 0; i < _dataset.InputCount; i++)
            {
                inputLayer.NodeList.Add(new SubstrateNode(nodeId++, SetInputNodePosition(i).Extend(-1)));
            }

            //-- Create output layer nodes.
            var outputLayers = new SubstrateNodeSet[nbClusters];
            for (var i = 0; i < nbClusters; i++)
            {
                outputLayers[i] = new SubstrateNodeSet(1);
                outputLayers[i].NodeList.Add(new SubstrateNode(nodeId++, SetOutputNodePosition(i).Extend(+1 + i)));
            }

            //-- Create hidden layer nodes.
            SubstrateNodeSet hiddenLayer = null;
            if (HiddenNodesCount > 0)
            {
                hiddenLayer = new SubstrateNodeSet(HiddenNodesCount);
                for (var i = 0; i < HiddenNodesCount; i++)
                {
                    hiddenLayer.NodeList.Add(new SubstrateNode(nodeId++, SetHiddenNodePosition(i).Extend(0)));
                }
            }

            // Connect up layers.
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2 + nbClusters + (HiddenNodesCount > 0 ? 1 : 0));
            nodeSetList.Add(inputLayer);
            for (var i = 0; i < nbClusters; i++)
            {
                nodeSetList.Add(outputLayers[i]);
            }
            if (HiddenNodesCount > 0)
            {
                nodeSetList.Add(hiddenLayer);
            }

            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(nbClusters * (1 + (HiddenNodesCount > 0 ? 2 : 0)));
            var inputIdx = 0;
            var hiddenIdx = nbClusters + 1;
            for (var i = 0; i < nbClusters; i++)
            {
                var outputIdx = i + 1;
                if (HiddenNodesCount > 0)
                {
                    nodeSetMappingList.Add(NodeSetMapping.Create(inputIdx, hiddenIdx, (double?)null));  // Input -> Hidden.
                    nodeSetMappingList.Add(NodeSetMapping.Create(hiddenIdx, outputIdx, (double?)null));  // Hidden-> Output.
                    nodeSetMappingList.Add(NodeSetMapping.Create(inputIdx, outputIdx, (double?)null));  // Input -> Output.
                }
                else
                {
                    nodeSetMappingList.Add(NodeSetMapping.Create(inputIdx, outputIdx, (double?)null));  // Input -> Output.
                }
            }

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList,
                DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance),
                0, 0.2, 5,
                nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, _lengthCppnInput);
            return genomeDecoder;

        }
        /// <summary>
        /// Create a genome decoder for the experiment.
        /// </summary>
        protected override IGenomeDecoder<NeatGenome, IBlackBox> CreateHyperNeatGenomeDecoder()
        {
            // Create HyperNEAT network substrate.

            uint nodeId = 1;

            //-- Create input layer nodes.
            var inputLayer = new SubstrateNodeSet(nbInputs * f2);
            for (var i = 0; i < nbInputs; i++)
            {
                for (var j = 0; j < f; j++)
                {
                    for (var k = 0; k < f; k++)
                    {
                        if (filter[j, k])
                        {
                            var x = (double)j / f - 0.5;
                            var y = (double)k / f - 0.5;
                            inputLayer.NodeList.Add(new SubstrateNode(nodeId++, new double[] { x, y, -1 - i }));
                        }
                    }
                }
            }

            //-- Create hidden layer nodes.
            var hiddenLayers = new SubstrateNodeSet[nbClusters];
            for (var i = 0; i < nbClusters; i++)
            {
                hiddenLayers[i] = new SubstrateNodeSet(f2);

                for (var j = 0; j < f; j++)
                {
                    for (var k = 0; k < f; k++)
                    {
                        if (filter[j, k])
                        {
                            var x = (double)j / f - 0.5;
                            var y = (double)k / f - 0.5;
                            hiddenLayers[i].NodeList.Add(new SubstrateNode(nodeId++, new double[] { x, y, 0 }));
                        }
                    }
                }
            }

            //-- Create output layer nodes.
            var outputLayers = new SubstrateNodeSet[nbClusters];
            for (var i = 0; i < nbClusters; i++)
            {
                outputLayers[i] = new SubstrateNodeSet(f2);

                for (var j = 0; j < f; j++)
                {
                    for (var k = 0; k < f; k++)
                    {
                        if (filter[j, k])
                        {
                            var x = (double)j / f - 0.5;
                            var y = (double)j / f - 0.5;
                            outputLayers[i].NodeList.Add(new SubstrateNode(nodeId++, new double[] { x, y, +1 + i }));
                        }
                    }
                }
            }

            // Connect up layers.
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(1 + 2 * nbClusters);
            nodeSetList.Add(inputLayer);
            foreach (var layer in hiddenLayers)
            {
                nodeSetList.Add(layer);
            }
            foreach (var layer in outputLayers)
            {
                nodeSetList.Add(layer);
            }


            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(2 * nbClusters);
            for (int i = 0; i < nbClusters; i++)
            {
                var inputLayerIdx = 0;
                var hiddenLayerIdx = 1 + i;
                var outputLayerIdx = 1 + nbClusters + i;
                nodeSetMappingList.Add(NodeSetMapping.Create(inputLayerIdx, hiddenLayerIdx, (double?)null));
                nodeSetMappingList.Add(NodeSetMapping.Create(hiddenLayerIdx, outputLayerIdx, (double?)null));
            }

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList,
                DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance),
                0, 0.2, 5,
                nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, _lengthCppnInput);
            return genomeDecoder;

        }
        public IGenomeDecoder<NeatGenome, IBlackBox> CreateGenomeDecoder(bool lengthCppnInput)
        {
            // Create two layer sandwhich substract substrate. Inputs on bottom of cube and Outputs are on the top.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(Constants.INPUT_SIZE);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(Constants.OUTPUT_SIZE);

            for (uint height = 0; height < Constants.INPUT_HEIGHT; height++)
            {
                for (uint width = 0; width < Constants.INPUT_WIDTH; width++)
                {
                    // start with 1 because of the bias node is 0
                    uint inputID = (height * Constants.INPUT_WIDTH) + width + 1;

                    // Get X and Y positions on the hypercube.
                    double posX = -1.0 + ((width * 1.0D / Constants.INPUT_WIDTH)  * 2);
                    double posY = -1.0 + ((height * 1.0D / Constants.INPUT_HEIGHT) * 2);

                    // Add nodes to inputs
                    inputLayer.NodeList.Add(new SubstrateNode(inputID, new double[] { posX, posY, -1.0 }));
                }
            }
			for (uint height = 0; height < Constants.OUTPUT_HEIGHT; height++)
            {
                for (uint width = 0; width < Constants.OUTPUT_WIDTH; width++)
                {
                    // start with INPUT_SIZE + 1 because id's need to be unique
                    uint outputID = Constants.INPUT_SIZE + (height * Constants.OUTPUT_WIDTH) + width + 1;

                    // Get X and Y positions on the hypercube.
                    double posX = -1.0 + ((width * 1.0D / Constants.OUTPUT_WIDTH)  * 2);
                    double posY = -1.0 + ((height * 1.0D / Constants.OUTPUT_HEIGHT) * 2);

                    // Add nodes to ouputs
                    outputLayer.NodeList.Add(new SubstrateNode(outputID, new double[] { posX, posY, 1.0 }));
                }
            }
			
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);

            // Define connection mappings between layers/sets.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1, (double?)null));

            // Construct substrate.
            Substrate substrate = new Substrate(nodeSetList, GetCppnActivationFunctionLibrary(), 0, Constants.THRESHOLD_WEIGHT, Constants.MAX_WEIGHT, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, lengthCppnInput);
            return genomeDecoder;
        }
        /// <summary>
        /// Creates a genome decoder. We split this code into a separate  method so that it can be re-used by the problem domain visualization code
        /// (it needs to decode genomes to phenomes in order to create a visualization).
        /// </summary>
        /// <param name="visualFieldResolution">The visual field's pixel resolution, e.g. 11 means 11x11 pixels.</param>
        /// <param name="lengthCppnInput">Indicates if the CPPNs being decoded have an extra input for specifying connection length.</param>
        public IGenomeDecoder<NeatGenome,IBlackBox> CreateGenomeDecoder(int visualFieldResolution, bool lengthCppnInput)
        {
            // Create two layer 'sandwich' substrate.
            int pixelCount = visualFieldResolution * visualFieldResolution;
            double pixelSize = BoxesVisualDiscriminationEvaluator.VisualFieldEdgeLength / visualFieldResolution;
            double originPixelXY = -1 + (pixelSize/2.0);

            SubstrateNodeSet inputLayer = new SubstrateNodeSet(pixelCount);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(pixelCount);

            // Node IDs start at 1. (bias node is always zero).
            uint inputId = 1;
            uint outputId = (uint)(pixelCount + 1);
            double yReal = originPixelXY;

            for(int y=0; y<visualFieldResolution; y++, yReal += pixelSize)
            {
                double xReal = originPixelXY;
                for(int x=0; x<visualFieldResolution; x++, xReal += pixelSize, inputId++, outputId++)
                {
                    inputLayer.NodeList.Add(new SubstrateNode(inputId, new double[] {xReal, yReal, -1.0}));
                    outputLayer.NodeList.Add(new SubstrateNode(outputId, new double[] {xReal, yReal, 1.0}));
                }
            }
            /*
            //////////////////////////////////////////////////////////////////////////////
            // Create two layer substrate.
            SubstrateNodeSet inputLayer = new SubstrateNodeSet(Constants.INPUT_SIZE);
            SubstrateNodeSet outputLayer = new SubstrateNodeSet(Constants.FULLY_CONNECTED_SIZE);

            // Inputs to Outputs Mapping
            //              1 1 2 2 3 3
            // 1 2 3        1 1 2 2 3 3
            // 4 5 6  --->  4 4 5 5 6 6
            // 7 8 9        4 4 5 5 6 6
            //              7 7 8 8 9 9
            //              7 7 8 8 9 9

            for (uint width = 0; width < Constants.INPUT_WIDTH; width++)
            {
                for (uint height = 0; height < Constants.INPUT_HEIGHT; height++)
                {
                    // start + 1 because of bias node
                    uint inputID = (width * Constants.INPUT_HEIGHT) + height + 1;
                    inputLayer.NodeList.Add(new SubstrateNode(inputID, new double[] { inputID }));

                }
            }

            // we're fully connected so we will have NEXT_LAYER_SIZE * IMAGE_SIZE weights. This is a lot. yes.
            for (uint height = 0; height < Constants.OUTPUT_SIZE; height++)
            {
                for (uint width = 0; width < Constants.INPUT_SIZE; width++)
                {
                    uint outputID = Constants.INPUT_SIZE + (height * Constants.INPUT_SIZE) + width + 1;

                    outputLayer.NodeList.Add(new SubstrateNode(outputID, new double[] { outputID }));
                }
            }
            ///////////////////////////////////////////////////////////////////////////
            */
            List<SubstrateNodeSet> nodeSetList = new List<SubstrateNodeSet>(2);
            nodeSetList.Add(inputLayer);
            nodeSetList.Add(outputLayer);

            // Define connection mappings between layers/sets.
            List<NodeSetMapping> nodeSetMappingList = new List<NodeSetMapping>(1);
            nodeSetMappingList.Add(NodeSetMapping.Create(0, 1,(double?)null));

            // Construct substrate.
            //Substrate substrate = new Substrate(nodeSetList, DefaultActivationFunctionLibrary.CreateLibraryNeat(SteepenedSigmoid.__DefaultInstance), 0, 0.2, 5, nodeSetMappingList);
            Substrate substrate = new Substrate(nodeSetList, DefaultActivationFunctionLibrary.CreateLibraryCppn(), 0, 0.2, 5, nodeSetMappingList);

            // Create genome decoder. Decodes to a neural network packaged with an activation scheme that defines a fixed number of activations per evaluation.
            IGenomeDecoder<NeatGenome,IBlackBox> genomeDecoder = new HyperNeatDecoder(substrate, _activationSchemeCppn, _activationScheme, lengthCppnInput);
            return genomeDecoder;
        }
Exemple #14
0
        /// <summary>
        /// Returns an IEnumerable that yields the mappings/connections defined by the mapping function (from the source nodes to
        /// the target nodes) as a sequence. The alternative of returning a list would require a very long list in extreme scenarios;
        /// this approach minimizes down memory usage.
        /// </summary>
        public IEnumerable <SubstrateConnection> GenerateConnections(SubstrateNodeSet srcNodeSet, SubstrateNodeSet tgtNodeSet)
        {
            // Test for scenario where srcNodeSet and tgtNodeSet are the same node set, that is, we are creating
            // connections within a nodeset and therefore we need to test (and honour) _allowLocalRecurrentConnections.
            if (srcNodeSet == tgtNodeSet)
            {
                // Mapping between nodes within a single node set.

                // Break the inner loop into two. This avoids having to make the local recurrent test connection
                // for every node pair - we only test when we actually have the same node as source and target.
                IList <SubstrateNode> nodeList = srcNodeSet.NodeList;
                int count = nodeList.Count;
                for (int i = 0; i < count; i++)
                {
                    // Loop over all nodes prior to the i'th.
                    SubstrateNode srcNode = nodeList[i];
                    for (int j = 0; j < i; j++)
                    {
                        if (_testNodePair(srcNode, nodeList[j]))
                        {
                            yield return(new SubstrateConnection(srcNode, nodeList[j]));
                        }
                    }

                    //  Test for local recurrent connection.
                    if (_allowLocalRecurrentConnections && _testNodePair(srcNode, srcNode))
                    {
                        yield return(new SubstrateConnection(srcNode, srcNode));
                    }

                    // Loop over all nodes after the i'th.
                    for (int j = i + 1; j < count; j++)
                    {
                        if (_testNodePair(srcNode, nodeList[j]))
                        {
                            yield return(new SubstrateConnection(srcNode, nodeList[j]));
                        }
                    }
                }
            }
            else
            {
                // Mapping between nodes in two distinct node sets.
                foreach (SubstrateNode srcNode in srcNodeSet.NodeList)
                {
                    foreach (SubstrateNode tgtNode in tgtNodeSet.NodeList)
                    {
                        if (_testNodePair(srcNode, tgtNode))
                        {
                            yield return(new SubstrateConnection(srcNode, tgtNode));
                        }
                    }
                }
            }
        }