/*
Author: Juan Rada-Vilela, Ph.D.
Copyright (C) 2010-2014 FuzzyLite Limited
All rights reserved
This file is part of fuzzylite.
fuzzylite is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
fuzzylite is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU Lesser General Public License
along with fuzzylite. If not, see .
fuzzyliteâ„¢ is a trademark of FuzzyLite Limited.
*/
#include "fl/rule/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
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& 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::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 (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();
}
}