fp.overlapr.algorithmen.StressMajorization.java Source code

Java tutorial

Introduction

Here is the source code for fp.overlapr.algorithmen.StressMajorization.java

Source

package fp.overlapr.algorithmen;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.ConjugateGradient;
import org.apache.commons.math3.linear.DefaultRealMatrixChangingVisitor;
import org.apache.commons.math3.linear.JacobiPreconditioner;
import org.apache.commons.math3.util.FastMath;

import fp.overlapr.typen.Graph;
import mdsj.StressMinimization;

/**
 * Implementiert die Stress Majorization - Technik
 * 
 * @author Frankford
 *
 */
public class StressMajorization {

    // Abruchkriterium des CG-Verfahrens
    private static final double TOL = 0.0001;

    // Iterationen des Stress Majorization Algorithmus
    private static final int ITER = 1;

    /**
     * Fhrt die Stress-Majorization durch. siehe: Gansner, Koren, North: Graph
     * Drawing by Stress Majorization, 2004
     * 
     * @param graph
     *            Graph, dessen Knoten-Positionen neu berechnet werden sollen
     * @param d
     *            Matrix, die die idealen Distanzen (d_ij) zwischen den Knoten
     *            enthlt
     * @return Matrix, die die neuen x- und y-Werte der einzelnen Knoten enthlt
     */
    public static double[][] doStressMajorization(Graph graph, double[][] d) {

        int iter = 0;

        // X holen
        Array2DRowRealMatrix X = new Array2DRowRealMatrix(graph.getKnotenAnzahl(), 2);
        for (int i = 0; i < graph.getKnotenAnzahl(); i++) {
            X.setEntry(i, 0, graph.getKnoten().get(i).getX());
            X.setEntry(i, 1, graph.getKnoten().get(i).getY());
        }

        // D holen
        Array2DRowRealMatrix D = new Array2DRowRealMatrix(d);

        // W berechnen
        Array2DRowRealMatrix W = new Array2DRowRealMatrix(D.getRowDimension(), D.getColumnDimension());
        W.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
            @Override
            public double visit(int row, int column, double value) {
                if (D.getEntry(row, column) == 0) {
                    return 0.0;
                } else {
                    return 1.0 / (D.getEntry(row, column) * D.getEntry(row, column));
                }
            }
        });

        // LW berechnen
        Array2DRowRealMatrix LW = new Array2DRowRealMatrix(D.getRowDimension(), D.getColumnDimension());
        LW.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
            @Override
            public double visit(int row, int column, double value) {
                if (row != column) {
                    return (-1) * W.getEntry(row, column);
                } else {

                    return value;
                }
            }
        });
        LW.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
            @Override
            public double visit(int row, int column, double value) {
                if (row == column) {

                    double sum = 0;

                    for (int k = 0; k < LW.getColumnDimension(); k++) {
                        if (k != row) {
                            sum = sum + W.getEntry(row, k);
                        }
                    }

                    return sum;
                } else {

                    return value;
                }
            }
        });

        double[][] x = null;

        while (iter < ITER) {

            iter++;

            // LX berechnen
            Array2DRowRealMatrix LX = new Array2DRowRealMatrix(D.getRowDimension(), D.getColumnDimension());
            LX.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
                @Override
                public double visit(int row, int column, double value) {
                    if (row != column) {

                        // norm 2
                        double term1 = FastMath.pow((X.getEntry(row, 0) - X.getEntry(column, 0)), 2);
                        double term2 = FastMath.pow((X.getEntry(row, 1) - X.getEntry(column, 1)), 2);

                        double abst = Math.sqrt(term1 + term2);

                        return (-1) * W.getEntry(row, column) * D.getEntry(row, column) * inv(abst);
                    } else {
                        return value;
                    }
                }
            });
            LX.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
                @Override
                public double visit(int row, int column, double value) {
                    if (row == column) {

                        double sum = 0;

                        for (int k = 0; k < LX.getColumnDimension(); k++) {
                            if (k != row) {
                                sum = sum + LX.getEntry(row, k);
                            }
                        }
                        return (-1) * sum;
                    } else {
                        return value;
                    }
                }
            });

            /*
             * Lineare Gleichungssysteme lsen
             */
            // x-Werte holen
            ArrayRealVector xWerte = new ArrayRealVector(X.getColumn(0));

            // y-Werte holen
            ArrayRealVector yWerte = new ArrayRealVector(X.getColumn(1));

            // b_x berechnen
            ArrayRealVector b_x = (ArrayRealVector) LX.operate(xWerte);

            // b_y berechnen
            ArrayRealVector b_y = (ArrayRealVector) LX.operate(yWerte);

            /*
             * CG-Verfahren anwenden
             */
            // neue x-Werte berechnen mittels PCG
            // xWerte = conjugateGradientsMethod(LW, b_x, xWerte);

            // neue y-Werte berechnen mittels PCG
            // yWerte = conjugateGradientsMethod(LW, b_y, yWerte);

            ConjugateGradient cg = new ConjugateGradient(Integer.MAX_VALUE, TOL, false);
            xWerte = (ArrayRealVector) cg.solve(LW, JacobiPreconditioner.create(LW), b_x);
            yWerte = (ArrayRealVector) cg.solve(LW, JacobiPreconditioner.create(LW), b_y);

            /*
             * neue Positiones-Werte zurckgeben
             */
            x = new double[X.getRowDimension()][2];
            for (int i = 0; i < x.length; i++) {

                x[i][0] = xWerte.getEntry(i);
                x[i][1] = yWerte.getEntry(i);

                X.setEntry(i, 0, xWerte.getEntry(i));
                X.setEntry(i, 1, yWerte.getEntry(i));

            }
        }

        return x;
    }

    /*
     * Verwendet im Algorithmus Stress Majorization, wie in Gansner, Koren,
     * North: Graph Drawing by Stress Majorization, 2004
     * 
     * @param x
     * 
     * @return 1/x, wenn x!=0, sonst 0
     */
    private static double inv(double x) {

        if (x != 0) {
            return 1 / x;
        } else {
            return 0;
        }

    }

    /*
     * Berechnet das Ergebnis der Gleichung Ax = b mittels des Verfahrens der
     * konjugierten Gradienten Gibt den Lsungsvektor x zurck aus:
     * https://de.wikipedia.org/wiki/CG-Verfahren
     * 
     * @return
     */
    @Deprecated
    private static ArrayRealVector conjugateGradientsMethod(Array2DRowRealMatrix A, ArrayRealVector b,
            ArrayRealVector werte) {

        Array2DRowRealMatrix preJacobi = new Array2DRowRealMatrix(A.getRowDimension(), A.getColumnDimension());

        // Predconditioner berechnen
        preJacobi.walkInRowOrder(new DefaultRealMatrixChangingVisitor() {
            @Override
            public double visit(int row, int column, double value) {
                if (row == column) {
                    return 1 / A.getEntry(row, column);
                } else {
                    return 0.0;
                }
            }
        });

        // x_k beliebig whlen
        ArrayRealVector x_k = new ArrayRealVector(werte);

        // r_k berechnen
        ArrayRealVector r_k = b.subtract(A.operate(x_k));

        // h_k berechnen
        ArrayRealVector h_k = (ArrayRealVector) preJacobi.operate(r_k);

        // d_k = r_k
        ArrayRealVector d_k = h_k;

        // x_k+1 und r_k+1 und d_k+1, sowie alpha und beta und z erzeugen
        ArrayRealVector x_k1;
        ArrayRealVector r_k1;
        ArrayRealVector d_k1;
        ArrayRealVector h_k1;
        double alpha;
        double beta;
        ArrayRealVector z;

        do {
            // Speichere Matrix-Vektor-Produkt, um es nur einmal auszurechnen
            z = (ArrayRealVector) A.operate(d_k);

            // Finde von x_k in Richtung d_k den Ort x_k1 des Minimums der
            // Funktion E
            // und aktualisere den Gradienten bzw. das Residuum
            alpha = r_k.dotProduct(h_k) / d_k.dotProduct(z);
            x_k1 = x_k.add(d_k.mapMultiply(alpha));
            r_k1 = r_k.subtract(z.mapMultiply(alpha));
            h_k1 = (ArrayRealVector) preJacobi.operate(r_k1);

            // Korrigiere die Suchrichtung d_k1
            beta = r_k1.dotProduct(h_k1) / r_k.dotProduct(h_k);
            d_k1 = h_k1.add(d_k.mapMultiply(beta));

            // Werte "eins" weitersetzen
            x_k = x_k1;
            r_k = r_k1;
            d_k = d_k1;
            h_k = h_k1;

        } while (r_k1.getNorm() > TOL);

        return x_k1;
    }

    /**
     * Lst die Minimierung des Stress Models. Genutzt wird die Bibliothek mdsj:
     * License Conditions: MDSJ is available under the terms and conditions of
     * the Creative Commons License "by-nc-sa" 3.0. Algorithmics Group. MDSJ:
     * Java Library for Multidimensional Scaling (Version 0.2). Available at
     * http://www.inf.uni-konstanz.de/algo/software/mdsj/. University of
     * Konstanz, 2009.
     * 
     * @param graph
     *            Graph, dessen Knoten-Positionen neu berechnet werden sollen
     * @param distMatrix,
     *            Matrix, die die gewnschten neuen Distanzen beinhaltet
     * @return Neues Layout des Eingabegraphens
     */
    @Deprecated
    public static double[][] doMDSJ(Graph graph, double[][] distMatrix) {

        // Eingabe Matrix berechnen
        double[][] koord = new double[2][graph.getKnotenAnzahl()];
        for (int i = 0; i < graph.getKnotenAnzahl(); i++) {
            koord[0][i] = graph.getKnoten().get(i).getX();
            koord[1][i] = graph.getKnoten().get(i).getY();
        }

        // Matrix mit den Gewichtungen berechnen
        double[][] weights = StressMinimization.weightMatrix(distMatrix, -2);

        // Stress Minimization ausfhren mit lediglich einer Iteration
        StressMinimization.majorize(koord, distMatrix, weights, 1);

        return koord;
    }
}