|
|
@@ -0,0 +1,79 @@
|
|
|
+import SugiyamaOptimizerStep, {Context} from "../../abstract/sugiyama-based/SugiyamaOptimizerStep.js";
|
|
|
+import OptimizerError from "../../errors/OptimizerError.js";
|
|
|
+import XDirectedGraph from "../../../graphs/types/XDirectedGraph.js";
|
|
|
+import XVertex from "../../../graphs/types/XVertex.js";
|
|
|
+
|
|
|
+export default class LayerAssignmentStep extends SugiyamaOptimizerStep {
|
|
|
+ process(context: Context): Context {
|
|
|
+ const { acyclicGraph } = context;
|
|
|
+
|
|
|
+ if(!acyclicGraph)
|
|
|
+ throw new OptimizerError("Directed acyclic graph is null or undefined");
|
|
|
+
|
|
|
+ context.layers = this.assignLayers(acyclicGraph);
|
|
|
+
|
|
|
+ console.log(context.layers)
|
|
|
+
|
|
|
+ return super.process(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected assignLayers(graph: XDirectedGraph): XVertex[][] {
|
|
|
+ const topologicalOrder = this.topologicalSort(graph);
|
|
|
+
|
|
|
+ const layers = new Map<XVertex, number>()
|
|
|
+
|
|
|
+ for(const vertex of topologicalOrder) {
|
|
|
+ const inputs = graph.getInputs(vertex.id);
|
|
|
+
|
|
|
+ if(inputs.length == 0) {
|
|
|
+ layers.set(vertex, 0);
|
|
|
+ } else {
|
|
|
+ const maxInputsLayer = Math.max(
|
|
|
+ ...inputs.map((input) => layers.get(input) ?? 0)
|
|
|
+ );
|
|
|
+ layers.set(vertex, maxInputsLayer + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const maxLayer = Math.max(...layers.values());
|
|
|
+ const result: XVertex[][] = Array.from({ length: maxLayer + 1 }, () => []);
|
|
|
+
|
|
|
+ for(const [vertex, layer] of layers)
|
|
|
+ result[layer]!.push(vertex);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private topologicalSort(graph: XDirectedGraph): XVertex[] {
|
|
|
+ const degreeMap = new Map<XVertex, number>();
|
|
|
+
|
|
|
+ for(const vertex of graph.vertices)
|
|
|
+ degreeMap.set(vertex, graph.getInputs(vertex.id).length);
|
|
|
+
|
|
|
+ const stack: XVertex[] = [];
|
|
|
+
|
|
|
+ for(const [vertex, degree] of degreeMap)
|
|
|
+ if(degree == 0)
|
|
|
+ stack.push(vertex);
|
|
|
+
|
|
|
+ const result: XVertex[] = [];
|
|
|
+
|
|
|
+ while(stack.length > 0) {
|
|
|
+ const curr = stack.shift()!;
|
|
|
+ result.push(curr);
|
|
|
+
|
|
|
+ for(const neighbour of graph.getOutputs(curr.id)) {
|
|
|
+ const newDegree = (degreeMap.get(neighbour) ?? 0) - 1;
|
|
|
+ degreeMap.set(neighbour, newDegree);
|
|
|
+
|
|
|
+ if(newDegree == 0)
|
|
|
+ stack.push(neighbour);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(result.length != graph.vertices.length)
|
|
|
+ throw new OptimizerError("Cycle is detected..")
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+}
|