/***********************************************************************
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/
**********************************************************************/
/**
* <p>
* @author Written by Albert Orriols (La Salle, Ram�n Llull University - Barcelona) 28/03/2004
* @author Modified by Xavi Sol� (La Salle, Ram�n Llull University - Barcelona) 03/12/2008
* @version 1.1
* @since JDK1.2
* </p>
*/
package keel.Algorithms.Genetic_Rule_Learning.XCS;
import keel.Algorithms.Genetic_Rule_Learning.XCS.KeelParser.Config;
import java.util.*;
import java.lang.*;
import java.io.*;
public class Representation {
/**
* <p>
* A classifer can contain three types of representations: ternary
* representatiton (each alelle can take 3 possible values, 0, 1 or don't
* care), real representation, where each alelle can take any real value,
* and the mixed one, that can take character or real representation for
* each alelle. In fact, the real representation is the mixed representation
* with all the alelles being reals.
* </p>
*/
///////////////////////////////////////
// attributes
/**
* <p>
* Represents the action of the classifier.
* </p>
*/
private int action;
///////////////////////////////////////
// associations
/**
* <p>
* It is an array of references to attributes
* </p>
*/
private Attribute rep[];
/**
* <p>
* It creates a new representation with the specified condition and a
* random action.
* </p>
* @param envState is the environtment state.
*/
public Representation(double[] envState) {
int i = 0;
rep = new Attribute [Config.clLength];
if (Config.ternaryRep){
for (i=0; i<rep.length; i++) rep[i] = new TernaryRep(envState[i]);
}
else{
for (i=0; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("character"))
rep[i] = new TernaryRep(envState[i]);
else if (Config.typeOfAttributes[i].equals("integer"))
rep[i] = new IntegerRep(envState[i],i);
else
rep[i] = new RealRep(envState[i]);
}
}
action = (int) (Config.rand() * (double) Config.numberOfActions);
if (action == Config.numberOfActions) action --;
} // end Representation
/**
* <p>
* It creates a new representation with the specified condition and action.
* </p>
* <p>
* @param envState is the environment state
* @param act is the action that the classifier has to take.
* </p>
*/
public Representation(double[] envState, int act) {
int i = 0;
rep = new Attribute [Config.clLength];
if (Config.ternaryRep){ // It's a ternary representation. We have to create an array.
for (i=0; i<rep.length; i++){
rep[i] = new TernaryRep(envState[i]);
}
}
else{
for (i=0; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("character"))
rep[i] = new TernaryRep(envState[i]);
else if (Config.typeOfAttributes[i].equals("integer"))
rep[i] = new IntegerRep(envState[i],i);
else rep[i] = new RealRep(envState[i]);
}
}
action = act;
} // end Representation
/**
* <p>
* It creates a new representation that is a clone of the representation
* passed.
* </p>
* @param r is the representation that has to be cloned.
*/
public Representation(Representation r) {
int i = 0;
rep = new Attribute[Config.clLength];
if (Config.ternaryRep){ // It's a ternary representation. We have to create an array.
for (i=0; i<rep.length; i++){
rep[i] = new TernaryRep((TernaryRep)r.rep[i]);
}
}
else{
for (i=0; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("character")){
rep[i] = new TernaryRep((TernaryRep)r.rep[i]);
}else if (Config.typeOfAttributes[i].equals("integer")){
rep[i] = new IntegerRep((IntegerRep)r.rep[i]);
}else{
rep[i] = new RealRep((RealRep)r.rep[i]);
}
}
}
action = r.action;
} //end Representation
/**
* <p>
* It creates a new representation that is a clone of the representation
* passed.
* </p>
* @param t is an String that contains the representation that has to be cloned.
*/
public Representation(StringTokenizer t) {
int i = 0;
rep = new Attribute[Config.clLength];
if (Config.ternaryRep){ // It's a ternary representation. We have to create an array.
for (i=0; i<rep.length; i++){
rep[i] = new TernaryRep( t.nextToken().charAt(0));
}
}
else{
for (i=0; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("character")){
rep[i] = new TernaryRep (t.nextToken().charAt(0));
}
else if (Config.typeOfAttributes[i].equals("integer")){
rep[i] = new IntegerRep ( (int) (new Double(t.nextToken())).doubleValue(), (int) (new Double(t.nextToken())).doubleValue(), i );
}
else{
rep[i] = new RealRep ( (new Double(t.nextToken())).doubleValue(), (new Double(t.nextToken())).doubleValue());
}
}
}
action = new Integer(t.nextToken()).intValue();
} // end Representation
/**
* <p>
* Sets the action passed as a parameter.
* </p>
* @param act is the action to be set.
*/
public void setAction(int act) {
action = act;
} // end setAction
/**
* <p>
* It returns the action of the current classifier.
* </p>
* @return a int with the action
*/
public int getAction() {
return action;
} // end getAction
/**
* <p>
* It returns the generality of the classifier.
* </p>
* @return a double with the generality of this classifier representation.
*/
public double getGenerality() {
double genSum = 0.;
for (int i=0; i<rep.length; i++){
genSum += rep[i].getGenerality();
}
return genSum;
} // end getGenerality
/**
* <p>
* Mutates the classifier. It mutates the action and the condition,
* according to the probability of mutation.
* </p>
* return a boolean indicating if the action has been mutated.
*/
public boolean mutate(double[] currentState) {
int i=0;
for (i=0; i<rep.length; i++){
rep[i].mutate(currentState[i]);
}
//Now, the action has to be mutated.
return mutateAction();
} // end mutate
/**
* <p>
* Mutates the action of the classifier.
* </p>
* return a boolean indicating if the action has been mutated.
*/
public boolean mutateAction() {
int act = 0;
if (Config.rand() < Config.pM){
do{
act = (int)(Config.rand() * (double)Config.numberOfActions);
}while (action == act);
action = act;
return true;
}
return false;
} // end mutateAction
/**
* <p>
* Returns the value of the alelle
* </p>
* @param i is the position.
* @return a real with the value of the lower allele
*/
public double getLowerAllele(int i) {
return rep[i].getLowerAllele();
} // end getLowerAllele
/**
* <p>
* Returns the value of the alelle
* </p>
* @param i is the position.
* @return a real with the value of the lower allele
*/
public double getUpperAllele(int i) {
return rep[i].getUpperAllele();
} // end getUpperAllele
/**
* <p>
* Sets the allele value
* </p>
* @param i is the position.
* @param lowerValue is the lower value that has to be set.
* @param upperValue is the upper value that has to be set.
*/
public void setAllele(int i, double lowerValue, double upperValue) {
rep[i].setAllele(lowerValue,upperValue);
} // end setAllele
/**
* <p>
* Sets the allele value
* </p>
* @param i is the position.
* @param r is the representation of the classifier that has to be copied.
*/
public void setAllele(int i, Representation r) {
rep[i].setAllele(r.rep[i]);
} // end setAllele
/**
* <p>
* Sets the allele value
* </p>
* @param i is the position.
* @param val is the character value.
*/
public void setAllele(int i, char val){
rep[i].setAllele((double)val, (double) val);
}
/**
* <p>
* It crosses a real allele within two parents. If the representation is
* a ternary representation, a crossover within intervals is not possible
* because there is only one gene in each position. So, in this case, the gene
* of the second parent will be copied.
* In case of being a real representation, a random number is generated to
* decide where to cross the interval. It it is crossed within the interval,
* the crossAllele method will make it.
* </p>
* @param i is the allele that has to be crossed.
* @param parent1 is the attribute object of the first parent.
* @param parent2 is the attribute object of the second parent.
*/
public void crossAllele(int i, Representation parent1, Representation parent2){
if (Config.ternaryRep){
rep[i].setAllele(parent2.rep[i]);
}
else{
if (Config.typeOfAttributes[i].equals("character")){
rep[i].setAllele(parent2.rep[i]);
}
else{
if (Config.rand() < 0.5)
rep[i].setAllele(parent2.rep[i]);
else{ //The alleles has to be crossed
rep[i].setAllele(parent1.rep[i].getLowerAllele(), parent2.rep[i].getUpperAllele());
rep[i].verifyInterval();
}
}
}
}
/**
* <p>
* Changes all the don't care symbols by the state in the environment, with Pspecify probability
* </p>
* @param env is the environment.
*/
public void makeSpecify (double []env){
int i = 0;
for (i=0; i<rep.length; i++){
rep[i].makeSpecify(env[i]);
}
}
/**
* <p>
* Returns true if the allele matches with the environment
* </p>
* @param env is the value of the environment.
* @return a boolean indicating if the allele matches with the environmental value.
*/
public boolean match (double[] env){
for (int i=0; i<rep.length; i++){
if (!rep[i].match(env[i])) return false;
}
return true;
} // end match
/**
* <p>
* Indicates if the classifier of the class is equal to the classifier
* passed.
* </p>
* @return a boolean indicating if they are equals.
* @param clRep is the representation of the classifier.
*/
public boolean equals(Representation clRep) {
try{
for (int i=0; i<rep.length; i++){
if (! rep[i].equals (clRep.rep[i]) ) return false;
}
return true;
}catch (Exception e){
return false;
}
} // end equals
/**
* <p>
* Returns true if the current representation subsumes the representation passed as a parameter
* </p>
* @param r is the representation of the subsumer.
* @return a boolean indicating if the classifier subsumes.
*/
public boolean subsumes (Representation r){
if (action != r.action) return false;
for (int i =0; i < rep.length; i++){
if (! rep[i].subsumes(r.rep[i]) ) return false;
}
return true;
} // end subusmes
/**
* <p>
* Returns the number of don't care symbols in the
* classifier. It is used by action set subsumption
* </p>
* @return a double with the number of don't care symbols.
*/
public double numberOfDontCareSymbols(){
double num = 0.;
for (int i=0; i<rep.length; i++){
num += rep[i].isDontCareSymbol();
}
return num;
} // end numberOfDontCareSymbols
/**
* <p>
* Indicate if the current representation is more general than
* the representation passed as a parameter.
* </p>
* @param r is the representation to which the current representation
* is compared.
* @return a boolean indicating if the current representation is more general.
*/
public boolean isMoreGeneral(Representation r){
boolean ret = false;
// We need to check the condition for the "insertInPSubsumingCl
if (action != r.action) return false;
if (numberOfDontCareSymbols() >= r.numberOfDontCareSymbols()){
for (int i=0; i<rep.length;i++){
if (!rep[i].isMoreGeneral(r.rep[i])) return false;
}
return true;
}
return false;
}
public void print (){
System.out.print ("\t Act: "+action);
System.out.print ("\t Cond: ");
System.out.println (" ");
for (int i=0; i<rep.length; i++){
rep[i].print();
}
}//end print
/**
* <p>
* Prints the classifier to the specified file.
* </p>
* @param fout is the output file.
*/
public void printNotNorm (PrintWriter fout){
int i=0;
fout.print(" ");
try{
for (i=0; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("enumerate")){
rep[i].printNotNorm(fout, Config.enumConv[i]);
}else if (Config.typeOfAttributes[i].equals("integer")){
if (Config.enumConv[i].size() > 0)
rep[i].printNotNorm(fout, Config.enumConv[i]);
else
rep[i].printNotNorm(fout, (int) Config.attBounds[i][0]);
}else if (Config.typeOfAttributes[i].equals("real")){
rep[i].printNotNorm(fout, Config.attBounds[i][0], Config.attBounds[i][1]);
}
}
}catch(Exception e){
System.out.println ("Exception when printing the attribute: "+i);
e.printStackTrace();
}
fout.print ("\t "+(String)Config.classConv.elementAt(action));
}//end print
/**
* <p>
* Prints the classifier to the specified file.
* </p>
* @param fout is the output file.
*/
public void print (PrintWriter fout){
fout.print(" ");
for (int i=0; i<rep.length; i++){
rep[i].print(fout);
}
fout.print ("\t "+action);
}//end print
/**
* <p>
* It draws the population to a file. A character allele is
* drawn as 1 o 0. Otherwise, a real allele is drawn in ten
* points, that represent the interval [0..1] divided in ten
* fragments. In each fragment, three types of symbol are possible:
* . --> The fragment is not covered by the classifier.
* o --> The fragment is partially covered by the classifier.
* O --> The fragment is totally covered by the classifier.
*
* This notation has been obtained from Wilson2000 XCSR
* </p>
* @param fout is the file where the population has to be drawn.
*/
public void draw(PrintWriter fout) {
double lower=0,upper=0;
if (Config.ternaryRep){
fout.print (" "+ ((char)rep[0].getAllele())+" ");
for (int i=1; i<rep.length; i++){
fout.print ("| "+((char)rep[i].getAllele())+" ");
}
}
else{
if (Config.typeOfAttributes[0].equals("character")){
fout.print (" "+((char)rep[0].getAllele())+" ");
}
else{
printInterval(fout,rep[0].getLowerAllele(),rep[0].getUpperAllele());
}
for (int i=1; i<rep.length; i++){
if (Config.typeOfAttributes[i].equals("character")){
fout.print ("| "+((char)rep[i].getAllele())+" ");
}
else if (Config.typeOfAttributes[i].equals("integer")){
printInterval(i,fout,((int) rep[i].getLowerAllele()), ((int)rep[i].getUpperAllele()));
}
else{
printInterval(fout,rep[i].getLowerAllele(),rep[i].getUpperAllele());
}
}
}
fout.print ("\t "+action);
} //end draw
/**
* It draws a real interval
* @param fout is a reference to the file where the
* classifier has to be drawn.
* @param lower is the lower value of the interval
* @param upper is the upper value of the interval.
*/
private void printInterval(PrintWriter fout,double lower, double upper){
double aux = 0.0;
int points = Config.realDrawnPrecision;
double inc = 1. / (double)points;
for (double i=0.; i<(double)points; i++){
if ( i*inc < lower && (i*inc + inc) <= lower){
fout.print (".");
}
else if(i*inc < lower && (i*inc + inc) > lower){
fout.print ("o");
} // In the next if, the value is bigger than the lower
else if(i*inc < upper && (i*inc + inc) <= upper){
fout.print ("O");
}
else if(i*inc < upper && (i*inc + inc) > upper){
fout.print("o");
}
else if (i*inc >=lower){
fout.print(".");
}
else{
fout.print ("ERR. Case not covered!!");
}
}
fout.print (" | ");
}
/**
* It draws an integer interval
* @param fout is a reference to the file where the
* classifier has to be drawn.
* @param numInterval is the position of the interval in the
* classifier.
* @param lower is the lower value of the interval
* @param upper is the upper value of the interval.
*/
private void printInterval(int numInterval,PrintWriter fout,int lower, int upper){
double aux = 0.0;
int points = Config.realDrawnPrecision;
int inc = ((int)Config.attBounds[numInterval][1] - (int)Config.attBounds[numInterval][0] + 1) / points;
for (int i=0; i<points; i++){
if (i*inc >=lower && i*inc <=upper){
fout.print ("O");
}
else{
fout.print(".");
}
}
fout.print (" | ");
}
} // end Representation