/*
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/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
#include
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::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::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::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& hedges) {
this->_hedges = hedges;
}
const std::map& Rule::hedges() const {
return this->_hedges;
}
std::map& 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::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 result(new Rule);
result->load(rule, engine);
return result.release();
}
}