Introduction

Some 15 or more years ago, I was involved in a project (Brulé, et. al., 1995) that needed an expert system to choose a suitable option based on some basic parameters. Several approaches were tried, including the use of predicate calculus (i.e., Prolog). Essentially, none of the approaches worked very well. Finally, I interviewed several human experts in the subject. I would ask what choice they would make given a set of parameters, and they invariably would answer with something like "If X is Y, then I would use A, but if X is Z, then I'd use B" -- where X is a parameter (i.e., water depth), Y and Z are qualifiers (deep), and A and B are options that can be chosen. A little reflection, and it suddenly became apparent that they were describing a fuzzy system. As a result, I ended up coding a simple fuzzy logic based expert system, and solved the problem satisfactorily.

More recently, I decided to dive into F#. As a novice in functional programming, I thought that some of the features of F# might be a good match for a simple expert system, similar to what I developed in the last century. My initial observations on F# and writing a Windows Forms application are in a previous article, Getting Started in F# - A Windows Forms Application[^].

In this article, I'll present FuzzyAdvisor, a simple fuzzy logic based expert system useful for making choices based on simple parameter estimates. The download contains three Visual Studio 2008 projects:

FuzzyAdvisor - the core library (*.dll) written in F# that implements the advisor system.

FuzzyTest - a C# project that uses FuzzyAdvisor as an example of combining languages.

Of course, I'll add my usual disclaimer: I'm still a beginner at F#, so I don't make any claims that the code is elegant, efficient, etc. It does work and, as always, any comments on the coding style or alternate F# techniques will be appreciated.

Description of the FuzzyAdvisor System

In the oil business, as well as other industries, it is often necessary to make preliminary estimates of costs before there is a lot of data available. In the development of offshore oil fields, the cost of an offshore platform or other type of facilities design has extremely important implications on costs, lead time before installation, drilling constraints, environmental risks, etc. Due to these concerns, it is very important that a reasonable choice of facility is selected at the outset of a project, long before any other meaningful data is available. The example discussed here will use some rudimentary data to make an educated guess at the best type or types of offshore oil facilities. It should be noted that the early version of this Fuzzy Advisor system was successfully used in many other similar decision processes, including the selection of gas compressors, pumps, and refinery equipment.

You can also be assured that I do not know the exact rules that should be used, so the information presented here is contrived, but reasonable. In other words, don't think you can apply these rules to making real world decisions! You'll need to figure out your own rules, or hire an expert to help with that.

The overall design of FuzzyAdvisor is to process fuzzy rules of the form:

if <parameter> is <quantifier> then <option> <weight>

For example:

if Water Depth is VeryDeep then SubseaCompletions (0.9)

In the example, notice that Water Depth represents a data parameter, VeryDeep describes a fuzzy set, SubseaCompletions is an option that might be selected, and 0.9 is a weighting factor that describes how important the rule is.

Notice also that the statement can be easily read in plain English, even by experts who know nothing about fuzzy logic or programming, yet it can be translated to fuzzy logic operations in a very straightforward manner. This form of statement grammar is extremely important, since the subject experts can easily read the rules and decide whether or not they make sense. Such considerations are often of paramount importance in gaining acceptance of the system and ensuring that the subject experts are not alienated in the decision making process.

The entire system then consists of a list of rules, a list of parameter values, a list of fuzzy sets, and a list of choices. Once the parameters have been specified, the membership in each fuzzy set is evaluated, and the adjusted weight is added to the choice specified in each rule. When all of the rules have been evaluated, the choices are sorted in decreasing order of their value and presented to the user for consideration.

It is also important to note that in the "real world", there quite often is no "right answer", because everything involves tradeoffs in terms of time, resources, cost, and other considerations. FuzzyAdvisor recognizes this by ranking the options and letting the user choose the best one based on any other subjective information they might have. On the other hand, FuzzyAdvisor, with a properly defined set of options and sufficiently well calibrated rules, will indicate any options that are either totally impractical or head and shoulders above other choices.

In implementing a FuzzyAdvisor system, it is necessary to define the rules, determine the fuzzy sets, and specify the weighting factors for each rule. Although I won't go into the details of that process, the preliminary definitions are normally determined by interviewing subject matter experts. Once the preliminary rules, fuzzy sets, and weights are determined, the system is implemented and reviewed once again by the experts. When they note a bad decision, it is then necessary to figure out whether to adjust a fuzzy set or a weight, or perhaps add a different rule. Actually, doing that is beyond the scope of this article.

FuzzyAdvisor Grammar

The FuzzyAdvisor system reads parameters, fuzzy set definitions, and fuzzy rules in plain text format using a very simple grammar. There are three types of statements in the grammar:

Parameters: VAR <varname> of <context> = <value>

Fuzzy Sets: FSET <fsetname> of <context> <membership list>

Fuzzy Rules: IF <varname> of <context> IS <fsetname> THEN <option> <weight>

Note that the context of a parameter and a fuzzy set must match in order to distinguish between parameters with similar or identical names, but different meanings; for example, Depth of Water, as opposed to Depth of a Well. In addition, for convenience, any of the <name> of <context> constructs can be replaced with <context> <name> for flexibility. In other words, we can equivalently write Depth of Water or Water Depth.

The FuzzyAdvisor is implemented in F# using four types. The complete code is in the project files, but I'll mention some of the interesting points.

First, since the various types (classes in C#) reference each other, in F#, they must be defined together. The first type is preceded by the type keyword, but the following ones use the and keyword instead, as shown in the following code snippet:

Note also that I've overridden the normal ToString() method for each of the objects. This allows me to add the objects to a list box and have meaningful names displayed.

To perform the text parsing, I chose to use a brute force method, due to the simplicity of the grammar. In F#, this is easily accomplished with list processing and pattern matching. The following snippet shows part of the parser. Note that a line is split into a list of words separated by spaces, then pattern matching is used to determine what type of statement it represents based on the first word. Finally, pattern matching is again used to extract the various <name><context> items and any associated values. The FuzzyAdvisorEngine contains functions to read and parse text from either strings or a text file. During the parsing, lists of variables, Fuzzy sets, and Fuzzy rules are accumulated. Valid options are also determined from the rules and saved in a list of choices.

FuzzyWorkshop

The FuzzyWorkshop application is a Windows Forms application written completely in F# that allows testing the FuzzyAdvisor system. The TabControl on the main form contains pages for text, items (Fuzzy sets and parameters), rules, and results. Text can be saved to and read from a text file using the menu options. Pressing the Parse button will attempt to parse the text and determine the Fuzzy sets, variables, Fuzzy rules and choices. Once parsed, double clicking on a Fuzzy set will display a graph of the set membership to help in troubleshooting. On the Results tab, pressing the Calculate button will process the rules and show the weighted ranks of all the possible choices.

Most of the FuzzyWorkshop code is straightforward, but there are several interesting parts. First, the TabControl is added to the main form as follows, with the required buttons, list boxes, etc., added as controls on the individual TabPages. As I mentioned in the previous article, all of this must be done manually, since there are no form designers available for F# (yet?).

In addition, the FuzzyGraph is implemented as a user control. The details are in the source files, but basically a FSharpGraph type is defined that inherits from a .NET UserControl. Members are defined to react to mouse movements, to add data to the graph, etc. All of the graphics is programmed using basic GDI methods. Once defined, the FSharpGraph component is added to a regular form, which is loaded on the MouseDoubleClick event for the FuzzySet listbox. Portions of the applicable code are shown below:

Note that the last line of code above assigns a function to the GraphMouseMove member in order to trap the mouse location parameters and display them on the form. This is similar to the use of a delegate in C#.

Additionally, the following code is used to populate the list boxes and other displays after parsing a text file. To me, it's amazing what can be done with a single line of code and F#'s internal list processing functions, such as List.iter and List.rev. Note that the lists are reversed using List.rev only so that the displays show up in the same order as the text file declarations. Using the lstVariables line as an example, the F# code basically says to iterate over a list of variables, adding each one to the listbox and discarding the result (an integer). The list to iterate over is fuzzyEngine.FVars after it has been reversed using (List.rev fuzzyEngine.FVars).

FuzzyTest

The FuzzyTest application is a simple Windows Forms application written in C# that accesses the FuzzyAdvisor written in F#. The C# source code used to access the FuzzyAdvisor system is shown below. Note that the FuzzyAdvisorEngine is created, a text file is selected using a standard .NET FileOpen dialog, and the engine reads and parses the file and then calculates the choices. In the sample, the ranked choices are simply shown using a MessageBox, but of course, they could be presented in an alternate form, or program actions could be determined based on the rankings. Note that the F# tuples used to define choices are accessed from C# using the Microsoft.FSharp.Core.Tuple<string,> generic class.

To create the FuzzyTest project, a C# project was first created, then an existing project was added to the solution. Choosing the FuzzyAdvisor project adds it to the solution easily enough. It is also necessary to add the F# references FSharp.Core and FuzzyAdvisor to the C# project, since Visual Studio will not automatically recognize those that are required. Once the projects are created, they can be built and tested normally, and debugging can even step through both the C# and the F# code.

Conclusions

While the FuzzyAdvisor system presented here is fairly simple, it provides an example of a complete F# program, and shows how classes written in F# can be used in C# or other .NET languages. The system can be extended to incorporate more complex fuzzy logic, include hedges, and allow ranges of results when some of the variables are not precisely known. In addition, rather than simply presenting the choices, it would be fairly straightforward to apply an additional defuzzification step and then use the results to automatically perform other actions. Such techniques have been tested and found to work for some automated control systems, but have not been implemented in this example.

After learning to use F# and figuring out how to make components, forms, libraries, and how to use F# code along with C#, it seems to me that the optimum use of F# for the short term is to handle processes which mainly involve non-graphical and non-user input. Partly due to the availability of the form designers, other languages such as C# seem much better for user interfaces.

On the other hand, the ability to simply do recursive programming, define generic first-class functions, and process lists makes F# ideal for some tasks. In particular, once we get used to the syntax, things which are somewhat complex iterations in other languages suddenly become elegant one liners in F#.

Finally, I still recommend that anyone with even the slightest curiosity spend some time and learn how to use F#. It never hurts to be able to add another tool to one's programming toolbox, nor is it a disadvantage to be able to look at a problem from a different viewpoint. Personally, I'm sure I'll find uses for F# in the future, combining it with other programming languages.

Share

About the Author

Walt has been playing with software since around 1967 and has generated more runtime errors than the average village idiot. He is a CEO, Petroleum Engineer, software consultant, janitor, and now a graduate student again. Rather than sleep, he also plays with algorithms and systems for technical computing, develops software for engineering evaluations and is an avid amateur radio operator.

Walt was admitted back to UT Austin and is actually attempting to complete a PhD in engineering, thereby proving that he is crazier than the average old fart.

And now UT has gone and admitted Walt to PhD candidacy,now my disertation has been submitted and it is underreview, has been reviewed,so I'm preparing for my defense to I can wrtap things up and graduate. proving that old guys can still ... what was he doing again?

Comments and Discussions

Dear Walt,
I am looking at using your tools to develop a rules engine for government decision making.
Some of the rules will be fuzzy and some not.
Can you give us an example of your system and also perhaps and example where you include non fuzzy rules. I would like to see how you mix fuzzy and non-fuzzy rules, but I guess its straight forward.

Thanks very much, your stuff looks really good. I would like to use a system that can handle fuzzy rules rather than a standard rules engine!

I'm happy to hear you found the code interesting. I hope it helps with your problem.

Actually I haven't formally combined fuzzy and non-fuzzy logic rules. When I did need to include a non-fuzzy rule, I simply defined a fuzzy set as a step function, so that the membership is either 0 or 1. I'm not sure that is feasible for your case.

First of all, sorry if I don't speak correctly, I am not english speaker and I learned about fuzzy in germany.
Second... I am not speaking about the programm of this article, just about the fuzzy theory.

If the boolean expresion are in the input side, the fuzzy logic has the possibility of "Single tons" attributes. These are completely vertical lines, so if the attribute is on 10 and the input is 10, you have a weight of 1 in this condition but if the input is 11 then your input weight is 0.

If you are interested in the boolean expression of the output, you can always add a rounding system at the end of the fuzy evaluations doing a "rude" bigger than or equal to 0,5 then 1 and lower than 0,5 then 0

I hope I undestood you correctly and that I could help you.

Regards.
--------
M.D.V.

If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
“The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.” - Michael A. Jackson
Rating helpfull answers is nice, but saying thanks can be even nicer.