//--------------------------------------------------------------------------------//
// 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. //
//--------------------------------------------------------------------------------//
package xfuzzy.xfsp.model.rulebases;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import xfuzzy.lang.Rulebase;
import xfuzzy.lang.Specification;
import xfuzzy.lang.Variable;
/**
* <p> <b>Descripci�n:</b> simplificador de bases de reglas mediante un m�todo
* tabular similar al de Quine-McCluskey.
* <p> <b>E-mail</b>: <ADDRESS>joragupra@us.es</ADDRRESS>
* @author Jorge Agudo Praena
* @version 5.0
* @see XfspModel
* @see IXfspRulebaseSimplifier
*
*/
public class XfspTabularSimplifier
implements IXfspRulebaseSimplifier {
//indica si se deben mantener las funciones de pertenencia situadas en los
//extremos del universo de discurso de cada variable
private boolean maintainExtremeMF;
/**
* <p> <b>Descripci�n:</b> crea un objeto que simplifica bases de reglas
* mediante un m�todo tabular basado en el m�todo de Quine-McCluskey para
* simplificaci�n de funciones de conmutaci�n del �lgebra booleana.
* @param maintainExtremeMF Si vale cierto se mantendr�n las funciones de
* pertenencia situadas en los extremos del universo de discurso de cada tipo
* tras realizar la simplificaci�n de bases de reglas y, en caso contrario,
* no se mantendr�n.
*
*/
public XfspTabularSimplifier(boolean maintainExtremeMF){
//establece si se deben mantener las funciones de pertenencia situadas en
//los extremos de los tipos
this.maintainExtremeMF = maintainExtremeMF;
}
/**
* <p> <b>Descripci�n:</b> simplifica una base de reglas de un sistem difuso
* mediante m�todos tabulares.
* @param rulebase Base de reglas que se quiere simplificar.
* @param spec Especificaci�n del sistema cuya base de reglas se va a
* simplificar.
*
*/
public void simplify(Rulebase rulebase, Specification spec) {
//System.out.println("Base de reglas original");
//System.out.println(rulebase.toXfl());
//crea un objeto capaz de transformar una base de reglas en un conjunto de
//mint�rminos
XfspMintermDirector director = new XfspMintermDirector();
//crea un conjunto de mint�rminos correspondiente a la base de reglas que
//se quiere simplificar
Map minterms = director.build(rulebase);
/*
System.out.println("Mint�rminos iniciales:");
Iterator itaux = minterms.values().iterator();
while(itaux.hasNext()){
List l = (List) itaux.next();
Set s = (Set) l.get(0);
Iterator itaux2 = s.iterator();
while(itaux2.hasNext()){
List l2 = (List) itaux2.next();
Iterator itaux3 = l2.iterator();
while(itaux3.hasNext()){
Set s2 = (Set) itaux3.next();
Iterator itaux4 = s2.iterator();
while(itaux4.hasNext()){
System.out.println((XfspMinterm)itaux4.next());
}
}
}
}*/
//iterador para el conjunto de mint�rminos equivalente a la base de reglas
Iterator it = minterms.values().iterator();
//mientras queden mint�rminos para una determinada conclusi�n por
//procesar...
while (it.hasNext()) {
//...obtiene la lista de iteraciones para una conclusi�n
List l = (List) it.next();
boolean end = false;
while (!end) {
end = true;
//obtiene la �ltima iteraci�n que se ha creado
Set currentIteration = (Set) l.get(l.size() - 1);
//inicializa la siguiente iteraci�n como un conjunto vac�o
Set nextIteration = new HashSet();
//iterador para la iteraci�n actual
Iterator it1 = currentIteration.iterator();
//mientras queden mint�rminos agrupables en la iteraci�n actual...
while (it1.hasNext()) {
//...obtiene la lista de mint�rminos agrupables de la iteraci�n actual
List aux = (List) it1.next();
//reduce la lista de mint�rminos agrupables de la iteraci�n actual
//(si es posible)
List newList = reduce(aux, rulebase.getInputs());
//si se ha podido reducir la lista de mint�rminos agrupables de la
//iteraci�n actual...
if (!newList.isEmpty()) {
//...contin�a iterando
end = false;
}
//a�ade a la siguiente iteraci�n el resultado de reducir la iteraci�n
//actual
nextIteration.add(newList);
}
//si se ha a�adido alguna lista reducida no vac�a a la siguiente
//iteraci�n...
if (!end) {
//...a�ade la siguiente iteraci�n a la lista de iteraciones
l.add(nextIteration);
}
}
}
//crea un objeto capaz de encontrar el cubrimiento m�nimo para un conjunto
//de mint�rminos asociados a una conclusi�n
XfspMinimalCoveringFinder finder = new XfspMinimalCoveringFinder();
//busca el cubrimiento m�nimo para el conjunto de mint�rminos
Map minimalCovering = finder.findMinimalCovering(minterms);
//crea un objeto que construye bases de reglas a partir de un cubrimiento
//m�nimo de un conjunto de mint�rminos
XfspRulebaseTabularDirector td = new XfspRulebaseTabularDirector();
//nuevo
td.setMaintainExtremeMF(this.maintainExtremeMF);
//crea la nueva base de reglas a partir del cubrimiento m�nimo
Rulebase newRulebase = td.build(minimalCovering, rulebase);
//cambia en la especificaci�n del sistema la base de reglas original por la
//nueva
spec.exchangeRulebase(rulebase, newRulebase);
//quita todas las reglas de la base de reglas original...
rulebase.removeAllRules();
//...y le a�ade todas las reglas de la nueva base de reglas
for (int i = 0; i < newRulebase.getRules().length; i++) {
rulebase.addRule(newRulebase.getRules()[i]);
}
}
/**
* <p> <b>Descripci�n:</b> reduce una lista cuyos elementos son conjuntos de
* mint�rminos produciendo una nueva lista cuyos elementos son conjuntos de
* tal forma que cada conjunto de la nueva lista es resultado de las uniones
* de mint�rminos de conjuntos adyacentes en la lista original.
* @param oldMinterms Lista de conjuntos de mint�rminos de tal forma que dos
* conjuntos adyacentes en la lista contiene mint�rminos posiblemente
* agrupables.
* @param vars Variables de entrada del sistema al que pertenecen los
* mint�rminos de la lista original.
*
*/
private List reduce(List oldMinterms, Variable[] vars) {
//almacenan mint�rminos consecutivos de la lista de mint�rminos que se
//quiere simplificar
XfspMinterm mt1, mt2;
//nueva lista resultado de reducir la lista de mint�rminos original
List result = new ArrayList();
//para toda pareja de conjuntos de mint�rminos agrupables (consecutivos) de
//la lista original...
for (int i = 0; i < (oldMinterms.size() - 1); i++) {
//...toma una nueva pareja de conjuntos de mint�rminos agrupables
Set s1 = (Set) oldMinterms.get(i);
Set s2 = (Set) oldMinterms.get(i + 1);
//conjunto resultado de la fusi�n de aquellas parejas de mint�rminos (uno
//de cada conjunto) que se puedan fusionar
Set merged = new HashSet();
//iterador que permite recorrer todos los mint�rminos del primer conjunto
Iterator it1 = s1.iterator();
//para todos los mint�rminos del primer conjunto...
while (it1.hasNext()) {
//...obtiene un nuevo mint�rmino del primer conjunto
mt1 = (XfspMinterm) it1.next();
//iterador que permite recorre todos los mint�rminos del segundo
//conjunto
Iterator it2 = s2.iterator();
//para todos los mint�rminos del segundo conjunto...
while (it2.hasNext()) {
//...obtiene un nuevo mint�rmino del segundo conjunto
mt2 = (XfspMinterm) it2.next();
//examina los dos mint�rminos y determina si se pueden agrupar en
//relaci�n a alguna de sus variables
Variable var = canBeMerged(mt1, mt2, vars);
//si los mint�rminos pueden ser fusionados...
if (var != null) {
//...comprueba que el mint�rmino resultante de la fusi�n de los dos
//anteriores no estaba ya en el conjunto y, si es el caso, lo a�ade
//al mismo (esto se hace porque el conjunto "merged" no comprueba
//correctamente que los elementos nuevos a a�adir no estuvieran
//antes en dicho conjunto, cosa que deber�a hacer)
boolean found = false;
Iterator it = merged.iterator();
while (it.hasNext()) {
XfspMinterm aux = (XfspMinterm) it.next();
if (aux.equals(merge(mt1, mt2, var))) {
found = true;
}
}
//si el resultado de la fusi�n no se encontraba ya en el conjunto
//de mint�rminos fusionados...
if (!found) {
//...lo a�ade a dicho conjunto
merged.add(merge(mt1, mt2, var));
}
}
}
//si se ha podido fusionar alguna pareja de mint�rminos...
if (!merged.isEmpty()) {
//...se a�ade el conjunto de mint�rminos fusionados a la lista que
//corresponde a la reducci�n de la lista original
result.add(merged);
}
}
}
//devuelve el resultado de reducir la lista de mint�rminos originales
return result;
}
/**
* <p> <b>Descripci�n:</b> comprueba si dos mint�rminos o implicantes pueden
* ser reducidas en un nuevo implicante.
* @param oldMinterms Lista de conjuntos de mint�rminos de tal forma que dos
* conjuntos adyacentes en la lista contiene mint�rminos posiblemente
* agrupables.
* @param vars Variables de entrada del sistema al que pertenecen los
* mint�rminos de la lista original.
*
*/
private Variable canBeMerged(XfspMinterm mt1, XfspMinterm mt2,
Variable[] vars) {
//map cuyas claves son las variables del primer mint�rmino y cuyos datos
//son las funciones de pertenencia que pueden tomar como valor dichas
//variables
Map mp1 = mt1.getVariables();
//map cuyas claves son las variables del segundo mint�rmino y cuyos datos
//son las funciones de pertenencia que pueden tomar como valor dichas
//variables
Map mp2 = mt2.getVariables();
//indica si se han encontrado dos conjuntos de funciones de pertenencia que
//se diferencien s�lo en un elemento
boolean found = false;
boolean end = false;
Variable var = null;
//versi�n nueva
//para todas las variables de entrada del sistema...
for(int i=0;i<vars.length & !end;i++){
//...obtiene las funciones de pertenencia asignadas a dicha variable en
//el primer mintermino...
Set s1 = (Set) mp1.get(vars[i]);
//...y en el segundo mint�rmino
Set s2 = (Set) mp2.get(vars[i]);
/*
System.out.println("Comparamos los conjuntos:");
System.out.println(s1);
System.out.println(s2);
*/
//si la variable actual tiene asignado un conjunto de funciones de
//pertenencia distinto en ambos mint�rminos y todav�a no se ha encontrado
//ninguna variable con asignaciones distintas hasta ahora...
if(!s1.equals(s2) & !found){
//...comprueba que las asignaciones en ambos mint�rminos s�lo se
//diferencien en un elemento...
if (differenceOne( (Set) mp1.get(vars[i]), (Set) mp2.get(vars[i]))) {
//...y en ese caso ya se ha encontrado la variable por la que se
//pueden fusionar ambos mint�rminos
found = true;
var = vars[i];
}
//nuevo!!!!!
else{
var = null;
end = true;
}
}
//si la variable actual tiene asignado un conjunto de funciones de
//pertenencia distinto en ambos mint�rminos y pero ya se ha encontrado
//alguna variable con asignaciones distintas...
else if(!s1.equals(s2) & found){
//...los dos mint�rminos no se podr�n fusionar por diferenciarse en
//m�s de una variable
var = null;
}
}
//versi�n antigua
//para todas las variables de entrada del sistema...
/*
for (int i = 0; i < vars.length && !found; i++) {
Variable aux = vars[i];
Set s1 = new HashSet();
Set s2 = new HashSet();
//iterador que permite recorrer todas las variables del primer map
Iterator it = mp1.keySet().iterator();
//para todas las variables del primer map...
while (it.hasNext()) {
//...obtiene una variable
Variable current = (Variable) it.next();
//si dicha variable no coincide con la variable de entrada que se est�
//examinando...
if (!current.equals(aux.getName())) {
//...la a�ade al primer conjunto
s1.add(mp1.get(current));
}
}
//iterador que permite recorrer todas las variables del segundo map
it = mp2.keySet().iterator();
//para todas las variables del segundo map...
while (it.hasNext()) {
//...obtiene una variable
Variable current = (Variable) it.next();
//si dicha variable no coincide con la variable de entrada que se est�
//examinando...
if (!current.equals(aux.getName())) {
//...la a�ade al segundo conjunto
s2.add(mp2.get(current));
}
}
//si ambos conjuntos contienen las mismas variables (esta comprobaci�n es
//trivial)...
if (s1.equals(s2)) {
//...comprueba que el conjunto de funciones de pertenencia que pueden
//tomar las variables de ambos conjuntos se diferencian en s�lo una
//variable...
if (differenceOne( (Set) mp1.get(aux), (Set) mp2.get(aux))) { //true){
//...y en ese caso ya se ha encontrado la variable por la que se
//pueden fusionar ambos mint�rminos
found = true;
var = aux;
}
}
}
*/
if(var!=null){
boolean prueba = differenceOneBis( (Set) mp1.get(var), (Set) mp2.get(var))==differenceOne( (Set) mp1.get(var), (Set) mp2.get(var));
if(prueba == false){
System.out.println("****************************** OJO ***********************************");
System.out.println("Se pueden fusionar" + mt1 + " y " + mt2 + " en " + var);
System.out.println("y el m�todo difference one para " + var + " devuelve " + prueba);
System.out.println("**********************************************************************");
}
}/*
else{
System.out.println("No se pueden fusionar" + mt1 + " y " + mt2);
}
*/
//devuelve la variable de entrada por la que se pueden fusionar ambos
//mint�rminos
return var;
}
//quitar (temporal)
private boolean differenceOneBis(Set s1, Set s2) {
//clona el primer conjunto
Set s1Clone = (Set) ( (HashSet) s1).clone();
//clona el segundo conjunto
Set s2Clone = (Set) ( (HashSet) s2).clone();
//elimina todos los elementos del segundo conjunto
s1Clone.retainAll(s2);
//elimina todos los elementos del primer conjunto
s2Clone.retainAll(s1);
//la diferencia entre los dos conjuntos es uno si, y s�lo si, tras quitar
//los elementos del conjunto contrario, quedan los mismos elementos que
//ten�a al principio excepto uno
boolean result = false;
if(s1.size()>1 && s2.size()>1){
result = (s1Clone.size() == (s1.size() - 1)) &&
(s2Clone.size() == (s2.size() - 1));
}else{
Iterator it = s1.iterator();
Integer i1 = (Integer) it.next();
it = s2.iterator();
Integer i2 = (Integer) it.next();
result = i1.equals(new Integer(i2.intValue()-1)) || i2.equals(new Integer(i1.intValue()-1));
}
return result;
}
/**
* <p> <b>Descripci�n:</b> comprueba si los conjuntos de funciones de
* pertenencia asociados a una variable de dos mint�rmino se diferencia en
* s�lo un elemento.
* @param s1 Primer conjunto de enteros que representa las funciones de
* pertenencia asociadas a una variable en un mint�rmino.
* @param s2 Segundo conjunto de enteros que representa las funciones de
* pertenencia asociadas a una variable en un mint�rmino.
*
*/
private boolean differenceOne(Set s1, Set s2) {
//clona el primer conjunto
Set s1Clone = (Set) ( (HashSet) s1).clone();
//clona el segundo conjunto
Set s2Clone = (Set) ( (HashSet) s2).clone();
//elimina todos los elementos del segundo conjunto
s1Clone.retainAll(s2);
//elimina todos los elementos del primer conjunto
s2Clone.retainAll(s1);
//la diferencia entre los dos conjuntos es uno si, y s�lo si, tras quitar
//los elementos del conjunto contrario, quedan los mismos elementos que
//ten�a al principio excepto uno
//obtiene los elementos extremos de ambos conjuntos
int m1Min = findMin(s1);
int m1Max = findMax(s1);
int m2Min = findMin(s2);
int m2Max = findMax(s2);
//comprueba que los extremos de ambos conjuntos s�lo se diferencian en una
//unidad
boolean extremeCond = Math.abs(m1Min-m2Min) == 1 && Math.abs(m1Max-m2Max) == 1;
//((m1Min==m2Min-1)&&(m1Max==m2Max-1)) || (m1Min==m2Min+1 && m1Max==m2Max+1));
return (s1Clone.size() == (s1.size() - 1)) &&
(s2Clone.size() == (s2.size() - 1)) && extremeCond;
}
/**
* <p> <b>Descripci�n:</b> mezcla dos mint�rminos uniendo los conjuntos de
* funciones de pertenencia asociados a una determinada variable.
* @param mt1 Primer mint�rmino que se fusionar�.
* @param mt2 Segundo mint�rmino que se fusionar�.
* @param var Variable cuyas funciones de pertenencia asociadas en los dos
* mint�rminos anteriores ser�n unidas en el nuevo mint�rmino.
* @return Devuelve un nuevo mint�rmino similar a los dos anteriores excepto
* para la variable indicada, en cuyo caso se le asignar� la uni�n de las
* funciones de pertenencia asociadas a dicha variable en los dos mint�rminos
* originales.
*
*/
private XfspMinterm merge(XfspMinterm mt1, XfspMinterm mt2, Variable var) {
//resultado de la fusi�n de ambos mint�rminos
XfspMinterm result = new XfspMinterm(false);
//map con las variables del primer mint�rmino y las funciones de
//pertenencia que dichas variables pueden tomar
Map var1 = (Map) ( (HashMap) mt1.getVariables()).clone();
//map con las variables del segundo mint�rmino y las funciones de
//pertenencia que dichas variables pueden tomar
Map var2 = (Map) ( (HashMap) mt2.getVariables()).clone();
//conjunto que contendr� las funciones de pertenencia que puede tomar la
//variable que se pasa como par�metros en el primer mint�rmino
Set s1 = null;
//iterador que permite recorrer todas las variables del primer mint�rmino
Iterator it = var1.keySet().iterator();
//a�ade a s1 las funciones de pertenencia que puede tomar la variable var
//en el primer mint�rmino
while (it.hasNext()) {
Variable aux = (Variable) it.next();
if (aux.equals(var.getName())) {
s1 = (Set) var1.get(aux);
}
}
//conjunto que contendr� las funciones de pertenencia que puede tomar la
//variable que se pasa como par�metros en el segundo mint�rmino
Set s2 = null;
//iterador que permite recorrer todas las variables del segundo mint�rmino
it = var2.keySet().iterator();
//a�ade a s2 las funciones de pertenencia que puede tomar la variable var
//en el segundo mint�rmino
boolean found = false;
while (it.hasNext() && !found) {
Variable aux = (Variable) it.next();
if (aux.equals(var.getName())) {
s2 = (Set) var2.get(aux);
found = true;
}
}
//establece la conclusi�n del mintermino resultado de fusionar los dos
//mint�rminos originales
result.setConclusion(mt1.getConclusion());
//establece las variables del resultado de la fusi�n y les da como
//asignaci�n inicial la misma que ten�an en el primer mint�rmino
result.setVariables( (Map) ( (HashMap) mt1.getVariables()).clone());
//conjunto que almacenar� las funciones de pertenencia que puede tomar la
//variable var tanto en el primer como en el segundo mint�rmino
Set s3 = new HashSet();
s3.addAll(s1);
s3.addAll(s2);
//iterador que permite recorrer las variables del resultado de la fusi�n
it = result.getVariables().keySet().iterator();
//busca en el resultado de la fusi�n...
found = false;
while (it.hasNext() && !found) {
Variable aux = (Variable) it.next();
//...hasta que encuentra la variable por la que se tienen que fusionar
//los dos mint�rminos...
if (aux.equals(var.getName())) {
//...en cuyo caso sustituye las funciones de pertenencia que puede
//tomar por la uni�n de las funciones de pertenencia que puede tomar en
//ambos mint�rminos
result.getVariables().put(aux, s3);
found = true;
}
}
//el resultado de la fusi�n cubrir� los mint�rminos que cubr�a el primero...
result.getCoveredMinterms().addAll(mt1.getCoveredMinterms());
//...as� como los que cubr�a el segundo
result.getCoveredMinterms().addAll(mt2.getCoveredMinterms());
/*
System.out.println("El resultado de la fusi�n de " + mt1 + " y " + mt2);
System.out.println("es " + result);
System.out.println("y cubre los mint�rminos:");
Iterator it1 = result.getCoveredMinterms().iterator();
while(it1.hasNext()){
XfspMinterm aux = (XfspMinterm) it1.next();
System.out.println(aux);
}
*/
//devuelve la fusi�n de ambos mint�rminos
return result;
}
/**
* <p> <b>Descripci�n:</b> busca el elemento m�nimo de un conjunto de enteros.
* @param s Conjunto no vac�o de objetos de tipo Integer.
* @return Menor elemento contenido en el conjunto s.
*
*/
private int findMin(Set s){
Iterator it = s.iterator();
int result = ((Integer) it.next()).intValue();
while(it.hasNext()){
int aux = ((Integer) it.next()).intValue();
if(aux<result){
result = aux;
}
}
return result;
}
/**
* <p> <b>Descripci�n:</b> busca el elemento m�ximo de un conjunto de enteros.
* @param s Conjunto no vac�o de objetos de tipo Integer.
* @return Mayor elemento contenido en el conjunto s.
*
*/
private int findMax(Set s){
Iterator it = s.iterator();
int result = ((Integer) it.next()).intValue();
while(it.hasNext()){
int aux = ((Integer) it.next()).intValue();
if(aux>result){
result = aux;
}
}
return result;
}
}