summaryrefslogtreecommitdiff
path: root/fuzzylite/src/term/Function.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzylite/src/term/Function.cpp')
-rw-r--r--fuzzylite/src/term/Function.cpp266
1 files changed, 122 insertions, 144 deletions
diff --git a/fuzzylite/src/term/Function.cpp b/fuzzylite/src/term/Function.cpp
index c0f54b8..42f4aaa 100644
--- a/fuzzylite/src/term/Function.cpp
+++ b/fuzzylite/src/term/Function.cpp
@@ -1,43 +1,30 @@
/*
- 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/term/Function.h"
#include "fl/Engine.h"
#include "fl/factory/FactoryManager.h"
-#include "fl/factory/FunctionFactory.h"
#include "fl/rule/Rule.h"
#include "fl/variable/InputVariable.h"
#include "fl/variable/OutputVariable.h"
-#include <cctype>
-#include <functional>
#include <queue>
-#include <signal.h>
#include <stack>
-
namespace fl {
/**
@@ -47,32 +34,26 @@ namespace fl {
Function::Element::Element(const std::string& name, const std::string& description, Type type)
: name(name), description(description), type(type), unary(fl::null), binary(fl::null), arity(0),
- precedence(0), associativity(-1) {
-
- }
+ precedence(0), associativity(-1) { }
Function::Element::Element(const std::string& name, const std::string& description,
Type type, Unary unary, int precedence, int associativity)
: name(name), description(description), type(type), unary(unary), binary(fl::null), arity(1),
- precedence(precedence), associativity(associativity) {
- }
+ precedence(precedence), associativity(associativity) { }
Function::Element::Element(const std::string& name, const std::string& description,
Type type, Binary binary, int precedence, int associativity)
: name(name), description(description), type(type), unary(fl::null), binary(binary), arity(2),
- precedence(precedence), associativity(associativity) {
- }
+ precedence(precedence), associativity(associativity) { }
- Function::Element::~Element() {
-
- }
+ Function::Element::~Element() { }
bool Function::Element::isOperator() const {
- return type == OPERATOR;
+ return type == Operator;
}
bool Function::Element::isFunction() const {
- return type == FUNCTION;
+ return type == Function;
}
Function::Element* Function::Element::clone() const {
@@ -82,7 +63,7 @@ namespace fl {
std::string Function::Element::toString() const {
std::ostringstream ss;
- if (type == OPERATOR) {
+ if (type == Operator) {
ss << "Operator (name=" << name << ", "
<< "description=" << description << ", "
<< "precedence=" << precedence << ", "
@@ -92,7 +73,7 @@ namespace fl {
else if (arity == 2) ss << "pointer=" << binary;
else ss << "pointer=error";
ss << ")";
- } else if (type == FUNCTION) {
+ } else if (type == Function) {
ss << "Function (name=" << name << ", "
<< "description=" << description << ", "
<< "arity=" << arity << ", "
@@ -110,16 +91,13 @@ namespace fl {
******************************/
Function::Node::Node(Element* element, Node* left, Node* right)
- : element(element), left(left), right(right), variable(""), value(fl::nan) {
- }
+ : element(element), left(left), right(right), variable(""), value(fl::nan) { }
Function::Node::Node(const std::string& variable)
- : element(fl::null), left(fl::null), right(fl::null), variable(variable), value(fl::nan) {
- }
+ : element(fl::null), left(fl::null), right(fl::null), variable(variable), value(fl::nan) { }
Function::Node::Node(scalar value)
- : element(fl::null), left(fl::null), right(fl::null), variable(""), value(value) {
- }
+ : element(fl::null), left(fl::null), right(fl::null), variable(""), value(value) { }
Function::Node::Node(const Node& other)
: element(fl::null), left(fl::null), right(fl::null), variable(""), value(fl::nan) {
@@ -145,8 +123,7 @@ namespace fl {
value = other.value;
}
- Function::Node::~Node() {
- }
+ Function::Node::~Node() { }
scalar Function::Node::evaluate(const std::map<std::string, scalar>* variables) const {
scalar result = fl::nan;
@@ -160,17 +137,17 @@ namespace fl {
ex << "[function error] arity <" << element->arity << "> of "
<< (element->isOperator() ? "operator" : "function") <<
" <" << element->name << "> is fl::null";
- throw fl::Exception(ex.str(), FL_AT);
+ throw Exception(ex.str(), FL_AT);
}
} else if (not variable.empty()) {
if (not variables) {
- throw fl::Exception("[function error] "
+ throw Exception("[function error] "
"expected a map of variables, but none was provided", FL_AT);
}
std::map<std::string, scalar>::const_iterator it = variables->find(variable);
if (it != variables->end()) result = it->second;
- else throw fl::Exception("[function error] "
+ else throw Exception("[function error] "
"unknown variable <" + variable + ">", FL_AT);
} else {
result = value;
@@ -178,6 +155,36 @@ namespace fl {
return result;
}
+ std::size_t Function::Node::treeSize(const Node* root) const {
+ if (not root) root = this;
+ std::size_t result = 0;
+ if (root->left.get()) {
+ result += treeSize(root->left.get());
+ }
+ if (root->right.get()) {
+ result += treeSize(root->right.get());
+ }
+ if (root->element.get()) {
+ result += 1;
+ }
+ return result;
+ }
+
+ std::size_t Function::Node::treeSize(Element::Type type, const Node* root) const {
+ if (not root) root = this;
+ std::size_t result = 0;
+ if (root->left.get()) {
+ result += treeSize(type, root->left.get());
+ }
+ if (root->right.get()) {
+ result += treeSize(type, root->right.get());
+ }
+ if (root->element.get() and root->element->type == type) {
+ result += 1;
+ }
+ return result;
+ }
+
Function::Node* Function::Node::clone() const {
return new Node(*this);
}
@@ -186,14 +193,14 @@ namespace fl {
std::ostringstream ss;
if (element.get()) ss << element->name;
else if (not variable.empty()) ss << variable;
- else ss << fl::Op::str(value);
+ else ss << Op::str(value);
return ss.str();
}
std::string Function::Node::toPrefix(const Node* node) const {
if (not node) node = this;
- if (not fl::Op::isNaN(node->value)) { //is terminal
- return fl::Op::str(node->value);
+ if (not Op::isNaN(node->value)) { //is terminal
+ return Op::str(node->value);
}
if (not node->variable.empty()) {
return node->variable;
@@ -210,8 +217,8 @@ namespace fl {
std::string Function::Node::toInfix(const Node* node) const {
if (not node) node = this;
- if (not fl::Op::isNaN(node->value)) { //is proposition
- return fl::Op::str(node->value);
+ if (not Op::isNaN(node->value)) { //is proposition
+ return Op::str(node->value);
}
if (not node->variable.empty()) {
return node->variable;
@@ -228,8 +235,8 @@ namespace fl {
std::string Function::Node::toPostfix(const Node* node) const {
if (not node) node = this;
- if (not fl::Op::isNaN(node->value)) { //is proposition
- return fl::Op::str(node->value);
+ if (not Op::isNaN(node->value)) { //is proposition
+ return Op::str(node->value);
}
if (not node->variable.empty()) {
return node->variable;
@@ -249,8 +256,7 @@ namespace fl {
**********************************/
Function::Function(const std::string& name,
const std::string& formula, const Engine* engine)
- : Term(name), _root(fl::null), _formula(formula), _engine(engine) {
- }
+ : Term(name), _root(fl::null), _formula(formula), _engine(engine) { }
Function::Function(const Function& other) : Term(other),
_root(fl::null), _formula(other._formula), _engine(other._engine) {
@@ -271,25 +277,41 @@ namespace fl {
return *this;
}
- Function::~Function() {
- }
+ Function::~Function() { }
std::string Function::className() const {
return "Function";
}
+ Complexity Function::complexity() const {
+ Complexity result;
+ result.comparison(2 + 2); //membership(scalar) + membership(std::map)
+ if (_engine) { //insert variables in map
+ const std::size_t engineVariables = _engine->variables().size();
+ result.function(engineVariables * std::log(scalar(variables.size() + engineVariables)));
+ result.function(1 * std::log(scalar(variables.size() + engineVariables)));
+ }
+ if (_root.get()) {
+ //Node::evaluate multiplies by tree size
+ const scalar treeSize = scalar(_root->treeSize());
+ result.comparison(3 * treeSize); //if element, unary, binary
+ result.function(treeSize * std::log(treeSize)); //only operands in tree
+ }
+ return result;
+ }
+
scalar Function::membership(scalar x) const {
- if (not this->_root.get()) {
- throw fl::Exception("[function error] function <" + _formula + "> not loaded.", FL_AT);
+ if (not _root.get()) {
+ throw Exception("[function error] function <" + _formula + "> not loaded.", FL_AT);
}
- if (this->_engine) {
- for (int i = 0; i < this->_engine->numberOfInputVariables(); ++i) {
- InputVariable* input = this->_engine->getInputVariable(i);
- this->variables[input->getName()] = input->getInputValue();
+ if (_engine) {
+ for (std::size_t i = 0; i < _engine->numberOfInputVariables(); ++i) {
+ InputVariable* input = _engine->getInputVariable(i);
+ this->variables[input->getName()] = input->getValue();
}
- for (int i = 0; i < this->_engine->numberOfOutputVariables(); ++i) {
- OutputVariable* output = this->_engine->getOutputVariable(i);
- this->variables[output->getName()] = output->getOutputValue();
+ for (std::size_t i = 0; i < _engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* output = _engine->getOutputVariable(i);
+ this->variables[output->getName()] = output->getValue();
}
}
this->variables["x"] = x;
@@ -297,15 +319,15 @@ namespace fl {
}
scalar Function::evaluate(const std::map<std::string, scalar>* localVariables) const {
- if (not this->_root.get())
- throw fl::Exception("[function error] evaluation failed because the function is not loaded", FL_AT);
+ if (not _root.get())
+ throw Exception("[function error] evaluation failed because the function is not loaded", FL_AT);
if (localVariables)
- return this->_root->evaluate(localVariables);
- return this->_root->evaluate(&this->variables);
+ return _root->evaluate(localVariables);
+ return _root->evaluate(&this->variables);
}
std::string Function::parameters() const {
- return _formula;
+ return getFormula();
}
void Function::configure(const std::string& parameters) {
@@ -329,18 +351,17 @@ namespace fl {
}
void Function::load() {
- load(this->_formula);
+ load(getFormula());
}
void Function::load(const std::string& formula) {
- load(formula, this->_engine);
+ load(formula, getEngine());
}
void Function::load(const std::string& formula,
const Engine* engine) {
- unload();
- this->_formula = formula;
- this->_engine = engine;
+ setFormula(formula);
+ setEngine(engine);
this->_root.reset(parse(formula));
membership(0.0); //make sure function evaluates without throwing exception.
}
@@ -373,23 +394,32 @@ namespace fl {
return new Function;
}
+ void Function::updateReference(const Engine* engine) {
+ setEngine(engine);
+ try {
+ load();
+ } catch (...) {
+ //ignore
+ }
+ }
+
std::string Function::space(const std::string& formula) const {
std::vector<std::string> chars;
chars.push_back("(");
chars.push_back(")");
chars.push_back(",");
- std::vector<std::string> operators = fl::FactoryManager::instance()->function()->availableOperators();
+ std::vector<std::string> operators = FactoryManager::instance()->function()->availableOperators();
for (std::size_t i = 0; i < operators.size(); ++i) {
- if (not (operators.at(i) == fl::Rule::andKeyword() or
- operators.at(i) == fl::Rule::orKeyword())) {
+ if (not (operators.at(i) == Rule::andKeyword() or
+ operators.at(i) == Rule::orKeyword())) {
chars.push_back(operators.at(i));
}
}
std::string result = formula;
for (std::size_t i = 0; i < chars.size(); ++i) {
- result = fl::Op::findReplace(result, chars.at(i), " " + chars.at(i) + " ");
+ result = Op::findReplace(result, chars.at(i), " " + chars.at(i) + " ");
}
return result;
}
@@ -397,7 +427,7 @@ namespace fl {
/****************************************
* The Glorious Parser
* Shunting-yard algorithm
- * TODO: Maybe change it for http://en.wikipedia.org/wiki/Operator-precedence_parser
+ * @todo: maybe change it for http://en.wikipedia.org/wiki/Operator-precedence_parser
***************************************/
std::string Function::toPostfix(const std::string& formula) const {
@@ -408,7 +438,7 @@ namespace fl {
std::stringstream tokenizer(spacedFormula);
std::string token;
- FunctionFactory* factory = fl::FactoryManager::instance()->function();
+ FunctionFactory* factory = FactoryManager::instance()->function();
while (tokenizer >> token) {
Element* element = factory->getObject(token);
bool isOperand = not element and token != "(" and token != ")" and token != ",";
@@ -427,7 +457,7 @@ namespace fl {
if (stack.empty() or stack.top() != "(") {
std::ostringstream ex;
ex << "[parsing error] mismatching parentheses in: " << formula;
- throw fl::Exception(ex.str(), FL_AT);
+ throw Exception(ex.str(), FL_AT);
}
} else if (element and element->isOperator()) {
@@ -457,7 +487,7 @@ namespace fl {
if (stack.empty() or stack.top() != "(") {
std::ostringstream ex;
ex << "[parsing error] mismatching parentheses in: " << formula;
- throw fl::Exception(ex.str(), FL_AT);
+ throw Exception(ex.str(), FL_AT);
}
stack.pop(); //get rid of "("
@@ -470,7 +500,7 @@ namespace fl {
} else {
std::ostringstream ex;
ex << "[parsing error] unexpected error with token <" << token << ">";
- throw fl::Exception(ex.str(), FL_AT);
+ throw Exception(ex.str(), FL_AT);
}
}
@@ -478,7 +508,7 @@ namespace fl {
if (stack.top() == "(" or stack.top() == ")") {
std::ostringstream ex;
ex << "[parsing error] mismatching parentheses in: " << formula;
- throw fl::Exception(ex.str(), FL_AT);
+ throw Exception(ex.str(), FL_AT);
}
queue.push(stack.top());
stack.pop();
@@ -490,43 +520,31 @@ namespace fl {
queue.pop();
if (not queue.empty()) ssPostfix << " ";
}
- // FL_DBG("postfix=" << ssPostfix.str());
return ssPostfix.str();
}
- // bool FunctionFactory::isOperand(const std::string& name) const {
- // //An operand is not a parenthesis...
- // if (name == "(" or name == ")" or name == ",") return false;
- // //nor an operator...
- // if (isOperator(name)) return false;
- // //nor a function...
- // if (isFunction(name)) return false;
- // //...it is everything else :)
- // return true;
- // }
-
Function::Node* Function::parse(const std::string& formula) {
if (formula.empty())
- throw fl::Exception("[function error] formula is empty", FL_AT);
+ throw Exception("[function error] formula is empty", FL_AT);
std::string postfix = toPostfix(formula);
std::stack<Node*> stack;
std::istringstream tokenizer(postfix);
std::string token;
- FunctionFactory* factory = fl::FactoryManager::instance()->function();
+ FunctionFactory* factory = FactoryManager::instance()->function();
while (tokenizer >> token) {
Element* element = factory->getObject(token);
bool isOperand = not element and token != "(" and token != ")" and token != ",";
if (element) {
- if (element->arity > (int) stack.size()) {
+ if (element->arity > static_cast<int> (stack.size())) {
std::ostringstream ss;
ss << "[function error] " << (element->isOperator() ? "operator" : "function") <<
" <" << element->name << "> has arity <" << element->arity << ">, "
"but found <" << stack.size() << "> element" <<
(stack.size() == 1 ? "" : "s");
- throw fl::Exception(ss.str(), FL_AT);
+ throw Exception(ss.str(), FL_AT);
}
Node* node = new Node(element->clone());
@@ -541,10 +559,10 @@ namespace fl {
} else if (isOperand) {
Node* node;
try {
- scalar value = fl::Op::toScalar(token);
+ scalar value = Op::toScalar(token);
node = new Node(value);
} catch (std::exception& ex) {
- (void) ex;
+ FL_IUNUSED(ex);
node = new Node(token);
}
stack.push(node);
@@ -552,49 +570,9 @@ namespace fl {
}
if (stack.size() != 1)
- throw fl::Exception("[function error] ill-formed formula <" + formula + ">", FL_AT);
+ throw Exception("[function error] ill-formed formula <" + formula + ">", FL_AT);
return stack.top();
}
- void Function::main() {
- Function f;
- std::string text = "3+4*2/(1-5)^2^3";
- FL_LOG(f.toPostfix(text));
- FL_LOG("P: " << f.parse(text)->toInfix());
- FL_LOG(">" << f.parse(text)->evaluate());
- //3 4 2 * 1 5 - 2 3 ^ ^ / +
-
- f.variables["y"] = 1.0;
- text = "sin(y*x)^2/x";
- FL_LOG("pre: " << f.parse(text)->toPrefix());
- FL_LOG("in: " << f.parse(text)->toInfix());
- FL_LOG("pos: " << f.parse(text)->toPostfix());
- f.load(text);
- FL_LOG("Result: " << f.membership(1));
- //y x * sin 2 ^ x /
-
-
- text = "(Temperature is High and Oxygen is Low) or "
- "(Temperature is Low and (Oxygen is Low or Oxygen is High))";
- FL_LOG(f.toPostfix(text));
-
- f.variables["pi"] = 3.14;
- text = "-5 *4/sin(-pi/2)";
- FL_LOG(f.toPostfix(text));
- try {
- FL_LOG(f.parse(text)->evaluate());
- } catch (std::exception& e) {
- FL_LOG(e.what());
- }
- f.variables["pi"] = 3.14;
- text = "~5 *4/sin(~pi/2)";
- FL_LOG(f.toPostfix(text));
- try {
- FL_LOG(f.parse(text)->evaluate(&f.variables));
- } catch (std::exception& e) {
- FL_LOG(e.what());
- }
- }
-
}