public NeatGenomeAcyclicBuilder(MetaNeatGenome <T> metaNeatGenome)
 {
     Debug.Assert(null != metaNeatGenome && metaNeatGenome.IsAcyclic);
     _metaNeatGenome     = metaNeatGenome;
     _graphDepthAnalysis = new AcyclicGraphDepthAnalysis();
     _workingIdSet       = new HashSet <int>();
 }
Beispiel #2
0
 /// <summary>
 /// Construct with the given NEAT genome metadata.
 /// </summary>
 /// <param name="metaNeatGenome">NEAT genome metadata.</param>
 /// <param name="validateAcyclic">Enable acyclic graph validation.</param>
 /// <remarks>
 /// If the caller can guarantee that calls to Create() will provide acyclic graphs only, then
 /// <paramref name="validateAcyclic"/> can be set to false to avoid the cost of the cyclic graph check (which is relatively expensive to perform).
 /// </remarks>
 public NeatGenomeBuilderAcyclic(MetaNeatGenome <T> metaNeatGenome, bool validateAcyclic)
 {
     Debug.Assert(metaNeatGenome is object && metaNeatGenome.IsAcyclic);
     _metaNeatGenome     = metaNeatGenome;
     _graphDepthAnalysis = new AcyclicGraphDepthAnalysis(validateAcyclic);
     _workingIdSet       = new HashSet <int>();
 }
Beispiel #3
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]);
        }
Beispiel #4
0
    /// <summary>
    /// Create from the provided <see cref="WeightedDirectedGraph{T}"/>.
    /// </summary>
    /// <remarks>
    /// The provided graph is expected to describe an acyclic graph; this method asserts that is the case and builds
    /// a formal acyclic graph representation.
    /// </remarks>
    /// <param name="digraph">The directed graph.</param>
    /// <returns>A new instance of <see cref="WeightedDirectedGraphAcyclic{T}"/>.</returns>
    public static WeightedDirectedGraphAcyclic <T> Create(
        WeightedDirectedGraph <T> digraph)
    {
        // Calc the depth of each node in the digraph.
        // ENHANCEMENT: Use a re-usable instance of AcyclicGraphDepthAnalysis.
        GraphDepthInfo depthInfo = new AcyclicGraphDepthAnalysis().CalculateNodeDepths(digraph);

        return(CreateInner(digraph, depthInfo));
    }
    public void Random1()
    {
        // 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(2, 10),
            new DirectedConnection(3, 1),
            new DirectedConnection(3, 4),
            new DirectedConnection(3, 6),
            new DirectedConnection(3, 7),
            new DirectedConnection(4, 1),
            new DirectedConnection(4, 6),
            new DirectedConnection(4, 7),
            new DirectedConnection(4, 10),
            new DirectedConnection(5, 4),
            new DirectedConnection(5, 8),
            new DirectedConnection(6, 9),
            new DirectedConnection(7, 9),
            new DirectedConnection(7, 10),
            new DirectedConnection(8, 1),
            new DirectedConnection(8, 3),
            new DirectedConnection(8, 9),
            new DirectedConnection(9, 1),
            new DirectedConnection(10, 1),
        };

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

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

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

        // Assertions.
        Assert.Equal(9, depthInfo._graphDepth);
        Assert.Equal(11, depthInfo._nodeDepthArr.Length);

        // Node depths.
        Assert.Equal(0, depthInfo._nodeDepthArr[0]);
        Assert.Equal(8, depthInfo._nodeDepthArr[1]);
        Assert.Equal(1, depthInfo._nodeDepthArr[2]);
        Assert.Equal(4, depthInfo._nodeDepthArr[3]);
        Assert.Equal(5, depthInfo._nodeDepthArr[4]);
        Assert.Equal(2, depthInfo._nodeDepthArr[5]);
        Assert.Equal(6, depthInfo._nodeDepthArr[6]);
        Assert.Equal(6, depthInfo._nodeDepthArr[7]);
        Assert.Equal(3, depthInfo._nodeDepthArr[8]);
        Assert.Equal(7, depthInfo._nodeDepthArr[9]);
        Assert.Equal(7, depthInfo._nodeDepthArr[10]);
    }
        /// <summary>
        /// Create a NeatGenome with the given meta data, connection genes and supplementary data.
        /// </summary>
        /// <param name="id">Genome ID.</param>
        /// <param name="birthGeneration">Birth generation.</param>
        /// <param name="connGenes">Connection genes.</param>
        /// <param name="hiddenNodeIdArr">An array of the hidden node IDs in the genome, in ascending order.</param>
        /// <returns>A new NeatGenome instance.</returns>
        public NeatGenome <T> Create(
            int id, int birthGeneration,
            ConnectionGenes <T> connGenes,
            int[] hiddenNodeIdArr)
        {
            int inputCount       = _metaNeatGenome.InputNodeCount;
            int outputCount      = _metaNeatGenome.OutputNodeCount;
            int inputOutputCount = _metaNeatGenome.InputOutputNodeCount;

            // Create a mapping from node IDs to node indexes.
            Dictionary <int, int> nodeIdxById = BuildNodeIndexById(hiddenNodeIdArr);

            // Create a DictionaryNodeIdMap.
            DictionaryNodeIdMap nodeIndexByIdMap = new DictionaryNodeIdMap(inputCount, nodeIdxById);

            // Create a digraph from the genome.
            DirectedGraph digraph = NeatGenomeBuilderUtils.CreateDirectedGraph(
                _metaNeatGenome, connGenes, nodeIndexByIdMap);

            // Calc the depth of each node in the digraph.
            GraphDepthInfo depthInfo = AcyclicGraphDepthAnalysis.CalculateNodeDepths(digraph);

            // Create a weighted acyclic digraph.
            // Note. This also outputs connectionIndexMap. For each connection in the acyclic graph this gives
            // the index of the same connection in the genome; this is because connections are re-ordered based
            // on node depth in the acyclic graph.
            AcyclicDirectedGraph acyclicDigraph = AcyclicDirectedGraphBuilderUtils.CreateAcyclicDirectedGraph(
                digraph,
                depthInfo,
                out int[] newIdByOldId,
                out int[] connectionIndexMap,
                ref _timesortWorkArr,
                ref _timesortWorkVArr);

            // TODO: Write unit tests to cover this!
            // Update nodeIdxById with the new depth based node index allocations.
            // Notes.
            // The current nodeIndexByIdMap maps node IDs (also know as innovation IDs in NEAT) to a compact
            // ID space in which any gaps have been removed, i.e. a compacted set of IDs that can be used as indexes,
            // i.e. if there are N nodes in total then the highest node ID will be N-1.
            //
            // Here we map the new compact IDs to an alternative ID space that is also compact, but ensures that nodeIDs
            // reflect the depth of a node in the acyclic graph.
            UpdateNodeIndexById(nodeIdxById, hiddenNodeIdArr, newIdByOldId);

            // Create the neat genome.
            return(new NeatGenome <T>(
                       _metaNeatGenome, id, birthGeneration,
                       connGenes,
                       hiddenNodeIdArr,
                       nodeIndexByIdMap,
                       acyclicDigraph,
                       connectionIndexMap));
        }
    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 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++;
                }
            }
        }