//--------------------------------------------------------------------------------//
// 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 SENHADJI DE REGLAS NO REDUNDANTES //
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
package xfuzzy.xfdm;
import xfuzzy.lang.*;
import java.util.Vector;
public class XfdmSenhadji extends XfdmActiveRules {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// MIEMBROS PRIVADOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
private int numrules;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// CONSTRUCTOR //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//-------------------------------------------------------------//
// Constructor con el numero de clases //
//-------------------------------------------------------------//
public XfdmSenhadji(int numrules) {
this.numrules = numrules;
}
//-------------------------------------------------------------//
// Constructor por defecto //
//-------------------------------------------------------------//
public XfdmSenhadji() {
this.numrules = 5;
}
//-------------------------------------------------------------//
// Constructor desde el fichero de configuracion //
//-------------------------------------------------------------//
public XfdmSenhadji(double param[]) {
this.numrules = (int) param[0];
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// METODOS PUBLICOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//-------------------------------------------------------------//
// Obtiene un duplicado del objeto //
//-------------------------------------------------------------//
public Object clone() {
return new XfdmSenhadji(numrules);
}
//-------------------------------------------------------------//
// Obtiene el nombre del algoritmo //
//-------------------------------------------------------------//
public String toString() {
return "Senhadji (Non-redundant rule extraction)";
}
//-------------------------------------------------------------//
// Representacion en el fichero de configuracion //
//-------------------------------------------------------------//
public String toCode() {
return "xfdm_algorithm(Senhadji,"+numrules+")";
}
//-------------------------------------------------------------//
// Obtiene el numero de reglas a seleccionar //
//-------------------------------------------------------------//
public int getNumberOfRules() {
return numrules;
}
//-------------------------------------------------------------//
// Asigna el numero de reglas a seleccionar //
//-------------------------------------------------------------//
public void setNumberOfRules(int number) {
this.numrules = number;
}
//-------------------------------------------------------------//
// Selecciona las reglas mas correctas //
//-------------------------------------------------------------//
public Vector pruneRules(Vector rules) {
int sort[] = getSortedList(rules);
Vector selected = new Vector();
Vector nonselected = new Vector();
Vector redundant = new Vector();
for(int i=0; i<numrules; i++) selected.add(rules.elementAt(sort[i]));
for(int i=numrules; i<sort.length; i++) {
XfdmPseudoRule pseudo = (XfdmPseudoRule) rules.elementAt(sort[i]);
double perform = pseudo.getPerformance();
if(perform < 0) break;
nonselected.add(rules.elementAt(sort[i]));
}
boolean linkable[][][] = createLinkMatrix(true);
boolean links[][][] = createLinkMatrix(false);
boolean tentative[][][] = createLinkMatrix(false);
updateLinkable(linkable,selected);
XfdmPseudoRule rule2=searchRedundant(selected,redundant,linkable);
while(rule2 != null && !nonselected.isEmpty()) {
XfdmPseudoRule rule1 = getRedundant(selected,rule2,linkable);
createTentativeLinks(rule1, rule2, links, tentative);
boolean subst = false;
while(!subst && !nonselected.isEmpty()) {
XfdmPseudoRule rule3 = (XfdmPseudoRule) nonselected.elementAt(0);
if(breakTentativeLinks(selected, rule3, tentative)) {
nonselected.removeElement(rule3);
} else {
nonselected.removeElement(rule3);
selected.addElement(rule3);
redundant.addElement(rule2);
updateLinks(links,tentative);
updateLinkable(linkable,selected);
subst = true;
}
}
rule2=searchRedundant(selected,redundant,linkable);
}
removeRedundantRules(selected,redundant);
return selected;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// METODOS PRIVADOS //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//-------------------------------------------------------------//
// Obtiene la lista de reglas ordenada por eficiencia //
//-------------------------------------------------------------//
private int[] getSortedList(Vector rules) {
double perform[][] = new double[rules.size()][2];
for(int i=0; i<perform.length; i++) {
perform[i][0] = ((XfdmPseudoRule) rules.elementAt(i)).getPerformance();
perform[i][1] = 1.0*i;
}
double aux0, aux1;
for(int i=0; i<perform.length-1; i++) {
for(int j=i+1; j<perform.length; j++) {
if(perform[i][0]<perform[j][0]) {
aux0 = perform[i][0]; aux1 = perform[i][1];
perform[i][0] = perform[j][0]; perform[i][1] = perform[j][1];
perform[j][0] = aux0; perform[j][1] = aux1;
}
}
}
int sorted[] = new int[rules.size()];
for(int i=0; i<sorted.length; i++) sorted[i] = (int) perform[i][1];
return sorted;
}
//-------------------------------------------------------------//
// Crea una matriz de enlazabilidad de las MF de un tipo //
//-------------------------------------------------------------//
private boolean[][][] createLinkMatrix(boolean init) {
boolean link[][][] = new boolean[inputtype.length][][];
for(int i=0; i<link.length; i++) {
LinguisticLabel label[] = inputtype[i].getAllMembershipFunctions();
link[i] = new boolean[label.length][label.length];
for(int j=0; j<label.length; j++) for(int k=0; k<label.length; k++) {
link[i][j][k] = (j==k || init);
}
}
return link;
}
//-------------------------------------------------------------//
// Actualiza la matriz de enlaces //
//-------------------------------------------------------------//
private void updateLinks(boolean links[][][], boolean tentative[][][]) {
for(int i=0; i<links.length; i++) {
for(int j=0; j<links[i].length; j++) {
for(int k=0; k<links[i][j].length; k++) {
links[i][j][k] = tentative[i][j][k];
}
}
}
}
//-------------------------------------------------------------//
// Actualiza la matriz de funciones enlazables //
//-------------------------------------------------------------//
private void updateLinkable(boolean linkable[][][], Vector rules) {
int size = rules.size();
for(int i=0; i<size; i++) for(int j=i+1; j<size; j++) {
XfdmPseudoRule rule1 = (XfdmPseudoRule) rules.elementAt(i);
XfdmPseudoRule rule2 = (XfdmPseudoRule) rules.elementAt(j);
int input1[] = rule1.getAntecedent();
int input2[] = rule2.getAntecedent();
int column = differentColumn(input1,input2);
if(column == -1 || hasSameOutputs(rule1,rule2)) continue;
int min = (input1[column]<input2[column] ? input1[column] : input2[column]);
int max = (input1[column]<input2[column] ? input2[column] : input1[column]);
for(int k=0;k<=min; k++) for(int l=max; l<linkable[column].length; l++) {
linkable[column][l][k] = false;
linkable[column][k][l] = false;
}
}
}
//-------------------------------------------------------------//
// Estudia si dos reglas tienen la misma salida //
//-------------------------------------------------------------//
private boolean hasSameOutputs(XfdmPseudoRule rule1, XfdmPseudoRule rule2) {
int output1[] = rule1.getClassIndexes();
int output2[] = rule2.getClassIndexes();
for(int i=0; i<output1.length; i++) if(output1[i] != output2[i]) return false;
return true;
}
//-------------------------------------------------------------//
// Devuelve el indice de la columna en que se diferencian //
// o -1 si se diferencian en mas de una columna //
//-------------------------------------------------------------//
private int differentColumn(int list1[], int list2[]) {
int column = -1;
int count = 0;
for(int i=0; i<list1.length; i++) {
if(list1[i] != list2[i]) { column=i; count++; }
}
if(count > 1) return -1;
return column;
}
//-------------------------------------------------------------//
// Estudia si dos reglas son redundantes //
//-------------------------------------------------------------//
private boolean isRedundant(XfdmPseudoRule rule1,
XfdmPseudoRule rule2,
boolean linkable[][][]) {
if(!hasSameOutputs(rule1,rule2)) return false;
int input1[] = rule1.getAntecedent();
int input2[] = rule2.getAntecedent();
for(int i=0; i<linkable.length; i++) {
if(!linkable[i][input1[i]][input2[i]]) return false;
}
if(differentColumn(input1,input2)<0) return false;
return true;
}
//-------------------------------------------------------------//
// Estudia si una regla es redundante respecto a alguna de //
// mayor eficiencia //
//-------------------------------------------------------------//
private XfdmPseudoRule getRedundant(Vector selected,
XfdmPseudoRule rule,
boolean linkable[][][]) {
int size = selected.size();
if(selected.contains(rule)) size = selected.indexOf(rule);
for(int i=0; i<size; i++) {
XfdmPseudoRule rule1 = (XfdmPseudoRule) selected.elementAt(i);
if(isRedundant(rule1,rule,linkable)) return rule1;
}
return null;
}
//-------------------------------------------------------------//
// Busca la regla redundante de menor eficiencia que no haya //
// sido sustituida //
//-------------------------------------------------------------//
private XfdmPseudoRule searchRedundant(Vector selected,
Vector redundant,
boolean linkable[][][]) {
for(int i=selected.size()-1; i>=0; i--) {
XfdmPseudoRule rule1 = (XfdmPseudoRule) selected.elementAt(i);
if(redundant.contains(rule1)) continue;
for(int j=i-1; j>=0; j--) {
XfdmPseudoRule rule2 = (XfdmPseudoRule) selected.elementAt(j);
if(isRedundant(rule2,rule1,linkable)) return rule1;
}
}
return null;
}
//-------------------------------------------------------------//
// Calcula los enlaces para sustituir rule2 //
//-------------------------------------------------------------//
private void createTentativeLinks(XfdmPseudoRule rule1,
XfdmPseudoRule rule2,
boolean links[][][],
boolean tentative[][][]) {
int input1[] = rule1.getAntecedent();
int input2[] = rule2.getAntecedent();
int min,max;
for(int i=0; i<links.length; i++) {
for(int j=0; j<links[i].length; j++) {
for(int k=0; k<links[i][j].length; k++) {
tentative[i][j][k] = links[i][j][k];
if(input1[i]<=input2[i]) { min = input1[i]; max = input2[i]; }
else { min = input2[i]; max = input1[i]; }
if(j>=min && j<=max && k>=min && k<=max) tentative[i][j][k] = true;
}
}
}
}
//-------------------------------------------------------------//
// Estudia si una regla rompe los enlaces en que participa otra//
//-------------------------------------------------------------//
private boolean breakTentativeLinks(XfdmPseudoRule rule1,
XfdmPseudoRule rule2,
boolean tentative[][][]) {
if(hasSameOutputs(rule1,rule2)) return false;
int input1[] = rule1.getAntecedent();
int input2[] = rule2.getAntecedent();
int column = differentColumn(input1,input2);
if(column == -1) return false;
return tentative[column][input1[column]][input2[column]];
}
//-------------------------------------------------------------//
// Estudia si una regla rompe algun enlace //
//-------------------------------------------------------------//
private boolean breakTentativeLinks(Vector selected,
XfdmPseudoRule rule,
boolean tentative[][][]) {
int size = selected.size();
for(int i=0; i<size; i++) {
XfdmPseudoRule prule = (XfdmPseudoRule) selected.elementAt(i);
if(breakTentativeLinks(prule,rule,tentative)) return true;
}
return false;
}
//-------------------------------------------------------------//
// Elimina de la seleccion las reglas redundantes //
//-------------------------------------------------------------//
private void removeRedundantRules(Vector selected, Vector redundant) {
int size = redundant.size();
for(int i=0; i<size; i++) selected.removeElement(redundant.elementAt(i));
}
}