de.tud.kom.p2psim.impl.network.gnp.topology.GnpSpace.java Source code

Java tutorial

Introduction

Here is the source code for de.tud.kom.p2psim.impl.network.gnp.topology.GnpSpace.java

Source

/*
 * Copyright (c) 2005-2011 KOM - Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.network.gnp.topology;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;

import org.apache.commons.math.stat.StatUtils;
import org.apache.log4j.Logger;

import de.tud.kom.p2psim.impl.util.logging.SimLogger;

/**
 * Implements the downhill simplx algorithm for positioning host in an
 * multi-dimensional space, so that the difference betwen the measured rtts and
 * the calculated euclidean distance is minimized.
 * 
 * @author Gerald Klunker <peerfact@kom.tu-darmstadt.de>
 * @version 0.1, 09.01.2008
 * 
 */
public class GnpSpace implements Comparable<GnpSpace> {

    private static Logger log = SimLogger.getLogger(GnpSpace.class);

    private static final long serialVersionUID = 6941552219570807190L;

    private HostMap mapRef;

    private int noOfDimensions;

    private HashMap<Integer, GnpPosition> coordinateIndex;

    private GnpPosition[] monitorPositions;

    /*
     * Attributes for statistical analysis of measured to calculated errors
     */
    private double[] relativeErrorDistribution = new double[0];

    private LinkedList<Double>[] directionalRelativeErrors = new LinkedList[0];

    private HashSet<Host>[] hostsByRelativeError = new HashSet[0];

    private double maxRelativeError = 0;

    /*
     * Status information about current calculation and progress will be used by
     * a gui-thread
     */
    public static int calculationStepStatus = 0;

    public static int calculationProgressStatus = 0;

    public static boolean calculationInProgress = false;

    /**
     * 
     * @param noOfDimensions
     *            number of dimensions of the generated gnp space
     * @param mapRef
     *            reference to the related HostMap
     */
    public GnpSpace(int noOfDimensions, HostMap mapRef) {
        super();

        this.mapRef = mapRef;
        this.noOfDimensions = noOfDimensions;

        this.coordinateIndex = new HashMap<Integer, GnpPosition>();
        this.monitorPositions = new GnpPosition[mapRef.getNoOfMonitors()];

        int c = 0;
        for (Host monitor : mapRef.getMonitorIndex().values()) {
            monitorPositions[c] = new GnpPosition(noOfDimensions, monitor, this);
            c++;
        }
    }

    /**
     * 
     * @return number of monitor positions
     */
    protected int getNumberOfMonitors() {
        return monitorPositions.length;
    }

    /**
     * 
     * @param monitorIndex
     *            internal index of monitor
     * @param monitor
     *            new monitor position
     */
    private void setMonitorPosition(int monitorIndex, GnpPosition monitor) {
        monitorPositions[monitorIndex] = monitor;
    }

    /**
     * 
     * @param monitorIndex
     * @return position of monitor with internal index monitorIndex
     */
    protected GnpPosition getMonitorPosition(int monitorIndex) {
        return monitorPositions[monitorIndex];
    }

    /**
     * 
     * @return related HostMap object
     */
    protected HostMap getMapRef() {
        return mapRef;
    }

    /**
     * Calcuates the dimension of the gnp space.
     * 
     * @return array[x][y] x:pos in coordinate array, y=0: minimum of all
     *         values, y=1: maximum of all values , y=2: max - min value
     */
    private double[][] getDimension() {
        double[][] returnvalue = new double[this.noOfDimensions][3];
        double min;
        double max;
        for (int c = 0; c < this.noOfDimensions; c++) {
            min = this.monitorPositions[0].getGnpCoordinates(c);
            max = min;
            for (int d = 1; d < this.getNumberOfMonitors(); d++) {
                double current = this.monitorPositions[d].getGnpCoordinates(c);
                min = (min < current) ? min : current;
                max = (max > current) ? max : current;
            }
            returnvalue[c][0] = min;
            returnvalue[c][1] = max;
            returnvalue[c][2] = max - min;
        }
        return returnvalue;
    }

    /**
     * 
     * @return number of dimensions of the gnp space
     */
    private int getNoOfDimensions() {
        return this.noOfDimensions;
    }

    /**
     * Removes the gnp position of a host
     * 
     * @param hostIP
     */
    protected void removePosition(long hostIP) {
        this.coordinateIndex.remove(hostIP);
    }

    /*
     * ======================================================================
     * The following Methods implements methods for the statistical analysis of
     * the quality of positions according to the measured rtts.
     * ======================================================================
     */

    /**
     * Iterates through all Positions to calculate statistical data like -
     * statistic 1: maximum relative error - statistic 2: directional relative
     * errors grouped by rtt - statistic 3: hosts grouped by maximum relative
     * error - statistic 4: distribution of relative errors
     */
    public void builtStatisticalData() {

        // set calculation status to 3 (calculation of statistical data)
        calculationStepStatus = 3;

        maxRelativeError = 0;
        directionalRelativeErrors = new LinkedList[21];
        hostsByRelativeError = new HashSet[21];
        ArrayList<Double> relativeErrorsList = new ArrayList<Double>(4000000);

        for (int d = 0; d < hostsByRelativeError.length; d++)
            hostsByRelativeError[d] = new HashSet<Host>();
        for (int d = 0; d < directionalRelativeErrors.length; d++)
            directionalRelativeErrors[d] = new LinkedList<Double>();

        calculationProgressStatus = 0;
        for (GnpPosition host : coordinateIndex.values()) {
            calculationProgressStatus++;
            double maxError = 0;
            for (GnpPosition monitor : monitorPositions) {
                double measuredDistace = host.getMeasuredRtt(monitor);
                if (measuredDistace > 0) {
                    double directionalRelativeError = host.getDirectionalRelativError(monitor);
                    double relativeError = Math.abs(directionalRelativeError);
                    relativeErrorsList.add(relativeError);

                    // statistic 1: maximum relative error
                    if (maxError < relativeError)
                        maxError = relativeError;

                    // statistic 2: directional relative errors grouped by rtt
                    int groupID = (int) Math.floor(measuredDistace / 50);
                    if (groupID < 20)
                        directionalRelativeErrors[groupID].add(directionalRelativeError);
                    else
                        directionalRelativeErrors[20].add(directionalRelativeError);
                }
                // statistic 3: hosts grouped by maximum relative error
                int groupID = (int) Math.floor(maxError * 10);
                if (groupID >= 20)
                    hostsByRelativeError[20].add(host.getHostRef());
                else
                    hostsByRelativeError[groupID].add(host.getHostRef());

            }
            // statistic 1: maximum relative error
            if (maxRelativeError < maxError)
                maxRelativeError = maxError;
        }

        // statistic 4: distribution of relative errors
        double[] relativeErrorsArray = new double[relativeErrorsList.size()];
        for (int c = 0; c < relativeErrorsList.size(); c++)
            relativeErrorsArray[c] = relativeErrorsList.get(c);
        relativeErrorDistribution = new double[100];
        for (int c = 1; c < 100; c++)
            relativeErrorDistribution[c] = StatUtils.percentile(relativeErrorsArray, c);

        // set calculation status to 0 (no calculation)
        calculationStepStatus = 0;
    }

    /**
     * 
     * @return array with distribution of relativ errors of all positions.
     *         index: 0-100%
     */
    public double[] getRelativeErrorDistribution() {
        return relativeErrorDistribution;
    }

    /**
     * 
     * @return array of lists, that contains error values. index x=0: 0-50ms,
     *         x=1: 50-100ms, ... x=20: >1000ms
     */
    public LinkedList<Double>[] getDirectionalRelativeErrorsGroupedByRtt() {
        return directionalRelativeErrors;
    }

    /**
     * 
     * @return array of sets, that contains host with related relative errors of
     *         a certain amount. index x=0: error 0-0.1, x=1: error 0.1-0.2 ...
     *         x=20 error > 2
     */
    public HashSet<Host>[] getHostsGroupedByMaxRelativeError() {
        return hostsByRelativeError;
    }

    /**
     * 
     * @return maximum relative error within tis gnp space
     */
    public double getMaximumRelativeError() {
        return maxRelativeError;
    }

    /*
     * ======================================================================
     * The following Methods implements 2 downhill simplex algorithms for the
     * optimization of positions of monitors and host in the gnp space
     * ======================================================================
     */

    /**
     * Run this method to generate GNP Positions for Monitors and Hosts
     */
    public static GnpSpace getGnp(int noOfDimensions, int monitorResheduling, int hostResheduling, HostMap mapRef) {
        // positioning of monitors
        GnpSpace gnp = getGnpWithDownhillSimplex(noOfDimensions, monitorResheduling, mapRef);
        // positionning of ordinary hosts
        gnp.insertCoordinates(hostResheduling);
        return gnp;
    }

    /**
     * 
     * @param noOfDimensions
     *            number of Dimensions must be smaller than number of Monitors
     * @param monitorResheduling
     *            number of rescheduling the downhill simplex
     * @param mapRef
     *            reference to HostMap
     * @return optimized positions for Monitors
     */
    private static GnpSpace getGnpWithDownhillSimplex(int noOfDimensions, int monitorResheduling, HostMap mapRef) {

        GnpSpace.calculationStepStatus = 1;
        GnpSpace.calculationInProgress = true;

        double alpha = 1.0;
        double beta = 0.5;
        double gamma = 2;
        double maxDiversity = 0.5;

        // N + 1 initial random Solutions
        int dhs_N = mapRef.getNoOfMonitors();
        ArrayList<GnpSpace> solutions = new ArrayList<GnpSpace>(dhs_N + 1);
        for (int c = 0; c < dhs_N + 1; c++)
            solutions.add(new GnpSpace(noOfDimensions, mapRef));

        // best and worst solution
        GnpSpace bestSolution = Collections.min(solutions);
        GnpSpace worstSolution = Collections.max(solutions);
        double bestError = bestSolution.getObjectiveValueMonitor();
        double worstError = worstSolution.getObjectiveValueMonitor();

        for (int z = 0; z < monitorResheduling; z++) {
            GnpSpace.calculationProgressStatus = z;

            // resheduling
            int count = 0;
            for (GnpSpace gnp : solutions) {
                if (gnp != bestSolution) {
                    GnpPosition monitor = gnp.getMonitorPosition(count);
                    monitor.diversify(gnp.getDimension(), maxDiversity);
                    count++;
                }
            }

            // best and worst solution
            bestSolution = Collections.min(solutions);
            worstSolution = Collections.max(solutions);
            bestError = bestSolution.getObjectiveValueMonitor();
            worstError = worstSolution.getObjectiveValueMonitor();

            // stop criterion
            while (worstError - bestError > 0.00001 && calculationInProgress) {

                // move to center ...
                GnpSpace center = GnpSpace.getCenterSolution(solutions);
                GnpSpace newSolution1 = GnpSpace.getMovedSolution(worstSolution, center, 1 + alpha);
                double newError1 = newSolution1.getObjectiveValueMonitor();
                if (newError1 <= bestError) {
                    int IndexOfWorstSolution = solutions.indexOf(worstSolution);
                    GnpSpace newSolution2 = GnpSpace.getMovedSolution(worstSolution, center, 1 + alpha + gamma);
                    double newError2 = newSolution2.getObjectiveValueMonitor();
                    if (newError2 <= newError1) {
                        solutions.set(IndexOfWorstSolution, newSolution2);
                        bestError = newError2;
                    } else {
                        solutions.set(IndexOfWorstSolution, newSolution1);
                        bestError = newError1;
                    }
                    bestSolution = solutions.get(IndexOfWorstSolution);
                } else if (newError1 < worstError) {
                    int IndexOfWorstSolution = solutions.indexOf(worstSolution);
                    solutions.set(IndexOfWorstSolution, newSolution1);
                } else { // ... or contract around best solution
                    for (int c = 0; c < solutions.size(); c++) {
                        if (solutions.get(c) != bestSolution)
                            solutions.set(c, GnpSpace.getMovedSolution(solutions.get(c), bestSolution, beta));
                    }
                    bestSolution = Collections.min(solutions);
                    bestError = bestSolution.getObjectiveValueMonitor();
                }
                worstSolution = Collections.max(solutions);
                worstError = worstSolution.getObjectiveValueMonitor();
            }

        }

        // Set the Coordinate Reference to the Peer
        for (int c = 0; c < bestSolution.getNumberOfMonitors(); c++) {
            bestSolution.getMonitorPosition(c).getHostRef()
                    .setPositionReference(bestSolution.getMonitorPosition(c));
        }

        // GnpSpace.calculationStepStatus = 0;
        // GnpSpace.calculationInProgress = false;
        return bestSolution;
    }

    /**
     * Calculates good positions for all Hosts in Map
     * 
     * @param monitorResheduling
     *            number of rescheduling the downhill simplex
     */
    private void insertCoordinates(int monitorResheduling) {
        GnpSpace.calculationStepStatus = 2;
        coordinateIndex.clear();
        HashMap<Integer, Host> peers = this.getMapRef().getHostIndex();
        int c = 0;
        for (Host host : peers.values()) {
            GnpSpace.calculationProgressStatus = c;
            if (host.getHostType() == Host.HOST) {
                GnpPosition coord = this.insertCoordinateDownhillSimplex(host, monitorResheduling);
                coordinateIndex.put(host.getIpAddress(), coord);
                c++;
                if (c % 1000 == 0)
                    log.debug(c + " of " + peers.size() + " are positioned in gnp");
            }
            if (!calculationInProgress)
                return;
        }
        GnpSpace.calculationStepStatus = 0;
        GnpSpace.calculationInProgress = false;
    }

    /**
     * Calculates a good positions for the host
     * 
     * @param host
     *            to position
     * @param monitorResheduling
     *            number of rescheduling the downhill simplex
     * @return gnp position for peer
     */
    private GnpPosition insertCoordinateDownhillSimplex(Host host, int monitorResheduling) {

        double alpha = 1.0;
        double beta = 0.5;
        double gamma = 2;
        double maxDiversity = 0.5;

        // N + 1 initial random Solutions
        ArrayList<GnpPosition> solutions = new ArrayList<GnpPosition>(noOfDimensions + 1);

        for (int c = -1; c < noOfDimensions; c++) {
            GnpPosition coord = new GnpPosition(noOfDimensions, host, this);
            solutions.add(coord);
        }

        // best and worst solution
        GnpPosition bestSolution = Collections.min(solutions);
        GnpPosition worstSolution = Collections.max(solutions);
        double bestError = bestSolution.getDownhillSimplexError();
        double worstError = worstSolution.getDownhillSimplexError();

        double newError = 0.0;

        for (int z = 0; z < monitorResheduling; z++) {

            // resheduling
            for (GnpPosition coord : solutions) {
                if (coord != bestSolution) {
                    coord.diversify(this.getDimension(), maxDiversity);
                }
            }

            // best and worst solution
            bestSolution = Collections.min(solutions);
            worstSolution = Collections.max(solutions);
            bestError = bestSolution.getDownhillSimplexError();
            worstError = worstSolution.getDownhillSimplexError();

            // stop criterion
            while (worstError - bestError > 0.000001 && calculationInProgress) {

                // move to center ...
                GnpPosition center = GnpPosition.getCenterSolution(solutions);
                GnpPosition newSolution1 = GnpPosition.getMovedSolution(worstSolution, center, 1 + alpha);
                newError = newSolution1.getDownhillSimplexError();
                if (newError <= bestError) {
                    GnpPosition newSolution2 = GnpPosition.getMovedSolution(worstSolution, center,
                            1 + alpha + gamma);
                    int IndexOfWorstSolution = solutions.indexOf(worstSolution);
                    if (newSolution2.getDownhillSimplexError() <= newError) {
                        solutions.set(IndexOfWorstSolution, newSolution2);
                    } else {
                        solutions.set(IndexOfWorstSolution, newSolution1);
                    }
                    bestSolution = solutions.get(IndexOfWorstSolution);
                    bestError = bestSolution.getDownhillSimplexError();
                } else if (newError < worstError) {
                    int IndexOfWorstSolution = solutions.indexOf(worstSolution);
                    solutions.set(IndexOfWorstSolution, newSolution1);
                } else { // ... or contract around best solution
                    for (int c = 0; c < solutions.size(); c++) {
                        if (solutions.get(c) != bestSolution)
                            solutions.set(c, GnpPosition.getMovedSolution(solutions.get(c), bestSolution, beta));
                    }
                    bestSolution = Collections.min(solutions);
                    bestError = bestSolution.getDownhillSimplexError();
                }
                worstSolution = Collections.max(solutions);
                worstError = worstSolution.getDownhillSimplexError();
            }
        }

        // Set the Coordinate Reference to the Peer
        host.setPositionReference(bestSolution);
        return bestSolution;
    }

    /**
     * objective function for monitor downhill simplex
     * 
     * @return summation of monitor error values
     */
    private double getObjectiveValueMonitor() {
        double value = 0.0;
        for (int i = 0; i < this.getNumberOfMonitors() - 1; i++) {
            for (int j = i + 1; j < this.getNumberOfMonitors(); j++) {
                value = value + monitorPositions[i].getDownhillSimplexError(monitorPositions[j]);
            }
        }
        return value;
    }

    /**
     * Method must be overwrite to sort different GnpSpaces in order of the
     * quality of monitor positions.
     * 
     * Is needed for the positioning of monitors with the downhill simplex
     * 
     */
    public int compareTo(GnpSpace gnp) {
        double val1 = this.getObjectiveValueMonitor();
        double val2 = gnp.getObjectiveValueMonitor();
        if (val1 < val2)
            return -1;
        if (val1 > val2)
            return 1;
        else
            return 0;
    }

    /**
     * Static method generates a new GnpSpace according to the downhill simplex
     * operator
     * 
     * @param solutions
     * @return center solution
     */
    private static GnpSpace getCenterSolution(ArrayList<GnpSpace> solutions) {
        GnpSpace returnValue = new GnpSpace(solutions.get(0).getNoOfDimensions(), solutions.get(0).getMapRef());
        for (int c = 0; c < returnValue.getNumberOfMonitors(); c++) {
            ArrayList<GnpPosition> coords = new ArrayList<GnpPosition>();
            for (int d = 0; d < solutions.size(); d++) {
                coords.add(solutions.get(d).getMonitorPosition(c));
            }
            returnValue.setMonitorPosition(c, GnpPosition.getCenterSolution(coords));
        }
        return returnValue;
    }

    /**
     * Static method generates a new GnpSpace according to the downhill simplex
     * operator
     * 
     * @param solution
     * @param moveToSolution
     * @param moveFactor
     * @return moved solution
     */
    private static GnpSpace getMovedSolution(GnpSpace solution, GnpSpace moveToSolution, double moveFactor) {
        GnpSpace returnValue = new GnpSpace(solution.getNoOfDimensions(), solution.getMapRef());
        for (int c = 0; c < returnValue.getNumberOfMonitors(); c++) {
            returnValue.setMonitorPosition(c, GnpPosition.getMovedSolution(solution.getMonitorPosition(c),
                    moveToSolution.getMonitorPosition(c), moveFactor));
        }
        return returnValue;
    }

}