/* Author: Juan Rada-Vilela, Ph.D. Copyright (C) 2010-2014 FuzzyLite Limited All rights reserved This file is part of fuzzylite. fuzzylite is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. fuzzylite is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with fuzzylite. If not, see . fuzzyliteâ„¢ is a trademark of FuzzyLite Limited. */ #include "fl/rule/Antecedent.h" #include "fl/Engine.h" #include "fl/factory/HedgeFactory.h" #include "fl/factory/FactoryManager.h" #include "fl/hedge/Any.h" #include "fl/hedge/Hedge.h" #include "fl/norm/SNorm.h" #include "fl/norm/TNorm.h" #include "fl/rule/Expression.h" #include "fl/rule/Rule.h" #include "fl/term/Accumulated.h" #include "fl/term/Function.h" #include "fl/term/Term.h" #include "fl/variable/InputVariable.h" #include "fl/variable/OutputVariable.h" #include #include namespace fl { Antecedent::Antecedent() : _text(""), _expression(fl::null) { } Antecedent::~Antecedent() { unload(); } void Antecedent::setText(const std::string& text) { this->_text = text; } std::string Antecedent::getText() const { return this->_text; } Expression* Antecedent::getExpression() const { return this->_expression; } bool Antecedent::isLoaded() const { return this->_expression != fl::null; } scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const { return this->activationDegree(conjunction, disjunction, this->_expression); } scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction, const Expression* node) const { if (not isLoaded()) { throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } const Proposition* proposition = dynamic_cast (node); if (proposition) { if (not proposition->variable->isEnabled()) { return 0.0; } if (not proposition->hedges.empty()) { //if last hedge is "Any", apply hedges in reverse order and return degree std::vector::const_reverse_iterator rit = proposition->hedges.rbegin(); if (dynamic_cast (*rit)) { scalar result = (*rit)->hedge(fl::nan); while (++rit != proposition->hedges.rend()) { result = (*rit)->hedge(result); } return result; } } scalar result = fl::nan; if (InputVariable * inputVariable = dynamic_cast (proposition->variable)) { result = proposition->term->membership(inputVariable->getInputValue()); } else if (OutputVariable * outputVariable = dynamic_cast (proposition->variable)) { result = outputVariable->fuzzyOutput()->activationDegree(proposition->term); } for (std::vector::const_reverse_iterator rit = proposition->hedges.rbegin(); rit != proposition->hedges.rend(); ++rit) { result = (*rit)->hedge(result); } return result; } //if node is an operator const Operator* fuzzyOperator = dynamic_cast (node); if (not (fuzzyOperator->left and fuzzyOperator->right)) { std::ostringstream ex; ex << "[syntax error] left and right operands must exist"; throw fl::Exception(ex.str(), FL_AT); } if (fuzzyOperator->name == Rule::andKeyword()) { if (not conjunction) throw fl::Exception("[conjunction error] " "the following rule requires a conjunction operator:\n" + _text, FL_AT); return conjunction->compute( this->activationDegree(conjunction, disjunction, fuzzyOperator->left), this->activationDegree(conjunction, disjunction, fuzzyOperator->right)); } if (fuzzyOperator->name == Rule::orKeyword()) { if (not disjunction) throw fl::Exception("[disjunction error] " "the following rule requires a disjunction operator:\n" + _text, FL_AT); return disjunction->compute( this->activationDegree(conjunction, disjunction, fuzzyOperator->left), this->activationDegree(conjunction, disjunction, fuzzyOperator->right)); } std::ostringstream ex; ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized"; throw fl::Exception(ex.str(), FL_AT); } void Antecedent::unload() { if (_expression) { delete _expression; _expression = fl::null; } } void Antecedent::load(fl::Rule* rule, const Engine* engine) { load(_text, rule, engine); } void Antecedent::load(const std::string& antecedent, fl::Rule* rule, const Engine* engine) { FL_DBG("Antecedent: " << antecedent); unload(); this->_text = antecedent; if (fl::Op::trim(antecedent).empty()) { throw fl::Exception("[syntax error] antecedent is empty", FL_AT); } /* Builds an proposition tree from the antecedent of a fuzzy rule. The rules are: 1) After a variable comes 'is', 2) After 'is' comes a hedge or a term 3) After a hedge comes a hedge or a term 4) After a term comes a variable or an operator */ Function function; std::string postfix = function.toPostfix(antecedent); FL_DBG("Postfix: " << postfix); std::stringstream tokenizer(postfix); std::string token; enum FSM { S_VARIABLE = 1, S_IS = 2, S_HEDGE = 4, S_TERM = 8, S_AND_OR = 16 }; int state = S_VARIABLE; std::stack expressionStack; Proposition* proposition = fl::null; try { while (tokenizer >> token) { if (state bitand S_VARIABLE) { Variable* variable = fl::null; if (engine->hasInputVariable(token)) variable = engine->getInputVariable(token); else if (engine->hasOutputVariable(token)) variable = engine->getOutputVariable(token); if (variable) { proposition = new Proposition; proposition->variable = variable; expressionStack.push(proposition); state = S_IS; FL_DBG("Token <" << token << "> is variable"); continue; } } if (state bitand S_IS) { if (token == Rule::isKeyword()) { state = S_HEDGE bitor S_TERM; FL_DBG("Token <" << token << "> is keyword"); continue; } } if (state bitand S_HEDGE) { Hedge* hedge = rule->getHedge(token); if (not hedge) { HedgeFactory* factory = FactoryManager::instance()->hedge(); if (factory->hasConstructor(token)) { hedge = factory->constructObject(token); rule->addHedge(hedge); } } if (hedge) { proposition->hedges.push_back(hedge); if (dynamic_cast (hedge)) { state = S_VARIABLE bitor S_AND_OR; } else { state = S_HEDGE bitor S_TERM; } FL_DBG("Token <" << token << "> is hedge"); continue; } } if (state bitand S_TERM) { if (proposition->variable->hasTerm(token)) { proposition->term = proposition->variable->getTerm(token); state = S_VARIABLE bitor S_AND_OR; FL_DBG("Token <" << token << "> is term"); continue; } } if (state bitand S_AND_OR) { if (token == Rule::andKeyword() or token == Rule::orKeyword()) { if (expressionStack.size() < 2) { std::ostringstream ex; ex << "[syntax error] logical operator <" << token << "> expects two operands," << "but found <" << expressionStack.size() << "> in antecedent"; throw fl::Exception(ex.str(), FL_AT); } Operator* fuzzyOperator = new Operator; fuzzyOperator->name = token; fuzzyOperator->right = expressionStack.top(); expressionStack.pop(); fuzzyOperator->left = expressionStack.top(); expressionStack.pop(); expressionStack.push(fuzzyOperator); state = S_VARIABLE bitor S_AND_OR; FL_DBG("Subtree: " << fuzzyOperator->toString() << "(" << fuzzyOperator->left->toString() << ") " << "(" << fuzzyOperator->right->toString() << ")"); continue; } } //If reached this point, there was an error if ((state bitand S_VARIABLE) or (state bitand S_AND_OR)) { std::ostringstream ex; ex << "[syntax error] antecedent expected variable or logical operator, but found <" << token << ">"; throw fl::Exception(ex.str(), FL_AT); } if (state bitand S_IS) { std::ostringstream ex; ex << "[syntax error] antecedent expected keyword <" << Rule::isKeyword() << ">, but found <" << token << ">"; throw fl::Exception(ex.str(), FL_AT); } if ((state bitand S_HEDGE) or (state bitand S_TERM)) { std::ostringstream ex; ex << "[syntax error] antecedent expected hedge or term, but found <" << token << ">"; throw fl::Exception(ex.str(), FL_AT); } std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> in antecedent"; throw fl::Exception(ex.str(), FL_AT); } if (not ((state bitand S_VARIABLE) or (state bitand S_AND_OR))) { //only acceptable final state if (state bitand S_IS) { std::ostringstream ex; ex << "[syntax error] antecedent expected keyword <" << Rule::isKeyword() << "> after <" << token << ">"; throw fl::Exception(ex.str(), FL_AT); } if ((state bitand S_HEDGE) or (state bitand S_TERM)) { std::ostringstream ex; ex << "[syntax error] antecedent expected hedge or term after <" << token << ">"; throw fl::Exception(ex.str(), FL_AT); } } if (expressionStack.size() != 1) { std::vector errors; while (expressionStack.size() > 1) { Expression* expression = expressionStack.top(); expressionStack.pop(); errors.push_back(expression->toString()); delete expression; } std::ostringstream ex; ex << "[syntax error] unable to parse the following expressions in antecedent <" << Op::join(errors, " ") << ">"; throw fl::Exception(ex.str(), FL_AT); } } catch (...) { for (std::size_t i = 0; i < expressionStack.size(); ++i) { delete expressionStack.top(); expressionStack.pop(); } throw; } this->_expression = expressionStack.top(); } std::string Antecedent::toString() const { return toInfix(this->_expression); } std::string Antecedent::toPrefix(const Expression* node) const { if (not isLoaded()) { throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } if (not node) node = this->_expression; if (dynamic_cast (node)) { return node->toString(); } const Operator* fuzzyOperator = dynamic_cast (node); std::stringstream ss; ss << fuzzyOperator->toString() << " " << toPrefix(fuzzyOperator->left) << " " << toPrefix(fuzzyOperator->right) << " "; return ss.str(); } std::string Antecedent::toInfix(const Expression* node) const { if (not isLoaded()) { throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } if (not node) node = this->_expression; if (dynamic_cast (node)) { return node->toString(); } const Operator* fuzzyOperator = dynamic_cast (node); std::stringstream ss; ss << toInfix(fuzzyOperator->left) << " " << fuzzyOperator->toString() << " " << toInfix(fuzzyOperator->right) << " "; return ss.str(); } std::string Antecedent::toPostfix(const Expression* node) const { if (not isLoaded()) { throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } if (not node) node = this->_expression; if (dynamic_cast (node)) { return node->toString(); } const Operator* fuzzyOperator = dynamic_cast (node); std::stringstream ss; ss << toPostfix(fuzzyOperator->left) << " " << toPostfix(fuzzyOperator->right) << " " << fuzzyOperator->toString() << " "; return ss.str(); } }