/***********************************************************************
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 University Ram�n Lull, Barcelona) 28/03/2004
* @author Modified by Xavi Sol� (La Salle University Ram�n Lull, Barcelona) 03/12/2008
* @version 1.1
* @since JDK1.2
* </p>
*/
package keel.Algorithms.Genetic_Rule_Learning.UCS;
import java.util.*;
import java.lang.*;
import java.io.*;
public class Population {
/**
* <p>
* This class contains a rule-set. It is used to create the folowing sets:
* population [P], match set [M], and correct set [C].
*
* There are three different constructors, one for each set.
* There are a lot of access methods to the population parameters, such as the
* numerosities sums, get and set classifiers, etc.
* Moreover, there are specific methods used by UCS, like methods that
* insert, insert subsuming, insert deleting, deletes, subsumes new classifiers
* in the population.
* </p>
*/
/**
* The population
*/
private Classifier []set;
/**
* Number of macroClassifiers in the set.
*/
private int macroClSum = 0;
/**
* Number of microClassifiers in the set (the sum of all classifiers numerosity)
*/
private int microClSum = 0;
/**
* Reference to the parent population.
*/
private Population parentRef;
/**
* Reference to a genetic algorithm object.
*/
private GA ga;
/**
* <p>
* Population constructor: it creates an empty population.
* </p>
* @param numCl is the number of classifiers in the population.
* It is defined in the configuration parameters.
*/
public Population( int numCl ) {
macroClSum = 0;
microClSum = 0;
parentRef = null;
set = new Classifier[numCl]; //It creates a new classifier set of numCl size.
}// end Population
/**
* <p>
* Math set [M] constructor. It selects all the matching classifiers from [P] to create [M].
* </p>
* @param pop is the population [P].
* @param envState is the input example.
*/
public Population( Population pop, double []envState ) {
int pos;
boolean coveringApplied = false;
// We resize the population to make room for new classifiers
set = new Classifier [ pop.macroClSum + Config.numberOfActions];
microClSum = 0;
macroClSum = 0;
parentRef = pop;
for ( pos=0; pos<pop.getMacroClSum(); pos++ ){
if ( pop.set[pos].match( envState ) ){
addClassifier( pop.set[pos] );
}
}
} // end Population
/**
* <p>
* Correct set [C] constructor. It selects all the classifiers from [M] that advocate the class
* of the example.
* </p>
*
* @param matchSet is the match set of the population
* @param envState is the input example.
* @param classOfExample is the class of the input example.
*/
public Population( Population matchSet, double []envState, int classOfExample, int tStamp ) {
int pos;
macroClSum = 0;
microClSum = 0;
parentRef = matchSet;
set = new Classifier [ matchSet.macroClSum + 2 ];
for ( int i=0; i<matchSet.macroClSum; i++ ){
if ( matchSet.set[i].getAction() == classOfExample ){
if (Config.debugLevel > 0) matchSet.set[i].print();
addClassifier(matchSet.set[i]);
}
}
ga = new GA();
Population initialPop = matchSet.parentRef;
// Covering if there is not any classifier in [C]
boolean coveringApplied = false;
while (macroClSum == 0){
coveringApplied = true;
// We have to cover the action if there isn't any classifier in [C].
Classifier cl = new Classifier ( envState, classOfExample, microClSum+1, tStamp );
if ( initialPop.microClSum + 1 >= Config.popSize ){ // The maximum population size has been overcomen
// Deleting one classifier from [C]
Classifier clDeleted = initialPop.deleteClassifier();
// [M] is searched for the deleted classifier. If it exists, it is removed.
pos = matchSet.isThereClassifier(clDeleted);
if (pos >=0){
matchSet.microClSum --;
if ( clDeleted.getNumerosity() == 0 ){
matchSet.deleteClassifier(pos);
}
}
// [C] is searched for the deleted classifier. If it exists, it is removed
pos = isThereClassifier(clDeleted);
if (pos >=0){
microClSum --;
if ( clDeleted.getNumerosity() == 0 ){
deleteClassifier(pos);
}
}
}
//The new classifier is added to [C], [M] and [P]
addClassifier(cl);
matchSet.addClassifier(cl);
initialPop.addClassifier(cl);
}
} // end Population
/**
* <p>
* Updates UCS' parameters.
* </p>
*
* @param microClInC is the number of microclassifiers in [C]
* @param envState is the input example
* @param classOfExample is the class of the input example
* @param tStamp is the current time stamp
*/
public void updateParametersSet( double microClInC, double []envState, int classOfExample, int tStamp) {
int i;
for ( i=0; i<macroClSum; i++ ){
set[i].updateParameters( microClInC, classOfExample );
}
double kSum = 0;
double []kVector = new double [ macroClSum ];
for ( i=0; i<macroClSum; i++ ){
if ( set[i].getAction() != classOfExample ){
kVector[i] = 0;
}
else{
kVector[i] = set[i].getAccuracy();
if ( kVector[i] < Config.acc_0 ){
kVector[i] = Config.alpha * Math.pow( Config.acc_0 / kVector[i] , -Config.nu);
}
else{
kVector[i] = 1;
}
}
kSum += kVector[i] * set[i].getNumerosity();
}
for ( i=0; i<macroClSum; i++ ){
set[i].updateFitness( kSum, kVector[i] );
}
} // end updateParametersSet
/**
* <p>
* This method means that the classifer has to be definetely removed from
* the population because its numerosity has decreased to 0.
* </p>
* @param pos is the position of the population from where the classifier has to be deleted.
*/
public void deleteClassifier ( int pos ){
if (pos < macroClSum){
macroClSum --;
set[pos] = set[macroClSum];
set[macroClSum] = null;
}
}//end deleteClassifier
/**
* <p>
* Adds a classifier in the population.
* </p>
* @param cl is the new classifier to be added.
*/
public void addClassifier(Classifier cl) {
try {
set[macroClSum] = cl;
microClSum += cl.getNumerosity();
macroClSum++;
}catch (Exception e){
System.out.println ("Exception in the insertion of a new classifier. The macroClSum is : "+macroClSum);
System.out.println (" and the microClsum: "+microClSum);
System.out.println ("And the maximum number of classifiers in the population is: "+Config.popSize);
e.printStackTrace();
}
} // end addClassifier
/**
* <p>
* Inserts the classifier in the population. Before that, it looks if there
* is a classifier in the population with the same action and condition (in
* this case, increments its numerosity). After, it checks that the number
* of micro classifiers is less than the maximum population size. If it
* isn't, it deletes one classifier from the population calling the
* deleteClassifier function.
* It inserts the classifier in the population and in the action set if it's
* not null.
* </p>
* @param cl is the classifier that has to be inserted in the population.
*/
public void insertInPopulation( Classifier cl, Population ASet ) {
boolean found = false;
int i=0;
while ( i<macroClSum && !found ){
if ( set[i].equals(cl) ){
set[i].increaseNumerosity( cl.getNumerosity() );
microClSum += cl.getNumerosity();
if (ASet != null){
if (ASet.isThereClassifier(set[i]) >= 0) ASet.microClSum += cl.getNumerosity();
}
found = true;
}
i++;
}
if (!found){
addClassifier(cl);
}
// Here, the classifier has been added to the population
if ( microClSum > Config.popSize ){ //If we have inserted to many classifiers, we have to delete one.
deleteClFromPopulation(ASet);
}
} // end insertInPopulation
/**
* <p>
* Inserts the classifier into the population. Before, it looks if there
* is a classifier in the population that can subsume the new one (in
* this case, increments its numerosity). After, it checks that the number
* of micro classifiers is less than the maximum population size. If it
* isn't, it deletes one classifier of the population calling the
* deleteClassifier function.
* It inserts the classifier in the population and in the action set if it's
* not null.
* </p>
* @param cl is the classifier that has to be inserted in the population.
*/
public void insertInPSubsumingCl( Classifier cl ) {
int i=0;
Classifier bestSubsumer = null;
Classifier equalClassifier = null;
//We look for the best subsumer or for an equal classifier.
while ( i<macroClSum ){
if ( set[i].doesSubsume( cl ) ){
if ( bestSubsumer == null ) bestSubsumer = set[i];
else if ( set[i].doesSubsume( bestSubsumer ) ) bestSubsumer = set[i];
}
if ( set[i].equals(cl) ) equalClassifier = set[i];
i++;
}
//If there is a subsumer, its numerosity is increased.
if ( bestSubsumer != null ){
bestSubsumer.increaseNumerosity( cl.getNumerosity() );
microClSum += cl.getNumerosity();
}
else if (equalClassifier != null){
equalClassifier.increaseNumerosity( cl.getNumerosity() );
microClSum += cl.getNumerosity();
}
else{
addClassifier(cl);
}
//There's no classifier deletion, independent of if the maximum size
//has been overcomen
} // end insertInPSubsumingCl
/**
* <p>
* Deletes one classifier from the population. After that, if
* the population passed as a parameter is not null, it looks for
* the deleted classifier. If it is in the second population, it
* will delete it too.
* </p>
* @param aSet is the population where the deleted classifier has to be searched.
* @return a Classifier that contains the deleted classifier.
*/
public Classifier deleteClFromPopulation (Population aSet){
//A classifier has been deleted from the population
Classifier clDeleted = deleteClassifier();
if (aSet != null){ //Now, this classifier has to be deleted from the action set (if it exists in).
int pos = aSet.isThereClassifier(clDeleted); // It is searched in the action set.
if (pos >=0){ // It has to be deleted from the action set too.
aSet.microClSum --;
// If the classifier has 0 numerosity, we remove it from the population.
if (clDeleted.getNumerosity() == 0){ //It has to be completely deleted from action set.
aSet.macroClSum --; // Decrements the number of macroclassifiers
aSet.set[pos] = aSet.set[aSet.macroClSum]; // Moves the last classifier to the deleted one
aSet.set[aSet.macroClSum] = null; // Puts the last classifier to null.
}
}
}
return clDeleted;
}//end deleteClFromPopulation
/**
* <p>
* Deletes a classifier from this population. It chooses the classifier to be deleted.
* </p>
* @return the deleted classifier.
*/
public Classifier deleteClassifier() {
double avFitness = 0;
int i=0;
for (i=0; i<macroClSum; i++){
avFitness += set[i].getMacroClFitness();
}
avFitness /= (double)microClSum;
double voteSum = 0;
Roulette rul = new Roulette( macroClSum );
for (i=0; i<macroClSum; i++){
rul.add( set[i].deletionVote(avFitness) );
}
i = rul.selectRoulette();
// Now we have to remove the selected classifier.
microClSum--;
set[i].increaseNumerosity(-1);
Classifier deleted = set[i];
if (set[i].getNumerosity() == 0){
macroClSum--;
set[i] = set[macroClSum];
set[macroClSum]=null;
}
return deleted;
} // end deleteClassifier
/**
* <p>
* Returns the experience average of the population.
* It is used by specify.
* </p>
* @return a double with the experience average.
*/
public double getExperienceAverage() {
double expAv = 0;
for (int i=0; i<macroClSum; i++){
expAv += (double) set[i].getExperience()* set[i].getNumerosity();
}
return expAv/microClSum;
} // end getExperienceAverage
/**
* <p>
* Returns the number of macro classifiers in the set.
* </p>
* @return a double with the number of macro classifiers.
*/
public int getMacroClSum() {
return macroClSum;
} // end getNumerosity
/**
* <p>
* Returns the number of micro classifiers in the set.
* </p>
* @return a double with the number of micro classifiers.
*/
public int getMicroClSum() {
return microClSum;
} // end getMicroClSum
/**
* <p>
* Returns the generalization average of the classifiers
* in the population.
* </p>
* @return a double with the generalization average.
*/
public double getGeneralityAverage() {
double value=0.;
for (int i=0; i<macroClSum; i++){
value += set[i].getGenerality() * set[i].getNumerosity();
}
value /= (double)Config.clLength;
return value / (double)microClSum;
} // end getMicroClSum
/**
* <p>
* Returns a reference of the parent set.
* </p>
* @return a Population with the parent set.
*/
public Population getParentRef() {
return parentRef;
} // end getMicroClSum
/**
* <p>
* Sets the number of macro classifiers in the set.
* </p>
* @param mCl is the number of macro classifiers.
*/
public void setMacroClSum(int mCl) {
macroClSum = mCl;
} // end setNumerosity
/**
* <p>
* Sets the number of micro classifiers in the set.
* </p>
* @param mCl is the number of micro classifiers.
*/
public void setMicroClSum(int mCl) {
microClSum = mCl;
} // end setMicroClSum
/**
* <p>
* Increases the number of micro classifiers in the set.
* </p>
* @param mCl is the number that the microClSum has to be increased.
*/
public void increaseMicroClSum(int mCl) {
microClSum += mCl;
} // end increaseMicroClSum
/**
* <p>
* Returns the position of the classifier in the set. If it is not in
* the set, it returns -1.
* </p>
* @param cl is the classifier to be searched in the set
* @return an integer with the position of the classifier in the set
*/
public int isThereClassifier(Classifier cl) {
for (int i=0; i<macroClSum; i++){
if (set[i] == cl) return i;
}
return -1;
} // end isThereClassifier
/**
* <p>
* Returns the classifier in the given position.
* </p>
* @param i is the position of the classifier.
* @return the classifier in the given position.
*/
public Classifier getClassifier(int i) {
if (i<macroClSum) return set[i];
return null;
} // end getClassifier
/**
* <p>
* Runs the GA if the time since the last application of the GA is greater than
* the threshold.
* </p>
* @param tStamp is the actual time
* @param envState is the environment state.
*/
public void runGA(int tStamp, double[] envState, int classOfExample ) {
if ((double)tStamp - getAverageClTime() >= Config.theta_GA && macroClSum > 0){
setTimeOfClassifiers(tStamp);
ga.runGA( this, envState, classOfExample, tStamp);
}
} // end runGA
/**
* It implements tournament selection.
* This method is called from TournamentSelection.makeSelection.
* The tournament Selection has been implemented as defined by Butz.
* It possible to choose the same classifier in [A] to be
* the parent of both tournaments
*/
public Classifier tournamentSelection(){
int i,j;
double maxFitness = -1.;
Classifier cl_t = null;
while ( cl_t == null ){
for ( i=0; i<macroClSum; i++ ){
if(set[i].getMicroClFitness() > maxFitness){
for (j=0; j<set[i].getNumerosity(); j++){
if (Config.rand() < Config.tournamentSize){
cl_t = set[i];
maxFitness = set[i].getMicroClFitness();
break;
}
}
}
}
}
return cl_t;
}//end tournamentSelection
/**
* <p>
* Initializes the classifiers' time stamp to the tStamp value
* </p>
*/
public void setTimeOfClassifiers (int tStamp){
for (int i=0; i<macroClSum; i++){
set[i].setTime(tStamp);
}
}
/**
* <p>
* Returns the average time of classifiers in the population.
* </p>
* @return an integer with the average time.
*/
public double getAverageClTime() {
int averageTime=0;
for (int i=0; i<macroClSum; i++){
averageTime += (double)set[i].getTime() * (double)set[i].getNumerosity();
}
return averageTime/(double)microClSum;
} // end getAverageClTime
/**
* <p>
* Sorts the population with a quicksort algorithm. Its criteria is
* to sort the population by the numerosity or the experience.
* </p>
* @param i is the position where the sort algorithm has to start.
* @param j is the position where the sort algorithm has to finish.
* @param typeOfSort is to indicate if the parameter to sort the population is the numerosity (0 value) or the experience (1 value)
*/
public void sortPopulation(int i, int j, int typeOfSort){
int s=0,t=0,aux=0;
int [] st = new int [2];
if (i<j){
st[0]=i;
st[1]=j;
partition(i,j,st,typeOfSort);
sortPopulation(i,st[1],typeOfSort);
sortPopulation(st[0],j,typeOfSort);
}
} // end orderPopulation
/**
* <p>
* It is an auxiliar function for the quicksort algorithm. It is
* used to make the partitions of the population that has to be
* ordered.
* </p>
* @param i is the position where the sort algorithm has to start.
* @param j is the position where the sort algorithm has to finish.
* @param st is the two positions of the population that have to be sorted.
* @param typeOfSort indicates if the parameter to sort the population is the numerosity (0 value) or the experience (1 value)
*/
private void partition(int i, int j, int []st,int typeOfSort){
int middle=0, pivot=0;
Classifier tmp = null;
st[0] = i;
st[1] = j;
middle = (i+j)/2;
if (typeOfSort == 0) pivot = set[middle].getNumerosity();
else pivot = set[middle].getExperience();
while (st[0]<=st[1]){
if(typeOfSort == 0){
while (set[st[0]].getNumerosity() > pivot) st[0]++;
while (set[st[1]].getNumerosity() < pivot) st[1]--;
}
else{
while (set[st[0]].getExperience() > pivot) st[0]++;
while (set[st[1]].getExperience() < pivot) st[1]--;
}
if (st[0] < st[1]){
tmp = set[st[0]];
set[st[0]] = set[st[1]];
set[st[1]] = tmp;
st[0]++;
st[1]--;
}
else{
if (st[0] == st[1]){
st[0]++;
st[1]--;
}
}
}
}
/**
* <p>
* Returns the position in the population of a classifier.
*^If this classifier does not exist, it returns -1.
* </p>
* @param cl is the classifier that has to be searched in the population.
* @return a int indicating the postion of the classifier. If it doesn't exists, a -1 is returned.
*/
private int getPosition(Classifier cl){
for (int i=0; i<macroClSum; i++){
if (set[i] == cl) return i;
}
return -1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////// FUNCTIONS FOR THE STATISTICS //////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* <p>
* Prints the population into the specified file.
* </p>
* @param fileName is the file name where the classifiers have to be printed.
*/
public void printPopulationToFile(String fileName) {
try{
PrintWriter fout = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
printPopulationToFile ( fout );
}catch (IOException eio){
System.out.println ("ERROR when writing the population to a out file. ");
eio.printStackTrace();
}
} // end printPopulationToFile
/**
* <p>
* Prints the population into the file specified.
* </p>
* @param fout is the file where the population has to be printed.
*/
public void printPopulationToFile(PrintWriter fout) {
fout.println (macroClSum); // We write the number of macro classifiers.
fout.println (microClSum); // We write the number of micro classifiers.
fout.println ("Cond - Action - accuracy - fitness - Numerosity - Experience - [C]SetSize - Generality - timeStamp");
for (int i=0; i<macroClSum; i++){
set[i].print(fout);
fout.println("");
}
} // end printPopulationToFile
public void print(){
System.out.println ("POPULATION: ");
System.out.println ("\tmacroClassifierSum = "+macroClSum);
System.out.println ("\tmicroClassifierSum = "+microClSum+"\n");
for (int i=0; i<macroClSum; i++){
System.out.print ("\n Cl_"+i);
set[i].print();
}
System.out.println ("");
}
} // end Population