summaryrefslogtreecommitdiff
path: root/fuzzylite/src/rule/Consequent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzylite/src/rule/Consequent.cpp')
-rw-r--r--fuzzylite/src/rule/Consequent.cpp243
1 files changed, 243 insertions, 0 deletions
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();
+ }
+
+}