//--------------------------------------------------------------------------------//
// 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 PARTICION INCREMENTAL //
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
package xfuzzy.xfdm;
import xfuzzy.lang.*;
import xfuzzy.xfsl.*;
public class XfdmIncGrid extends XfdmAlgorithm {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// MIEMBROS PRIVADOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
private int mfs_limit;
private int rule_limit;
private double rmse_limit;
private boolean learning;
private double input[][];
private double output[][];
private double last_rmse;
private int worst_pattern;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// CONSTRUCTOR //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//-------------------------------------------------------------//
// Constructor por defecto //
//-------------------------------------------------------------//
public XfdmIncGrid() {
this.mfs_limit = -1;
this.rule_limit = -1;
this.rmse_limit = -1;
this.learning = false;
}
//-------------------------------------------------------------//
// Constructor desde la interfaz grafica //
//-------------------------------------------------------------//
public XfdmIncGrid(int mfs, int rule, double rmse, boolean learn) {
this.mfs_limit = mfs;
this.rule_limit = rule;
this.rmse_limit = rmse;
this.learning = learn;
}
//-------------------------------------------------------------//
// Constructor desde el fichero de configuracion //
//-------------------------------------------------------------//
public XfdmIncGrid(double[] param) {
this.mfs_limit = (int) param[0];
this.rule_limit = (int) param[1];
this.rmse_limit = (double) param[2];
this.learning = (((int) param[3]) == 1);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// METODOS PUBLICOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//=============================================================//
// Metodos de acceso a la configuracion //
//=============================================================//
//-------------------------------------------------------------//
// Obtiene el valor del maximo numero de MFs permitido //
//-------------------------------------------------------------//
public int getMfsLimit() {
return this.mfs_limit;
}
//-------------------------------------------------------------//
// Asigna el valor del maximo numero de MFs permitido //
//-------------------------------------------------------------//
public void setMfsLimit(int limit) {
this.mfs_limit = limit;
}
//-------------------------------------------------------------//
// Obtiene el valor del maximo numero de reglas permitido //
//-------------------------------------------------------------//
public int getRuleLimit() {
return this.rule_limit;
}
//-------------------------------------------------------------//
// Asigna el valor del maximo numero de reglas permitido //
//-------------------------------------------------------------//
public void setRuleLimit(int limit) {
this.rule_limit = limit;
}
//-------------------------------------------------------------//
// Obtiene el valor de la minima desviacion permitida //
//-------------------------------------------------------------//
public double getRMSELimit() {
return this.rmse_limit;
}
//-------------------------------------------------------------//
// Asigna el valor de la minima desviacion permitida //
//-------------------------------------------------------------//
public void setRMSELimit(double limit) {
this.rmse_limit = limit;
}
//-------------------------------------------------------------//
// Obtiene el valor de la opcion de aprendizaje //
//-------------------------------------------------------------//
public boolean isLearning() {
return this.learning;
}
//-------------------------------------------------------------//
// Asigna el valor de la opcion de aprendizaje //
//-------------------------------------------------------------//
public void setLearning(boolean learn) {
this.learning = learn;
}
//=============================================================//
// Metodos de desarrollo de XfdmAlgorithm //
//=============================================================//
//-------------------------------------------------------------//
// Obtiene un duplicado del objeto //
//-------------------------------------------------------------//
public Object clone() {
return new XfdmIncGrid(mfs_limit,rule_limit,rmse_limit,learning);
}
//-------------------------------------------------------------//
// Obtiene el nombre del algoritmo //
//-------------------------------------------------------------//
public String toString() {
return "Incremental Grid Partition";
}
//-------------------------------------------------------------//
// Representacion en el fichero de configuracion //
//-------------------------------------------------------------//
public String toCode() {
String code = "xfdm_algorithm(IncGrid,";
code += " "+mfs_limit+", "+rule_limit+", "+rmse_limit;
code += (learning? ", 1" : ", 0")+" )";
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);
initInputPartition();
initOutputPartition();
createContent();
if(learning) learning();
computeError();
while(!satisfyEndCondition()) {
updatePartition();
createContent();
if(learning) learning();
computeError();
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// METODOS PRIVADOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//=============================================================//
// Funciones que generan el contenido del sistema //
//=============================================================//
//-------------------------------------------------------------//
// Genera el contenido del sistema a partir de la particion //
//-------------------------------------------------------------//
private void createContent() {
for(int i=0; i<config.numinputs; i++) {
int style = config.commonstyle.style;
if(config.inputstyle != null && config.inputstyle.length > i &&
config.inputstyle[i] != null) {
style = config.inputstyle[i].style;
}
if(style == XfdmInputStyle.FREE_TRIANGLES) {
createFreeTriangles(inputtype[i],input[i]);
} else {
createTriangularFamily(inputtype[i],input[i]);
}
}
if(config.systemstyle.defuz == XfdmSystemStyle.FUZZYMEAN) {
for(int i=0; i<outputtype.length; i++) {
createSingletons(outputtype[i],output[i]);
}
} else {
for(int i=0; i<outputtype.length; i++) createBells(outputtype[i],output[i]);
}
createRules();
}
//-------------------------------------------------------------//
// 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;
}
//-------------------------------------------------------------//
// Genera el antecedente correspondiente a un punto de la //
// particion //
//-------------------------------------------------------------//
private Relation createAntecedent(int[] index) {
Variable ivar[] = rulebase.getInputs();
int is = Relation.IS;
LinguisticLabel pmf[] = new LinguisticLabel[index.length];
for(int i=0; i<index.length; i++) {
pmf[i] = ivar[i].getType().getAllMembershipFunctions()[index[i]];
}
Relation rel = Relation.create(is,null,null,ivar[0],pmf[0],rulebase);
for(int j=1; j<index.length; j++) {
Relation nrel = Relation.create(is,null,null,ivar[j],pmf[j],rulebase);
rel = Relation.create(Relation.AND,rel,nrel,null,null,rulebase);
}
return rel;
}
//-------------------------------------------------------------//
// Genera las reglas correspondientes a la particion //
//-------------------------------------------------------------//
private void createRules() {
rulebase.removeAllRules();
int index[] = new int[input.length];
Variable ovar[] = rulebase.getOutputs();
int count = computeNumberOfRules();
for(int i=0; i<count; i++) {
Rule rule = new Rule(createAntecedent(index));
for(int j=0; j<ovar.length; j++) {
LinguisticLabel pmf = ovar[j].getType().getAllMembershipFunctions()[i];
rule.add(new Conclusion(ovar[j],pmf,rulebase));
}
rulebase.addRule(rule);
incrementIndex(index);
}
}
//-------------------------------------------------------------//
// Calcula el numero de reglas en funcion de la particion //
//-------------------------------------------------------------//
private int computeNumberOfRules() {
int number = 1;
for(int i=0; i<input.length; i++) number = number*(input[i].length);
return number;
}
//=============================================================//
// Funciones que generan las funciones de pertenencia //
//=============================================================//
//-------------------------------------------------------------//
// Crea una familia de triangulos //
//-------------------------------------------------------------//
private void createTriangularFamily(Type type, double[] partition) {
type.removeAllLabels();
type.removeAllFamilies();
double param[] = new double[partition.length-2];
for(int i=0; i<param.length; i++) param[i] = partition[i+1];
Family fam = new pkg.xfl.family.triangular();
fam.set("fam",type);
try { fam.set(param); type.addFamily(fam); } catch(XflException ex) {}
for(int i=0; i<fam.members(); i++) {
FamiliarMemFunc fmf = new FamiliarMemFunc("mf"+i,fam,i);
try { type.add(fmf); } catch(XflException e) {}
}
}
//-------------------------------------------------------------//
// Crea un conjunto de triangulos equiespaciados //
//-------------------------------------------------------------//
private void createFreeTriangles(Type type, double[] partition) {
type.removeAllLabels();
type.removeAllFamilies();
double param[] = new double[partition.length+2];
param[0] = partition[0]-1;
param[param.length-1] = partition[partition.length-1]+1;
for(int i=0; i<partition.length; i++) param[i+1] = partition[i];
Universe u = type.getUniverse();
double pp[] = new double[3];
for(int i=0; i<partition.length; i++) {
pp[0] = param[i];
pp[1] = param[i+1];
pp[2] = param[i+2];
ParamMemFunc pmf = new pkg.xfl.mfunc.triangle();
pmf.set("mf"+i,u);
try { pmf.set(pp); type.add(pmf); } catch(XflException ex) {}
}
}
//-------------------------------------------------------------//
// Crea un conjunto de singularidades //
//-------------------------------------------------------------//
private void createSingletons(Type type, double[] value) {
type.removeAllLabels();
Universe u = type.getUniverse();
for(int i=0; i<value.length; i++) {
ParamMemFunc pmf = new pkg.xfl.mfunc.singleton();
pmf.set("mf"+i, u);
pmf.set(value[i]);
try { type.add(pmf); } catch(XflException e) {}
}
}
//-------------------------------------------------------------//
// Crea un conjunto de campanas //
//-------------------------------------------------------------//
private void createBells(Type type, double[] value) {
type.removeAllLabels();
Universe u = type.getUniverse();
double min = u.min();
double max = u.max();
double param[] = new double[2];
param[1] = (max - min)/10;
for(int i=0; i<value.length; i++) {
ParamMemFunc pmf = new pkg.xfl.mfunc.bell();
pmf.set("mf"+i, u);
param[0] = value[i];
try { pmf.set(param); type.add(pmf); } catch(XflException ex) {}
}
}
//=============================================================//
// Funciones que manejan las particiones //
//=============================================================//
//-------------------------------------------------------------//
// Crea la particion inicial de las entradas (con min y max) //
//-------------------------------------------------------------//
private void initInputPartition() {
this.input = new double[inputtype.length][2];
for(int i=0; i<input.length; i++) {
input[i][0] = inputtype[i].getUniverse().min();
input[i][1] = inputtype[i].getUniverse().max();
}
}
//-------------------------------------------------------------//
// Crea los valores de salida de la particion inicial //
//-------------------------------------------------------------//
private void initOutputPartition() {
int points = computeNumberOfRules();
this.output = new double[outputtype.length][points];
int index[] = new int[input.length];
for(int i=0; i<points; i++) {
int pp = getPatternIndex( getPartitionFromIndexes(index) );
for(int j=0; j<output.length; j++) output[j][i] = pattern.output[pp][j];
incrementIndex(index);
}
}
//-------------------------------------------------------------//
// Incrementa un vector de indices sobre la particion //
//-------------------------------------------------------------//
private void incrementIndex(int[] index) {
boolean overhead = true;
int i = index.length-1;
while(overhead && i>=0) {
index[i]++;
if(index[i]>=input[i].length) { index[i] = 0; i--; }
else overhead = false;
}
}
//-------------------------------------------------------------//
// Obtiene un punto de la particion a partir de los indices //
//-------------------------------------------------------------//
private double[] getPartitionFromIndexes(int[] index) {
double partition[] = new double[input.length];
for(int i=0; i<input.length; i++) partition[i] = input[i][index[i]];
return partition;
}
//=============================================================//
// Funciones de busqueda en los patrones //
//=============================================================//
//-------------------------------------------------------------//
// Busca el patron mas cercano a un punto del espacio de //
// entrada //
//-------------------------------------------------------------//
private int getPatternIndex(double[] input) {
int index = 0;
double dist = patternDistance(0,input);
for(int i=1; i<pattern.input.length; i++) {
double nd = patternDistance(i,input);
if(nd<dist) { dist = nd; index = i; }
}
return index;
}
//-------------------------------------------------------------//
// Calcula la distancia de un patron a un punto del espacio de //
// entrada //
//-------------------------------------------------------------//
private double patternDistance(int index, double[] input) {
double dist = 0;
for(int i=0; i<input.length; i++) {
dist+=(pattern.input[index][i]-input[i])*(pattern.input[index][i]-input[i]);
}
return dist;
}
//=============================================================//
// Funciones de evaluacion del sistema //
//=============================================================//
//-------------------------------------------------------------//
// Verifica si se han cumplido las condiciones de termino //
//-------------------------------------------------------------//
private boolean satisfyEndCondition() {
if(rule_limit > 0 && rulebase.getRules().length > rule_limit) return true;
if(rmse_limit > 0 && last_rmse <= rmse_limit) return true;
for(int i=0; i<input.length; i++) {
if(mfs_limit > 0 && input[i].length > mfs_limit) return true;
}
if(worst_pattern < 0) return true;
return false;
}
//-------------------------------------------------------------//
// Calcula el error medio que comete el sistema y el patron en //
// el que la desviacion es mayor //
//-------------------------------------------------------------//
private void computeError() {
SystemModule system = spec.getSystemModule();
double mxae=0;
double mse=0;
int index = -1;
for(int p=0; p<pattern.input.length; p++) {
double[] out = system.crispInference(pattern.input[p]);
for(int i=0; i<out.length; i++) {
double dev = (out[i]-pattern.output[p][i])/pattern.range[i];
if(dev<0) dev = -dev;
mse += dev*dev/out.length;
if(dev>mxae) { mxae = dev; index = p; }
}
}
this.last_rmse = Math.sqrt(mse/pattern.input.length);
int list[] = new int[0];
while(!testNewPoint(index)) {
int nl[] = new int[list.length+1];
System.arraycopy(list,0,nl,0,list.length);
nl[list.length] = index;
list = nl;
mxae = 0;
index = -1;
for(int p=0; p<pattern.input.length; p++) {
boolean excluded = false;
for(int l=0; l<list.length; l++) if(list[l] == p) excluded = true;
if(excluded) continue;
double[] out = system.crispInference(pattern.input[p]);
for(int i=0; i<out.length; i++) {
double dev = (out[i]-pattern.output[p][i])/pattern.range[i];
if(dev<0) dev = -dev;
if(dev>mxae) { mxae = dev; index = p; }
}
}
}
this.worst_pattern = index;
}
//-------------------------------------------------------------//
// Estudia el punto a incluir //
//-------------------------------------------------------------//
private boolean testNewPoint(int pattern_index) {
double newpoint[] = pattern.input[pattern_index];
int position[] = new int[input.length];
for(int i=0; i<input.length; i++) {
double range = input[i][input[i].length-1] - input[i][0];
int j;
for(j=0; j<input[i].length; j++) if(input[i][j] > newpoint[i]) break;
double prev = (j==0? input[i][0] : input[i][j-1]);
double next = (j==input[i].length? input[i][j-1] : input[i][j]);
boolean insert = true;
if(newpoint[i]-prev < range/100) insert = false;
if(next-newpoint[i] < range/100) insert = false;
if(insert) position[i] = j; else position[i] = -1;
}
for(int i=0; i<position.length; i++) if(position[i] != -1) return true;
return false;
}
//-------------------------------------------------------------//
// Actualiza la particion //
//-------------------------------------------------------------//
private void updatePartition() {
if(worst_pattern < 0) return;
double newpoint[] = pattern.input[worst_pattern];
int position[] = new int[input.length];
for(int i=0; i<input.length; i++) {
double range = input[i][input[i].length-1] - input[i][0];
int j;
for(j=0; j<input[i].length; j++) if(input[i][j] > newpoint[i]) break;
double prev = (j==0? input[i][0] : input[i][j-1]);
double next = (j==input[i].length? input[i][j-1] : input[i][j]);
boolean insert = true;
if(newpoint[i]-prev < range/100) insert = false;
if(next-newpoint[i] < range/100) insert = false;
if(insert) {
double ni[] = new double[input[i].length+1];
System.arraycopy(input[i],0,ni,0,j);
ni[j] = newpoint[i];
System.arraycopy(input[i],j,ni,j+1,input[i].length-j);
input[i] = ni;
position[i] = j;
} else position[i] = -1;
}
int old=0;
int index[] = new int[input.length];
int count = computeNumberOfRules();
double no[][] = new double[outputtype.length][count];
for(int i=0; i<count; i++) {
boolean newvalue = false;
for(int j=0; j<index.length; j++) if(index[j] == position[j]) newvalue=true;
if(!newvalue) {
for(int j=0; j<no.length; j++) no[j][i] = output[j][old];
old++;
} else {
int pp = getPatternIndex( getPartitionFromIndexes(index) );
for(int j=0; j<no.length; j++) no[j][i] = pattern.output[pp][j];
}
incrementIndex(index);
}
output = no;
}
//-------------------------------------------------------------//
// Ajusta las salidas del sistema con el algoritmo de //
// Marquardt-Levenberg //
//-------------------------------------------------------------//
private void learning() {
double param[] = { 0.1, 10.0, 0.1};
int alg = XfslAlgorithm.MARQUARDT;
int err = XfslErrorFunction.MEAN_SQUARE_ERROR;
int end = XfslEndCondition.TRN_VAR;
XfslConfig xfslconfig = new XfslConfig();
try {
xfslconfig.trainingfile = config.patternfile;
xfslconfig.algorithm = XfslAlgorithm.create(alg,param);
xfslconfig.errorfunction = new XfslErrorFunction(err);
xfslconfig.endcondition.setLimit(end,0.001);
xfslconfig.addSetting("ANY.ANY.ANY",false);
for(int i=0; i<outputtype.length; i++) {
xfslconfig.addSetting(outputtype[i].getName()+".ANY.ANY",true);
}
XfslThread lrnthread = new XfslThread(spec,xfslconfig);
lrnthread.run();
} catch (Exception ex) { }
for(int i=0; i<outputtype.length; i++) {
LinguisticLabel label[] = outputtype[i].getAllMembershipFunctions();
for(int j=0; j<label.length; j++) {
this.output[i][j] = label[j].get()[0];
}
}
}
}