|
@@ -1,8 +1,19 @@
|
|
|
import LayerAssignmentStepError from "../../errors/optimizer/LayerAssignmentStepError.js";
|
|
import LayerAssignmentStepError from "../../errors/optimizer/LayerAssignmentStepError.js";
|
|
|
|
|
+import Edge from "../../graph/Edge.js";
|
|
|
|
|
+import Graph from "../../graph/Graph.js";
|
|
|
|
|
+import Node from "../../graph/Node.js";
|
|
|
|
|
+import GraphService from "../../services/GraphService.js";
|
|
|
import AlgorithmStep from "../AlgorithmStep.js";
|
|
import AlgorithmStep from "../AlgorithmStep.js";
|
|
|
-import { SiguiyamaContext } from "./SiguiyamaContext.js";
|
|
|
|
|
|
|
+import { GraphLayering, SiguiyamaContext } from "./SiguiyamaContext.js";
|
|
|
|
|
|
|
|
|
|
+// TODO
|
|
|
export default class LayerAssignmentStep implements AlgorithmStep<SiguiyamaContext> {
|
|
export default class LayerAssignmentStep implements AlgorithmStep<SiguiyamaContext> {
|
|
|
|
|
+ private readonly _graphService: GraphService;
|
|
|
|
|
+
|
|
|
|
|
+ public constructor(graphService: GraphService) {
|
|
|
|
|
+ this._graphService = graphService;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
run(context: SiguiyamaContext): void {
|
|
run(context: SiguiyamaContext): void {
|
|
|
const { graph, feedbackSet } = context;
|
|
const { graph, feedbackSet } = context;
|
|
|
|
|
|
|
@@ -10,7 +21,82 @@ export default class LayerAssignmentStep implements AlgorithmStep<SiguiyamaConte
|
|
|
throw new LayerAssignmentStepError("Graph was not found!");
|
|
throw new LayerAssignmentStepError("Graph was not found!");
|
|
|
if(!feedbackSet)
|
|
if(!feedbackSet)
|
|
|
throw new LayerAssignmentStepError("Feedback set is undefined!");
|
|
throw new LayerAssignmentStepError("Feedback set is undefined!");
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
+ const initialLayering = this.longestPathAlgorithm(graph);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getSpanningTree(layering: NonNullable<SiguiyamaContext["layering"]>, dag: Graph<Node, Edge<Node>>) : Graph<Node, Edge<Node>> {
|
|
|
|
|
+ throw new Error("Not implemented!")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getNodeRank(layering: GraphLayering, nodeId: Node["id"]) : number | null {
|
|
|
|
|
+ for(let i = 0; i < layering.length; i++) {
|
|
|
|
|
+ const layer = layering[i]!;
|
|
|
|
|
+ if(layer.includes(nodeId))
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getEdgeSpan(layering: GraphLayering, edge: Edge<Node>) : number | null {
|
|
|
|
|
+ const { from, to } = edge;
|
|
|
|
|
+ const fromRank = this.getNodeRank(layering, from), toRank = this.getNodeRank(layering, to);
|
|
|
|
|
+
|
|
|
|
|
+ if(fromRank == null || toRank == null)
|
|
|
|
|
+ return null;
|
|
|
|
|
+
|
|
|
|
|
+ return fromRank - toRank;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private isTightEdge(layering: GraphLayering, edge: Edge<Node>) : boolean {
|
|
|
|
|
+ return this.getEdgeSpan(layering, edge) === 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private isLongEdge(layering: GraphLayering, edge: Edge<Node>) : boolean {
|
|
|
|
|
+ const span = this.getEdgeSpan(layering, edge);
|
|
|
|
|
+ return span ? span > 1 : false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private longestPathAlgorithm(dag: Graph<Node, Edge<Node>>) : NonNullable<SiguiyamaContext["layering"]> {
|
|
|
|
|
+ const nodes = dag.getNodes();
|
|
|
|
|
+
|
|
|
|
|
+ const alreadyLayeredNodeIds = new Set<Node["id"]>();
|
|
|
|
|
+ const underCurrentLayerNodeIds = new Set<Node["id"]>();
|
|
|
|
|
+ const layering: NonNullable<SiguiyamaContext["layering"]> = [];
|
|
|
|
|
+
|
|
|
|
|
+ let currentLayerIndex = 0;
|
|
|
|
|
+
|
|
|
|
|
+ while(alreadyLayeredNodeIds.size !== nodes.length) {
|
|
|
|
|
+ let isNodeSelected = false;
|
|
|
|
|
+
|
|
|
|
|
+ for(const node of nodes) {
|
|
|
|
|
+ if(alreadyLayeredNodeIds.has(node.id))
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ const successors = dag.getNodeOutputs(node.id);
|
|
|
|
|
+ const isAllSuccessorsUnder = successors.every((node) => underCurrentLayerNodeIds.has(node.id));
|
|
|
|
|
+
|
|
|
|
|
+ if(!isAllSuccessorsUnder)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ const currentLayer = layering[currentLayerIndex];
|
|
|
|
|
+
|
|
|
|
|
+ if(!currentLayer)
|
|
|
|
|
+ layering[currentLayerIndex] = [node.id];
|
|
|
|
|
+ else
|
|
|
|
|
+ currentLayer.push(node.id);
|
|
|
|
|
+
|
|
|
|
|
+ alreadyLayeredNodeIds.add(node.id);
|
|
|
|
|
+ isNodeSelected = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!isNodeSelected) {
|
|
|
|
|
+ currentLayerIndex++;
|
|
|
|
|
+ alreadyLayeredNodeIds.forEach((id) => underCurrentLayerNodeIds.add(id));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return layering;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|