XFL3: El lenguaje de especificación de Xfuzzy 3

Definición de familias de funciones de pertenencia

Las familias de funciones de pertenencia describen conjuntos de funciones de pertenencia que comparten una lista de parámetros. Las familias se utilizan para definir conjuntos de funciones de pertenencia con unas ciertas restricciones, como la simetría entre las funciones, el orden entre ellas o un grado de solapamiento fijo. Cada función de pertenencia es referenciada por medio de su índice dentro de la familia. La estructura de una definición de una familia de funciones de pertenencia en un paquete de funciones es como sigue:

		family identifier { blocks }                    

Los bloques que pueden aparecer en la definición de una familia de funciones de pertenencia son: alias, parameter, requires, members, java, ansi_c, cplusplus, derivative, update y source.

El bloque alias se utiliza para definir nombres alternativos para identificar a la familia. Cualquiera de esos identificadores puede ser usado para hacer referencia a la familia. La sintaxis del bloque alias es:

		alias identifier, identifier, ... ;                   

El bloque parameter permite la definición de los parámetros de los que depende la familia. El último identificador puede ir seguido de corchetes para definir una lista de parámetros. Su formato es:

		parameter identifier, identifier, ..., identifier[] ;                   

El bloque requires expresa las restricciones sobre los valores de los parámetros por medio de una expresión Booleana en Java que valida los valores de los parámetros. Esta expresión puede usar también los valores de las variables 'min' y 'max', que representan los valores mínimo y máximo del universo de discurso de la variable lingüística considerada. La estructura de este bloque es:

		requires { expression }                    

El bloque members define el número de funciones de pertenencia incluidas en la familia por medio de una expresión en Java que devuelve un valor entero. La sintaxis de este bloque es:

		members { Java_function_body }                    

Los bloques java, ansi_c y cplusplus describen el comportamiento de la función por medio de su descripción como el cuerpo de una función en los lenguajes de programación Java, C y C++, respectivamente. El formato de estos bloques es el siguiente:

		java { Java_function_body } 
		ansi_c { C_function_body } 
		cplusplus { C++_function_body } 

La definición de una familia de funciones de pertenencia incluye no sólo la descripción del comportamiento de la función en sí misma, sino también del comportamiento de las funciones bajo la acción de los modificadores greater-or-equal y smaller-or-equal, así como el cálculo de los valores del centro y la base de las funciones de pertenencia. Como consecuencia, los bloques java, ansi_c y cplusplus se dividen en los siguientes subbloques:

		equal { code }
		greatereq { code }
		smallereq { code }
		center { code }
		basis { code }  

El subbloque equal describe el comportamiento de la función. Los subbloques greatereq y smallereq describen la acción de los modificadores greater-or-equal y smaller-or-equal respectivamente. La variable 'i' contiene el índice que permite identificar la función de pertenencia de la familia. La variable de entrada en estos subbloques se denomina 'x'. El código puede usar los valores de los parámetros de la función, así como las variables 'min' y 'max', que representan los valores mínimo y máximo del universo de discurso de la familia. Los subbloques greatereq y smallereq pueden ser omitidos. En ese caso las transformaciones correspondientes son calculadas recorriendo todos los valores del universo de discurso. Sin embargo, resulta mucho más eficiente usar la función analítica, por lo que la definición de estos subbloques está fuertemente recomendada.

Los subbloques center y basis describen el centro y la base de las funciones de pertenencia. El código de estos subbloques puede usar los valores de la variable 'i' (el índice de la función de pertenencia), los parámetros de la familia y las variables 'min' y 'max'. Esta información es usada por varios métodos de defuzzificación simplificados. Estos subbloques son opcionales y su función por defecto devuelve un valor nulo.

El bloque derivative describe la derivada de cada función con respecto a cada parámetro. Este bloque es también dividido en los subbloques equal, greatereq, smallereq, center y basis. El código de estos subbloques consiste en expresiones Java que asignan valores a la variable 'deriv[]'. El valor de 'deriv[i]' representa la derivada de cada función con respecto al i-ésimo parámetro de la familia. La descripción de la derivada de la función permite calcular la derivada de la función de error del sistema utilizada por los algoritmos de aprendizaje basados en gradiente descendente. El formato es:

		derivative { subblocks }                       

El bloque update se utiliza para calcular unos valores válidos para el conjunto de parámetros (almacenados en la variable pos[]) a partir de los desplazamientos deseados (almacenados en la variable disp[]) generados en un proceso de ajuste automático, teniendo en cuenta la selección de parámetros a ajustar (almacenados en la variable booleana adj[]). Un tipo de restricción muy común para el desplazamiento consiste en mantener el orden de los parámetros. Este proceso puede garantizarse utilizando la función sortedUpdate(pos,disp,adj). El código Java puede utilizar las variables 'min', 'max' y 'step', que representan el mínimo, el máximo y la división del universo de discurso, respectivamente. La sintaxis del bloque update es:

		update { Java_function_body }                      

El bloque source es utilizado para definir código Java que es directamente incluido en el código de la clase generada para la definición de la función. Este código nos permite definir métodos locales que pueden ser empleados dentro de otros bloques. La estructura es:

		source { Java_code }                      

El siguiente ejemplo muestra la definición de la familia de funciones de pertenencia triangular.

 family triangular {
   parameter p[];
   requires { p.length==0 || (p.length>0 && p[0]>min && p[p.length-1]<max && sorted(p)) }
   members { return p.length+2; }
   java {
    equal {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     double c = (i==p.length? max : (i==p.length+1? max+1 : p[i]));
     return (a<x && x<=b? (x-a)/(b-a) : (b<x && x<c? (c-x)/(c-b) : 0));
    }
    greatereq {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     return (x<a? 0 : (x>b? 1 : (x-a)/(b-a) ));
    }
    smallereq {
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     double c = (i==p.length? max : (i==p.length+1? max+1 : p[i]));
     return (x<b? 1 : (x>c? 0 : (c-x)/(c-b) ));
    }
    center {
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     return b;
    }
    basis {
     double a = (i<=1 ? min : p[i-2]);
     double c = (i>=p.length? max : p[i]);
     return (c-a);
    }
   }
   ansi_c {
    equal {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     double c = (i==length? max : (i==length+1? max+1 : p[i]));
     return (a<x && x<=b? (x-a)/(b-a) : (b<x && x<c? (c-x)/(c-b) : 0));
    }
    greatereq {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     return (x<a? 0 : (x>b? 1 : (x-a)/(b-a) ));
    }
    smallereq {
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     double c = (i==length? max : (i==length+1? max+1 : p[i]));
     return (x<b? 1 : (x>c? 0 : (c-x)/(c-b) ));
    }
    center {
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     return b;
    }
    basis {
     double a = (i<=1 ? min : p[i-2]);
     double c = (i>=length? max : p[i]);
     return (c-a);
    }
   }
   cplusplus {
    equal {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     double c = (i==length? max : (i==length+1? max+1 : p[i]));
     return (a<x && x<=b? (x-a)/(b-a) : (b<x && x<c? (c-x)/(c-b) : 0));
    }
    greatereq {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     return (x<a? 0 : (x>b? 1 : (x-a)/(b-a) ));
    }
    smallereq {
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     double c = (i==length? max : (i==length+1? max+1 : p[i]));
     return (x<b? 1 : (x>c? 0 : (c-x)/(c-b) ));
    }
    center {
     double b = (i==0? min : (i==length+1? max : p[i-1]));
     return b;
    }
    basis {
     double a = (i<=1 ? min : p[i-2]);
     double c = (i>=length? max : p[i]);
     return (c-a);
    }
   }
   derivative {
    equal {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     double c = (i==p.length? max : (i==p.length+1? max+1 : p[i]));
     if(i>=2) {
      if(a<x && x<b) deriv[i-2] = (x-b)/((b-a)*(b-a));
      else if(x==a) deriv[i-2] = 0.5/(a-b);
      else deriv[i-2] = 0;
     }
     if(i>=1 && i<=p.length) {
      if(a<x && x<b) deriv[i-1] = (a-x)/((b-a)*(b-a));
      else if(b<x && x<c) deriv[i-1] = (c-x)/((c-b)*(c-b));
      else if(x==b) deriv[i-1] = 0.5/(a-b) + 0.5/(c-b);
      else deriv[i-1] = 0;
     }
     if(i<p.length) {
      if(b<x && x<c) deriv[i] = (x-b)/((c-b)*(c-b));
      else if(x==c) deriv[i] = 0.5/(c-b);
      else deriv[i] = 0;
     }
    }
    greatereq {
     double a = (i==0? min-1 : (i==1 ? min : p[i-2]));
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     if(i>=2) {
      if(a<x && x<b) deriv[i-2] = (x-b)/((b-a)*(b-a));
      else if(x==a) deriv[i-2] = 0.5/(a-b);
      else deriv[i-2] = 0;
     }
     if(i>=1 && i<=p.length) {
      if(a<x && x<b) deriv[i-1] = (a-x)/((b-a)*(b-a));
      else if(x==b) deriv[i-1] = 0.5/(a-b);
      else deriv[i-1] = 0;
     }
    }
    smallereq {
     double b = (i==0? min : (i==p.length+1? max : p[i-1]));
     double c = (i==p.length? max : (i==p.length+1? max+1 : p[i]));
     if(i>=1 && i<=p.length) {
      if(b<x && x<c) deriv[i-1] = (c-x)/((c-b)*(c-b));
      else if(x==b) deriv[i-1] = 0.5/(c-b);
      else deriv[i-1] = 0;
     }
     if(i<p.length) {
      if(b<x && x<c) deriv[i] = (x-b)/((c-b)*(c-b));
      else if(x==c) deriv[i] = 0.5/(c-b);
      else deriv[i] = 0;
     }
    }
    center {
     if(i>=1 && i<=p.length) deriv[i-1] = 1;
    }
    basis {
     if(i>1) deriv[i-2] = -1;
     if(i<p.length) deriv[i] = 1;
    }
   }
   update {
    if(p.length == 0) return;
    pos = sortedUpdate(pos,desp,adj);
    if(pos[0]<=min) {
     pos[0]=min+step;
     for(int i=1;i<p.length; i++) {
      if(pos[i]<=pos[i-1]) pos[i] = pos[i-1]+step;
      else break;
     }
    }
    if(pos[p.length-1]>=max) {
     pos[p.length-1]=max-step;
     for(int i=p.length-2; i>=0; i--) {
      if(pos[i]>=pos[i+1]) pos[i] = pos[i+1]-step;
      else break;
     }
    }
   }
  }

Para comentarios, sugerencias, notificación de bugs, etc. contacte con nosotros en:   xfuzzy-team@imse-cnm.csic.es

©IMSE-CNM 2018