/***********************************************************************
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.OIGA;
import org.core.*;
import keel.Dataset.*;
import java.util.*;
/**
* <p>
* This class represents a set of rules in the OIGA algorithm
* </p>
*
* <p>
* @author Written by Juli�n Luengo Mart�n 08/02/2007
* @version 0.2
* @since JDK 1.5
* </p>
*/
public class RuleSet implements Comparable{
Rule reglas[];
int nAtt;
int numberRules;
double fitness;
boolean evaluated;
/**
* <p>
* Default constructor. No memory allocation
* </p>
*/
public RuleSet(){
reglas = null;
}
/**
* Constructor for a fixed number of rules and attributes
* @param numberRules number of rules of the set
* @param numAtt number of attributes of each rule
*/
public RuleSet(int numberRules,int numAtt){
reglas = new Rule[numberRules];
this.numberRules = numberRules;
nAtt = numAtt;
evaluated = false;
for(int i=0;i<numberRules;i++){
reglas[i] = new Rule(numAtt);
reglas[i].setLength(numAtt);
}
}
/**
* Deep-copy constructor
* @param orig the set of rules which will be copied over this set
*/
public RuleSet(RuleSet orig){
this.numberRules = orig.numberRules;
reglas = new Rule[numberRules];
for(int i=0;i<numberRules;i++)
reglas[i] = new Rule(orig.reglas[i]);
nAtt = orig.nAtt;
fitness = orig.fitness;
evaluated = orig.evaluated;
}
/**
* Reset the current rules, and creates a new -clean- set
* @param numberRules number of rules of the set
* @param numAtt number of attributes of each rule
*/
public void createRules(int numberRules,int numAtt){
reglas = new Rule[numberRules];
this.numberRules = numberRules;
nAtt = numAtt;
evaluated = false;
for(int i=0;i<numberRules;i++){
reglas[i] = new Rule(numAtt);
reglas[i].setLength(numAtt);
}
}
/**
* Initialize the set of rules
* @param IS train data set used for initialization
*/
public void randomizeRules(InstanceSet IS){
Attribute a =Attributes.getOutputAttribute(0);
int numClasses,clase;
double median;
Instance inst;
double value;
if(a.getType() == Attribute.NOMINAL)
numClasses = a.getNumNominalValues();
else
numClasses =(int)( a.getMaxAttribute() - a.getMinAttribute());
for(int j =0;j<nAtt;j++){
a = Attributes.getInputAttribute(Oiga.attributeOrder[j]);
for(int i=0;i<numberRules;i++){
median = a.getMaxAttribute() - a.getMinAttribute();
median = median/2.0;
reglas[i].setActivation(j,Randomize.Rand()<0.5);
if(a.getType() != Attribute.NOMINAL)
reglas[i].setLimits(j,Randomize.RanddoubleClosed(a.getMinAttribute(), a.getMaxAttribute()), Randomize.RanddoubleClosed(a.getMinAttribute(), a.getMaxAttribute()));
else
reglas[i].setLimits(j,Randomize.Randint(0, a.getNumNominalValues()), Randomize.Randint(0, a.getNumNominalValues()));
// reglas[i].setLimits(j,Randomize.RanddoubleClosed(a.getMinAttribute(), median), Randomize.RanddoubleClosed(median, a.getMaxAttribute()));
// if(reglas[i].getLimits(j)[0] > reglas[i].getLimits(j)[1])
// reglas[i].setLimits(j,reglas[i].getLimits(j)[1],reglas[i].getLimits(j)[0]);
if(j==0){
reglas[i].setClass(Randomize.Randint(0,numClasses));
// reglas[i].setClass(Math.abs(i-Randomize.Randint(0,numClasses))%numClasses);
}
// for(int l=0;l<IS.getNumInstances();l++){
// inst = IS.getInstance(l);
//
// value = inst.getAllInputValues()[Oiga.attributeOrder[j]];
// clase = (int)inst.getAllOutputValues()[0];
// if(reglas[i].getActivation(j) && clase == reglas[i].getClas()){
// if(reglas[i].getLimits(j)[0] > value){
// reglas[i].setLimits(j,value,reglas[i].getLimits(j)[1]);
// reglas[i].setActivation(j,false);
// }
// if(reglas[i].getLimits(j)[1] < value){
// reglas[i].setLimits(j,reglas[i].getLimits(j)[0],value);
// reglas[i].setActivation(j,false);
// }
// }
//
// }
}
}
}
/**
* Classifies the instances of a data sets, and updates the fitness function as
* the number of well classified instances
* @param ISet the data set that will be classified
* @return the obtained fitness
*/
public double classify(InstanceSet ISet){
Attribute a =Attributes.getOutputAttribute(0);
int numClasses;
if(a.getType() == Attribute.NOMINAL)
numClasses = a.getNumNominalValues();
else
numClasses =(int)( a.getMaxAttribute() - a.getMinAttribute());
AttributeCR classVotes[] = new AttributeCR[numClasses];
Rule r;
int classObtained;
double inputs[];
int output;
Instance inst;
if(!evaluated){
// for(int i=0;i<numClasses;i++){
// classVotes[i] = new AttributeCR(i,-1);
// }
fitness = 0;
for(int i=0;i<ISet.getNumInstances();i++){
inst = ISet.getInstance(i);
inputs = inst.getAllInputValues();
// for(int j=0;j<numClasses;j++){
// classVotes[j].CR = 0;
// }
output = (int)inst.getAllOutputValues()[0];
// for(int j=0;j<numberRules;j++){
// r = reglas[j];
// classObtained = r.evaluate(inputs);
// if(classObtained!=-1)
// classVotes[classObtained].CR++;
// }
// Arrays.sort(classVotes,Collections.reverseOrder());
// if(classVotes[0].CR > classVotes[1].CR){
// if(output == classVotes[0].attribute)
// fitness+=1;
// }
classObtained = this.classify(inst);
if(classObtained!=-1 && classObtained==output)
fitness++;
}
fitness /= ISet.getNumInstances();
evaluated = true;
}
return fitness;
}
/**
* Classifies an instance using the set of rules
* @param inst the instance that will be classified
* @return the predicted class, or -1 if not covered by any rule (unknown class)
*/
public int classify(Instance inst){
Attribute a =Attributes.getOutputAttribute(0);
int numClasses;
if(a.getType() == Attribute.NOMINAL)
numClasses = a.getNumNominalValues();
else
numClasses =(int)( a.getMaxAttribute() - a.getMinAttribute());
AttributeCR classVotes[] = new AttributeCR[numClasses];
Rule r;
int classObtained = -1;
double inputs[];
int output;
for(int i=0;i<numClasses;i++){
classVotes[i] = new AttributeCR(i,0);
}
inputs = inst.getAllInputValues();
output = (int)inst.getAllOutputValues()[0];
for(int j=0;j<numberRules;j++){
r = reglas[j];
classObtained = r.evaluate(inputs);
if(classObtained!=-1)
classVotes[classObtained].CR++;
}
Arrays.sort(classVotes,Collections.reverseOrder());
if(classVotes[0].CR == classVotes[1].CR){
classObtained = -1;
}
else{
classObtained = classVotes[0].attribute;
}
return classObtained;
}
/**
* Copies the rules from cutpoint to the end of the rule set
* @param rs rule set from which the rules will be copied
* @param cutpoint_rule the rule in which there is the cutpoint
* @param cutpoint_variable the variable (activation, limits or class) in which the cutpoint is
*/
public void copyFromPointtoEnd(RuleSet rs,int cutpoint_rule,int cutpoint_variable){
Rule rule1 = reglas[cutpoint_rule];
Rule rule2 = rs.reglas[cutpoint_rule];
//copy partial rule if needed
int i = cutpoint_variable;
if(cutpoint_variable%3 == 1){
// rule1.setActivation(cutpoint_variable/3, rule2.getActivation(cutpoint_variable/3));
rule1.setLimits(cutpoint_variable/3, rule2.getLimits(cutpoint_variable/3)[0], rule2.getLimits(cutpoint_variable/3)[1]);
i += 2;
}
if(cutpoint_variable%3 == 2){
rule1.setActivation(cutpoint_variable/3, rule2.getActivation(cutpoint_variable/3));
rule1.setLimits(cutpoint_variable/3, rule1.getLimits(cutpoint_variable/3)[0], rule2.getLimits(cutpoint_variable/3)[1]);
i += 1;
}
for(i = i/3;i<nAtt;i++){
// System.err.println(nAtt+" "+i);
rule1.setActivation(i, rule2.getActivation(i));
rule1.setLimits(i,rule2.getLimits(i)[0], rule2.getLimits(i)[1]);
}
rule1.setClass(rule2.getClas());
//copy complete rules
for(int j=cutpoint_rule+1;j<numberRules;j++){
reglas[j] = new Rule(rs.reglas[j]);
}
}
/**
* Copies the rules from the beginning of the rule set to the selected cutpoint
* @param rs rule set from which the rules will be copied
* @param cutpoint_rule the rule in which there is the cutpoint
* @param cutpoint_variable the variable (activation, limits or class) in which the cutpoint is
*/
public void copyFromBegintoPoint(RuleSet rs,int cutpoint_rule,int cutpoint_variable){
Rule rule1 = reglas[cutpoint_rule];
Rule rule2 = rs.reglas[cutpoint_rule];
// copy complete rules
for(int j=0;j<cutpoint_rule;j++){
reglas[j] = new Rule(rs.reglas[j]);
}
//copy partial rule if needed
if(cutpoint_variable%3 == 1){
rule1.setActivation(cutpoint_variable/3, rule2.getActivation(cutpoint_variable/3));
// rule1.setLimits(cutpoint_variable/3, rule2.getLimits(cutpoint_variable/3)[0], rule1.getLimits(cutpoint_variable/3)[1]);
}
if(cutpoint_variable%3 == 2){
rule1.setActivation(cutpoint_variable/3, rule2.getActivation(cutpoint_variable/3));
rule1.setLimits(cutpoint_variable/3, rule2.getLimits(cutpoint_variable/3)[0], rule1.getLimits(cutpoint_variable/3)[1]);
}
for(int i = 0;i<cutpoint_variable/3;i++){
rule1.setActivation(i, rule2.getActivation(i));
rule1.setLimits(i,rule2.getLimits(i)[0], rule2.getLimits(i)[1]);
}
if(cutpoint_variable%3 == 0 && (cutpoint_variable+1)%numberRules ==0)
rule1.setClass(rule2.getClas());
}
/**
* Mutate a variable of the rule set (i.e. the activation, limits or class of a rule
* from the rule set)
* @param gene the gene (variable) that will be mutated
*/
public void mutate(int gene){
Attribute a = null;
int rule = gene / (3*nAtt+1); //rule affected
int attPos = gene%(3*nAtt+1); //position in the rule string affected
int attNum = attPos/3; // attribute of the data set affected
int att = attPos%3; //attribute property (active, minlimit, maxlimit) affected
if(attNum<nAtt)
a = Attributes.getInputAttribute(Oiga.attributeOrder[attNum]);
if(att == 1){ //min limit value
if(a.getType() != Attribute.NOMINAL)
reglas[rule].setLimits(attNum, Randomize.RanddoubleClosed(a.getMinAttribute(), a.getMaxAttribute()), reglas[rule].getLimits(attNum)[1]);
else
reglas[rule].setLimits(attNum, Randomize.Randint(0, a.getNumNominalValues()), reglas[rule].getLimits(attNum)[1]);
}
if(att == 2){ //max limit value
if(a.getType() != Attribute.NOMINAL)
reglas[rule].setLimits(attNum, reglas[rule].getLimits(attNum)[0] ,Randomize.RanddoubleClosed(a.getMinAttribute(), a.getMaxAttribute()));
else
reglas[rule].setLimits(attNum, reglas[rule].getLimits(attNum)[0] ,Randomize.Randint(0, a.getNumNominalValues()));
}
if(att==0){
if(attPos!=(3*nAtt)) //not the class label in the rule
reglas[rule].setActivation(attNum, !reglas[rule].getActivation(attNum));
else //class label
reglas[rule].setClass(Randomize.Randint(0, Attributes.getOutputAttribute(0).getNumNominalValues()));
}
}
/**
* Incremental Genetic Algorithm, which increases the size of the actual rule set
* appending all the attributes from rs to the rules of the actual rule set.
* The rules used for this task should have the same class as the rule from our
* rule set, but if we cannot find it, we use any rule.
* @param rs the rule set from which we will append the attributes
*/
public void iga(RuleSet rs){
Attribute a =Attributes.getOutputAttribute(0);
int numClasses;
if(a.getType() == Attribute.NOMINAL)
numClasses = a.getNumNominalValues();
else
numClasses =(int)( a.getMaxAttribute() - a.getMinAttribute());
Rule r,s;
int rnd;
int curClass;
Vector pool = new Vector();
for(int i=0;i<numberRules;i++){
r = reglas[i];
curClass = r.getClas();
//if it is not a created rule, assign a random class
//and append the attribute from SEM
if(curClass == -1){
r.setClass(Randomize.Randint(0, numClasses));
curClass = r.getClas();
}
for(int j=0;j<rs.numberRules;j++){
if(curClass == rs.reglas[j].getClas())
pool.addElement(rs.reglas[j]);
}
if(pool.size()!=0){
rnd = Randomize.Randint(0, pool.size());
s = (Rule)pool.elementAt(rnd);
}
else{
System.out.print(".");
System.out.flush();
s = rs.reglas[Randomize.Randint(0,rs.numberRules)];
}
r.append(s);
}
this.nAtt += rs.nAtt;
}
/**
* Test if the rule set is currently evaluated
* @return if the rule set is evaluated, so its fitness is OK
*/
public boolean isEvaluated(){
return evaluated;
}
/**
* Returns the evaluation state
* @param eval the evaluation state (true or false) of this rule set
*/
public void setEvaluated(boolean eval){
evaluated = eval;
}
/**
* Gets the fitness of this rule set
* @return the current fitness of the rule set (updated or NOT!)
*/
public double getFitness(){
return fitness;
}
/**
* It compares the RuleSet with other one
*
* @param o the rule set to compare
* @return 0 if both rule sets have the same fitness, 1 if the fitness of this is higher, and -1 otherwise
*/
public int compareTo(Object o){
RuleSet rs = (RuleSet) o;
if(this.fitness < rs.fitness)
return -1;
if(this.fitness > rs.fitness)
return 1;
return 0;
}
/**
* Test if the fitness of the rule sets are equal
* @param rs the rule set which will be compared to ours
* @return if they have equal fitness or not
*/
public boolean equals(RuleSet rs){
return (this.fitness == rs.fitness);
}
}