public void ConnectionSorterBenchmark() { for (int i = 0; i < _dataArr.Length; i++) { ConnectionData connData = _dataArr[i]; ConnectionSorter <double> .Sort(connData._connIdArrays, connData._weightArr); } }
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]; } }
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)); }