summaryrefslogtreecommitdiff
path: root/fuzzylite/src/rule
diff options
context:
space:
mode:
authorJohannes Schauer <josch@debian.org>2015-07-07 10:12:00 +0100
committerJohannes Schauer <josch@debian.org>2015-07-07 10:12:00 +0100
commit0e8c1c9d31d15034b1ff1062c9bf0bfcdc849dd9 (patch)
tree840a335d9a4487d3f1909a31c8526a2e647809e7 /fuzzylite/src/rule
Import fuzzylite_5.1+dfsg.orig.tar.xz
[dgit import orig fuzzylite_5.1+dfsg.orig.tar.xz]
Diffstat (limited to 'fuzzylite/src/rule')
-rw-r--r--fuzzylite/src/rule/Antecedent.cpp368
-rw-r--r--fuzzylite/src/rule/Consequent.cpp243
-rw-r--r--fuzzylite/src/rule/Expression.cpp83
-rw-r--r--fuzzylite/src/rule/Rule.cpp262
-rw-r--r--fuzzylite/src/rule/RuleBlock.cpp210
5 files changed, 1166 insertions, 0 deletions
diff --git a/fuzzylite/src/rule/Antecedent.cpp b/fuzzylite/src/rule/Antecedent.cpp
new file mode 100644
index 0000000..aa18a68
--- /dev/null
+++ b/fuzzylite/src/rule/Antecedent.cpp
@@ -0,0 +1,368 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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 <algorithm>
+#include <stack>
+
+
+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<const Proposition*> (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<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin();
+ if (dynamic_cast<Any*> (*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<InputVariable*> (proposition->variable)) {
+ result = proposition->term->membership(inputVariable->getInputValue());
+ } else if (OutputVariable * outputVariable = dynamic_cast<OutputVariable*> (proposition->variable)) {
+ result = outputVariable->fuzzyOutput()->activationDegree(proposition->term);
+ }
+ 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)) {
+ 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<Expression*> 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<Any*> (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<std::string> 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<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) << " ";
+ 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<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) << " ";
+ 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<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() << " ";
+ return ss.str();
+ }
+
+
+}
diff --git a/fuzzylite/src/rule/Consequent.cpp b/fuzzylite/src/rule/Consequent.cpp
new file mode 100644
index 0000000..28d3390
--- /dev/null
+++ b/fuzzylite/src/rule/Consequent.cpp
@@ -0,0 +1,243 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Consequent.h"
+
+#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/variable/OutputVariable.h"
+
+#include <algorithm>
+
+namespace fl {
+
+ Consequent::Consequent() {
+ }
+
+ Consequent::~Consequent() {
+ unload();
+ }
+
+ std::string Consequent::getText() const {
+ return this->_text;
+ }
+
+ void Consequent::setText(const std::string& text) {
+ this->_text = text;
+ }
+
+ const std::vector<Proposition*>& Consequent::conclusions() const {
+ return this->_conclusions;
+ }
+
+ void Consequent::modify(scalar activationDegree, const TNorm* activation) {
+ if (not isLoaded()) {
+ throw fl::Exception("[consequent error] consequent <" + _text + "> is not loaded", FL_AT);
+ }
+ for (std::size_t i = 0; i < _conclusions.size(); ++i) {
+ Proposition* proposition = _conclusions.at(i);
+ if (proposition->variable->isEnabled()) {
+ if (not proposition->hedges.empty()) {
+ for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin();
+ rit != proposition->hedges.rend(); ++rit) {
+ 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());
+ }
+ }
+ }
+
+ bool Consequent::isLoaded() {
+ return not _conclusions.empty();
+ }
+
+ void Consequent::unload() {
+ for (std::size_t i = 0; i < _conclusions.size(); ++i) {
+ delete _conclusions.at(i);
+ }
+ _conclusions.clear();
+ }
+
+ void Consequent::load(Rule* rule, const Engine* engine) {
+ load(_text, rule, engine);
+ }
+
+ void Consequent::load(const std::string& consequent, Rule* rule, const Engine* engine) {
+ unload();
+ this->_text = consequent;
+
+ if (fl::Op::trim(consequent).empty()) {
+ throw fl::Exception("[syntax error] consequent is empty", FL_AT);
+ }
+
+ /**
+ Extracts the list of propositions from the consequent
+ The rules are:
+ 1) After a variable comes 'is' or '=',
+ 2) After 'is' comes a hedge or a term
+ 3) After a hedge comes a hedge or a term
+ 4) After a term comes operators 'and' or 'with'
+ 5) After operator 'and' comes a variable
+ 6) After operator 'with' comes a float
+ */
+ enum FSM {
+ S_VARIABLE = 1, S_IS = 2, S_HEDGE = 4, S_TERM = 8,
+ S_AND = 16, S_WITH = 32
+ };
+ int state = S_VARIABLE;
+
+ Proposition* proposition = fl::null;
+
+ std::stringstream tokenizer(consequent);
+ std::string token;
+ try {
+ while (tokenizer >> token) {
+ if (state bitand S_VARIABLE) {
+ if (engine->hasOutputVariable(token)) {
+ proposition = new Proposition;
+ proposition->variable = engine->getOutputVariable(token);
+ _conclusions.push_back(proposition);
+
+ state = S_IS;
+ continue;
+ }
+ }
+
+ if (state bitand S_IS) {
+ if (token == Rule::isKeyword()) {
+ state = S_HEDGE bitor S_TERM;
+ 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);
+ state = S_HEDGE bitor S_TERM;
+ continue;
+ }
+ }
+
+ if (state bitand S_TERM) {
+ if (proposition->variable->hasTerm(token)) {
+ proposition->term = proposition->variable->getTerm(token);
+ state = S_AND bitor S_WITH;
+ continue;
+ }
+ }
+
+ if (state bitand S_AND) {
+ if (token == Rule::andKeyword()) {
+ state = S_VARIABLE;
+ continue;
+ }
+ }
+
+ //if reached this point, there was an error:
+ 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);
+ }
+ 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);
+ }
+
+ 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);
+ }
+
+ if ((state bitand S_AND) or (state bitand S_WITH)) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected operator <" << Rule::andKeyword() << "> "
+ << "or keyword <" << Rule::withKeyword() << ">, "
+ << "but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected token <" << token << "> in consequent";
+ throw fl::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);
+ }
+ 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);
+ }
+ 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);
+ }
+ }
+ } catch (...) {
+ unload();
+ throw;
+ }
+ }
+
+ 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())
+ ss << " " << Rule::andKeyword() << " ";
+ }
+ return ss.str();
+ }
+
+}
diff --git a/fuzzylite/src/rule/Expression.cpp b/fuzzylite/src/rule/Expression.cpp
new file mode 100644
index 0000000..ea7873e
--- /dev/null
+++ b/fuzzylite/src/rule/Expression.cpp
@@ -0,0 +1,83 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Expression.h"
+
+#include "fl/hedge/Hedge.h"
+#include "fl/term/Term.h"
+#include "fl/rule/Rule.h"
+#include "fl/variable/Variable.h"
+
+namespace fl {
+
+ Expression::Expression() {
+ }
+
+ Expression::~Expression() {
+ }
+
+ Proposition::Proposition()
+ : Expression(), variable(fl::null), term(fl::null) {
+ }
+
+ Proposition::~Proposition() {
+
+ }
+
+ std::string Proposition::toString() const {
+ std::ostringstream ss;
+ if (variable) {
+ ss << variable->getName();
+ } else {
+ ss << "?";
+ }
+ if (not hedges.empty()) {
+ ss << " " << Rule::isKeyword() << " ";
+ for (std::size_t i = 0; i < hedges.size(); ++i) {
+ ss << hedges.at(i)->name() << " ";
+ }
+ }
+
+ if (term) { //term is fl::null if hedge is any
+ if (hedges.empty()) {
+ ss << " " << Rule::isKeyword() << " ";
+ }
+ ss << term->getName();
+ }
+ return ss.str();
+ }
+
+ Operator::Operator() : Expression(), name(""), left(fl::null), right(fl::null) {
+ }
+
+ Operator::~Operator() {
+ if (left) delete left;
+ if (right) delete right;
+ }
+
+ std::string Operator::toString() const {
+ return name;
+ }
+
+}
diff --git a/fuzzylite/src/rule/Rule.cpp b/fuzzylite/src/rule/Rule.cpp
new file mode 100644
index 0000000..446290b
--- /dev/null
+++ b/fuzzylite/src/rule/Rule.cpp
@@ -0,0 +1,262 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a 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>
+
+namespace fl {
+
+ Rule::Rule(const std::string& text, scalar weight)
+ : _text(text), _weight(weight), _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::operator=(const Rule& other) {
+ if (this != &other) {
+ unload();
+
+ _text = other._text;
+ _weight = other._weight;
+ _antecedent.reset(new Antecedent);
+ _consequent.reset(new Consequent);
+ }
+ return *this;
+ }
+
+ Rule::~Rule() {
+ unload();
+ }
+
+ void Rule::setText(const std::string& text) {
+ this->_text = text;
+ }
+
+ std::string Rule::getText() const {
+ return this->_text;
+ }
+
+ void Rule::setWeight(scalar weight) {
+ this->_weight = weight;
+ }
+
+ scalar Rule::getWeight() const {
+ return this->_weight;
+ }
+
+ void Rule::setAntecedent(Antecedent* antecedent) {
+ this->_antecedent.reset(antecedent);
+ }
+
+ Antecedent* Rule::getAntecedent() const {
+ return this->_antecedent.get();
+ }
+
+ void Rule::setConsequent(Consequent* consequent) {
+ this->_consequent.reset(consequent);
+ }
+
+ Consequent* Rule::getConsequent() const {
+ return this->_consequent.get();
+ }
+
+ /**
+ * Operations for std::vector _hedges
+ */
+ void Rule::addHedge(Hedge* hedge) {
+ this->_hedges[hedge->name()] = hedge;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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());
+ }
+
+ int Rule::numberOfHedges() const {
+ return this->_hedges.size();
+ }
+
+ void Rule::setHedges(const std::map<std::string, Hedge*>& hedges) {
+ this->_hedges = hedges;
+ }
+
+ const std::map<std::string, Hedge*>& Rule::hedges() const {
+ return this->_hedges;
+ }
+
+ std::map<std::string, Hedge*>& Rule::hedges() {
+ return this->_hedges;
+ }
+
+ 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);
+ }
+ return _weight * getAntecedent()->activationDegree(conjunction, disjunction);
+ }
+
+ 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);
+ }
+ _consequent->modify(degree, activation);
+ }
+
+ bool Rule::isLoaded() const {
+ return _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();
+ }
+
+ void Rule::load(const Engine* engine) {
+ load(_text, engine);
+ }
+
+ void Rule::load(const std::string& rule, const Engine* engine) {
+ this->_text = rule;
+ std::istringstream tokenizer(rule.substr(0, rule.find_first_of('#')));
+ std::string token;
+ std::ostringstream ossAntecedent, ossConsequent;
+ scalar weight = 1.0;
+
+ enum FSM {
+ S_NONE, S_IF, S_THEN, S_WITH, S_END
+ };
+ FSM state = S_NONE;
+ try {
+ while (tokenizer >> token) {
+
+ switch (state) {
+ case S_NONE:
+ if (token == Rule::ifKeyword()) state = S_IF;
+ else {
+ std::ostringstream ex;
+ ex << "[syntax error] expected keyword <" << Rule::ifKeyword() <<
+ ">, but found <" << token << "> in rule: " << rule;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ break;
+ case S_IF:
+ if (token == Rule::thenKeyword()) state = S_THEN;
+ else ossAntecedent << token << " ";
+ break;
+ case S_THEN:
+ if (token == Rule::withKeyword()) state = S_WITH;
+ else ossConsequent << token << " ";
+ break;
+ case S_WITH:
+ try {
+ weight = fl::Op::toScalar(token);
+ state = S_END;
+ } catch (fl::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;
+ }
+ 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);
+ }
+ }
+ if (state == S_NONE) {
+ std::ostringstream ex;
+ ex << "[syntax error] " << (rule.empty() ? "empty rule" : "ignored rule: " + rule);
+ throw fl::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);
+ } 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);
+ }
+
+ _antecedent->load(ossAntecedent.str(), this, engine);
+ _consequent->load(ossConsequent.str(), this, engine);
+ _weight = weight;
+
+ } catch (...) {
+ unload();
+ throw;
+ }
+ }
+
+ std::string Rule::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ Rule* Rule::clone() const {
+ return new Rule(*this);
+ }
+
+ Rule* Rule::parse(const std::string& rule, const Engine* engine) {
+ FL_unique_ptr<Rule> result(new Rule);
+ result->load(rule, engine);
+ return result.release();
+ }
+
+}
diff --git a/fuzzylite/src/rule/RuleBlock.cpp b/fuzzylite/src/rule/RuleBlock.cpp
new file mode 100644
index 0000000..9ab813e
--- /dev/null
+++ b/fuzzylite/src/rule/RuleBlock.cpp
@@ -0,0 +1,210 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/RuleBlock.h"
+
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/TNorm.h"
+#include "fl/norm/SNorm.h"
+#include "fl/rule/Rule.h"
+
+#include <sstream>
+
+namespace fl {
+
+ RuleBlock::RuleBlock(const std::string& name)
+ : _name(name), _enabled(true) {
+ }
+
+ RuleBlock::RuleBlock(const RuleBlock& other) : _name(other._name),
+ _enabled(true) {
+ copyFrom(other);
+ }
+
+ RuleBlock& RuleBlock::operator=(const RuleBlock& other) {
+ if (this != &other) {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ delete _rules.at(i);
+ }
+ _rules.clear();
+ _conjunction.reset(fl::null);
+ _disjunction.reset(fl::null);
+ _activation.reset(fl::null);
+
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ void RuleBlock::copyFrom(const RuleBlock& source) {
+ _name = source._name;
+ _enabled = source._enabled;
+ if (source._activation.get()) _activation.reset(source._activation->clone());
+ if (source._conjunction.get()) _conjunction.reset(source._conjunction->clone());
+ if (source._disjunction.get()) _disjunction.reset(source._disjunction->clone());
+ for (std::size_t i = 0; i < source._rules.size(); ++i) {
+ _rules.push_back(source._rules.at(i)->clone());
+ }
+ }
+
+ RuleBlock::~RuleBlock() {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ delete _rules.at(i);
+ }
+ _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());
+ }
+ }
+ }
+
+ void RuleBlock::unloadRules() const {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ _rules.at(i)->unload();
+ }
+ }
+
+ void RuleBlock::loadRules(const Engine* engine) {
+ std::ostringstream exceptions;
+ bool throwException = false;
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ Rule* rule = _rules.at(i);
+ if (rule->isLoaded()) {
+ rule->unload();
+ }
+ try {
+ rule->load(engine);
+ } catch (std::exception& ex) {
+ throwException = true;
+ exceptions << ex.what() << "\n";
+ }
+ }
+ if (throwException) {
+ fl::Exception exception("[ruleblock error] the following "
+ "rules could not be loaded:\n" + exceptions.str(), FL_AT);
+ throw exception;
+ }
+ }
+
+ void RuleBlock::reloadRules(const Engine* engine) {
+ unloadRules();
+ loadRules(engine);
+ }
+
+ void RuleBlock::setName(std::string name) {
+ this->_name = name;
+ }
+
+ std::string RuleBlock::getName() const {
+ return this->_name;
+ }
+
+ void RuleBlock::setConjunction(TNorm* tnorm) {
+ this->_conjunction.reset(tnorm);
+ }
+
+ TNorm* RuleBlock::getConjunction() const {
+ return this->_conjunction.get();
+ }
+
+ void RuleBlock::setDisjunction(SNorm* snorm) {
+ this->_disjunction.reset(snorm);
+ }
+
+ SNorm* RuleBlock::getDisjunction() const {
+ return this->_disjunction.get();
+ }
+
+ void RuleBlock::setActivation(TNorm* activation) {
+ this->_activation.reset(activation);
+ }
+
+ TNorm* RuleBlock::getActivation() const {
+ return this->_activation.get();
+ }
+
+ void RuleBlock::setEnabled(bool enabled) {
+ this->_enabled = enabled;
+ }
+
+ bool RuleBlock::isEnabled() const {
+ return this->_enabled;
+ }
+
+ std::string RuleBlock::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ /**
+ * Operations for std::vector _rules
+ */
+ void RuleBlock::addRule(Rule* rule) {
+ this->_rules.push_back(rule);
+ }
+
+ void RuleBlock::insertRule(Rule* rule, int index) {
+ this->_rules.insert(this->_rules.begin() + index, rule);
+ }
+
+ Rule* RuleBlock::getRule(int index) const {
+ return this->_rules.at(index);
+ }
+
+ Rule* RuleBlock::removeRule(int index) {
+ Rule* result = this->_rules.at(index);
+ this->_rules.erase(this->_rules.begin() + index);
+ return result;
+ }
+
+ int RuleBlock::numberOfRules() const {
+ return this->_rules.size();
+ }
+
+ const std::vector<Rule*>& RuleBlock::rules() const {
+ return this->_rules;
+ }
+
+ void RuleBlock::setRules(const std::vector<Rule*>& rules) {
+ this->_rules = rules;
+ }
+
+ std::vector<Rule*>& RuleBlock::rules() {
+ return this->_rules;
+ }
+
+
+}