public void GetConnectionIndexBySourceNodeId()
        {
            var connArr = new DirectedConnection[9];

            connArr[0] = new DirectedConnection(1, 70);
            connArr[1] = new DirectedConnection(4, 14);
            connArr[2] = new DirectedConnection(6, 23);
            connArr[3] = new DirectedConnection(7, 36);
            connArr[4] = new DirectedConnection(10, 18);
            connArr[5] = new DirectedConnection(10, 31);
            connArr[6] = new DirectedConnection(14, 2);
            connArr[7] = new DirectedConnection(20, 24);
            connArr[8] = new DirectedConnection(25, 63);

            int idx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, 10);

            Assert.Equal(4, idx);

            idx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, 25);
            Assert.Equal(8, idx);

            idx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, 26);
            Assert.Equal(~9, idx);

            idx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, 1);
            Assert.Equal(0, idx);

            idx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, 0);
            Assert.Equal(~0, idx);
        }
Ejemplo n.º 2
0
        private bool IsConnectionCyclicInner(IList <DirectedConnection> connList, DirectedConnection newConn)
        {
            // Test if the new connection is pointing to itself.
            if (newConn.SourceId == newConn.TargetId)
            {
                return(true);
            }

            // Note. We traverse forwards starting at the new connection's target node. If the new connection's source node is
            // encountered during traversal then the connection would form a cycle in the graph as a whole, and we return true.
            int startNodeId = newConn.TargetId;

            // Search for outgoing connections from the starting node.
            int connIdx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connList, startNodeId);

            if (connIdx < 0)
            {   // The current node has no outgoing connections, therefore newConn does not form a cycle.
                return(false);
            }

            // Initialise and run the graph traversal algorithm.
            InitGraphTraversal(startNodeId, connIdx);

            // Note. we pass newConn.SourceId as the terminalNodeId; if traversal reaches this node then a cycle has been detected.
            return(TraverseGraph(connList, newConn.SourceId));
        }
Ejemplo n.º 3
0
        private bool TryGetConnectionInner(NeatGenome <T> parent, out DirectedConnection conn, out int insertIdx)
        {
            int inputCount  = _metaNeatGenome.InputNodeCount;
            int outputCount = _metaNeatGenome.OutputNodeCount;
            int hiddenCount = parent.HiddenNodeIdArray.Length;

            // Select a source node at random.

            // Note. Valid source nodes are input and hidden nodes. Output nodes are not source node candidates
            // for acyclic nets, because that can prevent future connections from targeting the output if it would
            // create a cycle.
            int inputHiddenCount = inputCount + hiddenCount;
            int srcIdx           = _rng.Next(inputHiddenCount);

            if (srcIdx >= inputCount)
            {
                srcIdx += outputCount;
            }
            int srcId = GetNodeIdFromIndex(parent, srcIdx);

            // Select a target node at random.
            // Note. Valid target nodes are all hidden and output nodes (cannot be an input node).
            int outputHiddenCount = outputCount + hiddenCount;
            int tgtId             = GetNodeIdFromIndex(parent, inputCount + _rng.Next(outputHiddenCount));;

            // Test for simplest cyclic connectivity - node connects to itself.
            if (srcId == tgtId)
            {
                conn      = default(DirectedConnection);
                insertIdx = default(int);
                return(false);
            }

            // Test if the chosen connection already exists.
            // Note. Connection genes are always sorted by sourceId then targetId, so we can use a binary search to
            // find an existing connection in O(log(n)) time.
            conn = new DirectedConnection(srcId, tgtId);

            if ((insertIdx = Array.BinarySearch(parent.ConnectionGenes._connArr, conn)) >= 0)
            {
                // The proposed new connection already exists.
                conn      = default(DirectedConnection);
                insertIdx = default(int);
                return(false);
            }

            // Test if the connection would form a cycle if added to the parent genome.
            if (_cyclicTest.IsConnectionCyclic(parent.DirectedGraph, DirectedConnectionUtils.CloneAndMap(conn, parent.NodeIndexByIdMap)))
            {
                conn      = default(DirectedConnection);
                insertIdx = default(int);
                return(false);
            }

            // Get the position in parent.ConnectionGeneArray that the new connection should be inserted at (to maintain sort order).
            insertIdx = ~insertIdx;
            return(true);
        }
        /// <summary>
        /// The graph traversal algorithm.
        /// </summary>
        /// <param name="connArr">A set of connections that represents the graph to traverse.</param>
        /// <param name="terminalNodeId">// The 'terminal' node ID, i.e. if traversal reaches this node then newConn would form a cycle and we stop/terminate traversal.</param>
        /// <returns></returns>
        private bool TraverseGraph(DirectedConnection[] connArr, int terminalNodeId)
        {
            // While there are entries on the stack.
            while (0 != _traversalStack.Count)
            {
                // Get the connection index from the top of stack; this is the next connection to be traversed.
                int currConnIdx = _traversalStack.Peek();

                // Notes.
                // Before we traverse the current connection, update the stack state to point to the next connection
                // to be traversed from the current node. I.e. set up the stack state ready for when the traversal down
                // into the current connection completes and returns back to the current node.
                //
                // This is essentially tail call optimisation, and will result in shorter stacks on average and also
                // has the side effect that we can no longer examine the stack to observe the traversal path at a given
                // point in time.
                MoveForward(connArr, currConnIdx);

                // Test if the next traversal child node has already been visited.
                int childNodeId  = connArr[currConnIdx].TargetId;
                int childNodeIdx = _nodeIdxByIdMap.Map(childNodeId);
                if (_visitedNodes[childNodeIdx])
                {
                    continue;
                }

                // Test if the connection target is the terminal node.
                if (childNodeId == terminalNodeId)
                {
                    return(true);
                }

                // We're about to traverse into childNodeId, so mark it as visited to prevent re-traversal.
                _visitedNodes[childNodeIdx] = true;

                // Search for outgoing connections from childNodeId.
                // ENHANCEMENT: Eliminate binary search for childNodeId.
                int connIdx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, childNodeId);
                if (connIdx >= 0)
                {   // childNodeId has outgoing connections; push the first connection onto the stack to mark it for traversal.
                    _traversalStack.Push(connIdx);
                }
            }

            // Traversal has completed without visiting the terminal node, therefore the new connection
            // does not form a cycle in the graph.
            return(false);
        }
        /// <summary>
        /// The graph traversal algorithm.
        /// </summary>
        /// <param name="connList">A set of connections that represents the graph to traverse.</param>
        /// <param name="terminalNodeId">// The 'terminal' node ID, i.e. if traversal reaches this node then newConn would form a cycle and we stop/terminate traversal.</param>
        /// <returns></returns>
        private bool TraverseGraph(IList <DirectedConnection> connList, int terminalNodeId)
        {
            // While there are entries on the stack.
            while (0 != _traversalStack.Count)
            {
                // Get the connection index from the top of stack; this is the next connection to be traversed.
                int currConnIdx = _traversalStack.Peek();

                // Notes.
                // Before we traverse the current connection, update the stack state to point to the next connection to be
                // traversed, either from the current node or a parent node. I.e. we modify the stack state  ready for when
                // the traversal down into the current connection completes and returns back to the current node.
                //
                // This approach results in tail call optimisation and thus will result in a shallower stack on average. It
                // also has the side effect that we can no longer examine the stack to observe the traversal path at a given
                // point in time, since some of the path may no longer be on the stack.
                MoveForward(connList, currConnIdx);

                // Test if the next traversal child node has already been visited.
                int childNodeId = connList[currConnIdx].TargetId;
                if (_visitedNodes.Contains(childNodeId))
                {
                    continue;
                }

                // Test if the connection target is the terminal node.
                if (childNodeId == terminalNodeId)
                {
                    return(true);
                }

                // We're about to traverse into childNodeId, so mark it as visited to prevent re-traversal.
                _visitedNodes.Add(childNodeId);

                // Search for outgoing connections from childNodeId.
                int connIdx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connList, childNodeId);
                if (connIdx >= 0)
                {   // childNodeId has outgoing connections; push the first connection onto the stack to mark it for traversal.
                    _traversalStack.Push(connIdx);
                }
            }

            // Traversal has completed without visiting the terminal node, therefore the new connection
            // does not form a cycle in the graph.
            return(false);
        }