Before we start the application, the interfaces to the fuzzy module must be defined. The implementation is a straightforward application of the theory in Chapter 30, "Fuzzy Logic," but interfaces are necessary to interact with it. Because fuzzy expert systems are surprisingly close to rule-based systems, the design is mostly borrowed from Part II.
First, we'll consider how the fuzzy module can initialize itself from disk, loading the different components separately.
The working memory contains the fuzzy terms used by the interpreter. These terms may be individual variables or part of linguistic variables. There is no difference between these two types of fuzzy terms, except the way they are referred to later by the rules.
Some fuzzy terms need to be associated with a membership function. This is necessary for fuzzy variables that need fuzzifying by the system (inputs), or those that need defuzzifying automatically (outputs). We'll define the membership function for a symmetric triangle, which is often sufficient (see Listing 31.1).
The term open is defined as a membership function over the base variable of door. The other two terms will be set manually by the user.
<memory> <Variable name="platform"> <Term name="ready" /> <Term name="arrived" /> </Variable> <Variable name="door"> <Term name="open"> <triangle center="0" base="45" /> </Term> </Variable> </memory>
The rulebase must express the relationships between the fuzzy variables. This is done by a set of rules, each with conditions and actions. In the example from Listing 31.2, the rule is defined by a single fuzzy term and a linguistic one. An arbitrary number of statements are allowed within both the head and body of the rule.
The conditions are understood implicitly as a conjunction (AND) of all the statements. Just like for the rule-based system, there are no particular limitations with this paradigm, because disjunctions (OR) can be split into multiple rules.
<rulebase> <Rule> <conditions> <Term name="aboard" /> <Variable name="platform" value="arrived" /> </conditions> <body> <Variable name="move" term="forward" value="1" /> <Variable name="turn" term="exit" value="1" /> </body> </Rule> </rulebase>
Modifiers are handled by nesting the fuzzy terms within a modifier tag such as very, extremely, somewhat, and so on. Currently, these need to be defined within the fuzzy system.
After the mechanisms for initialization, the discussion can focus on the runtime functionality. This native interface handles callbacks automatically for convenience and efficiency. The other type of interface allows dynamic queries, covered shortly after.
To prevent having to feed data into the fuzzy system, a native interface is defined. Using C++ callbacks allows the fuzzy system to gather the information by itself. This proves more convenient and simplifies the code tremendously:
void SetSensor( const SensorFunc& s, const string& symbol ); void SetEffector( const EffectorFunc& e, const string& symbol );
Two functions are used: one to gather the content of a fuzzy term (sensor), and the other to pass the value of the fuzzy term back to the client (effector). The callback functions must be defined according to an existing prototype, specified by the fuzzy system.
Because these two functions are only convenient for accessing single fuzzy terms, there are actually another two ways to declare sensors and effectors. These take two string parameters rather than one; the first specifies the linguistic variable, the second refers to the nested fuzzy term.
void Set( const string& symbol, const float value ); float Get( const string& symbol ) const;
These can query the content of any single fuzzy term. Once again, two more of these functions deal with linguistic variables, using two strings as parameters.