public void Regression1()
        {
            // Simple acyclic graph.
            var connList = new LightweightList <DirectedConnection>
            {
                new DirectedConnection(0, 2),
                new DirectedConnection(0, 3),
                new DirectedConnection(0, 4),
                new DirectedConnection(2, 5),
                new DirectedConnection(3, 6),
                new DirectedConnection(4, 7),
                new DirectedConnection(5, 8),
                new DirectedConnection(6, 4),
                new DirectedConnection(6, 9),
                new DirectedConnection(7, 10),
                new DirectedConnection(8, 1),
                new DirectedConnection(9, 1),
                new DirectedConnection(10, 1)
            };

            // Create graph.
            var connSpan = connList.AsSpan();

            connSpan.Sort();
            var digraph = DirectedGraphBuilder.Create(connSpan, 0, 0);

            // Test if cyclic.
            var  cyclicGraphCheck = new CyclicGraphCheck();
            bool isCyclic         = cyclicGraphCheck.IsCyclic(digraph);

            Assert.False(isCyclic);
        }
        public void SimpleCyclic_DefinedNodes_NodeIdGap()
        {
            // Simple acyclic graph.
            var connList = new LightweightList <DirectedConnection>
            {
                new DirectedConnection(100, 103),
                new DirectedConnection(101, 103),
                new DirectedConnection(102, 103),
                new DirectedConnection(102, 104),
                new DirectedConnection(104, 101),
                new DirectedConnection(101, 102)
            };

            // Create graph.
            var connSpan = connList.AsSpan();

            connSpan.Sort();
            var digraph = DirectedGraphBuilder.Create(connSpan, 0, 10);

            // Test if cyclic.
            var  cyclicGraphCheck = new CyclicGraphCheck();
            bool isCyclic         = cyclicGraphCheck.IsCyclic(digraph);

            Assert.True(isCyclic);
        }
        public void SimpleCyclic()
        {
            // Simple acyclic graph.
            var connList = new LightweightList <DirectedConnection>
            {
                new DirectedConnection(0, 3),
                new DirectedConnection(1, 3),
                new DirectedConnection(2, 3),
                new DirectedConnection(2, 4),
                new DirectedConnection(4, 1),
                new DirectedConnection(1, 2)
            };

            // Create graph.
            var connSpan = connList.AsSpan();

            connSpan.Sort();
            var digraph = DirectedGraphBuilder.Create(connSpan, 0, 0);

            // Test if cyclic.
            var  cyclicGraphCheck = new CyclicGraphCheck();
            bool isCyclic         = cyclicGraphCheck.IsCyclic(digraph);

            Assert.True(isCyclic);
        }
        public void ConnectThroughInput()
        {
            // Simple acyclic graph.
            var connList = new List <DirectedConnection>
            {
                new DirectedConnection(0, 3),
                new DirectedConnection(1, 3),
                new DirectedConnection(2, 3),
                new DirectedConnection(2, 4),
                new DirectedConnection(4, 1)
            };

            // Create graph.
            connList.Sort();
            var digraph = DirectedGraphBuilder.Create(connList, 3, 2);

            // Assert is acyclic.
            var cyclicGraphCheck = new CyclicGraphCheck();

            Assert.False(cyclicGraphCheck.IsCyclic(digraph));

            // Depth analysis.
            GraphDepthInfo depthInfo = new AcyclicGraphDepthAnalysis().CalculateNodeDepths(digraph);

            // Assertions.
            Assert.Equal(4, depthInfo._graphDepth);
            Assert.Equal(5, depthInfo._nodeDepthArr.Length);

            // Node depths.
            Assert.Equal(0, depthInfo._nodeDepthArr[0]);
            Assert.Equal(2, depthInfo._nodeDepthArr[1]);
            Assert.Equal(0, depthInfo._nodeDepthArr[2]);
            Assert.Equal(3, depthInfo._nodeDepthArr[3]);
            Assert.Equal(1, depthInfo._nodeDepthArr[4]);
        }
        public void AddAcyclicConnection()
        {
            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, generationSeq);

            IRandomSource rng = RandomDefaults.CreateRandomSource();

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

            CyclicGraphCheck cyclicGraphCheck = new CyclicGraphCheck();

            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 (childGenome is null)
                {
                    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(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));

                // 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));

                // Increment for successful tests only.
                i++;
            }
        }
Exemple #6
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));
            }
        }
    public void ShortAndLongPath()
    {
        // Simple acyclic graph.
        var connList = new LightweightList <DirectedConnection>
        {
            new DirectedConnection(0, 4),
            new DirectedConnection(4, 5),
            new DirectedConnection(5, 2),
            new DirectedConnection(1, 2),
            new DirectedConnection(2, 3)
        };

        // Create graph.
        var connSpan = connList.AsSpan();

        connSpan.Sort();
        var digraph = DirectedGraphBuilder.Create(connSpan, 2, 2);

        // Assert is acyclic.
        var cyclicGraphCheck = new CyclicGraphCheck();

        Assert.False(cyclicGraphCheck.IsCyclic(digraph));

        // Depth analysis.
        GraphDepthInfo depthInfo = new AcyclicGraphDepthAnalysis().CalculateNodeDepths(digraph);

        // Assertions.
        Assert.Equal(5, depthInfo._graphDepth);
        Assert.Equal(6, depthInfo._nodeDepthArr.Length);

        // Node depths.
        Assert.Equal(0, depthInfo._nodeDepthArr[0]);
        Assert.Equal(0, depthInfo._nodeDepthArr[1]);
        Assert.Equal(3, depthInfo._nodeDepthArr[2]);
        Assert.Equal(4, depthInfo._nodeDepthArr[3]);
        Assert.Equal(1, depthInfo._nodeDepthArr[4]);
        Assert.Equal(2, depthInfo._nodeDepthArr[5]);
    }
        public void AddAcyclicConnection_CumulativeAdditions()
        {
            var pop           = CreateNeatPopulation();
            var generationSeq = new Int32Sequence();
            var genomeBuilder = NeatGenomeBuilderFactory <double> .Create(pop.MetaNeatGenome);

            var rootGenome = pop.GenomeList[0];

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

            IRandomSource rng = RandomDefaults.CreateRandomSource();

            var nodeIdSet = GetNodeIdSet(rootGenome);

            CyclicGraphCheck cyclicGraphCheck = new CyclicGraphCheck();

            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(parentGenome, 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 (childGenome is null)
                    {
                        continue;
                    }

                    // The child genome should have one more connection than parent.
                    Assert.Equal(parentGenome.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(parentGenome);
                    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(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));

                    // 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));

                    // 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.Equal(nodeIdSet.Count, depthInfo._nodeDepthArr.Length);
                    Assert.Equal(nodeIdSet.Count, depthInfo2._nodeDepthArr.Length);
                    Assert.Equal(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++;
                }
            }
        }