public void Regression1()
        {
            // Simple acyclic graph.
            var connList = new List <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.
            connList.Sort();
            var digraph = DirectedGraphBuilder.Create(connList, 0, 0);

            // Test if cyclic.
            var  cyclicGraphAnalysis = new CyclicGraphAnalysis();
            bool isCyclic            = cyclicGraphAnalysis.IsCyclic(digraph);

            Assert.IsFalse(isCyclic);
        }
Example #2
0
        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 cyclicGraphAnalysis = new CyclicGraphAnalysis();

            Assert.IsTrue(!cyclicGraphAnalysis.IsCyclic(digraph));

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

            // Assertions.
            Assert.AreEqual(4, depthInfo._networkDepth);
            Assert.AreEqual(5, depthInfo._nodeDepthArr.Length);

            // Node depths.
            Assert.AreEqual(0, depthInfo._nodeDepthArr[0]);
            Assert.AreEqual(2, depthInfo._nodeDepthArr[1]);
            Assert.AreEqual(0, depthInfo._nodeDepthArr[2]);
            Assert.AreEqual(3, depthInfo._nodeDepthArr[3]);
            Assert.AreEqual(1, depthInfo._nodeDepthArr[4]);
        }
Example #3
0
        /// <summary>
        /// Calculate node depths in an acyclic network.
        /// </summary>
        public GraphDepthInfo CalculateNodeDepths(DirectedGraph digraph)
        {
            #if DEBUG
            // Check for attempts to re-enter this method.
            if (1 == Interlocked.CompareExchange(ref _reentranceFlag, 1, 0))
            {
                throw new InvalidOperationException("Attempt to re-enter non reentrant method.");
            }

            // Debug assert the graph is acyclic.
            // Note. In a release build this test is not performed because we expect this method to be called from
            // code handling acyclic graphs only. If digraph is cyclic then the graph traversal implemented here will
            // cause _traversalStack to grow indefinitely, ultimately causing an out-of-memory exception.
            Debug.Assert(!_cyclicGraphAnalysis.IsCyclic(digraph));
            #endif

            _nodeDepthByIdx = new int[digraph.TotalNodeCount];

            try
            {
                CalculateNodeDepthsInner(digraph);

                // Determine the maximum depth of the graph.
                int maxDepth = (0 == _nodeDepthByIdx.Length) ? 0 : _nodeDepthByIdx.Max();

                // Return depth analysis info.
                return(new GraphDepthInfo(maxDepth + 1, _nodeDepthByIdx));
            }
            finally
            {
                Cleanup();
            }
        }
        /// <summary>
        /// Calculate node depths in an acyclic network.
        /// </summary>
        private GraphDepthInfo CalculateNodeDepthsInner()
        {
            #if DEBUG
            // Debug assert the graph is acyclic.
            // Note. In a release build this test is not performed because we expect this method to be called from
            // code handling acyclic graphs only. If digraph is cyclic then the graph traversal implemented here will
            // cause a stack overflow, so at the very least there isn't a silent error.
            Debug.Assert(!_cyclicGraphAnalysis.IsCyclic(_digraph));
            #endif

            // Loop over all connections exiting from input nodes, and perform a depth first traversal of each in turn.
            int   inputCount = _digraph.InputCount;
            int[] srcIdxArr  = _digraph.ConnectionIdArrays._sourceIdArr;
            int[] tgtIdxArr  = _digraph.ConnectionIdArrays._targetIdArr;

            for (int connIdx = 0; connIdx < srcIdxArr.Length && srcIdxArr[connIdx] < inputCount; connIdx++)
            {
                // Traverse into the target node.
                TraverseNode(tgtIdxArr[connIdx], 1);
            }

            // Determine the maximum depth of the graph.
            int maxDepth = (0 == _nodeDepthByIdx.Length) ? 0 : _nodeDepthByIdx.Max();

            // Return depth analysis info.
            return(new GraphDepthInfo(maxDepth + 1, _nodeDepthByIdx));
        }
        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++;
            }
        }
Example #6
0
        public void TestCreateGenome()
        {
            var metaNeatGenome = new MetaNeatGenome <double>(
                inputNodeCount: 10,
                outputNodeCount: 20,
                isAcyclic: true,
                activationFn: new SharpNeat.NeuralNet.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 cyclicGraphAnalysis = new CyclicGraphAnalysis();

            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.IsTrue(SortUtils.IsSortedAscending(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.IsFalse(cyclicGraphAnalysis.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.IsTrue(childNodeIdSet.IsSupersetOf(parentIdSet));
            }
        }
        public void SimpleAcyclic_DefinedNodes_NodeIdGap()
        {
            // Simple acyclic graph.
            var connList = new List <DirectedConnection>
            {
                new DirectedConnection(100, 103),
                new DirectedConnection(101, 103),
                new DirectedConnection(102, 103),
                new DirectedConnection(102, 104),
                new DirectedConnection(104, 101)
            };

            // Create graph.
            connList.Sort();
            var digraph = DirectedGraphBuilder.Create(connList, 0, 10);

            // Test if cyclic.
            var  cyclicGraphAnalysis = new CyclicGraphAnalysis();
            bool isCyclic            = cyclicGraphAnalysis.IsCyclic(digraph);

            Assert.IsFalse(isCyclic);
        }
        public void SimpleAcyclic()
        {
            // 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, 0, 0);

            // Test if cyclic.
            var  cyclicGraphAnalysis = new CyclicGraphAnalysis();
            bool isCyclic            = cyclicGraphAnalysis.IsCyclic(digraph);

            Assert.IsFalse(isCyclic);
        }
        public void SimpleCyclic_DefinedNodes()
        {
            // Simple acyclic graph.
            var connList = new List <DirectedConnection>
            {
                new DirectedConnection(10, 13),
                new DirectedConnection(11, 13),
                new DirectedConnection(12, 13),
                new DirectedConnection(12, 14),
                new DirectedConnection(14, 11),
                new DirectedConnection(11, 12)
            };

            // Create graph.
            connList.Sort();
            var digraph = DirectedGraphBuilder.Create(connList, 0, 10);

            // Test if cyclic.
            var  cyclicGraphAnalysis = new CyclicGraphAnalysis();
            bool isCyclic            = cyclicGraphAnalysis.IsCyclic(digraph);

            Assert.IsTrue(isCyclic);
        }
        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++;
                }
            }
        }