Showing posts with label Expert Systems. Show all posts
Showing posts with label Expert Systems. Show all posts

Saturday, September 11, 2010

F# Fuzzy0 Update

Just a quick note to say that the Fuzzy0 reference code has been updated. I added a few comments, a constant output function, and the input modifiers “very” and “somewhat.” These latter constrict and loosen the slope of fuzzification trapezoids or triangles in a manner similar to that in the following graphic:



More important in the long run, perhaps, is the reason I haven’t posted all week: I’ve been integrating the F# proof-of-concept code I discussed several months ago into the actual product I was writing it for! Since the product itself is still at the pre-sales stage, I can’t say much more about it until I clear it with the people who own it, but there it is: my first “real” F# project!

-Neil

Saturday, September 4, 2010

Discrete Classification using F# and Fuzzy Logic

This post shows the first example based on the F# fuzzy logic reference module Fuzzy0. I continue with the theme of tomatoes. The example shows how fuzzy logic can be used to classify items such as tomatoes into discrete categories.

One of the nice things about the model used in Fuzzy0 is that it is so generic it can be adapted to anything. The input and fuzzification is simply a function, and the output and defuzzification is just another function. Several useful examples are included as part of Fuzzy0 both for the input function and the functionality connecting the input to the output, but many others are possible.

Previous examples have used as output a defuzzification method which produced a single number. This post shows something different. The output is a weight for each of several categories. Rather than combining these weights, they are kept discrete and a category is chosen based on the highest weight.

In the example below, the input uses the (fast becoming traditional) tomato diameter and color, while the output is a series of categories representing the highest-value use. The example is small and fairly straightforward, but some things merit pointing out:

1) The rule set uses “conjoin” to combine multiple input fuzzifications based on the minimum.

2) The tomato types are given a desirability factor. This is a dimensionless quantity representing all the things that go into deciding the value of a tomato: market price, cost of production, current supply, etc.

3) “inConst” is used to produce a constant value of 1.0 for ketchup, since all tomatoes are usable in some processed product such as ketchup.

4) The color is determined by a simple ratio similar to that you could get from a resistance bridge and a couple of photocells. Likewise, the size could be estimated using very simple sensor circuitry.

5) I don’t know a darn thing about the tomato industry.

If you're like me, one of the first questions that comes up when looking at a fuzzy logic system like this is: "why not do it some traditional way, such as a decision tree or a set of differential equations?" Here's one reason: with a few minutes of explanation, a domain expert such as a tomato buyer could look at the fuzzy logic rules and understand what is going on well enough to judge their quality and even update them. Try that with a set of differential equations; even most engineers wouldn't like to spend time doing it that way.

Without further ado, other than to add that, as always: this is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk, here is the code:

open Fuzzy0
 
// Diameter of the tomato in inches.
let small  = inMin 1.0 2.0
let medium = inMid 1.0 2.0 3.0 4.0
let large  = inMax 3.0 4.0
 
// Color on a scale of green..red.
// If Red,Green are sensor values,
// could be computed by something like:
// color = (Red-Green)/(Red+Green+epsilon)
// ("epsilon" prevents division by zero.)
let green  = inMin -1.0 0.0
let yellow = inTri -1.0 0.0 1.0
let red    = inMax  0.0 1.0
 
/// Defines a tomato variety.
let variety label desirability  certainty =
  (label,desirability *certainty)
 
/// Rules to classify tomatoes.
// I just made these up.  
// In case it's still not obvious 
// by this blog post, I am clueless 
// about tomatoes.  
let tomatoRules = 
  [
    (conjoin[medium;green], (variety "canning"     0.7))
    (conjoin[large; green], (variety "fryer"       0.6))
    (conjoin[small; yellow],(variety "decorative"  1.0))
    (conjoin[medium;yellow],(variety "sandwich"    0.5))
    (conjoin[small; red],   (variety "cherry"      0.8))
    (conjoin[medium;red],   (variety "salad"       0.9))
    (conjoin[large; red],   (variety "ripe"        0.7))
    (inConst(1.0),          (variety "ketchup"     0.1))
  ]
 
/// Classify a tomato.
/// Returns all non-zero assignments.
/// In actual use, one might use List.max 
/// to return a single value.
let classified = 
  fireAll tomatoRules [3.7;-0.5] // [diameter;color]
  |> List.filter (fun (_,c)->c>0.0)
  |> List.sortBy (fun (_,c)->1.0-c)
 
// Output using the values above is as follows.
// (Note: rounding assumes at least two significant
// digits throughout the process.)
//
//   fryer, 0.3
//   canning, 0.21
//   sandwich, 0.15
//   ketchup, 0.1
 
printfn "Your breakpoint here."

Friday, September 3, 2010

Fuzzy Logic F# Reference Module: Fuzzy0

I want to post some more fuzzy logic examples, but first I want to devote a single post to containing the fuzzy logic core code. I could have done that using CodePlex or something, but that seemed too grandiose for such a small block of code. So I’ll place the code here, and if I make changes to it, I’ll post those changes here with a link back from the later blog post where I indicate the changes.

I’m calling this module “Fuzzy0” in anticipation of doing experiments using other models of fuzzy logic. As I’ve indicated, the techniques used in this module represent only a tiny fraction of the vast family of techniques in the domain of fuzzy engineering.

The code below is a cleanup and slight refactoring of the earlier examples. Of particular note, I have stuck with the paired height function/output function model, but have made it even more general. This makes it possible to add things like conjunction, disjunction, different defuzzification techniques, etc., by extending the code rather than changing it.

I’d also like to give a shout out to Alec Zorab, who posted a comment which helped me clean up the earlier code considerably and make it more readable.

Below is the fuzzy logic reference code for module Fuzzy0. Tomorrow I’ll post an example of its use that explores some extensions of earlier techniques. As always, all the code here is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.

(Note: updated 2010.09.11)

module Fuzzy0
 
// The input functions are trapezoids.
// (One is a triangle: a degenerate trapezoid.)
// The precomputations and closures
// make them look more complicated than
// they really are.
 
/// Infinite to the left.
let inMin x0 x1 =
  let m = 1.0/(x0-x1)
  let b = 1.0-x0*m 
  (function
    | x when x<=x0 -> 1.0
    | x when x<=x1 -> x*m+b
    | _ -> 0.0)
 
/// In the middle.
/// Simplification of the original
/// is courtesy of Alex Zorab.
/// (Ditto similar functions elsewhere.)
let inMid x0 x1 x2 x3 =
  let ml = 1.0/(x1-x0)
  let mh = 1.0/(x2-x3)
  let bl = 1.0-x1*ml
  let bh = 1.0-x2*mh
  (function
    | x when x<x0 -> 0.0
    | x when x<x1 -> x*ml+bl
    | x when x<=x2 -> 1.0
    | x when x<=x3 -> x*mh+bh
    | _ -> 0.0)
 
/// Simplified definition for triangles.
let inTri xl xc xh =
  let ml = 1.0/(xc-xl)
  let mh = 1.0/(xc-xh)
  let bl = 1.0-xc*ml
  let bh = 1.0-xc*mh
  (function 
    | x when x<xl -> 0.0
    | x when x<xc -> x*ml+bl
    | x when x<=xh -> x*mh+bh
    | _ -> 0.0)
 
/// Infinite to the right.
let inMax x0 x1 =
  let m = 1.0/(x1-x0)
  let b = 1.0-x1*m 
  (function 
    | x when x>=x1 -> 1.0
    | x when x>=x0 -> x*m+b
    | _ -> 0.0)
 
/// A constant height (infinite line).
let inConst h = (fun _->h)
 
// Modifier narrows towards the peak.
let very (f:float->float) = 
  (fun x ->
    let y = f x
    y*y)
 
// Modifier widens out from the peak.
let somewhat (f:float->float) = 
  (fun x ->
    let y = f x
    sqrt y)
 
/// Works like: 
/// List.map2 (fun f x->f x) fl al |> List.map min
/// but short-circuits on 0.0 for efficiency.
let conjoin =
  let rec f acc (fl:(float->float) list) (al:float list) =
    match fl with
    | [] -> acc
    | h::t ->
    match h al.Head with
    | 0.0 -> 0.0  // Short circuit.
    | n -> f (min n acc) t al.Tail 
  f 1.0
 
/// Output as a symmetric triangle.
/// This function returns the centroid and area.
let outSym xc dx h = 
  xc,dx*(2.0-h)*h   
 
/// Output as a trapzoid.
/// This function returns the centroid and area.
/// Can also be used to create 
/// asymmetric triangles.
let outTrap x0 x1 x2 x3 = 
  let x3x0 = x3+x0
  let dx1x0 = x1-x0
  let dx3x2 = x3-x2
  let dx3x0 = x3-x0
  (fun h ->
    let x0n = h*dx1x0+x0
    let x2n = h*dx3x2+x2
    let a = (dx3x0+x2n-x0n)*h/2.0
    // This is a quick approximation of
    // the centroid.  It can skew towards
    // the peak in some cases, which is OK.
    let c = (x3x0+x0n+x2n)/4.0
    c,a )
 
/// Output as a constant centroid 
/// and proportional area.
let outConst (c:float) (a:float) h = 
  c,a*h
 
 
/// Some simple, common functions
/// to help clean up the syntax.
 
/// Defuzzify by weighted centroid.
/// Note: will return NaN if all sets have zero area.
/// This is by design, since different implementations
/// may mandate different behavior in this situation.
let inline weightedCentroid xl =
  List.fold (fun (cc,aa)(c,a)->(cc+a*c,aa+a)) (0.0,0.0) xl
  ||> (/)
 
/// Maps a scalar onto list of functions.
/// Useful for multi-output rules.
let inline mapScalar xl h = 
  List.map (fun f->f h) xl
 
/// Fire a rule 1 to 1.
let inline fire x (inSet,outSet) =
  inSet x |> outSet
 
/// Fire a ruleset,
/// but don't defuzzify the result.
/// Allows for custom defuzzification.
let inline fireAll sets x = 
  List.map (fire x) sets 
 
/// Fire and defuzzify a ruleset.
/// Note: will return NaN if all sets have zero area.
/// This is by design, since different implementations
/// may mandate different behavior in this situation.
let inline fireAllDef sets x = 
  fireAll sets x |> weightedCentroid 

Monday, August 30, 2010

F#, Fuzzy Logic, WPF, and Tomatoes!

This is my 100th blog post, and to celebrate, I’m pulling out all the stops. This example will combine F#, fuzzy logic, WPF, and tomatoes!

The example below illustrates a simple fuzzy logic control simulator. In this case, what’s being controlled is the behavior of graphical tomatoes which “chase” the mouse cursor. The fuzzy inference system has two inputs: 1) the distance from the tomato to the cursor, and 2) the speed of the cursor. There is a single output: the speed at which a tomato should chase the cursor. To make things even more interesting, I’ve simulated tomatoes of three dispositions: timid, cautious, and aggressive. The green tomato, being green, is the timid one. The yellow tomato, like the traffic light of the same color, is cautious. The red tomato, like its active color, is aggressive.

Below is the application with the window reduced a bit to fit the blog. In real life, it’s more fun to run it full screen.



This is a WPF application, so it will require a number of additional steps when making it a Visual Studio project:

1) First, create a new F# project of type console and copy and paste the code below. (To make things more convenient, I made the entire project, fuzzy logic included, into one big file. If there are problems with carriage returns and line feeds, try pasting via an intermediate editor such as WordPad.)

2) Second, under project properties, set the application type to “Windows Application.”

3) Third, add the appropriate references. The references for VS2010 and .NET 4.0 are listed in the source code below. For other versions of Visual Studio and .NET, you can use the time honored trial and error technique of letting the compiler complain about the missing references and then adding them.

And that’s it! As always, all the code here is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.

// I have the following libraries referenced
// in the solution as 4.0 Client Profile.
// These may vary for other versions of .NET.
//
// Accessibility
// FSharp.Core
// mscorlib
// PresentationCore
// PresentationFramework
// System
// System.Core
// System.Numerics
// System.Xaml
// UIAutomationTypes
// WindowsBase
 
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media
 
// Here is the fuzzy logic subsystem.
 
// The input functions are trapezoids.
// The precomputations and closures
// make them look more complicated than
// they really are.
 
// Technically, since Min and Max are
// trapezoids with one side at infinity,
// a single function would suffice.
// But three functions are more efficient
// and comprehensible.
 
// Infinite to the left.
let inMin x0 x1 =
  let m = 1.0/(x0-x1)
  let b = 1.0-x0*m 
  (fun x ->  
     match x<=x0 with
     | true -> 1.0
     | _ -> 
     match x<=x1 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// In the middle.
let inMid x0 x1 x2 x3 =
  let ml = 1.0/(x1-x0)
  let mh = 1.0/(x2-x3)
  let bl = 1.0-x1*ml
  let bh = 1.0-x2*mh
  (fun x -> 
     match x<x0 with
     | true -> 0.0
     | _ ->
     match x<x1 with
     | true -> x*ml+bl
     | _ ->
     match x<=x2 with
     | true -> 1.0
     | _ ->
     match x<=x3 with
     | true -> x*mh+bh
     | _ -> 0.0 )
 
// Infinite to the right.
let inMax x0 x1 =
  let m = 1.0/(x1-x0)
  let b = 1.0-x1*m 
  (fun x ->  
     match x>=x1 with
     | true -> 1.0
     | _ -> 
     match x>=x0 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// The output set is a symmetric triangle.
// This function returns the area and centroid.
let outSym xc dx h = 
  dx*(2.0-h)*h,xc   
 
// Fire one rule.
let fire x (inSet,outSet) =
  x |> inSet |> outSet
 
// Fire a rule vector.
let fireV xl (inSets,outSet) =
  Seq.map2 (fun f x->f x) inSets xl 
  |> Seq.min
  |> outSet 
 
// Fire and defuzzify a ruleset.
let private fireAll0 f sets x =
  List.map (f x) sets 
  |> List.fold (fun (aa,cc)(a,c)->(aa+a,cc+a*c)) (0.0,0.0)
  |> (fun (aa,cc)->cc/aa)
 
// Scalar fire and defuzzify a ruleset.
let fireAll sets x = 
  fireAll0 fire sets x
 
// Vector fire and defuzzify a ruleset.
let fireAllV sets x = 
  fireAll0 fireV sets x
 
// Helper functions for this ruleset.
 
let estimate rules distance speed =
  (fireAllV rules [distance;speed])
 
let estimateXY rules dX sX dY sY =
  estimate rules dX sX,
  estimate rules dY sY
 
// Here are the rules.
// I got them about 90% of the way
// on the first shot just by thinking 
// about them, and about 10% by
// experimentation and tweaking.
// That's the power of fuzzy logic!
 
// Distance values in WPF units.
let adjacent = inMin 0.0 4.0
let near     = inMid 2.0 4.0 10.0 80.0
let far      = inMid 10.0 80.0 140.0 200.0
let wayOff   = inMax 140.0 200.0
 
// Cursor movement values in WPF units.
let still  = inMin 0.0 2.0
let medium = inMid 0.0 2.0 10.0 40.0
let fast   = inMax 10.0 40.0
 
// Tomato movement values in WPF units.
let hold  = outSym 0.0 1.0
let creep = outSym 1.0 1.0
let walk  = outSym 2.5 1.0
let run   = outSym 7.0 1.0
 
// Constant.
let setConst h = (fun _->h)
 
// Three basic behaviour types
// will be illustrated using 
// three "tomatoes" of
// varying disposition.
 
let rulesTimid = 
  [
    ([adjacent;still], hold);
    ([near;    still], creep);
    ([far;     still], walk);
    ([wayOff;  still], run);
 
    ([(setConst 1.0);medium],creep);
    ([(setConst 1.0);fast],hold);
  ]
 
let rulesCautious =
  [
    ([adjacent;still], hold);
    ([near;    still], walk);
    ([far;     still], walk);
    ([wayOff;  still], run);
 
    ([adjacent;medium],hold);
    ([near;    medium],walk);
    ([far;     medium],walk);
    ([wayOff;  medium],run);
 
    ([(setConst 1.0);fast],hold);
  ]
 
let rulesAggressive =
  [
    ([adjacent;still], hold);
    ([near;    still], walk);
    ([far;     still], run);
    ([wayOff;  still], run);
 
    ([adjacent;medium],hold);
    ([near;    medium],walk);
    ([far;     medium],walk);
    ([wayOff;  medium],run);
 
    ([adjacent;fast],walk);
    ([near;    fast],walk);
    ([far;     fast],run);
    ([wayOff;  fast],run);
  ]
 
 
// Tomato record.
type Tomato<'Rules> = 
  {
    Shape : Shapes.Ellipse; 
    Rules : 'Rules;
    // These store momentum.
    mutable Mx : float; 
    mutable My : float; 
  }
 
let makeTomato rules brush =
  let shape = new Shapes.Ellipse();
  shape.Fill <- brush
  { 
    Shape = shape; 
    Rules = rules;
    Mx = 0.0;
    My = 0.0;
  }
 
// Define the tomatoes.
// This could also be done in MainWindow;
// I do it here for ease of reading.
 
let tomatoes = 
  [
    makeTomato rulesTimid Brushes.PaleGreen;
    makeTomato rulesCautious Brushes.Yellow;
    makeTomato rulesAggressive Brushes.Tomato;
  ]
 
 
// Here is the user interface subsystem.
 
/// WPF main window.
type MainWindow (app: Application) =
  inherit Window()
 
  let canvas = System.Windows.Controls.Canvas() 
 
  /// Momentum factor.
  /// Lower = more dampening.
  /// This can be fun to play with.
  [<Literal>]
  let momentum = 0.7
 
  [<Literal>]
  let tomatoRadiusX = 10.0
  [<Literal>]
  let tomatoRadiusY = 10.0
 
  let mutable tomatoTargetLeft = 0.0
  let mutable tomatoTargetTop = 0.0
  let mutable tomatoTargetLeft0 = 0.0
  let mutable tomatoTargetTop0 = 0.0
 
  // This timer handles tomato movement.
  let timer = 
    new System.Windows.Threading.DispatcherTimer()
 
  /// Tomato movement timer callback.
  let moveTomato e =
    // Target speed.
    let sX = tomatoTargetLeft-tomatoTargetLeft0
    let sY = tomatoTargetTop-tomatoTargetTop0
    // Cache current target.
    tomatoTargetLeft0 <- tomatoTargetLeft
    tomatoTargetTop0 <- tomatoTargetTop
    for t in tomatoes do
      // Tomato position.
      let tX = Canvas.GetLeft t.Shape
      let tY = Canvas.GetTop t.Shape
      // Distance to target.
      let dX = tX-tomatoTargetLeft
      let dY = tY-tomatoTargetTop
      // Estimate absolute delta x,y.
      let x = estimate t.Rules (abs(dX))(abs(sX))
      let y = estimate t.Rules (abs(dY))(abs(sY))
      // Restore the sign.
      let x0 = (float (sign(dX)))*x+t.Mx
      let y0 = (float (sign(dY)))*y+t.My
      // Cache the new momentum.
      t.Mx <- x0*momentum
      t.My <- y0*momentum
      // Move the tomato.
      Canvas.SetLeft(t.Shape,tX-x0) 
      Canvas.SetTop(t.Shape,tY-y0)
 
  /// Mouse move callback.
  let onMouseMove (e:Input.MouseEventArgs)  =
    let p = e.GetPosition(canvas) 
    // Mouse position is the target
    // for the tomato center.
    tomatoTargetLeft <- p.X-tomatoRadiusX 
    tomatoTargetTop <- p.Y-tomatoRadiusY 
 
  /// Initialize all and sundry.
  member this.Initialize () =
    // Window setup.
    this.Title <- "Fuzzy Tracker Tomatoes"
    this.Width <- 400.0
    this.Height <- this.Width
    // Tomato setup.
    tomatoTargetLeft <- this.Width/2.0
    tomatoTargetTop <- this.Height/2.0
    tomatoTargetLeft0 <- tomatoTargetLeft
    tomatoTargetTop0 <- tomatoTargetTop
    for t in tomatoes do
      t.Shape.Width <- tomatoRadiusX*2.0
      t.Shape.Height <- tomatoRadiusY*2.0
      Canvas.SetLeft(t.Shape,tomatoTargetLeft)
      Canvas.SetTop(t.Shape,tomatoTargetTop)
      canvas.Children.Add t.Shape |> ignore
    // Canvas setup.
    canvas.Background <- Brushes.SlateGray
    this.AddChild canvas
    // Start interaction.
    this.MouseMove.Add onMouseMove
    timer.Tick.Add moveTomato
    timer.Interval <- new System.TimeSpan(0,0,0,0,50)
    timer.Start()
 
 
/// WPF application.
type App () =
  inherit Application()
 
  static member Go () =
    let app = new App()
    let win = new MainWindow(app)
    win.Initialize()
    app.Run(win) |> ignore
 
 
/// Main entry point.
/// Runs the application.
[<STAThread>] 
do
  App.Go()
 
 

Saturday, August 28, 2010

Conjunctive Fuzzy Logic Rules in F#

Today’s installment is just a small change from yesterday’s. It shows how to make multipart conjunctive rules by storing the input sets in a list and using the “min” operator to combine the results into a truncation height. To do this, it adds vector versions of the fire and fire all functions.

As a test, I continue the tomato theme, this time adding a second set of fuzzy sets to indicate whether the tomatoes are green or red. Green tomatoes, in Neil’s tomato world, are generally only held to be superior to red tomatoes when they are small frying tomatoes. I ran some test numbers, and the graph is shown below:



So what good is this? Would anyone ever build such a system? Probably not; I’m sure there are better ways to sort tomatoes. But it illustrates one of the general classes of problems for which fuzzy logic is appropriate. So just for fun, let’s imagine how one might us such a system in the real world:

Neil is a tomato buyer for a local food co-op. He buys tomatoes from a large number of small growers, all of whom produce crops of mixed tomatoes, which are harvested sporadically throughout the growing season. The problem is: how to make sure each buyer gets a fair proportion of the return without increasing the overhead too much? Neil notices a bunch of spare parts in his workshop that may help. Using these, he quickly rigs up a small, simple conveyor system – small enough to fit on a pickup truck – for grading the tomatoes. Each tomato rolls over a thin window in the conveyor, where the diminution of light is used to estimate the size of the tomato. It also rolls past two phototransistors, one with a green filter and one with a red, which estimate its color. This information is fed to Neil’s laptop, where the fuzzy logic system defined below is used to estimate the value of the crop. Since the rules are so simple, every couple of weeks, based on discussions with the co-op, the fuzzy logic rules are adjusted to account for the current price of tomatoes. So there it is: all that tomato wisdom, distilled by fuzzy logic into a ketchup of happy farmers, grocers, and customers!

And below is the code. (As always, all the code here is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)

module Fuzzy
 
// The input functions are trapezoids.
// The precomputations and closures
// make them look more complicated than
// they really are.
 
// Technically, since Min and Max are
// trapezoids with one side at infinity,
// a single function would suffice.
// But three functions are more efficient
// and comprehensible.
 
// Infinite to the left.
let inMin x0 x1 =
  let m = 1.0/(x0-x1)
  let b = 1.0-x0*m 
  (fun x ->  
     match x<=x0 with
     | true -> 1.0
     | _ -> 
     match x<=x1 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// In the middle.
let inMid x0 x1 x2 x3 =
  let ml = 1.0/(x1-x0)
  let mh = 1.0/(x2-x3)
  let bl = 1.0-x1*ml
  let bh = 1.0-x2*mh
  (fun x -> 
     match x<x0 with
     | true -> 0.0
     | _ ->
     match x<x1 with
     | true -> x*ml+bl
     | _ ->
     match x<=x2 with
     | true -> 1.0
     | _ ->
     match x<=x3 with
     | true -> x*mh+bh
     | _ -> 0.0 )
 
// Infinite to the right.
let inMax x0 x1 =
  let m = 1.0/(x1-x0)
  let b = 1.0-x1*m 
  (fun x ->  
     match x>=x1 with
     | true -> 1.0
     | _ -> 
     match x>=x0 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// The output set is a symmetric triangle.
// This function returns the area and centroid.
let outSym xc dx h = 
  dx*(2.0-h)*h,xc   
 
// Fire one rule.
let fire x (inSet,outSet) =
  x |> inSet |> outSet
 
// Fire a rule vector.
let fireV xl (inSets,outSet) =
  Seq.map2 (fun f x->f x) inSets xl 
  |> Seq.min
  |> outSet 
 
// Fire and defuzzify a ruleset.
let private fireAll0 f sets x =
  List.map (f x) sets 
  |> List.fold (fun (aa,cc)(a,c)->(aa+a,cc+a*c)) (0.0,0.0)
  |> (fun (aa,cc)->cc/aa)
 
// Scalar fire and defuzzify a ruleset.
let fireAll sets x = 
  fireAll0 fire sets x
 
// Vector fire and defuzzify a ruleset.
let fireAllV sets x = 
  fireAll0 fireV sets x
 
// Here's the now familiar tomato problem.
// By happy coincidence, all the input sets 
// can be coded as trapezoids and the output
// sets as symmetric triangles.
 
let tiny   = inMin 1.0 2.0 
let small  = inMid 1.0 2.0 2.0 3.0
let medium = inMid 2.0 3.0 3.0 4.0
let large  = inMax 3.0 5.0
 
let green  = inMin 0.0 1.0 
let red    = inMax 0.0 1.0 
 
let isGreen = 0.0
let isRed   = 1.0
 
let cheap      = outSym 1.00 0.50
let moderate   = outSym 1.50 0.50
let expensive  = outSym 2.00 0.50
let outrageous = outSym 4.00 2.00
 
let rules =
  [
    ([tiny;green],cheap);
    ([small;green],expensive);
    ([medium;green],moderate);
    ([large;green],cheap);
 
    ([tiny;red],outrageous);
    ([small;red],cheap);
    ([medium;red],expensive);
    ([large;red],moderate);
  ]
 
// Inspection will show that the results
// are identical to the "tzoid" version.
 
for size in 0.25..0.25..5.00 do
  printfn "%f, %f, %f" 
    size 
    (fireAllV rules [size;isGreen])
    (fireAllV rules [size;isRed])
 
printf "Your breakpoint here."
 

Friday, August 27, 2010

Fuzzy Logic in F#, Now More Functional!

Before moving forward with some fuzzy logic examples, I decided to take a step back in complexity. I decided that my “tzoid” approach was overkill for 80% of the cool stuff you can do with fuzzy logic. So I came up with something simpler and more functional. But first, a bit more background on fuzzy inference and what I’m trying to achieve.

In its most basic form, fuzzy inference uses at least one pair of fuzzy sets to map a crisp input value onto a new crisp output value. There are a number of ways to do this, but the most common is to take the height of the input set at the input value as x, use that height to truncate the output set, and then “defuzzify” that output set to a crisp value. (Different defuzzification methods work better depending on the domain, but using the centroid is one common method.)

In pseudo-F#, and graphically, this process might look like:

inputValue |> (heightAt inputSet) |> (truncate outputSet) |> defuzzify



The former is for a single rule. However, most applications require a rule set of a number of rules. The process can be extended by mapping and folding, and then applying some defuzzification function such as a weighted average. Again in pseudo-F#, and graphically, this extended process might look like:

inputValue
|> map ( (heightAt inputSet) >> (truncate outputSet)) ruleset
|> fold defuzzify



And lastly, the input value and input set might be extended to vectors of paired values, each handling a different type of input (e.g. speed, height, acceleration, etc.). These can be combined by some simple method, for example, taking the minimum height for conjunctive rules, the maximum height for disjunctive rules, etc.



So what is the minimal useful implementation of this? Here are some ideas:

1) To start with, I’ll assume a single input set rather than a vector. This will simplify things, and the resulting code will be easy to extend to vectors.

2) Any input set can be described as a simple trapezoid. (Perhaps with an infinite extension on one axis or another.)

3) Any output set can be described as a symmetric triangle. (i.e. an isosceles triangle with its base on the x-axis.)

4) All the sets have a maximum membership value of one.

5) The defuzzification function is a weighted average of truncated output triangle area and centroid.

The code below demonstrates an implementation of this using the tomato price example from an earlier post. Since all the input sets in the original problem were describable as trapezoids and all the output sets were describable as symmetric triangles, the result is identical. Except, of course, there is a lot less source code this time around. (As always, all the code here is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)

So what is the “take away”?

It’s not that fuzzy logic is some magic method which yields results not achievable by any other method. The truth is, most problems solvable by fuzzy logic are solvable by other means, such as regression, differential equations, numeric modeling, or other types of rule-based systems. And sometimes, if they are made complex enough or are based on enough data, these other methods can yield more exact results.

What the take away is, is this: fuzzy logic is a conceptually compact and computationally efficient means for transferring imprecise, high-level solutions into precise computer logic.

Very simple, in fact so simple that:

1) Fuzzy logic can be made cheap enough for consumer appliances like microwave ovens, washing machines, and vacuum cleaners.

2) Fuzzy logic can be made reasonably cheap and fast. Fast enough to hundreds or even thousands of inferences per second for things like video cameras and machine control.

3) Fuzzy logic can be made reasonably cheap and parallel. For example, image enhancement for a digital camera by processing entire blocks of pixels. And fuzzy logic is simple enough that it can be implemented on other-purpose parallel hardware – such as graphics adapters – without too much effort. It’s not difficult to imagine an F# implementation of some fuzzy logic system which uses a domain specific language (DSL) and/or F# quotations to interface using CUDA or even High Level Shader Language (HLSL).

module Fuzzy
 
// Type abbreviations.
type InFunc = float->float
type OutFunc = float->(float*float)
 
// The input functions are trapezoids.
// The precomputations and closures
// make them look more complicated than
// they really are.
 
// Technically, since Min and Max are
// trapezoids with one side at infinity,
// a single function would suffice.
// But three functions are more efficient
// and comprehensible.
 
// Infinite to the left.
let inMin x0 x1 =
  let m = 1.0/(x0-x1)
  let b = 1.0-x0*m 
  (fun x ->  
     match x<=x0 with
     | true -> 1.0
     | _ -> 
     match x<=x1 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// In the middle.
let inMid x0 x1 x2 x3 =
  let ml = 1.0/(x1-x0)
  let mh = 1.0/(x2-x3)
  let bl = 1.0-x1*ml
  let bh = 1.0-x2*mh
  (fun x -> 
     match x<x0 with
     | true -> 0.0
     | _ ->
     match x<x1 with
     | true -> x*ml+bl
     | _ ->
     match x<=x2 with
     | true -> 1.0
     | _ ->
     match x<=x3 with
     | true -> x*mh+bh
     | _ -> 0.0 )
 
// Infinite to the right.
let inMax x0 x1 =
  let m = 1.0/(x1-x0)
  let b = 1.0-x1*m 
  (fun x ->  
     match x>=x1 with
     | true -> 1.0
     | _ -> 
     match x>=x0 with
     | true -> x*m+b
     | _ -> 0.0 )
 
// The output function is a symmetric triangle.
// This function produces the area and centroid.
let outSym xc dx h = 
  dx*(2.0-h)*h,xc   
 
// Fire one rule.
let fire x (inSet:InFunc,outSet:OutFunc) =
  x |> inSet |> outSet
 
// Fire and defuzzify a ruleset.
let fireAll sets x =
  List.map (fire x) sets 
  |> List.fold (fun (aa,cc)(a,c)->(aa+a,cc+a*c)) (0.0,0.0)
  |> (fun (aa,cc)->cc/aa)
 
 
// Here's the now familiar tomato problem.
// By happy coincidence, all the input sets 
// can be coded as trapezoids and the output
// sets as symmetric triangles.
 
let tiny   = inMin 1.0 2.0 
let small  = inMid 1.0 2.0 2.0 3.0
let medium = inMid 2.0 3.0 3.0 4.0
let large  = inMax 3.0 5.0
 
let cheap      = outSym 1.00 0.50
let moderate   = outSym 1.50 0.50
let expensive  = outSym 2.00 0.50
let outrageous = outSym 4.00 2.00
 
let rules =
  [
    (tiny,outrageous);
    (small,cheap);
    (medium,expensive);
    (large,moderate);
  ]
 
// Inspection will show that the results
// are identical to the "tzoid" version.
 
for size in 0.25..0.25..5.00 do
  printfn "%f, %f" size (fireAll rules size)
 
printf "Your breakpoint here."