Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
KahnTopologicalSort.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Algo/Sort.h"
6#include "Algo/Unique.h"
7#include "Containers/Array.h"
8#include "Containers/ArrayView.h"
9#include "Containers/Map.h"
10#include "Containers/Set.h"
11#include "Misc/EnumClassFlags.h"
12#include "Templates/Invoke.h"
13#include "Templates/UnrealTemplate.h"
14#include "Traits/ElementType.h"
15
16namespace AlgoImpl
17{
18 /**
19 * KahnTopologicalSort converts vertices and edges from ElementType to indexes of the vertex in the original
20 * UniqueRange of ElementType. FKahnHandle is how vertices are represented during the graph algorithm
21 */
22 typedef int32 FKahnHandle;
23
24 /** Scratch variables shared across multiple calls to FindMostIndependentMutuallyReachableVertexSet */
26 {
33 };
34 /** Some variables shared with subfunctions */
36 {
42 };
43
44 // Helper functions
45 template <typename RangeType, typename GetElementDependenciesType>
46 void KahnTopologicalSort_CreateWorkingGraph(FKahnContext& Context, RangeType& UniqueRange,
47 GetElementDependenciesType GetElementDependencies, TSet<FKahnHandle>& OutInitialIndependents);
49}
50
51namespace Algo
52{
53 /** Flags for behavior of TopologicalSort; see the function comment in TopologicalSort.h */
55 {
56 None,
58 };
60
61 /** Public entrypoint. Implements Algo::TopologicalSort using the Kahn Topological Sort algorithm. */
62 template <typename RangeType, typename GetElementDependenciesType>
63 bool KahnTopologicalSort(RangeType& UniqueRange, GetElementDependenciesType GetElementDependencies,
64 ETopologicalSort Flags)
65 {
66 using namespace AlgoImpl;
67 using ElementType = typename TElementType<RangeType>::Type;
68
73
74 // Initialize the graph search
85 {
87 }
88
89 // Sort graph so that vertices with no dependencies (leaves) always go first.
90 while (RemainingVertices.Num() > 0)
91 {
92 if (IndependentVertices.Num() == 0)
93 {
94 // If there are no independent vertices then there is a cycle in the graph
96 {
97 return false;
98 }
99
100 // When all remaining vertices are in cycles, find the maximal MutuallyReachableVertexSet
101 // (a cycle is an MRVS) that is independent of all the other MRVS's.
103 check(!MRVS.IsEmpty());
104 for (FKahnHandle Vertex : MRVS)
105 {
106 // Add all the vertices in the MutuallyReachableVertexSet to the SortedRange in arbitrary order
108 // Mark that we should no longer consider any vertices in the MRVS when looking for NewIndependentVertices;
109 // they have already been removed
111 }
112 }
113
116 {
118 {
121 {
122 // Don't read or write dependencycount for referencers we removed due to a cycle
123 continue;
124 }
126 if (--ReferencerDependencyCount == 0)
127 {
129 }
130 }
131#if DO_CHECK
133#endif
135 check(RemainingVertices.Num() == RemainingNum - 1); // Confirm Vertex was in RemainingVertices
137 }
139 }
140
141 // Shuffle the input according to the SortOrder found by the graph search
145 {
147 }
148 int32 SourceIndex = 0;
150 {
152 }
153
154 return true;
155 }
156}
157
158namespace AlgoImpl
159{
160 /**
161 * Convert UniqueRange and GetElementDependencies into handles,
162 * dependency count, dependencies, and referencers
163 */
164 template <typename RangeType, typename GetElementDependenciesType>
165 inline void KahnTopologicalSort_CreateWorkingGraph(FKahnContext& Context, RangeType& UniqueRange,
166 GetElementDependenciesType GetElementDependencies, TSet<FKahnHandle>& OutInitialIndependents)
167 {
168 using ElementType = typename TElementType<RangeType>::Type;
169
173
178 for (const ElementType& Element : UniqueRange)
179 {
183 }
184
188 Handle = 0;
189 for (const ElementType& Element : UniqueRange)
190 {
193
195 {
198 {
200 }
201 }
206 if (NumUniqueDependencies == 0)
207 {
209 }
211 {
214 }
215 ++Handle;
216 }
217 }
218
219 /**
220 * Called when there is a MutuallyReachableVertexSet (aka no vertices are independent). It returns the most-independent
221 * maximal MutuallyReachableVertexSet.
222 */
224 {
225 if (!Context.MRVSContext.IsSet())
226 {
227 int32 NumVertices = Context.RemainingVertices.Num();
228 FMutuallyReachableVertexSetContext& InitContext = Context.MRVSContext.Emplace();
229 InitContext.VisitedReferences.Reserve(NumVertices);
230 InitContext.VisitedDependencies.Reserve(NumVertices);
231 InitContext.Culprits.Reserve(NumVertices);
232 InitContext.Stack.Reserve(NumVertices);
233 InitContext.Cycle.Reserve(NumVertices);
234 }
235 const TArray<TArray<FKahnHandle>>& Dependencies = Context.Dependencies;
236 const TArray<TArray<FKahnHandle>>& Referencers = Context.Referencers;
237 FMutuallyReachableVertexSetContext& MRVSContext = *Context.MRVSContext;
238 TSet<FKahnHandle>& VisitedReferences = MRVSContext.VisitedReferences;
239 TSet<FKahnHandle>& VisitedDependencies = MRVSContext.VisitedDependencies;
240 TSet<FKahnHandle>& MaximalMRVS = MRVSContext.MaximalMRVS;
241 TSet<FKahnHandle>& Culprits = MRVSContext.Culprits;
242 TArray<FKahnHandle>& Stack = MRVSContext.Stack;
243 TArray<FKahnHandle>& Cycle = MRVSContext.Cycle;
244
245 // Copy Remaining Vertices into Culprits; we will be modifying the list
246 // as we search for the most-independent MRVS.
247 Culprits.Reset();
248 Culprits.Append(Context.RemainingVertices);
249
250 // Start from an arbitrary vertex
251 check(!Culprits.IsEmpty());
252 FKahnHandle StartingVertex = *Culprits.CreateIterator();
253
254 int32 StartingVertexLoopCount = 0;
255 while (StartingVertex != INDEX_NONE)
256 {
257 // Find a cycle by arbitrarily following dependencies until we revisit a vertex.
258 VisitedReferences.Reset();
259 Stack.Reset();
260 VisitedReferences.Add(StartingVertex);
261 Stack.Add(StartingVertex);
262 FKahnHandle CycleWitnessVertex = StartingVertex;
263 bool bCycleWitnessWasAlreadyInStack = false;
264 while (!bCycleWitnessWasAlreadyInStack)
265 {
266 FKahnHandle NextVertex = INDEX_NONE;
267 for (FKahnHandle Dependency : Dependencies[CycleWitnessVertex])
268 {
269 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
270 {
271 NextVertex = Dependency;
272 break;
273 }
274 }
275 // Assert a dependency is found. This function is only called when every vertex has remaining dependencies.
276 check(NextVertex != INDEX_NONE);
277 CycleWitnessVertex = NextVertex;
278 VisitedReferences.Add(CycleWitnessVertex, &bCycleWitnessWasAlreadyInStack);
279 Stack.Add(CycleWitnessVertex);
280 }
281
282 // The cycle is everything on the stack after the first occurrence of CycleWitnessVertex.
283 // Cycle stacks will be like [7,7] or [7,3,2,7], always starting and ending with a vertex
284 // Pop the second occurrence of CycleWitnessVertex off the stack
285 check(Stack.Num() >= 2);
286 Stack.Pop(false /* bAllowShrinking */);
287 // Find the start of the Cycle
288 int32 StartIndex = Stack.Num() - 1;
289 while (StartIndex >= 0 && Stack[StartIndex] != CycleWitnessVertex)
290 {
291 --StartIndex;
292 }
293 check(StartIndex >= 0);
294 Cycle.Reset();
295 Cycle.Append(TArrayView<FKahnHandle>(Stack).Mid(StartIndex));
296
297 // We now have a cycle, which is an MRVS that may or may not be maximal. Expand it to a maximal MRVS by
298 // intersecting everything reachable from it with everything from which it is reachable.
299
300 // Find all references to the cycle. We start from VisitedReferences and Stack that we created when
301 // finding the cycle, since everything in that set is a referencer of the cycle
302 while (!Stack.IsEmpty())
303 {
304 FKahnHandle Vertex = Stack.Pop(false /* bAllowShrinking */);
305 for (FKahnHandle Referencer : Referencers[Vertex])
306 {
307 if (Culprits.Contains(Referencer)) // Only consider edges to remaining vertices
308 {
309 bool bAlreadyVisited;
310 VisitedReferences.Add(Referencer, &bAlreadyVisited);
311 if (!bAlreadyVisited)
312 {
313 Stack.Add(Referencer);
314 }
315 }
316 }
317 }
318
319 // Find all dependencies from the cycle.
320 VisitedDependencies.Reset();
321 Stack.Reset();
322 for (FKahnHandle Vertex : Cycle)
323 {
324 VisitedDependencies.Add(Vertex);
325 Stack.Add(Vertex);
326 }
327 while (!Stack.IsEmpty())
328 {
329 FKahnHandle Vertex = Stack.Pop(false /* bAllowShrinking */);
330 for (FKahnHandle Dependency : Dependencies[Vertex])
331 {
332 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
333 {
334 bool bAlreadyVisited;
335 VisitedDependencies.Add(Dependency, &bAlreadyVisited);
336 if (!bAlreadyVisited)
337 {
338 Stack.Add(Dependency);
339 }
340 }
341 }
342 }
343
344 MaximalMRVS = VisitedDependencies.Intersect(VisitedReferences);
345
346 // We now have a maximal MRVS, but there may be multiple maximal MRVS's, and we need to find the most independent
347 // one. Remove all elements of VisitedReferences from the remaining vertices in Culprits. We will not need to consider
348 // them when searching the graph for the more independent MRVS. Once removed, look for any dependencies from
349 // the MaximalMRVS to a remaining culprit. If any exist, then we can follow them to find a new maximal MRVS that
350 // is more independent than the current maximal MRVS.
351 for (FKahnHandle Vertex : VisitedReferences)
352 {
353 Culprits.Remove(Vertex);
354 }
355
356 StartingVertex = INDEX_NONE;
357 for (FKahnHandle Vertex : MaximalMRVS)
358 {
359 for (FKahnHandle Dependency : Dependencies[Vertex])
360 {
361 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
362 {
363 StartingVertex = Dependency;
364 break;
365 }
366 }
367 if (StartingVertex != INDEX_NONE)
368 {
369 break;
370 }
371 }
372
373 // If we found a new StartingVertex, the loop will continue and follow it to the new MaximalMRVS
374 // Otherwise the current MaximalMRVS is our returned value
375 checkf(StartingVertexLoopCount++ < Context.RemainingVertices.Num(), TEXT("Infinite loop in FindMostIndependentMutuallyReachableVertexSet"));
376 }
377
378 return MaximalMRVS;
379 }
380}
#define check(expr)
#define checkf(expr, format,...)
#define DO_CHECK
Definition Build.h:311
@ INDEX_NONE
#define ENUM_CLASS_FLAGS(Enum)
Definition Heapify.h:12
bool KahnTopologicalSort(RangeType &UniqueRange, GetElementDependenciesType GetElementDependencies, ETopologicalSort Flags)
void KahnTopologicalSort_CreateWorkingGraph(FKahnContext &Context, RangeType &UniqueRange, GetElementDependenciesType GetElementDependencies, TSet< FKahnHandle > &OutInitialIndependents)
const TSet< FKahnHandle > & FindMostIndependentMutuallyReachableVertexSet(FKahnContext &Context)
TArray< int32 > DependencyCount
TArray< TArray< FKahnHandle > > Dependencies
TSet< FKahnHandle > RemainingVertices
TArray< TArray< FKahnHandle > > Referencers
TOptional< FMutuallyReachableVertexSetContext > MRVSContext