![]() |
![]() |
|
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.