/*********************************************************************** 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.Fuzzy_Rule_Learning.Genetic.ClassifierFuzzySGERD; /** * <p>Codifies a Fuzzy Rule</p> * * @author Written by Alberto Fern�ndez (University of Granada) 29/10/2007 * @author Modified by Jesus Alcala Fernandez (University of Granada) 20/05/2009 * @version 1.5 * @since JDK1.5 */ public class Rule implements Comparable { int[] antecedent; int clas; double threshold, subspaceSize, fitness; int active, kj, firstActive, typeEvaluation, newRule; DataBase dataBase; /** * Copy Constructor * @param r Rule the rule to be copied */ public Rule(Rule r) { this.antecedent = r.antecedent.clone(); this.dataBase = r.dataBase; this.clas = r.clas; this.active = 0; this.dataBase = r.dataBase; this.typeEvaluation = r.typeEvaluation; this.newRule = r.newRule; this.threshold = r.threshold; this.subspaceSize = r.subspaceSize; this.fitness = r.fitness; this.active = r.active; this.kj = r.kj; this.firstActive = r.firstActive; } /** * Constructor with parameters * @param dataBase DataBase the DB * @param typeEvaluation int a code for the compatibility degre computation */ public Rule(DataBase dataBase, int typeEvaluation) { this.antecedent = new int[dataBase.numVariables()]; for (int i = 0; i < this.antecedent.length; i++) { this.antecedent[i] = -1; // Don't care } this.clas = -1; this.dataBase = dataBase; this.typeEvaluation = typeEvaluation; this.active = 0; this.firstActive = -1; this.newRule = 0; this.kj = 0; } /** * It assings the ancedent of the Rule * @param antecedent int[] An array containing the fuzzy labels */ public void asignAntecedent(int[] antecedent) { boolean first; first = true; this.active = 0; this.firstActive = -1; for (int i = 0; i < antecedent.length; i++) { this.antecedent[i] = antecedent[i]; if (this.antecedent[i] >= 0) { this.active++; if (first) { this.firstActive = i; first = false; } } } } /** * It carries out a copy of the current rule * @return Rule an exact copy of the rule */ public Rule clone() { Rule r = new Rule(this.dataBase, this.typeEvaluation); r.antecedent = new int[antecedent.length]; for (int i = 0; i < this.antecedent.length; i++) { r.antecedent[i] = this.antecedent[i]; } r.clas = this.clas; r.dataBase = this.dataBase; r.typeEvaluation = this.typeEvaluation; r.newRule = this.newRule; r.threshold = this.threshold; r.subspaceSize = this.subspaceSize; r.fitness = this.fitness; r.active = this.active; r.kj = this.kj; r.firstActive = this.firstActive; return (r); } /** * Procedure to compute the best consequent for a given rule * @param train myDataset the tranning set */ public void computeConsequent(myDataset train) { int i, best_class; int n_classes = train.getnClasses(); double comp, total, best_comp, second_comp, aux; double[] sum_classes = new double[n_classes]; /* sum_classes accumulates the compatibility degree of the antecedent of the rule with the examples of each class */ for (i = 0; i < n_classes; i++) { sum_classes[i] = 0.0; } total = 0.0; /* We computed the sum per classes */ for (i = 0; i < train.size(); i++) { comp = compatibility(train.getExample(i)); if (comp > 0.0) { sum_classes[train.getOutputAsInteger(i)] += comp; total += comp; } } for (i = 0; i < n_classes; i++) { sum_classes[i] /= total; } best_class = 0; best_comp = sum_classes[0]; for (i = 1; i < n_classes; i++) { comp = sum_classes[i]; if (comp >= best_comp) { best_class = i; best_comp = comp; } } this.clas = best_class; //I assign the class } /** * Evaluates the rule for computing its fitness * @param train myDataset the trianing set * @return double the fitness of the rule */ public double evaluation(myDataset train) { int i, numberPositiveExamplesDS, label, partition; double comp, total; double negativeExamples, positiveExamples; this.kj = this.lengthRule(); this.threshold = Math.pow (0.5, this.kj); total = 0.0; positiveExamples = negativeExamples = 0.0; numberPositiveExamplesDS = 0; /* Se calcula la suma por clases */ for (i = 0; i < train.size(); i++) { comp = compatibility(train.getExample(i)); if (comp > 0.0) { if (train.getOutputAsInteger(i) == this.clas) { positiveExamples += comp; if (comp > this.threshold) { numberPositiveExamplesDS++; } } else { negativeExamples += comp; } total += comp; } } if (this.typeEvaluation == 0) { this.fitness = Math.pow(positiveExamples, 2.0) / total; // fCS (8) } else if (this.typeEvaluation == 1) { this.fitness = positiveExamples - negativeExamples; // fF (10) } else { this.fitness = (positiveExamples - negativeExamples) + (numberPositiveExamplesDS * (1.0 - this.threshold)); // fFModified (13) } this.subspaceSize = 1; for (i = 0; i < this.antecedent.length; i++) { if (this.isActive(i)) { partition = this.dataBase.partition(this.antecedent[i]); label = this.dataBase.label(this.antecedent[i]); this.subspaceSize *= this.dataBase.covering(partition, i, label); } } if (this.kj <= (train.getnInputs() / 2.0)) { return (this.fitness); } else { this.fitness = (this.subspaceSize / Math.pow(train.getOverlapping(this.clas), this.kj)) * this.fitness; return (this.fitness); } } /** * Sets the class for the rule * @param clas int the class id */ public void setClass(int clas) { this.clas = clas; } /** * It sets the class for this rule * @param train myDataset the training set */ public void setConsequent(myDataset train) { computeConsequent(train); } /** * It computes the compatibility degree of an example for this rule * @param example double[] the example * @return double the compatibility degree */ public double compatibility(double[] example) { return product_t_norm(example); } /** * Product T-norm computation * @param example double[] Example * @return double the product T-norm */ private double product_t_norm(double[] example) { double product, membership_degree; int label, k; product = 1.0; for (int i = 0; (i < antecedent.length) && (product > 0); i++) { label = antecedent[i]; membership_degree = dataBase.membership(dataBase.partition(label), i, dataBase.label(label), example[i]); product = product * membership_degree; } return (product); } /** * Sets this rule as new */ public void onNew() { this.newRule = 1; } /** * Sets this rule as "non-new" */ public void offNew() { this.newRule = 0; } /** * It returns if the rule is new or not * @return boolean true if the rule is new, false otherwise */ public boolean isNew() { if (this.newRule > 0) { return (true); } else { return (false); } } /** * It returns the class of the rule * @return int the class of the rule */ public int getClas() { return (this.clas); } /** * It returns the activation of this rule * @return int the activation of this rule */ public int getActive() { return (this.active); } /** * It returns the number of valid labels for the rule until a given position * @param value int the position of the rule * @return int the number of valid labels for the rule until a given position */ public int getPosActive(int value) { int pos, nActive; for (pos = 0, nActive = 0; nActive < value; pos++) { if (this.antecedent[pos] >= 0) { nActive++; } } pos--; return (pos); } /** * It returns the length of the rule * @return int the length of the rule */ public int lengthRule() { return (this.active); } /** * It returns if a given condition is active or not * @param pos int the position * @return boolean true if the condition has a valid label, false otherwise */ public boolean isActive(int pos) { if (this.antecedent[pos] >= 0) { return (true); } else { return (false); } } /** * Classical Certainty Factor Computation (confidence of the rule) * @param train myDataset trainning set */ private void consequent_CF(myDataset train) { double[] sum_classes = new double[train.getnClasses()]; for (int i = 0; i < train.getnClasses(); i++) { sum_classes[i] = 0.0; } double total = 0.0; double comp; /* We compute the sum per classes */ for (int i = 0; i < train.size(); i++) { comp = this.compatibility(train.getExample(i)); sum_classes[train.getOutputAsInteger(i)] += comp; total += comp; } //weight = sum_classes[clas] / total; } /** * Heuristic Certainty Factor Computation by Ishibuchi. Rule Weight Heuristic II * @param train myDataset trainning set */ private void consequent_PCF2(myDataset train) { double[] sum_classes = new double[train.getnClasses()]; for (int i = 0; i < train.getnClasses(); i++) { sum_classes[i] = 0.0; } double total = 0.0; double comp; /* We compute the sum per classes */ for (int i = 0; i < train.size(); i++) { comp = this.compatibility(train.getExample(i)); sum_classes[train.getOutputAsInteger(i)] += comp; total += comp; } double sum = (total - sum_classes[clas]) / (train.getnClasses() - 1.0); } /** * Heuristic Certainty Factor Computation by Ishibuchi. Rule Weight Heuristic IV (penalised certainty factor) * @param train myDataset trainning set */ private void consequent_PCF4(myDataset train) { double[] sum_classes = new double[train.getnClasses()]; for (int i = 0; i < train.getnClasses(); i++) { sum_classes[i] = 0.0; } double total = 0.0; double comp; /* We compute the sum per classes */ for (int i = 0; i < train.size(); i++) { comp = this.compatibility(train.getExample(i)); sum_classes[train.getOutputAsInteger(i)] += comp; total += comp; } double sum = total - sum_classes[clas]; } /** * It sets a new label for the rule * @param pos int the position in the antecedent * @param label int the label id */ public void setLabel(int pos, int label) { if ((this.antecedent[pos] < 0) && (label >= 0)) this.active++; if ((this.antecedent[pos] >= 0) && (label < 0)) this.active--; this.antecedent[pos] = label; } /** * Compares the fitness of two rules for the ordering procedure * @param a Object a Rule * @return int -1 if the current rule is worst than the one that is compared, 1 for the contrary case and 0 * if they are equal. */ public int compareTo(Object a) { if ( ( (Rule) a).fitness < this.fitness) { return -1; } if ( ( (Rule) a).fitness > this.fitness) { return 1; } return 0; } }