Fuzzy Logic y expresiones lambda

Para un proyecto que estoy realizando, he realizado una prueba de concepto probado a aplicar lógica difusa con el fin de realizar ciertas acciones a partir de una serie de datos imprecisos.


A la hora de expresar las reglas encargadas de modelar nuestro sistema de lógica difusa, (IF-THEN) la mayoría de los sistemas que he visto requerían de un pequeño y simple analizador sintáctico para interpretar las reglas, en este punto me he preguntado si en vez de un analizador sintáctico, podríamos usar expresiones lambda y una interfaz fluida.

FuzzySystem (improvisado)

De modo que he construido un pequeño motor de lógica difusa que tiene la particularidad de usar expresiones lambda y una interfaz fluida para definir el conjunto de reglas.
Este pequeño motor incorpora un par de funciones miembro, una de tipo Trapezoide y otra Triangular, básicamente su uso es el siguiente.
Definimos el sistema, añadimos las variables con sus estados y funciones miembro…

01 public class FuzzyTest
02 {
03     private readonly FuzzySystem _engine = new FuzzySystem();
04  
05     public FuzzyTest()
06     {
07         FuzzyVariable varA = new FuzzyVariable("A");
08         varA.Memberships.Add(new TrapezoidMembership("Cerrado", 0, 0, 5, 5));
09         varA.Memberships.Add(new TrapezoidMembership("Medio", 4, 4, 6, 6));
10         varA.Memberships.Add(new TrapezoidMembership("Abierto", 7, 7, 9, 9));
11         _engine.Variables.Add(varA);
12  
13         FuzzyVariable varB = new FuzzyVariable("B");
14         varB.Memberships.Add(new TrapezoidMembership("Cerrado", 0, 0, 5, 5));
15         varB.Memberships.Add(new TrapezoidMembership("Medio", 4, 4, 6, 6));
16         varB.Memberships.Add(new TrapezoidMembership("Abierto", 7, 7, 9, 9));
17         _engine.Variables.Add(varB);
18  
19         FuzzyVariable varR = new FuzzyVariable("R");
20         varR.Memberships.Add(new TrapezoidMembership("Frio", 0, 0, 3, 3));
21         varR.Memberships.Add(new TrapezoidMembership("Templado", 3, 3, 6, 6));
22         varR.Memberships.Add(new TrapezoidMembership("Caliente", 7, 7, 9, 9));
23         _engine.Variables.Add(varR);

 

La definición de reglas mediante expresiones Lambda

El beneficio en este punto es que el sistema no requiere del analizador sintáctico para evaluar las expresiones si no que será el CLR quien se encargue de ello.
Para llevar a cabo esta evaluación de las reglas he definido una clase FuzzyExpresion que tiene definidos  los operadores && y || para así como el true y el false para poder evaluar las reglas en consecuencia.



Las reglas en la lógica difusa sirven para combinar las distintas proposiciones, no son reglas excesivamente complicadas, y son del tipo IF-THEN, básicamente hay 4 reglas (Implicación Conjunción, Disyunción  y negación) que se corresponden con:

-    Implicación IF variable==estado THEN variable = estado
-    Conjunción, equivalente al AND, si dos proposiciones son ciertas simultáneamente
-    Disyunción, cualquiera de las dos proposiciones es cierta OR
-    Negación, invierte la proposición

Finalmente podemos definir las reglas de este modo

01 _engine.AddRule(x =>
02                 x.Variable("A").Is("Abierto") &&
03                 x.Variable("B").Is("Abierto"))
04                 .Then(x => x.Variable("R").Set("Caliente"));
05  
06 _engine.AddRule(x =>
07                 x.Variable("A").Is("Abierto") &&
08                 (x.Variable("B").Is("Cerrado") || x.Variable("B").Is("Medio")))
09                 .Then(x => x.Variable("R").Set("Templado"));
10  
11 _engine.AddRule(x =>
12                 x.Variable("B").Is("Abierto") &&
13                 (x.Variable("A").Is("Cerrado") || x.Variable("A").Is("Medio")))
14                 .Then(x => x.Variable("R").Set("Templado"));
15  
16 _engine.AddRule(x =>
17                 x.Variable("A").Is("Cerrado") &&
18                 x.Variable("B").Is("Cerrado"))
19                 .Then(x => x.Variable("R").Set("Frio"));

¿Cómo funciona?

Una regla tiene una condición (IF) y una consecuencia (THEN), la condición es una función que recibe el sistema y debe devolver una FuzzyExpresion, (Func<FuzzySystem,FuzzyExpression>), esto es parte de la magia ya que una expresión condicional es siempre reducida a una única expresión y finalmente a un único valor.
Y curiosamente la consecuencia de la regla, es exactamente igual, solo que en este caso la expresión no devuelve nada porque la consecuencia es una acción, aquí hay otro pequeño truco que es que la propia regla contiene los dos elementos separados (condición y consecuencia) fijaros en que:

1 _engine.AddRule(x =>
2                      x.Variable("A").Is("Abierto") &&
3                      x.Variable("B").Is("Abierto"))

Está devolviendo una regla y el método Then es aplicado sobre la regla, y cuando se evalúa la regla si la condición es cierta se evalúa la consecuencia, que lo que hace es únicamente cambiar el estado (ver el método Set de FuzzyExpression)

01 public class FuzzyRule
02 {
03     private readonly FuzzySystem _engine;
04     private readonly Func _condition;
05     private Func _then;
06  
07  
08     public double Value { get; set; }
09  
10     public FuzzySystem Engine
11     {
12         get { return _engine; }
13     }
14  
15     public FuzzyRule(FuzzySystem engine, Func condition)
16     {
17         _engine = engine;
18         _condition = condition;
19     }
20  
21     public FuzzyExpression Eval()
22     {
23         Value = _condition(Engine).Value;
24  
25         if (Value > 0)
26         {
27             return _then(Engine);
28         }
29  
30         return null;
31     }
32  
33     public void Then(Func then)
34     {
35         _then = then;
36     }    
37 }


Defuzzyficando

Para evaluar nuestro sistema ..

 

01     for (int a = 0; a < 10; a++)
02     {
03         for (int b = 0; b < 10; b++)
04         {
05             _engine.Variables["A"].InputValue = a;
06             _engine.Variables["B"].InputValue = b;
07             _engine.Consecuent = "R";
08  
09             var r = _engine.Defuzzy();
10  
11             Console.WriteLine(string.Format("A {0} - B {1} = {2,-6} [{3,-10}][{4,-10}] = {5}",
12                 a,
13                 b,
14                 r,
15                 _engine.GetVariableState("A",a),
16                 _engine.GetVariableState("B",b),
17                 _engine.GetVariableState("R", r)));
18         }
19     }
20 }

Mejoras de diseño

Hay una cosa que no me gusta mucho y es el hecho de que mi clase FuzzyExpression, tiene tanto el método Is como el método Set y esto habría que dividirlo en dos para no llevar a errores a la hora de la usar la interfaz fluida.

Errores de diseño

Pero no todo es de color de rosa, cuando planifique el sistema, pensé en la posibilidad de guardar las expresiones lambda como texto y después compilarlas usando CodeDom, bien tengo que deciros que cuando me he puesto a ello me he llevado la desagradable sorpresa de que CodeDom no soporta expresiones lambda, de modo que mi gozo en un pozo.

Y digo esto porque es de vital importancia el poder mantener las reglas fuera del código, bien un archivo de configuración, un archivo XML, lo que sea… de este modo podemos realizar ajustes sin tener que recompilar todo …

Para una futura versión

Completar estas cositas …

Published 17/1/2010 22:24 por Carlos Segura
Archivado en:
Comparte este post:

Comentarios

# re: Fuzzy Logic y expresiones lambda

Sunday, January 17, 2010 11:13 PM por Octavio Hernández

Carlos,

¡Excelente post!

Ideas que me rondan la cabeza:

a) Sin temor alguno a equivocarme, creo que esto tiene todos los merecimientos para aparecer en alguna publicación impresa, como dotNetManía. ¿Por qué no lo amplías un poco y lo envías?

b) A CodeDom, de todos modos, no le queda mucho tiempo como principal (o único) mecanismo para generar código "on the fly". Te recomendaría echar un vistazo a las novedades de los árboles de expresiones en C# 4...

Abrazo - Octavio

# re: Fuzzy Logic y expresiones lambda

Monday, January 18, 2010 12:05 AM por Rodrigo Corral

El miércoles cenando que no se nos pase hablar largo y tendido sobre este tema... excelente post.

Secundo la moción de Octavio... ¡artículo y papel!

# re: Fuzzy Logic y expresiones lambda

Monday, January 18, 2010 10:41 AM por Carlos Segura

@Octavio, gracias por el comentario :-) en cuanto a las ideas, ya estoy mirando la pasobilidad de serializar las expresiones, a ver si lo desarollo un poco más y te lo paso.

@Rodrigo, ok, hablaremos de ello.

he tenido problemas pegando el código ya te contaré. podeís verlo tambien en (www.ideseg.com/.../Fuzzy-Logic-y-expresiones-lambda.aspx) en cuanto pueda dejaré aquí todo el código.

carlos.

# re: Fuzzy Logic y expresiones lambda

Thursday, January 21, 2010 9:19 AM por Crowley

Hola, me ha gustado mucho tu post. Yo llevo un tiempo enredando con XNA y con logica difusa para acciones simples de IA.

No tengo mucha idea de expresiones lambda, asi que puede que este preguntando una estupidez, pero ¿no seria posible definir reglas en XML como si fuesen nodos de un arbol de expresion (body, parameters, operation left, etc) y luego leer esa info, contruir un arbol de expresion y ejecutarlo? Si es posible, la cosa seria incluso mas potente, puesto que podrias definir nodos con expresiones simples y luego juntarlas en un arbol de expresion que sea una regla compleja formada por varias expresiones simples.

Gracias por tu post.

# re: Fuzzy Logic y expresiones lambda

Thursday, January 21, 2010 4:54 PM por Carlos Segura

@Crowley

Me alegro. Efectivamente ese es el cámino, al igual que apunta el gran Octavio, lo que pasa es que habría que hay que hacerlo con los árboles de expresiones del framework 4.0.

En cuanto saque un poco de tiempo.... le doy una vuelta.

carlos.