/*********************************************************************** 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.UnsupervisedLearning.AssociationRules.IntervalRuleLearning.MOPNAR; /** * <p> * @author Written by Diana Mart�n (dmartin@ceis.cujae.edu.cu) * @version 1.1 * @since JDK1.6 * </p> */ import java.util.ArrayList; import org.core.Randomize; public class Chromosome implements Comparable { /** * <p> * It is used for representing and handling a Chromosome throughout the evolutionary learning * </p> */ public Gene[] genes; public double[] objectives; public double [] weightVector; public int[] vectorsNeighbors; public int numObjectives; public int T; public double antsSupport; public double consSupport; public double support; public double confidence; public double conv; public double CF; public double netConf; public double yulesQ; public double sumInterval; public int nAnts; boolean n_e; int rank; /** * <p> * It creates a new chromosome by setting up its genes * </p> * @param genes The array of genes that the chromosome must handle */ public Chromosome(Gene[] genes, int numObjectives, int T) { this.genes = new Gene[genes.length]; this.nAnts = 0; for (int i=0; i < genes.length; i++) { this.genes[i] = genes[i].copy(); if (this.genes[i].getActAs() == Gene.ANTECEDENT) this.nAnts++; } this.numObjectives = numObjectives; this.T = T; this.objectives = new double[this.numObjectives]; this.weightVector = new double[this.numObjectives]; this.vectorsNeighbors = new int[this.T]; this.support = 0.0; this.antsSupport = 0.0; this.consSupport = 0.0; this.confidence = 0.0; this.conv = 0.0; this.CF = 0.0; this.netConf = 0.0; this.yulesQ = 0.0; this.sumInterval = 0.0; this.n_e = true; this.rank = -1; } /** * <p> * It allows to clone correctly a chromosome * </p> * @return A copy of the chromosome */ public Chromosome copy() { Chromosome chromo = new Chromosome(this.genes, this.numObjectives, this.T); for(int i=0; i < this.numObjectives; i++) chromo.objectives[i] = this.objectives[i]; for(int i=0; i < this.numObjectives; i++) chromo.weightVector[i] = this.weightVector[i]; for(int i=0; i < this.T; i++) chromo.vectorsNeighbors[i] = this.vectorsNeighbors[i]; chromo.support = this.support; chromo.antsSupport = this.antsSupport; chromo.consSupport = this.consSupport; chromo.confidence = this.confidence; chromo.CF = this.CF; chromo.netConf = this.netConf; chromo.conv = this.conv; chromo.yulesQ = this.yulesQ; chromo.nAnts = this.nAnts; chromo.sumInterval = this.sumInterval; chromo.rank = this.rank; chromo.n_e = this.n_e; return chromo; } /** * <p> * It sets the objectives for a chromosome * </p> * @param objectives The objectives value of the chromosome */ public void setObjetives(double[] objectives) { this.objectives = objectives; } /** * <p> * It returns the objectives of a chromosome * </p> * @return The objectives value of the chromosome */ public double[] getObjectives() { return objectives; } /** * <p> * It sets the support of the association rule represented by a chromosome * </p> * @param support The value representing the rule support */ public void setSupport(double support) { this.support = support; } /** * <p> * It returns the support of the association rule represented by a chromosome * </p> * @return A value representing the rule support */ public double getSupport() { return this.support; } /** * <p> * It returns the support of the association rule represented by a chromosome * </p> * @return A value representing the rule support */ public double getAntsSupport() { return this.antsSupport; } public double getConsSupport() { return this.consSupport; } /** * <p> * It sets the confidence of the association rule represented by a chromosome * </p> * @param confidence The value representing the rule confidence */ public void setConfidence(double confidence) { this.confidence = confidence; } /** * <p> * It returns the confidence of the association rule represented by a chromosome * </p> * @return A value representing the rule confidence */ public double getConfidence() { return this.confidence; } /** * <p> * It returns the genes of a chromosome * </p> * @return An array of genes for the chromosome being considered */ public void setGenes(Gene[] genes){ this.genes = genes; } public Gene[] getGenes() { return this.genes; } /** * <p> * It returns the "i-th" gene of a chromosome * </p> * @param i The index of the gene * @return The "i-th" gene of the chromosome being considered */ public Gene getGene(int i) { return this.genes[i]; } /** * <p> * It checks whether a chromosome always contains at least one antecedent gene as well as at least one consequent gene. * If not, it forces this constraint by randomly altering some of its genes * </p> */ public void forceConsistency() { int i, n_ant, n_cons, pos, count; n_ant = n_cons = 0; for (i=0; i < genes.length; i++) { if (genes[i].getActAs() == Gene.ANTECEDENT) n_ant++; if (genes[i].getActAs() == Gene.CONSEQUENT) n_cons++; } if (n_cons > 1) { pos = Randomize.RandintClosed(1, n_cons); for (i=0, count=1; i < genes.length; i++) { if (genes[i].getActAs() == Gene.CONSEQUENT) { if (count != pos) { genes[i].setActAs(Gene.ANTECEDENT); n_ant++; } count++; } } } else if (n_cons < 1) { if (n_ant > 1) { pos = Randomize.RandintClosed(1, n_ant); for (i=0, count=1; i < genes.length && count <= pos; i++) { if (genes[i].getActAs() == Gene.ANTECEDENT) { if (count == pos) { genes[i].setActAs(Gene.CONSEQUENT); n_ant--; } count++; } } } else { for (pos = Randomize.Randint(0, genes.length); genes[pos].getActAs() != Gene.NOT_INVOLVED; pos = Randomize.Randint(0, genes.length)); genes[pos].setActAs(Gene.CONSEQUENT); } n_cons++; } if (n_ant < 1) { for (pos = Randomize.Randint(0, genes.length); genes[pos].getActAs() != Gene.NOT_INVOLVED; pos = Randomize.Randint(0, genes.length)); genes[pos].setActAs(Gene.ANTECEDENT); } } /** * <p> * It indicates whether some other chromosome is "equal to" this one * </p> * @param obj The reference object with which to compare * @return True if this chromosome is the same as the argument; False otherwise */ public boolean equals(Chromosome chr) { int i; for (i=0; i < this.genes.length; i++) { if ((this.genes[i].getActAs() != Gene.NOT_INVOLVED) && (chr.genes[i].getActAs() == Gene.NOT_INVOLVED)) return (false); if ((this.genes[i].getActAs() == Gene.NOT_INVOLVED) && (chr.genes[i].getActAs() != Gene.NOT_INVOLVED)) return (false); if ((this.genes[i].getActAs() != Gene.NOT_INVOLVED) && (chr.genes[i].getActAs() != Gene.NOT_INVOLVED)) { if (!chr.genes[i].equals(this.genes[i])) return (false); } } return (true); } /** * <p> * It returns a string representation of a chromosome * </p> * @return A string representation of the chromosome */ public String toString() { String str = "Lift: " + this.objectives[0]+ "; Rule Support: " + this.support + "; Rule Confidence: " + this.confidence + "\n" ; for( int j=0; j < this.numObjectives; j++) str += "Objetives:" + "["+ j + "]"+ this.objectives[j] + "\n"; for (int i=0; i < this.genes.length; i++) str += this.genes[i] + "\n"; return str; } public double getObjective(int num) { if ((num < 0) || (num > this.numObjectives)) return (-1); return (this.objectives[num]); } /** * <p> * It adds a dataset records to the list of records being covered by a chromosome * </p> * @param tid The ID of the covered record in the dataset */ public boolean isCovered(double[] example) { int i; boolean covered; covered = true; for (i=0; i < this.genes.length && covered; i++) { if (this.genes[i].getActAs() != Gene.NOT_INVOLVED) covered = this.genes[i].isCover(i, example[i]); } return (covered); } public void setNew (boolean value) { this.n_e = value; } public boolean isNew () { return (this.n_e); } public void computeObjetives (myDataset dataset) { int i, j, nTrans, nVars, nCons; boolean antsCover, consCover; double amp, interval, lift, numeratorYules, denominatorYules; double [] example; int[] covered; nTrans = dataset.getnTrans(); nVars = dataset.getnVars(); this.antsSupport = 0.0; this.consSupport = 0.0; this.support = 0.0; this.confidence = 0.0; this.nAnts = 0; this.sumInterval = 0.0; nCons = 0; covered = new int[nTrans]; for (i=0; i < nTrans; i++) covered[i] = 0; for (i=0; i < this.numObjectives; i++) this.objectives[i] = 0.0; for (i = 0; i < nTrans; i++) { example = dataset.getExample(i); antsCover = consCover = true; for (j=0; j < nVars && (antsCover || consCover); j++) { if (this.genes[j].getActAs() == Gene.ANTECEDENT && antsCover) { if (!this.genes[j].isCover(j, example[j])) antsCover = false; } else if (this.genes[j].getActAs() == Gene.CONSEQUENT && consCover) { if (!this.genes[j].isCover(j, example[j])) consCover = false; } } if (antsCover) this.antsSupport++; if (consCover) this.consSupport++; if (antsCover && consCover) { this.support++; covered[i] = 1; } } this.antsSupport /= nTrans; this.consSupport /= nTrans; this.support /= nTrans; if (this.antsSupport > 0.0) this.confidence = this.support / this.antsSupport; for (i=0; i < nVars; i++) { if (this.genes[i].getActAs() == Gene.ANTECEDENT) this.nAnts++; if (this.genes[i].getActAs() == Gene.CONSEQUENT) nCons++; if (this.genes[i].getActAs() != Gene.NOT_INVOLVED) { this.genes[i].tuneInterval(dataset, covered); amp = this.genes[i].getUpperBound() - this.genes[i].getLowerBound(); interval = ( this.genes[i].getIsPositiveInterval() ) ? amp/dataset.getAmplitude(i) : (dataset.getAmplitude(i) - amp) /dataset.getAmplitude(i); this.sumInterval += interval; } } this.sumInterval /= (this.nAnts + nCons); //compute lift if((this.antsSupport == 0)||(this.consSupport == 0)) lift = 1; else lift = this.support/(this.antsSupport * this.consSupport); //compute conviction if((this.consSupport == 1)|| (this.antsSupport == 0)) this.conv = 1; else this.conv = (this.antsSupport*(1-this.consSupport))/(this.antsSupport-this.support); //compute netconf if ((this.antsSupport == 0)||(this.antsSupport == 1)||(Math.abs((this.antsSupport * (1-this.antsSupport))) <= 0.001)) this.netConf = 0; else this.netConf = (this.support - (this.antsSupport*this.consSupport))/(this.antsSupport * (1-this.antsSupport)); //compute yulesQ numeratorYules = ((this.support * (1 - this.consSupport - this.antsSupport + this.support)) - ((this.antsSupport - this.support)* (this.consSupport - this.support))); denominatorYules = ((this.support * (1 - this.consSupport - this.antsSupport + this.support)) + ((this.antsSupport - this.support)* (this.consSupport - this.support))); if ((this.antsSupport == 0)||(this.antsSupport == 1)|| (this.consSupport == 0)||(this.consSupport == 1)||(Math.abs(denominatorYules) <= 0.001)) yulesQ = 0; else yulesQ = numeratorYules/denominatorYules; // compute Certain Factor this.CF = 0; if(this.confidence > this.consSupport) CF = (this.confidence - this.consSupport)/(1-this.consSupport); else if(this.confidence < this.consSupport) CF = (this.confidence - this.consSupport)/(this.consSupport); if (this.numObjectives > 0) this.objectives[0] = lift; if (this.numObjectives > 1) this.objectives[1] = this.CF * this.support; // performance if (this.numObjectives > 2) this.objectives[2] = 1.0 / this.nAnts; // number of variables in the antecedent this.n_e = false; } public double computeDistance(double[] weightVector_B) { double result = 0.0; for (int i=0; i < this.weightVector.length; i++) result += Math.pow (this.weightVector[i] - weightVector_B[i], 2.0); return (result); } public boolean isSubChromo (Chromosome chromo2) { int i; Gene gen; if (this.getSupport() < chromo2.getSupport()) return (false); for (i=0; i < genes.length; i++) { gen = chromo2.getGene(i); if ((genes[i].getActAs() != Gene.NOT_INVOLVED) && (gen.getActAs() == Gene.NOT_INVOLVED)) return (false); if ((genes[i].getActAs() == Gene.NOT_INVOLVED) && (gen.getActAs() != Gene.NOT_INVOLVED)) return (false); if ((genes[i].getActAs() != Gene.NOT_INVOLVED) && (gen.getActAs() != Gene.NOT_INVOLVED)) { if (!genes[i].isSubGen(gen)) return (false); } } return (true); } public void calculateSumInterval(myDataset dataset) { int i, nVars, nCons; nVars = dataset.getnVars(); nCons = 0; this.sumInterval = 0.0; for (i=0; i < nVars; i++) { if (this.genes[i].getActAs() == Gene.CONSEQUENT) nCons++; if (this.genes[i].getActAs() != Gene.NOT_INVOLVED) this.sumInterval += ((this.genes[i].getUpperBound() - this.genes[i].getLowerBound()) / dataset.getAmplitude(i)); } this.sumInterval /= (this.nAnts + nCons); } public int getnAnts () { return (this.nAnts); } public void setnAnts (int value) { this.nAnts = value; } public int getRank () { return (this.rank); } public double getSumInterval () { return (this.sumInterval); } /** * <p> * It compares a chromosome with another one in order to accomplish ordering later. * The comparison is achieved by only considering objectives values. * For this reason, note that this method provides a natural ordering that is inconsistent with equals * </p> * @param obj The object to be compared * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object */ public ArrayList<Integer> getCoveredTIDs (myDataset dataset) { int i; double[] example; ArrayList<Integer> TIDs; TIDs = new ArrayList<Integer>(); for (i=0; i < dataset.getnTrans(); i++) { example = dataset.getExample(i); if (this.isCovered(example)) TIDs.add(i); } return (TIDs); } public int compareTo(Object chr) { if (((Chromosome) chr).nAnts > this.nAnts) return -1; else if (((Chromosome) chr).nAnts < this.nAnts) return 1; else return 0; } public int getT() { return T; } public void setT(int T) { this.T = T; } public int[] getVectorsNeighbors() { return vectorsNeighbors; } public int getVectorsNeighbors(int i) { return vectorsNeighbors[i]; } public void setVectorsNeighbors(int[] vectorsNeighbors) { for(int i=0; i < this.T; i++) this.vectorsNeighbors[i] = vectorsNeighbors[i]; } public double[] getWeightVector() { return weightVector; } public double getWeightVector(int i) { return weightVector[i]; } public void setWeightVector(double[] weightVector) { for (int i=0; i < this.numObjectives; i++) this.weightVector[i] = weightVector[i]; } public double getCF() { return CF; } public void setCF(double cf) { CF = cf; } public double getConv() { return conv; } public void setConv(double conv) { this.conv = conv; } public double getNetConf() { return netConf; } public void setNetConf(double netConf) { this.netConf = netConf; } public double getYulesQ() { return yulesQ; } }