/// <summary>
        /// Tests if the proposed new connection newConn would form a cycle if added to the existing directed
        /// acyclic graph described by connArr.
        /// </summary>
        /// <param name="connArr">A set of connections that describe a directed acyclic graph.</param>
        /// <param name="newConn">A proposed new connection to add to the graph.</param>
        public bool IsConnectionCyclic(
            DirectedConnection[] connArr,
            INodeIdMap nodeIdxByIdMap,
            int totalNodeCount,
            DirectedConnection newConn)
        {
            #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.");
            }
            #endif

            EnsureNodeCapacity(totalNodeCount);
            _nodeIdxByIdMap = nodeIdxByIdMap;

            try
            {
                return(IsConnectionCyclicInner(connArr, newConn));
            }
            finally
            {   // Ensure cleanup occurs before we return so that we can guarantee the class instance is ready for
                // re-use on the next call.
                Cleanup();
            }
        }
Ejemplo n.º 2
0
        public void TestIsConnectionCyclic3()
        {
            var cyclicTest = new CyclicConnectionTest();

            var connArr = new DirectedConnection[8];

            connArr[0] = new DirectedConnection(0, 10);
            connArr[1] = new DirectedConnection(0, 20);
            connArr[2] = new DirectedConnection(0, 30);
            connArr[3] = new DirectedConnection(10, 40);
            connArr[4] = new DirectedConnection(40, 20);
            connArr[5] = new DirectedConnection(20, 50);
            connArr[6] = new DirectedConnection(30, 60);
            connArr[7] = new DirectedConnection(30, 20);
            Array.Sort(connArr);

            INodeIdMap nodeIdxByIdMap = DirectedGraphUtils.CompileNodeIdMap_InputOutputCount_HiddenNodeIdArr(
                0, new int[] { 0, 10, 20, 30, 40, 50, 60 });

            // True tests (cycle).
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(20, 10)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(20, 40)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(50, 20)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(50, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(50, 40)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(60, 0)));

            // False tests (no cycle).
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(30, 50)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(10, 30)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(60, 10)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(60, 20)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(60, 40)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(30, 40)));
        }
Ejemplo n.º 3
0
        public void TestIsConnectionCyclic2()
        {
            var cyclicTest = new CyclicConnectionTest();

            var connArr = new DirectedConnection[8];

            connArr[0] = new DirectedConnection(0, 1);
            connArr[1] = new DirectedConnection(0, 2);
            connArr[2] = new DirectedConnection(0, 3);
            connArr[3] = new DirectedConnection(1, 4);
            connArr[4] = new DirectedConnection(4, 2);
            connArr[5] = new DirectedConnection(2, 5);
            connArr[6] = new DirectedConnection(3, 6);
            connArr[7] = new DirectedConnection(3, 2);
            Array.Sort(connArr);

            INodeIdMap nodeIdxByIdMap = DirectedGraphUtils.CompileNodeIdMap_InputOutputCount_HiddenNodeIdArr(
                0, new int[] { 0, 1, 2, 3, 4, 5, 6 });

            // True tests (cycle).
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(2, 1)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(2, 4)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(5, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(5, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(5, 4)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(6, 0)));

            // False tests (no cycle).
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(3, 5)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(1, 3)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(6, 1)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(6, 2)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(6, 4)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 7, new DirectedConnection(3, 4)));
        }
Ejemplo n.º 4
0
        public void TestIsConnectionCyclic1()
        {
            var cyclicTest = new CyclicConnectionTest();

            var connArr = new DirectedConnection[3];

            connArr[0] = new DirectedConnection(0, 1);
            connArr[1] = new DirectedConnection(1, 2);
            connArr[2] = new DirectedConnection(2, 3);

            INodeIdMap nodeIdxByIdMap = DirectedGraphUtils.CompileNodeIdMap_InputOutputCount_HiddenNodeIdArr(
                0, new int[] { 0, 1, 2, 3 });

            // True tests (cycle).
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(0, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(1, 1)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(2, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(3, 3)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(1, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(2, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(3, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(2, 1)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(3, 1)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(3, 2)));

            // False tests (no cycle).
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(0, 2)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(0, 3)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(1, 3)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(connArr, nodeIdxByIdMap, 4, new DirectedConnection(2, 3)));
        }
Ejemplo n.º 5
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 will form a cycle in the wider network.
            int totalNodeCount = _metaNeatGenome.InputOutputNodeCount + hiddenCount;

            if (_cyclicTest.IsConnectionCyclic(parent.ConnectionGenes._connArr, parent.NodeIndexByIdMap, totalNodeCount, conn))
            {
                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);
        }
        public void IsConnectionCyclic1()
        {
            var cyclicCheck = new CyclicConnectionCheck();

            var connArr = new DirectedConnection[3];

            connArr[0] = new DirectedConnection(0, 1);
            connArr[1] = new DirectedConnection(1, 2);
            connArr[2] = new DirectedConnection(2, 3);

            // True tests (cycle).
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(0, 0)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(1, 1)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 2)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 3)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(1, 0)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 0)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 0)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 1)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 1)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 2)));

            // False tests (no cycle).
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(0, 2)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(0, 3)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(1, 3)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 3)));
        }
        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);
        }
        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. this can be any node (input, output or hidden).
            int totalNodeCount = parent.MetaNeatGenome.InputOutputNodeCount + hiddenCount;
            int srcId          = GetNodeIdFromIndex(parent, _rng.Next(totalNodeCount));

            // Select a target node at random.
            // Note. This cannot be an input node (so must be a hidden or output node).
            int outputHiddenCount = outputCount + hiddenCount;
            int tgtId             = GetNodeIdFromIndex(parent, inputCount + _rng.Next(outputHiddenCount));

            // 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 does not already exist, therefore we can use it.
                // Get the position in parent.ConnectionGeneArray that the new connection should be inserted at (to maintain sort order).
                insertIdx = ~insertIdx;
                return(true);
            }

            conn      = default(DirectedConnection);
            insertIdx = default(int);
            return(false);
        }
Ejemplo n.º 9
0
        private static void AssertConnections(
            ConnectionGenes <T> connGenes,
            DirectedGraph digraph,
            INodeIdMap nodeIndexByIdMap,
            int[] connectionIndexMap)
        {
            // Connection counts.
            Debug.Assert(connGenes._connArr.Length == digraph.ConnectionIdArrays.Length);

            // Connection order.
            Debug.Assert(SortUtils.IsSortedAscending(connGenes._connArr));
            Debug.Assert(IsSortedAscending(digraph.ConnectionIdArrays));

            // Connection node ID mappings.
            DirectedConnection[] connArr = connGenes._connArr;
            int[] srcIdArr = digraph.ConnectionIdArrays._sourceIdArr;
            int[] tgtIdArr = digraph.ConnectionIdArrays._targetIdArr;

            for (int i = 0; i < connGenes._connArr.Length; i++)
            {
                DirectedConnection connGene = connArr[i];

                // Determine the index of he equivalent connection in the digraph.
                int genomeConnIdx = (null == connectionIndexMap) ? i : connectionIndexMap[i];

                Debug.Assert(nodeIndexByIdMap.Map(connArr[genomeConnIdx].SourceId) == srcIdArr[i]);
                Debug.Assert(nodeIndexByIdMap.Map(connArr[genomeConnIdx].TargetId) == tgtIdArr[i]);
            }
        }
        public void IsConnectionCyclic2()
        {
            var cyclicCheck = new CyclicConnectionCheck();

            var connArr = new DirectedConnection[8];

            connArr[0] = new DirectedConnection(0, 1);
            connArr[1] = new DirectedConnection(0, 2);
            connArr[2] = new DirectedConnection(0, 3);
            connArr[3] = new DirectedConnection(1, 4);
            connArr[4] = new DirectedConnection(4, 2);
            connArr[5] = new DirectedConnection(2, 5);
            connArr[6] = new DirectedConnection(3, 6);
            connArr[7] = new DirectedConnection(3, 2);
            Array.Sort(connArr);

            // True tests (cycle).
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 1)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(2, 4)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(5, 2)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(5, 0)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(5, 4)));
            Assert.True(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(6, 0)));

            // False tests (no cycle).
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 5)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(1, 3)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(6, 1)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(6, 2)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(6, 4)));
            Assert.False(cyclicCheck.IsConnectionCyclic(connArr, new DirectedConnection(3, 4)));
        }
        public void TestIsConnectionCyclic2()
        {
            var cyclicTest = new CyclicConnectionTest();

            var connArr = new DirectedConnection[8];

            connArr[0] = new DirectedConnection(0, 5);
            connArr[1] = new DirectedConnection(0, 2);
            connArr[2] = new DirectedConnection(0, 3);
            connArr[3] = new DirectedConnection(5, 4);
            connArr[4] = new DirectedConnection(4, 2);
            connArr[5] = new DirectedConnection(2, 1);
            connArr[6] = new DirectedConnection(3, 6);
            connArr[7] = new DirectedConnection(3, 2);
            Array.Sort(connArr);

            DirectedGraph digraph = DirectedGraphBuilder.Create(connArr, 1, 1);

            // True tests (cycle).
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(2, 5)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(2, 4)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 4)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(6, 0)));

            // False tests (no cycle).
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 1)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(5, 3)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(6, 5)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(6, 2)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(6, 4)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 4)));
        }
Ejemplo n.º 12
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));
        }
        public void TestIsConnectionCyclic1()
        {
            var cyclicTest = new CyclicConnectionTest();

            var connArr = new DirectedConnection[3];

            connArr[0] = new DirectedConnection(0, 2);
            connArr[1] = new DirectedConnection(2, 3);
            connArr[2] = new DirectedConnection(3, 1);

            DirectedGraph digraph = DirectedGraphBuilder.Create(connArr, 1, 1);

            // True tests (cycle).
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(0, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 1)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(2, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 3)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(2, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 0)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 2)));
            Assert.IsTrue(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(1, 3)));

            // False tests (no cycle).
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(0, 3)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(0, 1)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(2, 1)));
            Assert.IsFalse(cyclicTest.IsConnectionCyclic(digraph, new DirectedConnection(3, 1)));
        }
Ejemplo n.º 14
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);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Calculates the L2/Euclidean centroid for the given set of points.
        /// Note. In Euclidean space the centroid is given by calculating the componentwise mean over the
        /// set of points.
        /// </summary>
        /// <remarks>
        /// The euclidean centroid is a central position within a set of points that minimizes the sum of the squared
        /// distance between each of those points and the centroid. As such it can also be thought of as being an exemplar
        /// for a set of points.
        /// </remarks>
        public static ConnectionGenes <double> CalculateEuclideanCentroid(IEnumerable <ConnectionGenes <double> > coordList)
        {
            // Special case. One item in list, therefore it is the centroid.
            int count = coordList.Count();

            if (1 == count)
            {
                return(coordList.First());
            }

            // Each coordinate element has an ID. Here we calculate the total for each ID across all CoordinateVectors,
            // then divide the totals by the number of CoordinateVectors to get the average for each ID. That is, we
            // calculate the componentwise mean.
            //
            // Coord elements within a CoordinateVector must be sorted by ID, therefore we use a SortedDictionary here
            // when building the centroid coordinate to eliminate the need to sort elements later.
            //
            // We use SortedDictionary and not SortedList for performance. SortedList is fastest for insertion
            // only if the inserts are in order (sorted). However, this is generally not the case here because although
            // coordinate IDs are sorted within the source CoordinateVectors, not all IDs exist within all CoordinateVectors
            // therefore a low ID may be presented to coordElemTotals after a higher ID.
            var coordElemTotals = new SortedDictionary <DirectedConnection, WeightRef>();

            // Loop over coords.
            foreach (ConnectionGenes <double> coord in coordList)
            {
                // Loop over each element within the current coord.
                DirectedConnection[] connArr   = coord._connArr;
                double[]             weightArr = coord._weightArr;

                for (int i = 0; i < connArr.Length; i++)
                {
                    DirectedConnection conn   = connArr[i];
                    double             weight = weightArr[i];

                    // If the ID has previously been encountered then add the current element value to it, otherwise
                    // add a new double[1] to hold the value..
                    // Note that we wrap the double value in an object so that we do not have to re-insert values
                    // to increment them. In tests this approach was about 40% faster (including GC overhead).

                    // TODO: Review the use of WeightRef as a wrapper; this generates a lot of object allocations that we could avoid, e.g.
                    // with a custom dictionary implementation that allows accumulating a numeric value for an existing entry; to do this with a
                    // Dictionary requires the approach used here, or re-looking up the slot to update it.
                    if (coordElemTotals.TryGetValue(conn, out WeightRef weightRef))
                    {
                        weightRef.Weight += weight;
                    }
                    else
                    {
                        coordElemTotals.Add(conn, new WeightRef(weight));
                    }
                }
            }

            // Create and return the centroid.
            return(CreateCentroid(coordElemTotals, count));
        }
Ejemplo n.º 16
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);
    }
Ejemplo n.º 17
0
        /// <summary>
        /// Create a new child genome from a given parent genome.
        /// </summary>
        /// <param name="parent">The parent genome.</param>
        /// <returns>A new child genome.</returns>
        public NeatGenome <T> CreateChildGenome(NeatGenome <T> parent)
        {
            Debug.Assert(_metaNeatGenome == parent.MetaNeatGenome, "Parent genome has unexpected MetaNeatGenome.");

            // Attempt to find a new connection that we can add to the genome.
            DirectedConnection directedConn;

            if (!TryGetConnection(parent, out directedConn, out int insertIdx))
            {   // Failed to find a new connection.
                return(null);
            }

            // Determine the connection weight.
            // 50% of the time use weights very close to zero.
            // Note. this recreates the strategy used in SharpNEAT 2.x.
            // ENHANCEMENT: Reconsider the distribution of new weights and if there are better approaches (distributions) we could use.
            T weight = _rng.NextBool() ? _weightDistB.Sample() : _weightDistA.Sample();

            // Create a new connection gene array that consists of the parent connection genes plus the new gene
            // inserted at the correct (sorted) position.
            var parentConnArr   = parent.ConnectionGenes._connArr;
            var parentWeightArr = parent.ConnectionGenes._weightArr;
            int parentLen       = parentConnArr.Length;

            // Create the child genome's ConnectionGenes object.
            int childLen  = parentLen + 1;
            var connGenes = new ConnectionGenes <T>(childLen);
            var connArr   = connGenes._connArr;
            var weightArr = connGenes._weightArr;

            // Copy genes up to insertIdx.
            Array.Copy(parentConnArr, connArr, insertIdx);
            Array.Copy(parentWeightArr, weightArr, insertIdx);

            // Copy the new genome into its insertion point.
            connArr[insertIdx] = new DirectedConnection(
                directedConn.SourceId,
                directedConn.TargetId);

            weightArr[insertIdx] = weight;

            // Copy remaining genes (if any).
            Array.Copy(parentConnArr, insertIdx, connArr, insertIdx + 1, parentLen - insertIdx);
            Array.Copy(parentWeightArr, insertIdx, weightArr, insertIdx + 1, parentLen - insertIdx);

            // Create and return a new genome.
            // Notes.
            // The set of hidden node IDs remains unchanged from the parent, therefore we are able to re-use parent.HiddenNodeIdArray.
            // However, the presence of a new connection invalidates parent.NodeIndexByIdMap for use in the new genome, because the allocated
            // node indexes are dependent on node depth in the acyclic graph, which in turn can be modified by the presence of a new connection.
            return(_genomeBuilder.Create(
                       _genomeIdSeq.Next(),
                       _generationSeq.Peek,
                       connGenes,
                       parent.HiddenNodeIdArray));
        }
    /// <summary>
    /// Create a new child genome from a given parent genome.
    /// </summary>
    /// <param name="parent">The parent genome.</param>
    /// <param name="rng">Random source.</param>
    /// <returns>A new child genome.</returns>
    public NeatGenome <T>?CreateChildGenome(NeatGenome <T> parent, IRandomSource rng)
    {
        // Attempt to find a new connection that we can add to the genome.
        if (!TryGetConnection(parent, rng, out DirectedConnection directedConn, out int insertIdx))
        {
            // Failed to find a new connection.
            return(null);
        }

        // Determine the connection weight.
        // 50% of the time use weights very close to zero.
        // Note. this recreates the strategy used in SharpNEAT 2.x.
        // ENHANCEMENT: Reconsider the distribution of new weights and if there are better approaches (distributions) we could use.
        T weight = rng.NextBool() ? _weightSamplerB.Sample(rng) : _weightSamplerA.Sample(rng);

        // Create a new connection gene array that consists of the parent connection genes plus the new gene
        // inserted at the correct (sorted) position.
        var parentConnArr   = parent.ConnectionGenes._connArr;
        var parentWeightArr = parent.ConnectionGenes._weightArr;
        int parentLen       = parentConnArr.Length;

        // Create the child genome's ConnectionGenes object.
        int childLen  = parentLen + 1;
        var connGenes = new ConnectionGenes <T>(childLen);
        var connArr   = connGenes._connArr;
        var weightArr = connGenes._weightArr;

        // Copy genes up to insertIdx.
        Array.Copy(parentConnArr, connArr, insertIdx);
        Array.Copy(parentWeightArr, weightArr, insertIdx);

        // Copy the new genome into its insertion point.
        connArr[insertIdx] = new DirectedConnection(
            directedConn.SourceId,
            directedConn.TargetId);

        weightArr[insertIdx] = weight;

        // Copy remaining genes (if any).
        Array.Copy(parentConnArr, insertIdx, connArr, insertIdx + 1, parentLen - insertIdx);
        Array.Copy(parentWeightArr, insertIdx, weightArr, insertIdx + 1, parentLen - insertIdx);

        // Create and return a new genome.
        // Note. The set of hidden node IDs remains unchanged from the parent, therefore we are able to re-use
        // both parent.HiddenNodeIdArray and NodeIndexByIdMap.
        return(_genomeBuilder.Create(
                   _genomeIdSeq.Next(),
                   _generationSeq.Peek,
                   connGenes,
                   parent.HiddenNodeIdArray,
                   parent.NodeIndexByIdMap));
    }
    /// <summary>
    /// Calculates the L2/Euclidean centroid for the given set of points.
    /// </summary>
    /// <param name="pointList">The set of points.</param>
    /// <returns>A new instance of <see cref="ConnectionGenes{T}"/>.</returns>
    /// <remarks>
    /// The euclidean centroid is a central position within a set of points that minimizes the sum of the squared
    /// distance between each of those points and the centroid. As such it can also be thought of as being an exemplar
    /// for a set of points.
    ///
    /// In Euclidean space the centroid is obtained by calculating the componentwise mean over the set of points.
    /// </remarks>
    public static ConnectionGenes <double> CalculateEuclideanCentroid(
        IList <ConnectionGenes <double> > pointList)
    {
        if (pointList.Count == 1)
        {   // Special case. One item in list, therefore it is the centroid.
            return(pointList[0]);
        }
        else if (pointList.Count == 0)
        {   // This scenario isn't intended to occur; see https://github.com/colgreen/sharpneat-refactor/issues/5
            return(new ConnectionGenes <double>(0));
        }

        // ENHANCEMENT: Obtain dictionary from a pool to avoid allocation and initialisation cost on each call to this method.

        // Each coordinate element has an ID. Here we calculate the total for each ID across all CoordinateVectors,
        // then divide the totals by the number of CoordinateVectors to get the average for each ID. That is, we
        // calculate the componentwise mean.
        var coordElemTotals = new Dictionary <DirectedConnection, double>(
            Math.Max(pointList[0].Length, 32));

        // Loop over coords.
        foreach (ConnectionGenes <double> point in pointList)
        {
            // Loop over each element within the current coord.
            DirectedConnection[] connArr   = point._connArr;
            double[]             weightArr = point._weightArr;

            for (int i = 0; i < connArr.Length; i++)
            {
                DirectedConnection conn   = connArr[i];
                double             weight = weightArr[i];

                // ENHANCEMENT: Updating an existing entry here requires a second lookup; in principle this could be avoided,
                // e.g. by using a custom dictionary implementation with InsertOrSum() method.

                // If the ID has previously been encountered then add the current element value to it, otherwise
                // add a new entry to the dictionary.
                // TODO: [.NET 6+] Use Marshal.GetValueRefOrAddDefault here to avoid the second lookup for adding a missing item.
                if (coordElemTotals.TryGetValue(conn, out double weightAcc))
                {
                    coordElemTotals[conn] = weightAcc + weight;
                }
                else
                {
                    coordElemTotals.Add(conn, weight);
                }
            }
        }

        // Create and return the centroid.
        return(CreateCentroid(coordElemTotals, pointList.Count));
    }
Ejemplo n.º 20
0
        /// <summary>
        /// Calculates the L2/Euclidean centroid for the given set of points.
        /// Note. In Euclidean space the centroid is given by calculating the componentwise mean over the
        /// set of points.
        /// </summary>
        /// <remarks>
        /// The euclidean centroid is a central position within a set of points that minimizes the sum of the squared
        /// distance between each of those points and the centroid. As such it can also be thought of as being an exemplar
        /// for a set of points.
        /// </remarks>
        public static ConnectionGenes <double> CalculateEuclideanCentroid(
            IEnumerable <ConnectionGenes <double> > coordList)
        {
            // Special case. One item in list, therefore it is the centroid.
            int count = coordList.Count();

            if (1 == count)
            {
                return(coordList.First());
            }

            // ENHANCEMENT: Obtain dictionary from a pool to avoid allocation and initialisation cost on each call to this method.

            // Each coordinate element has an ID. Here we calculate the total for each ID across all CoordinateVectors,
            // then divide the totals by the number of CoordinateVectors to get the average for each ID. That is, we
            // calculate the componentwise mean.
            var coordElemTotals = new Dictionary <DirectedConnection, double>(64);

            // Loop over coords.
            foreach (ConnectionGenes <double> coord in coordList)
            {
                // Loop over each element within the current coord.
                DirectedConnection[] connArr   = coord._connArr;
                double[]             weightArr = coord._weightArr;

                for (int i = 0; i < connArr.Length; i++)
                {
                    DirectedConnection conn   = connArr[i];
                    double             weight = weightArr[i];

                    // ENHANCEMENT: Updating an existing entry here requires a second lookup; in principle this could be avoided,
                    // e.g. by using a custom dictionary implementation with InsertOrSum() method.

                    // If the ID has previously been encountered then add the current element value to it, otherwise
                    // add a new entry to the dictionary.
                    if (coordElemTotals.TryGetValue(conn, out double weightAcc))
                    {
                        coordElemTotals[conn] = weightAcc + weight;
                    }
                    else
                    {
                        coordElemTotals.Add(conn, weight);
                    }
                }
            }

            // Create and return the centroid.
            return(CreateCentroid(coordElemTotals, count));
        }
        private bool TryGetConnection(NeatGenome <T> parent, out DirectedConnection conn, out int insertIdx)
        {
            // Make several attempts at find a new connection, if not successful then give up.
            for (int attempts = 0; attempts < 5; attempts++)
            {
                if (TryGetConnectionInner(parent, out conn, out insertIdx))
                {
                    return(true);
                }
            }

            conn      = default(DirectedConnection);
            insertIdx = default(int);
            return(false);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Tests if the proposed new connection newConn would form a cycle if added to the existing directed
        /// acyclic graph described by connArr.
        /// </summary>
        /// <param name="connList">A set of connections that describe a directed acyclic graph.</param>
        /// <param name="newConn">A proposed new connection to add to the graph.</param>
        public bool IsConnectionCyclic(IList <DirectedConnection> connList, DirectedConnection newConn)
        {
            #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.");
            }
            #endif

            try
            {
                return(IsConnectionCyclicInner(connList, newConn));
            }
            finally
            {   // Ensure cleanup occurs before we return so that we can guarantee the class instance is ready for
                // re-use on the next call.
                Cleanup();
            }
        }
        /// <summary>
        /// Calculates the L2/Euclidean centroid for the given set of points.
        /// Note. In Euclidean space the centroid is given by calculating the componentwise mean over the
        /// set of points.
        /// </summary>
        /// <remarks>
        /// The euclidean centroid is a central position within a set of points that minimizes the sum of the squared
        /// distance between each of those points and the centroid. As such it can also be thought of as being an exemplar
        /// for a set of points.
        /// </remarks>
        public static ConnectionGenes <double> CalculateEuclideanCentroid(IEnumerable <ConnectionGenes <double> > coordList)
        {
            // Special case. One item in list, therefore it is the centroid.
            int count = coordList.Count();

            if (1 == count)
            {
                return(coordList.First());
            }

            // Each coordinate element has an ID. Here we calculate the total for each ID across all CoordinateVectors,
            // then divide the totals by the number of CoordinateVectors to get the average for each ID. That is, we
            // calculate the componentwise mean.
            //
            // Coord elements within a CoordinateVector must be sorted by ID, therefore we use a SortedDictionary here
            // when building the centroid coordinate to eliminate the need to sort elements later.
            //
            // We use SortedDictionary and not SortedList for performance. SortedList is fastest for insertion
            // only if the inserts are in order (sorted). However, this is generally not the case here because although
            // coordinate IDs are sorted within the source CoordinateVectors, not all IDs exist within all CoordinateVectors
            // therefore a low ID may be presented to coordElemTotals after a higher ID.
            var coordElemTotals = new SortedDictionary <DirectedConnection, double[]>();

            // Loop over coords.
            foreach (ConnectionGenes <double> coord in coordList)
            {
                // Loop over each element within the current coord.
                DirectedConnection[] connArr   = coord._connArr;
                double[]             weightArr = coord._weightArr;

                for (int i = 0; i < connArr.Length; i++)
                {
                    DirectedConnection conn   = connArr[i];
                    double             weight = weightArr[i];

                    // If the ID has previously been encountered then add the current element value to it, otherwise
                    // add a new double[1] to hold the value..
                    // Note that we wrap the double value in an object so that we do not have to re-insert values
                    // to increment them. In tests this approach was about 40% faster (including GC overhead).
                    if (coordElemTotals.TryGetValue(conn, out double[] doubleWrapper))
        private static bool ValidateConnections(
            ConnectionGenes <T> connGenes,
            DirectedGraph digraph,
            INodeIdMap nodeIndexByIdMap,
            int[] connectionIndexMap)
        {
            // Connection counts.
            if (connGenes._connArr.Length != digraph.ConnectionIdArrays.Length)
            {
                return(false);
            }

            // Connection order.
            if (!SortUtils.IsSortedAscending(connGenes._connArr) ||
                !IsSortedAscending(digraph.ConnectionIdArrays))
            {
                return(false);
            }

            // Connection node ID mappings.
            DirectedConnection[] connArr = connGenes._connArr;
            int[] srcIdArr = digraph.ConnectionIdArrays._sourceIdArr;
            int[] tgtIdArr = digraph.ConnectionIdArrays._targetIdArr;

            for (int i = 0; i < connGenes._connArr.Length; i++)
            {
                DirectedConnection connGene = connArr[i];

                // Determine the index of he equivalent connection in the digraph.
                int genomeConnIdx = (null == connectionIndexMap) ? i : connectionIndexMap[i];

                if (nodeIndexByIdMap.Map(connArr[genomeConnIdx].SourceId) != srcIdArr[i] ||
                    nodeIndexByIdMap.Map(connArr[genomeConnIdx].TargetId) != tgtIdArr[i])
                {
                    return(false);
                }
            }

            return(true);
        }
        /// <summary>
        /// Creates a single randomly initialised genome.
        /// </summary>
        private NeatGenome <T> CreateGenome()
        {
            // Determine how many connections to create in the new genome, as a proportion of all possible connections
            // between the input and output nodes.
            int connectionCount = (int)NumericsUtils.ProbabilisticRound(_connectionDefArr.Length * _connectionsProportion, _rng);

            // Ensure there is at least one connection.
            connectionCount = Math.Max(1, connectionCount);

            // Select a random subset of all possible connections between the input and output nodes.
            int[] sampleArr = new int[connectionCount];
            DiscreteDistributionUtils.SampleUniformWithoutReplacement(
                _connectionDefArr.Length, sampleArr, _rng);

            // Sort the samples.
            // Note. This results in the neural net connections being sorted by sourceID then targetID.
            Array.Sort(sampleArr);

            // Create the connection gene arrays and populate them.
            var connGenes = new ConnectionGenes <T>(connectionCount);
            var connArr   = connGenes._connArr;
            var weightArr = connGenes._weightArr;

            for (int i = 0; i < sampleArr.Length; i++)
            {
                DirectedConnection cdef = _connectionDefArr[sampleArr[i]];

                connArr[i] = new DirectedConnection(
                    cdef.SourceId,
                    cdef.TargetId);

                weightArr[i] = _connWeightDist.Sample(_metaNeatGenome.ConnectionWeightRange, true);
            }

            // Get create a new genome with a new ID, birth generation of zero.
            int id = _genomeIdSeq.Next();

            return(_genomeBuilder.Create(id, 0, connGenes));
        }
        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);
        }
        /// <summary>
        /// Calculates the distance between two positions.
        /// </summary>
        public double CalcDistance(ConnectionGenes <double> p1, ConnectionGenes <double> p2)
        {
            DirectedConnection[] connArr1   = p1._connArr;
            DirectedConnection[] connArr2   = p2._connArr;
            double[]             weightArr1 = p1._weightArr;
            double[]             weightArr2 = p2._weightArr;

            // Store these heavily used values locally.
            int length1 = connArr1.Length;
            int length2 = connArr2.Length;

            // Test for special cases.
            if (0 == length1 && 0 == length2)
            {   // Both arrays are empty. No disparities, therefore the distance is zero.
                return(0.0);
            }

            double distance = 0.0;

            if (0 == length1)
            {
                // All p2 genes are mismatches.
                for (int i = 0; i < length2; i++)
                {
                    distance += Math.Abs(weightArr2[i]);
                }
                return((_mismatchDistanceConstant * length2) + (distance * _mismatchDistanceCoeff));
            }

            if (0 == length2)
            {
                // All p1 elements are mismatches.
                for (int i = 0; i < length1; i++)
                {
                    distance += Math.Abs(weightArr1[i]);
                }
                return((_mismatchDistanceConstant * length1) + (distance * _mismatchDistanceCoeff));
            }

            // Both arrays contain elements.
            int arr1Idx = 0;
            int arr2Idx = 0;
            DirectedConnection conn1   = connArr1[arr1Idx];
            DirectedConnection conn2   = connArr2[arr2Idx];
            double             weight1 = weightArr1[arr1Idx];
            double             weight2 = weightArr2[arr2Idx];

            for (;;)
            {
                if (conn1 < conn2)
                {
                    // p2 doesn't specify a value in this dimension therefore we take its position to be 0.
                    distance += _mismatchDistanceConstant + (Math.Abs(weight1) * _mismatchDistanceCoeff);

                    // Move to the next element in p1.
                    arr1Idx++;
                }
                else if (conn1 == conn2)
                {
                    // Matching elements.
                    distance += Math.Abs(weight1 - weight2) * _matchDistanceCoeff;

                    // Move to the next element in both arrays.
                    arr1Idx++;
                    arr2Idx++;
                }
                else // conn2 > conn1
                {
                    // p1 doesn't specify a value in this dimension therefore we take its position to be 0.
                    distance += _mismatchDistanceConstant + (Math.Abs(weight2) * _mismatchDistanceCoeff);

                    // Move to the next element in p2.
                    arr2Idx++;
                }

                // Check if we have exhausted one or both of the arrays.
                if (arr1Idx == length1)
                {
                    // All remaining p2 elements are mismatches.
                    for (int i = arr2Idx; i < length2; i++)
                    {
                        distance += _mismatchDistanceConstant + (Math.Abs(weightArr2[i]) * _mismatchDistanceCoeff);
                    }
                    return(distance);
                }

                if (arr2Idx == length2)
                {
                    // All remaining p1 elements are mismatches.
                    for (int i = arr1Idx; i < connArr1.Length; i++)
                    {
                        distance += _mismatchDistanceConstant + (Math.Abs(weightArr1[i]) * _mismatchDistanceCoeff);
                    }
                    return(distance);
                }

                conn1   = connArr1[arr1Idx];
                conn2   = connArr2[arr2Idx];
                weight1 = weightArr1[arr1Idx];
                weight2 = weightArr2[arr2Idx];
            }
        }
        /// <summary>
        /// Tests if the distance between two positions is less than some threshold.
        ///
        /// A simple way of implementing this method would be to calculate the distance between the
        /// two coordinates and test if it is less than the threshold. However, that approach requires that all of the
        /// elements in both CoordinateVectors be fully compared. We can improve performance in the general case
        /// by testing if the threshold has been passed after each vector element comparison thus allowing an early exit
        /// from the method for many calls. Further to this, we can begin comparing from the ends of the vectors where
        /// differences are most likely to occur.
        /// </summary>
        public bool TestDistance(ConnectionGenes <double> p1, ConnectionGenes <double> p2, double threshold)
        {
            DirectedConnection[] connArr1   = p1._connArr;
            DirectedConnection[] connArr2   = p2._connArr;
            double[]             weightArr1 = p1._weightArr;
            double[]             weightArr2 = p2._weightArr;

            // Store these heavily used values locally.
            int length1 = connArr1.Length;
            int length2 = connArr2.Length;

            // Test for special case.
            if (0 == length1 && 0 == length2)
            {   // Both arrays are empty. No disparities, therefore the distance is zero.
                return(0.0 < threshold);
            }

            double distance = 0.0;

            if (0 == length1)
            {
                // All p2 elements are mismatches.
                // p1 doesn't specify a value in these dimensions therefore we take its position to be 0 in all of them.
                for (int i = 0; i < length2; i++)
                {
                    distance += Math.Abs(weightArr2[i]);
                }
                distance = (_mismatchDistanceConstant * length2) + (distance * _mismatchDistanceCoeff);
                return(distance < threshold);
            }

            if (0 == length2)
            {
                // All p1 elements are mismatches.
                // p2 doesn't specify a value in these dimensions therefore we take its position to be 0 in all of them.
                for (int i = 0; i < length1; i++)
                {
                    distance += Math.Abs(weightArr1[i]);
                }
                distance = (_mismatchDistanceConstant * length1) + (distance * _mismatchDistanceCoeff);
                return(distance < threshold);
            }

            // Both arrays contain elements. Compare the contents starting from the ends where the greatest discrepancies
            // between coordinates are expected to occur. In the general case this should result in less element comparisons
            // before the threshold is passed and we exit the method.
            int arr1Idx = length1 - 1;
            int arr2Idx = length2 - 1;
            DirectedConnection conn1   = connArr1[arr1Idx];
            DirectedConnection conn2   = connArr2[arr2Idx];
            double             weight1 = weightArr1[arr1Idx];
            double             weight2 = weightArr2[arr2Idx];

            for (;;)
            {
                if (conn1 > conn2)
                {
                    // p2 doesn't specify a value in this dimension therefore we take its position to be 0.
                    distance += _mismatchDistanceConstant + (Math.Abs(weight1) * _mismatchDistanceCoeff);

                    // Move to the next element in p1.
                    arr1Idx--;
                }
                else if (conn1 == conn2)
                {
                    // Matching elements.
                    distance += Math.Abs(weight1 - weight2) * _matchDistanceCoeff;

                    // Move to the next element in both arrays.
                    arr1Idx--;
                    arr2Idx--;
                }
                else // conn2 > conn1
                {
                    // p1 doesn't specify a value in this dimension therefore we take its position to be 0.
                    distance += _mismatchDistanceConstant + (Math.Abs(weight2) * _mismatchDistanceCoeff);

                    // Move to the next element in p12
                    arr2Idx--;
                }

                // Test the threshold.
                if (distance >= threshold)
                {
                    return(false);
                }

                // Check if we have exhausted one or both of the arrays.
                if (arr1Idx < 0)
                {   // Any remaining p2 elements are mismatches.
                    for (int i = arr2Idx; i > -1; i--)
                    {
                        distance += _mismatchDistanceConstant + (Math.Abs(weightArr2[i]) * _mismatchDistanceCoeff);
                    }
                    return(distance < threshold);
                }

                if (arr2Idx < 0)
                {   // All remaining p1 elements are mismatches.
                    for (int i = arr1Idx; i > -1; i--)
                    {
                        distance += _mismatchDistanceConstant + (Math.Abs(weightArr1[i]) * _mismatchDistanceCoeff);
                    }
                    return(distance < threshold);
                }

                conn1   = connArr1[arr1Idx];
                conn2   = connArr2[arr2Idx];
                weight1 = weightArr1[arr1Idx];
                weight2 = weightArr2[arr2Idx];
            }
        }
Ejemplo n.º 29
0
 public MockConnection(IWampFormatter <TMessage> formatter)
 {
     mSideAToSideBConnection = new DirectedConnection(mSideBToSideA, mSideAToSideB, formatter);
     mSideBToSideAConnection = new DirectedConnection(mSideAToSideB, mSideBToSideA, formatter);
 }
Ejemplo n.º 30
0
        /// <summary>
        /// Create a new child genome from a given parent genome.
        /// </summary>
        /// <param name="parent">The parent genome.</param>
        /// <param name="rng">Random source.</param>
        /// <returns>A new child genome.</returns>
        public NeatGenome <T>?CreateChildGenome(NeatGenome <T> parent, IRandomSource rng)
        {
            if (0 == parent.ConnectionGenes.Length)
            {
                // No connections to split (nodes are added by splitting an existing connection).
                return(null);
            }

            // Select a connection at random.
            int splitConnIdx = rng.Next(parent.ConnectionGenes.Length);
            var splitConn    = parent.ConnectionGenes._connArr[splitConnIdx];

            // The selected connection will be replaced with a new node and two new connections;
            // get an innovation ID for the new node.
            int addedNodeId = GetInnovationID(splitConn, parent, out bool newInnovationIdsFlag);

            // Create the two new connections.
            var newConnArr = new DirectedConnection[] {
                new DirectedConnection(splitConn.SourceId, addedNodeId),
                new DirectedConnection(addedNodeId, splitConn.TargetId)
            };

            // Get weights for the new connections.
            // Connection 1 gets the weight from the original connection; connection 2 gets a fixed
            // weight of _metaNeatGenome.ConnectionWeightRange.

            // ENHANCEMENT: Consider a better choice of weights for the new connections; this scheme has been
            // copied from sharpneat 2.x as a starting point, but can likely be improved upon.
            var newWeightArr = new T[] {
                parent.ConnectionGenes._weightArr[splitConnIdx],
                (T)Convert.ChangeType(_metaNeatGenome.ConnectionWeightScale, typeof(T))
            };

            // Ensure newConnArr is sorted.
            // Later on we'll determine their insertion indexes into the connection array, therefore this ensures that
            // the insert indexes will be sorted correctly.
            if (newConnArr[0].CompareTo(newConnArr[1]) > 0)
            {
                var tmpConn = newConnArr[0];
                newConnArr[0] = newConnArr[1];
                newConnArr[1] = tmpConn;

                T tmpWeight = newWeightArr[0];
                newWeightArr[0] = newWeightArr[1];
                newWeightArr[1] = tmpWeight;
            }

            // Create a new connection gene array that consists of the parent connection genes,
            // with the connection that was split removed, and the two new connection genes that
            // replace it inserted at the correct (sorted) positions.
            var parentConnArr   = parent.ConnectionGenes._connArr;
            var parentWeightArr = parent.ConnectionGenes._weightArr;
            int parentLen       = parentConnArr.Length;

            // Create the child genome's ConnectionGenes object.
            int childLen  = parentLen + 1;
            var connGenes = new ConnectionGenes <T>(childLen);
            var connArr   = connGenes._connArr;
            var weightArr = connGenes._weightArr;

            // Build an array of parent indexes to stop at when copying from the parent to the child connection array.
            // Note. Each index is combined with a second value; an index into newConnArr for insertions,
            // and -1 for the split index (the connection to be removed)
            int insertIdx1 = ~Array.BinarySearch(parent.ConnectionGenes._connArr, newConnArr[0]);
            int insertIdx2 = ~Array.BinarySearch(parent.ConnectionGenes._connArr, newConnArr[1]);

            (int, int)[] stopIdxArr = new []