diff options
Diffstat (limited to 'fuzzylite/src/rule')
-rw-r--r-- | fuzzylite/src/rule/Antecedent.cpp | 293 | ||||
-rw-r--r-- | fuzzylite/src/rule/Consequent.cpp | 117 | ||||
-rw-r--r-- | fuzzylite/src/rule/Expression.cpp | 48 | ||||
-rw-r--r-- | fuzzylite/src/rule/Rule.cpp | 182 | ||||
-rw-r--r-- | fuzzylite/src/rule/RuleBlock.cpp | 115 |
5 files changed, 427 insertions, 328 deletions
diff --git a/fuzzylite/src/rule/Antecedent.cpp b/fuzzylite/src/rule/Antecedent.cpp index aa18a68..5f0b0be 100644 --- a/fuzzylite/src/rule/Antecedent.cpp +++ b/fuzzylite/src/rule/Antecedent.cpp @@ -1,25 +1,17 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> 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. + the terms of the FuzzyLite License included with the software. - 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 <http://www.gnu.org/licenses/>. - - fuzzylite™ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/rule/Antecedent.h" @@ -28,29 +20,21 @@ #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/term/Aggregated.h" #include "fl/variable/InputVariable.h" #include "fl/variable/OutputVariable.h" -#include <algorithm> #include <stack> - namespace fl { Antecedent::Antecedent() - : _text(""), _expression(fl::null) { - } + : _text(""), _expression(fl::null) { } Antecedent::~Antecedent() { - unload(); + _expression.reset(fl::null); } void Antecedent::setText(const std::string& text) { @@ -62,24 +46,29 @@ namespace fl { } Expression* Antecedent::getExpression() const { - return this->_expression; + return this->_expression.get(); + } + + void Antecedent::setExpression(Expression* expression) { + this->_expression.reset(expression); } bool Antecedent::isLoaded() const { - return this->_expression != fl::null; + return _expression.get() != fl::null; } scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const { - return this->activationDegree(conjunction, disjunction, this->_expression); + return this->activationDegree(conjunction, disjunction, _expression.get()); } 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); + throw Exception("[antecedent error] antecedent <" + getText() + "> is not loaded", FL_AT); } - const Proposition* proposition = dynamic_cast<const Proposition*> (node); - if (proposition) { + const Expression::Type expression = node->type(); + if (expression == Expression::Proposition) { + const Proposition* proposition = static_cast<const Proposition*> (node); if (not proposition->variable->isEnabled()) { return 0.0; } @@ -96,62 +85,147 @@ namespace fl { } } scalar result = fl::nan; - if (InputVariable * inputVariable = dynamic_cast<InputVariable*> (proposition->variable)) { - result = proposition->term->membership(inputVariable->getInputValue()); - } else if (OutputVariable * outputVariable = dynamic_cast<OutputVariable*> (proposition->variable)) { - result = outputVariable->fuzzyOutput()->activationDegree(proposition->term); + Variable::Type variableType = proposition->variable->type(); + if (variableType == Variable::Input) { + result = proposition->term->membership(proposition->variable->getValue()); + } else if (variableType == Variable::Output) { + result = static_cast<OutputVariable*> (proposition->variable) + ->fuzzyOutput()->activationDegree(proposition->term); } - for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin(); - rit != proposition->hedges.rend(); ++rit) { - result = (*rit)->hedge(result); + + if (not proposition->hedges.empty()) { + for (std::vector<Hedge*>::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<const Operator*> (node); - if (not (fuzzyOperator->left and fuzzyOperator->right)) { + if (expression == Expression::Operator) { + const Operator* fuzzyOperator = static_cast<const Operator*> (node); + if (not (fuzzyOperator->left and fuzzyOperator->right)) { + std::ostringstream ex; + ex << "[syntax error] left and right operands must exist"; + throw Exception(ex.str(), FL_AT); + } + if (fuzzyOperator->name == Rule::andKeyword()) { + if (not conjunction) throw 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 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] left and right operands must exist"; - throw fl::Exception(ex.str(), FL_AT); + ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized"; + throw Exception(ex.str(), FL_AT); + + } else { + std::ostringstream ss; + ss << "[antecedent error] expected a Proposition or Operator, but found <"; + if (node) ss << node->toString(); + ss << ">"; + throw Exception(ss.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)); + } + + + Complexity Antecedent::complexity(const TNorm* conjunction, const SNorm* disjunction) const { + return complexity(conjunction, disjunction, _expression.get()); + } + + Complexity Antecedent::complexity(const TNorm* conjunction, const SNorm* disjunction, + const Expression* node) const { + if (not isLoaded()) { + return Complexity(); } - 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)); + Complexity result; + const Expression::Type expression = node->type(); + if (expression == Expression::Proposition) { + const Proposition* proposition = static_cast<const Proposition*> (node); + if (not proposition->variable->isEnabled()) { + return result; + } + + if (not proposition->hedges.empty()) { + //if last hedge is "Any", apply hedges in reverse order and return degree + std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin(); + if (dynamic_cast<Any*> (*rit)) { + result += (*rit)->complexity(); + while (++rit != proposition->hedges.rend()) { + result = (*rit)->complexity(); + } + return result; + } + } + Variable::Type variableType = proposition->variable->type(); + if (variableType == Variable::Input) { + result += proposition->term->complexity(); + } else if (variableType == Variable::Output) { + OutputVariable* outputVariable = static_cast<OutputVariable*> (proposition->variable); + result += outputVariable->fuzzyOutput()->complexityOfActivationDegree(); + } + + if (not proposition->hedges.empty()) { + for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin(); + rit != proposition->hedges.rend(); ++rit) { + result += (*rit)->complexity(); + } + } + return result; } - std::ostringstream ex; - ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized"; - throw fl::Exception(ex.str(), FL_AT); + //if node is an operator + if (expression == Expression::Operator) { + const Operator* fuzzyOperator = static_cast<const Operator*> (node); + if (not (fuzzyOperator->left and fuzzyOperator->right)) { + std::ostringstream ex; + ex << "[syntax error] left and right operands must exist"; + throw Exception(ex.str(), FL_AT); + } + if (fuzzyOperator->name == Rule::andKeyword()) { + if (conjunction) { + result += conjunction->complexity(); + } + result += complexity(conjunction, disjunction, fuzzyOperator->left) + + complexity(conjunction, disjunction, fuzzyOperator->right); + return result; + } + if (fuzzyOperator->name == Rule::orKeyword()) { + if (disjunction) { + result += disjunction->complexity(); + } + result += complexity(conjunction, disjunction, fuzzyOperator->left) + + complexity(conjunction, disjunction, fuzzyOperator->right); + return result; + } + } + return Complexity(); } void Antecedent::unload() { - if (_expression) { - delete _expression; - _expression = fl::null; - } + _expression.reset(fl::null); } - void Antecedent::load(fl::Rule* rule, const Engine* engine) { - load(_text, rule, engine); + void Antecedent::load(const Engine* engine) { + load(getText(), engine); } - void Antecedent::load(const std::string& antecedent, fl::Rule* rule, const Engine* engine) { + void Antecedent::load(const std::string& antecedent, 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); + setText(antecedent); + if (Op::trim(antecedent).empty()) { + throw Exception("[syntax error] antecedent is empty", FL_AT); } /* Builds an proposition tree from the antecedent of a fuzzy rule. @@ -179,8 +253,10 @@ namespace fl { 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 (engine->hasInputVariable(token)) + variable = engine->getInputVariable(token); + else if (engine->hasOutputVariable(token)) + variable = engine->getOutputVariable(token); if (variable) { proposition = new Proposition; proposition->variable = variable; @@ -201,15 +277,9 @@ namespace fl { } 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) { + HedgeFactory* factory = FactoryManager::instance()->hedge(); + if (factory->hasConstructor(token)) { + Hedge* hedge = factory->constructObject(token); proposition->hedges.push_back(hedge); if (dynamic_cast<Any*> (hedge)) { state = S_VARIABLE bitor S_AND_OR; @@ -236,7 +306,7 @@ namespace fl { 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); + throw Exception(ex.str(), FL_AT); } Operator* fuzzyOperator = new Operator; fuzzyOperator->name = token; @@ -258,33 +328,33 @@ namespace fl { 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); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> in antecedent"; - throw fl::Exception(ex.str(), FL_AT); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } } @@ -299,7 +369,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] unable to parse the following expressions in antecedent <" << Op::join(errors, " ") << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } catch (...) { for (std::size_t i = 0; i < expressionStack.size(); ++i) { @@ -308,59 +378,68 @@ namespace fl { } throw; } - this->_expression = expressionStack.top(); + setExpression(expressionStack.top()); } std::string Antecedent::toString() const { - return toInfix(this->_expression); + return toInfix(getExpression()); } std::string Antecedent::toPrefix(const Expression* node) const { if (not isLoaded()) { - throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); if (dynamic_cast<const Proposition*> (node)) { return node->toString(); } - const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node); std::stringstream ss; - ss << fuzzyOperator->toString() << " " - << toPrefix(fuzzyOperator->left) << " " - << toPrefix(fuzzyOperator->right) << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << fuzzyOperator->toString() << " " + << toPrefix(fuzzyOperator->left) << " " + << toPrefix(fuzzyOperator->right) << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } 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); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); if (dynamic_cast<const Proposition*> (node)) { return node->toString(); } - const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node); std::stringstream ss; - ss << toInfix(fuzzyOperator->left) << " " - << fuzzyOperator->toString() << " " - << toInfix(fuzzyOperator->right) << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << toInfix(fuzzyOperator->left) << " " + << fuzzyOperator->toString() << " " + << toInfix(fuzzyOperator->right) << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } 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); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); if (dynamic_cast<const Proposition*> (node)) { return node->toString(); } - const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node); std::stringstream ss; - ss << toPostfix(fuzzyOperator->left) << " " - << toPostfix(fuzzyOperator->right) << " " - << fuzzyOperator->toString() << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << toPostfix(fuzzyOperator->left) << " " + << toPostfix(fuzzyOperator->right) << " " + << fuzzyOperator->toString() << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } return ss.str(); } diff --git a/fuzzylite/src/rule/Consequent.cpp b/fuzzylite/src/rule/Consequent.cpp index 28d3390..a90b1a9 100644 --- a/fuzzylite/src/rule/Consequent.cpp +++ b/fuzzylite/src/rule/Consequent.cpp @@ -1,25 +1,17 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> 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. + the terms of the FuzzyLite License included with the software. - 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 <http://www.gnu.org/licenses/>. - - fuzzylite™ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/rule/Consequent.h" @@ -27,24 +19,21 @@ #include "fl/Engine.h" #include "fl/factory/HedgeFactory.h" #include "fl/factory/FactoryManager.h" -#include "fl/hedge/Hedge.h" #include "fl/hedge/Any.h" -#include "fl/norm/TNorm.h" #include "fl/rule/Expression.h" #include "fl/rule/Rule.h" -#include "fl/term/Accumulated.h" -#include "fl/term/Activated.h" +#include "fl/term/Aggregated.h" #include "fl/variable/OutputVariable.h" -#include <algorithm> - namespace fl { - Consequent::Consequent() { - } + Consequent::Consequent() { } Consequent::~Consequent() { - unload(); + for (std::size_t i = 0; i < _conclusions.size(); ++i) { + delete _conclusions.at(i); + } + _conclusions.clear(); } std::string Consequent::getText() const { @@ -59,9 +48,29 @@ namespace fl { return this->_conclusions; } - void Consequent::modify(scalar activationDegree, const TNorm* activation) { + std::vector<Proposition*>& Consequent::conclusions() { + return this->_conclusions; + } + + Complexity Consequent::complexity(const TNorm* implication) const { + Complexity result; + result.comparison(1); + + for (std::size_t i = 0; i < _conclusions.size(); ++i) { + Proposition* proposition = _conclusions.at(i); + result.comparison(2); + for (std::size_t h = 0; h < proposition->hedges.size(); ++h) { + result += proposition->hedges.at(h)->complexity(); + } + result += static_cast<OutputVariable*> (proposition->variable) + ->complexity(Activated(proposition->term, fl::nan, implication)); + } + return result; + } + + void Consequent::modify(scalar activationDegree, const TNorm* implication) { if (not isLoaded()) { - throw fl::Exception("[consequent error] consequent <" + _text + "> is not loaded", FL_AT); + throw Exception("[consequent error] consequent <" + getText() + "> is not loaded", FL_AT); } for (std::size_t i = 0; i < _conclusions.size(); ++i) { Proposition* proposition = _conclusions.at(i); @@ -72,10 +81,9 @@ namespace fl { activationDegree = (*rit)->hedge(activationDegree); } } - Activated* term = new Activated(_conclusions.at(i)->term, activationDegree, activation); - OutputVariable* outputVariable = dynamic_cast<OutputVariable*> (proposition->variable); - outputVariable->fuzzyOutput()->addTerm(term); - FL_DBG("Accumulating " << term->toString()); + + static_cast<OutputVariable*> (proposition->variable)->fuzzyOutput() + ->addTerm(proposition->term, activationDegree, implication); } } } @@ -91,16 +99,16 @@ namespace fl { _conclusions.clear(); } - void Consequent::load(Rule* rule, const Engine* engine) { - load(_text, rule, engine); + void Consequent::load(const Engine* engine) { + load(getText(), engine); } - void Consequent::load(const std::string& consequent, Rule* rule, const Engine* engine) { + void Consequent::load(const std::string& consequent, const Engine* engine) { unload(); - this->_text = consequent; + setText(consequent); - if (fl::Op::trim(consequent).empty()) { - throw fl::Exception("[syntax error] consequent is empty", FL_AT); + if (Op::trim(consequent).empty()) { + throw Exception("[syntax error] consequent is empty", FL_AT); } /** @@ -129,8 +137,7 @@ namespace fl { if (engine->hasOutputVariable(token)) { proposition = new Proposition; proposition->variable = engine->getOutputVariable(token); - _conclusions.push_back(proposition); - + conclusions().push_back(proposition); state = S_IS; continue; } @@ -144,15 +151,9 @@ namespace fl { } 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) { + HedgeFactory* factory = FactoryManager::instance()->hedge(); + if (factory->hasConstructor(token)) { + Hedge* hedge = factory->constructObject(token); proposition->hedges.push_back(hedge); state = S_HEDGE bitor S_TERM; continue; @@ -178,19 +179,19 @@ namespace fl { if (state bitand S_VARIABLE) { std::ostringstream ex; ex << "[syntax error] consequent expected output variable, but found <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if (state bitand S_IS) { std::ostringstream ex; ex << "[syntax error] consequent expected keyword <" << Rule::isKeyword() << ">, " "but found <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if ((state bitand S_HEDGE) or (state bitand S_TERM)) { std::ostringstream ex; ex << "[syntax error] consequent expected hedge or term, but found <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if ((state bitand S_AND) or (state bitand S_WITH)) { @@ -198,30 +199,30 @@ namespace fl { ex << "[syntax error] consequent expected operator <" << Rule::andKeyword() << "> " << "or keyword <" << Rule::withKeyword() << ">, " << "but found <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> in consequent"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if (not ((state bitand S_AND) or (state bitand S_WITH))) { //only acceptable final state if (state bitand S_VARIABLE) { std::ostringstream ex; ex << "[syntax error] consequent expected output variable after <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if (state bitand S_IS) { std::ostringstream ex; ex << "[syntax error] consequent expected keyword <" << Rule::isKeyword() << "> " "after <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if ((state bitand S_HEDGE) or (state bitand S_TERM)) { std::ostringstream ex; ex << "[syntax error] consequent expected hedge or term after <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } } catch (...) { @@ -232,9 +233,9 @@ namespace fl { std::string Consequent::toString() const { std::stringstream ss; - for (std::size_t i = 0; i < _conclusions.size(); ++i) { - ss << _conclusions.at(i)->toString(); - if (i + 1 < _conclusions.size()) + for (std::size_t i = 0; i < conclusions().size(); ++i) { + ss << conclusions().at(i)->toString(); + if (i + 1 < conclusions().size()) ss << " " << Rule::andKeyword() << " "; } return ss.str(); diff --git a/fuzzylite/src/rule/Expression.cpp b/fuzzylite/src/rule/Expression.cpp index ea7873e..9b0ab81 100644 --- a/fuzzylite/src/rule/Expression.cpp +++ b/fuzzylite/src/rule/Expression.cpp @@ -1,25 +1,17 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> 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. + the terms of the FuzzyLite License included with the software. - 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 <http://www.gnu.org/licenses/>. - - fuzzylite™ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/rule/Expression.h" @@ -31,18 +23,22 @@ namespace fl { - Expression::Expression() { - } + Expression::Expression() { } - Expression::~Expression() { - } + Expression::~Expression() { } - Proposition::Proposition() - : Expression(), variable(fl::null), term(fl::null) { - } + Proposition::Proposition() : Expression(), + variable(fl::null), term(fl::null) { } Proposition::~Proposition() { + for (std::size_t i = 0; i < hedges.size(); ++i) { + delete hedges.at(i); + } + hedges.clear(); + } + Expression::Type Proposition::type() const { + return Expression::Proposition; } std::string Proposition::toString() const { @@ -68,14 +64,18 @@ namespace fl { return ss.str(); } - Operator::Operator() : Expression(), name(""), left(fl::null), right(fl::null) { - } + Operator::Operator() : Expression(), + name(""), left(fl::null), right(fl::null) { } Operator::~Operator() { if (left) delete left; if (right) delete right; } + Expression::Type Operator::type() const { + return Expression::Operator; + } + std::string Operator::toString() const { return name; } diff --git a/fuzzylite/src/rule/Rule.cpp b/fuzzylite/src/rule/Rule.cpp index 446290b..0338cd9 100644 --- a/fuzzylite/src/rule/Rule.cpp +++ b/fuzzylite/src/rule/Rule.cpp @@ -1,55 +1,43 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> 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. + the terms of the FuzzyLite License included with the software. - 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 <http://www.gnu.org/licenses/>. - - fuzzylite™ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/rule/Rule.h" #include "fl/Exception.h" -#include "fl/hedge/Hedge.h" #include "fl/imex/FllExporter.h" #include "fl/norm/Norm.h" -#include "fl/rule/Antecedent.h" -#include "fl/rule/Consequent.h" - -#include <sstream> -#include <vector> +#include "fl/Operation.h" namespace fl { Rule::Rule(const std::string& text, scalar weight) - : _text(text), _weight(weight), _antecedent(new Antecedent), _consequent(new Consequent) { - } + : _enabled(true), _text(text), _weight(weight), _activationDegree(0.0), _triggered(false), + _antecedent(new Antecedent), _consequent(new Consequent) { } - Rule::Rule(const Rule& other) : _text(other._text), _weight(other._weight), - _antecedent(new Antecedent), _consequent(new Consequent) { - } + Rule::Rule(const Rule& other) : _enabled(other._enabled), _text(other._text), + _weight(other._weight), _activationDegree(other._activationDegree), _triggered(false), + _antecedent(new Antecedent), _consequent(new Consequent) { } Rule& Rule::operator=(const Rule& other) { if (this != &other) { - unload(); - + _enabled = other._enabled; _text = other._text; _weight = other._weight; + _activationDegree = other._activationDegree; + _triggered = other._triggered; _antecedent.reset(new Antecedent); _consequent.reset(new Consequent); } @@ -57,7 +45,8 @@ namespace fl { } Rule::~Rule() { - unload(); + if (_antecedent.get()) _antecedent->unload(); + if (_consequent.get()) _consequent->unload(); } void Rule::setText(const std::string& text) { @@ -92,87 +81,93 @@ namespace fl { return this->_consequent.get(); } - /** - * Operations for std::vector _hedges - */ - void Rule::addHedge(Hedge* hedge) { - this->_hedges[hedge->name()] = hedge; + void Rule::setEnabled(bool active) { + this->_enabled = active; } - Hedge* Rule::getHedge(const std::string& name) const { - std::map<std::string, Hedge*>::const_iterator it = this->_hedges.find(name); - if (it != this->_hedges.end()) { - if (it->second) return it->second; - } - return fl::null; + bool Rule::isEnabled() const { + return this->_enabled; } - Hedge* Rule::removeHedge(const std::string& name) { - Hedge* result = fl::null; - std::map<std::string, Hedge*>::iterator it = this->_hedges.find(name); - if (it != this->_hedges.end()) { - result = it->second; - this->_hedges.erase(it); - } - return result; + void Rule::setActivationDegree(scalar activationDegree) { + this->_activationDegree = activationDegree; } - bool Rule::hasHedge(const std::string& name) const { - std::map<std::string, Hedge*>::const_iterator it = this->_hedges.find(name); - return (it != this->_hedges.end()); + scalar Rule::getActivationDegree() const { + return this->_activationDegree; } - int Rule::numberOfHedges() const { - return this->_hedges.size(); + void Rule::deactivate() { + _activationDegree = 0.0; + _triggered = false; } - void Rule::setHedges(const std::map<std::string, Hedge*>& hedges) { - this->_hedges = hedges; + scalar Rule::activateWith(const TNorm* conjunction, const SNorm* disjunction) { + if (not isLoaded()) { + throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT); + } + _activationDegree = _weight * _antecedent->activationDegree(conjunction, disjunction); + return _activationDegree; } - const std::map<std::string, Hedge*>& Rule::hedges() const { - return this->_hedges; + void Rule::trigger(const TNorm* implication) { + if (not isLoaded()) { + throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT); + } + if (_enabled and Op::isGt(_activationDegree, 0.0)) { + FL_DBG("[firing with " << Op::str(_activationDegree) << "] " << toString()); + _consequent->modify(_activationDegree, implication); + _triggered = true; + } } - std::map<std::string, Hedge*>& Rule::hedges() { - return this->_hedges; + bool Rule::isTriggered() const { + return this->_triggered; } - scalar Rule::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const { - if (not isLoaded()) { - throw fl::Exception("[rule error] the following rule is not loaded: " + _text, FL_AT); + Complexity Rule::complexityOfActivation(const TNorm* conjunction, const SNorm* disjunction) const { + Complexity result; + result.comparison(1).arithmetic(1); + if (isLoaded()) { + result += _antecedent->complexity(conjunction, disjunction); } - return _weight * getAntecedent()->activationDegree(conjunction, disjunction); + return result; } - void Rule::activate(scalar degree, const TNorm* activation) const { - if (not isLoaded()) { - throw fl::Exception("[rule error] the following rule is not loaded: " + _text, FL_AT); + Complexity Rule::complexityOfFiring(const TNorm* implication) const { + Complexity result; + result.comparison(3); + if (isLoaded()) { + result += _consequent->complexity(implication); } - _consequent->modify(degree, activation); + return result; + } + + Complexity Rule::complexity(const TNorm* conjunction, const SNorm* disjunction, + const TNorm* implication) const { + return complexityOfActivation(conjunction, disjunction) + + complexityOfFiring(implication); } bool Rule::isLoaded() const { - return _antecedent->isLoaded() and _consequent->isLoaded(); + return _antecedent.get() and _consequent.get() + and _antecedent->isLoaded() and _consequent->isLoaded(); } void Rule::unload() { - _antecedent->unload(); - _consequent->unload(); - - for (std::map<std::string, Hedge*>::const_iterator it = _hedges.begin(); - it != _hedges.end(); ++it) { - delete it->second; - } - _hedges.clear(); + deactivate(); + if (getAntecedent()) getAntecedent()->unload(); + if (getConsequent()) getConsequent()->unload(); } void Rule::load(const Engine* engine) { - load(_text, engine); + load(getText(), engine); } void Rule::load(const std::string& rule, const Engine* engine) { - this->_text = rule; + deactivate(); + setEnabled(true); + setText(rule); std::istringstream tokenizer(rule.substr(0, rule.find_first_of('#'))); std::string token; std::ostringstream ossAntecedent, ossConsequent; @@ -192,7 +187,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected keyword <" << Rule::ifKeyword() << ">, but found <" << token << "> in rule: " << rule; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } break; case S_IF: @@ -205,39 +200,46 @@ namespace fl { break; case S_WITH: try { - weight = fl::Op::toScalar(token); + weight = Op::toScalar(token); state = S_END; - } catch (fl::Exception& e) { + } catch (Exception& e) { std::ostringstream ex; ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule; e.append(ex.str(), FL_AT); - throw e; + throw; } break; case S_END: + { std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> at the end of rule"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); + } + + default: + std::ostringstream ex; + ex << "[syntax error] unexpected state <" << state << ">"; + throw Exception(ex.str(), FL_AT); } } if (state == S_NONE) { std::ostringstream ex; - ex << "[syntax error] " << (rule.empty() ? "empty rule" : "ignored rule: " + rule); - throw fl::Exception(ex.str(), FL_AT); + ex << "[syntax error] " << (rule.empty() ? "empty rule" : ("ignored rule: " + rule)); + throw Exception(ex.str(), FL_AT); } else if (state == S_IF) { std::ostringstream ex; ex << "[syntax error] keyword <" << Rule::thenKeyword() << "> not found in rule: " << rule; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } else if (state == S_WITH) { std::ostringstream ex; ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } - _antecedent->load(ossAntecedent.str(), this, engine); - _consequent->load(ossConsequent.str(), this, engine); - _weight = weight; + getAntecedent()->load(ossAntecedent.str(), engine); + getConsequent()->load(ossConsequent.str(), engine); + setWeight(weight); } catch (...) { unload(); diff --git a/fuzzylite/src/rule/RuleBlock.cpp b/fuzzylite/src/rule/RuleBlock.cpp index 9ab813e..d4e2a82 100644 --- a/fuzzylite/src/rule/RuleBlock.cpp +++ b/fuzzylite/src/rule/RuleBlock.cpp @@ -1,44 +1,35 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> 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. + the terms of the FuzzyLite License included with the software. - 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 <http://www.gnu.org/licenses/>. - - fuzzylite™ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/rule/RuleBlock.h" +#include "fl/activation/General.h" #include "fl/imex/FllExporter.h" #include "fl/norm/TNorm.h" #include "fl/norm/SNorm.h" #include "fl/rule/Rule.h" - -#include <sstream> +#include "fl/Operation.h" namespace fl { RuleBlock::RuleBlock(const std::string& name) - : _name(name), _enabled(true) { - } + : _enabled(true), _name(name), _description("") { } - RuleBlock::RuleBlock(const RuleBlock& other) : _name(other._name), - _enabled(true) { + RuleBlock::RuleBlock(const RuleBlock& other) : _enabled(true), _name(other._name), + _description(other._description) { copyFrom(other); } @@ -50,6 +41,7 @@ namespace fl { _rules.clear(); _conjunction.reset(fl::null); _disjunction.reset(fl::null); + _implication.reset(fl::null); _activation.reset(fl::null); copyFrom(other); @@ -58,11 +50,13 @@ namespace fl { } void RuleBlock::copyFrom(const RuleBlock& source) { - _name = source._name; _enabled = source._enabled; - if (source._activation.get()) _activation.reset(source._activation->clone()); + _name = source._name; + _description = source._description; if (source._conjunction.get()) _conjunction.reset(source._conjunction->clone()); if (source._disjunction.get()) _disjunction.reset(source._disjunction->clone()); + if (source._implication.get()) _implication.reset(source._implication->clone()); + if (source._activation.get()) _activation.reset(source._activation->clone()); for (std::size_t i = 0; i < source._rules.size(); ++i) { _rules.push_back(source._rules.at(i)->clone()); } @@ -75,21 +69,25 @@ namespace fl { _rules.clear(); } - void RuleBlock::activate() { - FL_DBG("==================="); - FL_DBG("ACTIVATING RULEBLOCK " << _name); - for (std::size_t i = 0; i < _rules.size(); ++i) { - Rule* rule = _rules.at(i); - if (rule->isLoaded()) { - scalar activationDegree = rule->activationDegree(_conjunction.get(), _disjunction.get()); - FL_DBG("[degree=" << Op::str(activationDegree) << "] " << rule->toString()); - if (Op::isGt(activationDegree, 0.0)) { - rule->activate(activationDegree, _activation.get()); - } - } else { - FL_DBG("Rule not loaded: " << rule->toString()); + Complexity RuleBlock::complexity() const { + Complexity result; + result.comparison(1); + if (_activation.get()) { + result += _activation->complexity(this); + } else { + for (std::size_t i = 0; i < _rules.size(); ++i) { + result += _rules.at(i)->complexity( + _conjunction.get(), _disjunction.get(), _implication.get()); } } + return result; + } + + void RuleBlock::activate() { + if (not _activation.get()) { + _activation.reset(new General); + } + _activation->activate(this); } void RuleBlock::unloadRules() const { @@ -114,7 +112,7 @@ namespace fl { } } if (throwException) { - fl::Exception exception("[ruleblock error] the following " + Exception exception("[ruleblock error] the following " "rules could not be loaded:\n" + exceptions.str(), FL_AT); throw exception; } @@ -133,6 +131,14 @@ namespace fl { return this->_name; } + void RuleBlock::setDescription(const std::string& description) { + this->_description = description; + } + + std::string RuleBlock::getDescription() const { + return this->_description; + } + void RuleBlock::setConjunction(TNorm* tnorm) { this->_conjunction.reset(tnorm); } @@ -149,11 +155,19 @@ namespace fl { return this->_disjunction.get(); } - void RuleBlock::setActivation(TNorm* activation) { + void RuleBlock::setImplication(TNorm* implication) { + this->_implication.reset(implication); + } + + TNorm* RuleBlock::getImplication() const { + return this->_implication.get(); + } + + void RuleBlock::setActivation(Activation* activation) { this->_activation.reset(activation); } - TNorm* RuleBlock::getActivation() const { + Activation* RuleBlock::getActivation() const { return this->_activation.get(); } @@ -173,25 +187,25 @@ namespace fl { * Operations for std::vector _rules */ void RuleBlock::addRule(Rule* rule) { - this->_rules.push_back(rule); + _rules.push_back(rule); } - void RuleBlock::insertRule(Rule* rule, int index) { - this->_rules.insert(this->_rules.begin() + index, rule); + void RuleBlock::insertRule(Rule* rule, std::size_t index) { + _rules.insert(_rules.begin() + index, rule); } - Rule* RuleBlock::getRule(int index) const { - return this->_rules.at(index); + Rule* RuleBlock::getRule(std::size_t index) const { + return _rules.at(index); } - Rule* RuleBlock::removeRule(int index) { - Rule* result = this->_rules.at(index); - this->_rules.erase(this->_rules.begin() + index); + Rule* RuleBlock::removeRule(std::size_t index) { + Rule* result = _rules.at(index); + _rules.erase(_rules.begin() + index); return result; } - int RuleBlock::numberOfRules() const { - return this->_rules.size(); + std::size_t RuleBlock::numberOfRules() const { + return _rules.size(); } const std::vector<Rule*>& RuleBlock::rules() const { @@ -206,5 +220,8 @@ namespace fl { return this->_rules; } + RuleBlock* RuleBlock::clone() const { + return new RuleBlock(*this); + } } |