package de.s_node.bsc.borealis;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.play_project.dcep.api.measurement.Load;
import eu.play_project.dcep.api.measurement.LoadTimeSeries;
import eu.play_project.dcep.api.measurement.NodeMeasurementResult;
import eu.play_project.dcep.api.measurement.PatternMeasuringResult;
import eu.play_project.dcep.api.measurement.RoutingInformation;
public class StatisticsUnit {
private Logger logger;
int kPeriods; //Number of stored measurements.
double statisticsWindow; //Total time of k statistics measurements.
List<NodeMeasurementResult> nodes; //Values of the k measurements.
double epsilon =0; //Good mapping or little load.
double delta =0;
public StatisticsUnit(){
logger = LoggerFactory.getLogger(StatisticsUnit.class);
nodes = new LinkedList<NodeMeasurementResult>();
}
public double calcVariance(LoadTimeSeries loadTimeSerie) {
double variance = 99;
double sum1 = sum(loadTimeSerie, 2);
double sum2 = sum(loadTimeSerie, 1);
variance = (1.0 / kPeriods) * sum1 - Math.pow(((1.0 / kPeriods) * sum2), 2);
return variance;
}
public void setkPeriods(int kPeriods) {
this.kPeriods = kPeriods;
}
public double calcCovariance(LoadTimeSeries loadTimeSerie1, LoadTimeSeries loadTimeSerie2){
double covariance = 99;
//Check parameters
if(loadTimeSerie1.size()!= loadTimeSerie2.size()){
throw new RuntimeException("loadTimeSerie1 and loadTimeSerie2 have not the same size. LTS1: " + loadTimeSerie1.size() + " LTS2: " + loadTimeSerie2.size());
}
//Calc fist sum
double sum1 = 0;
for (int i = 0; i < loadTimeSerie1.size(); i++) {
sum1 += (loadTimeSerie1.get(i) * loadTimeSerie2.get(i));
}
double sum2 = 0;
sum2 = sum(loadTimeSerie1, 1);
double sum3 = 0;
sum3 = sum(loadTimeSerie2, 1);
covariance = ((1.0/kPeriods)*sum1) - (((1.0/kPeriods)*sum2)*((1.0/kPeriods)*sum3));
return covariance;
}
/**
* Correlation coefficient between the load time series of operator o and total sum of load time series of all operator on N except o.
* @param o Load time series of operator o.
* @param N All load time series of node N except load time series of operator o.
* @return
*/
public double p(LoadTimeSeries o, LoadTimeSeries[] N){
//Join LodTimeSeires
LoadTimeSeries sumN = new LoadTimeSeries("P1", kPeriods);
int seriesSize = N[0].size();
Double sum = new Double(0);
// List all elements in a load time series.
for(int i=0; i<seriesSize; i++){
// Get all load time series
for (int j = 0; j < N.length; j++) {
sum += N[j].get(i);
}
sumN.add(sum);
}
return calcCorrelationCoefficient(o, sumN);
}
public double calcCorrelationCoefficient(LoadTimeSeries loadTimeSerie1, LoadTimeSeries loadTimeSerie2){
double correlationCoefficient = 99;
correlationCoefficient = calcCovariance(loadTimeSerie1, loadTimeSerie2)/(Math.sqrt(calcVariance(loadTimeSerie1)) * Math.sqrt(calcVariance(loadTimeSerie2)));
return correlationCoefficient;
}
public double sum(LoadTimeSeries values, int exp){
double sum = 0;
for (Double value : values) {
sum += Math.pow(value, exp);
}
return sum;
}
/**
* Accumulate all values from the LoadTimeSeries in the list without the values of the LoadTimeSeries with the name given in except.
* @param loadTimeSeires
* @param exept Name of LoadTimeSeries with will not be part of the sum.
* @return Sum of all values \{except}.
*/
public LoadTimeSeries sum(Map<String, LoadTimeSeries> loadTimeSeires, String except){
// To know how many elements are in a LoadTimeSeries.
int windowTime = (loadTimeSeires.get(loadTimeSeires.keySet().iterator().next()).getNumberOfElementsInLoadTimeSeries());
LoadTimeSeries result = new LoadTimeSeries("sum", windowTime);
// Take first pattern name and add it to the values in result. Take second pattern name and andd it to the values in restult ...
for (String lsKey : loadTimeSeires.keySet()) {
if (!lsKey.equals(except)) { // Exlcude series o.
for (int j = 0; j < windowTime; j++) {
result.set(j, (result.get(j) + loadTimeSeires.get(lsKey).get(j)));
}
}
}
return result;
}
public void addLoadTimeSeries(String nodeName, LoadTimeSeries loadTimeSeries){
for (NodeMeasurementResult node : nodes) {
if(node.getName().equals(node)){
if(loadTimeSeries.size()==kPeriods){
node.setLoatTimeSeries(nodeName, loadTimeSeries);
}else{
throw new RuntimeException("Only k periods are allowed. K: " + kPeriods + ". loadTimeSeries.size(): " + loadTimeSeries.size() );
}
}
}
}
/**
* Calculates the load for this node an all Patterns in this measurementResult object.
*/
public Load calcLoad(NodeMeasurementResult measuredValues){
Load load = new Load(measuredValues.getName(), measuredValues.getMeasuringPeriod());
List<PatternMeasuringResult> patternLoad = new LinkedList<PatternMeasuringResult>();
int processedEvents=0;
//Calculate total load of the node.
load.setTotalLoad(measuredValues.getNumberOfComponentInputEvetns()*measuredValues.getProcessingTimeForOneEvent());
//Get number of processed events.
for (PatternMeasuringResult patternMeasuringResult : measuredValues.getMeasuredValues()) {
processedEvents += patternMeasuringResult.getProcessedEvents();
}
//Calculate load for every pattern.
for (PatternMeasuringResult patternMeasuringResult : measuredValues.getMeasuredValues()) {
PatternMeasuringResult ab = new PatternMeasuringResult(patternMeasuringResult.getName(), patternMeasuringResult.getProcessedEvents());
patternLoad.add(ab);
double loadD = ((((patternMeasuringResult.getProcessedEvents()* 1.0) / processedEvents)*measuredValues.getNumberOfComponentInputEvetns())*measuredValues.getProcessingTimeForOneEvent());
ab.setBorealisLoad(loadD);
}
// Set calculated values.
load.setLoadPerPattern(patternLoad);
return load;
}
// Algorithem of the paper
public List<RoutingInformation> makeDecisions() {
List<RoutingInformation> patternsToMove = new ArrayList<RoutingInformation>();
RoutingInformation pattern = null;
// Request statistics from all sites/instances
// Calculate utilization.
// Order all nodes by their average load.
Collections.sort(nodes, Collections.reverseOrder());
// Check if movement is needed. // Section 3.3 in paper. Pair-wise Algorithm.
for (int i = 0; i < (nodes.size() / 2); i++) {
NodeMeasurementResult donor = nodes.get(i);
NodeMeasurementResult receiver = nodes.get((nodes.size()) - (i + 1));
// LoadTimeSeries operator = nodes.get(i); //FIXME change value.
logger.debug("Donor is: " + donor.getName());
logger.debug("Receiver is: " + receiver.getName());
logger.debug("Donor totalLoad: " + donor.getTotalLoad());
logger.debug("Receiver totalLoad: " + receiver.getTotalLoad());
// Move only if difference is big.
if ((donor.getTotalLoad() - receiver.getTotalLoad()) > epsilon) {
logger.debug("Load difference is > epsilon. " + (donor.getTotalLoad() - receiver.getTotalLoad()));
// 3.3.1 One-way Correlation Based Load Balancing.
double maxLoadToMove = (donor.getTotalLoad()-receiver.getTotalLoad())/2;
double loadOfSelectedOperators =0;
List<BorealisScoreLoad> bLoad = new LinkedList<BorealisScoreLoad>();
//Calculate load for all operators. To move from N1 to N2.
for (LoadTimeSeries o : donor.getLoadTimeSeriesList()) {
LoadTimeSeries N1 = this.sum(donor.getAllLoatTimeSeries(), o.getName());
LoadTimeSeries N2 = this.sum(receiver.getAllLoatTimeSeries(), ""); //Exclude nothing.
bLoad.add(new BorealisScoreLoad(o.getName(), (calcCorrelationCoefficient(o, N1) - calcCorrelationCoefficient(o, N2)) / 2));
}
//Sort operators
Collections.sort(bLoad, Collections.reverseOrder());
//Select operators having the greatest score until the load of the selected operators exceed (L1-L2)/2
for(int j=0; (loadOfSelectedOperators < maxLoadToMove && j<bLoad.size()); j++){
loadOfSelectedOperators += bLoad.get(i).getLoad();
patternsToMove.add(new RoutingInformation(donor.getName(), receiver.getName(), bLoad.get(j).getName()));
}
} else {
System.out.println("Nicht umziehen.");
break; //List is sorted so it makes no sens to look for the next elements.
}
// Move operators with score > delta
// if (calcOneWayCorrelationBasedScore(operator, donor, receiver) > delta) {
// // Put operator to node receiver.
// }
}
return patternsToMove;
}
/**
* Calcualte scrore for moving operator o from node n1 to n2.
* @param o Load of o.
* @param n1 Load of node 1.
* @param n2 Load of node 2
* @return Score for moving o from n1 to n2.
*/
protected double calcOneWayCorrelationBasedScore(LoadTimeSeries o,LoadTimeSeries n1, LoadTimeSeries n2){
double score =0;
score = (calcCorrelationCoefficient(o, n1) - calcCorrelationCoefficient(o, n2)) / 2.0;
return score;
}
public double getkPeriods() {
return kPeriods;
}
/**
* Put measured data in StatisticsUnit.
* @param data
*/
public void addData(NodeMeasurementResult data){
Load l = this.calcLoad(data);
// Add data to LoadTimeSeries for this node.
NodeMeasurementResult node = this.getNode(data.getName()); //Get Node
// If node does not exists create one.
if(node==null){
nodes.add(data);
node = this.getNode(data.getName()); //Get Node
}
//Get load form measuring results add put it to the load time series of the given node.
for (PatternMeasuringResult patternLoad : l.getLoadPerPattern()) {
node.getLoatTimeSeries(patternLoad.getName()).add(patternLoad.getBorealisLoad());
}
}
/**
* Get node with the given name. If node does not exist return null.
* @param name Name of the node.
* @return
*/
private NodeMeasurementResult getNode(String name){
NodeMeasurementResult result = null;
for (NodeMeasurementResult node : nodes) {
if(node.getName().equals(name)){
result = node;
}
}
return result;
}
}