Explorar el Código

NEW: layer assignment step added

Pavel Zhigalov hace 2 semanas
padre
commit
58f4b34a07

+ 1 - 1
src/index.ts

@@ -9,6 +9,6 @@ const deserializer = new JointJsonDeserializer();
 const graph = deserializer.deserialize(String(readFileSync("./data/graph.json")));
 const graphService = new DefaultGraphService()
 
-const optimizer = new SigiuyamaAlgorithm().addStep(new CycleRemoveStep(graphService)).addStep(new LayerAssignmentStep());
+const optimizer = new SigiuyamaAlgorithm().addStep(new CycleRemoveStep(graphService)).addStep(new LayerAssignmentStep(graphService));
 
 optimizer.run(graph);

+ 8 - 2
src/v1/graph/Node.ts

@@ -4,16 +4,22 @@ export default class Node {
 	public readonly id: string;
 	public x: number;
 	public y: number;
+	public width: number;
+	public height: number;
 
-	public constructor(x: number, y: number, id?: string) {
+	public constructor(x: number, y: number, width: number, height: number, id?: string) {
 		this.id = id || crypto.randomUUID();
 		this.x = x;
 		this.y = y;
+		this.width = width;
+		this.height = height;
 	}
 }
 
 export const NodeSchema = object({
 	id: string(),
 	x: number(),
-	y: number()
+	y: number(),
+	width: number(),
+	height: number()
 })

+ 3 - 0
src/v1/optimizer/siguiyama/CycleRemoveStep.ts

@@ -6,6 +6,9 @@ import GraphService from "../../services/GraphService.js";
 import AlgorithmStep from "../AlgorithmStep.js";
 import { SiguiyamaContext } from "./SiguiyamaContext.js";
 
+/**
+ * Greedy Cycle Removal Algoirthm
+ */
 export default class CycleRemoveStep implements AlgorithmStep<SiguiyamaContext> {
 	private readonly _graphService: GraphService;
 

+ 88 - 2
src/v1/optimizer/siguiyama/LayerAssignmentStep.ts

@@ -1,8 +1,19 @@
 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 { SiguiyamaContext } from "./SiguiyamaContext.js";
+import { GraphLayering, SiguiyamaContext } from "./SiguiyamaContext.js";
 
+// TODO
 export default class LayerAssignmentStep implements AlgorithmStep<SiguiyamaContext> {
+	private readonly _graphService: GraphService;
+
+	public constructor(graphService: GraphService) {
+		this._graphService = graphService;
+	}
+	
 	run(context: SiguiyamaContext): void {
 		const { graph, feedbackSet } = context;
 
@@ -10,7 +21,82 @@ export default class LayerAssignmentStep implements AlgorithmStep<SiguiyamaConte
 			throw new LayerAssignmentStepError("Graph was not found!");
 		if(!feedbackSet)
 			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;
 	}
 }

+ 6 - 2
src/v1/optimizer/siguiyama/SiguiyamaContext.ts

@@ -2,7 +2,11 @@ import Edge from "../../graph/Edge.js"
 import Node from "../../graph/Node.js"
 import { AlgorithmContext } from "../AlgorithmContext.js"
 
+export type FeedbackSet = Edge<Node>[];
+
+export type GraphLayering = Node["id"][][];
+
 export type SiguiyamaContext = AlgorithmContext & Partial<{
-	feedbackSet: Edge<Node>[],
-	layers: Node[][]
+	feedbackSet: FeedbackSet,
+	layering: GraphLayering;
 }>