From bbecb8452292868372b92476b451ed0ad0f166cd Mon Sep 17 00:00:00 2001 From: Ilya Yaroshenko Date: Thu, 21 Nov 2019 13:56:25 +0700 Subject: [PATCH 1/2] add nogc tarjan algorithm --- source/mir/graph/package.d | 8 +++ source/mir/graph/tarjan.d | 94 ++++++++++++++++++++++++----------- source/mir/ndslice/topology.d | 4 +- source/mir/rc/array.d | 33 ++++++++++++ 4 files changed, 109 insertions(+), 30 deletions(-) diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d index 6bc31a6e..d1a31b1b 100644 --- a/source/mir/graph/package.d +++ b/source/mir/graph/package.d @@ -17,6 +17,7 @@ import mir.math.common: optmath; @optmath: import mir.series; +import mir.rc.array; import mir.ndslice.iterator: ChopIterator; /// @@ -26,6 +27,13 @@ alias Graph(I = uint, J = size_t) = Slice!(GraphIterator!(I, J)); /// alias GraphSeries(T, I = uint, J = size_t) = Series!(T*, GraphIterator!(I, J)); +/// +alias RCGraphIterator(I = uint, J = size_t) = ChopIterator!(RCI!size_t, RCI!uint); +/// +alias RCGraph(I = uint, J = size_t) = Slice!(RCGraphIterator!(I, J)); +/// +alias RCGraphSeries(T, I = uint, J = size_t) = Series!(RCI!T, RCGraphIterator!(I, J)); + /++ Param: aaGraph = graph that is represented as associative array diff --git a/source/mir/graph/tarjan.d b/source/mir/graph/tarjan.d index 78b9ea6b..43e0e668 100644 --- a/source/mir/graph/tarjan.d +++ b/source/mir/graph/tarjan.d @@ -32,6 +32,7 @@ The implementation is loop based. It does not use recursion and does not have st Complexity: worst-case `O(|V| + |E|)`. Params: + RC = nogc mode, refcounted output graph = components (ndslice) sorted in the direction of traversal of the graph. Each component is an array of indeces. Returns: components (ndslice of arrays of indexes) @@ -44,7 +45,7 @@ See_also: $(SUBREF utility, graph) +/ pragma(inline, false) -auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph) +auto tarjan(bool RC = false, G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph) if (isUnsigned!I) { import mir.utility: min; @@ -87,35 +88,49 @@ auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph) } } - bool[] onStack = new bool[graph.length]; - I[] stack; - IndexNode[] indeces; - LoopNode[] loopStack; - I index; + sizediff_t stackIndex; sizediff_t backStackIndex = graph.length; sizediff_t componentBackStackIndex = graph.length + 1; - if (__ctfe) + static if (RC) { - stack = new I[graph.length]; - indeces = new IndexNode[graph.length]; - loopStack = new LoopNode[componentBackStackIndex]; + import mir.rc.array; + auto onStack = RCArray!bool(graph.length); + auto stack = RCArray!I(graph.length, true); + auto indeces = RCArray!IndexNode(graph.length, true); + auto loopStack = RCArray!LoopNode(componentBackStackIndex, true); } else { - () @trusted { - import std.array: uninitializedArray; + I[] stack; + IndexNode[] indeces; + LoopNode[] loopStack; + + bool[] onStack = new bool[graph.length]; + if (__ctfe) + { + + stack = new I[graph.length]; + indeces = new IndexNode[graph.length]; + loopStack = new LoopNode[componentBackStackIndex]; + } + else + { + () @trusted { + import std.array: uninitializedArray; - stack = uninitializedArray!(I[])(graph.length); - indeces = uninitializedArray!(IndexNode[])(graph.length); - loopStack = uninitializedArray!(LoopNode[])(componentBackStackIndex); - } (); + stack = uninitializedArray!(I[])(graph.length); + indeces = uninitializedArray!(IndexNode[])(graph.length); + loopStack = uninitializedArray!(LoopNode[])(componentBackStackIndex); + } (); + } } foreach(ref node; indeces) node.index = undefined; + I index; foreach(size_t v; 0u .. graph.length) { if (indeces[v].isUndefined) @@ -191,26 +206,42 @@ auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph) } } - S[] pairwiseIndex; - if (__ctfe) + const indexLength = graph.length + 1 - componentBackStackIndex + 1; + static if (RC) { - pairwiseIndex = new S[graph.length - componentBackStackIndex + 1]; + auto pairwiseIndex = RCArray!S(indexLength, true); } else { - () @trusted { - import std.array: uninitializedArray; - pairwiseIndex = uninitializedArray!(S[])(graph.length + 1 - componentBackStackIndex + 1); - } (); + S[] pairwiseIndex; + if (__ctfe) + { + pairwiseIndex = new S[indexLength]; + } + else + { + () @trusted { + import std.array: uninitializedArray; + pairwiseIndex = uninitializedArray!(S[])(indexLength); + } (); + } } - foreach (i, ref e; loopStack[componentBackStackIndex .. $]) + foreach (i, ref e; loopStack[][componentBackStackIndex .. $]) { pairwiseIndex[i] = e.index; } pairwiseIndex[$ - 1] = cast(I) graph.length; import mir.ndslice.topology: chopped; - return (()@trusted {return stack.ptr; }()).chopped(pairwiseIndex); + static if (RC) + { + import core.lifetime: move; + return chopped(RCI!I(stack.move), pairwiseIndex.asSlice); + } + else + { + return (()@trusted {return stack.ptr; }()).chopped(pairwiseIndex); + } } /++ @@ -241,14 +272,21 @@ pure version(mir_test) unittest "11": [], ].graphSeries; - auto components = gs.data.tarjan; - assert(components == [ + static immutable result = [ [0], [1, 2, 5, 4, 3, 6], [10], [7, 8, 9], - [11]]); + [11]]; + + // chec GC interface + auto components = gs.data.tarjan; + assert(components == result); + // check @nogc interface + // Note: The lambda function is used here to show @nogc mode explicitly. + auto rccomponents = (() @nogc => gs.data.tarjan!true )(); + assert(rccomponents == result); } /++ diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d index 0a725dbe..6143dc1d 100644 --- a/source/mir/ndslice/topology.d +++ b/source/mir/ndslice/topology.d @@ -3209,7 +3209,7 @@ in assert(b); } do { - + import core.lifetime: move; sizediff_t length = bounds._lengths[0] <= 1 ? 0 : bounds._lengths[0] - 1; static if (hasLength!Sliceable) { @@ -3222,7 +3222,7 @@ do { } } - return typeof(return)([size_t(length)], ChopIterator!(Iterator, Sliceable)(bounds._iterator, sliceable)); + return typeof(return)([size_t(length)], ChopIterator!(Iterator, Sliceable)(bounds._iterator.move, sliceable.move)); } /// ditto diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d index 6fe34c0a..8d15d6f4 100644 --- a/source/mir/rc/array.d +++ b/source/mir/rc/array.d @@ -387,6 +387,9 @@ Thread safe reference counting iterator. +/ struct mir_rci(T) { + import mir.ndslice.slice: Slice; + import mir.ndslice.iterator: IotaIterator; + /// T* _iterator; @@ -479,6 +482,36 @@ struct mir_rci(T) return _iterator[index]; } + /// Returns: slice type of `Slice!(IotaIterator!size_t)` + Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) @safe scope const + if (dimension == 0) + in + { + assert(i <= j, "RCI!T.opSlice!0: the left opSlice boundary must be less than or equal to the right bound."); + } + body + { + return typeof(return)(j - i, typeof(return).Iterator(i)); + } + + /// Returns: ndslice on top of the refcounted iterator + auto opIndex(Slice!(IotaIterator!size_t) slice) + { + import core.lifetime: move; + auto it = this; + it += slice._iterator._index; + return Slice!(RCI!T)(slice.length, it.move); + } + + /// ditto + auto opIndex(Slice!(IotaIterator!size_t) slice) const + { + import core.lifetime: move; + auto it = lightConst; + it += slice._iterator._index; + return Slice!(RCI!(const T))(slice.length, it.move); + } + /// void opUnary(string op)() scope if (op == "--" || op == "++") From a0d3c5094f1ef8bc5614f3ad58b37298641d770f Mon Sep 17 00:00:00 2001 From: Ilya Yaroshenko Date: Thu, 21 Nov 2019 16:02:52 +0700 Subject: [PATCH 2/2] add rcgraph --- source/mir/graph/package.d | 83 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d index d1a31b1b..d2e94967 100644 --- a/source/mir/graph/package.d +++ b/source/mir/graph/package.d @@ -14,8 +14,6 @@ module mir.graph; import mir.math.common: optmath; -@optmath: - import mir.series; import mir.rc.array; import mir.ndslice.iterator: ChopIterator; @@ -34,13 +32,20 @@ alias RCGraph(I = uint, J = size_t) = Slice!(RCGraphIterator!(I, J)); /// alias RCGraphSeries(T, I = uint, J = size_t) = Series!(RCI!T, RCGraphIterator!(I, J)); +private static immutable exc_msg = "graphSeries: graph should contains keys for all vertixes"; +version(D_Exceptions) +{ + private static immutable exception = new Exception(exc_msg); +} + /++ -Param: +Params: aaGraph = graph that is represented as associative array Returns: A graph series composed of keys (sorted `.index`) and arrays of indeces (`.data`) Complexity: `O(log(V) (V + E))` +/ +@optmath GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aaGraph) { import mir.array.allocation: array; @@ -62,7 +67,13 @@ GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aa { import mir.ndslice.sorting: transitionIndex; auto index = keys.transitionIndex(elem); - assert(index < keys.length, "graphSeries: aaGraph should contains keys for all vertixes"); + if (index >= keys.length) + { + version(D_Exceptions) + throw exception; + else + assert(0, exc_msg); + } data[dataIndex++] = cast(I) index; } } @@ -87,3 +98,67 @@ pure version(mir_test) unittest [1], // c ]); } + +/++ +Params: + graph = graph that is represented a series +Returns: + A graph as an arrays of indeces +Complexity: `O(log(V) (V + E))` ++/ +@optmath +RCGraph!(I, J) rcgraph(I = uint, J = size_t, KeyIterator, RangeIterator)(Series!(KeyIterator, RangeIterator) graph) +{ + import mir.array.allocation: array; + import mir.ndslice.sorting; + import mir.ndslice; + auto scopeGraph = graph.lightScope; + auto keys = scopeGraph.index; + auto graphData = scopeGraph.data; + size_t dataLength; + foreach (ref v; graphData) + dataLength += v.length; + auto data = rcslice!I(dataLength); + auto components = rcslice!J(keys.length + 1); + size_t dataIndex; + + foreach (i; 0 .. keys.length) + { + components[i] = cast(J) dataIndex; + foreach(ref elem; graphData[i]) + { + import mir.ndslice.sorting: transitionIndex; + auto index = keys.transitionIndex(elem); + if (index >= keys.length) + { + version(D_Exceptions) + throw exception; + else + assert(0, exc_msg); + } + data[dataIndex++] = cast(I) index; + } + } + components[keys.length] = dataIndex; + return data._iterator.chopped(components); +} + +/// +@safe pure @nogc version(mir_test) +unittest +{ + static immutable keys = ["a", "b", "c"]; + static immutable data = [ + ["b", "c"], + ["a"], + ["b"], + ]; + + static immutable graphValue = [ + [1, 2], // a + [0], // b + [1], // c + ]; + + assert (series(keys, data).rcgraph == graphValue); +}