Fuzzy Rules

Simple Example using Java Code
The Rule Executors and Other Things to Consider
Graphical Explanation of Rule Firing
Detailed Example

Multiple Antecedents in a Rule

Other Rule Types and Inferencing Methods (Kosko’s SAM, Takagi-Sugeno-Kang, Tsukamoto)
Jess Integration
 

A FuzzyRule holds three sets of FuzzyValues representing the antecedents, conclusions and input values of a fuzzy rule. A rule might be written as follows:

   if antecedent1 and
      antecedent2 and
          ...
      antecedentn
   then
      conclusion1 and
      conclusion2 and
          ...
      conclusionm

The antecedents are the premises of the rule that must be true before the conclusions of the rule can be asserted. By attaching a set of fuzzy value inputs to the rule that correspond to actual values for the antecedents, a set of actual conclusions can be determined by executing (firing) the rule, using the FuzzyRuleExecutor that is associated with the rule. The executor will generally implement one of the common algorithms for fuzzy inferencing [Tsoukalas and Uhrig] such as the Mamdani min inference operator with the Max-Min composition operator.

Note that this allows only very simple (but still very useful) rules that have only fuzzy values on the left hand side (antecedents) and right hand side (conclusions). The FuzzyJ Toolkit is integrated with the Jess expert system shell and provides the ability to create much more complex rules, combining crisp and fuzzy values.

Simple Example using Java Code

A very simple example of Java code that implements a rule and its firing follows. In English-like syntax the rule being implemented can be written as:

if   temperature is hot
then pressure is low or medium

Then by providing an input value for the temperature, in this case temperature is very medium, the rule can be fired and a conclusion is provided as the output of the rule firing. Note that this example is also coded in Jess using no Java code in the section on FuzzyJess.

In order to create a rule that can be executed (fired) we need to create an instance of a FuzzyRule and then add the FuzzyValues that make up the antecedents, the conclusions and the inputs. In order to create the appropriate FuzzyValues for the rule we need to create the FuzzyVariables for the concepts we are using (temperature and pressure).

// some values used to describe the fuzzy terms in the temperature FuzzyVariable
double xHot[] = {25, 35};
double yHot[] = {0, 1};
double xCold[] = {5, 15};
double yCold[] = {1, 0};
// define our temperature FuzzyVariable with terms hot,
// cold, very hot and medium
FuzzyVariable temp = new FuzzyVariable("temperature", 0, 100, "C");
temp.addTerm("hot", xHot, yHot, 2);
temp.addTerm("cold", xCold, yCold, 2);
temp.addTerm("veryHot", "very hot");
temp.addTerm("medium", "(not hot and (not cold))");
// define our pressure FuzzyVariable with terms low, medium and high
FuzzyVariable pressure = new FuzzyVariable("pressure", 0, 10, "kilo-pascals");
pressure.addTerm("low", new ZFuzzySet(2.0, 5.0));
pressure.addTerm("medium", new PIFuzzySet(5.0, 2.5));
pressure.addTerm("high", new SFuzzySet(5.0, 8.0));
// let's build a rule ---
FuzzyRule rule1 = new FuzzyRule();
FuzzyValue antecedentFval = new FuzzyValue(temp, "hot");
FuzzyValue conclusionFval = new FuzzyValue(pressure, "low or medium");
FuzzyValue inputFval = new FuzzyValue(temp, "very medium");
rule1.addAntecedent(antecedentFval);
rule1.addConclusion(conclusionFval);
rule1.addInput(inputFval);
// execute this simple rule with a single antecedent and
// a single consequent using default rule executor --
// MamdaniMinMaxMinRuleExecutor
FuzzyValueVector fvv = rule1.execute();
// show the results using the plotting methods for FuzzyValues
FuzzyValue fvals[] = new FuzzyValue[2];
fvals[0] = antecedentFval;
fvals[1] = inputFval;
System.out.println(FuzzyValue.plotFuzzyValues("*+", 0, 50, fvals));
System.out.println(fval2.plotFuzzyValue("*", 0, 10));
System.out.println(fvv.fuzzyValueAt(0).plotFuzzyValue("*", 0, 10));
// execute again with a different rule executor --
// LarsenProductMaxMinRuleExecutor
fvv = rule1.execute(new LarsenProductMaxMinRuleExecutor());
// and show results
System.out.println(fvv.fuzzyValueAt(0).plotFuzzyValue("*", 0, 10));

The above would produce the following graphs as output:

The Antecedent (hot) and the input (very medium) fuzzy values

Fuzzy Value: temperature
Linguistic Value: hot (*),  very medium (+)

1.00               +++++++++++         ****************
0.95
0.90                                  *
0.85
0.80              +           +      *
0.75
0.70                                *
0.65             +             +
0.60                               *
0.55
0.50            +               + *
0.45
0.40                             *
0.35           +                 +
0.30                            *
0.25          +                   +
0.20                           *
0.15         +                     +
0.10        +                 *     +
0.05       +                         +
0.00+++++++*******************        +++++++++++++++++
    |----|----|----|----|----|----|----|----|----|----|
   0.00     10.00     20.00     30.00     40.00     50.00

The conclusion fuzzy value.

Fuzzy Value: pressure
Linguistic Value: low or medium (*)

1.00************            ***
0.95            *          *   *
0.90             *        *     *
0.85              *
0.80                     *       *
0.75               *
0.70                    *
0.65                *             *
0.60
0.55                 * *           *
0.50
0.45                  *
0.40                  *            *
0.35
0.30
0.25                                 *
0.20
0.15                                  *
0.10                                   *
0.05                                    *
0.00                                     **************
    |----|----|----|----|----|----|----|----|----|----|
   0.00      2.00      4.00      6.00      8.00     10.00

The output using MamdaniMinMaxMinRuleExecutor

Fuzzy Value: pressure
Linguistic Value: ??? (*)

1.00
0.95
0.90
0.85
0.80
0.75
0.70
0.65
0.60
0.55
0.50
0.45
0.40*********************************
0.35
0.30
0.25                                 *
0.20
0.15                                  *
0.10                                   *
0.05                                    *
0.00                                     **************
    |----|----|----|----|----|----|----|----|----|----|
   0.00      2.00      4.00      6.00      8.00     10.00

The conclusion using LarsenProductMaxMinRuleExecutor.

Fuzzy Value: pressure
Linguistic Value: ??? (*)

1.00
0.95
0.90
0.85
0.80
0.75
0.70
0.65
0.60
0.55
0.50
0.45
0.40************             *
0.35            ***       *** ***
0.30               *     *       *
0.25                *   *         *
0.20                 * *           *
0.15                  **            *
0.10                                 *
0.05                                  **
0.00                                    ***************
    |----|----|----|----|----|----|----|----|----|----|
   0.00      2.00      4.00      6.00      8.00     10.00

The Rule Executors and Other Things to Consider

There are a lot of methods that allow the antecedents, conclusions and inputs to be added, removed, and accessed more precisely than is shown by methods such as the addAntecedent method in the example above. The details can be checked in the Fuzzy Java Toolkit API documentation and by browsing other examples. However, let's look a bit more closely at some things that must be considered before executing rules.

First, in the example above we just went ahead and executed the rule after the inputs had been added to the rule. In this case everything works fine because we know (by the construction of the antecedent and the input) that in fact there is some matching (overlap) of the input and the antecedent fuzzy values. This means that the output will have a non-empty fuzzy value. However, had the input not matched the antecedent, the resulting output would have been an empty fuzzy value (i.e. the fuzzy set would have had zero values everywhere). A simple solution here is to execute the rule only when the method testRuleMatching returns true. In the above example we would have done:

if (rule1.testRuleMatching())
{
   FuzzyValueVector fvv = rule1.execute();
   ...
}

This also in general saves a lot of unnecessary time executing a rule when it will not yield a result. This is especially true for rules that have many antecedents. In a rule based system (like Jess with the Fuzzy extensions), this will be taken care of in the pattern matching of the antecedents as inputs are asserted into the system. It is also possible to control the degree of matching of the inputs and antecedents required to return a true from the testRuleMatching method. Normally any matching will result in a true value since the threshold value is defaulted to 0.0. One can change the threshold by calling the method testRuleMatching with an argument between 0 and 1. Suppose we use the value 0.1. Then the result is true only if the matching (overlap) of all of the corresponding inputs and antecedents is > 0.1. That is each pair of values must have an intersection with maximum value > 0.1. The default value for matching can also be controlled by calling the static FuzzyValue method, setMatchThreshold.

Secondly, if there are many rules that affect the same output variable (this is common in many applications), then it is often useful to merge the output variable of all of these rules into a single one. This is known as global contribution. A common strategy is to perform the union of all of the results from all of the rules using a fuzzy union. Then after all of the rules have fired we have the global effect of the contribution of each rule to an answer. The result is often then defuzzified to provide a crisp value to feed back to the system being controlled. The fuzzy shower problem provided as an example with the FuzzyJ Toolkit (and presented on the FuzzyJ web pages) is an example of this complete process. Note that in the FuzzyJess extensions, this global contribution is done for the user automatically as part of the rule's execution (when facts with fuzzy slots are asserted). As of Version 1.5 of FuzzyJ/FuzzyJess there is a Jess function set-fuzzy-global-contribution-operator that allows the user to control what operator (fuzzy union, fuzzy sum, none, etc.) is used to do this global contribution. In pure Java programs, the type of global contribution is easily controlled by performing the require operation in Java code. But previous to FuzzyJess 1.5 the global contribution was automatic and always a union operation. This provides the Jess programmer with the same flexibility and a way to extend the operators that can be used if required.

So the steps often used in many applications are:

  1. collect the system inputs.
  2. fuzzify the inputs (make them appropriate fuzzy values).
  3. apply the inputs to all of the rules in the system, executing the rules 1 at a time and performing a global accumulation of the outputs.
  4. defuzzify the outputs (create crisp numbers from the output fuzzy values).
  5. apply the crisp outputs to the system.
  6. repeat all steps until the system is in a controlled state.

Each rule has associated with it a FuzzyRuleExecutor. These FuzzyRuleExecutors are classes that implement the FuzzyRuleExecutorInterface and the Cloneable interface, so they must supply execute and clone methods. These execute methods are called when the FuzzyRule execute method is called. It is the FuzzyRuleExecutor's execute method that performs the magic of rule firing and produces the fuzzy outputs. There are many ways to perform this set of calculations. Currently three system defined FuzzyRuleExecutors are supplied, the MamdaniMinMaxMinRuleExecutor the LarsenProductMaxMinRuleExecutor and the TsukamotoRuleExecutor. These correspond using the Mamdani Min inference operator with MaxMin composition, the Larsen Product inference operator with MaxMin composition respectively and a special rule execution defined by Tsukamoto (details later in this text and in the New Features, Changes and Helpful Hints of the FuzzyJ User Guide). This is covered in detail in [Tsoukalas and Uhrig]. Other combinations may be implemented in the future but they do not lend themselves to such a simple and efficient implementation as these do due to some special characteristics. The first two are also the most commonly used approaches.

When a FuzzyRule is constructed, it will either be given a default rule executor (this can be controlled by the user, see method setDefaultRuleExecutor of the FuzzyRule class) or a specific rule executor is supplied to the constructor. Because the rule executors may want to store state from invocation to invocation, each FuzzyRule is given its own instance of the executor. This assists the executor in providing an efficient implementation. This is also the reason that FuzzyRuleExecutors must implement the Cloneable interface.
 

Graphical Explanation of  Rule Firing

Below is a graphical look at how the 2 system supplied executors operate using a simple fuzzy rule.

The diagrams depict the rule:

    If temperature is hot then turn thermostat down a lot, with the input temperature is warm.

Fist we’ll look at the MamdaniMinMaxMinRuleExecutor. The effect of this type of inference is to generate an output fuzzy value that is the conclusion fuzzy value clipped at the maximum value of the intersection of the antecedent fuzzy value and the input fuzzy value.

The next graphic depicts the use of the LarsenProductMaxMinRuleExecutor. This is similar to the previous method, except that the output is scaled by the maximum intersection value rather than being clipped.

Next is an example of the firing of 2 rules and the effect of globally combining the outputs of the two rules (using the default MamdaniMinMaxMinRuleExecutor and the default fuzzy union operation to combine the output of the 2 rules). The rules are:

    If temperature is hot then turn thermostat down a lot.
    If temperature is cool then turn thermostat up a little


    with the input temperature is warm.

Notice that in this case the outputs from each rule firing are combined using a fuzzy union operator. The graphic also depicts the value of the crisp output of the result if the output fuzzy value was defuzzified using the momentDefuzzify method.

Detailed Example

A partial listing of key parts of the Fuzzy Compiler example that is included with the FuzzyJ Toolkit are shown below. It shows the steps outlined above in some detail with a slightly more complex set of rules.

In this example we have 2 antecedent fuzzy variables and 1 conclusion fuzzy variable. They are:

    Antecedent Fuzzy Variables
    temperature 0 100 degrees C      pressure 0 500 kPa
       terms: low, medium, high                    terms: low, medium, high

    Conclusion Fuzzy Variable
    throttle 0 1 units
       terms: verylow, low, midlow, medium, midhigh, high

The 9 rules can be written as:
 

    if  temperature is low
    and pressure is low
    then set throttle to high

    if  temperature is low
    and pressure is medium
    then set throttle to medium

    if  temperature is low
    and pressure is high
    then set throttle to midlow

    if  temperature is medium
    and pressure is low
    then set throttle to midhigh

    if  temperature is medium
    and pressure is medium
    then set throttle to midlow

    if  temperature is medium
    and pressure is high
    then set throttle to low

    if  temperature is high
    and pressure is low
    then set throttle to midlow

    if  temperature is high
    and pressure is medium
    then set throttle to low

    if  temperature is high
    and pressure is high
    then set throttle to verylow

The partial code: (note that the rules could have been put into an array for efficiency but for readability this is better; in fact in the example shipped with version 1.5 or later of the FuzzyJ Toolkit some of the code was rewritten to use an array of rules; it is suggested that the new code be looked at as well)

    // define the FuzzyRules and Fuzzy Variables we will use
    FuzzyRule lowTempLowPressure = new FuzzyRule();
    FuzzyRule lowTempMediumPressure = new FuzzyRule();
    FuzzyRule lowTempHighPressure = new FuzzyRule();
    FuzzyRule mediumTempLowPressure = new FuzzyRule();
    FuzzyRule mediumTempMediumPressure = new FuzzyRule();
    FuzzyRule mediumTempHighPressure = new FuzzyRule();
    FuzzyRule highTempLowPressure = new FuzzyRule();
    FuzzyRule highTempMediumPressure = new FuzzyRule();
    FuzzyRule highTempHighPressure = new FuzzyRule();
    FuzzyVariable temperature;
    FuzzyVariable pressure;
    FuzzyVariable throttle;

    // define temperature variable with its terms
    temperature = new FuzzyVariable("temperature", 0.0, 100.0, "Degrees C");
    temperature.addTerm("low", new PIFuzzySet(20, 20));
    temperature.addTerm("medium", new PIFuzzySet(50, 20));
    temperature.addTerm("high", new PIFuzzySet(80, 20));
    // define pressure variable with its terms
    pressure = new FuzzyVariable("pressure", 0.0, 500.0, "kPa");
    pressure.addTerm("low", new PIFuzzySet(100, 100));
    pressure.addTerm("medium", new PIFuzzySet(250, 100));
    pressure.addTerm("high", new PIFuzzySet(400, 100));
    // define the throttle fuzzy variable with its terms
    throttle = new FuzzyVariable("throttle", 0.0, 1.0, "units");
    throttle.addTerm("verylow", new PIFuzzySet(.1, .1));
    throttle.addTerm("low", new PIFuzzySet(.25, .15));
    throttle.addTerm("midlow", new PIFuzzySet(.4, .1));
    throttle.addTerm("medium", new PIFuzzySet(.55, .15));
    throttle.addTerm("midhigh", new PIFuzzySet(.75, .1));
    throttle.addTerm("high", new PIFuzzySet(.9, .1));

    // define the 9 rules
    lowTempLowPressure.addAntecedent(new FuzzyValue(temperature,"low"));
    lowTempLowPressure.addAntecedent(new FuzzyValue(pressure,"low"));
    lowTempLowPressure.addConclusion(new FuzzyValue(throttle,"high"));
    lowTempMediumPressure.addAntecedent(new FuzzyValue(temperature,"low"));
    lowTempMediumPressure.addAntecedent(new FuzzyValue(pressure,"medium"));
    lowTempMediumPressure.addConclusion(new FuzzyValue(throttle,"medium"));
    lowTempHighPressure.addAntecedent(new FuzzyValue(temperature,"low"));
    lowTempHighPressure.addAntecedent(new FuzzyValue(pressure,"high"));
    lowTempHighPressure.addConclusion(new FuzzyValue(throttle,"midlow"));
    mediumTempLowPressure.addAntecedent(new FuzzyValue(temperature,"medium"));
    mediumTempLowPressure.addAntecedent(new FuzzyValue(pressure,"low"));
    mediumTempLowPressure.addConclusion(new FuzzyValue(throttle,"midhigh"));
    mediumTempMediumPressure.addAntecedent(new FuzzyValue(temperature,"medium"));
    mediumTempMediumPressure.addAntecedent(new FuzzyValue(pressure,"medium"));
    mediumTempMediumPressure.addConclusion(new FuzzyValue(throttle,"midlow"));
    mediumTempHighPressure.addAntecedent(new FuzzyValue(temperature,"medium"));
    mediumTempHighPressure.addAntecedent(new FuzzyValue(pressure,"high"));
    mediumTempHighPressure.addConclusion(new FuzzyValue(throttle,"low"));
    highTempLowPressure.addAntecedent(new FuzzyValue(temperature,"high"));
    highTempLowPressure.addAntecedent(new FuzzyValue(pressure,"low"));
    highTempLowPressure.addConclusion(new FuzzyValue(throttle,"midlow"));
    highTempMediumPressure.addAntecedent(new FuzzyValue(temperature,"high"));
    highTempMediumPressure.addAntecedent(new FuzzyValue(pressure,"medium"));
    highTempMediumPressure.addConclusion(new FuzzyValue(throttle,"low"));
    highTempHighPressure.addAntecedent(new FuzzyValue(temperature,"high"));
    highTempHighPressure.addAntecedent(new FuzzyValue(pressure,"high"));
    highTempHighPressure.addConclusion(new FuzzyValue(throttle,"verylow"));

    // fire the rules for values of temp from 5 to 95 (increments of 5)
    // and of pressure from 10 to 490, storing the results of the rule firings
    // (i.e. the de-fuzzified result of the combination of all rules) in a
    // 2D array.
    for (i=0; i<19; i++)
    { for (j=0; j<49; j++)
      { try
        { // each iteration corresponds to collecting a set of inputs
          // could have done this below more efficiently (codewise) but ...
          // ie. use an array of rules rather than specific named rules
          double temp = i*5+5;    // temperature crisp input
          double press = j*10+10; // pressure crisp input
          globalOutput = null;    // global contribution of outputs of each rule
          // Fuzzify the crisp inputs as fuzzy values
          FuzzyValue tempFv = new FuzzyValue(this.temperature,new PIFuzzySet(temp, 0.5));
          FuzzyValue pressFv = new FuzzyValue(this.pressure,new PIFuzzySet(press, 0.5));
          FuzzyValue fv;
          // add inputs to rules and fire them and do global contribution
          // Note: we use testRuleMatching to see if the rule really needed to fire
          this.lowTempLowPressure.removeAllInputs(); // clear previous inputs if any
          this.lowTempLowPressure.addInput(tempFv);
          this.lowTempLowPressure.addInput(pressFv);
          if (this.lowTempLowPressure.testRuleMatching())
          {
           fvv = lowTempLowPressure.execute(); // get output of the rule
           globalOutput = fvv.fuzzyValueAt(0); // 1st rule ... it becomes global output
          }
          this.lowTempMediumPressure.removeAllInputs();
          this.lowTempMediumPressure.addInput(tempFv);
          this.lowTempMediumPressure.addInput(pressFv);
          if (this.lowTempMediumPressure.testRuleMatching())
          {
           fvv = lowTempMediumPressure.execute(); // get output of the rule
           fv = fvv.fuzzyValueAt(0);              // not 1st rule so add to previous
           if (globalOutput == null)              // global output
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.lowTempHighPressure.removeAllInputs();
          this.lowTempHighPressure.addInput(tempFv);
          this.lowTempHighPressure.addInput(pressFv);
          if (this.lowTempHighPressure.testRuleMatching())
          {
           fvv = lowTempHighPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.mediumTempLowPressure.removeAllInputs();
          this.mediumTempLowPressure.addInput(tempFv);
          this.mediumTempLowPressure.addInput(pressFv);
          if (this.mediumTempLowPressure.testRuleMatching())
          {
           fvv = mediumTempLowPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.mediumTempMediumPressure.removeAllInputs();
          this.mediumTempMediumPressure.addInput(tempFv);
          this.mediumTempMediumPressure.addInput(pressFv);
          if (this.mediumTempMediumPressure.testRuleMatching())
          {
           fvv = mediumTempMediumPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.mediumTempHighPressure.removeAllInputs();
          this.mediumTempHighPressure.addInput(tempFv);
          this.mediumTempHighPressure.addInput(pressFv);
          if (this.mediumTempHighPressure.testRuleMatching())
          {
           fvv = mediumTempHighPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.highTempLowPressure.removeAllInputs();
          this.highTempLowPressure.addInput(tempFv);
          this.highTempLowPressure.addInput(pressFv);
          if (this.highTempLowPressure.testRuleMatching())
          {
           fvv = highTempLowPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.highTempMediumPressure.removeAllInputs();
          this.highTempMediumPressure.addInput(tempFv);
          this.highTempMediumPressure.addInput(pressFv);
          if (this.highTempMediumPressure.testRuleMatching())
          {
           fvv = highTempMediumPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          this.highTempHighPressure.removeAllInputs();
          this.highTempHighPressure.addInput(tempFv);
          this.highTempHighPressure.addInput(pressFv);
          if (this.highTempHighPressure.testRuleMatching())
          {
           fvv = highTempHighPressure.execute();
           fv = fvv.fuzzyValueAt(0);
           if (globalOutput == null)
              globalOutput = fv;
           else
              globalOutput = globalOutput.fuzzyUnion(fv);
          }
          // defuzzify the final output (global) to get a crisp value
          // ... in this case store it in an array
          results[i][j] = globalOutput.momentDefuzzify();
        }
       catch (FuzzyException e)
        { System.out.println(e);
          System.out.println("At "+i+","+j+" .../n"+ globalOutput);
        }
     }
  }
 

Multiple Antecedents in a Rule

In the last example there were two antecedents in the rule. Previous examples with one fuzzy antecedent showed that we used the match value (maximum value of the intersection of the antecedent and its input) to determine how to modify the output fuzzy values. For the MamdaniMinMaxMinRuleExecutor we clipped the output at this value and for the LarsenProductMaxMinRuleExecutor we scaled the outputs. However, when there are several match values due to multiple antecedents then we have to determine how these values should be combined to affect the output values. By default (and in all versions of FuzzyJ before 1.5), the combining operator was a minimum of the match values. As of version 1.5 an interface (AntecedentCombineOperatorInterface), an abstract class (AntecedentCombineOperator) and a couple of classes that extend the abstract class (MinimumAntecedentCombineOperator and ProductAntecedentCombineOperator) were added to allow us to easily control how these match values are combined. Each rule can determine it’s own combining operator and the default operator can be controlled as well (See the FuzzyRule class documentation in the FuzzyJ API). All of the Rule Executors use this information to decide how to manage multiple antecedents. In FuzzyJ version 1.5a a third combine operator was added (CompensatoryAndAntecedentCombineOperator). This is described in the API, but basically it provides an operator that gives a value larger than the minimum or product operators. For example, if you have three antecedents and two match at 1.0 and one at 0.1, then the CompensatoryAnd operator will give a value larger than 0.1 (it can be adjusted by a parameter setting).

The Fuzzy Compiler example shown above has been modified (in FuzzyJ Toolkit 1.5 and later) to allow the user to control the Rule Executor (as before), plus the Antecedent Combining Operator (minimum or product) and the operator to use when global contribution of output fuzzy values is done (fuzzy union or fuzzy sum). Take a look at the code of this example to see how these options can be controlled.

Other Rule Types and Inferencing Methods (Kosko’s SAM, Takagi-Sugeno-Kang, Tsukamoto)

So we see that there are now quite a few options that control the execution of rules: the Rule Executor, the Antecedent Combining Operator and the Global Contribution Operator. In many cases the defaults (mandami, minimum, union) are just fine. However, there are valid reasons to have a choice. In particular, Bart Kosko, in his book Fuzzy Engineering (see references section), clearly argues for a method called SAM (the Standard Additive Model), which is implemented in FuzzyJ using the MamdaniMinMaxMinRuleExecutor, the ProductAntecedentCombineOperator, the fuzzySum for Global Contribution and the momentDefuzzify method.

We have discussed the two most common rule inference procedures using MandaminiMinMaxMin or LarsenProductMaxMin rule executors. However, there are two other commonly used methods for creating and executing rules that can be implemented in FuzzyJ. These are the Tsukamoto inference method and Takagi-Sugeno-Kang (TSK) type rules.

Takagi-Sugeno-Kang rules have fuzzy inputs but they have specialized crisp outputs that are generally either constants (zero order TSK rules) or crisp outputs that are defined by a function that computes a crisp value (usually a function of the crisp values of the input variable). A description of how to implement these TSK rules in FuzzyJ/FuzzyJess is provided in the section New Features, Changes and Helpful Hints.

Tsukamoto rules use consequents that are strictly increasing or strictly decreasing functions. When a rule fires and the outputs are generated a crisp x value and a membership value are recorded. The membership value is the DOF (Degree of Fulfillment or degree of matching) of the rule. The x value is chosen by looking at the consequent fuzzy set to see which x value has that membership value. The final output of all of the rules is the weighted average of all the x values, using the membership values as the weights. In FuzzyJ we have a special RuleExecutor, TsukamotoRuleExecutor, that generates outputs that are singleton fuzzy values at the selected x value with a membership value equal to the DOF of the rule. These output fuzzy values should be combined using the fuzzySum method (rather than the default fuzzyUnion) and then defuzzified using the weightedAverageDefuzzify method. This produces the Tsukamoto inference results. In summary to implement Tsukamoto type rules:

 

1. Make sure the rules fuzzy outputs are defined using strictly increasing or strictly decreasing functions that cover all of the membership values from 0.0 to 1.0 (some example fuzzy sets that have this property are SFuzzySet, ZFuzzySet, LeftLinearFuzzySet, and RightLineraFuzzySet.

 

2. Use the fuzzySum method as the rule output combine (global contribution) operator.

 

3. Use the weightedAverageDefuzzify method to defuzzify the combined output of the rules.

 

This is shown below for a simple 2-rule situation.

 

 


Jess Integration

For details on building rules in Jess that have fuzzy components see the section on FuzzyJess.

 

Return to Table of Contents