/*********************************************************************** This file is part of KEEL-software, the Data Mining tool for regression, classification, clustering, pattern mining and so on. Copyright (C) 2004-2010 F. Herrera (herrera@decsai.ugr.es) L. S�nchez (luciano@uniovi.es) J. Alcal�-Fdez (jalcala@decsai.ugr.es) S. Garc�a (sglopez@ujaen.es) A. Fern�ndez (alberto.fernandez@ujaen.es) J. Luengo (julianlm@decsai.ugr.es) This program 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 (at your option) any later version. This program 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 this program. If not, see http://www.gnu.org/licenses/ **********************************************************************/ package keel.Algorithms.RE_SL_Methods.P_FCS1; /** * <p> * @author Written by Francisco Jos� Berlanga (University of Ja�n) 01/01/2007 * @version 1.0 * @since JDK 1.6 * </p> */ import java.io.IOException; import java.util.*; import org.core.*; public class Algorithm { /** * <p> * It contains the implementation of the algorithm * </p> */ myDataset train, val, test; String outputTr, outputTst, outputBC; double classProb[]; double attrProb[][][]; //atribute value, atribute position, class int entradas; ArrayList<Individual> Poblacion; ArrayList<Individual> Poblacion2; ArrayList<Individual> Descen; ArrayList<Individual> Hijos; long semilla; int tamPoblacion, n_rulesIndividual, max_n_rulesIndividual, Gen, numGeneraciones; double probCrossover, probMut; //We may declare here the algorithm's parameters private boolean somethingWrong = false; //to check if everything is correct. /** * <p> * Default constructor * </p> */ public Algorithm() { } /** * <p> * It reads the data from the input files (training, validation and test) and parse all the parameters * from the parameters array. * </p> * @param parameters parseParameters It contains the input files, output files and parameters */ public Algorithm(parseParameters parameters) { train = new myDataset(); val = new myDataset(); test = new myDataset(); try { System.out.println("\nReading the training set: " + parameters.getTrainingInputFile()); train.readRegressionSet(parameters.getTrainingInputFile(), true); System.out.println("\nReading the validation set: " + parameters.getValidationInputFile()); val.readRegressionSet(parameters.getValidationInputFile(), false); System.out.println("\nReading the test set: " + parameters.getTestInputFile()); test.readRegressionSet(parameters.getTestInputFile(), false); } catch (IOException e) { System.err.println( "There was a problem while reading the input data-sets: " + e); somethingWrong = true; } //We may check if there are some numerical attributes, because our algorithm may not handle them: //somethingWrong = somethingWrong || train.hasNumericalAttributes(); //somethingWrong = somethingWrong || train.hasMissingAttributes(); outputTr = parameters.getTrainingOutputFile(); outputTst = parameters.getTestOutputFile(); outputBC = parameters.getOutputFile(0); //Now we parse the parameters, for example: semilla = Long.parseLong(parameters.getParameter(0)); //... tamPoblacion = Integer.parseInt(parameters.getParameter(1)); n_rulesIndividual = Integer.parseInt(parameters.getParameter(2)); numGeneraciones = Integer.parseInt(parameters.getParameter(3)); probCrossover = Double.parseDouble(parameters.getParameter(4)); probMut = Double.parseDouble(parameters.getParameter(5)); entradas = train.getnInputs(); Poblacion = new ArrayList<Individual>(tamPoblacion); for (int i = 0; i < tamPoblacion; i++) { Individual indi = new Individual(n_rulesIndividual, entradas + 1); Poblacion.add(indi); } Poblacion2 = new ArrayList<Individual>(tamPoblacion); Descen = new ArrayList<Individual>(2); for (int i = 0; i < 2; i++) { Individual indi = new Individual(n_rulesIndividual, entradas + 1); Poblacion.add(indi); } Hijos = new ArrayList<Individual>(tamPoblacion); } /** * <p> * It launches the algorithm * </p> */ public void execute() { int i, j, num; double fitness; if (somethingWrong) { //We do not execute the program System.err.println("An error was found, either the data-set have numerical values or missing values."); System.err.println("Aborting the program"); //We should not use the statement: System.exit(-1); } else { //We do here the algorithm's operations Randomize.setSeed(semilla); /* Generation of the initial population */ System.out.println("Creating the initial population."); initializePopulation(); Gen = 0; /* evaluatePopulationtion of the initial population */ System.out.println("evaluatePopulationting the initial population."); evaluatePopulation(); /* Evolutionary process */ System.out.println("Starting the evolutionary process."); do { /* Reproduction stage */ Reproduction(); /* we increment the counter */ Gen++; System.out.println("Iteration: " + Gen + ". Best fitness: " + (1.0 / Poblacion.get(0).fitness)); } while (Gen <= numGeneraciones); String salida = new String(""); salida += Print_Population(); salida += "MSE Training:\t" + (1.0 / Poblacion.get(0).fitness) + "%\n"; salida += "MSE Test:\t\t" + (1.0 / eval(test, Poblacion.get(0))) + "%\n\n"; Files.writeFile(outputBC, salida); doOutput(this.val, this.outputTr); doOutput(this.test, this.outputTst); System.out.println("Algorithm Finished."); } } /** * <p> * It initializes each individual in the population * </p> */ void initializePopulation() { int i, j, k; for (i = 0; i < tamPoblacion; i++) { for (j = 0; j < n_rulesIndividual; j++) { for (k = 0; k <= entradas; k++) { Poblacion.get(i).RuleBase[j].memfunctions[k].center = Randomize.RanddoubleClosed(train.getMin(k), train.getMax(k)); Poblacion.get(i).RuleBase[j].memfunctions[k].width = Randomize.RanddoubleClosed(0.0, ((2.0 * (train.getMax(k) - train.getMin(k))) / (Math.sqrt(n_rulesIndividual)))); } } } } /** * <p> * It evaluates each individual in the population * </p> */ void evaluatePopulation() { int i, j; for (i = 0; i < tamPoblacion; i++) { /* if the chromosome aren't evaluated, it's evaluate */ if (Poblacion.get(i).n_e == 1) { // System.out.println("evaluatePopulationndo el individuo " + (i+1)); Poblacion.get(i).fitness = eval(train, Poblacion.get(i)); Poblacion.get(i).n_e = 0; } } } /** * <p> * It evaluates an individual * </p> * @param dataset myDataset The set of examples * @param indi Invidual The individual being evaluated * @return double The fitness of the individual */ public double eval(myDataset dataset, Individual indi) { int i; double result, suma, fuerza; suma = 0.0; for (i = 0; i < dataset.getnData(); i++) { fuerza = Output_fuzzy_system(dataset, indi, dataset.getExample(i)); suma += Math.pow(dataset.getOutputAsReal(i) - fuerza, 2.0); } result = suma / dataset.getnData(); /* We want to have a maximization problem so, we invert the error */ if (result != 0.0) { result = 1.0 / result; } else { result = 0.0; } return (result); } /** * <p> * It calculate the output of the fuzzy system encoded in the individual for a given example * </p> * @param dataset myDataset The set of examples * @param indi Individual The individual enconding several fuzzy rules * @param ejemplo double [] A given example * @return double The output value obtained as output of the fuzzy system for a given example */ double Output_fuzzy_system(myDataset dataset, Individual indi, double[] ejemplo) { int i; double result, suma1, suma2, omega, y; suma1 = suma2 = 0.0; for (i = 0; i < indi.num_reglas; i++) { omega = Matching_degree(indi.RuleBase[i], ejemplo); y = Output_Value(dataset, indi.RuleBase[i], omega); suma1 += (omega * y); suma2 += omega; } if (suma2 != 0.0) { result = suma1 / suma2; } else { // result = 0.0; result = ((dataset.getMax(entradas) - dataset.getMin(entradas)) / 2.0); } return (result); } /** * <p> * It calculate the matching degree between the antecedent of the rule and a given example * </p> * @param reg Rule The rule containing several gaussian fuzzy sets * @param ejemplo double [] A given example * @return double The matching degree between the example and the antecedent of the rule */ double Matching_degree(Rule reg, double[] ejemplo) { int i; double result, suma, numerador, denominador; suma = 0.0; for (i = 0; i < entradas; i++) { numerador = Math.pow((ejemplo[i] - reg.memfunctions[i].center), 2.0); denominador = Math.pow(reg.memfunctions[i].width, 2.0); suma += (numerador / denominador); } suma *= -1.0; result = Math.exp(suma); return (result); } /** * <p> * Defuzzification value for the rule * </p> * @param dataset myDataset The set of examples * @param reg Rule The rule containing several gaussian fuzzy sets * @param grado_entrada double The matching degree between a given example and the antecedent of the rule * @return double The defuzzification value for the rule */ double Output_Value(myDataset dataset, Rule reg, double grado_entrada) { int i; double result, ancho_intervalo, suma1, suma2, y, grado, inter; double numerador, denominador, suma; ancho_intervalo = dataset.getMax(entradas) - dataset.getMin(entradas); inter = (ancho_intervalo / 20.0); /* Defuzzification */ y = dataset.getMin(entradas) + inter; suma1 = suma2 = 0.0; for (i = 0; i < 19; i++) { numerador = Math.pow((y - reg.memfunctions[entradas].center), 2.0); denominador = Math.pow(reg.memfunctions[entradas].width, 2.0); suma = (numerador / denominador); suma *= -1.0; grado = Math.exp(suma); if (grado > grado_entrada) { grado = grado_entrada; } suma1 += y * grado; suma2 += grado; y += inter; } /* Now, we calculate the value for the output */ if (suma2 != 0.0) { result = suma1 / suma2; } else { result = ((dataset.getMax(entradas) - dataset.getMin(entradas)) / 2.0); } return (result); } /** * <p> * It performs the reproduction stage * </p> */ void Reproduction() { int i; int[] padres = new int[(tamPoblacion / 10)]; /* First, the individual in the population are ordered according to their fitness */ Collections.sort(Poblacion); Poblacion2.clear(); Hijos.clear(); /* The worst 10% of the individual in the population is replaced by the new offspring */ for (i = 0; i < (tamPoblacion - (tamPoblacion / 10)); i++) { Individual indi = new Individual(Poblacion.get(i)); Poblacion2.add(indi); } /* Parents are selected */ padres = Selection(); for (i = 0; i < (tamPoblacion / 10); i += 2) { /* Crossover operator */ Crossover(padres[i], padres[i + 1]); /* Mutation operator */ Mutation(); /* Now, we add the 2 new individuals to the population of children */ Hijos.add(Descen.get(0)); Hijos.add(Descen.get(1)); } /* Create the population for the next generation */ Poblacion.clear(); for (i = 0; i < Poblacion2.size(); i++) { Individual indi = new Individual(Poblacion2.get(i)); Poblacion.add(indi); } for (i = 0; i < Hijos.size(); i++) { Individual indi = new Individual(Hijos.get(i)); Poblacion.add(indi); } /* evaluatePopulationtion of new children */ evaluatePopulation(); /* The population is ordered again according the fitness of its individual */ Collections.sort(Poblacion); } /** * <p> * It selects the parents that will participate in the evolutionary process (by rank based roulette wheel selection). * </p> * @param int[] The positions in the population for the selected parents */ int[] Selection() { int i, j, k; double rank_min, rank_max, u; int[] parents; double[] sample; rank_min = 0.75; rank_max = 2.0 - rank_min; sample = new double[tamPoblacion]; parents = new int[(tamPoblacion / 10)]; for(i = 0; i < tamPoblacion; i++){ if(i != 0){ sample[i] = sample[i-1] + (rank_max - (rank_max - rank_min) * i / (double)(tamPoblacion-1)) / (double)tamPoblacion; } else{ sample[i] = (rank_max - (rank_max - rank_min) * i / (double)(tamPoblacion-1)) / (double)tamPoblacion; } } /* select the parent for creating children */ for(k = 0; k < (tamPoblacion / 10); k++){ u = Randomize.Rand(); i = 0; for (j = 0; j < tamPoblacion; j++) { for (; sample[i] < u; i++); parents[k] = i; } } return (parents); } /** * <p> * It applies a Ordered Two-point crossover genetic operator between individual in position "madre" and "padre" in the population. * The new generated children (2 descendants) are added in a population of descendants * </p> * @param madre int Parent number 1 is in position "madre" in the population * @param padre int Parent number 2 is in position "padre" in the population */ void Crossover(int madre, int padre) { int i, k, num1, num2, ind1, ind2; Individual Hijo1; Individual Hijo2; double u, R1, R2; int[] madre_reglas; int[] padre_reglas; double[] C1; double[] C2; boolean verifica; madre_reglas = new int[Poblacion.get(madre).num_reglas]; for (i = 0; i < Poblacion.get(madre).num_reglas; i++) { madre_reglas[i] = -1; } padre_reglas = new int[Poblacion.get(padre).num_reglas]; for (i = 0; i < Poblacion.get(padre).num_reglas; i++) { padre_reglas[i] = -1; } C1 = new double[entradas]; C2 = new double[entradas]; Descen.clear(); u = Randomize.RandClosed(); if (u < probCrossover) { /* We calculate the two crosspoints vectors C1 and C2 */ R1 = Randomize.RandClosed(); R2 = Randomize.RandClosed(); for (i = 0; i < entradas; i++) { C1[i] = train.getMin(i) + (train.getMax(i) - train.getMin(i)) * (Math.pow(R1, 1.0 / entradas)); C2[i] = C1[i] + (train.getMax(i) - train.getMin(i)) * (Math.pow(R2, 1.0 / entradas)); } num1 = num2 = 0; /* According to the crossover operator, we look which rules from the mother are going to be part of each one of the two children */ for (k = 0; k < Poblacion.get(madre).num_reglas; k++) { verifica = true; for (i = 0; (i < entradas) && (verifica == true); i++) { verifica = false; if ((Poblacion.get(madre).RuleBase[k].memfunctions[i]. center > C1[i]) && (Poblacion.get(madre).RuleBase[k].memfunctions[i]. center < C2[i])) { verifica = true; } if ((Poblacion.get(madre).RuleBase[k].memfunctions[i]. center + train.getMax(i) - train.getMin(i)) < C2[i]) { verifica = true; } } if (verifica == true) { madre_reglas[k] = 1; num1++; } else { madre_reglas[k] = 2; num2++; } } /* According to the crossover operator, we look which rules from the father are going to be part of each one of the two children */ for (k = 0; k < Poblacion.get(padre).num_reglas; k++) { verifica = true; for (i = 0; (i < entradas) && (verifica == true); i++) { verifica = false; if ((Poblacion.get(padre).RuleBase[k].memfunctions[i]. center > C1[i]) && (Poblacion.get(padre).RuleBase[k].memfunctions[i]. center < C2[i])) { verifica = true; } if ((Poblacion.get(padre).RuleBase[k].memfunctions[i]. center + train.getMax(i) - train.getMin(i)) < C2[i]) { verifica = true; } } if (verifica == true) { padre_reglas[k] = 2; num2++; } else { padre_reglas[k] = 1; num1++; } } if ((num1 != 0) && (num2 != 0)) { /* We created 2 new children */ Hijo1 = new Individual(num1, entradas + 1); Hijo2 = new Individual(num2, entradas + 1); ind1 = ind2 = 0; for (k = 0; k < Poblacion.get(madre).num_reglas; k++) { if (madre_reglas[k] == 1) { for (i = 0; i <= entradas; i++) { Hijo1.RuleBase[ind1].memfunctions[i].center = Poblacion.get(madre).RuleBase[k]. memfunctions[i].center; Hijo1.RuleBase[ind1].memfunctions[i].width = Poblacion.get(madre).RuleBase[k]. memfunctions[i].width; } ind1++; } else { for (i = 0; i <= entradas; i++) { Hijo2.RuleBase[ind2].memfunctions[i].center = Poblacion.get(madre).RuleBase[k]. memfunctions[i].center; Hijo2.RuleBase[ind2].memfunctions[i].width = Poblacion.get(madre).RuleBase[k]. memfunctions[i].width; } ind2++; } } for (k = 0; k < Poblacion.get(padre).num_reglas; k++) { if (padre_reglas[k] == 1) { for (i = 0; i <= entradas; i++) { Hijo1.RuleBase[ind1].memfunctions[i].center = Poblacion.get(padre).RuleBase[k]. memfunctions[i].center; Hijo1.RuleBase[ind1].memfunctions[i].width = Poblacion.get(padre).RuleBase[k]. memfunctions[i].width; } ind1++; } else { for (i = 0; i <= entradas; i++) { Hijo2.RuleBase[ind2].memfunctions[i].center = Poblacion.get(padre).RuleBase[k]. memfunctions[i].center; Hijo2.RuleBase[ind2].memfunctions[i].width = Poblacion.get(padre).RuleBase[k]. memfunctions[i].width; } ind2++; } } /* The 2 new created children are added for appling the mutation operator */ Descen.add(Hijo1); Descen.add(Hijo2); } else { /* The 2 parents are copied for appling the mutation operator */ Individual indi = new Individual(Poblacion.get(madre)); Descen.add(indi); Individual indi2 = new Individual(Poblacion.get(padre)); Descen.add(indi2); } } else { /* The 2 parents are copied for appling the mutation operator */ Individual indi = new Individual(Poblacion.get(madre)); Descen.add(indi); Individual indi2 = new Individual(Poblacion.get(padre)); Descen.add(indi2); } } /** * <p> * It applies mutation genetic operator to the descendants previosly created by crossover operator * This operator modifies the center or the width of a selected gaussian fuzzy set in the desdecendat * </p> */ void Mutation() { int reg, entrada; double u, factor; /* First child */ reg = Randomize.RandintClosed(0, Descen.get(0).num_reglas-1); entrada = Randomize.RandintClosed(0, entradas); factor = Randomize.RanddoubleClosed(0.9, 1.1); u = Randomize.RandClosed(); if (u < probMut) { u = Randomize.RandClosed(); /* We mutate the center of input "entrada" in the rule "reg" of the first child */ if (u < 0.5) { Descen.get(0).RuleBase[reg].memfunctions[entrada].center *= factor; } /* We mutate the width of input "entrada" in the rule "reg" of the first child */ else { Descen.get(0).RuleBase[reg].memfunctions[entrada].width *= factor; } } /* Second child */ reg = Randomize.RandintClosed(0, Descen.get(1).num_reglas-1); entrada = Randomize.RandintClosed(0, entradas); factor = Randomize.RanddoubleClosed(0.9, 1.1); u = Randomize.RandClosed(); if (u < probMut) { u = Randomize.RandClosed(); /* We mutate the center of input "entrada" in the rule "reg" of the first child */ if (u < 0.5) { Descen.get(1).RuleBase[reg].memfunctions[entrada].center *= factor; } /* We mutate the width of input "entrada" in the rule "reg" of the first child */ else { Descen.get(1).RuleBase[reg].memfunctions[entrada].width *= factor; } } } /** * <p> * It prints the current population as a String * </p> * @return String The current population as a String */ public String Print_Population() { int i, j; String output = new String(""); output += "Rule Base with " + Poblacion.get(0).num_reglas + " rules\n\n"; for (i = 0; i < Poblacion.get(0).num_reglas; i++) { output += "Rule " + (i + 1) + ": IF "; for (j = 0; j < entradas; j++) { output += "X(" + (j + 1) + ") is Gaussian(" + Poblacion.get(0).RuleBase[i].memfunctions[j].center + ", " + Poblacion.get(0).RuleBase[i].memfunctions[j].width + ")"; if (j != (entradas - 1)) { output += " and "; } } output += " THEN Y is Gaussian(" + Poblacion.get(0).RuleBase[i].memfunctions[entradas].center + ", " + Poblacion.get(0).RuleBase[i].memfunctions[entradas].width + ")\n\n"; ; } return (output); } /** * <p> * It generates the output file from a given dataset and stores it in a file * </p> * @param dataset myDataset input dataset * @param filename String the name of the file */ private void doOutput(myDataset dataset, String filename) { int i; double fuerza; String output = new String(""); output = dataset.copyHeader(); //we insert the header in the output file for (i = 0; i < dataset.getnData(); i++) { fuerza = Output_fuzzy_system(dataset, Poblacion.get(0), dataset.getExample(i)); output += (dataset.getOutputAsReal(i) + " " + fuerza + " " + "\n"); } Files.writeFile(filename, output); } }