FLINT toolkit

This document give a detailed description of the fuzzy logic features of FLINT, a powerful sub-system which augments the decision-making power of both Prolog and flex. FLINT provides a comprehensive and versatile set of facilities for programmers who wish to incorporate uncertainty within their expert systems and decision support applications.

FLINT

Traditional expert systems work on the basis that everything is either true or false, and that any rule whose conditions are satisfiable is useable, i.e. its conclusion(s) are true. This is rather simplistic and can lead to quite brittle expert systems.

FLINT provides support for where the domain knowledge is not so clearcut. FLINT supports three treatments of uncertainty, namely: Fuzzy logic, Bayesian updating and Certainty factors. FLINT does this by augmenting the normal backward-chaining rules of Prolog and, where Flex is present, by extending the KSL of Flex.

Fuzzy Logic and FLINT

FLINT supports the concept of fuzzy variables, (like speed, temperature) and the concept of fuzzy qualifiers (hot, cold, slow, fast). Applying a qualifier to a fuzzy variable generates a fuzzy set. For each fuzzy set there is a membership function relating crisp to fuzzy values, and which is defined in terms of its shape and location. FLINT supports various standard shapes (trapeziods, triangles) as well as any user-defined shape. For each of these lines can be specified as either staight or curved.

FLINT also supports the concept of fuzzy modifiers (very, extremely, not very), often referred to linguistic hedges. These affect the membership function by intensifying (concentrating) or spreading (diluting) its shape.

Fuzzy rules define relationships between different fuzzy sets as if-then rules. In FLINT, these rules are expressed using a simple, uncluttered syntax. Furthermore, they can be grouped into matrices, commonly known as fuzzy associative memory (FAM).

In fuzzy reasoning over sets there are standard operations such as union and intersection. These operations can be defined in terms of simple mathmetical operations such as maximum, minimum, addition, strengthening (difference between sum and product). FLINT supports various mathemetical operators which can be combined to implement numerous strategies including min-max, additive, and strengthening.

The final stage of a fuzzy evaluation is the conversion back from fuzzy membership values to crisp values for the output variables which is referred to as defuzzification. FLINT supports two standard defuzzifiers, the centroid method which is based on the centre of gravity, and the peak method which is based on the highest fuzzy value. In addition, FLINT allows you to nominate a user-defined defuzzifier.

Fuzzy Logic - A Worked Example

In this section we will implement a fuzzy logic controller. In the general model the device being controlled has a set of activators that take input and use this to affect the settings of the device and a set of sensors that get information from the device. The fuzzy logic controller takes the 'crisp' information from the sensors and fuzzifies the input into some fuzzy variables, propagates membership values using the fuzzy rules and finally de-fuzzifies the output and returns these 'crisp' values to the activators. The structure of the a fuzzy logic controller is shown in Figure 1.

Figure 1 - A Fuzzy Logic Controller

A Fuzzy Turbine Controller

The controller we will implement will use a fuzzy set of rules to adjust the throttle of a steam turbine according to its current temperature and pressure to keep it running smoothly. The structure of the turbine controller is shown in Figure 2.

Figure 2 - A Fuzzy Turbine Controller

Defining Fuzzy Operators

Before coding the example we need to define the operators that are going to be used in defining the fuzzy rules. These are the words that are used to define conditions, conclusions, conjunctions, disjunctions and negations in fuzzy rules.

%%%%%%%%%%%%%%%%%%%%%%%%
% Linguistic operators %
%%%%%%%%%%%%%%%%%%%%%%%%
:- op( 1200, xfy, ( if   ) ),
   op( 1200, xfy, ( then ) ),
   op( 1200, xfx, ( else ) ),
   op( 1100, xfy, ( or   ) ),
   op( 1000, xfy, ( and  ) ),
   op(  700, xfx, ( is   ) ),
   op(  600,  fy, ( not  ) ).

Defining Fuzzy Variables

First we need to decide upon the fuzzy variables and qualifiers that are going to be used in the fuzzy program. We do this first by looking at the quantities that will be used by the fuzzy rules, in this case 'temperature', 'pressure' and 'throttle'. We also know that we want to work out values for the 'throttle' using the values for the 'temperature' and 'pressure' so we can guess that 'temperature' and 'pressure' will be input variables and 'throttle' will be an output variable. We will define a fuzzy variable for each of these.

Then we need to know what sorts of words the fuzzy rules are going to use to describe each fuzzy variable. These words may exist in 'rules of thumb' already used by turbine operators or we may need to decide upon them ourselves. These words become the linguistic qualifiers of the fuzzy variable.

Let's look at the 'temperature' fuzzy variable first. The definition of a fuzzy variable may also include a definition of the range of 'crisp' values it may take. The range of temperatures possible for a turbine may broadly be defined as being between 0 and 500 degrees Celsius, though the significant values lie between 100 and 350. The start of our fuzzy 'temperature' variable will be:

fuzzy_variable( temperature ) :-
	[ 0 , 500 ] ;

The temperature of the turbine is normally described using the adjectives: cold, cool, normal, warm and hot. We need to define a qualifier for the 'temperature' fuzzy variable for each of these.

The easiest way to implement a rough guide to any qualifier is to define it using a linear shape so that the lines between the points are always straight and not curved. Refinements to the membership function can be introduced, if needed, at a later stage.

We can guess that the 'cold' qualifier in our example should refer to temperatures at the lower end of the fuzzy variable this probably means it will use the downward slope shape. By talking to the steam turbine experts or making a rough estimate, we want the 'cold' qualifier to definitely refer to all the values below 110 and possibly to the values below 165 and not to the values above 165. The values between 110 and 165 should become steadily less 'cold' in a linear fashion. This could be done by adding the following definition to the 'temperature' fuzzy variable:

      cold,   \ , linear, [ 110 , 165       ] ;

Notice that this 'cold' qualifier is notionally cold for temperatures in a steam turbine other situations may require a completely different definition for 'cold'.

Next we want a 'cool' qualifier. We can guess that this qualifier is not going to apply to the extremes of the temperature range and that it is a positive qualifier (as opposed to 'uncool'), so it will probably be an upwards pointing triangle or an upwards pointing trapezoid. Again by examining the real situation, talking to the experts or estimating, we decide that 'cool' refers to values between 110 and 220, reaching a definite peak at 165. The values between 110 and 165 should become more 'cool' and the values between 165 and 220 should become less 'cool' in a linear fashion. This can be added to our 'temperature' fuzzy variable using the following 'cool' definition:

       cold,   /\ , linear, [ 110 , 165 , 220 ] ;

In this fashion we can continue to add the other qualifiers for the 'temperature' fuzzy variable ending up with the following definition:

fuzzy_variable( temperature ) :-
   [ 0 , 500 ] ;
   cold,   \ , linear, [ 110 , 165       ] ;
   cool,   /\, linear, [ 110 , 165 , 220 ] ;
   normal, /\, linear, [ 165 , 220 , 275 ] ;
   warm,   /\, linear, [ 220 , 275 , 330 ] ;
   hot,     /, linear, [       275 , 330 ] .

The following diagram shows the membership functions for all the 'temperature' qualifiers combined on a single graph:

Figure 3 - the qualifiers for the 'temperature' variable

The other input fuzzy variable, 'pressure', can be defined in a similar way. The pressure of the turbine is normally described using the adjectives: weak, low, ok, strong and high, a qualifier for the 'pressure' fuzzy variable should be added for each of these. The fuzzy 'pressure' variable should have a range of 0 to 300 (measured in 100 Kpa units of pressure) and each qualifier needs reasonable values to define its membership function in the context of this example. The result is the following definition for the 'pressure' fuzzy variable:

fuzzy_variable( pressure ) :-
   [ 0 , 300 ] ;
   weak,   \ , linear, [  10 ,  70       ] ;
   low,    /\, linear, [  10 ,  70 , 130 ] ;
   ok,     /\, linear, [  70 , 130 , 190 ] ;
   strong, /\, linear, [ 130 , 190 , 250 ] ;
   high,    /, linear, [       190 , 250 ] .

The following diagram shows the membership functions for all the 'pressure' qualifiers combined on a single graph:

Figure 4 - the qualifiers for the 'pressure' variable

The final fuzzy variable in the example is the 'throttle' output variable.

For convenience we will define the range for the 'throttle' as being between -60 and 60 (a negative number indicating that the throttle should be moved back and a positive value that it should be moved forward). The problem results in moving the throttle by large, medium or small amounts in the negative and positive directions. So we'll assign seven qualifiers that will cover these resultant actions: 'negative_large', 'negative_medium', 'negative_small', zero, 'positive_small', 'positive_medium' and 'positive_large' and give them values suitable to their range. This time we'll also need to consider how to return a 'crisp' value from the variable at the end of the fuzzy program. As a reasonable starting point we'll use the default 'centroid' method. This results in the following definition for the 'throttle' fuzzy variable:

fuzzy_variable( throttle ) :-
   [ -60 , 60 ] ;
   negative_large,  \ , linear, [ -45 , -30       ] ;
   negative_medium, /\, linear, [ -45 , -30 , -15 ] ;
   negative_small,  /\, linear, [ -30 , -15 ,   0 ] ;
   zero,            /\, linear, [ -15 ,   0 ,  15 ] ;
   positive_small,  /\, linear, [   0 ,  15 ,  30 ] ;
   positive_medium, /\, linear, [  15 ,  30 ,  45 ] ;
   positive_large,   /, linear, [        30 ,  45 ] ;
   centroid .

The following diagram shows the membership functions for all the 'throttle' qualifiers combined on a single graph:

Figure 5 - the qualifiers for the 'throttle' variable

Defining Fuzzy Rules

After defining the fuzzy variables for our problem we can then proceed to the fuzzy rules. Let's suppose that there are many rules relating to temperature, pressure and throttle, that have been obtained by talking to the steam turbine experts. In this example the rules will cover all the possible combinations of temperature and pressure and give a resultant throttle change for each.

Three example rules, from the experts, are written below:

'if the temperature is cold and the pressure is weak then increase the throttle by a large amount'

'if the temperature is hot and the pressure is high then decrease the throttle by a large amount'

'if the temperature is normal and the pressure is ok then don't change the throttle'

Each of these rules could be defined separately using the following fuzzy rule definitions:

fuzzy_rule(throttle1)
   if temperature is cold
   and pressure is weak
   then throttle is positive_large.

fuzzy_rule(throttle2)
   if temperature is hot
   and pressure is high
   then throttle is negative_large.

fuzzy_rule(throttle3)
   if temperature is normal
   and pressure is ok
   then throttle is zero.

We could then proceed to define the remaining rules in this form. Alternatively, because the rules always apply to temperature, pressure and throttle, we can combine them all into a single fuzzy matrix. The complete set of rules for the problem would then be as follows:

fuzzy_matrix throttle_value
   temperature  *  pressure  ->  throttle         ;
   cold         *  weak      ->  positive_large   ;
   cold         *  low       ->  positive_medium  ;
   cold         *  ok        ->  positive_small   ;
   cold         *  strong    ->  negative_small   ;
   cold         *  high      ->  negative_medium  ;
   cool         *  weak      ->  positive_large   ;
   cool         *  low       ->  positive_medium  ;
   cool         *  ok        ->  zero             ;
   cool         *  strong    ->  negative_medium  ;
   cool         *  high      ->  negative_medium  ;
   normal       *  weak      ->  positive_medium  ;
   normal       *  low       ->  positive_small   ;
   normal       *  ok        ->  zero             ;
   normal       *  strong    ->  negative_small   ;
   normal       *  high      ->  negative_medium  ;
   warm         *  weak      ->  positive_medium  ;
   warm         *  low       ->  positive_small   ;
   warm         *  ok        ->  negative_small   ;
   warm         *  strong    ->  negative_medium  ;
   warm         *  high      ->  negative_large   ;
   hot          *  weak      ->  positive_small   ;
   hot          *  low       ->  positive_small   ;
   hot          *  ok        ->  negative_medium  ;
   hot          *  strong    ->  negative_large   ;
   hot          *  high      ->  negative_large   .

Wrapping Up Fuzzy Programs

The concluding process in implementing the turbine program is to wrap up the fuzzy variables and rules in a program that can be run. This program will set the inputs for the 'temperature' and 'pressure' fuzzy variables, decide how to propagate the degrees of membership using the fuzzy rule matrix and finally, after propagation, get the result from the 'throttle' fuzzy variable.

The predicate we will define will have three arguments. The first two will be the input values for the 'temperature' and 'pressure' fuzzy variables and the third argument will return the resultant value of the 'throttle' fuzzy variable. Setting the inputs for and getting the results from fuzzy variables can be done using the predicate fuzzy_variable_value/2. There will be three calls to this predicate in our program one for each fuzzy variable.

The fuzzy rule propagation can be arranged using the fuzzy_propagate/4 predicate. We need to decide how the membership values will be propagated from the condition qualifiers to the conclusion qualifier according to the conjunctions, disjunctions and negations contained within the fuzzy rules. As a starting point we will use the defaults: minimum for conjunctions (the minimum of the membership values in the conjunction will be propagated), maximum for disjunction (the maximum of the membership values in the conjunction will be propagated) and complement for negations (1 minus the negated membership value will be propagated).

Combining the setting of inputs, fuzzy propagation and the getting of outputs into a single program we end up with the following:

find_throttle( Temperature, Pressure, Throttle ) :-
   fuzzy_variable_value( temperature, Temperature ),
   fuzzy_variable_value( pressure,    Pressure    ),
   fuzzy_propagate( minimum, maximum, complement, [t] ),
   fuzzy_variable_value( throttle,    Throttle    ).

This program will work fine the first time it is run, but it is not quite complete. We also need to ensure that the 'throttle' fuzzy variable does not contain any 'old' values that might affect the fuzzy propagation. The following definition shows the complete find_throttle/3 predicate:

find_throttle( Temperature, Pressure, Throttle ) :-
   fuzzy_reset_membership( throttle ),
   fuzzy_variable_value( temperature, Temperature ),
   fuzzy_variable_value( pressure,    Pressure    ),
   fuzzy_propagate( minimum, maximum, complement, [t] ),
   fuzzy_variable_value( throttle,    Throttle    ).

Compiling The Fuzzy Program

Fuzzy logic programs consist of extensions to the normal Prolog syntax. Prior to compiling any fuzzy logic program you will need to load the fuzzy logic interpreter. This is in the file FUZZY.PC which will have been placed in the SYSTEM directory when you installed the FLINT fuzzy logic toolkit disk. To load this file enter the following command at the Prolog command line:

?- ensure_loaded(system(fuzzy)).

A fuzzy logic example, TURBINE.PL, containing the complete 'turbine' program will also have been installed when you installed the FLINT fuzzy logic toolkit disk, this time in the EXAMPLES directory. Having loaded the fuzzy logic interpreter this example may then be compiled using the command:

?- ensure_loaded(examples(turbine)).

Running The Fuzzy Program

The fuzzy turbine program has the predicate find_throttle/3 as its top-level goal. This program can be queried at the Prolog command-line using the following goal:

?- find_throttle( 300, 150, Throttle ) .

Throttle = -26.040413058933

Fuzzy Logic A KSL Example

This section investigates how to define the steam turbine example from the previous section this time using flex and its Knowledge Specification Language (KSL). The basic idea of the example was to define a set of fuzzy rules to adjust the throttle of a steam turbine according to its current temperature and pressure to keep it running smoothly.

The fuzzy logic components of this KSL example are similar to their equivalents in the Prolog example shown in the previous section and uses the fuzzy rule matrix given above:

%%%%%%%%%%%%%%%%%%%
% Fuzzy Variables %
%%%%%%%%%%%%%%%%%%%
fuzzy_variable temperature
   ranges from 0 to 500 ;
   qualifier  cold   is \  shaped and linear at  110,165     ;
   qualifier  cool   is /\ shaped and linear at  110,165,220 ;
   qualifier  normal is /\ shaped and linear at  165,220,275 ;
   qualifier  warm   is /\ shaped and linear at  220,275,330 ;
   qualifier  hot    is /  shaped and linear at  275,330     .

fuzzy_variable pressure
   ranges  from 0 to  300 ;
   qualifier  weak   is \  shaped and linear at  10,70       ;
   qualifier  low    is /\ shaped and linear at  10,70,130   ;
   qualifier  ok     is /\ shaped and linear at  70,130,190  ;
   qualifier  strong is /\ shaped and linear at  130,190,250 ;
   qualifier  high   is /  shaped and linear at  190,250     .

fuzzy_variable throttle
   ranges from -60 to 60  ;
   qualifier  negative_large  is \  shaped and linear at  -45,  -30      ;
   qualifier  negative_medium is /\ shaped and linear at  -45,  -30, -15 ;
   qualifier  negative_small  is /\ shaped and linear at  -30 , -15,   0 ;
   qualifier  zero            is /\ shaped and linear at  -15 ,   0,  15 ;
   qualifier  positive_small  is /\ shaped and linear at    0 ,  15,  30 ;
   qualifier  positive_medium is /\ shaped and linear at   15 ,  30,  45 ;
   qualifier  positive_large  is /  shaped and linear at   30 ,  45      ;
   centroid  .

Wrapping Up Fuzzy KSL Programs

As in the Prolog example the KSL find_throttle/3 program will set the inputs for the 'temperature' and 'pressure' fuzzy variables, decide how to propagate the degrees of membership using the fuzzy rule matrix and finally, after propagation, get the result from the 'throttle' fuzzy variable.

relation get_throttle_value(Temperature,Pressure,Throttle)
   if reset all fuzzy values
   and the absolute value of temperature is Temperature
   and the absolute value of pressure    is Pressure
   and propagate throttle_value fuzzy rules
   and the absolute value of throttle    is Throttle .

So far we have simply provided a syntactic re-write of the Prolog example. The flex KSL language provides access to many features aimed at the expert system builder. The code that follows ties the example closer to the flex methodology using frames, attributes and data-driven programming. As a start we will create a 'turbine' frame that will model the real turbine that we are attempting to control:

frame turbine
   default temperature is 0
   and default pressure is 0
   and default throttle is 0 .

When a temperature or pressure measurement comes from the physical turbine we will update the appropriate attributes in the 'turbine' frame this can be done with the following KSL actions.

action set_turbine_temperature(T)
   do the temperature of turbine becomes T .

action set_turbine_pressure(P)
   do the pressure of turbine becomes P .

We then need an action to get the 'temperature' and 'pressure' values from the 'turbine' frame pass them through the fuzzy logic program and finally assign the resultant 'throttle' value to the 'turbine' frame's 'throttle' attribute. This is done in the following action called set_turbine_throttle/0:

action set_turbine_throttle
   do  check the temperature of turbine is Temperature
   and check the pressure of turbine is Pressure
   and get_throttle_value(Temperature,Pressure,Throttle)
   and the throttle of turbine becomes Throttle .

We will now provide a way of automatically ensuring that the 'throttle' value is calculated whenever the 'temperature' or 'pressure' attributes are set. This is done by implementing the following two demons:

demon react_to_temperature_update
   when the  temperature of  turbine changes to T
   then set_turbine_throttle .

demon react_to_pressure_update
   when the pressure of  turbine changes to P
   then set_turbine_throttle .

The following action will print out the current state of the 'turbine' frame:

action display_turbine_values
   do write ('The current temperature is: ')
   and write (the temperature of turbine)
   and nl
   and write ('The current pressure is: ')
   and write (the pressure of turbine)
   and nl
   and write ('The current throttle is: ')
   and write (the throttle of turbine)
   and nl  .

The following action will test setting the parameters of the turbine:

action test_turbine_values
   do  set_turbine_temperature(300)
   and set_turbine_pressure(150)
   and display_turbine_values .

Compiling The Fuzzy KSL Program

Prior to compiling any fuzzy logic KSL program you will need to load the fuzzy logic interpreter. This is in the file FUZZY.PC which will have been placed in the SYSTEM directory when you installed the FLINT fuzzy logic toolkit disk. To load this file enter the following command at the Prolog command line:

?- ensure_loaded(system(fuzzy)).

A fuzzy logic example, TURBINE.KSL, containing the complete 'turbine' program will also have been installed when you installed the FLINT fuzzy logic toolkit disk, this time in the EXAMPLES directory. Having loaded the fuzzy logic interpreter this example may then be compiled using the command:

?- reconsult_rules(examples(turbine)).

Running The Fuzzy KSL Program

After the KSL fuzzy turbine program has been compiled, you can set values for the temperature and pressure attributes of the turbine frame and the throttle value will be assigned automatically. Our test program shows this:

?- test_turbine_values.
The current temperature is: 300
The current pressure is: 150
The current throttle is: -26.040413058933
yes
Yes

If we wanted to, we could also set the temperature and pressure separately. The following goals individually set the temperature to 300 and the pressure to 150 (as in our test program):

?- set_turbine_temperature(300).
Yes
?- set_turbine_pressure(150).
yes

The next goal reports the current state of the turbine frame showing that the throttle has been set:

?- display_turbine_values.
The current temperature is: 300
The current pressure is: 150
The current throttle is: -26.040413058933
yes

More details on the other modes of uncertainty support provided by Flint can be found in the Flint Reference Manual.