Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / artificial-intelligence

C++ Fuzzy Logic API + Simple DSL

4.59/5 (11 votes)
18 Jan 2012CPOL8 min read 58.3K   1.7K  
Fuzzy Logic API and Fuzzy Logic DSL implemented with C++.

Fuzzy Logic?

In this article, I will introduce you to my Fuzzy Logic implementation, called FuzzyLogic API. I believe this implementation is simple, effective, and quite fast. Do you know what Fuzzy Logic is? Here I won't talk about Fuzzy Logic theory too much, because there are many existing cool articles about Fuzzy Logic (see links below).

Fuzzy Logic implementation

In this section, I will describe my FuzzyLogic API implementation. I have tried to make a clean and simple implementation that would be easy to understand for everyone. At first I will try to describe some simple classes which we will later use in more complicated and important classes in the FuzzyLogic API.

CLinguisticVariable

UML_0.jpg

CLinguisticVariable is the class used for representing the linguistic variables. There are three class variables. variableName is used for defining the linguistic variable name and should be unique, because we're using a hash map for storing CLinguisticVariable, so we can find specific linguistic variables in time O(1). The next variable, b_output declares if a linguistic variable is output or not. Please note, there can be only one output variable in the FuzzyLogic API. The last variable, h_linguisticValuesHashMap is used for storing linguistic values. One linguistic variable can have one or more linguistic values. There are several class methods. AddLinguisticValue adds LinguisticValue to the selected CLinguisticVariable. In the next image, you can see four linguistic variables: Distance, Approaching, Speed, and Signal.

UML_linguistic_variables.jpg

The next method adds the linguistic value to a hash_map.

C++
//Method add Linguistic value to hash map
void AddLinguisticValue(LinguisticValue* p_linguisticValue)
{
    h_linguisticValuesHashMap.insert(LinguisticValue_Pairs(
        p_linguisticValue->ToString(), p_linguisticValue));
}

where LinguisticValue_Pairs is a typedef declared in:

C++
//Typedef for linguistic values hash map.
typedef pair <string, LinguisticValue*> LinguisticValue_Pairs;

The method ResetLinguisticValues just resets all linguistic values to -1. A more interesting method is FindLinguisticValueByName. The input parameter of the method is the linguistic value name and the return value is a pointer to the object of the LinguisticValue. Because we are using a hash map, the time complexity is O(1).

C++
LinguisticValue* FindLinguisticValueByName(string name)
{
    //Define iterator
    hash_map <string, LinguisticValue*> :: const_iterator iterator;
    //Fint linguistic value by name
    iterator = h_linguisticValuesHashMap.find(name);
    if(iterator == h_linguisticValuesHashMap.end())
    {
        //SASO, this should never happends!!!!!
        assert(NULL);
        return NULL;
    }
    //Return LinguisticValue
    return (LinguisticValue*)iterator->second;
}

CalculateLinguisticValueByName is just a bridge between CFuzzyReasoner and LinguisticValue for calculating the linguistic value. A little bit more complicated is UpdateLinguisticValueByName. At first we have to find LinguisticValue by name. If there is a value stored already, we will perform the fuzzy operation OR, else we will just store newVal. Let's see the code:

C++
//Update Linguistic value by name
void UpdateLinguisticValueByName(string name, double newVal)
{
    //Find LinguisticValue
    LinguisticValue* value = FindLinguisticValueByName(name);

    //If there is value, we should perform operator OR
    if(value->GetLinguisticValue() != -1)
        value->SetLinguisticValue(CFuzzyCalculator::OR(value->GetLinguisticValue(), newVal));
    else
        value->SetLinguisticValue(newVal); //There is no value, just set newVal
}

LinguisticValue

UML_linguistic_value.jpg

UML_linguistic_variables.jpg

The class LinguisticValue is used for storing linguistic values. Variables A, B, C, and D are "borders" of the trapezium (trapezoid). For example, the linguistic variable Distance has three linguistic values (Low, Average, and Height) and for the linguistic value "Low", the variables A, B, C, and D would be A = 0, B = 0, C = 1, D = 2. Every LinguisticValue has a unique name (like in CLinguisticVariable). The variable linguisticValue is used for storing a numeric value. The default value is -1. This value means, how much input value belongs to this LinguisticValue trapezium. For the linguistic value Average (linguistic variable Speed, input: 0 (km/h)), this value would be equal to 0.5 (see image above). There is only one method (except the constructor, getters, and setters) called CalculateLinguisticValue. We have to take care of the correctness of the inputs, so if inputs are out of range, we should normalize them. Then we need to calculate the trapezoidal function. The result of the trapezoidal function is the linguistic value for a specific variable.

C++
double CalculateLinguisticValue(double input)
{
    //Normalize variables
    if(input < A)
        input = A;
    else if(input > D)
        input = D;
    //Calculating Trapezoid function
    if ((input <= A)
        || (input > D ))
        return 0;
    else if ((A < input) && (input < B))
        return (input - A) / (B - A);
    else if ((B <= input) && (input <= C))
        return 1;
    else 
        return (D - input) / (D - C);
}

CFuzzyCalculator

UML_calculator.jpg

Probably the simplest class in the project. There are only two static methods. The method AND implements the operator AND, which is equal to returning the minimal value.

C++
//Operator AND
static double AND(double a, double b)
{
    if(a < b)
        return a;
    return b;
}

and method OR implements operator OR, returning the maximal value.

C++
//Operator OR
static double OR(double a, double b)
{
    if(a > b)
        return a;
    return b;

}

CFuzzyRule

UML_fuzzy_rule.jpg

Fuzzy Logic consists of linguistic variables and fuzzy rules. This class is used for storing a fuzzy rule. A fuzzy rule consists of objects of the class CFuzzyToken. The only functionality of the class is adding CFuzzyRuleToken to a rule. For example, CFuzzyRule could be:

"IF NOT SPEED = SLOW AND DISTANCE = CLOSE, SIGNAL = BRAKE"

Every fuzzy rule should have an output variable!

CFuzzyRuleToken

UML_fuzzy_rule_token.jpg

Like I said before, CFuzzyRuleToken is just one part of a CFuzzyRule. Let's describe all variables. operation describes what kind of operation is between the previous and current CFuzzyRuleToken. Supported operators are OR, AND, and EMPTY. EMPTY means there isn't any operator ("VOID"). The EMPTY token is just used in the first and last tokens in the rule. Also tokens can be negated (variable b_negation). Every CFuzzyToken has a pointer to CLinguisticVariable and the name of the linguistic value. Let's see the CFuzzyRule example a little bit closer.

"IF NOT SPEED = SLOW AND DISTANCE = CLOSE, SIGNAL = BRAKE"

What are tokens? There are three token examples:

Token_1
operation = EMPTY
b_Negate = true
p_linguisticVariable= SPEED
linguisticValueName = "SLOW"

Token_2 operation = AND
b_Negate = false
p_linguisticVariable= DISTANCE 
linguisticValueName = "CLOSE"

Token_3
operation =EMPTY
b_Negate = false
p_linguisticVariable= SIGNAL 
linguisticValueName = BRAKE"

Please note, TOKEN_3 is the output token! Every CFuzzyRule must have a token with an output variable!!!

The method CalculateTokenOutputValue is used for getting an input value from the hash map h_fuzzyInputHashMap and calling a method for calculating the linguistic value. The input variable h_fuzzyInputHashMap is a hash_map filled with objects (class CFuzzyInput). In CFuzzyInput is stored input values for specific linguistic variables. CFuzzyInput will be explained later.

C++
//Calculate token value
double CalculateTokenOutputValue(hash_map <string, CFuzzyInput*> h_fuzzyInputHashMap)
{
    hash_map <string,> :: const_iterator iterator;
    //Find linguistic variable
    iterator = h_fuzzyInputHashMap.find(p_linguisticVariable->ToString());
    CFuzzyInput* temp = (CFuzzyInput*)iterator->second;
    double input = temp->GetInputValue();
    //Calculate linguistic value
    return p_linguisticVariable->CalculateLinguisticValueByName(linguisticValueName, input);
}

CFuzzyInput

UML_fuzzy_input.jpg

CFuzzyInput has only two variables, variableName for the linguistic variable name and inputValue (type double) for the value. For example, CFuzzyInput could be variableName = "SPEED" and inputValue = "43" (km/h).

CFuzzyReasoner

UML_fuzzy_reasoner.jpg

CFuzzyReasoner is the heart of the FuzzyLogic system. It's used for calculating fuzzy rule output values and the final defuzzyfication. The CFuzzyReasoner variable v_fuzzyRulesVector is used for storing fuzzy rules (CFuzzyRule). Let's see the most important method in FuzzyLogic, FuzzyRuleReasoner. The method takes as inputs a fuzzy rule and a hash map filled with fuzzy inputs. Every fuzzy rule token is checked if it is an output variable or not. If not, output variables are calculated linguistic values of the current token variable. Then we check if the token is negated or not. If the linguistic variable is not first in the fuzzy rule and it's not an output variable then we should have an operator different than EMPTY. Only the first variable in the fuzzy rule must have the operator EMPTY (for the output value, operator isn't important). For the output value we just set a new value (resetVal) and return a pointer to the output token.

C++
//Start fuzzy rule reasoner
CFuzzyRuleToken* FuzzyRuleReasoner(CFuzzyRule* fuzzyRule, 
                 hash_map <string, CFuzzyInput*> h_fuzzyInputs)
{
    double resultVal = 0;
    for(unsigned int i = 0; i < fuzzyRule->GetFuzzyRuleTokens().size(); i++)
    {
        //Get fuzzy rule token
        CFuzzyRuleToken* token = fuzzyRule->GetFuzzyRuleTokens()[i];
            if(token->IsOutput())
        {
            //Update output object
            token->UpdateTokenValue(resultVal);
            //Return result token
            return token;
        }
        else
        {
            double tokenVal = token->CalculateTokenOutputValue(h_fuzzyInputs);
            token->UpdateTokenValue(tokenVal);
            if(token->IsNegated())
                tokenVal = 1 - tokenVal;    //Negate value
            if(i == 0)
                resultVal = tokenVal;        //Set value
            else if(token->IsOrOperator())
                resultVal = CFuzzyCalculator::OR(resultVal, tokenVal); //OR operator
            else if(token->IsAndOperator())
                resultVal = CFuzzyCalculator::AND(resultVal, tokenVal); //AND operator
        }
    }
    //This won't happends saso
    assert(NULL);
    return NULL;
}

The following method just calls reasoning for every fuzzy rule in the FuzzyLogic API.

C++
//Start reasoner for fuzzy rulles
CFuzzyRuleToken* CalculateFuzzyRules(hash_map <string, CFuzzyInput*> h_fuzzyInputs)
{
    //Reset all values
    CFuzzyRuleToken* outputObject;
    //Calculate all fuzzy rules
    for(unsigned int i = 0; i < v_fuzzyRulesVector.size(); i++)
    {
        //Glede na izhodno vrednost izhodne spremeljivke naredi update izhodnega objekta
        outputObject = FuzzyRuleReasoner((CFuzzyRule*)v_fuzzyRulesVector[i], h_fuzzyInputs);
    }
    //Return output object
    return outputObject;
}

The last step in Fuzzy Logic reasoning is Defuzzyfication. It's implemented in the next two methods.

C++
//Defuzzyfication
double Defuzzy(CFuzzyRuleToken *outputToken)
{
    //For every output value
    CLinguisticVariable* lVar = outputToken->GetLinguisticVariable();
    vector<LinguisticValue*> valuesList = lVar->GetLinguisticValuesList();

    double upEqualation = 0;
    double downEqualation = 0;
    //Calculating defuzzy value
    for(unsigned int i = 0; i < valuesList.size(); i++)
    {
        LinguisticValue* val = valuesList.at(i);
        upEqualation += val->GetLinguisticValue()
            * CalculateTrapezoidBalance(val->GetA(), val->GetB(), val->GetC(), val->GetD()); 
        downEqualation += val->GetLinguisticValue();
    }
    //Return output value of system
    if(downEqualation == 0)
        return 0;
    return upEqualation / downEqualation;
}

//Calculating surface of trapezoid 
double CalculateTrapezoidBalance(double A, double B, double C, double D)
{
    return ((1 / (B - A)) * (2 * pow(B,3) - 3 * A * pow(B,2) + pow(A,3)) + 
        3 * (pow(C,2) - pow(B,2)) + (1 / (D-C)) * (2 * pow(C,3) - 3 * D * pow(C,2) + pow(D,3)))
    / (3 * (B - A) + 6 * (C - B) + 3 * (D - C));
}

How to use it?

Probably you are wondering how to use this FuzzyLogic API. It's not so complicated. For making your life easier, I have made three simple examples. The first one is for a Fuzzy Cruise Control, the second for a basketball player, and the last one for a ski jumper. All examples are in the class called CExperiments. Let's see the simple basketball player example. We create three linguistic variables, then we add linguistic values to the linguistic variables. With the linguistic variables, we create two rules and add the rules to FuzzyLogic.

C++
void BuildBasketballPlayersRules()
{
    //Method for building necessary rules for Basketball players
    CLinguisticVariable *height = new CLinguisticVariable(false,"Height");
    CLinguisticVariable *age = new CLinguisticVariable(false,"Age");
    CLinguisticVariable *basketball_player = 
              new CLinguisticVariable(true,"Basketball_player");

    //Add linguistic values to variable
    height->AddLinguisticValue(new LinguisticValue("Low_height",150,150,170,180));
    height->AddLinguisticValue(new LinguisticValue("Average_height",170,180,185,190));
    height->AddLinguisticValue(new LinguisticValue("Tall",185,195,210,210));

    age->AddLinguisticValue(new LinguisticValue("Young",10,10,25,30));
    age->AddLinguisticValue(new LinguisticValue("Average_young",25,35,40,45));
    age->AddLinguisticValue(new LinguisticValue("Old",50,60,80,80));
    age->AddLinguisticValue(new LinguisticValue("Average_old",40,45,50,55));
                
    basketball_player->AddLinguisticValue(new LinguisticValue("Suitable",0.5,0.6,1,1));
    basketball_player->AddLinguisticValue(new LinguisticValue("Unsuitable ",0,0,0.4,0.5));

    //Rule1
    //IF age = Young AND height = Tall THEN basketball_player = "Suitable"
    CFuzzyRule* rule1 = new CFuzzyRule();
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, age,"Young"));
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,AND, height,"Tall"));
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, basketball_player,"Suitable"));

    //Rule2
    //IF age = "Old" THEN basketball_player = "Unsuitable"
    CFuzzyRule* rule2 = new CFuzzyRule();
    rule2->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, age,"Old"));
    rule2->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, 
                             basketball_player,"Unsuitable "));

    //Add rules
    fr->AddFuzzyRule(rule1);
    fr->AddFuzzyRule(rule2);

    //Add linguistic variables, if you want to obtain
    //more informations about fuzzy calculations
    AddLinguisticVariables(height);
    AddLinguisticVariables(age);
    AddLinguisticVariables(basketball_player);
}

How to execute the example

With the next sample you should understand how to use the class CExperiment.

C++
int _tmain(int argc, _TCHAR* argv[])
{
    //Create instance of CExperiments.
    CExperiments* experiment = new CExperiments();
    //Create rules
    experiment->BuildCruiseControlRules();

    //Hash_map for storing inputs
    hash_map <string, CFuzzyInput*> h_fuzzyInputs;
    
    //Put some values to inputs
    CFuzzyInput* inputRazdalja = new CFuzzyInput("Distance", 1.8);
    CFuzzyInput* inputPriblizevanje = new CFuzzyInput("Approaching", 15);
    CFuzzyInput* inputHitrost = new CFuzzyInput("Speed", -8);
    
    //Insert inputs to the hash_map
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputRazdalja->GetVariableName(), inputRazdalja));
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputPriblizevanje->GetVariableName(), inputPriblizevanje));
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputHitrost->GetVariableName(), inputHitrost));

    //Calculate fuzzy Logic output signal
    double outputSignal = experiment->CalculateFuzzyRules(h_fuzzyInputs);
    
    //Get some additional informations from fuzzy logic 
    double t = experiment->GetC_ByName("Distance","Average");
    double v1 = experiment->GetLinguisticVariableValue("Signal","Brake");
    double v2 = experiment->GetLinguisticVariableValue("Signal","Maintain");
    double v3 = experiment->GetLinguisticVariableValue("Signal","Accelerate");

    double v4 = experiment->GetLinguisticVariableValue("Distance","Low");
    double v5 = experiment->GetLinguisticVariableValue("Distance","Average");
    double v6 = experiment->GetLinguisticVariableValue("Distance","High");

    double v7 = experiment->GetLinguisticVariableValue("Approaching","Slow");
    double v8 = experiment->GetLinguisticVariableValue("Approaching","Average");
    double v9 = experiment->GetLinguisticVariableValue("Approaching","Fast");

    double v10 = experiment->GetLinguisticVariableValue("Speed","Slow");
    double v11 = experiment->GetLinguisticVariableValue("Speed","Acceptable");
    double v12 = experiment->GetLinguisticVariableValue("Speed","Fast");

    //YOU HAVE TO RESET TOKEN VALUES BEFORE NEW CALCULATIONS!!!
    experiment->ResetTokenValues();
    delete experiment;

    return 0;
}

Too complicated?

For even easier defining of rules and attributes, I made a simple DSL (domain specific language).

CFuzzyLanguageParser

The only class of the project CFuzzyLogicDSL is CFuzzyLanguageParser. This class is used for parsing fuzzy rules and attributes. The input value is the path to the file with fuzzy rules / attributes, and output values are updated rules and attributes in CExperiment (you have to put a CExperiment* pointer to the CFuzzyLanguageParser constructor). Let's see the input file format. Please, please be very accurate with spacing. If you forget spaces, the parser won't work (I have tried to keep a simple implementation). You can define the attributes in this format:

Linguistic_Variable_Name Input/Output ValueName_0 (A,B,C,D) ValueName_N (A,B,C,D)

and rules:

IF Linguistic_Variable_Name = Linguistic_Value_Name AND ... 
  AND Linguistic_Variable_Name NOT Linguistic_Value_Name THEN 
  Linguistic_Variable_Name = Linguistic_Value_Name

Also there is the possibility to write comments, using:

% This is comment

The next step is code explanation. The "main" method is Parse. Simple reading line by line, splitting tokens by space, then checking if is a commend, empty line, rule, or attribute.

C++
void Parse(char* filePath)
{
    ifstream ifs(filePath);
    string line;
    while( getline( ifs, line ) )
    {
        vector<string> tokens = SplitBy(line,' ');
        //Skip lines without text
        if(tokens.size() > 0)
        {
            //Skip comments
            if(!IsComment(tokens[0]))
            {
                if(IsRule(tokens[0]))
                    ParseFuzzyRule(tokens);
                else
                    ParseFuzzyAtribute(tokens);
            }
        }
    }
    //Close stream
    ifs.close();
}

Here are the methods for checking if a line is a comment or rule:

C++
//Check is line is comment
bool IsComment(string token)
{
    //Return true if is comment
    if(token.at(0) == '%')
        return true;
    return false;
}

bool IsRule(string token)
{
    //Return true if is rule
    if(token == "IF")
        return true;
    return false;
}

How do we parse rules and attributes? Check the next two simple methods. ParseFuzzyRule just goes over all tokens (we don't need to process the first token, its should be "IF") and check if it is an operator token (AND / OR) and set the appropriate operation. Then we check if there is negation or we are on the "set" (token "="). If this condition is true, we just have to set the previous and next token, create a new CFuzzyRuleToken, and add it to the rule.

C++
void ParseFuzzyRule(vector<string> ruleTokens)
{
    //Rule example
    //IF distance = low AND approaching NOT slow THEN signal=breake
    //SKIP IF token
    bool negated = false;
    string leftSide;
    string rightSide;
    Operation operation = EMPTY; 
    CFuzzyRule* rule = new CFuzzyRule();

    for(unsigned int i = 1; i < ruleTokens.size(); i++)
    {
        //Check for operator
        //OPERATORS =, Operator(AND, OR), NOT, THEN
        if((ruleTokens[i] == "AND") || (ruleTokens[i] == "OR"))
        {
            if(ruleTokens[i] == "AND")
                operation = AND;
            else
                operation = OR;
        }
        else if((ruleTokens[i] == "=") || (ruleTokens[i] == "NOT"))
        {
            leftSide = ruleTokens[i - 1];
                //Next token is right side
            if(ruleTokens[i] == "NOT")
                negated = true;
            rightSide = ruleTokens[++i];
            
            //ADD TOKEN TO RULE
            CLinguisticVariable* linVar = 
              fuzzyInterface->FindLinguisticVariableByName(leftSide);
            assert(linVar != NULL);
            rule->AddTokenToRule(
              new CFuzzyRuleToken(negated,operation,linVar,rightSide));
            //RESET
            operation = EMPTY;
            negated = false;
        }
    }
    fuzzyInterface->AddFuzzyRule(rule);
}

Now, parsing the attributes. The first token is always the linguistic variable name, the second token should be information of the attribute. There can be N linguistic values. We have to go over all tokens and parsing is Input or Output. With that information, we can create CLinguisticVariable. Then we can start to parse the linguistic values. We have to go over all the remaining tokens. The next token has to be the linguistic value name, followed by the linguistic values (method ParseLinguisticValues). When all linguistic values are parsed, we can add LinguisticVariable to the list (in CExperiment).

C++
void ParseFuzzyAtribute(vector<string> attTokens)
{
    //Distance Input Low (0,0,1,2) Average (1,2,3,4) High (3,4,5,5)
    //Linguistic variable name
    //Input or output 
    //Linguistic value name
    //Values A,B,C,D
    string linguisticVariableName = attTokens.at(0);
    bool output = true;
    if(attTokens.at(1) == "Input")
        output = false;
    //Creating new linguistic variable
    CLinguisticVariable *linguisticVariable = 
       new CLinguisticVariable(output,linguisticVariableName);

    //Adding linguistic values to variable
    for(unsigned int i = 2; i < attTokens.size(); i++)
    {
        string linguisticValueName = attTokens.at(i++);
        vector<double> linguisticValues = ParseLinguisticValues(attTokens.at(i));
        //Check for error!
        assert(linguisticValues.size() == 4);
        LinguisticValue* linValue = new LinguisticValue(linguisticValueName,
            linguisticValues[0],
            linguisticValues[1],
            linguisticValues[2],
            linguisticValues[3]);
        linguisticVariable->AddLinguisticValue(linValue);
    }
    fuzzyInterface->AddLinguisticVariables(linguisticVariable);
}

vector<double> ParseLinguisticValues(string token)
{
    vector<double> linguisticValues;
    //Remove first and last char
    token = token.erase(0,1);
    token = token.erase(token.size() - 1, 1);
    vector<string> values = SplitBy(token, ',');

    //Values size should be 4!
    for(unsigned int i = 0; i < values.size(); i++)
        linguisticValues.push_back(strtod(values.at(i).c_str(),NULL));
    return linguisticValues;
}

Input file example

%ATTRIBUTES 
Distance Input Low (0,0,1,2) Average (1,2,3,4) High (3,4,5,5)
Approaching Input Slow (0,0,10,20) Average (10,20,30,40) Fast (30,40,50,50)
Speed Input Slow (-50,-50,-10,0) Acceptable (-10,0,0,10) Fast (0,10,50,50)
Signal Output Brake (-5,-5,-2,-1) Maintain (-2,-1,1,2) Accelerate (1,2,5,5)

%RULES
IF Distance = Low AND Approaching NOT Slow THEN Signal = Brake
IF Distance = Average AND Approaching = Fast THEN Signal = Brake
IF Speed = Fast THEN Signal = Brake
IF Distance NOT Low AND Approaching = Average THEN Signal = Maintain
IF Distance = High AND Approaching NOT Fast AND Speed = Acceptable THEN Signal = Maintain
IF Distance = Average AND Speed = Slow THEN Signal = Maintain
IF Distance = High AND Speed = Slow THEN Signal = Accelerate

I want to see it!

My friend Jan Bezget made a really cool 3D visualization (using OGRE) of the Cruise Control example. You can find his visualization here: Click me!

ScreenShot.jpg

Summary

You have now seen that implementation of fuzzy logic can be very simple and effective. It's impressive that an easy AI approach such us fuzzy logic can be so useful. Canon used fuzzy logic for autofocusing, it's often used also in air conditioners, even in the Sendai subway system! I hope that you liked my article and my FuzzyLogic API implementation. Thank you for reading :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)