//--------------------------------------------------------------------------------// // COPYRIGHT NOTICE // //--------------------------------------------------------------------------------// // Copyright (c) 2012, Instituto de Microelectronica de Sevilla (IMSE-CNM) // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are met: // // // // * Redistributions of source code must retain the above copyright notice, // // this list of conditions and the following disclaimer. // // // // * Redistributions in binary form must reproduce the above copyright // // notice, this list of conditions and the following disclaimer in the // // documentation and/or other materials provided with the distribution. // // // // * Neither the name of the IMSE-CNM nor the names of its contributors may // // be used to endorse or promote products derived from this software // // without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE // // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //--------------------------------------------------------------------------------// //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // ALGORITMO DE CLUSTERING INCREMENTAL (SUBSTRACTIVE) // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// package xfuzzy.xfdm; import xfuzzy.lang.*; public class XfdmIncClustering extends XfdmAlgorithm { //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // MIEMBROS PRIVADOS // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// private int clusters_limit; private double radius; private double point[][]; private double cluster[][]; private double potential[]; private double cl_potential[]; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // CONSTRUCTOR // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// //-------------------------------------------------------------// // Constructor por defecto // //-------------------------------------------------------------// public XfdmIncClustering() { this.clusters_limit = 10; this.radius = 0.1; } //-------------------------------------------------------------// // Constructor desde la interfaz grafica // //-------------------------------------------------------------// public XfdmIncClustering(int limit, double radius) { this.clusters_limit = limit; this.radius = radius; } //-------------------------------------------------------------// // Constructor desde el fichero de configuracion // //-------------------------------------------------------------// public XfdmIncClustering(double[] param) { this.clusters_limit = (int) param[0]; this.radius = (double) param[1]; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // METODOS PUBLICOS // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// //=============================================================// // Metodos de acceso a la configuracion // //=============================================================// //-------------------------------------------------------------// // Obtiene el valor del maximo numero de clusters permitido // //-------------------------------------------------------------// public int getClustersLimit() { return this.clusters_limit; } //-------------------------------------------------------------// // Asigna el valor del maximo numero de clusters permitido // //-------------------------------------------------------------// public void setClustersLimit(int limit) { this.clusters_limit = limit; } //-------------------------------------------------------------// // Obtiene el valor del radio de vecindad // //-------------------------------------------------------------// public double getRadius() { return this.radius; } //-------------------------------------------------------------// // Asigna el valor del radio de vecindad // //-------------------------------------------------------------// public void setRadius(double radius) { this.radius = radius; } //=============================================================// // Metodos de desarrollo de XfdmAlgorithm // //=============================================================// //-------------------------------------------------------------// // Obtiene un duplicado del objeto // //-------------------------------------------------------------// public Object clone() { return new XfdmIncClustering(clusters_limit,radius); } //-------------------------------------------------------------// // Obtiene el nombre del algoritmo // //-------------------------------------------------------------// public String toString() { return "Incremental Clustering (Substractive clustering)"; } //-------------------------------------------------------------// // Representacion en el fichero de configuracion // //-------------------------------------------------------------// public String toCode() { String code = "xfdm_algorithm(IncClustering,"; code += " "+clusters_limit+", "+radius+" )"; return code; } //-------------------------------------------------------------// // Metodo que construye el sistema a partir de los datos // //-------------------------------------------------------------// public void compute(Specification spec,XfdmConfig config) throws XflException { this.spec = spec; this.config = config; this.pattern = config.getPatterns(); this.opset = createOperatorSet(); spec.addOperatorset(opset); this.inputtype = createInputTypes(); for(int i=0; i<inputtype.length; i++) spec.addType(inputtype[i]); this.outputtype = createOutputTypes(); for(int i=0; i<outputtype.length; i++) spec.addType(outputtype[i]); this.rulebase = createEmptyRulebase(); this.spec.addRulebase(rulebase); createSystemStructure(); this.spec.setModified(true); this.pattern.setRanges(spec); this.point = createPointsFromPatterns(); this.potential = createPotentials(); this.cluster = new double[0][]; this.cl_potential = new double[0]; do { selectCluster(); updatePotentials(); } while(!satisfyEndCondition()); createContent(); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // METODOS PRIVADOS // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// //=============================================================// // Metodos que desarrollan el algoritmo // //=============================================================// //-------------------------------------------------------------// // Obtiene el conjunto normalizado de patrones // //-------------------------------------------------------------// private double[][] createPointsFromPatterns() { int length = this.pattern.input.length; int width = this.inputtype.length + this.outputtype.length; double point[][] = new double[length][width]; double min[] = new double[width]; double max[] = new double[width]; for(int i=0; i<width; i++) { if(i<inputtype.length) { min[i] = inputtype[i].min(); max[i] = inputtype[i].max(); } else { min[i] = outputtype[i-inputtype.length].min(); max[i] = outputtype[i-inputtype.length].max(); } } for(int i=0; i<length; i++) for(int j=0; j<width; j++) { if(j<inputtype.length) { point[i][j] = (pattern.input[i][j]-min[j])/(max[j]-min[j]); } else { point[i][j] =(pattern.output[i][j-inputtype.length]-min[j])/(max[j]-min[j]); } } return point; } //-------------------------------------------------------------// // Genera los potenciales asociados a cada punto // //-------------------------------------------------------------// private double[] createPotentials() { double potential[] = new double[point.length]; double alpha = 4/(radius*radius); for(int i=0; i<point.length; i++) for(int j=0; j<point.length; j++) { if(i!=j) potential[i] += Math.exp(-alpha*distance(point[i],point[j])); } return potential; } //-------------------------------------------------------------// // Annade como cluster el punto de mayor potencial // //-------------------------------------------------------------// private void selectCluster() { int index = -1; double max = 0; for(int i=0; i<potential.length; i++) { if(potential[i]>max) { max = potential[i]; index = i; } } if(index <0) return; double[][] ac = new double[cluster.length+1][]; System.arraycopy(cluster,0,ac,0,cluster.length); ac[cluster.length] = point[index]; cluster = ac; double[] acp = new double[cl_potential.length+1]; System.arraycopy(cl_potential,0,acp,0,cl_potential.length); acp[cl_potential.length] = potential[index]; cl_potential = acp; double[][] ap = new double[point.length-1][]; System.arraycopy(point,0,ap,0,index); System.arraycopy(point,index+1,ap,index,point.length-index-1); point = ap; double[] app = new double[potential.length-1]; System.arraycopy(potential,0,app,0,index); System.arraycopy(potential,index+1,app,index,potential.length - index -1); potential = app; } //-------------------------------------------------------------// // Actualiza los potenciales considerando el ultimo cluster // //-------------------------------------------------------------// private void updatePotentials() { double[] lastcluster = cluster[cluster.length-1]; double lastpotential = cl_potential[cluster.length-1]; double rb = 1.25*radius; double beta = -4/(rb*rb); for(int i=0; i<point.length; i++) { potential[i] -= lastpotential*Math.exp(beta*distance(point[i],lastcluster)); } } //-------------------------------------------------------------// // Verifica si se han cumplido las condiciones de termino // //-------------------------------------------------------------// private boolean satisfyEndCondition() { if(cluster.length >= clusters_limit) return true; for(int i=0; i<potential.length; i++) { if(potential[i] > 0.15*cl_potential[0]) return false; } // Finished if not potential is found to be significant enough, return true; } //-------------------------------------------------------------// // Calcula la distancia euclidea entre dos puntos (al cuadrado)// //-------------------------------------------------------------// private double distance(double[] x, double[] y) { double dist = 0; for(int i=0; i<x.length; i++) dist += (x[i]-y[i])*(x[i]-y[i]); return dist; } //=============================================================// // Metodos que generan el contenido del sistema // //=============================================================// //-------------------------------------------------------------// // Genera el contenido del sistema a partir de los clusters // //-------------------------------------------------------------// private void createContent() { for(int i=0; i<inputtype.length; i++) createBells(inputtype[i],i); for(int i=0; i<outputtype.length; i++) { switch(config.systemstyle.defuz) { case XfdmSystemStyle.FUZZYMEAN: createSingletons(outputtype[i],inputtype.length + i); break; case XfdmSystemStyle.WEIGHTED: createBells(outputtype[i],inputtype.length + i); break; case XfdmSystemStyle.TAKAGI: createParametric(outputtype[i],inputtype.length + i); break; } } createRules(); } //-------------------------------------------------------------// // Genera las reglas correspondientes a los clusters // //-------------------------------------------------------------// private void createRules() { Variable ivar[] = rulebase.getInputs(); Variable ovar[] = rulebase.getOutputs(); int is = Relation.IS; for(int i=0; i<cluster.length; i++) { LinguisticLabel pmf = ivar[0].getType().getAllMembershipFunctions()[i]; Relation rel = Relation.create(is,null,null,ivar[0],pmf,rulebase); for(int j=1; j<ivar.length; j++) { pmf = ivar[j].getType().getAllMembershipFunctions()[i]; Relation nrel = Relation.create(is,null,null,ivar[j],pmf,rulebase); rel = Relation.create(Relation.AND,rel,nrel,null,null,rulebase); } Rule rule = new Rule(rel); for(int j=0; j<ovar.length; j++) { pmf = ovar[j].getType().getAllMembershipFunctions()[i]; rule.add(new Conclusion(ovar[j],pmf,rulebase)); } rulebase.addRule(rule); } } //-------------------------------------------------------------// // Genera el conjunto de tipos de variables de entrada // //-------------------------------------------------------------// protected Type[] createInputTypes() { Type itp[] = new Type[config.numinputs]; for(int i=0; i<config.numinputs; i++) { String tname = "Tin"+i; Universe universe = null; if(config.commonstyle.isUniverseDefined()) { try { universe=new Universe(config.commonstyle.min,config.commonstyle.max);} catch(Exception ex) {} } if(config.inputstyle != null && config.inputstyle.length > i && config.inputstyle[i] != null) { tname = "T"+config.inputstyle[i].name; if(config.inputstyle[i].isUniverseDefined()) { try { universe=new Universe(config.inputstyle[i].min,config.inputstyle[i].max); } catch(Exception ex) { } } else { universe = null; } } if(universe == null) universe = pattern.getUniverse(i,true); itp[i] = new Type(tname,universe); } return itp; } //-------------------------------------------------------------// // Genera el conjunto de tipos de variables de salida // //-------------------------------------------------------------// private Type[] createOutputTypes() { Type otp[] = new Type[config.numoutputs]; for(int i=0; i<config.numoutputs; i++) { String tname = "T"+config.systemstyle.outputname; if(config.numoutputs>1) tname += ""+i; otp[i] = new Type(tname,pattern.getUniverse(i,false)); } return otp; } //=============================================================// // Metodos que generan las funciones de pertenencia // //=============================================================// //-------------------------------------------------------------// // Crea un conjunto de singularidades // //-------------------------------------------------------------// private void createSingletons(Type type, int index) { Universe u = type.getUniverse(); double min = u.min(); double max = u.max(); for(int i=0; i<cluster.length; i++) { ParamMemFunc pmf = new pkg.xfl.mfunc.singleton(); pmf.set("mf"+i, u); pmf.set( min + cluster[i][index]*(max-min) ); try { type.add(pmf); } catch(XflException e) {} } } //-------------------------------------------------------------// // Crea un conjunto de funciones parametricas // //-------------------------------------------------------------// private void createParametric(Type type, int index) { Universe u = type.getUniverse(); double min = u.min(); double max = u.max(); double param[] = new double[inputtype.length+1]; for(int i=0; i<cluster.length; i++) { ParamMemFunc pmf = new pkg.xfl.mfunc.parametric(); pmf.set("mf"+i, u); param[0] = min + cluster[i][index]*(max-min); try { pmf.set(param); type.add(pmf); } catch(XflException ex) {} } } //-------------------------------------------------------------// // Crea un conjunto de campanas // //-------------------------------------------------------------// private void createBells(Type type, int index) { Universe u = type.getUniverse(); double min = u.min(); double max = u.max(); double param[] = new double[2]; param[1] = radius*(max - min)/2; for(int i=0; i<cluster.length; i++) { ParamMemFunc pmf = new pkg.xfl.mfunc.bell(); pmf.set("mf"+i, u); param[0] = min + cluster[i][index]*(max-min); try { pmf.set(param); type.add(pmf); } catch(XflException ex) {} } } }