/** Copyright 2014 ATOS SPAIN S.A. Licensed under the Apache License, Version 2.0 (the License); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Authors Contact: Francisco Javier Nieto. Atos Research and Innovation, Atos SPAIN SA @email francisco.nieto@atos.net **/ package eu.betaas.taas.securitymanager.taastrustmanager.taastrustcalculator; import java.util.ArrayList; import org.apache.log4j.Logger; public class ExponentialSmoothingCalculator { public static final int BATTERY = 0; public static final int EXECTIME = 1; private Logger logger= Logger.getLogger("betaas.taas"); public ExponentialSmoothingCalculator () { } public double calculateAggregation (String idService, double currentValue, int parameter) { logger.debug ("Starting exponential smoothing agregation..."); double result = 0.0; ArrayList<Double> valuesList = null; //Retrieve last calculations done for the service switch (parameter) { case BATTERY: valuesList = retrievePreviousBattery(idService); break; case EXECTIME: valuesList = retrievePreviousExecTime(idService); break; default: return Double.NaN; } // Add last value obtained from current calculation for completing the list valuesList.add(0, new Double (currentValue)); // Transform array and call simple exponential smoothing valuesList.trimToSize(); Double[]orderedList = new Double[valuesList.size()]; valuesList.toArray(orderedList); result = simpleExponentialSmoothing(0.5, orderedList); logger.debug ("Calculated aggregation: " + result); return result; } public double simpleExponentialSmoothing (double alpha, Double[] valuesList) { double previousPrediction=0.0; double localPrediction=0.0; //Loop for calculating the smoothed series, starting from the last element for (int i=valuesList.length-1; i>=0; i--) { double localValue = valuesList[i].doubleValue(); previousPrediction = localPrediction; localPrediction = alpha * localValue + (1 - alpha) * previousPrediction; logger.debug ("---Iteration n. " + i + " --"); logger.debug ("Previous Prediction: " + previousPrediction); logger.debug ("Local Value: " + localValue); logger.debug ("Local Prediction: " + localPrediction); logger.debug ("----------------------------"); } return localPrediction; } private ArrayList<Double> retrievePreviousBattery (String idService) { ArrayList<Double> valuesList = new ArrayList<Double>(); // Here, perform actions for retrieving battery level measured return valuesList; } private ArrayList<Double> retrievePreviousExecTime (String idService) { ArrayList<Double> valuesList = new ArrayList<Double>(); // Here, perform actions for retrieving execution time measured for an application return valuesList; } public double calculateTripleAggregation (String idService, double currentValue, int parameter, int m) { logger.debug ("Starting triple exponential smoothing agregation..."); // In principle, 1 season = 1 day (minimum seasonality in the data) // For the Monitoring, 720 measures per day (1 season = 720 values) int period = 720; //String interval = PropertiesUtils.getProperty("TRUST","interval"); double result = 0.0; ArrayList<Double> valuesList = null; //Retrieve last calculations done for the service switch (parameter) { case BATTERY: valuesList = retrievePreviousBattery(idService); break; case EXECTIME: valuesList = retrievePreviousExecTime(idService); break; default: return Double.NaN; } // Add last value obtained from current calculation for completing the list if (!Double.isNaN(currentValue)) { valuesList.add(0, new Double (currentValue)); } // Transform array and call triple exponential smoothing valuesList.trimToSize(); Double[]orderedList = new Double[valuesList.size()]; valuesList.toArray(orderedList); // Use period and check the calculation can be done if (orderedList.length/period<2) { // We require minimum 2 seasons. If not available, it's better to return SES logger.debug("Not enough data for 2 seasons (" + orderedList.length + ") --> Calculatig Simple Exponential Smoothing"); return simpleExponentialSmoothing(0.5, orderedList); } else if (orderedList.length/period>14) { // We take as period the complete week --> 1 Season = 1 week period = period * 7; } result = tripleExponentialSmoothing(0.4863, 0.0001, 0.0011, period, orderedList, m); logger.debug ("Calculated aggregation: " + result); return result; } public double tripleExponentialSmoothing (double alpha, double beta, double gamma, int period, Double[] valuesList, int m) { logger.debug("Starting Holt-Winters forecast calculation with values..."); logger.debug("Alpha: " + alpha + "; Beta: " + beta + "; Gamma: " + gamma + "; Period: " + period + "; M: " + m); // Calculate initial values for the Holt-Winters int seasons = valuesList.length / period; double S0 = valuesList[0].doubleValue(); double b0 = calculateInitialTrend(valuesList, period); double[] initSeasonalIndices = calculateInitSeasonalIndices(valuesList, period, seasons); logger.debug("Total values: " + valuesList.length + ", Seasons: " + seasons + ", Period: " +period); logger.debug("Initial level value S0: " + S0); logger.debug("Initial trend value b0: " + b0); logger.debug("Seasonal Indices: " + initSeasonalIndices.toString()); double forecast = calculateHoltWinters(valuesList, S0, b0, alpha, beta, gamma, initSeasonalIndices, period, m); return forecast; } private double calculateInitialTrend(Double[] values, int period) { double trend = 0; for (int i = 0; i < period; i++) { trend = trend + (values[period + i] - values[i]); } return trend / (period * period); } private double[] calculateInitSeasonalIndices(Double[] values, int period, int seasons) { // Step 1: Compute the averages for each of the seasons double[] seasonalAvg = new double[seasons]; for (int i = 0; i < seasons; i++) { int actSeason = i * period; for (int j = 0; j < period; j++) { seasonalAvg[i] = seasonalAvg[i] + values[actSeason + j]; } seasonalAvg[i] = seasonalAvg[i] / period; } // Step 2: Divide the observations by the appropriate seasonal mean double[] avgObservations = new double[values.length]; for (int i = 0; i < seasons; i++) { int actSeason = i * period; for (int j = 0; j < period; j++) { avgObservations[actSeason + j] = values[actSeason + j] / seasonalAvg[i]; } } // Step 3: Now the seasonal indices are formed by computing the average of each row // (i.e. Avgs of same parts of the seasons: periods #1 of each season). double[] seasonalIndices = new double[period]; for (int i = 0; i < period; i++) { for (int j = 0; j < seasons; j++) { seasonalIndices[i] = seasonalIndices[i] + avgObservations[(j * period) + i]; } seasonalIndices[i] = seasonalIndices[i] / seasons; } return seasonalIndices; } // Multiplicative Holt-Winters (Triple Exponential Smoothing) private double calculateHoltWinters(Double[] y, double a0, double b0, double alpha, double beta, double gamma, double[] initialSeasonalIndices, int period, int m) { double[] St = new double[y.length]; double[] Bt = new double[y.length]; double[] It = new double[y.length]; double[] Ft = new double[y.length + m]; // Initialize base values //St[1] = a0; //Bt[1] = b0; St[0] = a0; Bt[0] = b0; for (int i = 0; i < period; i++) { It[i] = initialSeasonalIndices[i]; } // Start calculations for (int i = 1; i < y.length; i++) { logger.debug ("---Iteration n. " + i + " --"); // Calculate overall smoothing if ((i - period) >= 0) { St[i] = alpha * y[i] / It[i - period] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]); } else { St[i] = alpha * y[i] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]); } logger.debug ("Overall Smoothing: " + St[i]); // Calculate trend smoothing Bt[i] = gamma * (St[i] - St[i - 1]) + (1 - gamma) * Bt[i - 1]; logger.debug ("Trend Smoothing: " + It[i]); // Calculate seasonal smoothing if ((i - period) >= 0) { It[i] = beta * y[i] / St[i] + (1.0 - beta) * It[i - period]; } logger.debug ("Seasonal Smoothing: " + It[i]); // Calculate forecast if (((i + m) >= period)) { Ft[i + m] = (St[i] + (m * Bt[i])) * It[i - period + m]; } logger.debug ("Prediction done: " + Ft[i + m]); logger.debug ("----------------------------"); } // Return only the next value return Ft[Ft.length-1]; } public double doubleExponentialSmoothing (double alpha, double beta, Double[] valuesList, int m) { logger.debug("Starting Holt's Linear forecast calculation with values..."); logger.debug("Alpha: " + alpha + "; Beta: " + beta + "; M: " + m); // Calculate initial values for the Holt's Linear double L0 = valuesList[0].doubleValue(); double b0 = valuesList[1].doubleValue() - valuesList[0].doubleValue(); logger.debug("Total values: " + valuesList.length); logger.debug("Initial level value L0: " + L0); logger.debug("Initial trend value b0: " + b0); double forecast = calculateHoltLinear(valuesList, L0, b0, alpha, beta, m); return forecast; } // Multiplicative Holt (Double Exponential Smoothing) private double calculateHoltLinear(Double[] y, double a0, double b0, double alpha, double beta, int k) { double[] Lt = new double[y.length]; double[] Bt = new double[y.length]; double[] Ft = new double[y.length + k]; // Initialize base values Lt[0] = a0; Bt[0] = b0; Ft[0] = a0; // Start calculations for (int i = 1; i < y.length ; i++) { logger.debug ("---Iteration n. " + i + " --"); logger.debug ("Actual value: " + y[i]); // Calculate overall smoothing Lt[i] = alpha * y[i] + (1.0 - alpha) * (Lt[i-1] + Bt[i-1]); logger.debug ("Overall Smoothing: " + Lt[i]); // Calculate trend smoothing Bt[i] = beta * (Lt[i] - Lt[i - 1]) + (1.0 - beta) * Bt[i - 1]; logger.debug ("Trend Smoothing: " + Bt[i]); // Calculate forecast Ft[i + 1] = Lt[i] + Bt[i]; logger.debug ("Prediction done: " + Ft[i + k]); logger.debug ("----------------------------"); } Ft[y.length-1 + k ] = Lt[y.length-1] + k * Bt[y.length-1]; // Return only the last value return Ft[Ft.length-1]; } /* public static void main(String[] args) { //ExponentialSmoothingCalculator myCalculator = new ExponentialSmoothingCalculator(); //Double[] valuesList = new Double[] {1.2, 1.3, 2.5, 1.5, 1.1, 1.2, 2.4, 1.4, 0.9, 1.0, 2.3, 1.3}; //double result = myCalculator.tripleExponentialSmoothing(0.5, 0.1, 0.3, 4, valuesList, 1); //double result = myCalculator.doubleExponentialSmoothing(0.5, 0.1, valuesList, 1); //System.out.println ("Received value: " + result); //double res2 = myCalculator.calculateTripleAggregation("a4169454-a7bc-441c-b1b2-378ede095180", 0.276043186320266, ExponentialSmoothingAggregator.SERVTRUST, 1); //double res2 = myCalculator.calculateTripleAggregation("atos", 0.276043186320266, ExponentialSmoothingCalculator.BATTERY, 1); //System.out.println ("Received value: " + res2); ArrayList<String> myList = new ArrayList<String> (); myList.add("Hola"); myList.add("casa"); myList.add("cosa"); myList.add("prueba"); System.out.println ("Tiene hola? -> " + myList.contains("hola")); System.out.println ("Tiene Hola? -> " + myList.contains("Hola")); } */ }