HyperNEAT substrate. Encapsulates substrate nodes in sets and connections. Connections can be defined explicitly or by providing mapping functions that map (connect) between nodes in sets. Node sets can be arranged as layers, however there is no limitation on node positions within the substrate - nodes in a set can be distributed throughout the substrate with no restrictions based on e.g. where nodes in other sets are located.
        /// <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;

        }
Example #2
0
        /// <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;
        }
 public void SetupTest()
 {
     _inputFilePath = Path.GetDirectoryName(Path.GetDirectoryName(Directory.GetCurrentDirectory())) + _genomeFileIn;
     _substrate = GenomeHelper.CreateSubstrate(_visualFieldResolution / _reduceAmountPerSide, true);
     _genomeDecoder = (HyperNeatDecoder) GenomeHelper.CreateGenomeDecoder(_substrate, true);
 }
Example #4
0
        /// <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>
        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;
        }
Example #7
0
 /// <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="substrate">The substrate used for the HyperNeatDecoder.</param>
 /// <param name="lengthCppnInput">Indicates if the CPPNs being decoded have an extra input for specifying connection length.</param>
 public static IGenomeDecoder<NeatGenome, IBlackBox> CreateGenomeDecoder(Substrate substrate, bool lengthCppnInput = false)
 {
     // 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.CreateAcyclicScheme(), 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> 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;
        }