Пример #1
0
        public void Simple_DefinedNodes()
        {
            var metaNeatGenome = new MetaNeatGenome <double>(0, 10, false, new ReLU());
            var genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

            // Simple acyclic graph.
            var connGenes = new ConnectionGenes <double>(4);

            connGenes[0] = (10, 13, 0.0);
            connGenes[1] = (11, 13, 1.0);
            connGenes[2] = (12, 13, 2.0);
            connGenes[3] = (12, 14, 3.0);

            // Wrap in a genome.
            var genome = genomeBuilder.Create(0, 0, connGenes);

            // Note. The genome builder creates a digraph representation of the genome and attaches/caches it on the genome object.
            var digraph = genome.DirectedGraph;

            // The graph should be unchanged from the input connections.
            CompareConnectionLists(connGenes, digraph.ConnectionIdArrays);

            // Check the node count.
            Assert.AreEqual(15, digraph.TotalNodeCount);
        }
Пример #2
0
    public void SaveAndLoadGenome()
    {
        var metaNeatGenome = new MetaNeatGenome <double>(3, 2, true, new ReLU());
        var genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

        // Simple acyclic graph.
        var connGenes = new ConnectionGenes <double>(6);

        connGenes[0] = (0, 3, 0.123);
        connGenes[1] = (1, 3, 1.234);
        connGenes[2] = (2, 3, -0.5835);
        connGenes[3] = (2, 4, 5.123456789);
        connGenes[4] = (2, 5, 2.5);
        connGenes[5] = (5, 4, 5.4);

        // Wrap in a genome.
        NeatGenome <double> genome = genomeBuilder.Create(0, 0, connGenes);

        // Create a memory stream to save the genome into.
        using (MemoryStream ms = new(1024))
        {
            // Save the genome.
            NeatGenomeSaver <double> .Save(genome, ms);

            // Load the genome.
            ms.Position = 0;
            NeatGenomeLoader <double> loader       = NeatGenomeLoaderFactory.CreateLoaderDouble(metaNeatGenome);
            NeatGenome <double>       genomeLoaded = loader.Load(ms);

            // Compare the original genome with the loaded genome.
            IOTestUtils.CompareGenomes(genome, genomeLoaded);
        }
    }
Пример #3
0
        public void Simple_DefinedNodes_NodeIdGap()
        {
            var metaNeatGenome = new MetaNeatGenome <double>(0, 10, false, new ReLU());
            var genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

            // Simple acyclic graph.
            var connGenes = new ConnectionGenes <double>(4);

            connGenes[0] = (100, 103, 0.0);
            connGenes[1] = (101, 103, 1.0);
            connGenes[2] = (102, 103, 2.0);
            connGenes[3] = (102, 104, 3.0);

            // Wrap in a genome.
            var genome = genomeBuilder.Create(0, 0, connGenes);

            // Note. The genome builder creates a digraph representation of the genome and attaches/caches it on the genome object.
            var digraph = genome.DirectedGraph;

            // The gaps in the node IDs should be removed such that node IDs form a contiguous span starting from zero.
            var connArrExpected   = new DirectedConnection[4];
            var weightArrExpected = new double[4];

            connArrExpected[0] = new DirectedConnection(10, 13); weightArrExpected[0] = 0.0;
            connArrExpected[1] = new DirectedConnection(11, 13); weightArrExpected[1] = 1.0;
            connArrExpected[2] = new DirectedConnection(12, 13); weightArrExpected[2] = 2.0;
            connArrExpected[3] = new DirectedConnection(12, 14); weightArrExpected[3] = 3.0;

            // The graph should be unchanged from the input connections.
            CompareConnectionLists(connArrExpected, digraph.ConnectionIdArrays);

            // Check the node count.
            Assert.AreEqual(15, digraph.TotalNodeCount);
        }
        public void TestAddCyclicConnection()
        {
            var pop           = CreateNeatPopulation();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new AddCyclicConnectionStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, pop.GenerationSeq,
                RandomDefaults.CreateRandomSource());

            var nodeIdSet = GetNodeIdSet(genome);
            var connSet   = GetDirectedConnectionSet(genome);

            for (int i = 0; i < 1000; i++)
            {
                var childGenome = strategy.CreateChildGenome(genome);

                // The child genome should have one more connection than parent.
                Assert.AreEqual(genome.ConnectionGenes.Length + 1, childGenome.ConnectionGenes.Length);

                // The child genome's new connection should not be a duplicate of any of the existing/parent connections.
                var childConnSet = GetDirectedConnectionSet(childGenome);
                var newConnList  = new List <DirectedConnection>(childConnSet.Except(connSet));
                Assert.AreEqual(1, newConnList.Count);

                // The connection genes should be sorted.
                Assert.IsTrue(SortUtils.IsSortedAscending(childGenome.ConnectionGenes._connArr));

                // The child genome should have the same set of node IDs as the parent.
                var childNodeIdSet = GetNodeIdSet(childGenome);
                Assert.IsTrue(nodeIdSet.SetEquals(childNodeIdSet));
            }
        }
Пример #5
0
    public void Simple()
    {
        var metaNeatGenome = new MetaNeatGenome <double>(3, 2, true, new ReLU());
        var genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

        // Simple acyclic graph.
        var connGenes = new ConnectionGenes <double>(4);

        connGenes[0] = (0, 3, 0.0);
        connGenes[1] = (1, 3, 1.0);
        connGenes[2] = (2, 3, 2.0);
        connGenes[3] = (2, 4, 3.0);

        // Wrap in a genome.
        var genome = genomeBuilder.Create(0, 0, connGenes);

        // Note. The genome builder creates a digraph representation of the genome and attaches/caches it on the genome object.
        var acyclicDigraph = (DirectedGraphAcyclic)genome.DirectedGraph;

        Assert.NotNull(acyclicDigraph);

        // The graph should be unchanged from the input connections.
        CompareConnectionLists(connGenes, acyclicDigraph.ConnectionIds, genome.ConnectionIndexMap);

        // Check the node count.
        Assert.Equal(5, acyclicDigraph.TotalNodeCount);
    }
        public void TestAddNode3()
        {
            var pop           = CreateNeatPopulation();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new AddNodeStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, pop.GenerationSeq,
                pop.AddedNodeBuffer);

            IRandomSource rng = RandomDefaults.CreateRandomSource();

            CircularBuffer <NeatGenome <double> > genomeRing = new CircularBuffer <NeatGenome <double> >(10);

            genomeRing.Enqueue(genome);

            for (int i = 0; i < 5000; i++)
            {
                NeatGenome <double> childGenome = CreateAndTestChildGenome(genome, strategy, rng);

                // Add the new child genome to the ring.
                genomeRing.Enqueue(childGenome);

                // Take the genome at the tail of the ring for the next parent.
                genome = genomeRing[0];
            }
        }
Пример #7
0
        private NeatGenome <double> CreateGenome1(MetaNeatGenome <double> metaNeatGenome)
        {
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

            // Define a genome that matches the one defined in example1.genome.
            var connGenes = new ConnectionGenes <double>(12);

            connGenes[0]  = (0, 5, 0.5);
            connGenes[1]  = (0, 7, 0.7);
            connGenes[2]  = (0, 3, 0.3);
            connGenes[3]  = (1, 5, 1.5);
            connGenes[4]  = (1, 7, 1.7);
            connGenes[5]  = (1, 3, 1.3);
            connGenes[6]  = (1, 6, 1.6);
            connGenes[7]  = (1, 8, 1.8);
            connGenes[8]  = (1, 4, 1.4);
            connGenes[9]  = (2, 6, 2.6);
            connGenes[10] = (2, 8, 2.8);
            connGenes[11] = (2, 4, 2.4);

            // Ensure the connections are sorted correctly.
            connGenes.Sort();

            // Wrap in a genome.
            NeatGenome <double> genome = genomeBuilder.Create(0, 0, connGenes);

            return(genome);
        }
Пример #8
0
    public void DepthNodeReorderTest()
    {
        var metaNeatGenome = new MetaNeatGenome <double>(2, 2, true, new ReLU());
        var genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

        // Define graph connections.
        var connGenes = new ConnectionGenes <double>(5);

        connGenes[0] = (0, 4, 0.0);
        connGenes[1] = (4, 5, 1.0);
        connGenes[2] = (5, 2, 2.0);
        connGenes[3] = (1, 2, 3.0);
        connGenes[4] = (2, 3, 4.0);
        connGenes.Sort();

        // Wrap in a genome.
        var genome = genomeBuilder.Create(0, 0, connGenes);

        // Note. The genome builder creates a digraph representation of the genome and attaches/caches it on the genome object.
        var acyclicDigraph = (DirectedGraphAcyclic)genome.DirectedGraph;

        Assert.NotNull(acyclicDigraph);

        // Simulate the actual weight array that would occur in e.g. a WeightedDirectedGraphAcyclic or NeuralNetAcyclic.
        double[] weightArrActual = new double[connGenes._weightArr.Length];
        for (int i = 0; i < weightArrActual.Length; i++)
        {
            weightArrActual[i] = connGenes._weightArr[genome.ConnectionIndexMap[i]];
        }

        // The nodes should have IDs allocated based on depth, i.e. the layer they are in.
        // And connections should be ordered by source node ID.
        var connArrExpected   = new DirectedConnection[5];
        var weightArrExpected = new double[5];

        connArrExpected[0] = new DirectedConnection(0, 2); weightArrExpected[0] = 0.0;
        connArrExpected[1] = new DirectedConnection(1, 4); weightArrExpected[1] = 3.0;
        connArrExpected[2] = new DirectedConnection(2, 3); weightArrExpected[2] = 1.0;
        connArrExpected[3] = new DirectedConnection(3, 4); weightArrExpected[3] = 2.0;
        connArrExpected[4] = new DirectedConnection(4, 5); weightArrExpected[4] = 4.0;

        // Compare actual and expected connections.
        CompareConnectionLists(connArrExpected, weightArrExpected, acyclicDigraph.ConnectionIds, weightArrActual);

        // Test layer info.
        LayerInfo[] layerArrExpected = new LayerInfo[5];
        layerArrExpected[0] = new LayerInfo(2, 2);
        layerArrExpected[1] = new LayerInfo(3, 3);
        layerArrExpected[2] = new LayerInfo(4, 4);
        layerArrExpected[3] = new LayerInfo(5, 5);
        layerArrExpected[4] = new LayerInfo(6, 5);
        Assert.Equal(5, acyclicDigraph.LayerArray.Length);

        // Check the node count.
        Assert.Equal(6, acyclicDigraph.TotalNodeCount);
    }
        public void TestAddAcyclicConnection()
        {
            var pop           = CreateNeatPopulation();
            var generationSeq = new Int32Sequence();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new AddAcyclicConnectionStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, generationSeq);

            IRandomSource rng = RandomDefaults.CreateRandomSource();

            var nodeIdSet = GetNodeIdSet(genome);
            var connSet   = GetDirectedConnectionSet(genome);

            CyclicGraphAnalysis cyclicGraphAnalysis = new CyclicGraphAnalysis();

            for (int i = 0; i < 1000;)
            {
                var childGenome = strategy.CreateChildGenome(genome, rng);

                // Note. the strategy will return a null if it cannot find an acyclic connection to add;
                // test for this and try again. The test will be for N successful mutations rather than N attempts.
                if (null == childGenome)
                {
                    continue;
                }

                // The child genome should have one more connection than parent.
                Assert.AreEqual(genome.ConnectionGenes.Length + 1, childGenome.ConnectionGenes.Length);

                // The child genome's new connection should not be a duplicate of any of the existing/parent connections.
                var childConnSet = GetDirectedConnectionSet(childGenome);
                var newConnList  = new List <DirectedConnection>(childConnSet.Except(connSet));
                Assert.AreEqual(1, newConnList.Count);

                // The connection genes should be sorted.
                Assert.IsTrue(SortUtils.IsSortedAscending(childGenome.ConnectionGenes._connArr));

                // The child genome should have the same set of node IDs as the parent.
                var childNodeIdSet = GetNodeIdSet(childGenome);
                Assert.IsTrue(nodeIdSet.SetEquals(childNodeIdSet));

                // The child genome should describe an acyclic graph, i.e. the new connection should not have
                // formed a cycle in the graph.
                var digraph = childGenome.DirectedGraph;
                Assert.IsFalse(cyclicGraphAnalysis.IsCyclic(digraph));

                // Increment for successful tests only.
                i++;
            }
        }
    public void AddCyclicConnection()
    {
        var pop           = CreateNeatPopulation();
        var generationSeq = new Int32Sequence();
        var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

        var genome = pop.GenomeList[0];

        var strategy = new AddCyclicConnectionStrategy <double>(
            pop.MetaNeatGenome, genomeBuilder,
            pop.GenomeIdSeq, generationSeq);

        IRandomSource rng = RandomDefaults.CreateRandomSource();

        var nodeIdSet = GetNodeIdSet(genome);
        var connSet   = GetDirectedConnectionSet(genome);

        const int loops             = 1000;
        int       nullResponseCount = 0;

        for (int i = 0; i < loops; i++)
        {
            var childGenome = strategy.CreateChildGenome(genome, rng);

            // The strategy may return null if no appropriately connection could be found to add.
            if (childGenome is null)
            {
                nullResponseCount++;
                continue;
            }

            // The child genome should have one more connection than parent.
            Assert.Equal(genome.ConnectionGenes.Length + 1, childGenome.ConnectionGenes.Length);

            // The child genome's new connection should not be a duplicate of any of the existing/parent connections.
            var childConnSet = GetDirectedConnectionSet(childGenome);
            var newConnList  = new List <DirectedConnection>(childConnSet.Except(connSet));
            Assert.Single(newConnList);

            // The connection genes should be sorted.
            Assert.True(SortUtils.IsSortedAscending <DirectedConnection>(childGenome.ConnectionGenes._connArr));

            // The child genome should have the same set of node IDs as the parent.
            var childNodeIdSet = GetNodeIdSet(childGenome);
            Assert.True(nodeIdSet.SetEquals(childNodeIdSet));
        }

        // nullResponseProportion will typically be 0, with 1.0% being so unlikely
        // it probably will never be observed.
        double nullResponseProportion = nullResponseCount / (double)loops;

        Assert.True(nullResponseProportion <= 0.01);
    }
Пример #11
0
        protected NeatGenomeLoader(
            MetaNeatGenome <T> metaNeatGenome,
            int connCountEstimate)
        {
            _metaNeatGenome = metaNeatGenome ?? throw new ArgumentNullException(nameof(metaNeatGenome));
            _genomeBuilder  = NeatGenomeBuilderFactory <T> .Create(metaNeatGenome);

            _activationFnName = metaNeatGenome.ActivationFn.GetType().Name;
            _connList         = new List <DirectedConnection>(connCountEstimate);
            _weightList       = new List <T>(connCountEstimate);
            _actFnList        = new List <ActivationFunctionRow>();
        }
Пример #12
0
        public void CreateGenome()
        {
            var metaNeatGenome = new MetaNeatGenome <double>(
                inputNodeCount: 10,
                outputNodeCount: 20,
                isAcyclic: true,
                activationFn: new NeuralNets.Double.ActivationFunctions.ReLU());

            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

            int count = 100;
            NeatPopulation <double> pop = NeatPopulationFactory <double> .CreatePopulation(metaNeatGenome, 0.1, count, RandomDefaults.CreateRandomSource());

            var generationSeq = new Int32Sequence();

            var strategy = new UniformCrossoverReproductionStrategy <double>(
                pop.MetaNeatGenome.IsAcyclic,
                0.02,
                genomeBuilder,
                pop.GenomeIdSeq, generationSeq);

            IRandomSource rng = RandomDefaults.CreateRandomSource(0);

            var cyclicGraphCheck = new CyclicGraphCheck();

            for (int i = 0; i < 1000; i++)
            {
                // Randomly select two parents from the population.
                var genome1 = pop.GenomeList[rng.Next(count)];
                var genome2 = pop.GenomeList[rng.Next(count)];

                var childGenome = strategy.CreateGenome(genome1, genome2, rng);

                // The connection genes should be sorted.
                Assert.True(SortUtils.IsSortedAscending <DirectedConnection>(childGenome.ConnectionGenes._connArr));

                // The child genome should describe an acyclic graph, i.e. the new connection should not have
                // formed a cycle in the graph.
                var digraph = childGenome.DirectedGraph;
                Assert.False(cyclicGraphCheck.IsCyclic(digraph));

                // The child genome node IDs should be a superset of those from parent1 + parent2.
                var childNodeIdSet = GetNodeIdSet(childGenome);
                var parentIdSet    = GetNodeIdSet(genome1);
                parentIdSet.IntersectWith(GetNodeIdSet(genome2));

                Assert.True(childNodeIdSet.IsSupersetOf(parentIdSet));
            }
        }
Пример #13
0
        public static NeatPopulation <double> CreateNeatPopulation()
        {
            var metaNeatGenome = new MetaNeatGenome <double>(
                inputNodeCount: 1,
                outputNodeCount: 1,
                isAcyclic: false,
                activationFn: new NeuralNet.Double.ActivationFunctions.ReLU());

            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(metaNeatGenome);

            var genome     = CreateNeatGenome(metaNeatGenome, genomeBuilder);
            var genomeList = new List <NeatGenome <double> >()
            {
                genome
            };

            return(new NeatPopulation <double>(metaNeatGenome, genomeList));
        }
Пример #14
0
        public void TestAddNode1()
        {
            var pop           = CreateNeatPopulation();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new AddNodeStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, pop.GenerationSeq,
                pop.AddedNodeBuffer,
                RandomDefaults.CreateRandomSource());

            for (int i = 0; i < 10000; i++)
            {
                NeatGenome <double> childGenome = CreateAndTestChildGenome(genome, strategy);
            }
        }
Пример #15
0
        public void TestAddNode2()
        {
            var pop           = CreateNeatPopulation();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new AddNodeStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, pop.GenerationSeq,
                pop.AddedNodeBuffer,
                RandomDefaults.CreateRandomSource());

            for (int i = 0; i < 2000; i++)
            {
                NeatGenome <double> childGenome = CreateAndTestChildGenome(genome, strategy);

                // Make the child genome the parent in the next iteration. I.e. accumulate add node mutations.
                genome = childGenome;
            }
        }
        private NeatPopulationFactory(
            MetaNeatGenome <T> metaNeatGenome,
            double connectionsProportion,
            IRandomSource rng)
        {
            _metaNeatGenome = metaNeatGenome;
            _genomeBuilder  = NeatGenomeBuilderFactory <T> .Create(metaNeatGenome);

            _connectionsProportion = connectionsProportion;

            // Define the set of all possible connections between the input and output nodes (fully interconnected).
            int inputCount  = metaNeatGenome.InputNodeCount;
            int outputCount = metaNeatGenome.OutputNodeCount;

            _connectionDefArr = new DirectedConnection[inputCount * outputCount];

            // Notes.
            // Nodes are assigned innovation IDs. By convention the input nodes are assigned IDs first starting at zero, then the output nodes.
            // Thus, because all of the evolved networks have a fixed number of inputs and outputs, the IDs of these nodes are always fixed.
            int firstOutputNodeId = inputCount;

            for (int srcId = 0, i = 0; srcId < inputCount; srcId++)
            {
                for (int tgtIdx = 0; tgtIdx < outputCount; tgtIdx++)
                {
                    _connectionDefArr[i++] = new DirectedConnection(srcId, firstOutputNodeId + tgtIdx);
                }
            }

            // Init RNG and ID sequences.
            _rng         = rng;
            _genomeIdSeq = new Int32Sequence();
            int nextInnovationId = inputCount + outputCount;

            _innovationIdSeq = new Int32Sequence(nextInnovationId);

            // Init random connection weight source.
            _connWeightDist = ContinuousDistributionFactory.CreateUniformDistribution <T>(_metaNeatGenome.ConnectionWeightRange, true);
        }
        public void TestDeleteConnection()
        {
            var pop           = CreateNeatPopulation();
            var generationSeq = new Int32Sequence();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var genome = pop.GenomeList[0];

            var strategy = new DeleteConnectionStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, generationSeq);

            IRandomSource rng = RandomDefaults.CreateRandomSource();

            var nodeIdSet = GetNodeIdSet(genome);
            var connSet   = GetDirectedConnectionSet(genome);

            for (int i = 0; i < 1000; i++)
            {
                var childGenome = strategy.CreateChildGenome(genome, rng);

                // The child genome should have one less connection than the parent.
                Assert.AreEqual(genome.ConnectionGenes.Length - 1, childGenome.ConnectionGenes.Length);

                // The child genome's connections should be a proper subset of the parent genome's.
                var childConnSet = GetDirectedConnectionSet(childGenome);
                Assert.IsTrue(childConnSet.IsProperSubsetOf(connSet));

                // The connection genes should be sorted.
                Assert.IsTrue(SortUtils.IsSortedAscending(childGenome.ConnectionGenes._connArr));

                // Test that the array of hidden node IDs is correct, i.e. corresponds with the hidden node IDs described by the connections.
                Assert.IsTrue(ConnectionGenesUtils.ValidateHiddenNodeIds(
                                  childGenome.HiddenNodeIdArray,
                                  childGenome.ConnectionGenes._connArr,
                                  childGenome.MetaNeatGenome.InputOutputNodeCount));
            }
        }
        public void TestAddAcyclicConnection_CumulativeAdditions()
        {
            var pop           = CreateNeatPopulation();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var rootGenome = pop.GenomeList[0];

            var strategy = new AddAcyclicConnectionStrategy <double>(
                pop.MetaNeatGenome, genomeBuilder,
                pop.GenomeIdSeq, pop.InnovationIdSeq, pop.GenerationSeq);

            IRandomSource rng = RandomDefaults.CreateRandomSource();

            var nodeIdSet = GetNodeIdSet(rootGenome);

            CyclicGraphAnalysis cyclicGraphAnalysis = new CyclicGraphAnalysis();

            AcyclicGraphDepthAnalysis graphDepthAnalysis = new AcyclicGraphDepthAnalysis();

            // Run the inner loop test multiple times.
            // Note. The add-connection mutations are random, thus each loop accumulates a different set of mutations.
            for (int i = 0; i < 50; i++)
            {
                var parentGenome = rootGenome;

                // Accumulate random mutations for some number of loops.
                for (int j = 0; j < 20;)
                {
                    var childGenome = strategy.CreateChildGenome(rootGenome, rng);

                    // Note. the strategy will return a null if it cannot find an acyclic connection to add;
                    // test for this and try again. The test will be for N successful mutations rather than N attempts.
                    if (null == childGenome)
                    {
                        continue;
                    }

                    // The child genome should have one more connection than parent.
                    Assert.AreEqual(rootGenome.ConnectionGenes.Length + 1, childGenome.ConnectionGenes.Length);

                    // The child genome's new connection should not be a duplicate of any of the existing/parent connections.
                    var connSet      = GetDirectedConnectionSet(rootGenome);
                    var childConnSet = GetDirectedConnectionSet(childGenome);
                    var newConnList  = new List <DirectedConnection>(childConnSet.Except(connSet));
                    Assert.AreEqual(1, newConnList.Count);

                    // The connection genes should be sorted.
                    Assert.IsTrue(SortUtils.IsSortedAscending(childGenome.ConnectionGenes._connArr));

                    // The child genome should have the same set of node IDs as the parent.
                    var childNodeIdSet = GetNodeIdSet(childGenome);
                    Assert.IsTrue(nodeIdSet.SetEquals(childNodeIdSet));

                    // The child genome should describe an acyclic graph, i.e. the new connection should not have
                    // formed a cycle in the graph.
                    var digraph = childGenome.DirectedGraph;
                    Assert.IsFalse(cyclicGraphAnalysis.IsCyclic(digraph));

                    // Run the acyclic graph depth analysis algorithm.
                    GraphDepthInfo depthInfo = graphDepthAnalysis.CalculateNodeDepths(childGenome.DirectedGraph);

                    // Run again with the alternative algorithm (that uses function recursion).
                    GraphDepthInfo depthInfo2 = AcyclicGraphDepthAnalysisByRecursion.CalculateNodeDepths(childGenome.DirectedGraph);

                    Assert.AreEqual(nodeIdSet.Count, depthInfo._nodeDepthArr.Length);
                    Assert.AreEqual(nodeIdSet.Count, depthInfo2._nodeDepthArr.Length);
                    TestUtils.Compare(depthInfo2._nodeDepthArr, depthInfo._nodeDepthArr);

                    // Set the child genome to be the new parent, thus we accumulate random new connections over time.
                    parentGenome = childGenome;

                    // Increment for successful tests only.
                    j++;
                }
            }
        }
Пример #19
0
 static UniformCrossoverReproductionStrategyTestsUtils()
 {
     __metaNeatGenome = CreateMetaNeatGenome();
     __genomeBuilder  = NeatGenomeBuilderFactory <double> .Create(__metaNeatGenome);
 }