summaryrefslogtreecommitdiff
path: root/fuzzylite/src/rule
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzylite/src/rule')
-rw-r--r--fuzzylite/src/rule/Antecedent.cpp293
-rw-r--r--fuzzylite/src/rule/Consequent.cpp117
-rw-r--r--fuzzylite/src/rule/Expression.cpp48
-rw-r--r--fuzzylite/src/rule/Rule.cpp182
-rw-r--r--fuzzylite/src/rule/RuleBlock.cpp115
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);
+ }
}