/*********************************************************************** 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.Genetic_Rule_Learning.OCEC; /** * <p>Title: Algorithm</p> * * <p>Description: It contains the implementation of the algorithm</p> * * * <p>Company: KEEL </p> * * @author Alberto Fern�ndez * @version 1.0 */ import java.io.IOException; import org.core.*; import java.util.Arrays; public class OCEC { public static int MIGRATING = 0; public static int EXCHANGING = 1; public static int MERGING = 2; myDataset train, val, test; String outputTr, outputTst, ficheroBR; int nClasses, nGenerations, N, n; Poblacion[] p; Attribute a; BaseR baseReglas; private boolean somethingWrong = false; //to check if everything is correct. /** * Default constructor */ public OCEC() { } /** * It reads the data from the input files (training, validation and test) and parse all the parameters * from the parameters array. * @param parameters parseParameters It contains the input files, output files and parameters */ public OCEC(parseParameters parameters) { train = new myDataset(); val = new myDataset(); test = new myDataset(); try { System.out.println("\nReading the training set: " + parameters.getTrainingInputFile()); train.readClassificationSet(parameters.getTrainingInputFile(), true); System.out.println("\nReading the validation set: " + parameters.getValidationInputFile()); val.readClassificationSet(parameters.getValidationInputFile(), false); System.out.println("\nReading the test set: " + parameters.getTestInputFile()); test.readClassificationSet(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.hasRealAttributes(); //somethingWrong = somethingWrong || train.hasMissingAttributes(); //si hay valores reales empleamos una discretizacion uniforme en anchura (5) //if (train.hasRealAttributes()) { if (train.hasNumericalAttributes()) { train.discretize(5); val.discretize(5); test.discretize(5); } outputTr = parameters.getTrainingOutputFile(); outputTst = parameters.getTestOutputFile(); ficheroBR = parameters.getOutputFile(0); //Now we parse the parameters long semilla = Long.parseLong(parameters.getParameter(0)); nGenerations = Integer.parseInt(parameters.getParameter(1)); //N = Integer.parseInt(parameters.getParameter(2)); n = Integer.parseInt(parameters.getParameter(2)); N = (int) (0.1 * train.size()); //10% of the examples if (N > train.sumMinorityClasses()){ N = train.sumMinorityClasses(); } Randomize.setSeed(semilla); } /** * It launches the algorithm */ public void execute() { 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 nClasses = train.getnClasses(); a = new Attribute(train.nombres()); p = new Poblacion[nClasses]; for (int i = 0; i < nClasses; i++) { p[i] = new Poblacion(i, a, train); } baseReglas = algoritmo(); baseReglas.printFichero(this.ficheroBR); //Finally we should fill the training and test output files double accTr = doOutput(this.val, this.outputTr); double accTst = doOutput(this.test, this.outputTst); System.out.println("Number of Rules: " + baseReglas.size()); System.out.println("Accuracy in training: " + accTr); System.out.println("Accuracy in test: " + accTst); System.out.println("Algorithm Finished"); } } /** * It generates the output file from a given dataset and stores it in a file * @param dataset myDataset input dataset * @param filename String the name of the file * @return the Accuracy of the classifier */ private double doOutput(myDataset dataset, String filename) { String output = new String(""); output = dataset.copyHeader(); //we insert the header in the output file int aciertos = 0; //We write the output for each example for (int i = 0; i < dataset.getnData(); i++) { //for classification: String claseReal = dataset.getOutputAsString(i); String prediccion = this.classificationOutput(dataset.getExample(i)); output += claseReal + " " + prediccion + "\n"; if (claseReal.equalsIgnoreCase(prediccion)) { aciertos++; } } Fichero.escribeFichero(filename, output); return (1.0 * aciertos / dataset.size()); } /** * It returns the algorithm classification output given an input example * @param example double[] The input example * @return String the output generated by the algorithm */ private String classificationOutput(double[] example) { return baseReglas.clasifica(example); } private BaseR algoritmo() { BaseR baseReglas = new BaseR(train); Poblacion nueva = new Poblacion(); for (int i = 0; i < nGenerations; i++) { //System.out.print("Generation[" + i + "]: " + a.printString()); for (int j = 0; j < p.length; j++) { //para todas las poblaciones //System.out.println("Working with population P[" + j + "]: "+p[j].size()); //p[j].print(); while (p[j].size() > 1) { //Hay m�s de una organizacion //System.out.println("Working with population P[" + j + "]: "+p[j].size()); //p[j].print(); int aleat1, aleat2; aleat1 = Randomize.RandintClosed(0, p[j].size()-1); do { aleat2 = Randomize.RandintClosed(0, p[j].size()-1); } while (aleat2 == aleat1); Organizacion org1 = p[j].dameOrganizacion(aleat1); Organizacion org2 = p[j].dameOrganizacion(aleat2); int operador = Randomize.RandintClosed(0, 2); //uno entre tres //compruebo si elijo migrar cuando en verdad sera mezclar (porque solo hay un miembro) if ( (operador == this.MIGRATING) && (org1.miembros.length == 1)) { operador = this.MERGING; } if (operador == this.MIGRATING) { Organizacion orgc1 = new Organizacion(a, train); Organizacion orgc2 = new Organizacion(a, train); migrar(org1, org2, orgc1, orgc2); calculaSignificancia(orgc1, j); calculaSignificancia(orgc2, j); orgc1.calculaFitness(); orgc2.calculaFitness(); if (Math.max(orgc1.fitness, orgc2.fitness) > Math.max(org1.fitness, org2.fitness)) { //borrar ambos padres y meter los hijos if (orgc1.tipo == Organizacion.ANORMAL) { //es ANORMAL y tengo que generar organizaciones triviales a partir de esta generarTriviales(nueva, orgc1); //genero e inserto } else { nueva.organizaciones.add(orgc1); } if (orgc2.tipo == Organizacion.ANORMAL) { //es ANORMAL y tengo que generar organizaciones triviales a partir de esta generarTriviales(nueva, orgc2); //genero e inserto } else { nueva.organizaciones.add(orgc2); } } else { //mantener ambos padres en otro caso nueva.organizaciones.add(org1.copia()); nueva.organizaciones.add(org2.copia()); } //Borro los dos padres p[j].organizaciones.remove(org1); p[j].organizaciones.remove(org2); } else if (operador == this.MERGING) { //merging //System.out.println("Merging operator"); Organizacion orgc = new Organizacion(org1, org2); //mezcla de ambos calculaSignificancia(orgc, j); orgc.calculaFitness(); //Como solo hay un hijo, pasa directamente a la siguiente generacion if (orgc.tipo == Organizacion.ANORMAL) { //es ANORMAL y tengo que generar organizaciones triviales a partir de esta generarTriviales(nueva, orgc); //genero e inserto } else { nueva.organizaciones.add(orgc); } //Borro los dos padres p[j].organizaciones.remove(org1); p[j].organizaciones.remove(org2); } else if ( (operador == this.EXCHANGING) && ( (org1.miembros.length > 1) || (org2.miembros.length > 1))) { //System.out.println("Exchanging operator"); Organizacion orgc1 = new Organizacion(a, train); Organizacion orgc2 = new Organizacion(a, train); intercambiar(org1, org2, orgc1, orgc2); calculaSignificancia(orgc1, j); calculaSignificancia(orgc2, j); orgc1.calculaFitness(); orgc2.calculaFitness(); if (Math.max(orgc1.fitness, orgc2.fitness) > Math.max(org1.fitness, org2.fitness)) { //Inserto los hijos if (orgc1.tipo == Organizacion.ANORMAL) { //es ANORMAL y tengo que generar organizaciones triviales a partir de esta generarTriviales(nueva, orgc1); //genero e inserto } else { nueva.organizaciones.add(orgc1); } if (orgc2.tipo == Organizacion.ANORMAL) { //es ANORMAL y tengo que generar organizaciones triviales a partir de esta generarTriviales(nueva, orgc2); //genero e inserto } else { nueva.organizaciones.add(orgc2); } } else { //mantener ambos padres en otro caso nueva.organizaciones.add(org1.copia()); nueva.organizaciones.add(org2.copia()); } //Borro los dos padres p[j].organizaciones.remove(org1); p[j].organizaciones.remove(org2); } } //while (hay mas de una organizacion) //Actualizo la poblacion insertando todas las generadas: p[j].actualiza(nueva); nueva.limpia(); } } for (int i = 0; i < p.length; i++) { p[i].eliminarNoUtiles(); p[i].mezclar(); //junta dos organizaciones si los atributos �tiles de una estan contenidos en la otra (y tienen //lo mismos valores, obviamente. baseReglas.incluir(p[i]); //construyo y almaceno las reglas de p[i] } baseReglas.ordenar(); baseReglas.eliminarSubsumidas(); System.out.println("Base de Reglas: " + baseReglas.printString()); return baseReglas; } private void migrar(Organizacion padre, Organizacion madre, Organizacion hijo1, Organizacion hijo2) { hijo1.clase = padre.clase; hijo2.clase = padre.clase; int n_ = Math.min(n, padre.miembros.length - 1); //del padre a la madre int[] miembros = new int[n_]; int contador = 0; //Selecciono la posicion de los miembros del padre que voy a migrar do { int aleat; boolean condicion = false; do { aleat = Randomize.RandintClosed(0, padre.miembros.length-1); //elijo un miembro (posicion) for (int i = 0; (i < contador) && (!condicion); i++) { condicion = (miembros[i] == aleat); //si ya lo he elegido no me vale } } while (condicion); miembros[contador] = aleat; //guardo el miembro escogido contador++; } while (contador < n_); contador = 0; Arrays.sort(miembros); //ordeno las posiciones (ej: {2, 3, 0} -> {0, 2, 3} //Primero elimino los migrados del hijo1 int contador2 = 0; hijo1.miembros = new int[padre.miembros.length - n_]; for (int i = 0; i < padre.miembros.length; i++) { boolean tomarValor = true; for (int j = contador2; j < miembros.length; j++) { tomarValor = tomarValor && (i != miembros[j]); } if (tomarValor) { hijo1.miembros[contador] = padre.miembros[i]; contador++; } else { contador2++; } } //Ahora migro al hijo2 hijo2.miembros = new int[madre.miembros.length + n_]; //copio todos los de la madre for (int i = 0; i < madre.miembros.length; i++) { hijo2.miembros[i] = madre.miembros[i]; } //y ahora los migrados del padre for (int i = madre.miembros.length; i < madre.miembros.length + n_; i++) { hijo2.miembros[i] = padre.miembros[miembros[i - madre.miembros.length]]; } } private void intercambiar(Organizacion padre, Organizacion madre, Organizacion hijo1, Organizacion hijo2) { hijo1.clase = padre.clase; hijo2.clase = padre.clase; int n_ = Math.min(n, (int) Math.min(padre.miembros.length, madre.miembros.length)); //puedo intercambiar n �, el minimo de miembros (padre � madre) int[] miembrosP = new int[n_]; //posiciones de los miembros a intercambiar int[] miembrosM = new int[n_]; //posiciones de los miembros a intercambiar int contador = 0; do { int aleat1, aleat2; boolean condicion = false; do { aleat1 = Randomize.RandintClosed(0, padre.miembros.length-1); //elijo un miembro (posicion) for (int i = 0; (i < contador) && (!condicion); i++) { condicion = (miembrosP[i] == aleat1); //si ya lo he elegido no me vale } } while (condicion); condicion = false; do { aleat2 = Randomize.RandintClosed(0, madre.miembros.length-1); //elijo un miembro (posicion) for (int i = 0; (i < contador) && (!condicion); i++) { condicion = (miembrosM[i] == aleat2); //si ya lo he elegido no me vale } } while (condicion); miembrosP[contador] = aleat1; //guardo el miembro escogido del padre miembrosM[contador] = aleat2; //guardo el miembro escogido de la madre contador++; } while (contador < n_); contador = 0; //Primero copio todos hijo1.miembros = new int[padre.miembros.length]; for (int i = 0; i < padre.miembros.length; i++) { hijo1.miembros[i] = padre.miembros[i]; } hijo2.miembros = new int[madre.miembros.length]; for (int i = 0; i < madre.miembros.length; i++) { hijo2.miembros[i] = madre.miembros[i]; } //y ahora los intercambio for (int i = 0; i < miembrosP.length; i++) { int temp = hijo2.miembros[miembrosM[i]]; //saco el miembro de la madre hijo2.miembros[miembrosM[i]] = hijo1.miembros[miembrosP[i]]; //le pongo el miembro del padre hijo1.miembros[miembrosP[i]] = temp; //copio el miembro de la madre } } private void calculaSignificancia(Organizacion org, int pobl) { org.determinaForg(); //Determinar los atributos fijos (los que toman el mismo valor para todos los miembros) org.limpiaUtiles(); //Uorg = conjunto vacio (todos a false) for (int i = 0; i < org.Forg.length; i++) { //para todos los atributos fixed if (org.Forg[i]) { //Es un atributo Fixed int aleat; do { aleat = Randomize.RandintClosed(0, p.length-1); } while ((aleat == pobl)||(p[aleat].size() == 0)); Organizacion orgPrima = p[aleat].dameOrganizacion(Randomize. RandintClosed( 0, p[aleat].size()-1)); //org con clase != if (orgPrima.estaYdistinto(i, org.ForgValue[i])) { org.addU(i); //a�adir a Uorg } else { a.reducir(i); //reducir S_A_j } } } org.actualizarUorg(N); } private void generarTriviales(Poblacion p, Organizacion org) { int miembros = org.miembros.length; //System.err.println("anormal"); for (int i = 0; i < miembros; i++) { Organizacion nuevaOrg = new Organizacion(org.miembros[i], a, train); p.organizaciones.add(nuevaOrg); } } }