public void ConnectionSorterBenchmark()
 {
     for (int i = 0; i < _dataArr.Length; i++)
     {
         ConnectionData connData = _dataArr[i];
         ConnectionSorter <double> .Sort(connData._connIdArrays, connData._weightArr);
     }
 }
示例#2
0
        public void TestConnectionSorter()
        {
            IRandomSource rng = RandomDefaults.CreateRandomSource(0);
            int           len = 1000;

            // Create random connection ID arrays.
            int[] srcIdArr = CreateRandomConnectionIdArray(len, rng);
            int[] tgtIdArr = CreateRandomConnectionIdArray(len, rng);

            // Assign each connection's weight to be the sum of the source and target IDs.
            // This allows us to check that the weights are sorted correctly, i.e. remain aligned with the correct source and target IDs.
            double[] weightArr = new double[len];
            for (int i = 0; i < len; i++)
            {
                weightArr[i] = srcIdArr[i] + tgtIdArr[i];
            }

            // Package up the source and target ID arrays.
            ConnectionIdArrays connIdArrays = new ConnectionIdArrays(srcIdArr, tgtIdArr);

            // Sort the connections.
            ConnectionSorter <double> .Sort(connIdArrays, weightArr);

            // Test connections are correctly ordered.
            int srcIdPrev = srcIdArr[0];
            int tgtIdPrev = tgtIdArr[0];

            Assert.Equal(weightArr[0], (double)(srcIdArr[0] + tgtIdArr[0]));

            for (int i = 0; i < len; i++)
            {
                Assert.True(Compare(srcIdPrev, tgtIdPrev, srcIdArr[i], tgtIdArr[i]) <= 0);
                Assert.Equal(weightArr[i], (double)(srcIdArr[i] + tgtIdArr[i]));

                srcIdPrev = srcIdArr[i];
                tgtIdPrev = tgtIdArr[i];
            }
        }
示例#3
0
    public void TestConnectionSorter(int len)
    {
        IRandomSource rng = RandomDefaults.CreateRandomSource((uint)len);

        // Create random connection ID arrays.
        ConnectionIds connIds = new(len);
        var           srcIds  = connIds.GetSourceIdSpan();
        var           tgtIds  = connIds.GetTargetIdSpan();

        InitRandomValues(connIds.GetSourceIdSpan(), rng);
        InitRandomValues(connIds.GetTargetIdSpan(), rng);

        // Assign each connection's weight to be the sum of the source and target IDs.
        // This allows us to check that the weights are sorted correctly, i.e. remain aligned with the correct source and target IDs.
        double[] weightArr = new double[len];
        for (int i = 0; i < len; i++)
        {
            weightArr[i] = srcIds[i] + tgtIds[i];
        }

        // Sort the connections.
        ConnectionSorter <double> .Sort(connIds, weightArr);

        // Test connections are correctly ordered.
        int srcIdPrev = srcIds[0];
        int tgtIdPrev = tgtIds[0];

        Assert.Equal(weightArr[0], (double)(srcIds[0] + tgtIds[0]));

        for (int i = 1; i < len; i++)
        {
            Assert.True(Compare(srcIdPrev, tgtIdPrev, srcIds[i], tgtIds[i]) <= 0);
            Assert.Equal(weightArr[i], (double)(srcIds[i] + tgtIds[i]));

            srcIdPrev = srcIds[i];
            tgtIdPrev = tgtIds[i];
        }
    }
        public static DirectedGraphAcyclic CreateDirectedGraphAcyclic(
            DirectedGraph digraph,
            GraphDepthInfo depthInfo,
            out int[] newIdByOldId,
            out int[] connectionIndexMap,
            ref int[]?timsortWorkArr,
            ref int[]?timsortWorkVArr)
        {
            int inputCount  = digraph.InputCount;
            int outputCount = digraph.OutputCount;

            // Assert that all input nodes are at depth zero.
            // Any input node with a non-zero depth must have an input connection, and this is not supported.
            Debug.Assert(SpanUtils.Equals(depthInfo._nodeDepthArr.AsSpan(0, inputCount), 0));

            // Compile a mapping from current node IDs to new IDs (based on node depth in the graph).
            newIdByOldId = CompileNodeIdMap(depthInfo, digraph.TotalNodeCount, inputCount, ref timsortWorkArr, ref timsortWorkVArr);

            // Map the connection node IDs.
            ConnectionIdArrays connIdArrays = digraph.ConnectionIdArrays;

            MapIds(connIdArrays, newIdByOldId);

            // Init connection index map.
            int connCount = connIdArrays.Length;

            connectionIndexMap = new int[connCount];
            for (int i = 0; i < connCount; i++)
            {
                connectionIndexMap[i] = i;
            }

            // Sort the connections based on sourceID, targetId; this will arrange the connections based on the depth
            // of the source nodes.
            // Note. This sort routine will also sort a secondary array, i.e. keep the items in both arrays aligned;
            // here we use this to create connectionIndexMap.
            ConnectionSorter <int> .Sort(connIdArrays, connectionIndexMap);

            // Make a copy of the sub-range of newIdMap that represents the output nodes.
            // This is required later to be able to locate the output nodes now that they have been sorted by depth.
            int[] outputNodeIdxArr = new int[outputCount];
            Array.Copy(newIdByOldId, inputCount, outputNodeIdxArr, 0, outputCount);

            // Create an array of LayerInfo(s).
            // Each LayerInfo contains the index + 1 of both the last node and last connection in that layer.
            //
            // The array is in order of depth, from layer zero (inputs nodes) to the last layer (usually output nodes,
            // but not necessarily if there is a dead end pathway with a high number of hops).
            //
            // Note. There is guaranteed to be at least one connection with a source at a given depth level, this is
            // because for there to be a layer N there must necessarily be a connection from a node in layer N-1
            // to a node in layer N.
            int graphDepth = depthInfo._graphDepth;

            LayerInfo[] layerInfoArr = new LayerInfo[graphDepth];

            // Note. Scanning over nodes can start at inputCount instead of zero, because all nodes prior to that index
            // are input nodes and are therefore at depth zero. (input nodes are never the target of a connection,
            // therefore are always guaranteed to be at the start of a connectivity graph, and thus at depth zero).
            int nodeCount = digraph.TotalNodeCount;
            int nodeIdx   = inputCount;
            int connIdx   = 0;

            int[] nodeDepthArr = depthInfo._nodeDepthArr;
            int[] srcIdArr     = connIdArrays._sourceIdArr;

            for (int currDepth = 0; currDepth < graphDepth; currDepth++)
            {
                // Scan for last node at the current depth.
                for (; nodeIdx < nodeCount && nodeDepthArr[nodeIdx] == currDepth; nodeIdx++)
                {
                    ;
                }

                // Scan for last connection at the current depth.
                for (; connIdx < srcIdArr.Length && nodeDepthArr[srcIdArr[connIdx]] == currDepth; connIdx++)
                {
                    ;
                }

                // Store node and connection end indexes for the layer.
                layerInfoArr[currDepth] = new LayerInfo(nodeIdx, connIdx);
            }

            // Construct and return.
            return(new DirectedGraphAcyclic(
                       inputCount, outputCount, nodeCount,
                       connIdArrays,
                       layerInfoArr,
                       outputNodeIdxArr));
        }