/// <summary> /// Computes the "stress" of the current layout of the given graph: /// /// stress = sum_{(u,v) in V} D(u,v)^(-2) (d(u,v) - D(u,v))^2 /// /// where: /// V is the set of nodes /// d(u,v) is the euclidean distance between the centers of nodes u and v /// D(u,v) is the graph-theoretic path length between u and v - scaled by average edge length. /// /// The idea of “stress” in graph layout is that nodes that are immediate neighbors should be closer /// together than nodes that are a few hops apart (i.e. that have path length>1). More generally /// the distance between nodes in the drawing should be proportional to the path length between them. /// The lower the “stress” score of a particular graph layout the better it conforms to this ideal. /// /// </summary> /// <param name="graph"></param> /// <returns></returns> public static double Stress(GeometryGraph graph) { ValidateArg.IsNotNull(graph, "graph"); double stress = 0; if (graph.Edges.Count == 0) { return(stress); } var apd = new AllPairsDistances(graph); apd.Run(); var D = apd.Result; double l = graph.Edges.Average(e => e.Length); int i = 0; foreach (var u in graph.Nodes) { int j = 0; foreach (var v in graph.Nodes) { if (i != j) { double duv = (u.Center - v.Center).Length; double Duv = l * D[i][j]; double d = Duv - duv; stress += d * d / (Duv * Duv); } ++j; } ++i; } return(stress); }
/// <summary> /// Layouts a connected graph with Multidimensional Scaling, using /// shortest-path distances as Euclidean target distances. /// </summary> /// <param name="geometryGraph">A graph.</param> /// <param name="settings">The settings for the algorithm.</param> /// <param name="x">Coordinate vector.</param> /// <param name="y">Coordinate vector.</param> internal static void LayoutGraphWithMds(GeometryGraph geometryGraph, MdsLayoutSettings settings, out double[] x, out double[] y) { x = new double[geometryGraph.Nodes.Count]; y = new double[geometryGraph.Nodes.Count]; if (geometryGraph.Nodes.Count == 0) { return; } if (geometryGraph.Nodes.Count == 1) { x[0] = y[0] = 0; return; } int k = Math.Min(settings.PivotNumber, geometryGraph.Nodes.Count); int iter = settings.GetNumberOfIterationsWithMajorization(geometryGraph.Nodes.Count); double exponent = settings.Exponent; var pivotArray = new int[k]; PivotDistances pivotDistances = new PivotDistances(geometryGraph, false, pivotArray); pivotDistances.Run(); double[][] c = pivotDistances.Result; MultidimensionalScaling.LandmarkClassicalScaling(c, out x, out y, pivotArray); ScaleToAverageEdgeLength(geometryGraph, x, y); if (iter > 0) { AllPairsDistances apd = new AllPairsDistances(geometryGraph, false); apd.Run(); double[][] d = apd.Result; double[][] w = MultidimensionalScaling.ExponentialWeightMatrix(d, exponent); // MultidimensionalScaling.DistanceScaling(d, x, y, w, iter); MultidimensionalScaling.DistanceScalingSubset(d, x, y, w, iter); } }
/// <summary> /// Computes the "stress" of the current layout of the given graph: /// /// stress = sum_{(u,v) in V} D(u,v)^(-2) (d(u,v) - D(u,v))^2 /// /// where: /// V is the set of nodes /// d(u,v) is the euclidean distance between the centers of nodes u and v /// D(u,v) is the graph-theoretic path length between u and v - scaled by average edge length. /// /// The idea of “stress” in graph layout is that nodes that are immediate neighbors should be closer /// together than nodes that are a few hops apart (i.e. that have path length>1). More generally /// the distance between nodes in the drawing should be proportional to the path length between them. /// The lower the “stress” score of a particular graph layout the better it conforms to this ideal. /// /// </summary> /// <param name="graph"></param> /// <returns></returns> public static double Stress(GeometryGraph graph) { ValidateArg.IsNotNull(graph, "graph"); double stress = 0; if (graph.Edges.Count == 0) { return stress; } var apd = new AllPairsDistances(graph, false); apd.Run(); var D = apd.Result; double l = graph.Edges.Average(e => e.Length); int i = 0; foreach (var u in graph.Nodes) { int j = 0; foreach (var v in graph.Nodes) { if (i != j) { double duv = (u.Center - v.Center).Length; double Duv = l * D[i][j]; double d = Duv - duv; stress += d * d / (Duv * Duv); } ++j; } ++i; } return stress; }
/// <summary> /// Layouts a connected graph with Multidimensional Scaling, using /// shortest-path distances as Euclidean target distances. /// </summary> /// <param name="geometryGraph">A graph.</param> /// <param name="settings">The settings for the algorithm.</param> /// <param name="x">Coordinate vector.</param> /// <param name="y">Coordinate vector.</param> internal static void LayoutGraphWithMds(GeometryGraph geometryGraph, MdsLayoutSettings settings, out double[] x, out double[] y) { x = new double[geometryGraph.Nodes.Count]; y = new double[geometryGraph.Nodes.Count]; if (geometryGraph.Nodes.Count == 0) return; if (geometryGraph.Nodes.Count == 1) { x[0] = y[0] = 0; return; } int k = Math.Min(settings.PivotNumber, geometryGraph.Nodes.Count); int iter = settings.GetNumberOfIterationsWithMajorization(geometryGraph.Nodes.Count); double exponent = settings.Exponent; var pivotArray = new int[k]; PivotDistances pivotDistances = new PivotDistances(geometryGraph, false, pivotArray); pivotDistances.Run(); double[][] c = pivotDistances.Result; MultidimensionalScaling.LandmarkClassicalScaling(c, out x, out y, pivotArray); ScaleToAverageEdgeLength(geometryGraph, x, y); if (iter > 0) { AllPairsDistances apd = new AllPairsDistances(geometryGraph, false); apd.Run(); double[][] d = apd.Result; double[][] w = MultidimensionalScaling.ExponentialWeightMatrix(d, exponent); // MultidimensionalScaling.DistanceScaling(d, x, y, w, iter); MultidimensionalScaling.DistanceScalingSubset(d, x, y, w, iter); } }