diff options
author | Johannes 'josch' Schauer <josch@debian.org> | 2019-01-27 13:56:24 +0100 |
---|---|---|
committer | Johannes 'josch' Schauer <josch@debian.org> | 2019-01-27 13:56:33 +0100 |
commit | 6ce553563bc795f389f639a3a8cdfe356de71441 (patch) | |
tree | da4c9ede3087ca534d93bc1ac5a14f044f036600 /fuzzylite/src | |
parent | bbefa170378553e5a6e0d72e4d52328b61f3e8ac (diff) |
new upstream version 6.0
Diffstat (limited to 'fuzzylite/src')
108 files changed, 6384 insertions, 4853 deletions
diff --git a/fuzzylite/src/Benchmark.cpp b/fuzzylite/src/Benchmark.cpp new file mode 100644 index 0000000..9d318df --- /dev/null +++ b/fuzzylite/src/Benchmark.cpp @@ -0,0 +1,460 @@ +/* + 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 FuzzyLite License included with the software. + + 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/Benchmark.h" + +#include "fl/Engine.h" +#include "fl/Operation.h" +#include "fl/rule/Rule.h" +#include "fl/rule/RuleBlock.h" +#include "fl/variable/InputVariable.h" +#include "fl/variable/OutputVariable.h" + +#ifdef FL_CPP98 +//timing is only available in C++11 +#else +#include <chrono> +#endif + +namespace fl { + + Benchmark::Benchmark(const std::string& name, Engine* engine, scalar tolerance) + : _name(name), _engine(engine), _tolerance(tolerance) { } + + Benchmark::~Benchmark() { } + + void Benchmark::setName(const std::string& name) { + this->_name = name; + } + + std::string Benchmark::getName() const { + return this->_name; + } + + void Benchmark::setEngine(Engine* engine) { + this->_engine = engine; + } + + Engine* Benchmark::getEngine() const { + return this->_engine; + } + + void Benchmark::setExpected(const std::vector<std::vector<scalar> >& expected) { + this->_expected = expected; + } + + const std::vector<std::vector<scalar> >& Benchmark::getExpected() const { + return this->_expected; + } + + void Benchmark::setObtained(const std::vector<std::vector<scalar> >& obtained) { + this->_obtained = obtained; + } + + const std::vector<std::vector<scalar> >& Benchmark::getObtained() const { + return this->_obtained; + } + + void Benchmark::setTimes(const std::vector<scalar> nanoSeconds) { + this->_times = nanoSeconds; + } + + const std::vector<scalar>& Benchmark::getTimes() const { + return this->_times; + } + + void Benchmark::setTolerance(scalar tolerance) { + this->_tolerance = tolerance; + } + + scalar Benchmark::getTolerance() const { + return this->_tolerance; + } + + void Benchmark::prepare(int values, FldExporter::ScopeOfValues scope) { + if (not _engine) { + throw Exception("[benchmark error] engine not set before preparing for values and scope", FL_AT); + } + int resolution; + if (scope == FldExporter::AllVariables) + resolution = -1 + (int) std::max(1.0, std::pow( + values, 1.0 / _engine->numberOfInputVariables())); + else //if (scope == EachVariable) + resolution = values - 1; + + std::vector<int> sampleValues, minSampleValues, maxSampleValues; + for (std::size_t i = 0; i < _engine->numberOfInputVariables(); ++i) { + sampleValues.push_back(0); + minSampleValues.push_back(0); + maxSampleValues.push_back(resolution); + } + + _expected = std::vector<std::vector<scalar> >(); + do { + std::vector<scalar> expectedValues(_engine->numberOfInputVariables()); + for (std::size_t i = 0; i < _engine->numberOfInputVariables(); ++i) { + InputVariable* inputVariable = _engine->getInputVariable(i); + expectedValues.at(i) = inputVariable->getMinimum() + + sampleValues.at(i) * inputVariable->range() / std::max(1, resolution); + } + _expected.push_back(expectedValues); + } while (Op::increment(sampleValues, minSampleValues, maxSampleValues)); + } + + void Benchmark::prepare(std::istream& reader, long numberOfLines) { + _expected = std::vector<std::vector<scalar> >(); + std::string line; + int lineNumber = 0; + while (lineNumber != numberOfLines and std::getline(reader, line)) { + ++lineNumber; + line = Op::trim(line); + if (line.empty() or line.at(0) == '#') + continue; //comments are ignored, blank lines are retained + std::vector<scalar> expectedValues; + if (lineNumber == 1) { //automatic detection of header. + try { + expectedValues = Op::toScalars(line); + } catch (std::exception&) { + continue; + } + } else { + expectedValues = Op::toScalars(line); + } + _expected.push_back(expectedValues); + } + } + + scalar Benchmark::runOnce() { + return run(1).front(); + } + + std::vector<scalar> Benchmark::run(int times) { + if (not _engine) { + throw Exception("[benchmark error] engine not set for benchmark", FL_AT); + } + std::vector<scalar> runTimes(times, fl::nan); + const std::size_t offset(_engine->inputVariables().size()); + for (int t = 0; t < times; ++t) { + _obtained = std::vector<std::vector<scalar> >(_expected.size(), + std::vector<scalar>(_engine->variables().size())); + _engine->restart(); + +#ifdef FL_CPP98 + //ignore timing +#else + auto start = std::chrono::high_resolution_clock::now(); +#endif + + for (std::size_t evaluation = 0; evaluation < _expected.size(); ++evaluation) { + const std::vector<scalar>& expectedValues = _expected[evaluation]; + std::vector<scalar>& obtainedValues = _obtained[evaluation]; + + if (expectedValues.size() < _engine->inputVariables().size()) { + std::ostringstream ex; + ex << "[benchmark error] the number of input values given <" << + expectedValues.size() << "> at line <" << (evaluation + 1) << "> " + "must be at least the same number of input variables " + "<" << _engine->inputVariables().size() << "> in the engine"; + throw Exception(ex.str()); + } + for (std::size_t i = 0; i < _engine->inputVariables().size(); ++i) { + _engine->getInputVariable(i)->setValue(expectedValues[i]); + obtainedValues[i] = expectedValues[i]; + } + + _engine->process(); + + for (std::size_t i = 0; i < _engine->outputVariables().size(); ++i) { + obtainedValues[i + offset] = _engine->getOutputVariable(i)->getValue(); + } + } + +#ifdef FL_CPP98 + //ignore timing +#else + auto end = std::chrono::high_resolution_clock::now(); + runTimes.at(t) = std::chrono::duration<scalar, std::nano>(end - start).count(); +#endif + } + _times.insert(_times.end(), runTimes.begin(), runTimes.end()); + return runTimes; + } + + void Benchmark::reset() { + this->_obtained.clear(); + this->_times.clear(); + } + + bool Benchmark::canComputeErrors() const { + return not (_engine == fl::null or _expected.empty() or _obtained.empty() + or _expected.size() != _obtained.size() + or _expected.front().size() != _obtained.front().size() + or _expected.front().size() != _engine->variables().size()); + } + + scalar Benchmark::meanSquaredError() const { + return meanSquaredError(fl::null); + } + + scalar Benchmark::meanSquaredError(const OutputVariable* outputVariable) const { + if (not canComputeErrors()) { + return fl::nan; + } + + scalar mse = 0.0; + int errors = 0; + const std::size_t offset = _engine->numberOfInputVariables(); + for (std::size_t i = 0; i < _expected.size(); ++i) { + const std::vector<scalar>& e = _expected.at(i); + const std::vector<scalar>& o = _obtained.at(i); + + for (std::size_t y = 0; y < _engine->numberOfOutputVariables(); ++y) { + if (outputVariable == fl::null + or outputVariable == _engine->getOutputVariable(y)) { + scalar difference = e.at(offset + y) - o.at(offset + y); + if (Op::isFinite(difference) + and not Op::isEq(difference, 0.0, _tolerance)) { + mse += difference * difference; + ++errors; + } + } + } + } + + if (errors > 0) { + mse /= errors; + } + return mse; + } + + int Benchmark::allErrors() const { + return allErrors(fl::null); + } + + int Benchmark::allErrors(const OutputVariable* outputVariable) const { + return numberOfErrors(All, outputVariable); + } + + int Benchmark::nonFiniteErrors() const { + return nonFiniteErrors(fl::null); + } + + int Benchmark::nonFiniteErrors(const OutputVariable* outputVariable) const { + return numberOfErrors(NonFinite, outputVariable); + } + + int Benchmark::accuracyErrors() const { + return accuracyErrors(fl::null); + } + + int Benchmark::accuracyErrors(const OutputVariable* outputVariable) const { + return numberOfErrors(Accuracy, outputVariable); + } + + int Benchmark::numberOfErrors(ErrorType errorType) const { + return numberOfErrors(errorType, fl::null); + } + + int Benchmark::numberOfErrors(ErrorType errorType, + const OutputVariable* outputVariable) const { + if (not canComputeErrors()) { + return -1; + } + + int errors = 0; + const std::size_t offset = _engine->numberOfInputVariables(); + for (std::size_t i = 0; i < _expected.size(); ++i) { + const std::vector<scalar>& e = _expected.at(i); + const std::vector<scalar>& o = _obtained.at(i); + + for (std::size_t y = 0; y < _engine->numberOfOutputVariables(); ++y) { + if (outputVariable == fl::null + or outputVariable == _engine->getOutputVariable(y)) { + if (not Op::isEq(e.at(y + offset), o.at(y + offset), _tolerance)) { + scalar difference = e.at(y + offset) - o.at(y + offset); + if (errorType == Accuracy and Op::isFinite(difference)) { + ++errors; + } else if (errorType == NonFinite and not Op::isFinite(difference)) { + ++errors; + } else if (errorType == All) { + ++errors; + } + } + } + } + } + + return errors; + } + + std::string Benchmark::stringOf(TimeUnit unit) { + if (unit == NanoSeconds) return "nanoseconds"; + if (unit == MicroSeconds) return "microseconds"; + if (unit == MilliSeconds) return "milliseconds"; + if (unit == Seconds) return "seconds"; + if (unit == Minutes) return "minutes"; + if (unit == Hours) return "hours"; + return "undefined"; + } + + scalar Benchmark::factorOf(TimeUnit unit) { + if (unit == NanoSeconds) return 1.0; + else if (unit == MicroSeconds) return 1.0e-3; + else if (unit == MilliSeconds) return 1.0e-6; + else if (unit == Seconds) return 1.0e-9; + else if (unit == Minutes) return 1.0e-9 / 60; + else if (unit == Hours) return 1.0e-9 / 3600; + return fl::nan; + } + + scalar Benchmark::convert(scalar x, TimeUnit from, TimeUnit to) { + return x * factorOf(to) / factorOf(from); + } + + std::vector<std::string> Benchmark::header(int runs, bool includeErrors) { + Benchmark result; + + Engine dummy; + dummy.addOutputVariable(new OutputVariable); //canCompute() == true + result.setEngine(&dummy); + + result.setTimes(std::vector<scalar>(runs, fl::nan)); + + if (includeErrors) { + std::vector<std::vector<scalar> > dummyVector(1, + std::vector<scalar>(1, fl::nan)); + result.setExpected(dummyVector); + result.setObtained(dummyVector); + } + std::vector<Benchmark::Result> dummyResults = result.results(); + + std::vector<std::string> names; + for (std::size_t i = 0; i < dummyResults.size(); ++i) { + names.push_back(dummyResults.at(i).first); + } + return names; + } + + std::vector<Benchmark::Result> Benchmark::results(TimeUnit timeUnit, bool includeTimes) const { + return results(fl::null, timeUnit, includeTimes); + } + + std::vector<Benchmark::Result> Benchmark::results( + const OutputVariable* outputVariable, TimeUnit unit, bool includeTimes) const { + if (not _engine) { + throw Exception("[benchmark error] engine not set for benchmark", FL_AT); + } + + std::vector<scalar> time = _times; + + std::vector<Result> result; + result.push_back(Result("library", fuzzylite::library())); + result.push_back(Result("name", _name)); + result.push_back(Result("inputs", Op::str(_engine->numberOfInputVariables()))); + result.push_back(Result("outputs", Op::str(_engine->numberOfOutputVariables()))); + result.push_back(Result("ruleBlocks", Op::str(_engine->numberOfRuleBlocks()))); + std::size_t rules = 0; + for (std::size_t i = 0; i < _engine->ruleBlocks().size(); ++i) { + rules += _engine->ruleBlocks().at(i)->rules().size(); + } + result.push_back(Result("rules", Op::str(rules))); + result.push_back(Result("runs", Op::str(_times.size()))); + result.push_back(Result("evaluations", Op::str(_expected.size()))); + if (canComputeErrors()) { + std::vector<std::string> names; + scalar meanRange = 0.0; + scalar rmse = std::sqrt(meanSquaredError(outputVariable)); + scalar nrmse = 0.0; + scalar weights = 0.0; + for (std::size_t i = 0; i < _engine->outputVariables().size(); ++i) { + const OutputVariable* y = _engine->outputVariables().at(i); + if (outputVariable == fl::null or outputVariable == y) { + names.push_back(y->getName()); + meanRange += y->range(); + nrmse += std::sqrt(meanSquaredError(y)) * 1.0 / y->range(); + weights += 1.0 / y->range(); + } + } + meanRange /= names.size(); + nrmse /= weights; + + result.push_back(Result("outputVariable", Op::join(names, ","))); + result.push_back(Result("range", Op::str(meanRange))); + + result.push_back(Result("tolerance", Op::str(getTolerance(), -1, std::ios_base::fmtflags(0x0)))); + result.push_back(Result("errors", Op::str(allErrors(outputVariable)))); + + result.push_back(Result("nfErrors", Op::str(nonFiniteErrors(outputVariable)))); + result.push_back(Result("accErrors", Op::str(accuracyErrors(outputVariable)))); + + result.push_back(Result("rmse", Op::str(rmse, 6, std::ios_base::scientific))); + result.push_back(Result("nrmse", Op::str(nrmse, 6, std::ios_base::scientific))); + } + result.push_back(Result("units", stringOf(unit))); + result.push_back(Result("sum(t)", Op::str(convert(Op::sum(time), NanoSeconds, unit), + unit == NanoSeconds ? 0 : fuzzylite::decimals()))); + result.push_back(Result("mean(t)", Op::str(convert(Op::mean(time), NanoSeconds, unit)))); + result.push_back(Result("sd(t)", Op::str(convert(Op::standardDeviation(time), NanoSeconds, unit)))); + + if (includeTimes) { + for (std::size_t i = 0; i < time.size(); ++i) { + result.push_back(Result("t" + Op::str(i + 1), + Op::str(time.at(i), unit == NanoSeconds ? 0 : fuzzylite::decimals()))); + } + } + return result; + } + + std::string Benchmark::format(std::vector<Result> results, TableShape shape, + TableContents contents, const std::string& delimiter) const { + std::ostringstream os; + + if (shape == Vertical) { + for (std::size_t i = 0; i < results.size(); ++i) { + Result pair = results.at(i); + if (contents bitand Header) { + os << pair.first; + } + if (contents == HeaderAndBody) { + os << delimiter; + } + if (contents bitand Body) { + os << pair.second; + } + if (i + 1 < results.size()) os << "\n"; + } + + } else if (shape == Horizontal) { + std::ostringstream header; + std::ostringstream body; + for (std::size_t i = 0; i < results.size(); ++i) { + Result pair = results.at(i); + if (contents bitand Header) { + header << pair.first; + if (i + 1 < results.size()) header << delimiter; + } + if (contents bitand Body) { + body << pair.second; + if (i + 1 < results.size()) body << delimiter; + } + } + if (contents bitand Header) os << header.str(); + if (contents == HeaderAndBody) os << "\n"; + if (contents bitand Body) os << body.str(); + } + return os.str(); + } +} diff --git a/fuzzylite/src/Complexity.cpp b/fuzzylite/src/Complexity.cpp new file mode 100644 index 0000000..8a91c60 --- /dev/null +++ b/fuzzylite/src/Complexity.cpp @@ -0,0 +1,283 @@ +/* + 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 FuzzyLite License included with the software. + + 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/Complexity.h" + +#include "fl/Engine.h" + +#include "fl/variable/InputVariable.h" +#include "fl/variable/OutputVariable.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" + +namespace fl { + + Complexity::Complexity(scalar all) : + _comparison(all), _arithmetic(all), _function(all) { } + + Complexity::Complexity(scalar comparison, scalar arithmetic, + scalar function) + : _comparison(comparison), _arithmetic(arithmetic), _function(function) { } + + Complexity::~Complexity() { } + + Complexity& Complexity::operator+=(const Complexity& other) { + return this->plus(other); + } + + Complexity& Complexity::operator-=(const Complexity& other) { + return this->minus(other); + } + + Complexity& Complexity::operator*=(const Complexity& other) { + return this->multiply(other); + } + + Complexity& Complexity::operator/=(const Complexity& other) { + return this->divide(other); + } + + Complexity Complexity::operator+(const Complexity& rhs) const { + return Complexity(*this).plus(rhs); + } + + Complexity Complexity::operator-(const Complexity& rhs) const { + return Complexity(*this).minus(rhs); + } + + Complexity Complexity::operator*(const Complexity& rhs) const { + return Complexity(*this).multiply(rhs); + } + + Complexity Complexity::operator/(const Complexity& rhs) const { + return Complexity(*this).divide(rhs); + } + + bool Complexity::operator==(const Complexity& rhs) const { + return equals(rhs); + } + + bool Complexity::operator!=(const Complexity& rhs) const { + return not equals(rhs); + } + + bool Complexity::operator<(const Complexity& rhs) const { + return lessThan(rhs); + } + + bool Complexity::operator<=(const Complexity& rhs) const { + return lessThanOrEqualsTo(rhs); + } + + bool Complexity::operator>(const Complexity& rhs) const { + return greaterThan(rhs); + } + + bool Complexity::operator>=(const Complexity& rhs) const { + return greaterThanOrEqualsTo(rhs); + } + + Complexity& Complexity::plus(const Complexity& other) { + this->_arithmetic += other._arithmetic; + this->_comparison += other._comparison; + this->_function += other._function; + return *this; + } + + Complexity& Complexity::plus(scalar x) { + return this->plus(Complexity().arithmetic(x).comparison(x).function(x)); + } + + Complexity& Complexity::minus(const Complexity& other) { + this->_comparison -= other._comparison; + this->_arithmetic -= other._arithmetic; + this->_function -= other._function; + return *this; + } + + Complexity& Complexity::minus(scalar x) { + return this->minus(Complexity().arithmetic(x).comparison(x).function(x)); + } + + Complexity& Complexity::multiply(const Complexity& other) { + this->_comparison *= other._comparison; + this->_arithmetic *= other._arithmetic; + this->_function *= other._function; + return *this; + } + + Complexity& Complexity::multiply(scalar x) { + return this->multiply(Complexity().arithmetic(x).comparison(x).function(x)); + } + + Complexity& Complexity::divide(const Complexity& other) { + this->_comparison /= other._comparison; + this->_arithmetic /= other._arithmetic; + this->_function /= other._function; + return *this; + } + + Complexity& Complexity::divide(scalar x) { + return this->divide(Complexity().arithmetic(x).comparison(x).function(x)); + } + + bool Complexity::equals(const Complexity& x, scalar macheps) const { + return Op::isEq(_comparison, x._comparison, macheps) and + Op::isEq(_arithmetic, x._arithmetic, macheps) and + Op::isEq(_function, x._function, macheps); + } + + bool Complexity::lessThan(const Complexity& x, scalar macheps) const { + return Op::isLt(_comparison, x._comparison, macheps) and + Op::isLt(_arithmetic, x._arithmetic, macheps) and + Op::isLt(_function, x._function, macheps); + } + + bool Complexity::lessThanOrEqualsTo(const Complexity& x, scalar macheps) const { + return Op::isLE(_comparison, x._comparison, macheps) and + Op::isLE(_arithmetic, x._arithmetic, macheps) and + Op::isLE(_function, x._function, macheps); + } + + bool Complexity::greaterThan(const Complexity& x, scalar macheps) const { + return Op::isGt(_comparison, x._comparison, macheps) and + Op::isGt(_arithmetic, x._arithmetic, macheps) and + Op::isGt(_function, x._function, macheps); + } + + bool Complexity::greaterThanOrEqualsTo(const Complexity& x, scalar macheps) const { + return Op::isGE(_comparison, x._comparison, macheps) and + Op::isGE(_arithmetic, x._arithmetic, macheps) and + Op::isGE(_function, x._function, macheps); + } + + Complexity& Complexity::comparison(scalar comparison) { + this->_comparison += comparison; + return *this; + } + + void Complexity::setComparison(scalar comparison) { + this->_comparison = comparison; + } + + scalar Complexity::getComparison() const { + return _comparison; + } + + Complexity& Complexity::arithmetic(scalar arithmetic) { + this->_arithmetic += arithmetic; + return *this; + } + + void Complexity::setArithmetic(scalar arithmetic) { + this->_arithmetic = arithmetic; + } + + scalar Complexity::getArithmetic() const { + return _arithmetic; + } + + Complexity& Complexity::function(scalar trigonometric) { + this->_function += trigonometric; + return *this; + } + + void Complexity::setFunction(scalar trigonometric) { + this->_function = trigonometric; + } + + scalar Complexity::getFunction() const { + return _function; + } + + std::vector<Complexity::Measure> Complexity::measures() const { + std::vector<Measure> result; + result.push_back(Measure("arithmetic", _arithmetic)); + result.push_back(Measure("comparison", _comparison)); + result.push_back(Measure("function", _function)); + return result; + } + + scalar Complexity::sum() const { + return _arithmetic + _comparison + _function; + } + + scalar Complexity::norm() const { + return std::sqrt(Complexity(*this).multiply(*this).sum()); + } + + std::string Complexity::toString() const { + std::vector<std::string> result; + result.push_back("a=" + Op::str(_arithmetic)); + result.push_back("c=" + Op::str(_comparison)); + result.push_back("f=" + Op::str(_function)); + return "C[" + Op::join(result, ", ") + "]"; + } + + Complexity Complexity::compute(const Engine* engine) const { + return engine->complexity(); + } + + Complexity Complexity::compute(const InputVariable* inputVariable) const { + return inputVariable->complexity(); + } + + Complexity Complexity::compute(const OutputVariable* outputVariable) const { + return outputVariable->complexity(); + } + + Complexity Complexity::compute(const RuleBlock* ruleBlock) const { + return ruleBlock->complexity(); + } + + Complexity Complexity::compute(const std::vector<InputVariable*>& inputVariables) const { + Complexity result; + for (std::size_t i = 0; i < inputVariables.size(); ++i) { + result += inputVariables.at(i)->complexity(); + } + return result; + } + + Complexity Complexity::compute(const std::vector<OutputVariable*>& outputVariables, + bool complexityOfDefuzzification) const { + Complexity result; + for (std::size_t i = 0; i < outputVariables.size(); ++i) { + if (complexityOfDefuzzification) + result += outputVariables.at(i)->complexityOfDefuzzification(); + else + result += outputVariables.at(i)->complexity(); + } + return result; + } + + Complexity Complexity::compute(const std::vector<Variable*>& variables) const { + Complexity result; + for (std::size_t i = 0; i < variables.size(); ++i) { + result += variables.at(i)->complexity(); + } + return result; + } + + Complexity Complexity::compute(const std::vector<RuleBlock*>& ruleBlocks) const { + Complexity result; + for (std::size_t i = 0; i < ruleBlocks.size(); ++i) { + result += ruleBlocks.at(i)->complexity(); + } + return result; + } + +} diff --git a/fuzzylite/src/Console.cpp b/fuzzylite/src/Console.cpp index 3a55fc5..5a00c50 100644 --- a/fuzzylite/src/Console.cpp +++ b/fuzzylite/src/Console.cpp @@ -1,37 +1,24 @@ /* - 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/Console.h" #include "fl/Headers.h" -#include <algorithm> -#include <cctype> #include <fstream> -#include <stdlib.h> -#include <utility> -#include <vector> #ifdef FL_UNIX #include <termios.h> @@ -40,9 +27,6 @@ #include <conio.h> #endif -#ifdef FL_CPP11 -#include <chrono> -#endif namespace fl { const std::string Console::KW_INPUT_FILE = "-i"; @@ -51,11 +35,16 @@ namespace fl { const std::string Console::KW_OUTPUT_FORMAT = "-of"; const std::string Console::KW_EXAMPLE = "-example"; const std::string Console::KW_DECIMALS = "-decimals"; - const std::string Console::KW_DATA_INPUT = "-d"; - const std::string Console::KW_DATA_MAXIMUM = "-dmaximum"; + const std::string Console::KW_DATA_INPUT_FILE = "-d"; + const std::string Console::KW_DATA_VALUES = "-values"; + const std::string Console::KW_DATA_VALUES_SCOPE = "-scope"; + const std::string Console::KW_DATA_EXPORT_HEADER = "-dheader"; const std::string Console::KW_DATA_EXPORT_INPUTS = "-dinputs"; + Console::Option::Option(const std::string& key, const std::string& value, const std::string& description) : + key(key), value(value), description(description) { } + std::vector<Console::Option> Console::availableOptions() { std::vector<Console::Option> options; options.push_back(Option(KW_INPUT_FILE, "inputfile", "file to import your engine from")); @@ -64,8 +53,9 @@ namespace fl { options.push_back(Option(KW_OUTPUT_FORMAT, "format", "format of the file to export (fll | fld | cpp | java | fis | fcl)")); options.push_back(Option(KW_EXAMPLE, "letter", "if not inputfile, built-in example to use as engine: (m)amdani or (t)akagi-sugeno")); options.push_back(Option(KW_DECIMALS, "number", "number of decimals to write floating-poing values")); - options.push_back(Option(KW_DATA_INPUT, "datafile", "if exporting to fld, file of input values to evaluate your engine on")); - options.push_back(Option(KW_DATA_MAXIMUM, "number", "if exporting to fld without datafile, maximum number of results to export")); + options.push_back(Option(KW_DATA_INPUT_FILE, "file", "if exporting to fld, FLD file of input values to evaluate your engine on")); + options.push_back(Option(KW_DATA_VALUES, "number", "if exporting to fld without datafile, number of results to export within scope (default: EachVariable)")); + options.push_back(Option(KW_DATA_VALUES_SCOPE, "scope", "if exporting to fld without datafile, scope of " + KW_DATA_VALUES + ": [EachVariable|AllVariables]")); options.push_back(Option(KW_DATA_EXPORT_HEADER, "boolean", "if true and exporting to fld, include headers")); options.push_back(Option(KW_DATA_EXPORT_INPUTS, "boolean", "if true and exporting to fld, include input values")); return options; @@ -74,14 +64,16 @@ namespace fl { std::string Console::usage() { std::vector<Console::Option> options = availableOptions(); std::ostringstream ss; - + ss << "========================================\n"; ss << "fuzzylite: a fuzzy logic control library\n"; - ss << "version: " << fuzzylite::longVersion() << "\n"; + ss << "version: " << fuzzylite::version() << "\n"; ss << "author: " << fuzzylite::author() << "\n"; ss << "license: " << fuzzylite::license() << "\n"; ss << "========================================\n\n"; ss << "usage: fuzzylite inputfile outputfile\n"; + ss << " or: fuzzylite benchmark engine.fll input.fld runs [output.tsv]\n"; + ss << " or: fuzzylite benchmarks fllFiles.txt fldFiles.txt runs [output.tsv]\n"; ss << " or: fuzzylite "; for (std::size_t i = 0; i < options.size(); ++i) { ss << "[" << options.at(i).key << " " << options.at(i).value << "] "; @@ -103,15 +95,15 @@ namespace fl { ss << "\n"; ss << "Visit " << fuzzylite::website() << " for more information.\n\n"; - ss << "Copyright (C) 2010-2015 FuzzyLite Limited.\n"; + ss << "Copyright (C) 2010-2017 by FuzzyLite Limited.\n"; ss << "All rights reserved."; - + return ss.str(); } - std::map<std::string, std::string> Console::parse(int argc, char** argv) { + std::map<std::string, std::string> Console::parse(int argc, const char* argv[]) { if ((argc - 1) % 2 != 0) { - throw fl::Exception("[option error] incomplete number of parameters [key value]", FL_AT); + throw Exception("[option error] incomplete number of parameters [key value]", FL_AT); } std::map<std::string, std::string> options; for (int i = 1; i < argc - 1; i += 2) { @@ -139,7 +131,7 @@ namespace fl { } } if (not isValid) { - throw fl::Exception("[option error] option <" + it->first + "> not recognized", FL_AT); + throw Exception("[option error] option <" + it->first + "> not recognized", FL_AT); } } } @@ -151,7 +143,7 @@ namespace fl { it = options.find(KW_DECIMALS); if (it != options.end()) { - fl::fuzzylite::setDecimals((int) fl::Op::toScalar(it->second)); + fuzzylite::setDecimals((int) Op::toScalar(it->second)); } std::string example; @@ -170,7 +162,7 @@ namespace fl { } else if (example == "t" or example == "ts" or example == "takagi-sugeno") { engine = takagiSugeno(); } else { - throw fl::Exception("[option error] example <" + example + "> not available", FL_AT); + throw Exception("[option error] example <" + example + "> not available", FL_AT); } inputFormat = "fll"; textEngine << FllExporter().toString(engine); @@ -179,12 +171,12 @@ namespace fl { } else { it = options.find(KW_INPUT_FILE); if (it == options.end()) { - throw fl::Exception("[option error] no input file specified", FL_AT); + throw Exception("[option error] no input file specified", FL_AT); } std::string inputFilename = it->second; std::ifstream inputFile(inputFilename.c_str()); if (not inputFile.is_open()) { - throw fl::Exception("[file error] file <" + inputFilename + "> could not be opened", FL_AT); + throw Exception("[file error] file <" + inputFilename + "> could not be opened", FL_AT); } std::string line; while (std::getline(inputFile, line)) { @@ -200,7 +192,7 @@ namespace fl { if (extensionIndex != std::string::npos) { inputFormat = inputFilename.substr(extensionIndex + 1); } else { - throw fl::Exception("[format error] unspecified format of input file", FL_AT); + throw Exception("[format error] unspecified format of input file", FL_AT); } } } @@ -220,7 +212,7 @@ namespace fl { if (extensionIndex != std::string::npos) { outputFormat = outputFilename.substr(extensionIndex + 1); } else { - throw fl::Exception("[format error] unspecified format of output file", FL_AT); + throw Exception("[format error] unspecified format of output file", FL_AT); } } @@ -230,7 +222,7 @@ namespace fl { } else { std::ofstream writer(outputFilename.c_str()); if (not writer.is_open()) { - throw fl::Exception("[file error] file <" + outputFilename + "> could not be created", FL_AT); + throw Exception("[file error] file <" + outputFilename + "> could not be created", FL_AT); } process(textEngine.str(), writer, inputFormat, outputFormat, options); writer.flush(); @@ -252,7 +244,7 @@ namespace fl { } else if ("fis" == inputFormat) { importer.reset(new FisImporter); } else { - throw fl::Exception("[import error] format <" + inputFormat + "> " + throw Exception("[import error] format <" + inputFormat + "> " "not supported", FL_AT); } @@ -262,7 +254,7 @@ namespace fl { std::map<std::string, std::string>::const_iterator it; FldExporter fldExporter; - fldExporter.setSeparator("\t"); + fldExporter.setSeparator(" "); bool exportHeaders = true; if ((it = options.find(KW_DATA_EXPORT_HEADER)) != options.end()) { exportHeaders = ("true" == it->second); @@ -273,22 +265,32 @@ namespace fl { exportInputValues = ("true" == it->second); } fldExporter.setExportInputValues(exportInputValues); - if ((it = options.find(KW_DATA_INPUT)) != options.end()) { + if ((it = options.find(KW_DATA_INPUT_FILE)) != options.end()) { std::ifstream dataFile(it->second.c_str()); if (not dataFile.is_open()) { - throw fl::Exception("[export error] file <" + it->second + "> could not be opened", FL_AT); + throw Exception("[export error] file <" + it->second + "> could not be opened", FL_AT); } try { fldExporter.write(engine.get(), writer, dataFile); } catch (std::exception& ex) { - (void) ex; + FL_IUNUSED(ex); dataFile.close(); throw; } } else { - if ((it = options.find(KW_DATA_MAXIMUM)) != options.end()) { - fldExporter.write(engine.get(), writer, (int) fl::Op::toScalar(it->second)); + if ((it = options.find(KW_DATA_VALUES)) != options.end()) { + int values = (int) Op::toScalar(it->second); + FldExporter::ScopeOfValues scope = FldExporter::EachVariable; + if ((it = options.find(KW_DATA_VALUES_SCOPE)) != options.end()) { + if ("AllVariables" == it->second) + scope = FldExporter::AllVariables; + else if ("EachVariable" == it->second) + scope = FldExporter::EachVariable; + else throw Exception("[export error] unknown scope of values <" + + it->second + ">", FL_AT); + } + fldExporter.write(engine.get(), writer, values, scope); } else { std::ostringstream buffer; buffer << "#FuzzyLite Interactive Console (press H for help)\n"; @@ -311,7 +313,7 @@ namespace fl { exporter.reset(new CppExporter); } else if ("java" == outputFormat) { exporter.reset(new JavaExporter); - } else throw fl::Exception("[export error] format <" + outputFormat + "> " + } else throw Exception("[export error] format <" + outputFormat + "> " "not supported", FL_AT); writer << exporter->toString(engine.get()); } @@ -321,14 +323,14 @@ namespace fl { int ch = 0; #ifdef FL_UNIX struct termios oldt, newt; - tcgetattr(STDIN_FILENO, &oldt); + ::tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - ch = getchar(); - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + ::tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ch = ::getchar(); + ::tcsetattr(STDIN_FILENO, TCSANOW, &oldt); #elif defined(FL_WINDOWS) - ch = _getch(); + ch = ::_getch(); #endif return ch; } @@ -349,13 +351,17 @@ namespace fl { ch = readCharacter(); + if (ch == EOF) { + break; + } + if (std::isspace(ch)) { - scalar value = engine->getInputVariable(inputValues.size())->getInputValue(); + scalar value = engine->getInputVariable(inputValues.size())->getValue(); try { - value = fl::Op::toScalar(inputValue.str()); + value = Op::toScalar(inputValue.str()); } catch (std::exception& ex) { - (void) ex; - buffer << "[" << fl::Op::str(value) << "]"; + FL_IUNUSED(ex); + buffer << "[" << Op::str(value) << "]"; } buffer << space; inputValue.str(""); @@ -375,7 +381,7 @@ namespace fl { case 'r': case 'R': engine->restart(); buffer << "#[Restart]"; - //fall through + continue; //fall through case 'd': case 'D': inputValues.clear(); buffer << "#[Discard]\n>"; @@ -388,15 +394,15 @@ namespace fl { for (std::size_t i = 0; i < inputValues.size(); ++i) { InputVariable* inputVariable = engine->inputVariables().at(i); - inputVariable->setInputValue(inputValues.at(i)); + inputVariable->setValue(inputValues.at(i)); } std::vector<scalar> missingInputs; for (std::size_t i = inputValues.size(); i < engine->inputVariables().size(); ++i) { InputVariable* inputVariable = engine->inputVariables().at(i); - missingInputs.push_back(inputVariable->getInputValue()); + missingInputs.push_back(inputVariable->getValue()); } inputValues.clear(); - buffer << fl::Op::join(missingInputs, space); + buffer << Op::join(missingInputs, space); if (not missingInputs.empty()) buffer << space; buffer << "=" << space; try { @@ -405,9 +411,9 @@ namespace fl { for (std::size_t i = 0; i < engine->outputVariables().size(); ++i) { OutputVariable* outputVariable = engine->outputVariables().at(i); outputVariable->defuzzify(); - outputValues.push_back(outputVariable->getOutputValue()); + outputValues.push_back(outputVariable->getValue()); } - buffer << fl::Op::join(outputValues, space) << "\n>"; + buffer << Op::join(outputValues, space) << "\n>"; } catch (std::exception& ex) { buffer << "#[Error: " << ex.what() << "]"; @@ -422,7 +428,7 @@ namespace fl { inputValue.str(""); break; } - } while (not (ch == 'Q' or ch == 'q')); + } while (not (ch == 'Q' or ch == 'q' or ch == 4)); writer << std::endl; } @@ -439,145 +445,283 @@ namespace fl { } Engine* Console::mamdani() { - Engine* engine = new Engine("simple-dimmer"); - - InputVariable* ambient = new InputVariable("Ambient", 0, 1); - ambient->addTerm(new Triangle("DARK", .0, .25, .5)); - ambient->addTerm(new Triangle("MEDIUM", .25, .5, .75)); - ambient->addTerm(new Triangle("BRIGHT", .5, .75, 1)); + Engine* engine = new Engine; + engine->setName("simple-dimmer"); + engine->setDescription(""); + + InputVariable* ambient = new InputVariable; + ambient->setName("ambient"); + ambient->setDescription(""); + ambient->setEnabled(true); + ambient->setRange(0.000, 1.000); + ambient->setLockValueInRange(false); + ambient->addTerm(new Triangle("DARK", 0.000, 0.250, 0.500)); + ambient->addTerm(new Triangle("MEDIUM", 0.250, 0.500, 0.750)); + ambient->addTerm(new Triangle("BRIGHT", 0.500, 0.750, 1.000)); engine->addInputVariable(ambient); - - OutputVariable* power = new OutputVariable("Power", 0, 2); + OutputVariable* power = new OutputVariable; + power->setName("power"); + power->setDescription(""); + power->setEnabled(true); + power->setRange(0.000, 2.000); + power->setLockValueInRange(false); + power->setAggregation(new Maximum); + power->setDefuzzifier(new Centroid(200)); power->setDefaultValue(fl::nan); - power->addTerm(new Triangle("LOW", 0.0, 0.5, 1)); - power->addTerm(new Triangle("MEDIUM", 0.5, 1, 1.5)); - power->addTerm(new Triangle("HIGH", 1, 1.5, 2)); + power->setLockPreviousValue(false); + power->addTerm(new Triangle("LOW", 0.000, 0.500, 1.000)); + power->addTerm(new Triangle("MEDIUM", 0.500, 1.000, 1.500)); + power->addTerm(new Triangle("HIGH", 1.000, 1.500, 2.000)); engine->addOutputVariable(power); - RuleBlock* ruleblock = new RuleBlock(); - ruleblock->addRule(Rule::parse("if Ambient is DARK then Power is HIGH", engine)); - ruleblock->addRule(Rule::parse("if Ambient is MEDIUM then Power is MEDIUM", engine)); - ruleblock->addRule(Rule::parse("if Ambient is BRIGHT then Power is LOW", engine)); - - engine->addRuleBlock(ruleblock); - - engine->configure("", "", "Minimum", "Maximum", "Centroid"); + RuleBlock* ruleBlock = new RuleBlock; + ruleBlock->setName(""); + ruleBlock->setDescription(""); + ruleBlock->setEnabled(true); + ruleBlock->setConjunction(fl::null); + ruleBlock->setDisjunction(fl::null); + ruleBlock->setImplication(new Minimum); + ruleBlock->setActivation(new General); + ruleBlock->addRule(Rule::parse("if ambient is DARK then power is HIGH", engine)); + ruleBlock->addRule(Rule::parse("if ambient is MEDIUM then power is MEDIUM", engine)); + ruleBlock->addRule(Rule::parse("if ambient is BRIGHT then power is LOW", engine)); + engine->addRuleBlock(ruleBlock); return engine; } Engine* Console::takagiSugeno() { - Engine* engine = new Engine("approximation of sin(x)/x"); - - fl::InputVariable* inputX = new fl::InputVariable("inputX"); - inputX->setRange(0, 10); - inputX->addTerm(new fl::Triangle("NEAR_1", 0, 1, 2)); - inputX->addTerm(new fl::Triangle("NEAR_2", 1, 2, 3)); - inputX->addTerm(new fl::Triangle("NEAR_3", 2, 3, 4)); - inputX->addTerm(new fl::Triangle("NEAR_4", 3, 4, 5)); - inputX->addTerm(new fl::Triangle("NEAR_5", 4, 5, 6)); - inputX->addTerm(new fl::Triangle("NEAR_6", 5, 6, 7)); - inputX->addTerm(new fl::Triangle("NEAR_7", 6, 7, 8)); - inputX->addTerm(new fl::Triangle("NEAR_8", 7, 8, 9)); - inputX->addTerm(new fl::Triangle("NEAR_9", 8, 9, 10)); + Engine* engine = new Engine; + engine->setName("approximation"); + engine->setDescription("approximation of sin(x)/x"); + + InputVariable* inputX = new InputVariable; + inputX->setName("inputX"); + inputX->setDescription("value of x"); + inputX->setEnabled(true); + inputX->setRange(0.000, 10.000); + inputX->setLockValueInRange(false); + inputX->addTerm(new Triangle("NEAR_1", 0.000, 1.000, 2.000)); + inputX->addTerm(new Triangle("NEAR_2", 1.000, 2.000, 3.000)); + inputX->addTerm(new Triangle("NEAR_3", 2.000, 3.000, 4.000)); + inputX->addTerm(new Triangle("NEAR_4", 3.000, 4.000, 5.000)); + inputX->addTerm(new Triangle("NEAR_5", 4.000, 5.000, 6.000)); + inputX->addTerm(new Triangle("NEAR_6", 5.000, 6.000, 7.000)); + inputX->addTerm(new Triangle("NEAR_7", 6.000, 7.000, 8.000)); + inputX->addTerm(new Triangle("NEAR_8", 7.000, 8.000, 9.000)); + inputX->addTerm(new Triangle("NEAR_9", 8.000, 9.000, 10.000)); engine->addInputVariable(inputX); - - fl::OutputVariable* outputFx = new fl::OutputVariable("outputFx"); - outputFx->setRange(-1, 1); + OutputVariable* outputFx = new OutputVariable; + outputFx->setName("outputFx"); + outputFx->setDescription("value of the approximation of x"); + outputFx->setEnabled(true); + outputFx->setRange(-1.000, 1.000); + outputFx->setLockValueInRange(false); + outputFx->setAggregation(fl::null); + outputFx->setDefuzzifier(new WeightedAverage("Automatic")); outputFx->setDefaultValue(fl::nan); - outputFx->setLockPreviousOutputValue(true); //To use its value with diffFx - outputFx->addTerm(new Constant("f1", 0.84)); - outputFx->addTerm(new Constant("f2", 0.45)); - outputFx->addTerm(new Constant("f3", 0.04)); - outputFx->addTerm(new Constant("f4", -0.18)); - outputFx->addTerm(new Constant("f5", -0.19)); - outputFx->addTerm(new Constant("f6", -0.04)); - outputFx->addTerm(new Constant("f7", 0.09)); - outputFx->addTerm(new Constant("f8", 0.12)); - outputFx->addTerm(new Constant("f9", 0.04)); + outputFx->setLockPreviousValue(true); + outputFx->addTerm(new Constant("f1", 0.840)); + outputFx->addTerm(new Constant("f2", 0.450)); + outputFx->addTerm(new Constant("f3", 0.040)); + outputFx->addTerm(new Constant("f4", -0.180)); + outputFx->addTerm(new Constant("f5", -0.190)); + outputFx->addTerm(new Constant("f6", -0.040)); + outputFx->addTerm(new Constant("f7", 0.090)); + outputFx->addTerm(new Constant("f8", 0.120)); + outputFx->addTerm(new Constant("f9", 0.040)); engine->addOutputVariable(outputFx); - fl::OutputVariable* trueFx = new fl::OutputVariable("trueFx"); - trueFx->setRange(fl::nan, fl::nan); - trueFx->setLockPreviousOutputValue(true); //To use its value with diffFx - trueFx->addTerm(fl::Function::create("fx", "sin(inputX)/inputX", engine)); - engine->addOutputVariable(trueFx); - - fl::OutputVariable* diffFx = new fl::OutputVariable("diffFx"); - diffFx->addTerm(fl::Function::create("diff", "fabs(outputFx-trueFx)", engine)); - diffFx->setRange(fl::nan, fl::nan); - // diffFx->setLockValidOutput(true); //To use in input diffPreviousFx - engine->addOutputVariable(diffFx); - - fl::RuleBlock* block = new fl::RuleBlock(); - block->addRule(fl::Rule::parse("if inputX is NEAR_1 then outputFx is f1", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_2 then outputFx is f2", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_3 then outputFx is f3", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_4 then outputFx is f4", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_5 then outputFx is f5", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_6 then outputFx is f6", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_7 then outputFx is f7", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_8 then outputFx is f8", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_9 then outputFx is f9", engine)); - block->addRule(fl::Rule::parse("if inputX is any then trueFx is fx and diffFx is diff", engine)); - engine->addRuleBlock(block); - - engine->configure("", "", "AlgebraicProduct", "AlgebraicSum", "WeightedAverage"); + OutputVariable* trueValue = new OutputVariable; + trueValue->setName("trueValue"); + trueValue->setDescription("value of f(x)=sin(x)/x"); + trueValue->setEnabled(true); + trueValue->setRange(-1.060, 1.000); + trueValue->setLockValueInRange(false); + trueValue->setAggregation(fl::null); + trueValue->setDefuzzifier(new WeightedAverage("Automatic")); + trueValue->setDefaultValue(fl::nan); + trueValue->setLockPreviousValue(true); + trueValue->addTerm(Function::create("fx", "sin(inputX)/inputX", engine)); + engine->addOutputVariable(trueValue); + + OutputVariable* difference = new OutputVariable; + difference->setName("difference"); + difference->setDescription("error e=f(x) - f'(x)"); + difference->setEnabled(true); + difference->setRange(-1.000, 1.000); + difference->setLockValueInRange(false); + difference->setAggregation(fl::null); + difference->setDefuzzifier(new WeightedAverage("Automatic")); + difference->setDefaultValue(fl::nan); + difference->setLockPreviousValue(false); + difference->addTerm(Function::create("error", "outputFx-trueValue", engine)); + engine->addOutputVariable(difference); + + RuleBlock* ruleBlock = new RuleBlock; + ruleBlock->setName(""); + ruleBlock->setDescription(""); + ruleBlock->setEnabled(true); + ruleBlock->setConjunction(fl::null); + ruleBlock->setDisjunction(fl::null); + ruleBlock->setImplication(new AlgebraicProduct); + ruleBlock->setActivation(new General); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_1 then outputFx is f1", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_2 then outputFx is f2", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_3 then outputFx is f3", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_4 then outputFx is f4", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_5 then outputFx is f5", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_6 then outputFx is f6", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_7 then outputFx is f7", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_8 then outputFx is f8", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_9 then outputFx is f9", engine)); + ruleBlock->addRule(Rule::parse("if inputX is any then trueValue is fx and difference is error", engine)); + engine->addRuleBlock(ruleBlock); + + return engine; + } + + Engine* Console::hybrid() { + Engine* engine = new Engine; + engine->setName("tipper"); + engine->setDescription("(service and food) -> (tip)"); + + InputVariable* service = new InputVariable; + service->setName("service"); + service->setDescription("quality of service"); + service->setEnabled(true); + service->setRange(0.000, 10.000); + service->setLockValueInRange(true); + service->addTerm(new Trapezoid("poor", 0.000, 0.000, 2.500, 5.000)); + service->addTerm(new Triangle("good", 2.500, 5.000, 7.500)); + service->addTerm(new Trapezoid("excellent", 5.000, 7.500, 10.000, 10.000)); + engine->addInputVariable(service); + + InputVariable* food = new InputVariable; + food->setName("food"); + food->setDescription("quality of food"); + food->setEnabled(true); + food->setRange(0.000, 10.000); + food->setLockValueInRange(true); + food->addTerm(new Trapezoid("rancid", 0.000, 0.000, 2.500, 7.500)); + food->addTerm(new Trapezoid("delicious", 2.500, 7.500, 10.000, 10.000)); + engine->addInputVariable(food); + + OutputVariable* mTip = new OutputVariable; + mTip->setName("mTip"); + mTip->setDescription("tip based on Mamdani inference"); + mTip->setEnabled(true); + mTip->setRange(0.000, 30.000); + mTip->setLockValueInRange(false); + mTip->setAggregation(new Maximum); + mTip->setDefuzzifier(new Centroid(100)); + mTip->setDefaultValue(fl::nan); + mTip->setLockPreviousValue(false); + mTip->addTerm(new Triangle("cheap", 0.000, 5.000, 10.000)); + mTip->addTerm(new Triangle("average", 10.000, 15.000, 20.000)); + mTip->addTerm(new Triangle("generous", 20.000, 25.000, 30.000)); + engine->addOutputVariable(mTip); + + OutputVariable* tsTip = new OutputVariable; + tsTip->setName("tsTip"); + tsTip->setDescription("tip based on Takagi-Sugeno inference"); + tsTip->setEnabled(true); + tsTip->setRange(0.000, 30.000); + tsTip->setLockValueInRange(false); + tsTip->setAggregation(fl::null); + tsTip->setDefuzzifier(new WeightedAverage("TakagiSugeno")); + tsTip->setDefaultValue(fl::nan); + tsTip->setLockPreviousValue(false); + tsTip->addTerm(new Constant("cheap", 5.000)); + tsTip->addTerm(new Constant("average", 15.000)); + tsTip->addTerm(new Constant("generous", 25.000)); + engine->addOutputVariable(tsTip); + + RuleBlock* mamdaniRuleBlock = new RuleBlock; + mamdaniRuleBlock->setName("mamdani"); + mamdaniRuleBlock->setDescription("Mamdani inference"); + mamdaniRuleBlock->setEnabled(true); + mamdaniRuleBlock->setConjunction(new AlgebraicProduct); + mamdaniRuleBlock->setDisjunction(new AlgebraicSum); + mamdaniRuleBlock->setImplication(new Minimum); + mamdaniRuleBlock->setActivation(new General); + mamdaniRuleBlock->addRule(Rule::parse("if service is poor or food is rancid then mTip is cheap", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is good then mTip is average", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is excellent or food is delicious then mTip is generous with 0.5", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is excellent and food is delicious then mTip is generous with 1.0", engine)); + engine->addRuleBlock(mamdaniRuleBlock); + + RuleBlock* takagiSugenoRuleBlock = new RuleBlock; + takagiSugenoRuleBlock->setName("takagiSugeno"); + takagiSugenoRuleBlock->setDescription("Takagi-Sugeno inference"); + takagiSugenoRuleBlock->setEnabled(true); + takagiSugenoRuleBlock->setConjunction(new AlgebraicProduct); + takagiSugenoRuleBlock->setDisjunction(new AlgebraicSum); + takagiSugenoRuleBlock->setImplication(fl::null); + takagiSugenoRuleBlock->setActivation(new General); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is poor or food is rancid then tsTip is cheap", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is good then tsTip is average", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is excellent or food is delicious then tsTip is generous with 0.5", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is excellent and food is delicious then tsTip is generous with 1.0", engine)); + engine->addRuleBlock(takagiSugenoRuleBlock); return engine; } void Console::exportAllExamples(const std::string& from, const std::string& to) { - Console::exportAllExamples(from, to, "."); + Console::exportAllExamples(from, to, "./", "/tmp/"); } - void Console::exportAllExamples(const std::string& from, const std::string& to, const std::string& path) { + void Console::exportAllExamples(const std::string& from, const std::string& to, + const std::string& sourcePath, const std::string& targetPath) { std::vector<std::string> examples; - examples.push_back("/mamdani/AllTerms"); - // examples.push_back("/mamdani/Laundry"); - examples.push_back("/mamdani/SimpleDimmer"); - // examples.push_back("/mamdani/SimpleDimmerInverse"); - examples.push_back("/mamdani/matlab/mam21"); - examples.push_back("/mamdani/matlab/mam22"); - examples.push_back("/mamdani/matlab/shower"); - examples.push_back("/mamdani/matlab/tank"); - examples.push_back("/mamdani/matlab/tank2"); - examples.push_back("/mamdani/matlab/tipper"); - examples.push_back("/mamdani/matlab/tipper1"); - examples.push_back("/mamdani/octave/investment_portfolio"); - examples.push_back("/mamdani/octave/mamdani_tip_calculator"); - examples.push_back("/takagi-sugeno/approximation"); - examples.push_back("/takagi-sugeno/SimpleDimmer"); - examples.push_back("/takagi-sugeno/matlab/fpeaks"); - examples.push_back("/takagi-sugeno/matlab/invkine1"); - examples.push_back("/takagi-sugeno/matlab/invkine2"); - examples.push_back("/takagi-sugeno/matlab/juggler"); - examples.push_back("/takagi-sugeno/matlab/membrn1"); - examples.push_back("/takagi-sugeno/matlab/membrn2"); - examples.push_back("/takagi-sugeno/matlab/slbb"); - examples.push_back("/takagi-sugeno/matlab/slcp"); - examples.push_back("/takagi-sugeno/matlab/slcp1"); - examples.push_back("/takagi-sugeno/matlab/slcpp1"); - examples.push_back("/takagi-sugeno/matlab/sltbu_fl"); - examples.push_back("/takagi-sugeno/matlab/sugeno1"); - examples.push_back("/takagi-sugeno/matlab/tanksg"); - examples.push_back("/takagi-sugeno/matlab/tippersg"); - examples.push_back("/takagi-sugeno/octave/cubic_approximator"); - examples.push_back("/takagi-sugeno/octave/heart_disease_risk"); - examples.push_back("/takagi-sugeno/octave/linear_tip_calculator"); - examples.push_back("/takagi-sugeno/octave/sugeno_tip_calculator"); - examples.push_back("/tsukamoto/tsukamoto"); - - std::string sourceBase = path + "/original"; - std::string targetBase = path + "/tmp/"; + examples.push_back("mamdani/AllTerms"); + examples.push_back("mamdani/SimpleDimmer"); + examples.push_back("mamdani/Laundry"); + examples.push_back("mamdani/ObstacleAvoidance"); + examples.push_back("mamdani/SimpleDimmerChained"); + examples.push_back("mamdani/SimpleDimmerInverse"); + examples.push_back("mamdani/matlab/mam21"); + examples.push_back("mamdani/matlab/mam22"); + examples.push_back("mamdani/matlab/shower"); + examples.push_back("mamdani/matlab/tank"); + examples.push_back("mamdani/matlab/tank2"); + examples.push_back("mamdani/matlab/tipper"); + examples.push_back("mamdani/matlab/tipper1"); + examples.push_back("mamdani/octave/investment_portfolio"); + examples.push_back("mamdani/octave/mamdani_tip_calculator"); + examples.push_back("takagi-sugeno/approximation"); + examples.push_back("takagi-sugeno/ObstacleAvoidance"); + examples.push_back("takagi-sugeno/SimpleDimmer"); + examples.push_back("takagi-sugeno/matlab/fpeaks"); + examples.push_back("takagi-sugeno/matlab/invkine1"); + examples.push_back("takagi-sugeno/matlab/invkine2"); + examples.push_back("takagi-sugeno/matlab/juggler"); + examples.push_back("takagi-sugeno/matlab/membrn1"); + examples.push_back("takagi-sugeno/matlab/membrn2"); + examples.push_back("takagi-sugeno/matlab/slbb"); + examples.push_back("takagi-sugeno/matlab/slcp"); + examples.push_back("takagi-sugeno/matlab/slcp1"); + examples.push_back("takagi-sugeno/matlab/slcpp1"); + examples.push_back("takagi-sugeno/matlab/sltbu_fl"); + examples.push_back("takagi-sugeno/matlab/sugeno1"); + examples.push_back("takagi-sugeno/matlab/tanksg"); + examples.push_back("takagi-sugeno/matlab/tippersg"); + examples.push_back("takagi-sugeno/octave/cubic_approximator"); + examples.push_back("takagi-sugeno/octave/heart_disease_risk"); + examples.push_back("takagi-sugeno/octave/linear_tip_calculator"); + examples.push_back("takagi-sugeno/octave/sugeno_tip_calculator"); + examples.push_back("tsukamoto/tsukamoto"); + examples.push_back("hybrid/tipper"); + examples.push_back("hybrid/ObstacleAvoidance"); FL_unique_ptr<Importer> importer; if (from == "fll") importer.reset(new FllImporter); else if (from == "fis") importer.reset(new FisImporter); else if (from == "fcl") importer.reset(new FclImporter); - else throw fl::Exception("[examples error] unrecognized format <" + from + "> to import", FL_AT); + else throw Exception("[examples error] unrecognized format <" + from + "> to import", FL_AT); FL_unique_ptr<Exporter> exporter; if (to == "fll") exporter.reset(new FllExporter); @@ -586,16 +730,19 @@ namespace fl { else if (to == "fis") exporter.reset(new FisExporter); else if (to == "cpp") exporter.reset(new CppExporter); else if (to == "java") exporter.reset(new JavaExporter); - else throw fl::Exception("[examples error] unrecognized format <" + to + "> to export", FL_AT); + else if (to == "R") exporter.reset(new RScriptExporter()); + else throw Exception("[examples error] unrecognized format <" + to + "> to export", FL_AT); std::vector<std::pair<Exporter*, Importer*> > tests; tests.push_back(std::pair<Exporter*, Importer*>(new FllExporter, new FllImporter)); tests.push_back(std::pair<Exporter*, Importer*>(new FisExporter, new FisImporter)); tests.push_back(std::pair<Exporter*, Importer*>(new FclExporter, new FclImporter)); for (std::size_t i = 0; i < examples.size(); ++i) { - FL_LOG("Processing " << (i + 1) << "/" << examples.size() << ": " << examples.at(i)); + std::string example = examples.at(i); + FL_LOG((i + 1) << "/" << examples.size()); + FL_LOG("Importing from: " << sourcePath << "/" << example << "." << from); std::ostringstream ss; - std::string input = sourceBase + examples.at(i) + "." + from; + std::string input = sourcePath + "/" + example + "." + from; std::ifstream source(input.c_str()); if (source.is_open()) { std::string line; @@ -604,29 +751,40 @@ namespace fl { ss << line << "\n"; } source.close(); - } else throw fl::Exception("[examples error] file not found: " + input, FL_AT); + } else throw Exception("[examples error] file not found: " + input, FL_AT); FL_unique_ptr<Engine> engine(importer->fromString(ss.str())); for (std::size_t t = 0; t < tests.size(); ++t) { - std::string out = tests.at(t).first->toString(engine.get()); - FL_unique_ptr<Engine> copy(tests.at(t).second->fromString(out)); - std::string out_copy = tests.at(t).first->toString(copy.get()); - - if (out != out_copy) { - std::ostringstream ss; - ss << "[imex error] different results <" - << importer->name() << "," << exporter->name() << "> " - "at " + examples.at(t) + "." + from + ":\n"; - ss << "<Engine A>\n" << out << "\n\n" << + if ("mamdani/Laundry" == example + or "mamdani/SimpleDimmerInverse" == example + or "mamdani/SimpleDimmerChained" == example + or "hybrid/tipper" == example + or "hybrid/ObstacleAvoidance" == example) { + if (tests.at(t).second->name() == FisImporter().name()) { + continue; + } + } + + std::string exported = tests.at(t).first->toString(engine.get()); + FL_unique_ptr<Engine> engineFromExport(tests.at(t).second->fromString(exported)); + std::string imported = tests.at(t).first->toString(engineFromExport.get()); + + if (exported != imported) { + std::ostringstream msg; + msg << "[imex error] different results <" + << tests.at(t).first->name() << "," << tests.at(t).second->name() << "> " + "at " << example << "." << from << ":\n"; + msg << "<Engine A>\n" << exported << "\n\n" << "================================\n\n" << - "<Engine B>\n" << out_copy; - throw fl::Exception(ss.str(), FL_AT); + "<Engine B>\n" << imported; + throw Exception(msg.str(), FL_AT); } } - std::string output = targetBase + examples.at(i) + "." + to; + std::string output = targetPath + "/" + example + "." + to; std::ofstream target(output.c_str()); + FL_LOG("Exporting to: " << output << "\n"); if (target.is_open()) { if (to == "cpp") { target << "#include <fl/Headers.h>\n\n" @@ -634,8 +792,9 @@ namespace fl { << exporter->toString(engine.get()) << "\n}\n"; } else if (to == "java") { - std::string className = examples.at(i).substr(examples.at(i).find_last_of('/') + 1); + std::string className = example.substr(example.find_last_of('/') + 1); target << "import com.fuzzylite.*;\n" + << "import com.fuzzylite.activation.*\n" << "import com.fuzzylite.defuzzifier.*;\n" << "import com.fuzzylite.factory.*;\n" << "import com.fuzzylite.hedge.*;\n" @@ -650,171 +809,215 @@ namespace fl { << "public static void main(String[] args){\n" << exporter->toString(engine.get()) << "\n}\n}\n"; + } else if (to == "R") { + RScriptExporter* rScript = dynamic_cast<RScriptExporter*> (exporter.get()); + InputVariable* a = engine->getInputVariable(0); + InputVariable* b = engine->getInputVariable(1 % engine->numberOfInputVariables()); + std::string pathToDF = example.substr(example.find_last_of('/') + 1) + ".fld"; + rScript->writeScriptImportingDataFrame(engine.get(), target, + a, b, pathToDF, engine->outputVariables()); } else { target << exporter->toString(engine.get()); } target.close(); } Engine copyConstructor(*engine.get()); - (void) copyConstructor; + FL_IUNUSED(copyConstructor); Engine assignmentOperator = *engine.get(); - (void) assignmentOperator; + FL_IUNUSED(assignmentOperator); } + FL_LOG("Please, make sure the output contains the following structure:\n" + "mkdir -p " << targetPath << "mamdani/matlab; " + "mkdir -p " << targetPath << "mamdani/octave; " + "mkdir -p " << targetPath << "takagi-sugeno/matlab; " + "mkdir -p " << targetPath << "takagi-sugeno/octave; " + "mkdir -p " << targetPath << "tsukamoto; " + "mkdir -p " << targetPath << "hybrid;"); for (std::size_t i = 0; i < tests.size(); ++i) { delete tests.at(i).first; delete tests.at(i).second; } } -#ifdef FL_CPP11 - - void Console::benchmarkExamples(const std::string& path, int runs) { - std::string sourceBase = path + "/original"; - typedef std::pair<std::string, int > Example; - std::vector<Example> examples; - examples.push_back(Example("/mamdani/AllTerms", 1e4)); - examples.push_back(Example("/mamdani/SimpleDimmer", 1e5)); - examples.push_back(Example("/mamdani/matlab/mam21", 128)); - examples.push_back(Example("/mamdani/matlab/mam22", 128)); - examples.push_back(Example("/mamdani/matlab/shower", 256)); - examples.push_back(Example("/mamdani/matlab/tank", 256)); - examples.push_back(Example("/mamdani/matlab/tank2", 512)); - examples.push_back(Example("/mamdani/matlab/tipper", 256)); - examples.push_back(Example("/mamdani/matlab/tipper1", 1e5)); - examples.push_back(Example("/mamdani/octave/investment_portfolio", 256)); - examples.push_back(Example("/mamdani/octave/mamdani_tip_calculator", 256)); - examples.push_back(Example("/takagi-sugeno/approximation", 1e6)); - examples.push_back(Example("/takagi-sugeno/SimpleDimmer", 2e6)); - examples.push_back(Example("/takagi-sugeno/matlab/fpeaks", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/invkine1", 256)); - examples.push_back(Example("/takagi-sugeno/matlab/invkine2", 256)); - examples.push_back(Example("/takagi-sugeno/matlab/juggler", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/membrn1", 1024)); - examples.push_back(Example("/takagi-sugeno/matlab/membrn2", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/slbb", 20)); - examples.push_back(Example("/takagi-sugeno/matlab/slcp", 20)); - examples.push_back(Example("/takagi-sugeno/matlab/slcp1", 15)); - examples.push_back(Example("/takagi-sugeno/matlab/slcpp1", 9)); - examples.push_back(Example("/takagi-sugeno/matlab/sltbu_fl", 128)); - examples.push_back(Example("/takagi-sugeno/matlab/sugeno1", 2e6)); - examples.push_back(Example("/takagi-sugeno/matlab/tanksg", 1024)); - examples.push_back(Example("/takagi-sugeno/matlab/tippersg", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/cubic_approximator", 2e6)); - examples.push_back(Example("/takagi-sugeno/octave/heart_disease_risk", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/linear_tip_calculator", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/sugeno_tip_calculator", 512)); - examples.push_back(Example("/tsukamoto/tsukamoto", 1e6)); - - for (std::size_t i = 0; i < examples.size(); ++i) { - FL_LOG(examples.at(i).first << "\t" << examples.at(i).second); + void Console::benchmark(const std::string& fllFile, const std::string& fldFile, + int runs, std::ofstream* writer) const { + FL_unique_ptr<Engine> engine(FllImporter().fromFile(fllFile)); + std::ifstream reader(fldFile.c_str()); + if (not reader.is_open()) { + throw Exception("File <" + fldFile + "> could not be opened"); + } + Benchmark benchmark(engine->getName(), engine.get()); + benchmark.prepare(reader); + if (writer) { + FL_LOG("\tEvaluating on " << benchmark.getExpected().size() << + " values read from " << fldFile << " ..."); } - - std::vector<std::string> runNumbers(runs); for (int i = 0; i < runs; ++i) { - runNumbers.at(i) = std::to_string(i + 1); + benchmark.runOnce(); } - std::string spacedPath(40, ' '); - std::copy(path.begin(), path.end(), spacedPath.begin()); - FL_LOG(spacedPath << "\t" << "mean\tstdev\n" << Op::join(runNumbers, "\t")); - - FllImporter importer; - FldExporter exporter; - exporter.setExportHeader(false); - exporter.setExportInputValues(false); - exporter.setExportOutputValues(false); - std::ostream dummy(0); + if (writer) { + FL_LOG("\tMean(t)=" << Op::mean(benchmark.getTimes()) << " nanoseconds"); + *writer << benchmark.format(benchmark.results(), + Benchmark::Horizontal, Benchmark::Body) << "\n"; + } else { + FL_LOGP(benchmark.format(benchmark.results(), + Benchmark::Horizontal, Benchmark::Body)); + } + } - for (std::size_t e = 0; e < examples.size(); ++e) { - FL_unique_ptr<Engine> engine(importer.fromFile(sourceBase + examples.at(e).first + ".fll")); + void Console::benchmarks(const std::string& fllFileList, + const std::string& fldFileList, int runs, std::ofstream* writer) const { + std::vector<std::string> fllFiles, fldFiles; - std::vector<scalar> seconds; - int results = std::pow(1.0 * examples.at(e).second, engine->numberOfInputVariables()); + { + std::ifstream fllReader(fllFileList.c_str()); + if (not fllReader.is_open()) { + throw Exception("[error] file <" + fllFileList + "> could not be opened"); + } + std::ifstream fldReader(fldFileList.c_str()); + if (not fldReader.is_open()) { + throw Exception("[error] file <" + fldFileList + "> could not be opened"); + } - for (int r = 0; r < runs; ++r) { - auto start = std::chrono::system_clock::now(); - exporter.write(engine.get(), dummy, results); - auto end = std::chrono::system_clock::now(); + std::string fllLine, fldLine; + while (std::getline(fllReader, fllLine) and std::getline(fldReader, fldLine)) { + fllLine = Op::trim(fllLine); + fldLine = Op::trim(fldLine); + if (fllLine.empty() or fllLine[0] == '#') + continue; + fllFiles.push_back(fllLine); + fldFiles.push_back(fldLine); + } + } - auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds> (end - start); + if (writer) { + *writer << Op::join(Benchmark().header(runs, true), "\t") << "\n"; + } else { + FL_LOGP(Op::join(Benchmark().header(runs, true), "\t")); + } - seconds.push_back(elapsed.count() / 1e3); + for (std::size_t i = 0; i < fllFiles.size(); ++i) { + if (writer) { + FL_LOG("Benchmark " << (i + 1) << "/" << fllFiles.size() << ": " + << fllFiles.at(i)); } - scalar mean = Op::mean(seconds); - scalar stdev = Op::standardDeviation(seconds, mean); - - std::string spacedExample(40, ' '); - std::string exampleName = examples.at(e).first; - std::copy(exampleName.begin(), exampleName.end(), spacedExample.begin()); - FL_LOG(spacedExample << "\t" << fl::Op::str(mean) << "\t" << fl::Op::str(stdev) << "\n" << - Op::join(seconds, "\t")); + benchmark(fllFiles.at(i), fldFiles.at(i), runs, writer); } } -#endif - int Console::main(int argc, char** argv) { - (void) argc; - (void) argv; + int Console::main(int argc, const char* argv[]) { + fuzzylite::setLogging(true); + + Console console; if (argc <= 2) { - std::cout << usage() << std::endl; + FL_LOGP(console.usage() << "\n"); return EXIT_SUCCESS; } - const std::string firstArgument = std::string(argv[1]); - + const std::string firstArgument(argv[1]); if (firstArgument == "export-examples") { std::string path = "."; if (argc > 2) { path = std::string(argv[2]); } - FL_LOG("Path=" << path); + std::string outputPath = "/tmp/"; + if (argc > 3) { + outputPath = std::string(argv[3]); + } + FL_LOG("Origin=" << path); + FL_LOG("Target=" << outputPath); + fuzzylite::setDecimals(3); try { - fuzzylite::setDecimals(3); FL_LOG("Processing fll->fll"); - exportAllExamples("fll", "fll", path); + console.exportAllExamples("fll", "fll", path, outputPath); FL_LOG("Processing fll->fcl"); - exportAllExamples("fll", "fcl", path); + console.exportAllExamples("fll", "fcl", path, outputPath); FL_LOG("Processing fll->fis"); - exportAllExamples("fll", "fis", path); + console.exportAllExamples("fll", "fis", path, outputPath); FL_LOG("Processing fll->cpp"); - exportAllExamples("fll", "cpp", path); + console.exportAllExamples("fll", "cpp", path, outputPath); FL_LOG("Processing fll->java"); - exportAllExamples("fll", "java", path); - fuzzylite::setDecimals(8); - fuzzylite::setMachEps(1e-6); + console.exportAllExamples("fll", "java", path, outputPath); + FL_LOG("Processing fll->R"); + console.exportAllExamples("fll", "R", path, outputPath); + fuzzylite::setDecimals(9); FL_LOG("Processing fll->fld"); - exportAllExamples("fll", "fld", path); + console.exportAllExamples("fll", "fld", path, outputPath); + FL_LOG("Origin=" << path); + FL_LOG("Target=" << outputPath); + } catch (std::exception& ex) { + FL_LOGP(ex.what() << "\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + + } else if (firstArgument == "benchmark") { + if (argc < 5) { + FL_LOG("[error] not enough parameters"); + return EXIT_FAILURE; + } + std::string fllFile(argv[2]); + std::string fldFile(argv[3]); + try { + int runs = (int) Op::toScalar(argv[4]); + if (argc > 5) { + std::string filename(argv[5]); + std::ofstream outputFile; + outputFile.open(filename.c_str()); + if (not outputFile.is_open()) { + FL_LOG("[error] cannot create file <" << filename << ">"); + return EXIT_FAILURE; + } + outputFile << Op::join(Benchmark().header(runs, true), "\t") << "\n"; + console.benchmark(fllFile, fldFile, runs, &outputFile); + } else { + FL_LOGP(Op::join(Benchmark().header(runs, true), "\t")); + console.benchmark(fllFile, fldFile, runs, fl::null); + } } catch (std::exception& ex) { - std::cout << ex.what() << "\nBACKTRACE:\n" << - fl::Exception::btCallStack() << std::endl; + FL_LOGP(ex.what() << "\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; + } else if (firstArgument == "benchmarks") { -#ifdef FL_CPP11 - std::string path = "."; - if (argc > 2) { - path = std::string(argv[2]); + if (argc < 5) { + FL_LOG("[error] not enough parameters"); + return EXIT_FAILURE; } - int runs = 10; - if (argc > 3) { - runs = (int) Op::toScalar(argv[3]); + std::string fllFiles(argv[2]); + std::string fldFiles(argv[3]); + try { + int runs = (int) Op::toScalar(argv[4]); + if (argc > 5) { + std::string filename(argv[5]); + std::ofstream outputFile; + outputFile.open(filename.c_str()); + if (not outputFile.is_open()) { + FL_LOG("[error] cannot create file <" << filename << ">"); + return EXIT_FAILURE; + } + console.benchmarks(fllFiles, fldFiles, runs, &outputFile); + } else { + console.benchmarks(fllFiles, fldFiles, runs, fl::null); + } + } catch (std::exception& ex) { + FL_LOGP(ex.what() << "\n"); + return EXIT_FAILURE; } - fuzzylite::setDecimals(3); - Console::benchmarkExamples(path, runs); return EXIT_SUCCESS; -#else - throw fl::Exception("[benchmarks error] implementation available only when built with C++11 (-DFL_CPP11)", FL_AT); -#endif } + //MAIN: try { - std::map<std::string, std::string> options = parse(argc, argv); - process(options); + std::map<std::string, std::string> options = console.parse(argc, argv); + console.process(options); + } catch (std::exception& ex) { - std::cout << ex.what() << "\n" << std::endl; - // std::cout << fl::Exception::btCallStack() << std::endl; + FL_LOGP(ex.what() << "\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } + } diff --git a/fuzzylite/src/Engine.cpp b/fuzzylite/src/Engine.cpp index f1e23c0..dbe4b17 100644 --- a/fuzzylite/src/Engine.cpp +++ b/fuzzylite/src/Engine.cpp @@ -1,46 +1,35 @@ /* - 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/Engine.h" +#include "fl/activation/General.h" #include "fl/defuzzifier/WeightedAverage.h" #include "fl/defuzzifier/WeightedSum.h" #include "fl/factory/DefuzzifierFactory.h" #include "fl/factory/FactoryManager.h" -#include "fl/factory/SNormFactory.h" -#include "fl/factory/TNormFactory.h" -#include "fl/hedge/Hedge.h" #include "fl/imex/FllExporter.h" #include "fl/norm/t/AlgebraicProduct.h" #include "fl/rule/Consequent.h" #include "fl/rule/Expression.h" #include "fl/rule/Rule.h" #include "fl/rule/RuleBlock.h" -#include "fl/term/Accumulated.h" +#include "fl/term/Aggregated.h" #include "fl/term/Constant.h" #include "fl/term/Linear.h" -#include "fl/term/Function.h" #include "fl/term/Ramp.h" #include "fl/term/Sigmoid.h" #include "fl/term/SShape.h" @@ -48,21 +37,19 @@ #include "fl/variable/InputVariable.h" #include "fl/variable/OutputVariable.h" - namespace fl { - Engine::Engine(const std::string& name) : _name(name) { - } + Engine::Engine(const std::string& name) : _name(name) { } - Engine::Engine(const Engine& other) : _name("") { + Engine::Engine(const Engine& other) : _name(""), _description("") { copyFrom(other); } Engine& Engine::operator=(const Engine& other) { if (this != &other) { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) - delete _ruleblocks.at(i); - _ruleblocks.clear(); + for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) + delete _ruleBlocks.at(i); + _ruleBlocks.clear(); for (std::size_t i = 0; i < _outputVariables.size(); ++i) delete _outputVariables.at(i); _outputVariables.clear(); @@ -77,6 +64,7 @@ namespace fl { void Engine::copyFrom(const Engine& other) { _name = other._name; + _description = other._description; for (std::size_t i = 0; i < other._inputVariables.size(); ++i) _inputVariables.push_back(new InputVariable(*other._inputVariables.at(i))); for (std::size_t i = 0; i < other._outputVariables.size(); ++i) @@ -84,13 +72,14 @@ namespace fl { updateReferences(); - for (std::size_t i = 0; i < other._ruleblocks.size(); ++i) { - RuleBlock* ruleBlock = new RuleBlock(*other._ruleblocks.at(i)); + for (std::size_t i = 0; i < other._ruleBlocks.size(); ++i) { + RuleBlock* ruleBlock = new RuleBlock(*other._ruleBlocks.at(i)); try { ruleBlock->loadRules(this); } catch (...) { + //ignore } - _ruleblocks.push_back(ruleBlock); + _ruleBlocks.push_back(ruleBlock); } } @@ -98,78 +87,89 @@ namespace fl { std::vector<Variable*> myVariables = variables(); for (std::size_t i = 0; i < myVariables.size(); ++i) { Variable* variable = myVariables.at(i); - for (int t = 0; t < variable->numberOfTerms(); ++t) { - Term::updateReference(variable->getTerm(t), this); + for (std::size_t t = 0; t < variable->numberOfTerms(); ++t) { + variable->getTerm(t)->updateReference(this); } } } Engine::~Engine() { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) delete _ruleblocks.at(i); - for (std::size_t i = 0; i < _outputVariables.size(); ++i) delete _outputVariables.at(i); - for (std::size_t i = 0; i < _inputVariables.size(); ++i) delete _inputVariables.at(i); + for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) + delete _ruleBlocks.at(i); + for (std::size_t i = 0; i < _outputVariables.size(); ++i) + delete _outputVariables.at(i); + for (std::size_t i = 0; i < _inputVariables.size(); ++i) + delete _inputVariables.at(i); } - void Engine::configure(const std::string& conjunctionT, const std::string& disjunctionS, - const std::string& activationT, const std::string& accumulationS, - const std::string& defuzzifierName, int resolution) { + void Engine::configure(const std::string& conjunction, const std::string& disjunction, + const std::string& implication, const std::string& aggregation, + const std::string& defuzzifier, const std::string& activation) { TNormFactory* tnormFactory = FactoryManager::instance()->tnorm(); SNormFactory* snormFactory = FactoryManager::instance()->snorm(); DefuzzifierFactory* defuzzFactory = FactoryManager::instance()->defuzzifier(); - TNorm* conjunction = tnormFactory->constructObject(conjunctionT); - SNorm* disjunction = snormFactory->constructObject(disjunctionS); - TNorm* activation = tnormFactory->constructObject(activationT); - SNorm* accumulation = snormFactory->constructObject(accumulationS); - Defuzzifier* defuzzifier = defuzzFactory->constructObject(defuzzifierName); - IntegralDefuzzifier* integralDefuzzifier = dynamic_cast<IntegralDefuzzifier*> (defuzzifier); - if (integralDefuzzifier) integralDefuzzifier->setResolution(resolution); + ActivationFactory* activationFactory = FactoryManager::instance()->activation(); + + TNorm* conjunctionObject = tnormFactory->constructObject(conjunction); + SNorm* disjunctionObject = snormFactory->constructObject(disjunction); + TNorm* implicationObject = tnormFactory->constructObject(implication); + SNorm* aggregationObject = snormFactory->constructObject(aggregation); + Defuzzifier* defuzzifierObject = defuzzFactory->constructObject(defuzzifier); + Activation* activationObject = activationFactory->constructObject(activation); - configure(conjunction, disjunction, activation, accumulation, defuzzifier); + configure(conjunctionObject, disjunctionObject, + implicationObject, aggregationObject, defuzzifierObject, + activationObject); } void Engine::configure(TNorm* conjunction, SNorm* disjunction, - TNorm* activation, SNorm* accumulation, Defuzzifier* defuzzifier) { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - _ruleblocks.at(i)->setConjunction(conjunction ? conjunction->clone() : fl::null); - _ruleblocks.at(i)->setDisjunction(disjunction ? disjunction->clone() : fl::null); - _ruleblocks.at(i)->setActivation(activation ? activation->clone() : fl::null); + TNorm* implication, SNorm* aggregation, Defuzzifier* defuzzifier, + Activation* activation) { + for (std::size_t i = 0; i < numberOfRuleBlocks(); ++i) { + RuleBlock* ruleBlock = ruleBlocks().at(i); + ruleBlock->setConjunction(conjunction ? conjunction->clone() : fl::null); + ruleBlock->setDisjunction(disjunction ? disjunction->clone() : fl::null); + ruleBlock->setImplication(implication ? implication->clone() : fl::null); + ruleBlock->setActivation(activation ? activation->clone() : new General); } - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - _outputVariables.at(i)->setDefuzzifier(defuzzifier ? defuzzifier->clone() : fl::null); - _outputVariables.at(i)->fuzzyOutput()->setAccumulation( - accumulation ? accumulation->clone() : fl::null); + for (std::size_t i = 0; i < numberOfOutputVariables(); ++i) { + OutputVariable* outputVariable = getOutputVariable(i); + outputVariable->setDefuzzifier(defuzzifier ? defuzzifier->clone() : fl::null); + outputVariable->setAggregation(aggregation ? aggregation->clone() : fl::null); } if (defuzzifier) delete defuzzifier; - if (accumulation) delete accumulation; - if (activation) delete activation; + if (aggregation) delete aggregation; + if (implication) delete implication; if (disjunction) delete disjunction; if (conjunction) delete conjunction; + if (activation) delete activation; } bool Engine::isReady(std::string* status) const { std::ostringstream ss; - if (_inputVariables.empty()) { - ss << "- Engine <" << _name << "> has no input variables\n"; + if (inputVariables().empty()) { + ss << "- Engine <" << getName() << "> has no input variables\n"; } - for (std::size_t i = 0; i < _inputVariables.size(); ++i) { - InputVariable* inputVariable = _inputVariables.at(i); + for (std::size_t i = 0; i < inputVariables().size(); ++i) { + InputVariable* inputVariable = inputVariables().at(i); if (not inputVariable) { - ss << "- Engine <" << _name << "> has a fl::null input variable at index <" << i << ">\n"; - } else if (inputVariable->terms().empty()) { - //ignore because sometimes inputs can be empty: takagi-sugeno/matlab/slcpp1.fis - // ss << "- Input variable <" << _inputVariables.at(i)->getName() << ">" - // << " has no terms\n"; + ss << "- Engine <" << getName() << "> has a fl::null input variable at index <" << i << ">\n"; } + /*else if (inputVariable->terms().empty()) { + ignore because sometimes inputs can be empty: takagi-sugeno/matlab/slcpp1.fis + ss << "- Input variable <" << _inputVariables.at(i)->getName() << ">" + << " has no terms\n"; + }*/ } - if (_outputVariables.empty()) { + if (outputVariables().empty()) { ss << "- Engine <" << _name << "> has no output variables\n"; } - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); if (not outputVariable) { - ss << "- Engine <" << _name << "> has a fl::null output variable at index <" << i << ">\n"; + ss << "- Engine <" << getName() << "> has a fl::null output variable at index <" << i << ">\n"; } else { if (outputVariable->terms().empty()) { ss << "- Output variable <" << outputVariable->getName() << ">" @@ -180,29 +180,29 @@ namespace fl { ss << "- Output variable <" << outputVariable->getName() << ">" << " has no defuzzifier\n"; } - SNorm* accumulation = outputVariable->fuzzyOutput()->getAccumulation(); - if (not accumulation and dynamic_cast<IntegralDefuzzifier*> (defuzzifier)) { + SNorm* aggregation = outputVariable->fuzzyOutput()->getAggregation(); + if (not aggregation and dynamic_cast<IntegralDefuzzifier*> (defuzzifier)) { ss << "- Output variable <" << outputVariable->getName() << ">" - << " has no accumulation operator\n"; + << " has no aggregation operator\n"; } } } - if (_ruleblocks.empty()) { - ss << "- Engine <" << _name << "> has no rule blocks\n"; + if (ruleBlocks().empty()) { + ss << "- Engine <" << getName() << "> has no rule blocks\n"; } - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - RuleBlock* ruleblock = _ruleblocks.at(i); + for (std::size_t i = 0; i < ruleBlocks().size(); ++i) { + RuleBlock* ruleblock = ruleBlocks().at(i); if (not ruleblock) { - ss << "- Engine <" << _name << "> has a fl::null rule block at index <" << i << ">\n"; + ss << "- Engine <" << getName() << "> has a fl::null rule block at index <" << i << ">\n"; } else { if (ruleblock->rules().empty()) { ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no rules\n"; } int requiresConjunction = 0; int requiresDisjunction = 0; - int requiresActivation = 0; - for (int r = 0; r < ruleblock->numberOfRules(); ++r) { + int requiresImplication = 0; + for (std::size_t r = 0; r < ruleblock->numberOfRules(); ++r) { Rule* rule = ruleblock->getRule(r); if (not rule) { ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() @@ -224,7 +224,7 @@ namespace fl { const OutputVariable* outputVariable = dynamic_cast<const OutputVariable*> (proposition->variable); if (outputVariable and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier())) { - ++requiresActivation; + ++requiresImplication; break; } } @@ -243,11 +243,11 @@ namespace fl { ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has " << requiresDisjunction << " rules that require disjunction operator\n"; } - const TNorm* activation = ruleblock->getActivation(); - if (requiresActivation > 0 and not activation) { - ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no activation operator\n"; + const TNorm* implication = ruleblock->getImplication(); + if (requiresImplication > 0 and not implication) { + ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no implication operator\n"; ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has " - << requiresActivation << " rules that require activation operator\n"; + << requiresImplication << " rules that require implication operator\n"; } } } @@ -256,14 +256,25 @@ namespace fl { } void Engine::restart() { - for (std::size_t i = 0; i < _inputVariables.size(); ++i) { - _inputVariables.at(i)->setInputValue(fl::nan); + for (std::size_t i = 0; i < inputVariables().size(); ++i) { + inputVariables().at(i)->setValue(fl::nan); } - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - _outputVariables.at(i)->clear(); + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + outputVariables().at(i)->clear(); } } + Complexity Engine::complexity() const { + Complexity result; + for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) { + const RuleBlock* ruleBlock = _ruleBlocks.at(i); + if (ruleBlock->isEnabled()) { + result += ruleBlock->complexity(); + } + } + return result; + } + void Engine::process() { for (std::size_t i = 0; i < _outputVariables.size(); ++i) { _outputVariables.at(i)->fuzzyOutput()->clear(); @@ -274,7 +285,7 @@ namespace fl { FL_DBG("CURRENT INPUTS:"); for (std::size_t i = 0; i < _inputVariables.size(); ++i) { InputVariable* inputVariable = _inputVariables.at(i); - scalar inputValue = inputVariable->getInputValue(); + scalar inputValue = inputVariable->getValue(); if (inputVariable->isEnabled()) { FL_DBG(inputVariable->getName() << ".input = " << Op::str(inputValue)); FL_DBG(inputVariable->getName() << ".fuzzy = " << inputVariable->fuzzify(inputValue)); @@ -285,9 +296,11 @@ namespace fl { FL_DEBUG_END; - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - RuleBlock* ruleBlock = _ruleblocks.at(i); + for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) { + RuleBlock* ruleBlock = _ruleBlocks.at(i); if (ruleBlock->isEnabled()) { + FL_DBG("==============="); + FL_DBG("RULE BLOCK: " << ruleBlock->getName()); ruleBlock->activate(); } } @@ -306,12 +319,12 @@ namespace fl { << outputVariable->getDefaultValue()); FL_DBG(outputVariable->getName() << ".lockValueInRange = " - << outputVariable->isLockedOutputValueInRange()); + << outputVariable->isLockValueInRange()); FL_DBG(outputVariable->getName() << ".lockPreviousValue= " - << outputVariable->isLockedPreviousOutputValue()); + << outputVariable->isLockPreviousValue()); - scalar output = outputVariable->getOutputValue(); + scalar output = outputVariable->getValue(); FL_DBG(outputVariable->getName() << ".output = " << output); FL_DBG(outputVariable->getName() << ".fuzzy = " << outputVariable->fuzzify(output)); @@ -332,12 +345,20 @@ namespace fl { return this->_name; } + void Engine::setDescription(const std::string& description) { + this->_description = description; + } + + std::string Engine::getDescription() const { + return this->_description; + } + std::string Engine::toString() const { return FllExporter().toString(this); } Engine::Type Engine::type(std::string* name, std::string* reason) const { - if (_outputVariables.empty()) { + if (outputVariables().empty()) { if (name) *name = "Unknown"; if (reason) *reason = "- Engine has no output variables"; return Engine::Unknown; @@ -345,24 +366,24 @@ namespace fl { //Mamdani bool mamdani = true; - for (std::size_t i = 0; mamdani and i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; mamdani and i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); //Defuzzifier must be integral mamdani = mamdani and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier()); } //Larsen - bool larsen = mamdani and not _ruleblocks.empty(); - //Larsen is Mamdani with AlgebraicProduct as Activation + bool larsen = mamdani and not ruleBlocks().empty(); + //Larsen is Mamdani with AlgebraicProduct as Implication if (mamdani) { - for (std::size_t i = 0; larsen and i < _ruleblocks.size(); ++i) { - RuleBlock* ruleBlock = _ruleblocks.at(i); - larsen = larsen and dynamic_cast<const AlgebraicProduct*> (ruleBlock->getActivation()); + for (std::size_t i = 0; larsen and i < ruleBlocks().size(); ++i) { + RuleBlock* ruleBlock = ruleBlocks().at(i); + larsen = larsen and dynamic_cast<const AlgebraicProduct*> (ruleBlock->getImplication()); } } if (larsen) { if (name) *name = "Larsen"; if (reason) *reason = "- Output variables have integral defuzzifiers\n" - "- Rule blocks activate using the algebraic product T-Norm"; + "- Implication in rule blocks is the algebraic product T-Norm"; return Engine::Larsen; } if (mamdani) { @@ -374,8 +395,8 @@ namespace fl { //TakagiSugeno bool takagiSugeno = true; - for (std::size_t i = 0; takagiSugeno and i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; takagiSugeno and i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); //Defuzzifier is Weighted WeightedDefuzzifier* weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier()); @@ -386,7 +407,7 @@ namespace fl { if (takagiSugeno) { //Takagi-Sugeno has only Constant, Linear or Function terms - for (int t = 0; takagiSugeno and t < outputVariable->numberOfTerms(); ++t) { + for (std::size_t t = 0; takagiSugeno and t < outputVariable->numberOfTerms(); ++t) { Term* term = outputVariable->getTerm(t); takagiSugeno = takagiSugeno and weightedDefuzzifier->inferType(term) == WeightedDefuzzifier::TakagiSugeno; @@ -402,8 +423,8 @@ namespace fl { //Tsukamoto bool tsukamoto = true; - for (std::size_t i = 0; tsukamoto and i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; tsukamoto and i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); //Defuzzifier is Weighted WeightedDefuzzifier* weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier()); @@ -413,9 +434,9 @@ namespace fl { weightedDefuzzifier->getType() == WeightedDefuzzifier::Tsukamoto); if (tsukamoto) { //Tsukamoto has only monotonic terms: Concave, Ramp, Sigmoid, SShape, or ZShape - for (int t = 0; tsukamoto and t < outputVariable->numberOfTerms(); ++t) { + for (std::size_t t = 0; tsukamoto and t < outputVariable->numberOfTerms(); ++t) { Term* term = outputVariable->getTerm(t); - tsukamoto = tsukamoto and weightedDefuzzifier->isMonotonic(term); + tsukamoto = tsukamoto and term->isMonotonic(); } } } @@ -428,8 +449,8 @@ namespace fl { //Inverse Tsukamoto bool inverseTsukamoto = true; - for (std::size_t i = 0; inverseTsukamoto and i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; inverseTsukamoto and i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); //Defuzzifier cannot be integral WeightedDefuzzifier* weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier()); @@ -444,19 +465,19 @@ namespace fl { } bool hybrid = true; - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - OutputVariable* outputVariable = _outputVariables.at(i); + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + OutputVariable* outputVariable = outputVariables().at(i); //Output variables have non-fl::null defuzzifiers hybrid = hybrid and outputVariable->getDefuzzifier(); } if (hybrid) { if (name) *name = "Hybrid"; - if (reason) *reason = "- Output variables have different defuzzifiers"; + if (reason) *reason = "- Output variables have different types of defuzzifiers"; return Engine::Hybrid; } if (name) *name = "Unknown"; - if (reason) *reason = "- There are output variables without a defuzzifier"; + if (reason) *reason = "- One or more output variables do not have a defuzzifier"; return Engine::Unknown; } @@ -466,9 +487,9 @@ namespace fl { std::vector<Variable*> Engine::variables() const { std::vector<Variable*> result; - result.reserve(_inputVariables.size() + _outputVariables.size()); - result.insert(result.end(), _inputVariables.begin(), _inputVariables.end()); - result.insert(result.end(), _outputVariables.begin(), _outputVariables.end()); + result.reserve(inputVariables().size() + outputVariables().size()); + result.insert(result.end(), inputVariables().begin(), inputVariables().end()); + result.insert(result.end(), outputVariables().begin(), outputVariables().end()); return result; } @@ -477,63 +498,62 @@ namespace fl { */ void Engine::setInputValue(const std::string& name, scalar value) { InputVariable* inputVariable = getInputVariable(name); - inputVariable->setInputValue(value); + inputVariable->setValue(value); } void Engine::addInputVariable(InputVariable* inputVariable) { - this->_inputVariables.push_back(inputVariable); + inputVariables().push_back(inputVariable); } - InputVariable* Engine::setInputVariable(InputVariable* inputVariable, int index) { - InputVariable* result = this->_inputVariables.at(index); - this->_inputVariables.at(index) = inputVariable; + InputVariable* Engine::setInputVariable(InputVariable* inputVariable, std::size_t index) { + InputVariable* result = inputVariables().at(index); + inputVariables().at(index) = inputVariable; return result; } - void Engine::insertInputVariable(InputVariable* inputVariable, int index) { - this->_inputVariables.insert(this->_inputVariables.begin() + index, - inputVariable); + void Engine::insertInputVariable(InputVariable* inputVariable, std::size_t index) { + inputVariables().insert(inputVariables().begin() + index, inputVariable); } - InputVariable* Engine::getInputVariable(int index) const { - return this->_inputVariables.at(index); + InputVariable* Engine::getInputVariable(std::size_t index) const { + return inputVariables().at(index); } InputVariable* Engine::getInputVariable(const std::string& name) const { - for (std::size_t i = 0; i < _inputVariables.size(); ++i) { - if (_inputVariables.at(i)->getName() == name) - return _inputVariables.at(i); + for (std::size_t i = 0; i < inputVariables().size(); ++i) { + if (inputVariables().at(i)->getName() == name) + return inputVariables().at(i); } - throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT); + throw Exception("[engine error] input variable <" + name + "> not found", FL_AT); } bool Engine::hasInputVariable(const std::string& name) const { - for (std::size_t i = 0; i < _inputVariables.size(); ++i) { - if (_inputVariables.at(i)->getName() == name) + for (std::size_t i = 0; i < inputVariables().size(); ++i) { + if (inputVariables().at(i)->getName() == name) return true; } return false; } - InputVariable* Engine::removeInputVariable(int index) { - InputVariable* result = this->_inputVariables.at(index); - this->_inputVariables.erase(this->_inputVariables.begin() + index); + InputVariable* Engine::removeInputVariable(std::size_t index) { + InputVariable* result = inputVariables().at(index); + inputVariables().erase(inputVariables().begin() + index); return result; } InputVariable* Engine::removeInputVariable(const std::string& name) { - for (std::size_t i = 0; i < _inputVariables.size(); ++i) { - if (_inputVariables.at(i)->getName() == name) { - InputVariable* result = this->_inputVariables.at(i); - this->_inputVariables.erase(this->_inputVariables.begin() + i); + for (std::size_t i = 0; i < inputVariables().size(); ++i) { + if (inputVariables().at(i)->getName() == name) { + InputVariable* result = inputVariables().at(i); + inputVariables().erase(inputVariables().begin() + i); return result; } } - throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT); + throw Exception("[engine error] input variable <" + name + "> not found", FL_AT); } - int Engine::numberOfInputVariables() const { - return this->_inputVariables.size(); + std::size_t Engine::numberOfInputVariables() const { + return inputVariables().size(); } const std::vector<InputVariable*>& Engine::inputVariables() const { @@ -553,63 +573,62 @@ namespace fl { */ scalar Engine::getOutputValue(const std::string& name) { OutputVariable* outputVariable = getOutputVariable(name); - return outputVariable->getOutputValue(); + return outputVariable->getValue(); } void Engine::addOutputVariable(OutputVariable* outputVariable) { - this->_outputVariables.push_back(outputVariable); + outputVariables().push_back(outputVariable); } - OutputVariable* Engine::setOutputVariable(OutputVariable* outputVariable, int index) { - OutputVariable* result = this->_outputVariables.at(index); - this->_outputVariables.at(index) = outputVariable; + OutputVariable* Engine::setOutputVariable(OutputVariable* outputVariable, std::size_t index) { + OutputVariable* result = outputVariables().at(index); + outputVariables().at(index) = outputVariable; return result; } - void Engine::insertOutputVariable(OutputVariable* outputVariable, int index) { - this->_outputVariables.insert(this->_outputVariables.begin() + index, - outputVariable); + void Engine::insertOutputVariable(OutputVariable* outputVariable, std::size_t index) { + outputVariables().insert(outputVariables().begin() + index, outputVariable); } - OutputVariable* Engine::getOutputVariable(int index) const { - return this->_outputVariables.at(index); + OutputVariable* Engine::getOutputVariable(std::size_t index) const { + return outputVariables().at(index); } OutputVariable* Engine::getOutputVariable(const std::string& name) const { - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - if (_outputVariables.at(i)->getName() == name) - return _outputVariables.at(i); + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + if (outputVariables().at(i)->getName() == name) + return outputVariables().at(i); } - throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT); + throw Exception("[engine error] output variable <" + name + "> not found", FL_AT); } bool Engine::hasOutputVariable(const std::string& name) const { - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - if (_outputVariables.at(i)->getName() == name) + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + if (outputVariables().at(i)->getName() == name) return true; } return false; } - OutputVariable* Engine::removeOutputVariable(int index) { - OutputVariable* result = this->_outputVariables.at(index); - this->_outputVariables.erase(this->_outputVariables.begin() + index); + OutputVariable* Engine::removeOutputVariable(std::size_t index) { + OutputVariable* result = outputVariables().at(index); + outputVariables().erase(outputVariables().begin() + index); return result; } OutputVariable* Engine::removeOutputVariable(const std::string& name) { - for (std::size_t i = 0; i < _outputVariables.size(); ++i) { - if (_outputVariables.at(i)->getName() == name) { - OutputVariable* result = this->_outputVariables.at(i); - this->_outputVariables.erase(this->_outputVariables.begin() + i); + for (std::size_t i = 0; i < outputVariables().size(); ++i) { + if (outputVariables().at(i)->getName() == name) { + OutputVariable* result = outputVariables().at(i); + outputVariables().erase(outputVariables().begin() + i); return result; } } - throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT); + throw Exception("[engine error] output variable <" + name + "> not found", FL_AT); } - int Engine::numberOfOutputVariables() const { - return this->_outputVariables.size(); + std::size_t Engine::numberOfOutputVariables() const { + return outputVariables().size(); } const std::vector<OutputVariable*>& Engine::outputVariables() const { @@ -628,71 +647,70 @@ namespace fl { * Operations for iterable datatype _ruleblocks */ void Engine::addRuleBlock(RuleBlock* ruleblock) { - this->_ruleblocks.push_back(ruleblock); + ruleBlocks().push_back(ruleblock); } - RuleBlock* Engine::setRuleBlock(RuleBlock* ruleBlock, int index) { - RuleBlock* result = this->_ruleblocks.at(index); - this->_ruleblocks.at(index) = ruleBlock; + RuleBlock* Engine::setRuleBlock(RuleBlock* ruleBlock, std::size_t index) { + RuleBlock* result = ruleBlocks().at(index); + ruleBlocks().at(index) = ruleBlock; return result; } - void Engine::insertRuleBlock(RuleBlock* ruleblock, int index) { - this->_ruleblocks.insert(this->_ruleblocks.begin() + index, ruleblock); + void Engine::insertRuleBlock(RuleBlock* ruleblock, std::size_t index) { + ruleBlocks().insert(ruleBlocks().begin() + index, ruleblock); } - RuleBlock* Engine::getRuleBlock(int index) const { - return this->_ruleblocks.at(index); + RuleBlock* Engine::getRuleBlock(std::size_t index) const { + return ruleBlocks().at(index); } RuleBlock* Engine::getRuleBlock(const std::string& name) const { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - if (_ruleblocks.at(i)->getName() == name) - return _ruleblocks.at(i); + for (std::size_t i = 0; i < ruleBlocks().size(); ++i) { + if (ruleBlocks().at(i)->getName() == name) + return ruleBlocks().at(i); } - throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT); + throw Exception("[engine error] rule block <" + name + "> not found", FL_AT); } bool Engine::hasRuleBlock(const std::string& name) const { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - if (_ruleblocks.at(i)->getName() == name) + for (std::size_t i = 0; i < ruleBlocks().size(); ++i) { + if (ruleBlocks().at(i)->getName() == name) return true; } return false; } - RuleBlock* Engine::removeRuleBlock(int index) { - RuleBlock* result = this->_ruleblocks.at(index); - this->_ruleblocks.erase(this->_ruleblocks.begin() + index); + RuleBlock* Engine::removeRuleBlock(std::size_t index) { + RuleBlock* result = ruleBlocks().at(index); + ruleBlocks().erase(ruleBlocks().begin() + index); return result; } RuleBlock* Engine::removeRuleBlock(const std::string& name) { - for (std::size_t i = 0; i < _ruleblocks.size(); ++i) { - if (_ruleblocks.at(i)->getName() == name) { - RuleBlock* result = this->_ruleblocks.at(i); - this->_ruleblocks.erase(this->_ruleblocks.begin() + i); + for (std::size_t i = 0; i < ruleBlocks().size(); ++i) { + if (ruleBlocks().at(i)->getName() == name) { + RuleBlock* result = ruleBlocks().at(i); + ruleBlocks().erase(ruleBlocks().begin() + i); return result; } } - throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT); + throw Exception("[engine error] rule block <" + name + "> not found", FL_AT); } - int Engine::numberOfRuleBlocks() const { - return this->_ruleblocks.size(); + std::size_t Engine::numberOfRuleBlocks() const { + return ruleBlocks().size(); } const std::vector<RuleBlock*>& Engine::ruleBlocks() const { - return this->_ruleblocks; + return this->_ruleBlocks; } void Engine::setRuleBlocks(const std::vector<RuleBlock*>& ruleBlocks) { - this->_ruleblocks = ruleBlocks; + this->_ruleBlocks = ruleBlocks; } std::vector<RuleBlock*>& Engine::ruleBlocks() { - return this->_ruleblocks; + return this->_ruleBlocks; } - } diff --git a/fuzzylite/src/Exception.cpp b/fuzzylite/src/Exception.cpp index d24adac..1c27e70 100644 --- a/fuzzylite/src/Exception.cpp +++ b/fuzzylite/src/Exception.cpp @@ -1,48 +1,47 @@ /* - 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/Exception.h" -#ifdef FL_BACKTRACE_OFF -//do nothing +#ifdef FL_BACKTRACE -#elif defined FL_UNIX +#ifdef FL_UNIX #include <execinfo.h> #elif defined FL_WINDOWS #include <windows.h> #include <winbase.h> + #ifndef __MINGW32__ +/*Disable warning 8.1\Include\um\dbghelp.h(1544): +warning C4091: 'typedef ': ignored on left of '' when no variable is declared*/ +#pragma warning (push) +#pragma warning (disable:4091) #include <dbghelp.h> +#pragma warning (pop) #endif + +#endif + #endif -#include <signal.h> -#include <stdlib.h> -#include <string.h> +#include <csignal> +#include <cstring> namespace fl { @@ -58,8 +57,7 @@ namespace fl { FL_DBG(this->what()); } - Exception::~Exception() FL_INOEXCEPT { - } + Exception::~Exception() FL_INOEXCEPT { } void Exception::setWhat(const std::string& what) { this->_what = what; @@ -90,23 +88,25 @@ namespace fl { } std::string Exception::btCallStack() { -#ifdef FL_BACKTRACE_OFF - return "[backtrace disabled] fuzzylite was built with option -DFL_BACKTRACE_OFF"; +#ifndef FL_BACKTRACE + return "[backtrace disabled] fuzzylite was built without -DFL_BACKTRACE"; #elif defined FL_UNIX std::ostringstream btStream; const int bufferSize = 30; void* buffer[bufferSize]; - int backtraceSize = backtrace(buffer, bufferSize); - char **btSymbols = backtrace_symbols(buffer, backtraceSize); + int backtraceSize = ::backtrace(buffer, bufferSize); + char **btSymbols = ::backtrace_symbols(buffer, backtraceSize); if (btSymbols == fl::null) { btStream << "[backtrace error] no symbols could be retrieved"; } else { - if (backtraceSize == 0) btStream << "[backtrace is empty]"; + if (backtraceSize == 0) { + btStream << "[backtrace is empty]"; + } for (int i = 0; i < backtraceSize; ++i) { btStream << btSymbols[i] << "\n"; } } - free(btSymbols); + ::free(btSymbols); return btStream.str(); @@ -123,51 +123,52 @@ namespace fl { } else { btSymbol->MaxNameLen = 255; btSymbol->SizeOfStruct = sizeof ( SYMBOL_INFO); - if (backtraceSize == 0) btStream << "[backtrace is empty]"; + if (backtraceSize == 0) { + btStream << "[backtrace is empty]"; + } for (int i = 0; i < backtraceSize; ++i) { SymFromAddr(GetCurrentProcess(), (DWORD64) (buffer[ i ]), 0, btSymbol); btStream << (backtraceSize - i - 1) << ": " << btSymbol->Name << " at 0x" << btSymbol->Address << "\n"; } } - free(btSymbol); + ::free(btSymbol); return btStream.str(); #else return "[backtrace missing] supported only in Unix and Windows platforms"; #endif } - //execinfo - void Exception::signalHandler(int signal) { + void Exception::signalHandler(int unixSignal) { std::ostringstream ex; - ex << "[unexpected signal " << signal << "] "; + ex << "[unexpected signal " << unixSignal << "] "; #ifdef FL_UNIX - ex << strsignal(signal); + ex << ::strsignal(unixSignal); #endif ex << "\nBACKTRACE:\n" << btCallStack(); - fl::Exception::catchException(fl::Exception(ex.str(), FL_AT)); - exit(EXIT_FAILURE); + Exception::catchException(Exception(ex.str(), FL_AT)); + ::exit(EXIT_FAILURE); } - void Exception::convertToException(int signal) { + void Exception::convertToException(int unixSignal) { std::string signalDescription; #ifdef FL_UNIX //Unblock the signal sigset_t empty; sigemptyset(&empty); - sigaddset(&empty, signal); + sigaddset(&empty, unixSignal); sigprocmask(SIG_UNBLOCK, &empty, fl::null); - signalDescription = strsignal(signal); + signalDescription = ::strsignal(unixSignal); #endif std::ostringstream ex; - ex << "[signal " << signal << "] " << signalDescription << "\n"; + ex << "[signal " << unixSignal << "] " << signalDescription << "\n"; ex << "BACKTRACE:\n" << btCallStack(); - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } void Exception::terminate() { - fl::Exception::catchException(fl::Exception("[unexpected exception] BACKTRACE:\n" + btCallStack(), FL_AT)); - exit(EXIT_FAILURE); + Exception::catchException(Exception("[unexpected exception] BACKTRACE:\n" + btCallStack(), FL_AT)); + ::exit(EXIT_FAILURE); } void Exception::catchException(const std::exception& exception) { diff --git a/fuzzylite/src/Operation.cpp b/fuzzylite/src/Operation.cpp deleted file mode 100644 index 1596ea4..0000000 --- a/fuzzylite/src/Operation.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/* - 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/Operation.h" - -#include "fl/defuzzifier/Defuzzifier.h" -#include "fl/norm/Norm.h" -#include "fl/norm/SNorm.h" -#include "fl/norm/TNorm.h" - -#include <algorithm> -#include <iomanip> -#include <cstdarg> -#include <cctype> - -namespace fl { - - template <typename T> - T Operation::min(T a, T b) { - if (isNaN(a)) return b; - if (isNaN(b)) return a; - return a < b ? a : b; - } - template FL_API scalar Operation::min(scalar a, scalar b); - template FL_API int Operation::min(int a, int b); - - template <typename T> - T Operation::max(T a, T b) { - if (isNaN(a)) return b; - if (isNaN(b)) return a; - return a > b ? a : b; - } - template FL_API scalar Operation::max(scalar a, scalar b); - template FL_API int Operation::max(int a, int b); - - template <typename T> - T Operation::bound(T x, T min, T max) { - if (isGt(x, max)) return max; - if (isLt(x, min)) return min; - return x; - } - template FL_API scalar Operation::bound(scalar x, scalar min, scalar max); - template FL_API int Operation::bound(int x, int min, int max); - - template <typename T> - bool Operation::in(T x, T min, T max, bool geq, bool leq) { - bool left = geq ? isGE(x, min) : isGt(x, min); - bool right = leq ? isLE(x, max) : isLt(x, max); - return (left and right); - } - template FL_API bool Operation::in(scalar x, scalar min, scalar max, bool geq, bool leq); - template FL_API bool Operation::in(int x, int min, int max, bool geq, bool leq); - - template <typename T> - bool Operation::isInf(T x) { - return std::abs(x) == fl::inf; - } - template FL_API bool Operation::isInf(int x); - template FL_API bool Operation::isInf(scalar x); - - template <typename T> - bool Operation::isNaN(T x) { - return not (x == x); - } - template FL_API bool Operation::isNaN(int x); - template FL_API bool Operation::isNaN(scalar x); - - template<typename T> - bool Operation::isFinite(T x) { - return not (isNaN(x) or isInf(x)); - } - template FL_API bool Operation::isFinite(int x); - template FL_API bool Operation::isFinite(scalar x); - - bool Operation::isLt(scalar a, scalar b, scalar macheps) { - return not isEq(a, b, macheps) and a < b; - } - - bool Operation::isLE(scalar a, scalar b, scalar macheps) { - return isEq(a, b, macheps) or a < b; - } - - bool Operation::isEq(scalar a, scalar b, scalar macheps) { - return a == b or std::fabs(a - b) < macheps or (isNaN(a) and isNaN(b)); - } - - bool Operation::isGt(scalar a, scalar b, scalar macheps) { - return not isEq(a, b, macheps) and a > b; - } - - bool Operation::isGE(scalar a, scalar b, scalar macheps) { - return isEq(a, b, macheps) or a > b; - } - - scalar Operation::scale(scalar x, scalar fromMin, scalar fromMax, scalar toMin, scalar toMax, bool bounded) { - scalar result = (toMax - toMin) / (fromMax - fromMin) * (x - fromMin) + toMin; - return bounded ? fl::Op::bound(result, toMin, toMax) : result; - } - - scalar Operation::add(scalar a, scalar b) { - return a + b; - } - - scalar Operation::subtract(scalar a, scalar b) { - return a - b; - } - - scalar Operation::multiply(scalar a, scalar b) { - return a * b; - } - - scalar Operation::divide(scalar a, scalar b) { - return a / b; - } - - scalar Operation::modulo(scalar a, scalar b) { - return fmod(a, b); - } - - scalar Operation::logicalAnd(scalar a, scalar b) { - return (isEq(a, 1.0) and isEq(b, 1.0)) ? 1.0 : 0.0; - } - - scalar Operation::logicalOr(scalar a, scalar b) { - return (isEq(a, 1.0) or isEq(b, 1.0)) ? 1.0 : 0.0; - } - - scalar Operation::logicalNot(scalar a) { - return isEq(a, 1.0) ? 0.0 : 1.0; - } - - scalar Operation::negate(scalar a) { - return -a; - } - - scalar Operation::round(scalar x) { - return (x > 0.0) ? std::floor(x + 0.5) : std::ceil(x - 0.5); - } - - scalar Operation::gt(scalar a, scalar b) { - return isGt(a, b); - } - - scalar Operation::ge(scalar a, scalar b) { - return isGE(a, b); - } - - scalar Operation::eq(scalar a, scalar b) { - return isEq(a, b); - } - - scalar Operation::neq(scalar a, scalar b) { - return not isEq(a, b); - } - - scalar Operation::le(scalar a, scalar b) { - return isLE(a, b); - } - - scalar Operation::lt(scalar a, scalar b) { - return isLt(a, b); - } - - bool Operation::increment(std::vector<int>& x, std::vector<int>& min, std::vector<int>& max) { - return increment(x, (int) x.size() - 1, min, max); - } - - bool Operation::increment(std::vector<int>& x, int position, std::vector<int>& min, std::vector<int>& max) { - if (x.empty() or position < 0) return true; - - bool overflow = false; - if (x.at(position) < max.at(position)) { - ++x.at(position); - } else { - overflow = (position == 0); - x.at(position) = min.at(position); - --position; - if (position >= 0) { - overflow = increment(x, position, min, max); - } - } - return overflow; - } - - double Operation::mean(const std::vector<scalar>& x) { - if (x.size() == 0) return fl::nan; - scalar sum = 0.0; - for (std::size_t i = 0; i < x.size(); ++i) sum += x.at(i); - return sum / x.size(); - } - - double Operation::standardDeviation(const std::vector<scalar>& x) { - if (x.size() <= 1) return 0.0; - return standardDeviation(x, mean(x)); - } - - double Operation::standardDeviation(const std::vector<scalar>& x, scalar mean) { - if (x.size() <= 1) return 0.0; - return std::sqrt(variance(x, mean)); - } - - double Operation::variance(const std::vector<scalar>& x) { - if (x.size() <= 1) return 0.0; - return variance(x, mean(x)); - } - - double Operation::variance(const std::vector<scalar>& x, scalar mean) { - if (x.size() <= 1) return 0.0; - scalar result = 0; - for (std::size_t i = 0; i < x.size(); ++i) { - result += (x.at(i) - mean) * (x.at(i) - mean); - } - result /= -1 + x.size(); - return result; - } - - - - //Text Operations: - - std::string Operation::validName(const std::string& name) { - if (trim(name).empty()) return "unnamed"; - std::ostringstream ss; - for (std::size_t i = 0; i < name.length(); ++i) { - char c = name[i]; - if (c == '_' or c == '.' or isalnum(c)) { - ss << c; - } - } - return ss.str(); - } - - int Operation::isValidForName(int character) { - return character == '_' or character == '.' or isalnum(character); - } - - std::string Operation::findReplace(const std::string& str, const std::string& find, - const std::string& replace, bool replaceAll) { - std::ostringstream result; - std::size_t fromIndex = 0, nextIndex; - do { - nextIndex = str.find(find, fromIndex); - result << str.substr(fromIndex, nextIndex - fromIndex); - if (nextIndex != std::string::npos) - result << replace; - fromIndex = nextIndex + find.size(); - } while (replaceAll and nextIndex != std::string::npos); - return result.str(); - } - - std::vector<std::string> Operation::split(const std::string& str, - const std::string& delimiter, bool ignoreEmpty) { - std::vector<std::string> result; - if (str.empty() or delimiter.empty()) { - result.push_back(str); - return result; - } - std::string::const_iterator position = str.begin(), next = str.begin(); - while (next != str.end()) { - next = std::search(position, str.end(), delimiter.begin(), delimiter.end()); - std::string token(position, next); - if (not (token.empty() and ignoreEmpty)) { - result.push_back(token); - } - if (next != str.end()) { - position = next + delimiter.size(); - } - } - return result; - } - - std::string Operation::trim(const std::string& text) { - if (text.empty()) return text; - if (not (std::isspace(text.at(0)) or std::isspace(text.at(text.size() - 1)))) - return text; - int start = 0, end = text.size() - 1; - while (start <= end and std::isspace(text.at(start))) { - ++start; - } - while (end >= start and std::isspace(text.at(end))) { - --end; - } - int length = end - start + 1; - if (length <= 0) return ""; - return text.substr(start, length); - } - - std::string Operation::format(const std::string& text, int matchesChar(int), - const std::string& replacement) { - std::ostringstream ss; - std::string::const_iterator it = text.begin(); - while (it != text.end()) { - if (matchesChar(*it)) { - ss << *it; - } else { - ss << replacement; - } - ++it; - } - return ss.str(); - } - - scalar Operation::toScalar(const std::string& x) { - std::istringstream iss(x); - scalar result; - iss >> result; - char strict; - if (not (iss.fail() or iss.get(strict))) return result; - - std::ostringstream nan, pInf, nInf; - nan << fl::nan; - pInf << fl::inf; - nInf << (-fl::inf); - - if (x == nan.str() or x == "nan") - return fl::nan; - if (x == pInf.str() or x == "inf") - return fl::inf; - if (x == nInf.str() or x == "-inf") - return -fl::inf; - - std::ostringstream ex; - ex << "[conversion error] from <" << x << "> to scalar"; - throw fl::Exception(ex.str(), FL_AT); - } - - scalar Operation::toScalar(const std::string& x, scalar alternative) FL_INOEXCEPT { - std::istringstream iss(x); - scalar result; - iss >> result; - char strict; - if (not (iss.fail() or iss.get(strict))) return result; - - std::ostringstream nan, pInf, nInf; - nan << fl::nan; - pInf << fl::inf; - nInf << (-fl::inf); - - if (x == nan.str() or x == "nan") - return fl::nan; - if (x == pInf.str() or x == "inf") - return fl::inf; - if (x == nInf.str() or x == "-inf") - return -fl::inf; - - return alternative; - } - - bool Operation::isNumeric(const std::string& x) { - try { - fl::Op::toScalar(x); - return true; - } catch (std::exception& ex) { - (void) ex; - return false; - } - } - - template <typename T> - std::string Operation::str(T x, int decimals) { - std::ostringstream ss; - ss << std::setprecision(decimals) << std::fixed; - if (fl::Op::isNaN(x)) { - ss << "nan"; - } else if (fl::Op::isInf(x)) { - ss << (fl::Op::isLt(x, 0.0) ? "-inf" : "inf"); - } else if (fl::Op::isEq(x, 0.0)) { - ss << 0.0; - } else ss << x; - return ss.str(); - } - template FL_API std::string Operation::str(int x, int precision); - template FL_API std::string Operation::str(scalar x, int precision); - - template <> FL_API std::string Operation::str(const std::string& x, int precision) { - (void) precision; - return x; - } - - template <typename T> - std::string Operation::join(const std::vector<T>& x, - const std::string& separator) { - std::ostringstream ss; - for (std::size_t i = 0; i < x.size(); ++i) { - ss << str(x.at(i)); - if (i + 1 < x.size()) ss << separator; - } - return ss.str(); - } - template FL_API std::string Operation::join(const std::vector<int>& x, - const std::string& separator); - template FL_API std::string Operation::join(const std::vector<scalar>& x, - const std::string& separator); - - template <> FL_API - std::string Operation::join(const std::vector<std::string>& x, - const std::string& separator) { - std::ostringstream ss; - for (std::size_t i = 0; i < x.size(); ++i) { - ss << x.at(i); - if (i + 1 < x.size()) ss << separator; - } - return ss.str(); - } - - template <typename T> - std::string Operation::join(int items, const std::string& separator, T first, ...) { - std::ostringstream ss; - ss << str(first); - if (items > 1) ss << separator; - va_list args; - va_start(args, first); - for (int i = 0; i < items - 1; ++i) { - ss << str(va_arg(args, T)); - if (i + 1 < items - 1) ss << separator; - } - va_end(args); - return ss.str(); - } - - template FL_API std::string Operation::join(int items, const std::string& separator, - int first, ...); - template FL_API std::string Operation::join(int items, const std::string& separator, - double first, ...); - - template <> FL_API std::string Operation::join(int items, const std::string& separator, - float first, ...) { - std::ostringstream ss; - ss << str(first); - if (items > 1) ss << separator; - va_list args; - va_start(args, first); - for (int i = 0; i < items - 1; ++i) { - ss << str(va_arg(args, double)); //automatic promotion - if (i + 1 < items - 1) ss << separator; - } - va_end(args); - return ss.str(); - } - - template <> FL_API - std::string Operation::join(int items, const std::string& separator, const char* first, ...) { - std::ostringstream ss; - ss << first; - if (items > 1) ss << separator; - va_list args; - va_start(args, first); - for (int i = 0; i < items - 1; ++i) { - ss << va_arg(args, const char*); - if (i + 1 < items - 1) ss << separator; - } - va_end(args); - return ss.str(); - } - -} diff --git a/fuzzylite/src/activation/First.cpp b/fuzzylite/src/activation/First.cpp new file mode 100644 index 0000000..fcf7484 --- /dev/null +++ b/fuzzylite/src/activation/First.cpp @@ -0,0 +1,122 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/First.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +namespace fl { + + First::First(int numberOfRules, scalar threshold) : Activation(), + _numberOfRules(numberOfRules), _threshold(threshold) { } + + First::~First() { } + + std::string First::className() const { + return "First"; + } + + std::string First::parameters() const { + return Op::str(getNumberOfRules()) + " " + Op::str(getThreshold()); + } + + void First::configure(const std::string& parameters) { + if (parameters.empty()) return; + std::vector<std::string> values = Op::split(parameters, " "); + std::size_t required = 2; + if (values.size() < required) { + std::ostringstream ex; + ex << "[configuration error] activation <" << className() << ">" + << " requires <" << required << "> parameters"; + throw Exception(ex.str(), FL_AT); + } + setNumberOfRules((int) Op::toScalar(values.at(0))); + setThreshold(Op::toScalar(values.at(1))); + } + + Complexity First::complexity(const RuleBlock* ruleBlock) const { + Complexity result; + + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + Complexity meanFiring; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + result.comparison(1 + 3); + const Rule* rule = ruleBlock->getRule(i); + result += rule->complexityOfActivation(conjunction, disjunction); + meanFiring += rule->complexityOfFiring(implication); + } + meanFiring.divide(scalar(ruleBlock->numberOfRules())); + + result += meanFiring.multiply(getNumberOfRules()); + result += Complexity().arithmetic(1).multiply(getNumberOfRules()); + return result; + } + + void First::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + int activated = 0; + for (std::vector<Rule*>::const_iterator it = ruleBlock->rules().begin(); + it != ruleBlock->rules().end(); ++it) { + Rule* rule = (*it); + rule->deactivate(); + + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + if (activated < _numberOfRules + and Op::isGt(activationDegree, 0.0) + and Op::isGE(activationDegree, _threshold)) { + rule->trigger(implication); + ++activated; + } + } + } + } + + void First::setNumberOfRules(int numberOfRules) { + this->_numberOfRules = numberOfRules; + } + + int First::getNumberOfRules() const { + return this->_numberOfRules; + } + + void First::setThreshold(scalar threshold) { + this->_threshold = threshold; + } + + scalar First::getThreshold() const { + return this->_threshold; + } + + First* First::clone() const { + return new First(*this); + } + + Activation* First::constructor() { + return new First; + } + +} + diff --git a/fuzzylite/src/activation/General.cpp b/fuzzylite/src/activation/General.cpp new file mode 100644 index 0000000..a42c588 --- /dev/null +++ b/fuzzylite/src/activation/General.cpp @@ -0,0 +1,77 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/General.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +namespace fl { + + General::General() : Activation() { } + + General::~General() { } + + std::string General::className() const { + return "General"; + } + + std::string General::parameters() const { + return ""; + } + + void General::configure(const std::string& parameters) { + FL_IUNUSED(parameters); + } + + Complexity General::complexity(const RuleBlock* ruleBlock) const { + Complexity result; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + result.comparison(1); + result += ruleBlock->getRule(i)->complexity( + ruleBlock->getConjunction(), ruleBlock->getDisjunction(), + ruleBlock->getImplication()); + } + return result; + } + + void General::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + const std::size_t numberOfRules = ruleBlock->numberOfRules(); + for (std::size_t i = 0; i < numberOfRules; ++i) { + Rule* rule = ruleBlock->getRule(i); + rule->deactivate(); + if (rule->isLoaded()) { + rule->activateWith(conjunction, disjunction); + rule->trigger(implication); + } + } + } + + General* General::clone() const { + return new General(*this); + } + + Activation* General::constructor() { + return new General; + } + +} diff --git a/fuzzylite/src/activation/Highest.cpp b/fuzzylite/src/activation/Highest.cpp new file mode 100644 index 0000000..612c16b --- /dev/null +++ b/fuzzylite/src/activation/Highest.cpp @@ -0,0 +1,120 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/Highest.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +#include <queue> + +namespace fl { + + Highest::Highest(int numberOfRules) : Activation(), _numberOfRules(numberOfRules) { } + + Highest::~Highest() { } + + std::string Highest::className() const { + return "Highest"; + } + + std::string Highest::parameters() const { + return Op::str(getNumberOfRules()); + } + + void Highest::configure(const std::string& parameters) { + setNumberOfRules((int) Op::toScalar(parameters)); + } + + int Highest::getNumberOfRules() const { + return this->_numberOfRules; + } + + void Highest::setNumberOfRules(int numberOfRules) { + this->_numberOfRules = numberOfRules; + } + + Complexity Highest::complexity(const RuleBlock* ruleBlock) const { + //Cost of priority_queue: + //http://stackoverflow.com/questions/2974470/efficiency-of-the-stl-priority-queue + Complexity result; + + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + Complexity meanFiring; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + const Rule* rule = ruleBlock->getRule(i); + result += rule->complexityOfActivation(conjunction, disjunction); + meanFiring += rule->complexityOfFiring(implication); + } + meanFiring.divide(scalar(ruleBlock->numberOfRules())); + + //Complexity of push is O(log n) + result += Complexity().function(1).multiply(ruleBlock->numberOfRules() + * std::log(scalar(ruleBlock->numberOfRules()))); + + result += Complexity().comparison(2).arithmetic(1).multiply(getNumberOfRules()); + result += meanFiring.multiply(getNumberOfRules()); + //Complexity of pop is 2 * O(log n) + result += Complexity().function(1).multiply(getNumberOfRules() * + 2 * std::log(scalar(ruleBlock->numberOfRules()))); + return result; + } + + struct Descending { + + bool operator()(const Rule* a, const Rule* b) const { + return a->getActivationDegree() < b->getActivationDegree(); + } + }; + + void Highest::activate(RuleBlock* ruleBlock) { + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + std::priority_queue<Rule*, std::vector<Rule*>, Descending> rulesToActivate; + + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + Rule* rule = ruleBlock->getRule(i); + rule->deactivate(); + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + if (Op::isGt(activationDegree, 0.0)) + rulesToActivate.push(rule); + } + } + + int activated = 0; + while (rulesToActivate.size() > 0 and activated++ < _numberOfRules) { + Rule* rule = rulesToActivate.top(); + rule->trigger(implication); + rulesToActivate.pop(); + } + } + + Highest* Highest::clone() const { + return new Highest(*this); + } + + Activation* Highest::constructor() { + return new Highest; + } + +} diff --git a/fuzzylite/src/activation/Last.cpp b/fuzzylite/src/activation/Last.cpp new file mode 100644 index 0000000..90b5dc9 --- /dev/null +++ b/fuzzylite/src/activation/Last.cpp @@ -0,0 +1,121 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/Last.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +namespace fl { + + Last::Last(int numberOfRules, scalar threshold) : Activation(), + _numberOfRules(numberOfRules), _threshold(threshold) { } + + Last::~Last() { } + + std::string Last::className() const { + return "Last"; + } + + std::string Last::parameters() const { + return Op::str(getNumberOfRules()) + " " + Op::str(getThreshold()); + } + + void Last::configure(const std::string& parameters) { + if (parameters.empty()) return; + std::vector<std::string> values = Op::split(parameters, " "); + std::size_t required = 2; + if (values.size() < required) { + std::ostringstream ex; + ex << "[configuration error] activation <" << className() << ">" + << " requires <" << required << "> parameters"; + throw Exception(ex.str(), FL_AT); + } + setNumberOfRules((int) Op::toScalar(values.at(0))); + setThreshold(Op::toScalar(values.at(1))); + } + + void Last::setNumberOfRules(int numberOfRules) { + this->_numberOfRules = numberOfRules; + } + + int Last::getNumberOfRules() const { + return this->_numberOfRules; + } + + void Last::setThreshold(scalar threshold) { + this->_threshold = threshold; + } + + scalar Last::getThreshold() const { + return this->_threshold; + } + + Complexity Last::complexity(const RuleBlock* ruleBlock) const { + Complexity result; + + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + Complexity meanFiring; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + result.comparison(1 + 3); + const Rule* rule = ruleBlock->getRule(i); + result += rule->complexityOfActivation(conjunction, disjunction); + meanFiring += rule->complexityOfFiring(implication); + } + meanFiring.divide(scalar(ruleBlock->numberOfRules())); + + result += meanFiring.multiply(getNumberOfRules()); + result += Complexity().arithmetic(1).multiply(getNumberOfRules()); + return result; + } + + void Last::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + int activated = 0; + for (std::vector<Rule*>::const_reverse_iterator it = ruleBlock->rules().rbegin(); + it != ruleBlock->rules().rend(); ++it) { + Rule* rule = (*it); + rule->deactivate(); + + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + if (activated < _numberOfRules + and Op::isGt(activationDegree, 0.0) + and Op::isGE(activationDegree, _threshold)) { + rule->trigger(implication); + ++activated; + } + } + } + } + + Last* Last::clone() const { + return new Last(*this); + } + + Activation* Last::constructor() { + return new Last; + } + +} diff --git a/fuzzylite/src/activation/Lowest.cpp b/fuzzylite/src/activation/Lowest.cpp new file mode 100644 index 0000000..52e7e85 --- /dev/null +++ b/fuzzylite/src/activation/Lowest.cpp @@ -0,0 +1,122 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/Lowest.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +#include <queue> + +namespace fl { + + Lowest::Lowest(int numberOfRules) : Activation(), _numberOfRules(numberOfRules) { } + + Lowest::~Lowest() { } + + std::string Lowest::className() const { + return "Lowest"; + } + + std::string Lowest::parameters() const { + return Op::str(getNumberOfRules()); + } + + void Lowest::configure(const std::string& parameters) { + setNumberOfRules((int) Op::toScalar(parameters)); + } + + int Lowest::getNumberOfRules() const { + return this->_numberOfRules; + } + + void Lowest::setNumberOfRules(int activatedRules) { + this->_numberOfRules = activatedRules; + } + + Complexity Lowest::complexity(const RuleBlock* ruleBlock) const { + //Cost of priority_queue: + //http://stackoverflow.com/questions/2974470/efficiency-of-the-stl-priority-queue + Complexity result; + + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + Complexity meanFiring; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + const Rule* rule = ruleBlock->getRule(i); + result.comparison(2); + result += rule->complexityOfActivation(conjunction, disjunction); + meanFiring += rule->complexityOfFiring(implication); + } + meanFiring.divide(scalar(ruleBlock->numberOfRules())); + + //Complexity of push is O(log n) + result += Complexity().function(1).multiply(ruleBlock->numberOfRules() + * std::log(scalar(ruleBlock->numberOfRules()))); + + result += Complexity().comparison(2).arithmetic(1).multiply(getNumberOfRules()); + result += meanFiring.multiply(getNumberOfRules()); + //Complexity of pop is 2 * O(log n) + result += Complexity().function(1).multiply(getNumberOfRules() * + 2 * std::log(scalar(ruleBlock->numberOfRules()))); + return result; + } + + struct Ascending { + + bool operator()(const Rule* a, const Rule* b) { + return a->getActivationDegree() > b->getActivationDegree(); + } + }; + + void Lowest::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + std::priority_queue<Rule*, std::vector<Rule*>, Ascending> rulesToActivate; + + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + Rule* rule = ruleBlock->getRule(i); + rule->deactivate(); + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + if (Op::isGt(activationDegree, 0.0)) + rulesToActivate.push(rule); + } + } + + int activated = 0; + while (rulesToActivate.size() > 0 and activated++ < _numberOfRules) { + Rule* rule = rulesToActivate.top(); + rule->trigger(implication); + rulesToActivate.pop(); + } + } + + Lowest* Lowest::clone() const { + return new Lowest(*this); + } + + Activation* Lowest::constructor() { + return new Lowest; + } + +} diff --git a/fuzzylite/src/activation/Proportional.cpp b/fuzzylite/src/activation/Proportional.cpp new file mode 100644 index 0000000..07fa2aa --- /dev/null +++ b/fuzzylite/src/activation/Proportional.cpp @@ -0,0 +1,91 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/Proportional.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +namespace fl { + + Proportional::Proportional() : Activation() { } + + Proportional::~Proportional() { } + + std::string Proportional::className() const { + return "Proportional"; + } + + std::string Proportional::parameters() const { + return ""; + } + + void Proportional::configure(const std::string& parameters) { + FL_IUNUSED(parameters); + } + + Complexity Proportional::complexity(const RuleBlock* ruleBlock) const { + Complexity result; + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + result.comparison(1).arithmetic(1); + result += ruleBlock->getRule(i)->complexityOfActivation( + ruleBlock->getConjunction(), ruleBlock->getDisjunction()); + } + + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + result.arithmetic(1); + result += ruleBlock->getRule(i)->complexityOfFiring(ruleBlock->getImplication()); + } + return result; + } + + void Proportional::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + scalar sumActivationDegrees = 0.0; + std::vector<Rule*> rulesToActivate; + const std::size_t numberOfRules = ruleBlock->numberOfRules(); + for (std::size_t i = 0; i < numberOfRules; ++i) { + Rule* rule = ruleBlock->getRule(i); + rule->deactivate(); + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + rulesToActivate.push_back(rule); + sumActivationDegrees += activationDegree; + } + } + + for (std::size_t i = 0; i < rulesToActivate.size(); ++i) { + Rule* rule = rulesToActivate.at(i); + scalar activationDegree = rule->getActivationDegree() / sumActivationDegrees; + rule->setActivationDegree(activationDegree); + rule->trigger(implication); + } + } + + Proportional* Proportional::clone() const { + return new Proportional(*this); + } + + Activation* Proportional::constructor() { + return new Proportional; + } + +} diff --git a/fuzzylite/src/activation/Threshold.cpp b/fuzzylite/src/activation/Threshold.cpp new file mode 100644 index 0000000..cd1f034 --- /dev/null +++ b/fuzzylite/src/activation/Threshold.cpp @@ -0,0 +1,170 @@ +/* + 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 FuzzyLite License included with the software. + + 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/activation/Threshold.h" + +#include "fl/rule/RuleBlock.h" +#include "fl/rule/Rule.h" +#include "fl/Operation.h" + +namespace fl { + + Threshold::Threshold(Comparison comparison, scalar threshold) : Activation(), + _comparison(comparison), _value(threshold) { } + + Threshold::Threshold(const std::string& comparison, scalar threshold) : Activation(), + _comparison(parseComparison(comparison)), _value(threshold) { } + + Threshold::~Threshold() { } + + std::string Threshold::className() const { + return "Threshold"; + } + + std::string Threshold::parameters() const { + std::ostringstream ss; + ss << comparisonOperator() << " " << Op::str(getValue()); + return ss.str(); + } + + void Threshold::configure(const std::string& parameters) { + if (parameters.empty()) return; + std::vector<std::string> values = Op::split(parameters, " "); + std::size_t required = 2; + if (values.size() < required) { + std::ostringstream ex; + ex << "[configuration error] activation <" << className() << ">" + << " requires <" << required << "> parameters"; + throw Exception(ex.str(), FL_AT); + } + setComparison(parseComparison(values.at(0))); + setValue(Op::toScalar(values.at(1))); + } + + void Threshold::setComparison(Comparison comparison) { + this->_comparison = comparison; + } + + Threshold::Comparison Threshold::getComparison() const { + return this->_comparison; + } + + std::string Threshold::comparisonOperator() const { + return comparisonOperator(getComparison()); + } + + std::string Threshold::comparisonOperator(Comparison comparison) const { + switch (comparison) { + case LessThan: return "<"; + case LessThanOrEqualTo: return "<="; + case EqualTo: return "=="; + case NotEqualTo: return "!="; + case GreaterThanOrEqualTo: return ">="; + case GreaterThan: return ">"; + default: return "?"; + } + } + + std::vector<std::string> Threshold::availableComparisonOperators() const { + std::vector<std::string> result; + result.push_back("<"); + result.push_back("<="); + result.push_back("=="); + result.push_back("!="); + result.push_back(">="); + result.push_back(">"); + return result; + } + + Threshold::Comparison Threshold::parseComparison(const std::string& name) const { + if (name == "<") return LessThan; + if (name == "<=") return LessThanOrEqualTo; + if (name == "==") return EqualTo; + if (name == "!=") return NotEqualTo; + if (name == ">=") return GreaterThanOrEqualTo; + if (name == ">") return GreaterThan; + throw Exception("[syntax error] invalid threshold type by name <" + name + ">", FL_AT); + } + + void Threshold::setValue(scalar value) { + this->_value = value; + } + + scalar Threshold::getValue() const { + return this->_value; + } + + void Threshold::setThreshold(Comparison comparison, scalar threshold) { + setComparison(comparison); + setValue(threshold); + } + + void Threshold::setThreshold(const std::string& comparison, scalar value) { + setComparison(parseComparison(comparison)); + setValue(value); + } + + bool Threshold::activatesWith(scalar activationDegree) const { + switch (getComparison()) { + case LessThan: return Op::isLt(activationDegree, getValue()); + case LessThanOrEqualTo: return Op::isLE(activationDegree, getValue()); + case EqualTo: return Op::isEq(activationDegree, getValue()); + case NotEqualTo: return not Op::isEq(activationDegree, getValue()); + case GreaterThanOrEqualTo: return Op::isGE(activationDegree, getValue()); + case GreaterThan: return Op::isGt(activationDegree, getValue()); + default: return false; + } + } + + Complexity Threshold::complexity(const RuleBlock* ruleBlock) const { + Complexity result; + for (std::size_t i = 0; i < ruleBlock->rules().size(); ++i) { + result.comparison(2); + result += ruleBlock->rules().at(i)->complexity( + ruleBlock->getConjunction(), ruleBlock->getDisjunction(), + ruleBlock->getImplication()); + } + return result; + } + + void Threshold::activate(RuleBlock* ruleBlock) { + FL_DBG("Activation: " << className() << " " << parameters()); + const TNorm* conjunction = ruleBlock->getConjunction(); + const SNorm* disjunction = ruleBlock->getDisjunction(); + const TNorm* implication = ruleBlock->getImplication(); + + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { + Rule* rule = ruleBlock->getRule(i); + rule->deactivate(); + if (rule->isLoaded()) { + scalar activationDegree = rule->activateWith(conjunction, disjunction); + if (activatesWith(activationDegree)) { + rule->trigger(implication); + } + } + } + } + + Threshold* Threshold::clone() const { + return new Threshold(*this); + } + + Activation* Threshold::constructor() { + return new Threshold; + } + +} + diff --git a/fuzzylite/src/defuzzifier/Bisector.cpp b/fuzzylite/src/defuzzifier/Bisector.cpp index ee4d2fc..ed61b6f 100644 --- a/fuzzylite/src/defuzzifier/Bisector.cpp +++ b/fuzzylite/src/defuzzifier/Bisector.cpp @@ -1,76 +1,60 @@ /* - 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/defuzzifier/Bisector.h" -#include "fl/term/Accumulated.h" #include "fl/term/Term.h" namespace fl { Bisector::Bisector(int resolution) - : IntegralDefuzzifier(resolution) { - } + : IntegralDefuzzifier(resolution) { } - Bisector::~Bisector() { - - } + Bisector::~Bisector() { } std::string Bisector::className() const { return "Bisector"; } + Complexity Bisector::complexity(const Term* term) const { + return Complexity().comparison(1).arithmetic(1 + 2 + 5) + + term->complexity().comparison(1).arithmetic(1 + 5).multiply(getResolution()); + } + scalar Bisector::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - if (not fl::Op::isFinite(minimum + maximum)) { - return fl::nan; - } - if (maximum - minimum > _resolution) { - FL_DBG("[accuracy warning] the resolution <" << _resolution << "> " - "is smaller than the range <" << minimum << ", " << maximum << ">. In order to " - "improve the accuracy, the resolution should be at least equal to the range."); - } - scalar dx = (maximum - minimum) / _resolution; + if (not Op::isFinite(minimum + maximum)) return fl::nan; - int counter = _resolution; + const scalar dx = (maximum - minimum) / getResolution(); + int counter = getResolution(); int left = 0, right = 0; scalar leftArea = 0, rightArea = 0; scalar xLeft = minimum, xRight = maximum; while (counter-- > 0) { - if (fl::Op::isLE(leftArea, rightArea)) { + if (Op::isLE(leftArea, rightArea)) { xLeft = minimum + (left + 0.5) * dx; leftArea += term->membership(xLeft); - left++; + ++left; } else { xRight = maximum - (right + 0.5) * dx; rightArea += term->membership(xRight); - right++; + ++right; } } - //Inverse weighted average to compensate - scalar bisector = (leftArea * xRight + rightArea * xLeft) / (leftArea + rightArea); - return bisector; + return (leftArea * xRight + rightArea * xLeft) / (leftArea + rightArea); } Bisector* Bisector::clone() const { @@ -81,5 +65,4 @@ namespace fl { return new Bisector; } - } diff --git a/fuzzylite/src/defuzzifier/Centroid.cpp b/fuzzylite/src/defuzzifier/Centroid.cpp index 01490d3..177da26 100644 --- a/fuzzylite/src/defuzzifier/Centroid.cpp +++ b/fuzzylite/src/defuzzifier/Centroid.cpp @@ -1,71 +1,60 @@ /* - 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/defuzzifier/Centroid.h" -#include "fl/term/Accumulated.h" #include "fl/term/Term.h" - namespace fl { Centroid::Centroid(int resolution) - : IntegralDefuzzifier(resolution) { - } - - Centroid::~Centroid() { + : IntegralDefuzzifier(resolution) { } - } + Centroid::~Centroid() { } std::string Centroid::className() const { return "Centroid"; } + Complexity Centroid::complexity(const Term* term) const { + return Complexity().comparison(1).arithmetic(1 + 2 + 1) + + term->complexity().arithmetic(6).multiply(getResolution()); + } + scalar Centroid::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - if (not fl::Op::isFinite(minimum + maximum)) { - return fl::nan; - } - if (maximum - minimum > _resolution) { - FL_DBG("[accuracy warning] the resolution <" << _resolution << "> " - "is smaller than the range <" << minimum << ", " << maximum << ">. In order to " - "improve the accuracy, the resolution should be at least equal to the range."); - } - scalar dx = (maximum - minimum) / _resolution; + if (not Op::isFinite(minimum + maximum)) return fl::nan; + + const int resolution = getResolution(); + const scalar dx = (maximum - minimum) / resolution; scalar x, y; - scalar area = 0, xcentroid = 0, ycentroid = 0; - for (int i = 0; i < _resolution; ++i) { + scalar area = 0, xcentroid = 0; + //scalar ycentroid = 0; + for (int i = 0; i < resolution; ++i) { x = minimum + (i + 0.5) * dx; y = term->membership(x); xcentroid += y * x; - ycentroid += y * y; + //ycentroid += y * y; area += y; } - xcentroid /= area; - ycentroid /= 2 * area; - area *= dx; //total area... unused, but for future reference. - return xcentroid; + //Final results not computed for efficiency + //xcentroid /= area; + //ycentroid /= 2 * area; + //area *= dx; + return xcentroid / area; } Centroid* Centroid::clone() const { @@ -76,5 +65,4 @@ namespace fl { return new Centroid; } - } diff --git a/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp b/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp index 2badf14..3b06b42 100644 --- a/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp +++ b/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp @@ -1,32 +1,24 @@ /* - 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/defuzzifier/IntegralDefuzzifier.h" namespace fl { - int IntegralDefuzzifier::_defaultResolution = 200; + int IntegralDefuzzifier::_defaultResolution = 100; void IntegralDefuzzifier::setDefaultResolution(int defaultResolution) { _defaultResolution = defaultResolution; @@ -37,11 +29,9 @@ namespace fl { } IntegralDefuzzifier::IntegralDefuzzifier(int resolution) - : Defuzzifier(), _resolution(resolution) { - } + : Defuzzifier(), _resolution(resolution) { } - IntegralDefuzzifier::~IntegralDefuzzifier() { - } + IntegralDefuzzifier::~IntegralDefuzzifier() { } void IntegralDefuzzifier::setResolution(int resolution) { this->_resolution = resolution; @@ -50,4 +40,5 @@ namespace fl { int IntegralDefuzzifier::getResolution() const { return this->_resolution; } + } diff --git a/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp b/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp index 470af52..ff05707 100644 --- a/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp +++ b/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp @@ -1,25 +1,17 @@ /* - 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/defuzzifier/LargestOfMaximum.h" @@ -30,29 +22,27 @@ namespace fl { LargestOfMaximum::LargestOfMaximum(int resolution) - : IntegralDefuzzifier(resolution) { - } + : IntegralDefuzzifier(resolution) { } - LargestOfMaximum::~LargestOfMaximum() { - } + LargestOfMaximum::~LargestOfMaximum() { } std::string LargestOfMaximum::className() const { return "LargestOfMaximum"; } + Complexity LargestOfMaximum::complexity(const Term* term) const { + return Complexity().comparison(1).arithmetic(1 + 2) + + term->complexity().comparison(1).arithmetic(3).multiply(getResolution()); + } + scalar LargestOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - if (not fl::Op::isFinite(minimum + maximum)) { - return fl::nan; - } - if (maximum - minimum > _resolution) { - FL_DBG("[accuracy warning] the resolution <" << _resolution << "> " - "is smaller than the range <" << minimum << ", " << maximum << ">. In order to " - "improve the accuracy, the resolution should be at least equal to the range."); - } - scalar dx = (maximum - minimum) / _resolution; + if (not Op::isFinite(minimum + maximum)) return fl::nan; + + const int resolution = getResolution(); + const scalar dx = (maximum - minimum) / resolution; scalar x, y; scalar ymax = -1.0, xlargest = maximum; - for (int i = 0; i < _resolution; ++i) { + for (int i = 0; i < resolution; ++i) { x = minimum + (i + 0.5) * dx; y = term->membership(x); diff --git a/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp b/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp index 7c40527..961e505 100644 --- a/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp +++ b/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp @@ -1,25 +1,17 @@ /* - 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/defuzzifier/MeanOfMaximum.h" @@ -27,36 +19,33 @@ #include "fl/Exception.h" #include "fl/term/Term.h" - namespace fl { MeanOfMaximum::MeanOfMaximum(int resolution) - : IntegralDefuzzifier(resolution) { - } + : IntegralDefuzzifier(resolution) { } - MeanOfMaximum::~MeanOfMaximum() { - } + MeanOfMaximum::~MeanOfMaximum() { } std::string MeanOfMaximum::className() const { return "MeanOfMaximum"; } + Complexity MeanOfMaximum::complexity(const Term* term) const { + return Complexity().comparison(1).arithmetic(1 + 2 + 2) + + term->complexity().comparison(4).arithmetic(3).multiply(getResolution()); + } + scalar MeanOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - if (not fl::Op::isFinite(minimum + maximum)) { - return fl::nan; - } - if (maximum - minimum > _resolution) { - FL_DBG("[accuracy warning] the resolution <" << _resolution << "> " - "is smaller than the range <" << minimum << ", " << maximum << ">. In order to " - "improve the accuracy, the resolution should be at least equal to the range."); - } - scalar dx = (maximum - minimum) / _resolution; + if (not Op::isFinite(minimum + maximum)) return fl::nan; + + const int resolution = getResolution(); + const scalar dx = (maximum - minimum) / resolution; scalar x, y; scalar ymax = -1.0; scalar xsmallest = minimum; scalar xlargest = maximum; bool samePlateau = false; - for (int i = 0; i < _resolution; ++i) { + for (int i = 0; i < resolution; ++i) { x = minimum + (i + 0.5) * dx; y = term->membership(x); @@ -67,14 +56,14 @@ namespace fl { xlargest = x; samePlateau = true; - } else if (Op::isEq(y, ymax) and samePlateau) { + } else if (samePlateau and Op::isEq(y, ymax)) { xlargest = x; } else if (Op::isLt(y, ymax)) { samePlateau = false; } } - return (xlargest + xsmallest) / 2.0; + return 0.5 * (xlargest + xsmallest); } MeanOfMaximum* MeanOfMaximum::clone() const { diff --git a/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp b/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp index 1e67395..7333702 100644 --- a/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp +++ b/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp @@ -1,25 +1,17 @@ /* - 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/defuzzifier/SmallestOfMaximum.h" @@ -27,33 +19,30 @@ #include "fl/Exception.h" #include "fl/term/Term.h" - namespace fl { SmallestOfMaximum::SmallestOfMaximum(int resolution) - : IntegralDefuzzifier(resolution) { - } + : IntegralDefuzzifier(resolution) { } - SmallestOfMaximum::~SmallestOfMaximum() { - } + SmallestOfMaximum::~SmallestOfMaximum() { } std::string SmallestOfMaximum::className() const { return "SmallestOfMaximum"; } + Complexity SmallestOfMaximum::complexity(const Term* term) const { + return Complexity().comparison(1).arithmetic(1 + 2) + + term->complexity().comparison(1).arithmetic(3).multiply(getResolution()); + } + scalar SmallestOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - if (not fl::Op::isFinite(minimum + maximum)) { - return fl::nan; - } - if (maximum - minimum > _resolution) { - FL_DBG("[accuracy warning] the resolution <" << _resolution << "> " - "is smaller than the range <" << minimum << ", " << maximum << ">. In order to " - "improve the accuracy, the resolution should be at least equal to the range."); - } - scalar dx = (maximum - minimum) / _resolution; + if (not Op::isFinite(minimum + maximum)) return fl::nan; + + const int resolution = getResolution(); + const scalar dx = (maximum - minimum) / resolution; scalar x, y; scalar ymax = -1.0, xsmallest = minimum; - for (int i = 0; i < _resolution; ++i) { + for (int i = 0; i < resolution; ++i) { x = minimum + (i + 0.5) * dx; y = term->membership(x); @@ -74,4 +63,3 @@ namespace fl { } } - diff --git a/fuzzylite/src/defuzzifier/WeightedAverage.cpp b/fuzzylite/src/defuzzifier/WeightedAverage.cpp index 105c9d4..34af8c9 100644 --- a/fuzzylite/src/defuzzifier/WeightedAverage.cpp +++ b/fuzzylite/src/defuzzifier/WeightedAverage.cpp @@ -1,113 +1,89 @@ /* - 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/defuzzifier/WeightedAverage.h" -#include "fl/term/Accumulated.h" -#include "fl/term/Activated.h" -#include "fl/norm/Norm.h" -#include "fl/norm/SNorm.h" -#include "fl/norm/TNorm.h" +#include "fl/term/Aggregated.h" #include <map> namespace fl { - WeightedAverage::WeightedAverage(Type type) : WeightedDefuzzifier(type) { - } + WeightedAverage::WeightedAverage(Type type) : WeightedDefuzzifier(type) { } - WeightedAverage::WeightedAverage(const std::string& type) : WeightedDefuzzifier(type) { - } + WeightedAverage::WeightedAverage(const std::string& type) : WeightedDefuzzifier(type) { } - WeightedAverage::~WeightedAverage() { - } + WeightedAverage::~WeightedAverage() { } std::string WeightedAverage::className() const { return "WeightedAverage"; } + Complexity WeightedAverage::complexity(const Term* term) const { + Complexity result; + result.comparison(4).function(1); //for dynamic_cast + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (fuzzyOutput) { + result += term->complexity().arithmetic(3).multiply(scalar(fuzzyOutput->numberOfTerms())); + } + return result; + } + scalar WeightedAverage::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - const Accumulated* fuzzyOutput = dynamic_cast<const Accumulated*> (term); + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); if (not fuzzyOutput) { std::ostringstream ss; ss << "[defuzzification error]" - << "expected an Accumulated term instead of" - << "<" << term->toString() << ">"; - throw fl::Exception(ss.str(), FL_AT); + << "expected an Aggregated term instead of" + << "<" << (term ? term->toString() : "null") << ">"; + throw Exception(ss.str(), FL_AT); } + if (fuzzyOutput->isEmpty()) return fl::nan; + minimum = fuzzyOutput->getMinimum(); maximum = fuzzyOutput->getMaximum(); + Type type = getType(); + if (type == Automatic) { + type = inferType(&(fuzzyOutput->terms().front())); + } + scalar sum = 0.0; scalar weights = 0.0; - - if (not fuzzyOutput->getAccumulation()) { - Type type = _type; - for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) { - Activated* activated = fuzzyOutput->getTerm(i); - scalar w = activated->getDegree(); - - if (type == Automatic) type = inferType(activated->getTerm()); - - scalar z = (type == TakagiSugeno) - //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work. - ? activated->getTerm()->membership(w) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions - : tsukamoto(activated->getTerm(), w, minimum, maximum); - + const std::size_t numberOfTerms = fuzzyOutput->numberOfTerms(); + if (type == TakagiSugeno) { + //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions + scalar w, z; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->membership(w); sum += w * z; weights += w; } } else { - typedef std::map<const Term*, std::vector<Activated*> > TermGroup; - TermGroup groups; - for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) { - Activated* value = fuzzyOutput->getTerm(i); - const Term* key = value->getTerm(); - groups[key].push_back(value); - } - TermGroup::const_iterator it = groups.begin(); - Type type = _type; - while (it != groups.end()) { - const Term* activatedTerm = it->first; - scalar accumulatedDegree = 0.0; - for (std::size_t i = 0; i < it->second.size(); ++i) - accumulatedDegree = fuzzyOutput->getAccumulation()->compute( - accumulatedDegree, it->second.at(i)->getDegree()); - - if (type == Automatic) type = inferType(activatedTerm); - - scalar z = (type == TakagiSugeno) - //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work. - ? activatedTerm->membership(accumulatedDegree) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions - : tsukamoto(activatedTerm, accumulatedDegree, minimum, maximum); - - sum += accumulatedDegree * z; - weights += accumulatedDegree; - - ++it; + scalar w, z; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->tsukamoto(w, minimum, maximum); + sum += w * z; + weights += w; } } return sum / weights; @@ -120,4 +96,5 @@ namespace fl { Defuzzifier* WeightedAverage::constructor() { return new WeightedAverage; } + } diff --git a/fuzzylite/src/defuzzifier/WeightedAverageCustom.cpp b/fuzzylite/src/defuzzifier/WeightedAverageCustom.cpp new file mode 100644 index 0000000..0beb722 --- /dev/null +++ b/fuzzylite/src/defuzzifier/WeightedAverageCustom.cpp @@ -0,0 +1,117 @@ +/* + 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 FuzzyLite License included with the software. + + 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/defuzzifier/WeightedAverageCustom.h" + +#include "fl/term/Aggregated.h" + +#include <map> + +namespace fl { + + WeightedAverageCustom::WeightedAverageCustom(Type type) : WeightedDefuzzifier(type) { } + + WeightedAverageCustom::WeightedAverageCustom(const std::string& type) : WeightedDefuzzifier(type) { } + + WeightedAverageCustom::~WeightedAverageCustom() { } + + std::string WeightedAverageCustom::className() const { + return "WeightedAverageCustom"; + } + + Complexity WeightedAverageCustom::complexity(const Term* term) const { + Complexity result; + result.comparison(3).arithmetic(1).function(1); + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (fuzzyOutput) { + result += term->complexity().arithmetic(3).comparison(2) + .multiply(scalar(fuzzyOutput->numberOfTerms())); + } + return result; + } + + scalar WeightedAverageCustom::defuzzify(const Term* term, + scalar minimum, scalar maximum) const { + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (not fuzzyOutput) { + std::ostringstream ss; + ss << "[defuzzification error]" + << "expected an Aggregated term instead of" + << "<" << (term ? term->toString() : "null") << ">"; + throw Exception(ss.str(), FL_AT); + } + + if (fuzzyOutput->isEmpty()) return fl::nan; + + minimum = fuzzyOutput->getMinimum(); + maximum = fuzzyOutput->getMaximum(); + + SNorm* aggregation = fuzzyOutput->getAggregation(); + + Type type = getType(); + if (type == Automatic) { + type = inferType(&(fuzzyOutput->terms().front())); + } + + scalar sum = 0.0; + scalar weights = 0.0; + const std::size_t numberOfTerms = fuzzyOutput->numberOfTerms(); + if (type == TakagiSugeno) { + //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions + scalar w, z, wz; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->membership(w); + const TNorm* implication = activated.getImplication(); + wz = implication ? implication->compute(w, z) : (w * z); + if (aggregation) { + sum = aggregation->compute(sum, wz); + weights = aggregation->compute(weights, w); + } else { + sum += wz; + weights += w; + } + } + } else { + scalar w, z, wz; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->tsukamoto(w, minimum, maximum); + const TNorm* implication = activated.getImplication(); + wz = implication ? implication->compute(w, z) : (w * z); + if (aggregation) { + sum = aggregation->compute(sum, wz); + weights = aggregation->compute(weights, w); + } else { + sum += wz; + weights += w; + } + } + } + return sum / weights; + } + + WeightedAverageCustom* WeightedAverageCustom::clone() const { + return new WeightedAverageCustom(*this); + } + + Defuzzifier* WeightedAverageCustom::constructor() { + return new WeightedAverageCustom; + } + +} diff --git a/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp b/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp index 760a5dc..743c59f 100644 --- a/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp +++ b/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp @@ -1,25 +1,17 @@ /* - 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/defuzzifier/WeightedDefuzzifier.h" @@ -36,24 +28,20 @@ namespace fl { - WeightedDefuzzifier::WeightedDefuzzifier(Type type) : _type(type) { - - } + WeightedDefuzzifier::WeightedDefuzzifier(Type type) : _type(type) { } WeightedDefuzzifier::WeightedDefuzzifier(const std::string& type) { - if (type == "Automatic") _type = Automatic; - else if (type == "TakagiSugeno") _type = TakagiSugeno; - else if (type == "Tsukamoto") _type = Tsukamoto; + if (type == "Automatic") setType(Automatic); + else if (type == "TakagiSugeno") setType(TakagiSugeno); + else if (type == "Tsukamoto") setType(Tsukamoto); else { - _type = Automatic; + setType(Automatic); FL_LOG("[warning] incorrect type <" + type + "> of WeightedDefuzzifier" + " has been defaulted to <Automatic>"); } } - WeightedDefuzzifier::~WeightedDefuzzifier() { - - } + WeightedDefuzzifier::~WeightedDefuzzifier() { } std::string WeightedDefuzzifier::typeName(Type type) { switch (type) { @@ -73,7 +61,7 @@ namespace fl { } std::string WeightedDefuzzifier::getTypeName() const { - return typeName(this->_type); + return typeName(getType()); } WeightedDefuzzifier::Type WeightedDefuzzifier::inferType(const Term* term) const { @@ -85,94 +73,4 @@ namespace fl { return Tsukamoto; } - bool WeightedDefuzzifier::isMonotonic(const Term* term) const { - return (dynamic_cast<const Concave*> (term)) or - (dynamic_cast<const Ramp*> (term)) or - (dynamic_cast<const Sigmoid*> (term)) or - (dynamic_cast<const SShape*> (term)) or - (dynamic_cast<const ZShape*> (term)); - } - - /** - * Instead of computing y=f(x), the goal of Tsukamoto is to find x=f(w), - * where f is monotonic. - */ - scalar WeightedDefuzzifier::tsukamoto(const Term* monotonic, scalar activationDegree, - scalar minimum, scalar maximum) const { - scalar w = activationDegree; - scalar z = fl::nan; //result; - bool isTsukamoto = true; - if (const Ramp* ramp = dynamic_cast<const Ramp*> (monotonic)) { - z = Op::scale(w, 0, 1, ramp->getStart(), ramp->getEnd()); - - } else if (const Sigmoid* sigmoid = dynamic_cast<const Sigmoid*> (monotonic)) { - if (Op::isEq(w, 1.0)) { - if (Op::isGE(sigmoid->getSlope(), 0.0)) { - z = maximum; - } else { - z = minimum; - } - - } else if (Op::isEq(w, 0.0)) { - if (Op::isGE(sigmoid->getSlope(), 0.0)) { - z = minimum; - } else { - z = maximum; - } - } else { - scalar a = sigmoid->getSlope(); - scalar b = sigmoid->getInflection(); - z = b + (std::log(1.0 / w - 1.0) / -a); - } - - } else if (const SShape* sshape = dynamic_cast<const SShape*> (monotonic)) { - scalar difference = sshape->getEnd() - sshape->getStart(); - scalar a = sshape->getStart() + std::sqrt(w * difference * difference / 2.0); - scalar b = sshape->getEnd() + std::sqrt(difference * difference * (w - 1.0) / -2.0); - if (std::fabs(w - monotonic->membership(a)) < - std::fabs(w - monotonic->membership(b))) { - z = a; - } else { - z = b; - } - - } else if (const ZShape* zshape = dynamic_cast<const ZShape*> (monotonic)) { - scalar difference = zshape->getEnd() - zshape->getStart(); - scalar a = zshape->getStart() + std::sqrt(difference * difference * (w - 1.0) / -2.0); - scalar b = zshape->getEnd() + std::sqrt(w * difference * difference / 2.0); - if (std::fabs(w - monotonic->membership(a)) < - std::fabs(w - monotonic->membership(b))) { - z = a; - } else { - z = b; - } - - } else if (const Concave* concave = dynamic_cast<const Concave*> (monotonic)) { - scalar i = concave->getInflection(); - scalar e = concave->getEnd(); - z = (i - e) / concave->membership(w) + 2 * e - i; - } else { - isTsukamoto = false; - } - - if (isTsukamoto) { - //Compare difference between estimated and true value - scalar fz = monotonic->membership(z); - if (not Op::isEq(w, fz, 1e-2)) { - FL_DBG("[tsukamoto warning] difference <" << Op::str(std::abs(w - fz)) << "> " - "might suggest an inaccurate computation of z because it is " - "expected w=f(z) in " << monotonic->className() << - " term <" << monotonic->getName() << ">, but " - "w=" << w << " " - "f(z)=" << fz << " and " - "z=" << Op::str(z)); - } - } else { - // else fallback to the regular Takagi-Sugeno or inverse Tsukamoto (according to term) - z = monotonic->membership(w); - } - return z; - } - - } diff --git a/fuzzylite/src/defuzzifier/WeightedSum.cpp b/fuzzylite/src/defuzzifier/WeightedSum.cpp index fb3e2e3..6c2343d 100644 --- a/fuzzylite/src/defuzzifier/WeightedSum.cpp +++ b/fuzzylite/src/defuzzifier/WeightedSum.cpp @@ -1,110 +1,86 @@ /* - 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/defuzzifier/WeightedSum.h" -#include "fl/term/Accumulated.h" -#include "fl/term/Activated.h" -#include "fl/norm/SNorm.h" -#include "fl/norm/TNorm.h" +#include "fl/term/Aggregated.h" #include <map> + namespace fl { - WeightedSum::WeightedSum(Type type) : WeightedDefuzzifier(type) { - } + WeightedSum::WeightedSum(Type type) : WeightedDefuzzifier(type) { } - WeightedSum::WeightedSum(const std::string& type) : WeightedDefuzzifier(type) { + WeightedSum::WeightedSum(const std::string& type) : WeightedDefuzzifier(type) { } - } - - WeightedSum::~WeightedSum() { - } + WeightedSum::~WeightedSum() { } std::string WeightedSum::className() const { return "WeightedSum"; } + Complexity WeightedSum::complexity(const Term* term) const { + Complexity result; + result.comparison(4).function(1); + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (fuzzyOutput) { + result += term->complexity().arithmetic(2).multiply(scalar(fuzzyOutput->numberOfTerms())); + } + return result; + } + scalar WeightedSum::defuzzify(const Term* term, scalar minimum, scalar maximum) const { - const Accumulated* fuzzyOutput = dynamic_cast<const Accumulated*> (term); + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); if (not fuzzyOutput) { std::ostringstream ss; ss << "[defuzzification error]" - << "expected an Accumulated term instead of" - << "<" << term->toString() << ">"; - throw fl::Exception(ss.str(), FL_AT); + << "expected an Aggregated term instead of" + << "<" << (term ? term->toString() : "null") << ">"; + throw Exception(ss.str(), FL_AT); } + if (fuzzyOutput->isEmpty()) return fl::nan; + minimum = fuzzyOutput->getMinimum(); maximum = fuzzyOutput->getMaximum(); + Type type = getType(); + if (type == Automatic) { + type = inferType(&(fuzzyOutput->terms().front())); + } scalar sum = 0.0; - - if (not fuzzyOutput->getAccumulation()) { - Type type = _type; - for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) { - Activated* activated = fuzzyOutput->getTerm(i); - scalar w = activated->getDegree(); - - if (type == Automatic) type = inferType(activated->getTerm()); - - scalar z = (type == TakagiSugeno) - //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work. - ? activated->getTerm()->membership(w) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions - : tsukamoto(activated->getTerm(), w, minimum, maximum); - + const std::size_t numberOfTerms = fuzzyOutput->numberOfTerms(); + if (type == TakagiSugeno) { + //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions + scalar w, z; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->membership(w); sum += w * z; } } else { - typedef std::map<const Term*, std::vector<Activated*> > TermGroup; - TermGroup groups; - for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) { - Activated* value = fuzzyOutput->getTerm(i); - const Term* key = value->getTerm(); - groups[key].push_back(value); - } - TermGroup::const_iterator it = groups.begin(); - Type type = _type; - while (it != groups.end()) { - const Term* activatedTerm = it->first; - scalar accumulatedDegree = 0.0; - for (std::size_t i = 0; i < it->second.size(); ++i) - accumulatedDegree = fuzzyOutput->getAccumulation()->compute( - accumulatedDegree, it->second.at(i)->getDegree()); - - if (type == Automatic) type = inferType(activatedTerm); - - scalar z = (type == TakagiSugeno) - //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work. - ? activatedTerm->membership(accumulatedDegree) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions - : tsukamoto(activatedTerm, accumulatedDegree, minimum, maximum); - - sum += accumulatedDegree * z; - - ++it; + scalar w, z; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->tsukamoto(w, minimum, maximum); + sum += w * z; } } return sum; diff --git a/fuzzylite/src/defuzzifier/WeightedSumCustom.cpp b/fuzzylite/src/defuzzifier/WeightedSumCustom.cpp new file mode 100644 index 0000000..5a9084c --- /dev/null +++ b/fuzzylite/src/defuzzifier/WeightedSumCustom.cpp @@ -0,0 +1,112 @@ +/* + 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 FuzzyLite License included with the software. + + 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/defuzzifier/WeightedSumCustom.h" + +#include "fl/term/Aggregated.h" + +#include <map> + +namespace fl { + + WeightedSumCustom::WeightedSumCustom(Type type) : WeightedDefuzzifier(type) { } + + WeightedSumCustom::WeightedSumCustom(const std::string& type) : WeightedDefuzzifier(type) { } + + WeightedSumCustom::~WeightedSumCustom() { } + + std::string WeightedSumCustom::className() const { + return "WeightedSumCustom"; + } + + Complexity WeightedSumCustom::complexity(const Term* term) const { + Complexity result; + result.comparison(4).function(1); + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (fuzzyOutput) { + result += term->complexity().arithmetic(2).comparison(2) + .multiply(scalar(fuzzyOutput->numberOfTerms())); + } + return result; + } + + scalar WeightedSumCustom::defuzzify(const Term* term, + scalar minimum, scalar maximum) const { + const Aggregated* fuzzyOutput = dynamic_cast<const Aggregated*> (term); + if (not fuzzyOutput) { + std::ostringstream ss; + ss << "[defuzzification error]" + << "expected an Aggregated term instead of" + << "<" << (term ? term->toString() : "null") << ">"; + throw Exception(ss.str(), FL_AT); + } + + if (fuzzyOutput->isEmpty()) return fl::nan; + + minimum = fuzzyOutput->getMinimum(); + maximum = fuzzyOutput->getMaximum(); + + Type type = getType(); + if (type == Automatic) { + type = inferType(&(fuzzyOutput->terms().front())); + } + + SNorm* aggregation = fuzzyOutput->getAggregation(); + + scalar sum = 0.0; + const std::size_t numberOfTerms = fuzzyOutput->numberOfTerms(); + if (type == TakagiSugeno) { + //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions + scalar w, z, wz; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->membership(w); + const TNorm* implication = activated.getImplication(); + wz = implication ? implication->compute(w, z) : (w * z); + if (aggregation) { + sum = aggregation->compute(sum, wz); + } else { + sum += wz; + } + } + } else { + scalar w, z, wz; + for (std::size_t i = 0; i < numberOfTerms; ++i) { + const Activated& activated = fuzzyOutput->getTerm(i); + w = activated.getDegree(); + z = activated.getTerm()->tsukamoto(w, minimum, maximum); + const TNorm* implication = activated.getImplication(); + wz = implication ? implication->compute(w, z) : (w * z); + if (aggregation) { + sum = aggregation->compute(sum, wz); + } else { + sum += wz; + } + } + } + return sum; + } + + WeightedSumCustom* WeightedSumCustom::clone() const { + return new WeightedSumCustom(*this); + } + + Defuzzifier* WeightedSumCustom::constructor() { + return new WeightedSumCustom; + } + +} diff --git a/fuzzylite/src/factory/ActivationFactory.cpp b/fuzzylite/src/factory/ActivationFactory.cpp new file mode 100644 index 0000000..d000667 --- /dev/null +++ b/fuzzylite/src/factory/ActivationFactory.cpp @@ -0,0 +1,42 @@ +/* + 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 FuzzyLite License included with the software. + + 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/factory/ActivationFactory.h" + +#include "fl/activation/First.h" +#include "fl/activation/General.h" +#include "fl/activation/Highest.h" +#include "fl/activation/Last.h" +#include "fl/activation/Lowest.h" +#include "fl/activation/Proportional.h" +#include "fl/activation/Threshold.h" + +namespace fl { + + ActivationFactory::ActivationFactory() : ConstructionFactory<Activation*>("Activation") { + registerConstructor("", fl::null); + registerConstructor(First().className(), &(First::constructor)); + registerConstructor(General().className(), &(General::constructor)); + registerConstructor(Highest().className(), &(Highest::constructor)); + registerConstructor(Last().className(), &(Last::constructor)); + registerConstructor(Lowest().className(), &(Lowest::constructor)); + registerConstructor(Proportional().className(), &(Proportional::constructor)); + registerConstructor(Threshold().className(), &(Threshold::constructor)); + } + + ActivationFactory::~ActivationFactory() { } + +} diff --git a/fuzzylite/src/factory/CloningFactory.cpp b/fuzzylite/src/factory/CloningFactory.cpp deleted file mode 100644 index b187171..0000000 --- a/fuzzylite/src/factory/CloningFactory.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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/factory/CloningFactory.h" - -#include "fl/Exception.h" -#include "fl/term/Function.h" - -namespace fl { - - template<typename T> - CloningFactory<T>::CloningFactory(const std::string& name) : _name(name) { - - } - - template<typename T> - CloningFactory<T>::CloningFactory(const CloningFactory& other) { - typename std::map<std::string, T>::const_iterator it = other._objects.begin(); - while (it != other._objects.end()) { - T clone = fl::null; - if (it->second) clone = it->second->clone(); - this->_objects[it->first] = clone; - ++it; - } - } - - template<typename T> - CloningFactory<T>& CloningFactory<T>::operator=(const CloningFactory& other) { - if (this != &other) { - typename std::map<std::string, T>::const_iterator it = this->_objects.begin(); - while (it != this->_objects.end()) { - if (it->second) delete it->second; - ++it; - } - this->_objects.clear(); - - it = other._objects.begin(); - while (it != other._objects.end()) { - T clone = fl::null; - if (it->second) clone = it->second->clone(); - this->_objects[it->first] = clone; - ++it; - } - } - return *this; - } - - template<typename T> - CloningFactory<T>::~CloningFactory() { - typename std::map<std::string, T>::const_iterator it = this->_objects.begin(); - while (it != this->_objects.end()) { - if (it->second) delete it->second; - ++it; - } - } - - template<typename T> - std::string CloningFactory<T>::name() const { - return this->_name; - } - - template<typename T> - void CloningFactory<T>::registerObject(const std::string& key, T object) { - this->_objects[key] = object; - } - - template<typename T> - void CloningFactory<T>::deregisterObject(const std::string& key) { - typename std::map<std::string, T>::iterator it = this->_objects.find(key); - if (it != this->_objects.end()) { - this->_objects.erase(it); - delete it->second; - } - } - - template<typename T> - bool CloningFactory<T>::hasObject(const std::string& key) const { - typename std::map<std::string, T>::const_iterator it = this->_objects.find(key); - return (it != this->_objects.end()); - } - - template<typename T> - T CloningFactory<T>::getObject(const std::string& key) const { - typename std::map<std::string, T>::const_iterator it = this->_objects.find(key); - if (it != this->_objects.end()) { - if (it->second) return it->second; - } - return fl::null; - } - - template<typename T> - T CloningFactory<T>::cloneObject(const std::string& key) const { - typename std::map<std::string, T>::const_iterator it = this->_objects.find(key); - if (it != this->_objects.end()) { - if (it->second) return it->second->clone(); - return fl::null; - } - throw fl::Exception("[cloning error] " + _name + " object by name <" + key + "> not registered", FL_AT); - } - - template<typename T> - std::vector<std::string> CloningFactory<T>::available() const { - std::vector<std::string> result; - typename std::map<std::string, T>::const_iterator it = this->_objects.begin(); - while (it != this->_objects.end()) { - result.push_back(it->first); - } - return result; - } - - template class fl::CloningFactory<fl::Function::Element*>; -} - - diff --git a/fuzzylite/src/factory/ConstructionFactory.cpp b/fuzzylite/src/factory/ConstructionFactory.cpp deleted file mode 100644 index b316e52..0000000 --- a/fuzzylite/src/factory/ConstructionFactory.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - 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/factory/ConstructionFactory.h" - -#include "fl/Exception.h" -#include "fl/defuzzifier/Defuzzifier.h" -#include "fl/hedge/Hedge.h" -#include "fl/norm/SNorm.h" -#include "fl/norm/TNorm.h" -#include "fl/term/Function.h" -#include "fl/term/Term.h" - - - -namespace fl { - - template <typename T> - ConstructionFactory<T>::ConstructionFactory(const std::string& name) : _name(name) { - - } - - template <typename T> - ConstructionFactory<T>::~ConstructionFactory() { - } - - template<typename T> - std::string ConstructionFactory<T>::name() const { - return this->_name; - } - - template <typename T> - void ConstructionFactory<T>::registerConstructor(const std::string& key, Constructor constructor) { - this->_constructors[key] = constructor; - } - - template <typename T> - void ConstructionFactory<T>::deregisterConstructor(const std::string& key) { - typename std::map<std::string, Constructor>::iterator it = this->_constructors.find(key); - if (it != this->_constructors.end()) { - this->_constructors.erase(it); - } - } - - template <typename T> - bool ConstructionFactory<T>::hasConstructor(const std::string& key) const { - typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key); - return (it != this->_constructors.end()); - } - - template <typename T> - typename ConstructionFactory<T>::Constructor ConstructionFactory<T>::getConstructor(const std::string& key) const { - typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key); - if (it != this->_constructors.end()) { - return it->second; - } - return fl::null; - } - - template <typename T> - T ConstructionFactory<T>::constructObject(const std::string& key) const { - typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key); - if (it != this->_constructors.end()) { - if (it->second) { - return it->second(); - } - return fl::null; - } - std::ostringstream ss; - ss << "[factory error] constructor of " + _name + " <" << key << "> not registered"; - throw fl::Exception(ss.str(), FL_AT); - } - - template <typename T> - std::vector<std::string> ConstructionFactory<T>::available() const { - std::vector<std::string> result; - typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.begin(); - while (it != this->_constructors.end()) { - result.push_back(it->first); - ++it; - } - return result; - } - - template class ConstructionFactory<Defuzzifier*>; - template class ConstructionFactory<Hedge*>; - template class ConstructionFactory<SNorm*>; - template class ConstructionFactory<TNorm*>; - template class ConstructionFactory<Term*>; -} - - - diff --git a/fuzzylite/src/factory/DefuzzifierFactory.cpp b/fuzzylite/src/factory/DefuzzifierFactory.cpp index 8d89c2d..3b0bfb1 100644 --- a/fuzzylite/src/factory/DefuzzifierFactory.cpp +++ b/fuzzylite/src/factory/DefuzzifierFactory.cpp @@ -1,25 +1,17 @@ /* - 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/factory/DefuzzifierFactory.h" @@ -30,7 +22,9 @@ #include "fl/defuzzifier/LargestOfMaximum.h" #include "fl/defuzzifier/MeanOfMaximum.h" #include "fl/defuzzifier/WeightedAverage.h" +#include "fl/defuzzifier/WeightedAverageCustom.h" #include "fl/defuzzifier/WeightedSum.h" +#include "fl/defuzzifier/WeightedSumCustom.h" namespace fl { @@ -42,12 +36,12 @@ namespace fl { registerConstructor(MeanOfMaximum().className(), &(MeanOfMaximum::constructor)); registerConstructor(SmallestOfMaximum().className(), &(SmallestOfMaximum::constructor)); registerConstructor(WeightedAverage().className(), &(WeightedAverage::constructor)); +// registerConstructor(WeightedAverageCustom().className(), &(WeightedAverageCustom::constructor)); registerConstructor(WeightedSum().className(), &(WeightedSum::constructor)); +// registerConstructor(WeightedSumCustom().className(), &(WeightedSumCustom::constructor)); } - DefuzzifierFactory::~DefuzzifierFactory() { - - } + DefuzzifierFactory::~DefuzzifierFactory() { } Defuzzifier* DefuzzifierFactory::constructDefuzzifier(const std::string& key, int resolution, WeightedDefuzzifier::Type type) const { @@ -75,4 +69,5 @@ namespace fl { } return result; } + } diff --git a/fuzzylite/src/factory/FactoryManager.cpp b/fuzzylite/src/factory/FactoryManager.cpp index c2374dc..367d982 100644 --- a/fuzzylite/src/factory/FactoryManager.cpp +++ b/fuzzylite/src/factory/FactoryManager.cpp @@ -1,60 +1,46 @@ /* - 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/factory/FactoryManager.h" -#include "fl/factory/DefuzzifierFactory.h" -#include "fl/factory/FunctionFactory.h" -#include "fl/factory/HedgeFactory.h" -#include "fl/factory/SNormFactory.h" -#include "fl/factory/TermFactory.h" -#include "fl/factory/TNormFactory.h" - namespace fl { - FactoryManager FactoryManager::_instance; - FactoryManager* FactoryManager::instance() { + static FL_ITHREAD_LOCAL FactoryManager _instance; return &_instance; } FactoryManager::FactoryManager() : - _tnorm(new TNormFactory), _snorm(new SNormFactory), _defuzzifier(new DefuzzifierFactory), - _term(new TermFactory), _hedge(new HedgeFactory), _function(new FunctionFactory) { - } + _tnorm(new TNormFactory), _snorm(new SNormFactory), _activation(new ActivationFactory), + _defuzzifier(new DefuzzifierFactory), _term(new TermFactory), + _hedge(new HedgeFactory), _function(new FunctionFactory) { } FactoryManager::FactoryManager(TNormFactory* tnorm, SNormFactory* snorm, - DefuzzifierFactory* defuzzifier, TermFactory* term, - HedgeFactory* hedge, FunctionFactory* function) : - _tnorm(tnorm), _snorm(snorm), _defuzzifier(defuzzifier), _term(term), _hedge(hedge), - _function(function) { - } + ActivationFactory* activation, DefuzzifierFactory* defuzzifier, + TermFactory* term, HedgeFactory* hedge, FunctionFactory* function) : + _tnorm(tnorm), _snorm(snorm), _activation(activation), + _defuzzifier(defuzzifier), _term(term), _hedge(hedge), _function(function) { } FactoryManager::FactoryManager(const FactoryManager& other) - : _tnorm(fl::null), _snorm(fl::null), _defuzzifier(fl::null), _term(fl::null), _hedge(fl::null), _function(fl::null) { + : _tnorm(fl::null), _snorm(fl::null), _activation(fl::null), + _defuzzifier(fl::null), _term(fl::null), _hedge(fl::null), + _function(fl::null) { if (other._tnorm.get()) this->_tnorm.reset(new TNormFactory(*other._tnorm.get())); if (other._snorm.get()) this->_snorm.reset(new SNormFactory(*other._snorm.get())); + if (other._activation.get()) this->_activation.reset(new ActivationFactory(*other._activation.get())); if (other._defuzzifier.get()) this->_defuzzifier.reset(new DefuzzifierFactory(*other._defuzzifier.get())); if (other._term.get()) this->_term.reset(new TermFactory(*other._term.get())); if (other._hedge.get()) this->_hedge.reset(new HedgeFactory(*other._hedge.get())); @@ -63,8 +49,17 @@ namespace fl { FactoryManager& FactoryManager::operator=(const FactoryManager& other) { if (this != &other) { + _tnorm.reset(fl::null); + _snorm.reset(fl::null); + _activation.reset(fl::null); + _defuzzifier.reset(fl::null); + _term.reset(fl::null); + _hedge.reset(fl::null); + _function.reset(fl::null); + if (other._tnorm.get()) this->_tnorm.reset(new TNormFactory(*other._tnorm.get())); if (other._snorm.get()) this->_snorm.reset(new SNormFactory(*other._snorm.get())); + if (other._activation.get()) this->_activation.reset(new ActivationFactory(*other._activation.get())); if (other._defuzzifier.get()) this->_defuzzifier.reset(new DefuzzifierFactory(*other._defuzzifier.get())); if (other._term.get()) this->_term.reset(new TermFactory(*other._term.get())); if (other._hedge.get()) this->_hedge.reset(new HedgeFactory(*other._hedge.get())); @@ -73,8 +68,7 @@ namespace fl { return *this; } - FactoryManager::~FactoryManager() { - } + FactoryManager::~FactoryManager() { } void FactoryManager::setTnorm(TNormFactory* tnorm) { this->_tnorm.reset(tnorm); @@ -92,6 +86,14 @@ namespace fl { return this->_snorm.get(); } + void FactoryManager::setActivation(ActivationFactory* activation) { + this->_activation.reset(activation); + } + + ActivationFactory* FactoryManager::activation() const { + return this->_activation.get(); + } + void FactoryManager::setDefuzzifier(DefuzzifierFactory* defuzzifier) { this->_defuzzifier.reset(defuzzifier); } diff --git a/fuzzylite/src/factory/FunctionFactory.cpp b/fuzzylite/src/factory/FunctionFactory.cpp index 3c0718d..8e52ddf 100644 --- a/fuzzylite/src/factory/FunctionFactory.cpp +++ b/fuzzylite/src/factory/FunctionFactory.cpp @@ -1,25 +1,17 @@ /* - 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/factory/FunctionFactory.h" @@ -33,125 +25,130 @@ namespace fl { registerFunctions(); } - FunctionFactory::~FunctionFactory() { - - } + FunctionFactory::~FunctionFactory() { } void FunctionFactory::registerOperators() { //OPERATORS: int p = 100; //First order: not, negate: registerObject("!", new Function::Element("!", "Logical NOT", - Function::Element::OPERATOR, &(fl::Op::logicalNot), p, 1)); //logical not + Function::Element::Operator, &(Op::logicalNot), p, 1)); //logical not registerObject("~", new Function::Element("~", "Negation", - Function::Element::OPERATOR, &(fl::Op::negate), p, 1)); // ~ negates a number + Function::Element::Operator, &(Op::negate), p, 1)); // ~ negates a number p -= 10; //Second order: power registerObject("^", new Function::Element("^", "Power", - Function::Element::OPERATOR, &(std::pow), p, 1)); + Function::Element::Operator, &(std::pow), p, 1)); p -= 10; //Third order: multiplication, division, modulo registerObject("*", new Function::Element("*", "Multiplication", - Function::Element::OPERATOR, &(fl::Op::multiply), p)); + Function::Element::Operator, &(Op::multiply), p)); registerObject("/", new Function::Element("/", "Division", - Function::Element::OPERATOR, &(fl::Op::divide), p)); + Function::Element::Operator, &(Op::divide), p)); registerObject("%", new Function::Element("%", "Modulo", - Function::Element::OPERATOR, &(fl::Op::modulo), p)); + Function::Element::Operator, &(Op::modulo), p)); p -= 10; //Fourth order: addition, subtraction registerObject("+", new Function::Element("+", "Addition", - Function::Element::OPERATOR, &(fl::Op::add), p)); + Function::Element::Operator, &(Op::add), p)); registerObject("-", new Function::Element("-", "Subtraction", - Function::Element::OPERATOR, &(fl::Op::subtract), p)); + Function::Element::Operator, &(Op::subtract), p)); //Fifth order: logical and, logical or p -= 10; //Logical AND - registerObject(fl::Rule::andKeyword(), new Function::Element(fl::Rule::andKeyword(), "Logical AND", - Function::Element::OPERATOR, &(fl::Op::logicalAnd), p)); + registerObject(Rule::andKeyword(), new Function::Element(Rule::andKeyword(), "Logical AND", + Function::Element::Operator, &(Op::logicalAnd), p)); p -= 10; //Logical OR - registerObject(fl::Rule::orKeyword(), new Function::Element(fl::Rule::orKeyword(), "Logical OR", - Function::Element::OPERATOR, &(fl::Op::logicalOr), p)); + registerObject(Rule::orKeyword(), new Function::Element(Rule::orKeyword(), "Logical OR", + Function::Element::Operator, &(Op::logicalOr), p)); } void FunctionFactory::registerFunctions() { //FUNCTIONS registerObject("gt", new Function::Element("gt", "Greater than (>)", - Function::Element::FUNCTION, &(fl::Op::gt))); + Function::Element::Function, &(Op::gt))); registerObject("ge", new Function::Element("ge", "Greater than or equal to (>=)", - Function::Element::FUNCTION, &(fl::Op::ge))); + Function::Element::Function, &(Op::ge))); registerObject("eq", new Function::Element("eq", "Equal to (==)", - Function::Element::FUNCTION, &(fl::Op::eq))); + Function::Element::Function, &(Op::eq))); registerObject("neq", new Function::Element("neq", "Not equal to (!=)", - Function::Element::FUNCTION, &(fl::Op::neq))); + Function::Element::Function, &(Op::neq))); registerObject("le", new Function::Element("le", "Less than or equal to (<=)", - Function::Element::FUNCTION, &(fl::Op::le))); + Function::Element::Function, &(Op::le))); registerObject("lt", new Function::Element("lt", "Less than (<)", - Function::Element::FUNCTION, &(fl::Op::lt))); + Function::Element::Function, &(Op::lt))); + + registerObject("min", new Function::Element("min", "Minimum", + Function::Element::Function, &(Op::min))); + registerObject("max", new Function::Element("max", "Maximum", + Function::Element::Function, &(Op::max))); registerObject("acos", new Function::Element("acos", "Inverse cosine", - Function::Element::FUNCTION, &(std::acos))); + Function::Element::Function, &(std::acos))); registerObject("asin", new Function::Element("asin", "Inverse sine", - Function::Element::FUNCTION, &(std::asin))); + Function::Element::Function, &(std::asin))); registerObject("atan", new Function::Element("atan", "Inverse tangent", - Function::Element::FUNCTION, &(std::atan))); + Function::Element::Function, &(std::atan))); registerObject("ceil", new Function::Element("ceil", "Ceiling", - Function::Element::FUNCTION, &(std::ceil))); + Function::Element::Function, &(std::ceil))); registerObject("cos", new Function::Element("cos", "Cosine", - Function::Element::FUNCTION, &(std::cos))); + Function::Element::Function, &(std::cos))); registerObject("cosh", new Function::Element("cosh", "Hyperbolic cosine", - Function::Element::FUNCTION, &(std::cosh))); + Function::Element::Function, &(std::cosh))); registerObject("exp", new Function::Element("exp", "Exponential", - Function::Element::FUNCTION, &(std::exp))); + Function::Element::Function, &(std::exp))); + registerObject("abs", new Function::Element("abs", "Absolute", + Function::Element::Function, &(std::abs))); registerObject("fabs", new Function::Element("fabs", "Absolute", - Function::Element::FUNCTION, &(std::fabs))); + Function::Element::Function, &(std::fabs))); registerObject("floor", new Function::Element("floor", "Floor", - Function::Element::FUNCTION, &(std::floor))); + Function::Element::Function, &(std::floor))); registerObject("log", new Function::Element("log", "Natural logarithm", - Function::Element::FUNCTION, &(std::log))); + Function::Element::Function, &(std::log))); registerObject("log10", new Function::Element("log10", "Common logarithm", - Function::Element::FUNCTION, &(std::log10))); + Function::Element::Function, &(std::log10))); registerObject("round", new Function::Element("round", "Round", - Function::Element::FUNCTION, &(fl::Op::round))); + Function::Element::Function, &(Op::round))); registerObject("sin", new Function::Element("sin", "Sine", - Function::Element::FUNCTION, &(std::sin))); + Function::Element::Function, &(std::sin))); registerObject("sinh", new Function::Element("sinh", "Hyperbolic sine", - Function::Element::FUNCTION, &(std::sinh))); + Function::Element::Function, &(std::sinh))); registerObject("sqrt", new Function::Element("sqrt", "Square root", - Function::Element::FUNCTION, &(std::sqrt))); + Function::Element::Function, &(std::sqrt))); registerObject("tan", new Function::Element("tan", "Tangent", - Function::Element::FUNCTION, &(std::tan))); + Function::Element::Function, &(std::tan))); registerObject("tanh", new Function::Element("tanh", "Hyperbolic tangent", - Function::Element::FUNCTION, &(std::tanh))); + Function::Element::Function, &(std::tanh))); #if defined(FL_UNIX) && !defined(FL_USE_FLOAT) //found in Unix when using double precision. not found in Windows. registerObject("log1p", new Function::Element("log1p", "Natural logarithm plus one", - Function::Element::FUNCTION, &(log1p))); + Function::Element::Function, &(log1p))); registerObject("acosh", new Function::Element("acosh", "Inverse hyperbolic cosine", - Function::Element::FUNCTION, &(acosh))); + Function::Element::Function, &(acosh))); registerObject("asinh", new Function::Element("asinh", "Inverse hyperbolic sine", - Function::Element::FUNCTION, &(asinh))); + Function::Element::Function, &(asinh))); registerObject("atanh", new Function::Element("atanh", "Inverse hyperbolic tangent", - Function::Element::FUNCTION, &(atanh))); + Function::Element::Function, &(atanh))); #endif registerObject("pow", new Function::Element("pow", "Power", - Function::Element::FUNCTION, &(std::pow))); + Function::Element::Function, &(std::pow))); registerObject("atan2", new Function::Element("atan2", "Inverse tangent (y,x)", - Function::Element::FUNCTION, &(std::atan2))); + Function::Element::Function, &(std::atan2))); registerObject("fmod", new Function::Element("fmod", "Floating-point remainder", - Function::Element::FUNCTION, &(std::fmod))); + Function::Element::Function, &(std::fmod))); } std::vector<std::string> FunctionFactory::availableOperators() const { std::vector<std::string> result; - std::map<std::string, Function::Element*>::const_iterator it = this->_objects.begin(); - while (it != this->_objects.end()) { - if (it->second and it->second->type == Function::Element::OPERATOR) + std::map<std::string, Function::Element*>::const_iterator it = this->objects().begin(); + while (it != this->objects().end()) { + if (it->second and it->second->type == Function::Element::Operator) result.push_back(it->first); ++it; } @@ -160,9 +157,9 @@ namespace fl { std::vector<std::string> FunctionFactory::availableFunctions() const { std::vector<std::string> result; - std::map<std::string, Function::Element*>::const_iterator it = this->_objects.begin(); - while (it != this->_objects.end()) { - if (it->second and it->second->type == Function::Element::FUNCTION) + std::map<std::string, Function::Element*>::const_iterator it = this->objects().begin(); + while (it != this->objects().end()) { + if (it->second and it->second->type == Function::Element::Function) result.push_back(it->first); ++it; } diff --git a/fuzzylite/src/factory/HedgeFactory.cpp b/fuzzylite/src/factory/HedgeFactory.cpp index ea5f77a..1a6452c 100644 --- a/fuzzylite/src/factory/HedgeFactory.cpp +++ b/fuzzylite/src/factory/HedgeFactory.cpp @@ -1,25 +1,17 @@ /* - 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/factory/HedgeFactory.h" @@ -31,7 +23,6 @@ #include "fl/hedge/Somewhat.h" #include "fl/hedge/Very.h" - namespace fl { HedgeFactory::HedgeFactory() : ConstructionFactory<Hedge*>("Hedge") { @@ -44,8 +35,6 @@ namespace fl { registerConstructor(Very().name(), &(Very::constructor)); } - HedgeFactory::~HedgeFactory() { - - } + HedgeFactory::~HedgeFactory() { } } diff --git a/fuzzylite/src/factory/SNormFactory.cpp b/fuzzylite/src/factory/SNormFactory.cpp index 6e42551..fdaef87 100644 --- a/fuzzylite/src/factory/SNormFactory.cpp +++ b/fuzzylite/src/factory/SNormFactory.cpp @@ -1,25 +1,17 @@ /* - 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/factory/SNormFactory.h" @@ -32,6 +24,7 @@ #include "fl/norm/s/Maximum.h" #include "fl/norm/s/NilpotentMaximum.h" #include "fl/norm/s/NormalizedSum.h" +#include "fl/norm/s/UnboundedSum.h" namespace fl { @@ -45,11 +38,9 @@ namespace fl { registerConstructor(Maximum().className(), &(Maximum::constructor)); registerConstructor(NilpotentMaximum().className(), &(NilpotentMaximum::constructor)); registerConstructor(NormalizedSum().className(), &(NormalizedSum::constructor)); + registerConstructor(UnboundedSum().className(), &(UnboundedSum::constructor)); } - SNormFactory::~SNormFactory() { - - } - + SNormFactory::~SNormFactory() { } } diff --git a/fuzzylite/src/factory/TNormFactory.cpp b/fuzzylite/src/factory/TNormFactory.cpp index 95628cc..0dda02e 100644 --- a/fuzzylite/src/factory/TNormFactory.cpp +++ b/fuzzylite/src/factory/TNormFactory.cpp @@ -1,25 +1,17 @@ /* - 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/factory/TNormFactory.h" @@ -45,9 +37,6 @@ namespace fl { registerConstructor(NilpotentMinimum().className(), &(NilpotentMinimum::constructor)); } - TNormFactory::~TNormFactory() { - - } - + TNormFactory::~TNormFactory() { } } diff --git a/fuzzylite/src/factory/TermFactory.cpp b/fuzzylite/src/factory/TermFactory.cpp index 10f3c15..e135704 100644 --- a/fuzzylite/src/factory/TermFactory.cpp +++ b/fuzzylite/src/factory/TermFactory.cpp @@ -1,32 +1,23 @@ /* - 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/factory/TermFactory.h" -#include "fl/Exception.h" -#include "fl/term/Term.h" #include "fl/term/Bell.h" +#include "fl/term/Binary.h" #include "fl/term/Concave.h" #include "fl/term/Constant.h" #include "fl/term/Cosine.h" @@ -52,6 +43,7 @@ namespace fl { TermFactory::TermFactory() : ConstructionFactory<Term*>("Term") { registerConstructor("", fl::null); registerConstructor(Bell().className(), &(Bell::constructor)); + registerConstructor(Binary().className(), &(Binary::constructor)); registerConstructor(Concave().className(), &(Concave::constructor)); registerConstructor(Constant().className(), &(Constant::constructor)); registerConstructor(Cosine().className(), &(Cosine::constructor)); @@ -73,9 +65,6 @@ namespace fl { registerConstructor(ZShape().className(), &(ZShape::constructor)); } - TermFactory::~TermFactory() { - - } - + TermFactory::~TermFactory() { } } diff --git a/fuzzylite/src/fuzzylite.cpp b/fuzzylite/src/fuzzylite.cpp index 06b635a..db17fec 100644 --- a/fuzzylite/src/fuzzylite.cpp +++ b/fuzzylite/src/fuzzylite.cpp @@ -1,73 +1,31 @@ /* - 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/fuzzylite.h" namespace fl { + int fuzzylite::_decimals = 3; + std::ios_base::fmtflags fuzzylite::_scalarFormat = std::ios_base::fixed; scalar fuzzylite::_macheps = 1e-6; - bool fuzzylite::_debug = false; + bool fuzzylite::_debugging = false; bool fuzzylite::_logging = true; - std::string fuzzylite::name() { - return "fuzzylite"; - } - - std::string fuzzylite::fullname() { - return name() + "-" + longVersion(); - } - - std::string fuzzylite::version() { - return FL_VERSION; - } - - std::string fuzzylite::longVersion() { - return FL_VERSION "b" FL_DATE; - } - - std::string fuzzylite::license() { - return "GNU Lesser General Public License v3.0"; - } - - std::string fuzzylite::author() { - return "Juan Rada-Vilela, Ph.D."; - } - - std::string fuzzylite::company() { - return "FuzzyLite Limited"; - } - - std::string fuzzylite::website() { - return "http://www.fuzzylite.com/"; - } - - std::string fuzzylite::date() { - return FL_DATE; - } - - std::string fuzzylite::platform() { + std::string platform() { #ifdef FL_UNIX return "Unix"; #elif defined FL_WINDOWS @@ -77,9 +35,9 @@ namespace fl { #endif } - std::string fuzzylite::floatingPoint() { + std::string floatingPoint() { scalar someScalar = 0; - (void) someScalar; + FL_IUNUSED(someScalar); std::string type; std::ostringstream ss; @@ -92,37 +50,4 @@ namespace fl { sizeof (someScalar) << " bytes"; return ss.str(); } - - void fuzzylite::setDebug(bool debug) { - _debug = debug; - } - - bool fuzzylite::debug() { - return _debug; - } - - void fuzzylite::setDecimals(int decimals) { - _decimals = decimals; - } - - int fuzzylite::decimals() { - return _decimals; - } - - void fuzzylite::setMachEps(scalar macheps) { - _macheps = macheps; - } - - scalar fuzzylite::macheps() { - return _macheps; - } - - void fuzzylite::setLogging(bool logging) { - _logging = logging; - } - - bool fuzzylite::logging() { - return _logging; - } - } diff --git a/fuzzylite/src/hedge/Any.cpp b/fuzzylite/src/hedge/Any.cpp index 72724f1..9d04262 100644 --- a/fuzzylite/src/hedge/Any.cpp +++ b/fuzzylite/src/hedge/Any.cpp @@ -1,43 +1,37 @@ /* - 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/hedge/Any.h" namespace fl { - Any::Any() { - } + Any::Any() { } - Any::~Any() { - } + Any::~Any() { } std::string Any::name() const { return "any"; } + Complexity Any::complexity() const { + return Complexity(); + } + scalar Any::hedge(scalar x) const { - (void) x; + FL_IUNUSED(x); return 1.0; } @@ -50,3 +44,4 @@ namespace fl { } } + diff --git a/fuzzylite/src/hedge/Extremely.cpp b/fuzzylite/src/hedge/Extremely.cpp index eabbb60..60d3abd 100644 --- a/fuzzylite/src/hedge/Extremely.cpp +++ b/fuzzylite/src/hedge/Extremely.cpp @@ -1,25 +1,17 @@ /* - 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/hedge/Extremely.h" @@ -32,10 +24,14 @@ namespace fl { return "extremely"; } + Complexity Extremely::complexity() const { + return Complexity().comparison(1).arithmetic(5); + } + scalar Extremely::hedge(scalar x) const { return Op::isLE(x, 0.5) ? 2.0 * x * x - : 1.0 - 2.0 * (1.0 - x) * (1.0 - x); + : (1.0 - 2.0 * (1.0 - x) * (1.0 - x)); } Extremely* Extremely::clone() const { @@ -46,5 +42,5 @@ namespace fl { return new Extremely; } - } + diff --git a/fuzzylite/src/hedge/HedgeFunction.cpp b/fuzzylite/src/hedge/HedgeFunction.cpp new file mode 100644 index 0000000..c31c72c --- /dev/null +++ b/fuzzylite/src/hedge/HedgeFunction.cpp @@ -0,0 +1,63 @@ +/* + 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 FuzzyLite License included with the software. + + 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/hedge/HedgeFunction.h" + +namespace fl { + + HedgeFunction::HedgeFunction(const std::string& formula) : Hedge() { + _function.variables["x"] = fl::nan; + if (not formula.empty()) { + _function.load(formula); + } + } + + std::string HedgeFunction::name() const { + return "HedgeFunction"; + } + + Complexity HedgeFunction::complexity() const { + if (_function.root()) + return _function.complexity().function(2 * std::log(scalar(_function.variables.size()))); + return _function.complexity(); + } + + scalar HedgeFunction::hedge(scalar x) const { + _function.variables["x"] = x; + return _function.membership(x); + } + + Function& HedgeFunction::function() { + return this->_function; + } + + void HedgeFunction::setFormula(const std::string& formula) { + _function.load(formula); + } + + std::string HedgeFunction::getFormula() const { + return _function.getFormula(); + } + + HedgeFunction* HedgeFunction::clone() const { + return new HedgeFunction(*this); + } + + Hedge* HedgeFunction::constructor() { + return new HedgeFunction; + } + +} diff --git a/fuzzylite/src/hedge/Not.cpp b/fuzzylite/src/hedge/Not.cpp index 531213f..defbabd 100644 --- a/fuzzylite/src/hedge/Not.cpp +++ b/fuzzylite/src/hedge/Not.cpp @@ -1,25 +1,17 @@ /* - 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/hedge/Not.h" @@ -30,6 +22,10 @@ namespace fl { return "not"; } + Complexity Not::complexity() const { + return Complexity().arithmetic(1); + } + scalar Not::hedge(scalar x) const { return 1.0 - x; } @@ -42,5 +38,4 @@ namespace fl { return new Not; } - } diff --git a/fuzzylite/src/hedge/Seldom.cpp b/fuzzylite/src/hedge/Seldom.cpp index d87b48a..092e873 100644 --- a/fuzzylite/src/hedge/Seldom.cpp +++ b/fuzzylite/src/hedge/Seldom.cpp @@ -1,25 +1,17 @@ /* - 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/hedge/Seldom.h" @@ -32,10 +24,14 @@ namespace fl { return "seldom"; } + Complexity Seldom::complexity() const { + return Complexity().comparison(1).function(1).arithmetic(3); + } + scalar Seldom::hedge(scalar x) const { return Op::isLE(x, 0.5) - ? std::sqrt(x / 2.0) - : 1.0 - std::sqrt((1.0 - x) / 2.0); + ? std::sqrt(0.5 * x) + : (1.0 - std::sqrt(0.5 * (1.0 - x))); } Seldom* Seldom::clone() const { @@ -46,5 +42,4 @@ namespace fl { return new Seldom; } - } diff --git a/fuzzylite/src/hedge/Somewhat.cpp b/fuzzylite/src/hedge/Somewhat.cpp index 16c371b..4d68c20 100644 --- a/fuzzylite/src/hedge/Somewhat.cpp +++ b/fuzzylite/src/hedge/Somewhat.cpp @@ -1,25 +1,17 @@ /* - 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/hedge/Somewhat.h" @@ -30,6 +22,10 @@ namespace fl { return "somewhat"; } + Complexity Somewhat::complexity() const { + return Complexity().function(1); + } + scalar Somewhat::hedge(scalar x) const { return std::sqrt(x); } diff --git a/fuzzylite/src/hedge/Very.cpp b/fuzzylite/src/hedge/Very.cpp index dc1fbec..5ac7a86 100644 --- a/fuzzylite/src/hedge/Very.cpp +++ b/fuzzylite/src/hedge/Very.cpp @@ -1,25 +1,17 @@ /* - 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/hedge/Very.h" @@ -30,6 +22,10 @@ namespace fl { return "very"; } + Complexity Very::complexity() const { + return Complexity().arithmetic(1); + } + scalar Very::hedge(scalar x) const { return x * x; } diff --git a/fuzzylite/src/imex/CppExporter.cpp b/fuzzylite/src/imex/CppExporter.cpp index 7b21087..8a77c0c 100644 --- a/fuzzylite/src/imex/CppExporter.cpp +++ b/fuzzylite/src/imex/CppExporter.cpp @@ -1,75 +1,73 @@ /* - 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/imex/CppExporter.h" #include "fl/Headers.h" -#include <algorithm> - namespace fl { - CppExporter::CppExporter(bool prefixNamespace) : Exporter(), - _prefixNamespace(prefixNamespace) { - } + CppExporter::CppExporter(bool prefixNamespace, bool usingVariableNames) : Exporter(), + _usingNamespace(prefixNamespace), _usingVariableNames(usingVariableNames) { } - CppExporter::~CppExporter() { - } + CppExporter::~CppExporter() { } std::string CppExporter::name() const { return "CppExporter"; } std::string CppExporter::fl(const std::string& clazz) const { - return _prefixNamespace ? "fl::" + clazz : clazz; + return _usingNamespace ? "fl::" + clazz : clazz; + } + + void CppExporter::setUsingNamespace(bool usingNamespace) { + this->_usingNamespace = usingNamespace; } - void CppExporter::setPrefixNamespace(bool prefixNamespace){ - this->_prefixNamespace = prefixNamespace; + bool CppExporter::isUsingNamespace() const { + return this->_usingNamespace; } - bool CppExporter::isPrefixNamespace() const{ - return this->_prefixNamespace; + void CppExporter::setUsingVariableNames(bool usingVariableNames) { + this->_usingVariableNames = usingVariableNames; + } + + bool CppExporter::isUsingVariableNames() const { + return this->_usingVariableNames; } std::string CppExporter::toString(const Engine* engine) const { std::ostringstream cpp; - if (not _prefixNamespace) cpp << "using namespace fl;\n\n"; + cpp << "//Code automatically generated with " << fuzzylite::library() << ".\n\n"; + if (not isUsingNamespace()) cpp << "using namespace fl;\n\n"; cpp << fl("Engine* ") << "engine = new " << fl("Engine;\n"); cpp << "engine->setName(\"" << engine->getName() << "\");\n"; + cpp << "engine->setDescription(\"" << engine->getDescription() << "\");\n"; cpp << "\n"; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { cpp << toString(engine->getInputVariable(i), engine) << "\n"; } - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { cpp << toString(engine->getOutputVariable(i), engine) << "\n"; } - for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) { + for (std::size_t i = 0; i < engine->numberOfRuleBlocks(); ++i) { cpp << toString(engine->getRuleBlock(i), engine) << "\n"; } @@ -77,21 +75,28 @@ namespace fl { } std::string CppExporter::toString(const InputVariable* inputVariable, const Engine* engine) const { - std::ostringstream ss; - std::string name = "inputVariable"; - if (engine->numberOfInputVariables() > 1) { - int index = std::distance(engine->inputVariables().begin(), - std::find(engine->inputVariables().begin(), - engine->inputVariables().end(), inputVariable)); - name += Op::str<int>(index + 1); + std::string name; + if (isUsingVariableNames()) { + name = Op::validName(inputVariable->getName()); + } else { + name = "inputVariable"; + if (engine->numberOfInputVariables() > 1) { + std::size_t index = std::distance(engine->inputVariables().begin(), + std::find(engine->inputVariables().begin(), + engine->inputVariables().end(), inputVariable)); + name += Op::str(index + 1); + } } + std::ostringstream ss; ss << fl("InputVariable* ") << name << " = new " << fl("InputVariable;\n"); - ss << name << "->setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setName(\"" << inputVariable->getName() << "\");\n"; + ss << name << "->setDescription(\"" << inputVariable->getDescription() << "\");\n"; + ss << name << "->setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setRange(" << toString(inputVariable->getMinimum()) << ", " << toString(inputVariable->getMaximum()) << ");\n"; - for (int t = 0; t < inputVariable->numberOfTerms(); ++t) { + ss << name << "->setLockValueInRange(" << (inputVariable->isLockValueInRange() ? "true" : "false") << ");\n"; + for (std::size_t t = 0; t < inputVariable->numberOfTerms(); ++t) { ss << name << "->addTerm(" << toString(inputVariable->getTerm(t)) << ");\n"; } ss << "engine->addInputVariable(" << name << ");\n"; @@ -99,58 +104,73 @@ namespace fl { } std::string CppExporter::toString(const OutputVariable* outputVariable, const Engine* engine) const { - std::ostringstream ss; - std::string name = "outputVariable"; - if (engine->numberOfOutputVariables() > 1) { - int index = std::distance(engine->outputVariables().begin(), - std::find(engine->outputVariables().begin(), - engine->outputVariables().end(), outputVariable)); - name += Op::str<int>(index + 1); + std::string name; + if (isUsingVariableNames()) { + name = Op::validName(outputVariable->getName()); + } else { + name = "outputVariable"; + if (engine->numberOfOutputVariables() > 1) { + std::size_t index = std::distance(engine->outputVariables().begin(), + std::find(engine->outputVariables().begin(), + engine->outputVariables().end(), outputVariable)); + name += Op::str(index + 1); + } } + std::ostringstream ss; ss << fl("OutputVariable* ") << name << " = new " << fl("OutputVariable;\n"); - ss << name << "->setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setName(\"" << outputVariable->getName() << "\");\n"; + ss << name << "->setDescription(\"" << outputVariable->getDescription() << "\");\n"; + ss << name << "->setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setRange(" << toString(outputVariable->getMinimum()) << ", " << toString(outputVariable->getMaximum()) << ");\n"; - ss << name << "->fuzzyOutput()->setAccumulation(" << - toString(outputVariable->fuzzyOutput()->getAccumulation()) << ");\n"; + ss << name << "->setLockValueInRange(" << + (outputVariable->isLockValueInRange() ? "true" : "false") << ");\n"; + ss << name << "->setAggregation(" << + toString(outputVariable->fuzzyOutput()->getAggregation()) << ");\n"; ss << name << "->setDefuzzifier(" << toString(outputVariable->getDefuzzifier()) << ");\n"; ss << name << "->setDefaultValue(" << toString(outputVariable->getDefaultValue()) << ");\n"; - ss << name << "->setLockPreviousOutputValue(" << - (outputVariable->isLockedPreviousOutputValue() ? "true" : "false") << ");\n"; - ss << name << "->setLockOutputValueInRange(" << - (outputVariable->isLockedOutputValueInRange() ? "true" : "false") << ");\n"; - for (int t = 0; t < outputVariable->numberOfTerms(); ++t) { + ss << name << "->setLockPreviousValue(" << + (outputVariable->isLockPreviousValue() ? "true" : "false") << ");\n"; + for (std::size_t t = 0; t < outputVariable->numberOfTerms(); ++t) { ss << name << "->addTerm(" << toString(outputVariable->getTerm(t)) << ");\n"; } ss << "engine->addOutputVariable(" << name << ");\n"; return ss.str(); } - //TODO: addRules using `new Rule` instead of `Rule::parse` in version 6.0 std::string CppExporter::toString(const RuleBlock* ruleBlock, const Engine* engine) const { - std::ostringstream ss; - std::string name = "ruleBlock"; - if (engine->numberOfRuleBlocks() > 1) { - int index = std::distance(engine->ruleBlocks().begin(), - std::find(engine->ruleBlocks().begin(), - engine->ruleBlocks().end(), ruleBlock)); - name += Op::str<int>(index + 1); + std::string name; + + if (isUsingVariableNames() and not ruleBlock->getName().empty()) { + name = Op::validName(ruleBlock->getName()); + } else { + name = "ruleBlock"; + if (engine->numberOfRuleBlocks() > 1) { + std::size_t index = std::distance(engine->ruleBlocks().begin(), + std::find(engine->ruleBlocks().begin(), + engine->ruleBlocks().end(), ruleBlock)); + name += Op::str(index + 1); + } } + + std::ostringstream ss; ss << fl("RuleBlock* ") << name << " = new " << fl("RuleBlock;\n"); - ss << name << "->setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setName(\"" << ruleBlock->getName() << "\");\n"; + ss << name << "->setDescription(\"" << ruleBlock->getDescription() << "\");\n"; + ss << name << "->setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n"; ss << name << "->setConjunction(" << toString(ruleBlock->getConjunction()) << ");\n"; ss << name << "->setDisjunction(" << toString(ruleBlock->getDisjunction()) << ");\n"; + ss << name << "->setImplication(" + << toString(ruleBlock->getImplication()) << ");\n"; ss << name << "->setActivation(" << toString(ruleBlock->getActivation()) << ");\n"; - for (int r = 0; r < ruleBlock->numberOfRules(); ++r) { - ss << name << "->addRule(" << "fl::Rule::parse(\"" << + for (std::size_t r = 0; r < ruleBlock->numberOfRules(); ++r) { + ss << name << "->addRule(" << fl("Rule") << "::parse(\"" << ruleBlock->getRule(r)->getText() << "\", engine));\n"; } ss << "engine->addRuleBlock(" << name << ");\n"; @@ -158,12 +178,12 @@ namespace fl { } std::string CppExporter::toString(scalar value) const { - if (fl::Op::isNaN(value)) + if (Op::isNaN(value)) return "fl::nan"; - if (fl::Op::isInf(value)){ - return (value > 0 ? "fl::inf" : "-fl::inf"); + if (Op::isInf(value)) { + return (value > 0 ? "fl::inf" : "-fl::inf"); } - return fl::Op::str(value); + return Op::str(value); } std::string CppExporter::toString(const Term* term) const { @@ -172,8 +192,8 @@ namespace fl { if (const Discrete * discrete = dynamic_cast<const Discrete*> (term)) { std::ostringstream ss; ss << fl(term->className()) << "::create(\"" << term->getName() << "\", " - << discrete->xy().size() * 2 << ", " - << fl::Op::join(Discrete::toVector(discrete->xy()), ", ") << ")"; + << (discrete->xy().size() * 2) << ", " + << Op::join(Discrete::toVector(discrete->xy()), ", ") << ")"; return ss.str(); } @@ -187,7 +207,7 @@ namespace fl { if (const Linear * linear = dynamic_cast<const Linear*> (term)) { std::ostringstream ss; ss << fl(term->className()) << "::create(\"" << term->getName() << "\", " - << "engine, " << fl::Op::join(linear->coefficients(), ", ") << ")"; + << "engine, " << Op::join(linear->coefficients(), ", ") << ")"; return ss.str(); } @@ -217,7 +237,7 @@ namespace fl { if (const IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<const IntegralDefuzzifier*> (defuzzifier)) { return "new " + fl(integralDefuzzifier->className()) + "(" - + fl::Op::str(integralDefuzzifier->getResolution()) + ")"; + + Op::str(integralDefuzzifier->getResolution()) + ")"; } if (const WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) { @@ -227,6 +247,19 @@ namespace fl { return "new " + fl(defuzzifier->className()); } + std::string CppExporter::toString(const Activation* activation) const { + if (not activation) return "fl::null"; + std::string parameters = Op::trim(activation->parameters()); + if (parameters.empty()) return "new " + fl(activation->className()); + + std::vector<std::string> values = Op::split(parameters, " "); + for (std::size_t i = 0; i < values.size(); ++i) { + std::string parameter = values.at(i); + values.at(i) = (Op::isNumeric(parameter) ? parameter : ("\"" + parameter + "\"")); + } + return "new " + fl(activation->className()) + "(" + Op::join(values, ", ") + ")"; + } + CppExporter* CppExporter::clone() const { return new CppExporter(*this); } diff --git a/fuzzylite/src/imex/Exporter.cpp b/fuzzylite/src/imex/Exporter.cpp index 5b0fe1b..1aaf24e 100644 --- a/fuzzylite/src/imex/Exporter.cpp +++ b/fuzzylite/src/imex/Exporter.cpp @@ -1,25 +1,17 @@ /* - 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/imex/Exporter.h" @@ -29,18 +21,14 @@ namespace fl { - Exporter::Exporter() { - - } - - Exporter::~Exporter() { + Exporter::Exporter() { } - } + Exporter::~Exporter() { } void Exporter::toFile(const std::string& path, const Engine* engine) const { std::ofstream writer(path.c_str()); if (not writer.is_open()) { - throw fl::Exception("[file error] file <" + path + "> could not be created", FL_AT); + throw Exception("[file error] file <" + path + "> could not be created", FL_AT); } writer << toString(engine) << std::endl; writer.close(); diff --git a/fuzzylite/src/imex/FclExporter.cpp b/fuzzylite/src/imex/FclExporter.cpp index cf8ffb0..98f1177 100644 --- a/fuzzylite/src/imex/FclExporter.cpp +++ b/fuzzylite/src/imex/FclExporter.cpp @@ -1,39 +1,28 @@ /* - 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/imex/FclExporter.h" #include "fl/Headers.h" -#include <sstream> - namespace fl { - FclExporter::FclExporter(const std::string& indent) : Exporter(), _indent(indent) { - } + FclExporter::FclExporter(const std::string& indent) : Exporter(), _indent(indent) { } - FclExporter::~FclExporter() { - } + FclExporter::~FclExporter() { } void FclExporter::setIndent(const std::string& indent) { this->_indent = indent; @@ -49,31 +38,32 @@ namespace fl { std::string FclExporter::toString(const Engine* engine) const { std::ostringstream fcl; + fcl << "//Code automatically generated with " << fuzzylite::library() << ".\n\n"; fcl << "FUNCTION_BLOCK " << engine->getName() << "\n\n"; fcl << "VAR_INPUT\n"; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { fcl << _indent << Op::validName(engine->getInputVariable(i)->getName()) << ": REAL;\n"; } fcl << "END_VAR\n\n"; fcl << "VAR_OUTPUT\n"; - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { fcl << _indent << Op::validName(engine->getOutputVariable(i)->getName()) << ": REAL;\n"; } fcl << "END_VAR\n\n"; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { InputVariable* inputVariable = engine->getInputVariable(i); fcl << toString(inputVariable) << "\n"; } - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { OutputVariable* outputVariable = engine->getOutputVariable(i); fcl << toString(outputVariable) << "\n"; } - for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) { + for (std::size_t i = 0; i < engine->numberOfRuleBlocks(); ++i) { RuleBlock* ruleblock = engine->getRuleBlock(i); fcl << toString(ruleblock) << "\n"; } @@ -85,15 +75,11 @@ namespace fl { std::string FclExporter::toString(const InputVariable* inputVariable) const { std::ostringstream fcl; fcl << "FUZZIFY " << Op::validName(inputVariable->getName()) << "\n"; - if (not inputVariable->isEnabled()) { - fcl << _indent << "ENABLED : " << - (inputVariable->isEnabled() ? "TRUE" : "FALSE") << ";\n"; - } - fcl << _indent << "RANGE := (" << fl::Op::join(2, " .. ", + fcl << _indent << "RANGE := (" << Op::join(2, " .. ", inputVariable->getMinimum(), inputVariable->getMaximum()) << ");\n"; - for (int t = 0; t < inputVariable->numberOfTerms(); ++t) { + for (std::size_t t = 0; t < inputVariable->numberOfTerms(); ++t) { Term* term = inputVariable->getTerm(t); fcl << _indent << "TERM " << Op::validName(term->getName()) << " := " << toString(term) << ";\n"; @@ -105,15 +91,11 @@ namespace fl { std::string FclExporter::toString(const OutputVariable* outputVariable) const { std::ostringstream fcl; fcl << "DEFUZZIFY " << Op::validName(outputVariable->getName()) << "\n"; - if (not outputVariable->isEnabled()) { - fcl << _indent << "ENABLED : " << - (outputVariable->isEnabled() ? "TRUE" : "FALSE") << ";\n"; - } - fcl << _indent << "RANGE := (" << fl::Op::join(2, " .. ", + fcl << _indent << "RANGE := (" << Op::join(2, " .. ", outputVariable->getMinimum(), outputVariable->getMaximum()) << ");\n"; - for (int t = 0; t < outputVariable->numberOfTerms(); ++t) { + for (std::size_t t = 0; t < outputVariable->numberOfTerms(); ++t) { Term* term = outputVariable->getTerm(t); fcl << _indent << "TERM " << Op::validName(term->getName()) << " := " << toString(term) << ";\n"; @@ -121,19 +103,15 @@ namespace fl { if (outputVariable->getDefuzzifier()) { fcl << _indent << "METHOD : " << toString(outputVariable->getDefuzzifier()) << ";\n"; } - if (outputVariable->fuzzyOutput()->getAccumulation()) - fcl << _indent << "ACCU : " << toString(outputVariable->fuzzyOutput()->getAccumulation()) << ";\n"; + if (outputVariable->fuzzyOutput()->getAggregation()) + fcl << _indent << "ACCU : " << toString(outputVariable->fuzzyOutput()->getAggregation()) << ";\n"; - fcl << _indent << "DEFAULT := " << fl::Op::str(outputVariable->getDefaultValue()); - if (outputVariable->isLockedPreviousOutputValue()) { + fcl << _indent << "DEFAULT := " << Op::str(outputVariable->getDefaultValue()); + if (outputVariable->isLockPreviousValue()) { fcl << " | NC"; } fcl << ";\n"; - if (outputVariable->isLockedOutputValueInRange()) { - fcl << _indent << "LOCK : RANGE;\n"; - } - fcl << "END_DEFUZZIFY\n"; return fcl.str(); } @@ -141,28 +119,24 @@ namespace fl { std::string FclExporter::toString(const RuleBlock* ruleBlock) const { std::ostringstream fcl; fcl << "RULEBLOCK " << ruleBlock->getName() << "\n"; - if (not ruleBlock->isEnabled()) { - fcl << _indent << "ENABLED : " << - (ruleBlock->isEnabled() ? "TRUE" : "FALSE") << ";\n"; - } if (ruleBlock->getConjunction()) fcl << _indent << "AND : " << toString(ruleBlock->getConjunction()) << ";\n"; if (ruleBlock->getDisjunction()) fcl << _indent << "OR : " << toString(ruleBlock->getDisjunction()) << ";\n"; - if (ruleBlock->getActivation()) - fcl << _indent << "ACT : " << toString(ruleBlock->getActivation()) << ";\n"; + if (ruleBlock->getImplication()) + fcl << _indent << "ACT : " << toString(ruleBlock->getImplication()) << ";\n"; - for (int r = 0; r < ruleBlock->numberOfRules(); ++r) { + for (std::size_t r = 0; r < ruleBlock->numberOfRules(); ++r) { fcl << _indent << "RULE " << (r + 1) << " : " << ruleBlock->getRule(r)->getText() << "\n"; } fcl << "END_RULEBLOCK\n"; return fcl.str(); } - - std::string FclExporter::toString(const Norm* norm) const{ + + std::string FclExporter::toString(const Norm* norm) const { if (not norm) return "NONE"; - + std::string name = norm->className(); //TNorms if (name == Minimum().className()) return "MIN"; @@ -172,7 +146,7 @@ namespace fl { if (name == EinsteinProduct().className()) return "EPROD"; if (name == HamacherProduct().className()) return "HPROD"; if (name == NilpotentMinimum().className()) return "NMIN"; - + //SNorms if (name == Maximum().className()) return "MAX"; if (name == AlgebraicSum().className()) return "ASUM"; @@ -182,37 +156,8 @@ namespace fl { if (name == EinsteinSum().className()) return "ESUM"; if (name == HamacherSum().className()) return "HSUM"; if (name == NilpotentMaximum().className()) return "NMAX"; - - return norm->className(); - } - - //TODO: Delete in v6.0 - std::string FclExporter::toString(const TNorm* tnorm) const { - if (not tnorm) return "NONE"; - std::string name = tnorm->className(); - if (name == Minimum().className()) return "MIN"; - if (name == AlgebraicProduct().className()) return "PROD"; - if (name == BoundedDifference().className()) return "BDIF"; - if (name == DrasticProduct().className()) return "DPROD"; - if (name == EinsteinProduct().className()) return "EPROD"; - if (name == HamacherProduct().className()) return "HPROD"; - if (name == NilpotentMinimum().className()) return "NMIN"; - return tnorm->className(); - } - //TODO: Delete in v6.0 - std::string FclExporter::toString(const SNorm* snorm) const { - if (not snorm) return "NONE"; - std::string name = snorm->className(); - if (name == Maximum().className()) return "MAX"; - if (name == AlgebraicSum().className()) return "ASUM"; - if (name == NormalizedSum().className()) return "NSUM"; - if (name == BoundedSum().className()) return "BSUM"; - if (name == DrasticSum().className()) return "DSUM"; - if (name == EinsteinSum().className()) return "ESUM"; - if (name == HamacherSum().className()) return "HSUM"; - if (name == NilpotentMaximum().className()) return "NMAX"; - return snorm->className(); + return norm->className(); } std::string FclExporter::toString(const Defuzzifier* defuzzifier) const { @@ -232,15 +177,15 @@ namespace fl { if (const Discrete * discrete = dynamic_cast<const Discrete*> (term)) { std::ostringstream ss; for (std::size_t i = 0; i < discrete->xy().size(); ++i) { - ss << "(" << fl::Op::str(discrete->xy(i).first) << ", " - << fl::Op::str(discrete->xy(i).second) << ")"; + ss << "(" << Op::str(discrete->xy(i).first) << ", " + << Op::str(discrete->xy(i).second) << ")"; if (i + 1 < discrete->xy().size()) ss << " "; } return ss.str(); } if (const Constant * constant = dynamic_cast<const Constant*> (term)) { - return fl::Op::str(constant->getValue()); + return Op::str(constant->getValue()); } std::ostringstream ss; @@ -252,5 +197,4 @@ namespace fl { return new FclExporter(*this); } - } diff --git a/fuzzylite/src/imex/FclImporter.cpp b/fuzzylite/src/imex/FclImporter.cpp index a56a9e1..06879cd 100644 --- a/fuzzylite/src/imex/FclImporter.cpp +++ b/fuzzylite/src/imex/FclImporter.cpp @@ -1,41 +1,28 @@ /* - 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/imex/FclImporter.h" #include "fl/Headers.h" -#include <iostream> -#include <sstream> - namespace fl { - FclImporter::FclImporter() : Importer() { - } + FclImporter::FclImporter() : Importer() { } - FclImporter::~FclImporter() { - } + FclImporter::~FclImporter() { } std::string FclImporter::name() const { return "FclImporter"; @@ -57,24 +44,16 @@ namespace fl { std::istringstream fclReader(fcl); std::string line; - int lineNumber = 0; + std::size_t lineNumber = 0; while (std::getline(fclReader, line)) { ++lineNumber; - std::vector<std::string> comments; - comments = Op::split(line, "//"); - if (comments.size() > 1) { - line = comments.front(); - } - comments = Op::split(line, "#"); - if (comments.size() > 1) { - line = comments.front(); - } - line = Op::trim(line); - if (line.empty() or line.at(0) == '%' or line.at(0) == '#' - or (line.substr(0, 2) == "//")) { + line = Op::split(line, "//", false).front(); + line = Op::split(line, "#", false).front(); + line = Op::trim(Op::findReplace(line, ";", "")); + if (line.empty() or line.at(0) == '%') { continue; } - line = fl::Op::findReplace(line, ";", ""); + std::istringstream tokenizer(line); std::string firstToken; tokenizer >> firstToken; @@ -102,7 +81,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] unknown block definition <" << firstToken << "> " << " in line " << lineNumber << ": " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } currentTag = tagFinder->first; closingTag = tagFinder->second; @@ -122,7 +101,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected <" << closingTag << "> before <" << firstToken << "> in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } else { block << line << "\n"; } @@ -138,7 +117,7 @@ namespace fl { } else { ex << "expected <" << closingTag << ">, but not found"; } - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } return engine.release(); } @@ -155,7 +134,7 @@ namespace fl { } else { std::ostringstream ex; ex << "[syntax error] unexpected tag <" << tag << "> for block:\n" << block; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } @@ -169,9 +148,9 @@ namespace fl { if (token.size() != 2) { std::ostringstream ex; ex << "[syntax error] expected property of type (key : value) in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } - std::string name = fl::Op::validName(token.at(0)); + std::string name = Op::validName(token.at(0)); if (tag == "VAR_INPUT") engine->addInputVariable(new InputVariable(name)); else if (tag == "VAR_OUTPUT") @@ -179,7 +158,7 @@ namespace fl { else { std::ostringstream ex; ex << "[syntax error] unexpected tag <" << tag << "> in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } } @@ -192,17 +171,17 @@ namespace fl { std::string name; std::size_t index = line.find_first_of(' '); if (index != std::string::npos) { - name = fl::Op::validName(line.substr(index + 1)); + name = Op::validName(line.substr(index + 1)); } else { std::ostringstream ex; ex << "[syntax error] expected name of input variable in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if (not engine->hasInputVariable(name)) { std::ostringstream ex; ex << "[syntax error] engine does not contain " "input variable <" << name << "> from line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } InputVariable* inputVariable = engine->getInputVariable(name); @@ -219,9 +198,9 @@ namespace fl { inputVariable->setEnabled(parseEnabled(line)); } else if (firstToken == "TERM") { inputVariable->addTerm(parseTerm(line, engine)); - } else throw fl::Exception("[syntax error] unexpected token " + } else throw Exception("[syntax error] unexpected token " "<" + firstToken + ">" + line, FL_AT); - } catch (fl::Exception& ex) { + } catch (Exception& ex) { ex.append("At line: <" + line + ">"); throw; } @@ -237,23 +216,23 @@ namespace fl { std::string name; std::size_t index = line.find_first_of(' '); if (index != std::string::npos) { - name = fl::Op::validName(line.substr(index + 1)); + name = Op::validName(line.substr(index + 1)); } else { std::ostringstream ex; ex << "[syntax error] expected an output variable name in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } if (not engine->hasOutputVariable(name)) { std::ostringstream ex; ex << "[syntax error] output variable <" << name << "> not registered in engine. " << "Line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } OutputVariable* outputVariable = engine->getOutputVariable(name); while (std::getline(blockReader, line)) { - line = fl::Op::trim(line); + line = Op::trim(line); std::istringstream tokenizer(line); std::string firstToken; tokenizer >> firstToken; @@ -262,27 +241,27 @@ namespace fl { } else if (firstToken == "METHOD") { outputVariable->setDefuzzifier(parseDefuzzifier(line)); } else if (firstToken == "ACCU") { - outputVariable->fuzzyOutput()->setAccumulation(parseSNorm(line)); + outputVariable->fuzzyOutput()->setAggregation(parseSNorm(line)); } else if (firstToken == "DEFAULT") { std::pair<scalar, bool> defaultAndLock = parseDefaultValue(line); outputVariable->setDefaultValue(defaultAndLock.first); - outputVariable->setLockPreviousOutputValue(defaultAndLock.second or - outputVariable->isLockedPreviousOutputValue()); + outputVariable->setLockPreviousValue(defaultAndLock.second or + outputVariable->isLockPreviousValue()); } else if (firstToken == "RANGE") { std::pair<scalar, scalar> minmax = parseRange(line); outputVariable->setMinimum(minmax.first); outputVariable->setMaximum(minmax.second); } else if (firstToken == "LOCK") { std::pair<bool, bool> output_range = parseLocks(line); - outputVariable->setLockPreviousOutputValue(output_range.first); - outputVariable->setLockOutputValueInRange(output_range.second); + outputVariable->setLockPreviousValue(output_range.first); + outputVariable->setLockValueInRange(output_range.second); } else if (firstToken == "ENABLED") { outputVariable->setEnabled(parseEnabled(line)); } else { std::ostringstream ex; ex << "[syntax error] unexpected token <" << firstToken << "> in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } @@ -295,8 +274,9 @@ namespace fl { std::string name; std::getline(blockReader, line); std::size_t index = line.find_last_of(' '); - if (index != std::string::npos) name = line.substr(index); + if (index != std::string::npos) name = line.substr(index + 1); RuleBlock * ruleblock = new RuleBlock(name); + ruleblock->setActivation(new General); engine->addRuleBlock(ruleblock); while (std::getline(blockReader, line)) { @@ -306,14 +286,14 @@ namespace fl { } else if (firstToken == "OR") { ruleblock->setDisjunction(parseSNorm(line)); } else if (firstToken == "ACT") { - ruleblock->setActivation(parseTNorm(line)); + ruleblock->setImplication(parseTNorm(line)); } else if (firstToken == "ENABLED") { ruleblock->setEnabled(parseEnabled(line)); } else if (firstToken == "RULE") { std::size_t ruleStart = line.find_first_of(':'); if (ruleStart == std::string::npos) ruleStart = 4; // "RULE".size() std::string ruleText = line.substr(ruleStart + 1); - ruleText = fl::Op::trim(ruleText); + ruleText = Op::trim(ruleText); Rule* rule = new Rule(ruleText); try { rule->load(engine); @@ -325,7 +305,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] keyword <" << firstToken << "> not recognized in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } } @@ -336,7 +316,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key : value) in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::string name = Op::trim(token.at(1)); std::string className = name; @@ -358,7 +338,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key : value) in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::string name = Op::trim(token.at(1)); std::string className = name; @@ -412,7 +392,7 @@ namespace fl { continue; } if (state == S_TERMCLASS) { - if (fl::Op::isNumeric(token)) { + if (Op::isNumeric(token)) { termClass = Constant().className(); parameters.push_back(token); } else if (token == "(") { @@ -429,16 +409,16 @@ namespace fl { continue; } if (token == ";") break; - parameters.push_back(fl::Op::trim(token)); + parameters.push_back(Op::trim(token)); } } if (state <= S_TERMCLASS) - throw fl::Exception("[syntax error] malformed term in line: " + line, FL_AT); + throw Exception("[syntax error] malformed term in line: " + line, FL_AT); FL_unique_ptr<Term> term; term.reset(FactoryManager::instance()->term()->constructObject(termClass)); - Term::updateReference(term.get(), engine); - term->setName(fl::Op::validName(name)); + term->updateReference(engine); + term->setName(Op::validName(name)); std::string separator; if (not dynamic_cast<Function*> (term.get())) { separator = " "; @@ -453,10 +433,10 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key : value) in " << "line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } - std::string name = fl::Op::trim(token.at(1)); + std::string name = Op::trim(token.at(1)); std::string className = name; if (name == "NONE") className = ""; else if (name == "COG") className = Centroid().className(); @@ -476,7 +456,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key := value) in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::vector<std::string> values = Op::split(token.at(1), "|"); @@ -485,24 +465,24 @@ namespace fl { std::string nc; if (values.size() == 2) nc = values.back(); - defaultValue = fl::Op::trim(defaultValue); - nc = fl::Op::trim(nc); + defaultValue = Op::trim(defaultValue); + nc = Op::trim(nc); scalar value; try { - value = fl::Op::toScalar(defaultValue); + value = Op::toScalar(defaultValue); } catch (...) { std::ostringstream ex; ex << "[syntax error] expected numeric value, " << "but found <" << defaultValue << "> in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } bool lockPreviousOutput = (nc == "NC"); if (not (lockPreviousOutput or nc.empty())) { - throw fl::Exception("[syntax error] expected keyword <NC>, " + throw Exception("[syntax error] expected keyword <NC>, " "but found <" + nc + "> in line: " + line, FL_AT); } @@ -515,7 +495,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key := value) in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::string rangeToken = token.at(1); @@ -532,7 +512,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type 'start .. end', " << "but found <" << range.str() << "> in line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } scalar minimum, maximum; int index; @@ -540,11 +520,11 @@ namespace fl { minimum = Op::toScalar(token.at(index = 0)); maximum = Op::toScalar(token.at(index = 1)); } catch (std::exception& ex) { - (void) ex; + FL_IUNUSED(ex); std::ostringstream ss; ss << "[syntax error] expected numeric value, but found <" << token.at(index) << "> in " << "line: " << line; - throw fl::Exception(ss.str(), FL_AT); + throw Exception(ss.str(), FL_AT); } return std::pair<scalar, scalar>(minimum, maximum); } @@ -552,32 +532,32 @@ namespace fl { std::pair<bool, bool> FclImporter::parseLocks(const std::string& line) const { std::size_t index = line.find_first_of(":"); if (index == std::string::npos) { - throw fl::Exception("[syntax error] expected property of type " + throw Exception("[syntax error] expected property of type " "'key : value' in line: " + line, FL_AT); } bool output, range; std::string value = line.substr(index + 1); - std::vector<std::string> flags = fl::Op::split(value, "|"); + std::vector<std::string> flags = Op::split(value, "|"); if (flags.size() == 1) { - std::string flag = fl::Op::trim(flags.front()); + std::string flag = Op::trim(flags.front()); output = (flag == "PREVIOUS"); range = (flag == "RANGE"); if (not (output or range)) { - throw fl::Exception("[syntax error] expected locking flags " + throw Exception("[syntax error] expected locking flags " "<PREVIOUS|RANGE>, but found <" + flag + "> in line: " + line, FL_AT); } } else if (flags.size() == 2) { - std::string flagA = fl::Op::trim(flags.front()); - std::string flagB = fl::Op::trim(flags.back()); + std::string flagA = Op::trim(flags.front()); + std::string flagB = Op::trim(flags.back()); output = (flagA == "PREVIOUS" or flagB == "PREVIOUS"); range = (flagA == "RANGE" or flagB == "RANGE"); if (not (output and range)) { - throw fl::Exception("[syntax error] expected locking flags " + throw Exception("[syntax error] expected locking flags " "<PREVIOUS|RANGE>, but found " "<" + flags.front() + "|" + flags.back() + "> in line: " + line, FL_AT); } } else { - throw fl::Exception("[syntax error] expected locking flags " + throw Exception("[syntax error] expected locking flags " "<PREVIOUS|RANGE>, but found " "<" + value + "> in line: " + line, FL_AT); } @@ -590,13 +570,13 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected property of type (key : value) in " << "line: " << line; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } - std::string boolean = fl::Op::trim(tokens.at(1)); + std::string boolean = Op::trim(tokens.at(1)); if (boolean == "TRUE") return true; if (boolean == "FALSE") return false; - throw fl::Exception("[syntax error] expected boolean <TRUE|FALSE>, but found <" + line + ">", FL_AT); + throw Exception("[syntax error] expected boolean <TRUE|FALSE>, but found <" + line + ">", FL_AT); } FclImporter* FclImporter::clone() const { diff --git a/fuzzylite/src/imex/FisExporter.cpp b/fuzzylite/src/imex/FisExporter.cpp index 0934b33..2daac9a 100644 --- a/fuzzylite/src/imex/FisExporter.cpp +++ b/fuzzylite/src/imex/FisExporter.cpp @@ -1,25 +1,17 @@ /* - 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/imex/FisExporter.h" @@ -30,11 +22,9 @@ namespace fl { - FisExporter::FisExporter() : Exporter() { - } + FisExporter::FisExporter() : Exporter() { } - FisExporter::~FisExporter() { - } + FisExporter::~FisExporter() { } std::string FisExporter::name() const { return "FisExporter"; @@ -52,9 +42,10 @@ namespace fl { return fis.str(); } - //TODO: deal with multiple ruleblocks, merge them into one. + std::string FisExporter::exportSystem(const Engine* engine) const { std::ostringstream fis; + fis << "#Code automatically generated with " << fuzzylite::library() << ".\n\n"; fis << "[System]\n"; fis << "Name='" << engine->getName() << "'\n"; std::string type; @@ -66,57 +57,54 @@ namespace fl { type = "tsukamoto"; } else if (engine->type() == Engine::InverseTsukamoto) { type = "inverse tsukamoto"; - }else if (engine->type() == Engine::Hybrid){ + } else if (engine->type() == Engine::Hybrid) { type = "hybrid"; } else { type = "unknown"; } fis << "Type='" << type << "'\n"; - // fis << "Version=" << FL_VERSION << "\n"; + fis << "Version=" << fuzzylite::version() << "\n"; fis << "NumInputs=" << engine->numberOfInputVariables() << "\n"; fis << "NumOutputs=" << engine->numberOfOutputVariables() << "\n"; - - int numberOfRules = 0; + + std::size_t numberOfRules = 0; const TNorm* conjunction = fl::null; const SNorm* disjunction = fl::null; - const TNorm* activation = fl::null; - for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) { + const TNorm* implication = fl::null; + for (std::size_t i = 0; i < engine->numberOfRuleBlocks(); ++i) { RuleBlock* rb = engine->getRuleBlock(i); numberOfRules += rb->numberOfRules(); if (not conjunction) conjunction = rb->getConjunction(); if (not disjunction) disjunction = rb->getDisjunction(); - if (not activation) activation = rb->getActivation(); + if (not implication) implication = rb->getImplication(); } fis << "NumRules=" << numberOfRules << "\n"; - fis << "AndMethod='" << toString(conjunction) << "'\n"; - fis << "OrMethod='" << toString(disjunction) << "'\n"; - fis << "ImpMethod='" << toString(activation) << "'\n"; + fis << "AndMethod='" << (conjunction ? toString(conjunction) : "min") << "'\n"; + fis << "OrMethod='" << (disjunction ? toString(disjunction) : "max") << "'\n"; + fis << "ImpMethod='" << (implication ? toString(implication) : "min") << "'\n"; - const SNorm* accumulation = fl::null; + const SNorm* aggregation = fl::null; Defuzzifier* defuzzifier = fl::null; - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { OutputVariable* outputVariable = engine->getOutputVariable(i); - if (not accumulation) accumulation = outputVariable->fuzzyOutput()->getAccumulation(); + if (not aggregation) aggregation = outputVariable->fuzzyOutput()->getAggregation(); if (not defuzzifier) defuzzifier = outputVariable->getDefuzzifier(); } - fis << "AggMethod='" << toString(accumulation) << "'\n"; + fis << "AggMethod='" << (aggregation ? toString(aggregation) : "max") << "'\n"; fis << "DefuzzMethod='" << toString(defuzzifier) << "'\n"; return fis.str(); } std::string FisExporter::exportInputs(const Engine* engine) const { std::ostringstream fis; - for (int ixVar = 0; ixVar < engine->numberOfInputVariables(); ++ixVar) { + for (std::size_t ixVar = 0; ixVar < engine->numberOfInputVariables(); ++ixVar) { InputVariable* var = engine->getInputVariable(ixVar); fis << "[Input" << (ixVar + 1) << "]\n"; - if (not var->isEnabled()) { - fis << "Enabled=" << var->isEnabled() << "\n"; - } fis << "Name='" << Op::validName(var->getName()) << "'\n"; - fis << "Range=[" << fl::Op::join(2, " ", var->getMinimum(), var->getMaximum()) << "]\n"; + fis << "Range=[" << Op::join(2, " ", var->getMinimum(), var->getMaximum()) << "]\n"; fis << "NumMFs=" << var->numberOfTerms() << "\n"; - for (int ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) { + for (std::size_t ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) { fis << "MF" << (ixTerm + 1) << "='" << Op::validName(var->getTerm(ixTerm)->getName()) << "':" << toString(var->getTerm(ixTerm)) << "\n"; } @@ -127,25 +115,13 @@ namespace fl { std::string FisExporter::exportOutputs(const Engine* engine) const { std::ostringstream fis; - for (int ixVar = 0; ixVar < engine->numberOfOutputVariables(); ++ixVar) { + for (std::size_t ixVar = 0; ixVar < engine->numberOfOutputVariables(); ++ixVar) { OutputVariable* var = engine->getOutputVariable(ixVar); fis << "[Output" << (ixVar + 1) << "]\n"; - if (not var->isEnabled()) { - fis << "Enabled=" << var->isEnabled() << "\n"; - } fis << "Name='" << Op::validName(var->getName()) << "'\n"; - fis << "Range=[" << fl::Op::join(2, " ", var->getMinimum(), var->getMaximum()) << "]\n"; - if (not fl::Op::isNaN(var->getDefaultValue())) { - fis << "Default=" << fl::Op::str(var->getDefaultValue()) << "\n"; - } - if (var->isLockedPreviousOutputValue()) { - fis << "LockPrevious=" << var->isLockedPreviousOutputValue() << "\n"; - } - if (var->isLockedOutputValueInRange()) { - fis << "LockRange=" << var->isLockedOutputValueInRange() << "\n"; - } + fis << "Range=[" << Op::join(2, " ", var->getMinimum(), var->getMaximum()) << "]\n"; fis << "NumMFs=" << var->numberOfTerms() << "\n"; - for (int ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) { + for (std::size_t ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) { fis << "MF" << (ixTerm + 1) << "='" << Op::validName(var->getTerm(ixTerm)->getName()) << "':" << toString(var->getTerm(ixTerm)) << "\n"; } @@ -157,10 +133,10 @@ namespace fl { std::string FisExporter::exportRules(const Engine* engine) const { std::ostringstream fis; fis << "[Rules]\n"; - for (int ixRuleBlock = 0; ixRuleBlock < engine->numberOfRuleBlocks(); ++ixRuleBlock) { + for (std::size_t ixRuleBlock = 0; ixRuleBlock < engine->numberOfRuleBlocks(); ++ixRuleBlock) { RuleBlock* rb = engine->getRuleBlock(ixRuleBlock); if (engine->numberOfRuleBlocks() > 1) fis << "# RuleBlock " << rb->getName() << "\n"; - for (int ixRule = 0; ixRule < rb->numberOfRules(); ++ixRule) { + for (std::size_t ixRule = 0; ixRule < rb->numberOfRules(); ++ixRule) { Rule* rule = rb->getRule(ixRule); if (rule->isLoaded()) { fis << exportRule(rule, engine) << "\n"; @@ -198,15 +174,15 @@ namespace fl { } } if (not equalOperators) { - throw fl::Exception("[exporter error] " + throw Exception("[exporter error] " "fis files do not support rules with different connectors " "(i.e. ['and', 'or']). All connectors within a rule must be the same", FL_AT); } std::ostringstream fis; std::vector<Variable*> inputVariables, outputVariables; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) inputVariables.push_back(engine->getInputVariable(i)); - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) outputVariables.push_back(engine->getOutputVariable(i)); fis << translate(propositions, inputVariables) << ", "; @@ -226,14 +202,14 @@ namespace fl { std::ostringstream ss; for (std::size_t ixVariable = 0; ixVariable < variables.size(); ++ixVariable) { Variable* variable = variables.at(ixVariable); - int termIndexPlusOne = 0; + std::size_t termIndexPlusOne = 0; scalar plusHedge = 0; int negated = 1; for (std::size_t ixProposition = 0; ixProposition < propositions.size(); ++ixProposition) { Proposition* proposition = propositions.at(ixProposition); if (proposition->variable != variable) continue; - for (int termIndex = 0; termIndex < variable->numberOfTerms(); ++termIndex) { + for (std::size_t termIndex = 0; termIndex < variable->numberOfTerms(); ++termIndex) { if (variable->getTerm(termIndex) == proposition->term) { termIndexPlusOne = termIndex + 1; break; @@ -259,8 +235,8 @@ namespace fl { break; } if (negated < 0) ss << "-"; - if (not fl::Op::isNaN(plusHedge)) { - ss << fl::Op::str(termIndexPlusOne + plusHedge); + if (not Op::isNaN(plusHedge)) { + ss << Op::str(termIndexPlusOne + plusHedge); } else { ss << termIndexPlusOne << ".?"; // Unreconized hedge combination } @@ -268,29 +244,6 @@ namespace fl { } return ss.str(); } - - std::string FisExporter::toString(const Norm * norm) const { - if (not norm) return ""; - //TNorm - if (norm->className() == Minimum().className()) return "min"; - if (norm->className() == AlgebraicProduct().className()) return "prod"; - if (norm->className() == BoundedDifference().className()) return "bounded_difference"; - if (norm->className() == DrasticProduct().className()) return "drastic_product"; - if (norm->className() == EinsteinProduct().className()) return "einstein_product"; - if (norm->className() == HamacherProduct().className()) return "hamacher_product"; - if (norm->className() == NilpotentMinimum().className()) return "nilpotent_minimum"; - //SNorm - if (norm->className() == Maximum().className()) return "max"; - if (norm->className() == AlgebraicSum().className()) return "sum"; - if (norm->className() == BoundedSum().className()) return "bounded_sum"; - if (norm->className() == NormalizedSum().className()) return "normalized_sum"; - if (norm->className() == DrasticSum().className()) return "drastic_sum"; - if (norm->className() == EinsteinSum().className()) return "einstein_sum"; - if (norm->className() == HamacherSum().className()) return "hamacher_sum"; - if (norm->className() == NilpotentMaximum().className()) return "nilpotent_maximum"; - - return norm->className(); - } std::string FisExporter::toString(const TNorm * tnorm) const { if (not tnorm) return ""; @@ -304,16 +257,17 @@ namespace fl { return tnorm->className(); } - std::string FisExporter::toString(const SNorm * snorm) const { + std::string FisExporter::toString(const SNorm* snorm) const { if (not snorm) return ""; if (snorm->className() == Maximum().className()) return "max"; - if (snorm->className() == AlgebraicSum().className()) return "sum"; + if (snorm->className() == AlgebraicSum().className()) return "probor"; if (snorm->className() == BoundedSum().className()) return "bounded_sum"; if (snorm->className() == NormalizedSum().className()) return "normalized_sum"; if (snorm->className() == DrasticSum().className()) return "drastic_sum"; if (snorm->className() == EinsteinSum().className()) return "einstein_sum"; if (snorm->className() == HamacherSum().className()) return "hamacher_sum"; if (snorm->className() == NilpotentMaximum().className()) return "nilpotent_maximum"; + if (snorm->className() == UnboundedSum().className()) return "sum"; return snorm->className(); } @@ -332,30 +286,36 @@ namespace fl { std::string FisExporter::toString(const Term * term) const { std::ostringstream ss; if (const Bell * x = dynamic_cast<const Bell*> (term)) { - ss << "'gbellmf',[" << fl::Op::join(3, " ", + ss << "'gbellmf',[" << Op::join(3, " ", x->getWidth(), x->getSlope(), x->getCenter()) << "]"; return ss.str(); } + if (const Binary * x = dynamic_cast<const Binary*> (term)) { + ss << "'binarymf,[" << Op::join(2, " ", + x->getStart(), x->getDirection()) << "]"; + return ss.str(); + } + if (const Concave * x = dynamic_cast<const Concave*> (term)) { - ss << "'concavemf',[" << fl::Op::join(2, " ", + ss << "'concavemf',[" << Op::join(2, " ", x->getInflection(), x->getEnd()) << "]"; return ss.str(); } if (const Constant * x = dynamic_cast<const Constant*> (term)) { - ss << "'constant',[" << fl::Op::str(x->getValue()) << "]"; + ss << "'constant',[" << Op::str(x->getValue()) << "]"; return ss.str(); } if (const Cosine * x = dynamic_cast<const Cosine*> (term)) { - ss << "'cosinemf',[" << fl::Op::join(2, " ", + ss << "'cosinemf',[" << Op::join(2, " ", x->getCenter(), x->getWidth()) << "]"; return ss.str(); } if (const Discrete * x = dynamic_cast<const Discrete*> (term)) { - ss << "'discretemf',[" << fl::Op::join(Discrete::toVector(x->xy()), " ") << "]"; + ss << "'discretemf',[" << Op::join(Discrete::toVector(x->xy()), " ") << "]"; return ss.str(); } @@ -365,95 +325,95 @@ namespace fl { } if (const Gaussian * x = dynamic_cast<const Gaussian*> (term)) { - ss << "'gaussmf',[" << fl::Op::join(2, " ", + ss << "'gaussmf',[" << Op::join(2, " ", x->getStandardDeviation(), x->getMean()) << "]"; return ss.str(); } if (const GaussianProduct * x = dynamic_cast<const GaussianProduct*> (term)) { - ss << "'gauss2mf',[" << fl::Op::join(4, " ", + ss << "'gauss2mf',[" << Op::join(4, " ", x->getStandardDeviationA(), x->getMeanA(), x->getStandardDeviationB(), x->getMeanB()) << "]"; return ss.str(); } if (const Linear * x = dynamic_cast<const Linear*> (term)) { - ss << "'linear',[" << fl::Op::join<scalar>(x->coefficients(), " ") << "]"; + ss << "'linear',[" << Op::join<scalar>(x->coefficients(), " ") << "]"; return ss.str(); } if (const PiShape * x = dynamic_cast<const PiShape*> (term)) { - ss << "'pimf',[" << fl::Op::join(4, " ", + ss << "'pimf',[" << Op::join(4, " ", x->getBottomLeft(), x->getTopLeft(), x->getTopRight(), x->getBottomRight()) << "]"; return ss.str(); } if (const Ramp * x = dynamic_cast<const Ramp*> (term)) { - ss << "'rampmf',[" << fl::Op::join(2, " ", + ss << "'rampmf',[" << Op::join(2, " ", x->getStart(), x->getEnd()) << "]"; return ss.str(); } if (const Rectangle * x = dynamic_cast<const Rectangle*> (term)) { - ss << "'rectmf',[" << fl::Op::join(2, " ", + ss << "'rectmf',[" << Op::join(2, " ", x->getStart(), x->getEnd()) << "]"; return ss.str(); } if (const SigmoidDifference * x = dynamic_cast<const SigmoidDifference*> (term)) { - ss << "'dsigmf',[" << fl::Op::join(4, " ", + ss << "'dsigmf',[" << Op::join(4, " ", x->getRising(), x->getLeft(), x->getFalling(), x->getRight()) << "]"; return ss.str(); } if (const Sigmoid * x = dynamic_cast<const Sigmoid*> (term)) { - ss << "'sigmf',[" << fl::Op::join(2, " ", + ss << "'sigmf',[" << Op::join(2, " ", x->getSlope(), x->getInflection()) << "]"; return ss.str(); } if (const SigmoidProduct * x = dynamic_cast<const SigmoidProduct*> (term)) { - ss << "'psigmf',[" << fl::Op::join(4, " ", + ss << "'psigmf',[" << Op::join(4, " ", x->getRising(), x->getLeft(), x->getFalling(), x->getRight()) << "]"; return ss.str(); } if (const SShape * x = dynamic_cast<const SShape*> (term)) { - ss << "'smf',[" << fl::Op::join(2, " ", + ss << "'smf',[" << Op::join(2, " ", x->getStart(), x->getEnd()) << "]"; return ss.str(); } if (const Spike * x = dynamic_cast<const Spike*> (term)) { - ss << "'spikemf',[" << fl::Op::join(2, " ", + ss << "'spikemf',[" << Op::join(2, " ", x->getCenter(), x->getWidth()) << "]"; return ss.str(); } if (const Trapezoid * x = dynamic_cast<const Trapezoid*> (term)) { - ss << "'trapmf',[" << fl::Op::join(4, " ", + ss << "'trapmf',[" << Op::join(4, " ", x->getVertexA(), x->getVertexB(), x->getVertexC(), x->getVertexD()) << "]"; return ss.str(); } if (const Triangle * x = dynamic_cast<const Triangle*> (term)) { - ss << "'trimf',[" << fl::Op::join(3, " ", + ss << "'trimf',[" << Op::join(3, " ", x->getVertexA(), x->getVertexB(), x->getVertexC()) << "]"; return ss.str(); } if (const ZShape * x = dynamic_cast<const ZShape*> (term)) { - ss << "'zmf',[" << fl::Op::join(2, " ", + ss << "'zmf',[" << Op::join(2, " ", x->getStart(), x->getEnd()) << "]"; return ss.str(); } - ss << "[exporter error] term of class <" << term->className() << "> not supported"; - throw fl::Exception(ss.str(), FL_AT); + ss << "[exporter error] term of class <" << (term ? term->className() : "null") << "> not supported"; + throw Exception(ss.str(), FL_AT); } FisExporter* FisExporter::clone() const { diff --git a/fuzzylite/src/imex/FisImporter.cpp b/fuzzylite/src/imex/FisImporter.cpp index 741719c..f846469 100644 --- a/fuzzylite/src/imex/FisImporter.cpp +++ b/fuzzylite/src/imex/FisImporter.cpp @@ -1,42 +1,28 @@ /* - 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/imex/FisImporter.h" #include "fl/Headers.h" -#include <sstream> -#include <iostream> -#include <cctype> - namespace fl { - FisImporter::FisImporter() : Importer() { - } + FisImporter::FisImporter() : Importer() { } - FisImporter::~FisImporter() { - } + FisImporter::~FisImporter() { } std::string FisImporter::name() const { return "FisImporter"; @@ -47,27 +33,20 @@ namespace fl { std::istringstream fisReader(fis); std::string line; - int lineNumber = 0; + std::size_t lineNumber = 0; std::vector<std::string> sections; while (std::getline(fisReader, line)) { ++lineNumber; - std::vector<std::string> comments; - comments = Op::split(line, "//"); - if (comments.size() > 1) { - line = comments.front(); - } - comments = Op::split(line, "#"); - if (comments.size() > 1) { - line = comments.front(); - } + //remove comments + line = Op::split(line, "//", false).front(); + line = Op::split(line, "#", false).front(); line = Op::trim(line); - if (line.empty() or line.at(0) == '%' or line.at(0) == '#' - or (line.substr(0, 2) == "//")) { + if (line.empty() or line.at(0) == '%') { continue; } - line = fl::Op::findReplace(line, "'", ""); + line = Op::findReplace(line, "'", ""); if ("[System]" == line.substr(0, std::string("[System]").size()) or "[Input" == line.substr(0, std::string("[Input").size()) @@ -79,9 +58,9 @@ namespace fl { sections.at(sections.size() - 1) += "\n" + line; } else { std::ostringstream ss; - ss << "[import error] line " << lineNumber << " <" << line + "> " + ss << "[import error] line " << lineNumber << " <" << line << "> " "does not belong to any section"; - throw fl::Exception(ss.str(), FL_AT); + throw Exception(ss.str(), FL_AT); } } } @@ -96,12 +75,12 @@ namespace fl { importOutput(sections.at(i), engine.get()); else if ("[Rules]" == sections.at(i).substr(0, std::string("[Rules]").size())) importRules(sections.at(i), engine.get()); - else throw fl::Exception("[import error] section <" + else throw Exception("[import error] section <" + sections.at(i) + "> not recognized", FL_AT); } - engine->configure(extractTNorm(andMethod), extractSNorm(orMethod), - extractTNorm(impMethod), extractSNorm(aggMethod), - extractDefuzzifier(defuzzMethod)); + engine->configure(translateTNorm(andMethod), translateSNorm(orMethod), + translateTNorm(impMethod), translateSNorm(aggMethod), + translateDefuzzifier(defuzzMethod), General().className()); return engine.release(); } @@ -113,14 +92,14 @@ namespace fl { std::string line; std::getline(reader, line); //ignore first line [System] while (std::getline(reader, line)) { - std::vector<std::string> keyValue = fl::Op::split(line, "="); + std::vector<std::string> keyValue = Op::split(line, "="); - std::string key = fl::Op::trim(keyValue.at(0)); + std::string key = Op::trim(keyValue.at(0)); std::string value; for (std::size_t i = 1; i < keyValue.size(); ++i) { value += keyValue.at(i); } - value = fl::Op::trim(value); + value = Op::trim(value); if (key == "Name") engine->setName(value); else if (key == "AndMethod") andMethod = value; else if (key == "OrMethod") orMethod = value; @@ -131,7 +110,7 @@ namespace fl { or key == "NumInputs" or key == "NumOutputs" or key == "NumRules" or key == "NumMFs") { //ignore because are redundant. - } else throw fl::Exception("[import error] token <" + key + "> not recognized", FL_AT); + } else throw Exception("[import error] token <" + key + "> not recognized", FL_AT); } } @@ -144,18 +123,19 @@ namespace fl { engine->addInputVariable(input); while (std::getline(reader, line)) { - std::vector<std::string> keyValue = fl::Op::split(line, "="); + std::vector<std::string> keyValue = Op::split(line, "="); if (keyValue.size() != 2) - throw fl::Exception("[syntax error] expected a property of type " + throw Exception("[syntax error] expected a property of type " "'key=value', but found <" + line + ">", FL_AT); - std::string key = fl::Op::trim(keyValue.at(0)); - std::string value = fl::Op::trim(keyValue.at(1)); + std::string key = Op::trim(keyValue.at(0)); + std::string value = Op::trim(keyValue.at(1)); - if (key == "Name") input->setName(fl::Op::validName(value)); - else if (key == "Enabled") { + if (key == "Name") { + input->setName(Op::validName(value)); + } else if (key == "Enabled") { input->setEnabled(Op::isEq(Op::toScalar(value), 1.0)); } else if (key == "Range") { - std::pair<scalar, scalar> minmax = range(value); + std::pair<scalar, scalar> minmax = parseRange(value); input->setMinimum(minmax.first); input->setMaximum(minmax.second); } else if (key.substr(0, 2) == "MF") { @@ -163,7 +143,7 @@ namespace fl { } else if (key == "NumMFs") { //ignore } else { - throw fl::Exception("[import error] token <" + key + "> not recognized", FL_AT); + throw Exception("[import error] token <" + key + "> not recognized", FL_AT); } } } @@ -178,32 +158,33 @@ namespace fl { while (std::getline(reader, line)) { - std::vector<std::string> keyValue = fl::Op::split(line, "="); + std::vector<std::string> keyValue = Op::split(line, "="); if (keyValue.size() != 2) - throw fl::Exception("[syntax error] expected a property of type " + throw Exception("[syntax error] expected a property of type " "'key=value', but found < " + line + ">", FL_AT); - std::string key = fl::Op::trim(keyValue.at(0)); - std::string value = fl::Op::trim(keyValue.at(1)); + std::string key = Op::trim(keyValue.at(0)); + std::string value = Op::trim(keyValue.at(1)); - if (key == "Name") output->setName(fl::Op::validName(value)); - else if (key == "Enabled") { + if (key == "Name") { + output->setName(Op::validName(value)); + } else if (key == "Enabled") { output->setEnabled(Op::isEq(Op::toScalar(value), 1.0)); } else if (key == "Range") { - std::pair<scalar, scalar> minmax = range(value); + std::pair<scalar, scalar> minmax = parseRange(value); output->setMinimum(minmax.first); output->setMaximum(minmax.second); } else if (key.substr(0, 2) == "MF") { output->addTerm(parseTerm(value, engine)); } else if (key == "Default") { - output->setDefaultValue(fl::Op::toScalar(value)); + output->setDefaultValue(Op::toScalar(value)); } else if (key == "LockPrevious") { - output->setLockPreviousOutputValue(fl::Op::isEq(fl::Op::toScalar(value), 1.0)); + output->setLockPreviousValue(Op::isEq(Op::toScalar(value), 1.0)); } else if (key == "LockRange") { - output->setLockOutputValueInRange(fl::Op::isEq(fl::Op::toScalar(value), 1.0)); + output->setLockValueInRange(Op::isEq(Op::toScalar(value), 1.0)); } else if (key == "NumMFs") { //ignore } else { - throw fl::Exception("[import error] token <" + key + "> not recognized", FL_AT); + throw Exception("[import error] token <" + key + "> not recognized", FL_AT); } } } @@ -217,78 +198,78 @@ namespace fl { engine->addRuleBlock(ruleblock); while (std::getline(reader, line)) { - std::vector<std::string> inputsAndRest = fl::Op::split(line, ","); + std::vector<std::string> inputsAndRest = Op::split(line, ","); if (inputsAndRest.size() != 2) - throw fl::Exception("[syntax error] expected rule to match pattern " + throw Exception("[syntax error] expected rule to match pattern " "<'i '+, 'o '+ (w) : '1|2'>, but found instead <" + line + ">", FL_AT); - std::vector <std::string> outputsAndRest = fl::Op::split(inputsAndRest.at(1), ":"); + std::vector <std::string> outputsAndRest = Op::split(inputsAndRest.at(1), ":"); if (outputsAndRest.size() != 2) - throw fl::Exception("[syntax error] expected rule to match pattern " + throw Exception("[syntax error] expected rule to match pattern " "<'i '+, 'o '+ (w) : '1|2'>, but found instead <" + line + ">", FL_AT); - std::vector<std::string> inputs = fl::Op::split(inputsAndRest.at(0), " "); - std::vector<std::string> outputs = fl::Op::split(outputsAndRest.at(0), " "); + std::vector<std::string> inputs = Op::split(inputsAndRest.at(0), " "); + std::vector<std::string> outputs = Op::split(outputsAndRest.at(0), " "); std::string weightInParenthesis = outputs.at(outputs.size() - 1); outputs.erase(outputs.begin() + outputs.size() - 1); - std::string connector = fl::Op::trim(outputsAndRest.at(1)); + std::string connector = Op::trim(outputsAndRest.at(1)); - if ((int) inputs.size() != engine->numberOfInputVariables()) { + if (inputs.size() != engine->numberOfInputVariables()) { std::ostringstream ss; ss << "[syntax error] expected <" << engine->numberOfInputVariables() << ">" " input variables, but found <" << inputs.size() << ">" " input variables in rule <" << line << ">"; - throw fl::Exception(ss.str(), FL_AT); + throw Exception(ss.str(), FL_AT); } - if ((int) outputs.size() != engine->numberOfOutputVariables()) { + if (outputs.size() != engine->numberOfOutputVariables()) { std::ostringstream ss; ss << "[syntax error] expected <" << engine->numberOfOutputVariables() << ">" " output variables, but found <" << outputs.size() << ">" " output variables in rule <" << line << ">"; - throw fl::Exception(ss.str(), FL_AT); + throw Exception(ss.str(), FL_AT); } std::vector<std::string> antecedent, consequent; for (std::size_t i = 0; i < inputs.size(); ++i) { - scalar inputCode = fl::Op::toScalar(inputs.at(i)); - if (fl::Op::isEq(inputCode, 0.0)) continue; + scalar inputCode = Op::toScalar(inputs.at(i)); + if (Op::isEq(inputCode, 0.0)) continue; std::ostringstream ss; ss << engine->getInputVariable(i)->getName() << " " - << fl::Rule::isKeyword() << " " + << Rule::isKeyword() << " " << translateProposition(inputCode, engine->getInputVariable(i)); antecedent.push_back(ss.str()); } for (std::size_t i = 0; i < outputs.size(); ++i) { - scalar outputCode = fl::Op::toScalar(outputs.at(i)); - if (fl::Op::isEq(outputCode, 0.0)) continue; + scalar outputCode = Op::toScalar(outputs.at(i)); + if (Op::isEq(outputCode, 0.0)) continue; std::ostringstream ss; ss << engine->getOutputVariable(i)->getName() << " " - << fl::Rule::isKeyword() << " " + << Rule::isKeyword() << " " << translateProposition(outputCode, engine->getOutputVariable(i)); consequent.push_back(ss.str()); } std::ostringstream ruleText; - ruleText << fl::Rule::ifKeyword() << " "; + ruleText << Rule::ifKeyword() << " "; for (std::size_t i = 0; i < antecedent.size(); ++i) { ruleText << antecedent.at(i); if (i + 1 < antecedent.size()) { ruleText << " "; - if (connector == "1") ruleText << fl::Rule::andKeyword() << " "; - else if (connector == "2") ruleText << fl::Rule::orKeyword() << " "; - else throw fl::Exception("[syntax error] connector <" + if (connector == "1") ruleText << Rule::andKeyword() << " "; + else if (connector == "2") ruleText << Rule::orKeyword() << " "; + else throw Exception("[syntax error] connector <" + connector + "> not recognized", FL_AT); } } - ruleText << " " << fl::Rule::thenKeyword() << " "; + ruleText << " " << Rule::thenKeyword() << " "; for (std::size_t i = 0; i < consequent.size(); ++i) { ruleText << consequent.at(i); if (i + 1 < consequent.size()) { - ruleText << " " << fl::Rule::andKeyword() << " "; + ruleText << " " << Rule::andKeyword() << " "; } } @@ -300,9 +281,9 @@ namespace fl { ss << weightInParenthesis.at(i); } - scalar weight = fl::Op::toScalar(ss.str()); - if (not fl::Op::isEq(weight, 1.0)) - ruleText << " " << fl::Rule::withKeyword() << " " << Op::str(weight); + scalar weight = Op::toScalar(ss.str()); + if (not Op::isEq(weight, 1.0)) + ruleText << " " << Rule::withKeyword() << " " << Op::str(weight); Rule* rule = new Rule(ruleText.str()); try { rule->load(engine); @@ -314,34 +295,34 @@ namespace fl { } std::string FisImporter::translateProposition(scalar code, Variable* variable) const { - int intPart = (int) std::floor(std::fabs(code)) - 1; - scalar fracPart = std::fmod(std::fabs(code), 1.0); - if (intPart >= variable->numberOfTerms()) { + int intPart = (int) std::floor(std::abs(code)) - 1; + scalar fracPart = std::fmod(std::abs(code), scalar(1.0)); + if (intPart >= static_cast<int> (variable->numberOfTerms())) { std::ostringstream ex; ex << "[syntax error] the code <" << code << "> refers to a term " "out of range from variable <" << variable->getName() << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } bool isAny = intPart < 0; std::ostringstream ss; if (code < 0) ss << Not().name() << " "; - if (fl::Op::isEq(fracPart, 0.01)) ss << Seldom().name() << " "; - else if (fl::Op::isEq(fracPart, 0.05)) ss << Somewhat().name() << " "; - else if (fl::Op::isEq(fracPart, 0.2)) ss << Very().name() << " "; - else if (fl::Op::isEq(fracPart, 0.3)) ss << Extremely().name() << " "; - else if (fl::Op::isEq(fracPart, 0.4)) ss << Very().name() << " " << Very().name() << " "; - else if (fl::Op::isEq(fracPart, 0.99)) ss << Any().name() << " "; - else if (not fl::Op::isEq(fracPart, 0)) - throw fl::Exception("[syntax error] no hedge defined in FIS format for <" - + fl::Op::str(fracPart) + ">", FL_AT); + if (Op::isEq(fracPart, 0.01)) ss << Seldom().name() << " "; + else if (Op::isEq(fracPart, 0.05)) ss << Somewhat().name() << " "; + else if (Op::isEq(fracPart, 0.2)) ss << Very().name() << " "; + else if (Op::isEq(fracPart, 0.3)) ss << Extremely().name() << " "; + else if (Op::isEq(fracPart, 0.4)) ss << Very().name() << " " << Very().name() << " "; + else if (Op::isEq(fracPart, 0.99)) ss << Any().name() << " "; + else if (not Op::isEq(fracPart, 0.0)) + throw Exception("[syntax error] no hedge defined in FIS format for <" + + Op::str(fracPart) + ">", FL_AT); if (not isAny) { ss << variable->getTerm(intPart)->getName(); } return ss.str(); } - std::string FisImporter::extractTNorm(const std::string & name) const { + std::string FisImporter::translateTNorm(const std::string& name) const { if (name.empty()) return ""; if (name == "min") return Minimum().className(); if (name == "prod") return AlgebraicProduct().className(); @@ -353,20 +334,21 @@ namespace fl { return name; } - std::string FisImporter::extractSNorm(const std::string & name) const { + std::string FisImporter::translateSNorm(const std::string& name) const { if (name.empty()) return ""; if (name == "max") return Maximum().className(); - if (name == "sum" or name == "probor") return AlgebraicSum().className(); + if (name == "probor") return AlgebraicSum().className(); if (name == "bounded_sum") return BoundedSum().className(); if (name == "normalized_sum") return NormalizedSum().className(); if (name == "drastic_sum") return DrasticSum().className(); if (name == "einstein_sum") return EinsteinSum().className(); if (name == "hamacher_sum") return HamacherSum().className(); if (name == "nilpotent_maximum") return NilpotentMaximum().className(); + if (name == "sum") return UnboundedSum().className(); return name; } - std::string FisImporter::extractDefuzzifier(const std::string & name) const { + std::string FisImporter::translateDefuzzifier(const std::string& name) const { if (name.empty()) return ""; if (name == "centroid") return Centroid().className(); if (name == "bisector") return Bisector().className(); @@ -378,22 +360,22 @@ namespace fl { return name; } - std::pair<scalar, scalar> FisImporter::range(const std::string& range) const { - std::vector<std::string> parts = fl::Op::split(range, " "); + std::pair<scalar, scalar> FisImporter::parseRange(const std::string& range) const { + std::vector<std::string> parts = Op::split(range, " "); if (parts.size() != 2) - throw fl::Exception("[syntax error] expected range in format '[begin end]'," + throw Exception("[syntax error] expected range in format '[begin end]'," " but found <" + range + ">", FL_AT); std::string begin = parts.at(0), end = parts.at(1); if (begin.at(0) != '[' or end.at(end.size() - 1) != ']') - throw fl::Exception("[syntax error] expected range in format '[begin end]'," + throw Exception("[syntax error] expected range in format '[begin end]'," " but found <" + range + ">", FL_AT); std::pair<scalar, scalar> result; - result.first = fl::Op::toScalar(begin.substr(1)); - result.second = fl::Op::toScalar(end.substr(0, end.size() - 1)); + result.first = Op::toScalar(begin.substr(1)); + result.second = Op::toScalar(end.substr(0, end.size() - 1)); return result; } - Term * FisImporter::parseTerm(const std::string & fis, const Engine* engine) const { + Term * FisImporter::parseTerm(const std::string& fis, const Engine* engine) const { std::ostringstream ss; for (std::size_t i = 0; i < fis.size(); ++i) { if (not (fis.at(i) == '[' or fis.at(i) == ']')) { @@ -402,35 +384,36 @@ namespace fl { } std::string line = ss.str(); - std::vector<std::string> nameTerm = fl::Op::split(line, ":"); + std::vector<std::string> nameTerm = Op::split(line, ":"); if (nameTerm.size() != 2) { - throw fl::Exception("[syntax error] expected term in format 'name':'class',[params], " + throw Exception("[syntax error] expected term in format 'name':'class',[params], " "but found <" + line + ">", FL_AT); } - std::vector<std::string> termParams = fl::Op::split(nameTerm.at(1), ","); + std::vector<std::string> termParams = Op::split(nameTerm.at(1), ","); if (termParams.size() != 2) { - throw fl::Exception("[syntax error] expected term in format 'name':'class',[params], " + throw Exception("[syntax error] expected term in format 'name':'class',[params], " "but found " + line, FL_AT); } - std::vector<std::string> parameters = fl::Op::split(termParams.at(1), " "); + std::vector<std::string> parameters = Op::split(termParams.at(1), " "); for (std::size_t i = 0; i < parameters.size(); ++i) { - parameters.at(i) = fl::Op::trim(parameters.at(i)); + parameters.at(i) = Op::trim(parameters.at(i)); } return createInstance( - fl::Op::trim(termParams.at(0)), - fl::Op::trim(nameTerm.at(0)), + Op::trim(termParams.at(0)), + Op::trim(nameTerm.at(0)), parameters, engine); } - Term * FisImporter::createInstance(const std::string& mClass, + Term* FisImporter::createInstance(const std::string& mClass, const std::string& name, const std::vector<std::string>& params, const Engine* engine) const { std::map<std::string, std::string> mapping; - mapping["discretemf"] = Discrete().className(); + mapping["binarymf"] = Binary().className(); mapping["concavemf"] = Concave().className(); mapping["constant"] = Constant().className(); mapping["cosinemf"] = Cosine().className(); + mapping["discretemf"] = Discrete().className(); mapping["function"] = Function().className(); mapping["gbellmf"] = Bell().className(); mapping["gaussmf"] = Gaussian().className(); @@ -484,7 +467,7 @@ namespace fl { FL_unique_ptr<Term> term; term.reset(FactoryManager::instance()->term()->constructObject(flClass)); - Term::updateReference(term.get(), engine); + term->updateReference(engine); term->setName(Op::validName(name)); std::string separator; if (not dynamic_cast<Function*> (term.get())) { diff --git a/fuzzylite/src/imex/FldExporter.cpp b/fuzzylite/src/imex/FldExporter.cpp index 9064250..e25af1b 100644 --- a/fuzzylite/src/imex/FldExporter.cpp +++ b/fuzzylite/src/imex/FldExporter.cpp @@ -1,25 +1,17 @@ /* - 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/imex/FldExporter.h" @@ -30,20 +22,15 @@ #include "fl/variable/InputVariable.h" #include "fl/variable/OutputVariable.h" -#include <cmath> #include <fstream> -#include <vector> namespace fl { FldExporter::FldExporter(const std::string& separator) : Exporter(), _separator(separator), _exportHeaders(true), - _exportInputValues(true), _exportOutputValues(true) { - - } + _exportInputValues(true), _exportOutputValues(true) { } - FldExporter::~FldExporter() { - } + FldExporter::~FldExporter() { } std::string FldExporter::name() const { return "FldExporter"; @@ -84,156 +71,235 @@ namespace fl { std::string FldExporter::header(const Engine* engine) const { std::vector<std::string> result; if (_exportInputValues) { - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { InputVariable* inputVariable = engine->getInputVariable(i); - result.push_back("@InputVariable: " + inputVariable->getName() + ";"); + result.push_back(inputVariable->getName()); } } if (_exportOutputValues) { - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { OutputVariable* outputVariable = engine->getOutputVariable(i); - result.push_back("@OutputVariable: " + outputVariable->getName() + ";"); + result.push_back(outputVariable->getName()); } } - return "#@Engine: " + engine->getName() + ";\n#" + Op::join(result, _separator); + return Op::join(result, _separator); } std::string FldExporter::toString(const Engine* engine) const { - return toString(const_cast<Engine*> (engine), 1024); + return toString(const_cast<Engine*> (engine), 1024, AllVariables); } - std::string FldExporter::toString(Engine* engine, int maximumNumberOfResults) const { - std::ostringstream result; - write(engine, result, maximumNumberOfResults); - return result.str(); + std::string FldExporter::toString(Engine* engine, int values, ScopeOfValues scope) const { + return toString(engine, values, scope, engine->inputVariables()); } - void FldExporter::toFile(const std::string& path, Engine* engine, int maximumNumberOfResults) const { - std::ofstream writer(path.c_str()); - if (not writer.is_open()) { - throw fl::Exception("[file error] file <" + path + "> could not be created", FL_AT); - } - write(engine, writer, maximumNumberOfResults); - writer.close(); + std::string FldExporter::toString(Engine* engine, int values, ScopeOfValues scope, + const std::vector<InputVariable*>& activeVariables) const { + std::ostringstream result; + write(engine, result, values, scope, activeVariables); + return result.str(); } - std::string FldExporter::toString(Engine* engine, const std::string& inputData) const { + std::string FldExporter::toString(Engine* engine, std::istream& reader) const { std::ostringstream writer; if (_exportHeaders) writer << header(engine) << "\n"; - std::istringstream reader(inputData); std::string line; + int lineNumber = 0; while (std::getline(reader, line)) { + ++lineNumber; line = Op::trim(line); - if (not line.empty() and line.at(0) == '#') continue; //comments are ignored, blank lines are retained - std::vector<scalar> inputValues = parse(line); - write(engine, writer, inputValues); - writer.flush(); + if (not line.empty() and line.at(0) == '#') + continue; //comments are ignored, blank lines are retained + std::vector<scalar> inputValues; + if (lineNumber == 1) { //automatic detection of header. + try { + inputValues = parse(line); + } catch (std::exception&) { + continue; + } + } else { + inputValues = parse(line); + } + + write(engine, writer, inputValues, engine->inputVariables()); } return writer.str(); } - void FldExporter::toFile(const std::string& path, Engine* engine, const std::string& inputData) const { + void FldExporter::toFile(const std::string& path, Engine* engine, int values, ScopeOfValues scope) const { + toFile(path, engine, values, scope, engine->inputVariables()); + } + + void FldExporter::toFile(const std::string& path, Engine* engine, int values, ScopeOfValues scope, + const std::vector<InputVariable*>& activeVariables) const { std::ofstream writer(path.c_str()); if (not writer.is_open()) { - throw fl::Exception("[file error] file <" + path + "> could not be created", FL_AT); + throw Exception("[file error] file <" + path + "> could not be created", FL_AT); + } + write(engine, writer, values, scope, activeVariables); + writer.close(); + } + + void FldExporter::toFile(const std::string& path, Engine* engine, std::istream& reader) const { + std::ofstream writer(path.c_str()); + if (not writer.is_open()) { + throw Exception("[file error] file <" + path + "> could not be created", FL_AT); } if (_exportHeaders) writer << header(engine) << "\n"; - std::istringstream reader(inputData); + std::string line; + int lineNumber = 0; while (std::getline(reader, line)) { + ++lineNumber; line = Op::trim(line); - if (not line.empty() and line.at(0) == '#') continue; //comments are ignored, blank lines are retained - std::vector<scalar> inputValues = parse(line); - write(engine, writer, inputValues); - writer.flush(); + if (not line.empty() and line.at(0) == '#') + continue; //comments are ignored, blank lines are retained + std::vector<scalar> inputValues; + if (lineNumber == 1) { //automatic detection of header. + try { + inputValues = parse(line); + } catch (std::exception&) { + continue; + } + } else { + inputValues = parse(line); + } + + write(engine, writer, inputValues, engine->inputVariables()); } writer.close(); } - std::vector<scalar> FldExporter::parse(const std::string& x) const { + std::vector<scalar> FldExporter::parse(const std::string& values) const { std::vector<scalar> inputValues; - if (not (x.empty() or x.at(0) == '#')) { - std::istringstream tokenizer(x); - std::string token; - while (tokenizer >> token) - inputValues.push_back(fl::Op::toScalar(token)); + if (not (values.empty() or values.at(0) == '#')) { + inputValues = Op::toScalars(values); } return inputValues; } - void FldExporter::write(Engine* engine, std::ostream& writer, int maximum) const { + void FldExporter::write(Engine* engine, std::ostream& writer, int values, ScopeOfValues scope) const { + write(engine, writer, values, scope, engine->inputVariables()); + } + + void FldExporter::write(Engine* engine, std::ostream& writer, + int values, ScopeOfValues scope, + const std::vector<InputVariable*>& activeVariables) const { if (_exportHeaders) writer << header(engine) << "\n"; - int resolution = -1 + (int) std::max(1.0, std::pow( - maximum, 1.0 / engine->numberOfInputVariables())); + if (activeVariables.size() != engine->inputVariables().size()) { + std::ostringstream ex; + ex << "[exporter error] number of active variables " + "<" << activeVariables.size() << ">" + << "must match the number of input variables in the engine " + "<" << engine->inputVariables().size() << ">"; + throw Exception(ex.str(), FL_AT); + } + + int resolution; + if (scope == AllVariables) + resolution = -1 + (int) std::max(1.0, std::pow( + values, 1.0 / engine->numberOfInputVariables())); + else //if (scope == EachVariable) + resolution = values - 1; + std::vector<int> sampleValues, minSampleValues, maxSampleValues; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { sampleValues.push_back(0); minSampleValues.push_back(0); - maxSampleValues.push_back(resolution); + if (engine->inputVariables().at(i) == activeVariables.at(i)) + maxSampleValues.push_back(resolution); + else maxSampleValues.push_back(0); } - engine->restart(); - - bool overflow = false; std::vector<scalar> inputValues(engine->numberOfInputVariables()); - while (not overflow) { - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + do { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { InputVariable* inputVariable = engine->getInputVariable(i); - inputValues.at(i) = inputVariable->getMinimum() - + sampleValues.at(i) * inputVariable->range() / std::max(1, resolution); + if (inputVariable == activeVariables.at(i)) { + inputValues.at(i) = inputVariable->getMinimum() + + sampleValues.at(i) * inputVariable->range() / std::max(1, resolution); + } else { + inputValues.at(i) = inputVariable->getValue(); + } } - write(engine, writer, inputValues); - overflow = Op::increment(sampleValues, minSampleValues, maxSampleValues); - } + write(engine, writer, inputValues, activeVariables); + } while (Op::increment(sampleValues, minSampleValues, maxSampleValues)); } void FldExporter::write(Engine* engine, std::ostream& writer, std::istream& reader) const { if (_exportHeaders) writer << header(engine) << "\n"; - engine->restart(); - std::string line; - int lineNumber = 0; + std::size_t lineNumber = 0; while (std::getline(reader, line)) { ++lineNumber; - std::vector<scalar> inputValues = parse(Op::trim(line)); + line = Op::trim(line); + if (not line.empty() and line.at(0) == '#') + continue; //comments are ignored, blank lines are retained + std::vector<scalar> inputValues; + if (lineNumber == 1) { //automatic detection of header. + try { + inputValues = parse(line); + } catch (std::exception&) { + continue; + } + } else { + inputValues = parse(line); + } try { - write(engine, writer, inputValues); - } catch (fl::Exception& ex) { + write(engine, writer, inputValues, engine->inputVariables()); + } catch (Exception& ex) { ex.append(" writing line <" + Op::str(lineNumber) + ">"); throw; } } } - void FldExporter::write(Engine* engine, std::ostream& writer, const std::vector<scalar>& inputValues) const { + void FldExporter::write(Engine* engine, std::ostream& writer, + const std::vector<scalar>& inputValues) const { + write(engine, writer, inputValues, engine->inputVariables()); + } + + void FldExporter::write(Engine* engine, std::ostream& writer, + const std::vector<scalar>& inputValues, + const std::vector<InputVariable*>& activeVariables) const { if (inputValues.empty()) { writer << "\n"; return; } - if (int(inputValues.size()) < engine->numberOfInputVariables()) { + if (inputValues.size() < engine->numberOfInputVariables()) { std::ostringstream ex; ex << "[export error] engine has <" << engine->numberOfInputVariables() << "> " "input variables, but input data provides <" << inputValues.size() << "> values"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); + } + if (activeVariables.size() != engine->inputVariables().size()) { + std::ostringstream ex; + ex << "[exporter error] number of active variables <" << activeVariables.size() << "> " + "must match the number of input variables in the engine <" << engine->inputVariables().size() << ">"; + throw Exception(ex.str(), FL_AT); } - std::vector<std::string> values; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + std::vector<scalar> values; + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { InputVariable* inputVariable = engine->getInputVariable(i); - scalar inputValue = inputVariable->isEnabled() ? inputValues.at(i) : fl::nan; - inputVariable->setInputValue(inputValue); - if (_exportInputValues) values.push_back(Op::str(inputValue)); + scalar inputValue; + if (inputVariable == activeVariables.at(i)) { + inputValue = inputValues.at(i); + } else { + inputValue = inputVariable->getValue(); + } + inputVariable->setValue(inputValue); + if (_exportInputValues) values.push_back(inputValue); } engine->process(); - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { OutputVariable* outputVariable = engine->getOutputVariable(i); - outputVariable->defuzzify(); if (_exportOutputValues) - values.push_back(Op::str(outputVariable->getOutputValue())); + values.push_back(outputVariable->getValue()); } writer << Op::join(values, _separator) << "\n"; diff --git a/fuzzylite/src/imex/FllExporter.cpp b/fuzzylite/src/imex/FllExporter.cpp index 0ffeb2f..6a86152 100644 --- a/fuzzylite/src/imex/FllExporter.cpp +++ b/fuzzylite/src/imex/FllExporter.cpp @@ -1,25 +1,17 @@ /* - 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/imex/FllExporter.h" @@ -29,11 +21,9 @@ namespace fl { FllExporter::FllExporter(const std::string& indent, const std::string& separator) - : Exporter(), _indent(indent), _separator(separator) { - } + : Exporter(), _indent(indent), _separator(separator) { } - FllExporter::~FllExporter() { - } + FllExporter::~FllExporter() { } std::string FllExporter::name() const { return "FllExporter"; @@ -58,9 +48,17 @@ namespace fl { std::string FllExporter::toString(const Engine* engine) const { std::vector<std::string> result; result.push_back("Engine: " + engine->getName()); - result.push_back(toString(engine->inputVariables())); - result.push_back(toString(engine->outputVariables())); - result.push_back(toString(engine->ruleBlocks())); + if (not engine->getDescription().empty()) + result.push_back("description: " + engine->getDescription()); + for (std::size_t i = 0 ; i < engine->numberOfInputVariables(); ++i){ + result.push_back(toString(engine->getInputVariable(i))); + } + for (std::size_t i = 0 ; i < engine->numberOfOutputVariables(); ++i){ + result.push_back(toString(engine->getOutputVariable(i))); + } + for (std::size_t i = 0 ; i < engine->numberOfRuleBlocks(); ++i){ + result.push_back(toString(engine->getRuleBlock(i))); + } return Op::join(result, _separator); } @@ -99,10 +97,15 @@ namespace fl { std::string FllExporter::toString(const Variable* variable) const { std::vector<std::string> result; result.push_back("Variable: " + Op::validName(variable->getName())); + if (not variable->getDescription().empty()) { + result.push_back(_indent + "description: " + variable->getDescription()); + } result.push_back(_indent + "enabled: " + (variable->isEnabled() ? "true" : "false")); result.push_back(_indent + "range: " + Op::join(2, " ", variable->getMinimum(), variable->getMaximum())); - for (int i = 0; i < variable->numberOfTerms(); ++i) { + result.push_back(_indent + "lock-range: " + + (variable->isLockValueInRange() ? "true" : "false")); + for (std::size_t i = 0; i < variable->numberOfTerms(); ++i) { result.push_back(_indent + toString(variable->getTerm(i))); } return Op::join(result, _separator); @@ -111,10 +114,15 @@ namespace fl { std::string FllExporter::toString(const InputVariable* inputVariable) const { std::vector<std::string> result; result.push_back("InputVariable: " + Op::validName(inputVariable->getName())); + if (not inputVariable->getDescription().empty()) { + result.push_back(_indent + "description: " + inputVariable->getDescription()); + } result.push_back(_indent + "enabled: " + (inputVariable->isEnabled() ? "true" : "false")); result.push_back(_indent + "range: " + Op::join(2, " ", inputVariable->getMinimum(), inputVariable->getMaximum())); - for (int i = 0; i < inputVariable->numberOfTerms(); ++i) { + result.push_back(_indent + "lock-range: " + + (inputVariable->isLockValueInRange() ? "true" : "false")); + for (std::size_t i = 0; i < inputVariable->numberOfTerms(); ++i) { result.push_back(_indent + toString(inputVariable->getTerm(i))); } return Op::join(result, _separator); @@ -123,19 +131,22 @@ namespace fl { std::string FllExporter::toString(const OutputVariable* outputVariable) const { std::vector<std::string> result; result.push_back("OutputVariable: " + Op::validName(outputVariable->getName())); + if (not outputVariable->getDescription().empty()) { + result.push_back(_indent + "description: " + outputVariable->getDescription()); + } result.push_back(_indent + "enabled: " + (outputVariable->isEnabled() ? "true" : "false")); result.push_back(_indent + "range: " + Op::join(2, " ", outputVariable->getMinimum(), outputVariable->getMaximum())); - result.push_back(_indent + "accumulation: " + - toString(outputVariable->fuzzyOutput()->getAccumulation())); + result.push_back(_indent + "lock-range: " + + (outputVariable->isLockValueInRange() ? "true" : "false")); + result.push_back(_indent + "aggregation: " + + toString(outputVariable->fuzzyOutput()->getAggregation())); result.push_back(_indent + "defuzzifier: " + toString(outputVariable->getDefuzzifier())); result.push_back(_indent + "default: " + Op::str(outputVariable->getDefaultValue())); result.push_back(_indent + "lock-previous: " + - (outputVariable->isLockedPreviousOutputValue() ? "true" : "false")); - result.push_back(_indent + "lock-range: " + - (outputVariable->isLockedOutputValueInRange() ? "true" : "false")); - for (int i = 0; i < outputVariable->numberOfTerms(); ++i) { + (outputVariable->isLockPreviousValue() ? "true" : "false")); + for (std::size_t i = 0; i < outputVariable->numberOfTerms(); ++i) { result.push_back(_indent + toString(outputVariable->getTerm(i))); } return Op::join(result, _separator); @@ -144,12 +155,16 @@ namespace fl { std::string FllExporter::toString(const RuleBlock* ruleBlock) const { std::vector<std::string> result; result.push_back("RuleBlock: " + ruleBlock->getName()); + if (not ruleBlock->getDescription().empty()) { + result.push_back(_indent + "description: " + ruleBlock->getDescription()); + } result.push_back(_indent + "enabled: " + (ruleBlock->isEnabled() ? "true" : "false")); result.push_back(_indent + "conjunction: " + toString(ruleBlock->getConjunction())); result.push_back(_indent + "disjunction: " + toString(ruleBlock->getDisjunction())); + result.push_back(_indent + "implication: " + toString(ruleBlock->getImplication())); result.push_back(_indent + "activation: " + toString(ruleBlock->getActivation())); - for (int i = 0; i < ruleBlock->numberOfRules(); ++i) { + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { result.push_back(_indent + toString(ruleBlock->getRule(i))); } return Op::join(result, _separator); @@ -169,11 +184,17 @@ namespace fl { return "none"; } + std::string FllExporter::toString(const Activation* activation) const { + if (not activation) return "none"; + if (activation->parameters().empty()) return activation->className(); + return activation->className() + " " + activation->parameters(); + } + std::string FllExporter::toString(const Defuzzifier* defuzzifier) const { if (not defuzzifier) return "none"; if (const IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<const IntegralDefuzzifier*> (defuzzifier)) { - return defuzzifier->className() + " " + Op::str<int>(integralDefuzzifier->getResolution()); + return defuzzifier->className() + " " + Op::str(integralDefuzzifier->getResolution()); } else if (const WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) { diff --git a/fuzzylite/src/imex/FllImporter.cpp b/fuzzylite/src/imex/FllImporter.cpp index 2443d10..ac9d1fc 100644 --- a/fuzzylite/src/imex/FllImporter.cpp +++ b/fuzzylite/src/imex/FllImporter.cpp @@ -1,25 +1,17 @@ /* - 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/imex/FllImporter.h" @@ -31,12 +23,9 @@ namespace fl { FllImporter::FllImporter(const std::string& separator) : Importer(), - _separator(separator) { - } - - FllImporter::~FllImporter() { + _separator(separator) { } - } + FllImporter::~FllImporter() { } std::string FllImporter::name() const { return "FllImporter"; @@ -50,62 +39,48 @@ namespace fl { return this->_separator; } - Engine* FllImporter::fromString(const std::string& fll) const { + Engine* FllImporter::fromString(const std::string& code) const { FL_unique_ptr<Engine> engine(new Engine); + const std::string fll = Op::join(Op::split(code, _separator), "\n"); std::string tag; - std::ostringstream block; - std::istringstream fclReader(fll); + std::vector<std::string> block; + std::istringstream fllReader(fll); std::string line; - std::queue<std::string> lineQueue; - - bool processPending = false; - int lineNumber = 0; - while (not lineQueue.empty() or std::getline(fclReader, line)) { - if (not lineQueue.empty()) { - line = lineQueue.front(); - lineQueue.pop(); - } else { - line = clean(line); - if (line.empty()) continue; - std::vector<std::string> split = Op::split(line, _separator); - line = clean(split.front()); - for (std::size_t i = 1; i < split.size(); ++i) { - lineQueue.push(clean(split.at(i))); - } - ++lineNumber; - } + + while (std::getline(fllReader, line)) { + line = Op::trim(Op::split(line, "#", false).front()); //remove comments if (line.empty()) continue; std::size_t colon = line.find_first_of(':'); if (colon == std::string::npos) { - throw fl::Exception("[import error] expected a colon at line " + - Op::str(lineNumber) + ": " + line, FL_AT); + throw Exception("[import error] expected a colon here: " + line, FL_AT); } std::string key = Op::trim(line.substr(0, colon)); std::string value = Op::trim(line.substr(colon + 1)); if ("Engine" == key) { engine->setName(value); continue; - } else { - processPending = (key == "InputVariable" - or key == "OutputVariable" - or key == "RuleBlock"); - } - if (processPending) { - process(tag, block.str(), engine.get()); - block.str(""); //clear buffer + } else if (key == "description" and block.empty()){ + engine->setDescription(value); + continue; + } else if (key == "InputVariable" + or key == "OutputVariable" + or key == "RuleBlock") { + process(tag, Op::join(block, "\n"), engine.get()); block.clear(); //clear error flags - processPending = false; tag = key; + } else if (tag.empty()) { + throw Exception("[import error] unexpected block: " + line, FL_AT); } - block << key << ":" << value << "\n"; + block.push_back(key + ":" + value); } - process(tag, block.str(), engine.get()); + process(tag, Op::join(block, "\n"), engine.get()); return engine.release(); } void FllImporter::process(const std::string& tag, const std::string& block, Engine* engine) const { if (tag.empty()) return; + // FL_LOG("Processing " << tag << "\n" << block); if ("InputVariable" == tag) { processInputVariable(block, engine); } else if ("OutputVariable" == tag) { @@ -113,42 +88,47 @@ namespace fl { } else if ("RuleBlock" == tag) { processRuleBlock(block, engine); } else { - throw fl::Exception("[import error] block tag <" + tag + "> not recognized", FL_AT); + throw Exception("[import error] block tag <" + tag + "> not recognized", FL_AT); } } void FllImporter::processInputVariable(const std::string& block, Engine* engine) const { std::istringstream reader(block); std::string line; - InputVariable* inputVariable = new InputVariable; - engine->addInputVariable(inputVariable); + FL_unique_ptr<InputVariable> inputVariable(new InputVariable); while (std::getline(reader, line)) { std::pair<std::string, std::string> keyValue = parseKeyValue(line, ':'); if ("InputVariable" == keyValue.first) { inputVariable->setName(Op::validName(keyValue.second)); + } else if ("description" == keyValue.first) { + inputVariable->setDescription(keyValue.second); } else if ("enabled" == keyValue.first) { inputVariable->setEnabled(parseBoolean(keyValue.second)); } else if ("range" == keyValue.first) { std::pair<scalar, scalar> range = parseRange(keyValue.second); inputVariable->setRange(range.first, range.second); + } else if ("lock-range" == keyValue.first) { + inputVariable->setLockValueInRange(parseBoolean(keyValue.second)); } else if ("term" == keyValue.first) { inputVariable->addTerm(parseTerm(keyValue.second, engine)); } else { - throw fl::Exception("[import error] key <" + keyValue.first + "> not " + throw Exception("[import error] key <" + keyValue.first + "> not " "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT); } } + engine->addInputVariable(inputVariable.release()); } void FllImporter::processOutputVariable(const std::string& block, Engine* engine) const { std::istringstream reader(block); std::string line; - OutputVariable* outputVariable = new OutputVariable; - engine->addOutputVariable(outputVariable); + FL_unique_ptr<OutputVariable> outputVariable(new OutputVariable); while (std::getline(reader, line)) { std::pair<std::string, std::string> keyValue = parseKeyValue(line, ':'); if ("OutputVariable" == keyValue.first) { outputVariable->setName(Op::validName(keyValue.second)); + } else if ("description" == keyValue.first) { + outputVariable->setDescription(keyValue.second); } else if ("enabled" == keyValue.first) { outputVariable->setEnabled(parseBoolean(keyValue.second)); } else if ("range" == keyValue.first) { @@ -157,39 +137,64 @@ namespace fl { } else if ("default" == keyValue.first) { outputVariable->setDefaultValue(Op::toScalar(keyValue.second)); } else if ("lock-previous" == keyValue.first or "lock-valid" == keyValue.first) { - outputVariable->setLockPreviousOutputValue(parseBoolean(keyValue.second)); + outputVariable->setLockPreviousValue(parseBoolean(keyValue.second)); } else if ("lock-range" == keyValue.first) { - outputVariable->setLockOutputValueInRange(parseBoolean(keyValue.second)); + outputVariable->setLockValueInRange(parseBoolean(keyValue.second)); } else if ("defuzzifier" == keyValue.first) { outputVariable->setDefuzzifier(parseDefuzzifier(keyValue.second)); + } else if ("aggregation" == keyValue.first) { + outputVariable->fuzzyOutput()->setAggregation(parseSNorm(keyValue.second)); } else if ("accumulation" == keyValue.first) { - outputVariable->fuzzyOutput()->setAccumulation(parseSNorm(keyValue.second)); + outputVariable->fuzzyOutput()->setAggregation(parseSNorm(keyValue.second)); + FL_LOG("[warning] obsolete usage of identifier <accumulation: SNorm> in OutputVariable"); + FL_LOG("[information] from version 6.0, the identifier <aggregation: SNorm> should be used"); + FL_LOG("[backward compatibility] assumed " + "<aggregation: " << keyValue.second << "> " + "instead of <accumulation: " << keyValue.second << ">"); } else if ("term" == keyValue.first) { outputVariable->addTerm(parseTerm(keyValue.second, engine)); } else { - throw fl::Exception("[import error] key <" + keyValue.first + "> not " + throw Exception("[import error] key <" + keyValue.first + "> not " "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT); } } + engine->addOutputVariable(outputVariable.release()); } void FllImporter::processRuleBlock(const std::string& block, Engine* engine) const { std::istringstream reader(block); std::string line; - RuleBlock* ruleBlock = new RuleBlock; - engine->addRuleBlock(ruleBlock); + FL_unique_ptr<RuleBlock> ruleBlock(new RuleBlock); while (std::getline(reader, line)) { std::pair<std::string, std::string> keyValue = parseKeyValue(line, ':'); if ("RuleBlock" == keyValue.first) { ruleBlock->setName(keyValue.second); + } else if ("description" == keyValue.first) { + ruleBlock->setDescription(keyValue.second); } else if ("enabled" == keyValue.first) { ruleBlock->setEnabled(parseBoolean(keyValue.second)); } else if ("conjunction" == keyValue.first) { ruleBlock->setConjunction(parseTNorm(keyValue.second)); } else if ("disjunction" == keyValue.first) { ruleBlock->setDisjunction(parseSNorm(keyValue.second)); + } else if ("implication" == keyValue.first) { + ruleBlock->setImplication(parseTNorm(keyValue.second)); } else if ("activation" == keyValue.first) { - ruleBlock->setActivation(parseTNorm(keyValue.second)); + TNormFactory* tnorm = FactoryManager::instance()->tnorm(); + //@todo remove backwards compatibility in version 7.0 + if (tnorm->hasConstructor(keyValue.second)) { + ruleBlock->setImplication(parseTNorm(keyValue.second)); + FL_LOG("[warning] obsolete usage of identifier <activation: TNorm> " + "in RuleBlock"); + FL_LOG("[information] from version 6.0, the identifiers are " + "<activation: Activation> for Activation methods " + "and <implication: TNorm> for T-Norms"); + FL_LOG("[backward compatibility] assumed " + "<implication: " << keyValue.second << "> " + "instead of <activation: " << keyValue.second << ">"); + } else { + ruleBlock->setActivation(parseActivation(keyValue.second)); + } } else if ("rule" == keyValue.first) { Rule* rule = new Rule; rule->setText(keyValue.second); @@ -200,10 +205,14 @@ namespace fl { } ruleBlock->addRule(rule); } else { - throw fl::Exception("[import error] key <" + keyValue.first + "> not " + throw Exception("[import error] key <" + keyValue.first + "> not " "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT); } } + if (not ruleBlock->getActivation()){ + ruleBlock->setActivation(new General); + } + engine->addRuleBlock(ruleBlock.release()); } Term* FllImporter::parseTerm(const std::string& text, Engine* engine) const { @@ -212,12 +221,12 @@ namespace fl { //MEDIUM Triangle 0.500 1.000 1.500 if (tokens.size() < 2) { - throw fl::Exception("[syntax error] expected a term in format <name class parameters>, " + throw Exception("[syntax error] expected a term in format <name class parameters>, " "but found <" + text + ">", FL_AT); } FL_unique_ptr<Term> term; term.reset(FactoryManager::instance()->term()->constructObject(tokens.at(1))); - Term::updateReference(term.get(), engine); + term->updateReference(engine); term->setName(Op::validName(tokens.at(0))); std::ostringstream parameters; for (std::size_t i = 2; i < tokens.size(); ++i) { @@ -238,6 +247,20 @@ namespace fl { return FactoryManager::instance()->snorm()->constructObject(name); } + Activation* FllImporter::parseActivation(const std::string& name) const { + if (name == "none") return FactoryManager::instance()->activation()->constructObject(""); + std::vector<std::string> tokens = Op::split(name, " "); + Activation* result = FactoryManager::instance()->activation()->constructObject(tokens.front()); + + std::ostringstream parameters; + for (std::size_t i = 1; i < tokens.size(); ++i) { + parameters << tokens.at(i); + if (i + 1 < tokens.size()) parameters << " "; + } + result->configure(parameters.str()); + return result; + } + Defuzzifier* FllImporter::parseDefuzzifier(const std::string& text) const { std::vector<std::string> parameters = Op::split(text, " "); std::string name = parameters.at(0); @@ -252,7 +275,7 @@ namespace fl { if (parameter == "Automatic") type = WeightedDefuzzifier::Automatic; else if (parameter == "TakagiSugeno") type = WeightedDefuzzifier::TakagiSugeno; else if (parameter == "Tsukamoto") type = WeightedDefuzzifier::Tsukamoto; - else throw fl::Exception("[syntax error] unknown parameter of WeightedDefuzzifier <" + parameter + ">", FL_AT); + else throw Exception("[syntax error] unknown parameter of WeightedDefuzzifier <" + parameter + ">", FL_AT); weightedDefuzzifier->setType(type); } } @@ -267,7 +290,7 @@ namespace fl { bool FllImporter::parseBoolean(const std::string& boolean) const { if ("true" == boolean) return true; if ("false" == boolean) return false; - throw fl::Exception("[syntax error] expected boolean <true|false>, " + throw Exception("[syntax error] expected boolean <true|false>, " "but found <" + boolean + ">", FL_AT); } @@ -278,7 +301,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected pair in the form " "<key" << separator << "value>, but found <" << text << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::pair<std::string, std::string> result; result.first = text.substr(0, half); @@ -286,32 +309,8 @@ namespace fl { return result; } - std::string FllImporter::clean(const std::string& line) const { - if (line.empty()) return line; - if (line.size() == 1) return isspace(line.at(0)) ? "" : line; - int start = 0, end = line.size() - 1; - while (start <= end and isspace(line.at(start))) { - ++start; - } - int sharp = start; - while (sharp <= end) { - if (line.at(sharp) == '#') { - end = sharp - 1; - break; - } - ++sharp; - } - while (end >= start and (line.at(end) == '#' or isspace(line.at(end)))) { - --end; - } - - int length = end - start + 1; - return line.substr(start, length); - } - FllImporter* FllImporter::clone() const { return new FllImporter(*this); } - } diff --git a/fuzzylite/src/imex/Importer.cpp b/fuzzylite/src/imex/Importer.cpp index faef71d..c0be7f5 100644 --- a/fuzzylite/src/imex/Importer.cpp +++ b/fuzzylite/src/imex/Importer.cpp @@ -1,46 +1,34 @@ /* - 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/imex/Importer.h" #include "fl/Exception.h" #include <fstream> -#include <ostream> namespace fl { - Importer::Importer() { - } + Importer::Importer() { } - Importer::~Importer() { - - } + Importer::~Importer() { } Engine* Importer::fromFile(const std::string& path) const { std::ifstream reader(path.c_str()); if (not reader.is_open()) { - throw fl::Exception("[file error] file <" + path + "> could not be opened", FL_AT); + throw Exception("[file error] file <" + path + "> could not be opened", FL_AT); } std::ostringstream textEngine; std::string line; diff --git a/fuzzylite/src/imex/JavaExporter.cpp b/fuzzylite/src/imex/JavaExporter.cpp index a4948f8..6981dae 100644 --- a/fuzzylite/src/imex/JavaExporter.cpp +++ b/fuzzylite/src/imex/JavaExporter.cpp @@ -1,60 +1,59 @@ /* - 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/imex/JavaExporter.h" #include "fl/Headers.h" -#include <algorithm> namespace fl { - JavaExporter::JavaExporter() : Exporter() { - } + JavaExporter::JavaExporter(bool usingVariableNames) : Exporter(), + _usingVariableNames(usingVariableNames) { } - JavaExporter::~JavaExporter() { - - } + JavaExporter::~JavaExporter() { } std::string JavaExporter::name() const { return "JavaExporter"; } + void JavaExporter::setUsingVariableNames(bool usingVariableNames) { + this->_usingVariableNames = usingVariableNames; + } + + bool JavaExporter::isUsingVariableNames() const { + return this->_usingVariableNames; + } + std::string JavaExporter::toString(const Engine* engine) const { std::ostringstream ss; + ss << "//Code automatically generated with " << fuzzylite::library() << ".\n\n"; ss << "Engine engine = new Engine();\n"; ss << "engine.setName(\"" << engine->getName() << "\");\n"; + ss << "engine.setDescription(\"" << engine->getDescription() << "\");\n"; ss << "\n"; - for (int i = 0; i < engine->numberOfInputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfInputVariables(); ++i) { ss << toString(engine->getInputVariable(i), engine) << "\n"; } - for (int i = 0; i < engine->numberOfOutputVariables(); ++i) { + for (std::size_t i = 0; i < engine->numberOfOutputVariables(); ++i) { ss << toString(engine->getOutputVariable(i), engine) << "\n"; } - for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) { + for (std::size_t i = 0; i < engine->numberOfRuleBlocks(); ++i) { ss << toString(engine->getRuleBlock(i), engine) << "\n"; } @@ -63,21 +62,28 @@ namespace fl { std::string JavaExporter::toString(const InputVariable* inputVariable, const Engine* engine) const { std::ostringstream ss; - std::string name = "inputVariable"; - if (engine->numberOfInputVariables() > 1) { - int index = std::distance(engine->inputVariables().begin(), - std::find(engine->inputVariables().begin(), - engine->inputVariables().end(), inputVariable)); - name += Op::str<int>(index + 1); + std::string name; + if (isUsingVariableNames()) { + name = Op::validName(inputVariable->getName()); + } else { + name = "inputVariable"; + if (engine->numberOfInputVariables() > 1) { + std::size_t index = std::distance(engine->inputVariables().begin(), + std::find(engine->inputVariables().begin(), + engine->inputVariables().end(), inputVariable)); + name += Op::str(index + 1); + } } ss << "InputVariable " << name << " = new InputVariable();\n"; - ss << name << ".setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setName(\"" << inputVariable->getName() << "\");\n"; + ss << name << ".setDescription(\"" << inputVariable->getDescription() << "\");\n"; + ss << name << ".setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setRange(" << toString(inputVariable->getMinimum()) << ", " << toString(inputVariable->getMaximum()) << ");\n"; - - for (int i = 0; i < inputVariable->numberOfTerms(); ++i) { + ss << name << ".setLockValueInRange(" + << (inputVariable->isLockValueInRange() ? "true" : "false") << ");\n"; + for (std::size_t i = 0; i < inputVariable->numberOfTerms(); ++i) { ss << name << ".addTerm(" << toString(inputVariable->getTerm(i)) << ");\n"; } @@ -87,30 +93,36 @@ namespace fl { std::string JavaExporter::toString(const OutputVariable* outputVariable, const Engine* engine) const { std::ostringstream ss; - std::string name = "outputVariable"; - if (engine->numberOfOutputVariables() > 1) { - int index = std::distance(engine->outputVariables().begin(), - std::find(engine->outputVariables().begin(), - engine->outputVariables().end(), outputVariable)); - name += Op::str<int>(index + 1); + std::string name; + if (isUsingVariableNames()) { + name = Op::validName(outputVariable->getName()); + } else { + name = "outputVariable"; + if (engine->numberOfOutputVariables() > 1) { + std::size_t index = std::distance(engine->outputVariables().begin(), + std::find(engine->outputVariables().begin(), + engine->outputVariables().end(), outputVariable)); + name += Op::str(index + 1); + } } ss << "OutputVariable " << name << " = new OutputVariable();\n"; - ss << name << ".setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setName(\"" << outputVariable->getName() << "\");\n"; + ss << name << ".setDescription(\"" << outputVariable->getDescription() << "\");\n"; + ss << name << ".setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setRange(" << toString(outputVariable->getMinimum()) << ", " << toString(outputVariable->getMaximum()) << ");\n"; - ss << name << ".fuzzyOutput().setAccumulation(" << - toString(outputVariable->fuzzyOutput()->getAccumulation()) << ");\n"; + ss << name << ".setLockValueInRange(" << + (outputVariable->isLockValueInRange() ? "true" : "false") << ");\n"; + ss << name << ".setAggregation(" << + toString(outputVariable->fuzzyOutput()->getAggregation()) << ");\n"; ss << name << ".setDefuzzifier(" << toString(outputVariable->getDefuzzifier()) << ");\n"; ss << name << ".setDefaultValue(" << toString(outputVariable->getDefaultValue()) << ");\n"; - ss << name << ".setLockPreviousOutputValue(" << - (outputVariable->isLockedPreviousOutputValue() ? "true" : "false") << ");\n"; - ss << name << ".setLockOutputValueInRange(" << - (outputVariable->isLockedOutputValueInRange() ? "true" : "false") << ");\n"; - for (int i = 0; i < outputVariable->numberOfTerms(); ++i) { + ss << name << ".setLockPreviousValue(" << + (outputVariable->isLockPreviousValue() ? "true" : "false") << ");\n"; + for (std::size_t i = 0; i < outputVariable->numberOfTerms(); ++i) { ss << name << ".addTerm(" << toString(outputVariable->getTerm(i)) << ");\n"; } @@ -120,23 +132,33 @@ namespace fl { std::string JavaExporter::toString(const RuleBlock* ruleBlock, const Engine* engine) const { std::ostringstream ss; - std::string name = "ruleBlock"; - if (engine->numberOfRuleBlocks() > 1) { - int index = std::distance(engine->ruleBlocks().begin(), - std::find(engine->ruleBlocks().begin(), - engine->ruleBlocks().end(), ruleBlock)); - name += Op::str<int>(index + 1); + std::string name; + + if (isUsingVariableNames() and not ruleBlock->getName().empty()) { + name = Op::validName(ruleBlock->getName()); + } else { + name = "ruleBlock"; + if (engine->numberOfRuleBlocks() > 1) { + std::size_t index = std::distance(engine->ruleBlocks().begin(), + std::find(engine->ruleBlocks().begin(), + engine->ruleBlocks().end(), ruleBlock)); + name += Op::str(index + 1); + } } + ss << "RuleBlock " << name << " = new RuleBlock();\n"; - ss << name << ".setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setName(\"" << ruleBlock->getName() << "\");\n"; + ss << name << ".setDescription(\"" << ruleBlock->getDescription() << "\");\n"; + ss << name << ".setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n"; ss << name << ".setConjunction(" << toString(ruleBlock->getConjunction()) << ");\n"; ss << name << ".setDisjunction(" << toString(ruleBlock->getDisjunction()) << ");\n"; + ss << name << ".setImplication(" + << toString(ruleBlock->getImplication()) << ");\n"; ss << name << ".setActivation(" << toString(ruleBlock->getActivation()) << ");\n"; - for (int i = 0; i < ruleBlock->numberOfRules(); ++i) { + for (std::size_t i = 0; i < ruleBlock->numberOfRules(); ++i) { Rule* rule = ruleBlock->getRule(i); ss << name << ".addRule(Rule.parse(\"" << rule->getText() << "\", engine));\n"; } @@ -183,7 +205,7 @@ namespace fl { if (const IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<const IntegralDefuzzifier*> (defuzzifier)) { return "new " + integralDefuzzifier->className() + "(" - + fl::Op::str(integralDefuzzifier->getResolution()) + ")"; + + Op::str(integralDefuzzifier->getResolution()) + ")"; } if (const WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) { @@ -193,19 +215,22 @@ namespace fl { return "new " + defuzzifier->className() + "()"; } - std::string JavaExporter::toString(const Norm* norm) const{ - if (not norm) return "null"; - return "new " + norm->className() + "()"; - } - - std::string JavaExporter::toString(const TNorm* norm) const { + std::string JavaExporter::toString(const Norm* norm) const { if (not norm) return "null"; return "new " + norm->className() + "()"; } - std::string JavaExporter::toString(const SNorm* norm) const { - if (not norm) return "null"; - return "new " + norm->className() + "()"; + std::string JavaExporter::toString(const Activation* activation) const { + if (not activation) return "null"; + std::string parameters = Op::trim(activation->parameters()); + if (parameters.empty()) return "new " + activation->className() + "()"; + + std::vector<std::string> values = Op::split(parameters, " "); + for (std::size_t i = 0; i < values.size(); ++i) { + std::string parameter = values.at(i); + values.at(i) = (Op::isNumeric(parameter) ? parameter : ("\"" + parameter + "\"")); + } + return "new " + activation->className() + "(" + Op::join(values, ", ") + ")"; } std::string JavaExporter::toString(scalar value) const { @@ -223,6 +248,4 @@ namespace fl { return new JavaExporter(*this); } - } - diff --git a/fuzzylite/src/imex/RScriptExporter.cpp b/fuzzylite/src/imex/RScriptExporter.cpp new file mode 100644 index 0000000..5fe53c7 --- /dev/null +++ b/fuzzylite/src/imex/RScriptExporter.cpp @@ -0,0 +1,234 @@ +/* + 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 FuzzyLite License included with the software. + + 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/imex/RScriptExporter.h" + +#include "fl/Engine.h" +#include "fl/imex/FllExporter.h" +#include "fl/variable/InputVariable.h" +#include "fl/variable/OutputVariable.h" + +#include <fstream> +namespace fl { + + RScriptExporter::RScriptExporter() : Exporter(), + _minimumColor("yellow"), _maximumColor("red"), _contourColor("black") { } + + RScriptExporter::~RScriptExporter() { } + + std::string RScriptExporter::name() const { + return "RScriptExporter"; + } + + void RScriptExporter::setMinimumColor(const std::string& minimumColor) { + this->_minimumColor = minimumColor; + } + + std::string RScriptExporter::getMinimumColor() const { + return this->_minimumColor; + } + + void RScriptExporter::setMaximumColor(const std::string& maximumColor) { + this->_maximumColor = maximumColor; + } + + std::string RScriptExporter::getMaximumColor() const { + return _maximumColor; + } + + void RScriptExporter::setContourColor(const std::string& contourColor) { + this->_contourColor = contourColor; + } + + std::string RScriptExporter::getContourColor() const { + return this->_contourColor; + } + + RScriptExporter* RScriptExporter::clone() const { + return new RScriptExporter(*this); + } + + std::string RScriptExporter::toString(const Engine* engine) const { + if (engine->inputVariables().empty()) { + throw Exception("[exporter error] engine has no input variables to export the surface", FL_AT); + } + if (engine->outputVariables().empty()) { + throw Exception("[exporter error] engine has no output variables to export the surface", FL_AT); + } + InputVariable* a = engine->inputVariables().at(0); + InputVariable* b = engine->inputVariables().at(1 % engine->numberOfInputVariables()); + return toString(const_cast<Engine*> (engine), a, b, + 1024, FldExporter::AllVariables, engine->outputVariables()); + } + + std::string RScriptExporter::toString(Engine* engine, InputVariable* a, InputVariable* b, + int values, FldExporter::ScopeOfValues scope, + const std::vector<OutputVariable*>& outputVariables) const { + std::ostringstream writer; + writeScriptExportingDataFrame(engine, writer, a, b, values, scope, outputVariables); + return writer.str(); + } + + std::string RScriptExporter::toString(Engine* engine, InputVariable* a, InputVariable* b, + std::istream& reader, const std::vector<OutputVariable*>& outputVariables) const { + std::ostringstream writer; + writeScriptExportingDataFrame(engine, writer, a, b, reader, outputVariables); + return writer.str(); + } + + void RScriptExporter::toFile(const std::string& filePath, const Engine* engine) const { + if (engine->inputVariables().empty()) { + throw Exception("[exporter error] engine has no input variables to export the surface", FL_AT); + } + if (engine->outputVariables().empty()) { + throw Exception("[exporter error] engine has no output variables to export the surface", FL_AT); + } + InputVariable* a = engine->inputVariables().at(0); + InputVariable* b = engine->inputVariables().at(1 % engine->numberOfInputVariables()); + + toFile(filePath, const_cast<Engine*> (engine), a, b, + 1024, FldExporter::AllVariables, engine->outputVariables()); + } + + void RScriptExporter::toFile(const std::string& filePath, Engine* engine, + InputVariable* a, InputVariable* b, int values, FldExporter::ScopeOfValues scope, + const std::vector<OutputVariable*>& outputVariables) const { + std::ofstream writer(filePath.c_str()); + if (not writer.is_open()) { + throw Exception("[file error] file <" + filePath + "> could not be created", FL_AT); + } + writeScriptExportingDataFrame(engine, writer, a, b, values, scope, outputVariables); + writer.close(); + } + + void RScriptExporter::toFile(const std::string& filePath, Engine* engine, + InputVariable* a, InputVariable* b, std::istream& reader, + const std::vector<OutputVariable*>& outputVariables) const { + std::ofstream writer(filePath.c_str()); + if (not writer.is_open()) { + throw Exception("[file error] file <" + filePath + "> could not be created", FL_AT); + } + writeScriptExportingDataFrame(engine, writer, a, b, reader, outputVariables); + writer.close(); + } + + void RScriptExporter::writeScriptImportingDataFrame(const Engine* engine, std::ostream& writer, + InputVariable* a, InputVariable* b, const std::string& dfPath, + const std::vector<OutputVariable*>& outputVariables) const { + writeScriptHeader(writer, engine); + + writer << "engine.fldFile = \"" << dfPath << "\"\n"; + writer << "if (require(data.table)) {\n" + << " engine.df = data.table::fread(engine.fldFile, sep=\"auto\", header=\"auto\")\n" + << "} else {\n" + << " engine.df = read.table(engine.fldFile, header=TRUE)\n" + << "}\n"; + writer << "\n"; + + writeScriptPlots(writer, a, b, outputVariables); + } + + void RScriptExporter::writeScriptExportingDataFrame(Engine* engine, std::ostream& writer, + InputVariable* a, InputVariable* b, int values, FldExporter::ScopeOfValues scope, + const std::vector<OutputVariable*>& outputVariables) const { + writeScriptHeader(writer, engine); + + std::vector<InputVariable*> activeVariables = engine->inputVariables(); + for (std::size_t i = 0; i < activeVariables.size(); ++i) { + if (activeVariables.at(i) != a and activeVariables.at(i) != b) { + activeVariables.at(i) = fl::null; + } + } + writer << "engine.fld = \""; + FldExporter().write(engine, writer, values, scope, activeVariables); + writer << "\"\n\n"; + writer << "engine.df = read.delim(textConnection(engine.fld), header=TRUE, " + "sep=\" \", strip.white=TRUE)\n\n"; + + writeScriptPlots(writer, a, b, outputVariables); + } + + void RScriptExporter::writeScriptExportingDataFrame(Engine* engine, std::ostream& writer, + InputVariable* a, InputVariable* b, std::istream& reader, + const std::vector<OutputVariable*>& outputVariables) const { + writeScriptHeader(writer, engine); + + writer << "engine.fld = \""; + FldExporter().write(engine, writer, reader); + writer << "\"\n\n"; + + writer << "engine.df = read.delim(textConnection(engine.fld), header=TRUE, " + "sep=\" \", strip.white=TRUE)\n\n"; + + writeScriptPlots(writer, a, b, outputVariables); + } + + void RScriptExporter::writeScriptHeader(std::ostream& writer, const Engine* engine) const { + writer << "#Code automatically generated with " << fuzzylite::library() << ".\n\n" + << "library(ggplot2);\n" + << "\n"; + writer << "engine.name = \"" << engine->getName() << "\"\n"; + if (not engine->getDescription().empty()) + writer << "engine.description = \"" << engine->getDescription() << "\"\n"; + writer << "engine.fll = \"" << FllExporter().toString(engine) << "\"\n\n"; + } + + void RScriptExporter::writeScriptPlots(std::ostream& writer, + InputVariable* a, InputVariable* b, + const std::vector<OutputVariable*>& outputVariables) const { + std::ostringstream arrangeGrob; + arrangeGrob << "arrangeGrob("; + for (std::size_t i = 0; i < outputVariables.size(); ++i) { + OutputVariable* z = outputVariables.at(i); + if (a != b) { + writer << "engine.plot.i1i2_o" << (i + 1) << " = ggplot(engine.df, aes(" << a->getName() << ", " << b->getName() << ")) + \n" + << " geom_tile(aes(fill=" << z->getName() << ")) + \n" + << " scale_fill_gradient(low=\"" << _minimumColor << "\", high=\"" << _maximumColor << "\") + \n" + << " stat_contour(aes(x=" << a->getName() << ", y=" << b->getName() << ", z=" << z->getName() << "), color=\"" << _contourColor << "\") + \n" + << " ggtitle(\"(" << a->getName() << ", " << b->getName() << ") = " << z->getName() << "\")\n\n"; + + writer << "engine.plot.i2i1_o" << (i + 1) << " = ggplot(engine.df, aes(" << b->getName() << ", " << a->getName() << ")) + \n" + << " geom_tile(aes(fill=" << z->getName() << ")) + \n" + << " scale_fill_gradient(low=\"" << _minimumColor << "\", high=\"" << _maximumColor << "\") + \n" + << " stat_contour(aes(x=" << b->getName() << ", y=" << a->getName() << ", z=" << z->getName() << "), color=\"" << _contourColor << "\") + \n" + << " ggtitle(\"(" << b->getName() << ", " << a->getName() << ") = " << z->getName() << "\")\n\n"; + arrangeGrob << "engine.plot.i1i2_o" << (i + 1) << ", " << "engine.plot.i2i1_o" << (i + 1) << ", "; + } else { + writer << "engine.plot.i1_o" << (i + 1) << " = ggplot(engine.df, aes(" << a->getName() << ", " << z->getName() << ")) + \n" + << " geom_line(aes(color=" << z->getName() << "), size=3, lineend=\"round\", linejoin=\"mitre\") + \n" + << " scale_color_gradient(low=\"" << _minimumColor << "\", high=\"" << _maximumColor << "\") + \n" + << " ggtitle(\"" << a->getName() << " vs " << z->getName() << "\")\n\n"; + + writer << "engine.plot.o" << (i + 1) << "_i1 = ggplot(engine.df, aes(" << a->getName() << ", " << z->getName() << ")) + \n" + << " geom_line(aes(color=" << z->getName() << "), size=3, lineend=\"round\", linejoin=\"mitre\") + \n" + << " scale_color_gradient(low=\"" << _minimumColor << "\", high=\"" << _maximumColor << "\") + \n" + << " coord_flip() + \n" + << " ggtitle(\"" << z->getName() << " vs " << a->getName() << "\")\n\n"; + arrangeGrob << "engine.plot.i1_o" << (i + 1) << ", " << "engine.plot.o" << (i + 1) << "_i1, "; + } + + } + arrangeGrob << "ncol=2, top=engine.name)"; + writer << "if (require(gridExtra)) {\n" + << " engine.plots = " << arrangeGrob.str() << "\n" + << " ggsave(paste0(engine.name, \".pdf\"), engine.plots)\n" + << " if (require(grid)) {\n" + << " grid.newpage()\n" + << " grid.draw(engine.plots)\n" + << " }\n" + << "}\n"; + } +} diff --git a/fuzzylite/src/m/compare.m b/fuzzylite/src/m/compare.m deleted file mode 100755 index 56da68d..0000000 --- a/fuzzylite/src/m/compare.m +++ /dev/null @@ -1,60 +0,0 @@ -%{/* - 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 (R) is a registered trademark of FuzzyLite Limited. - */ -}% - -function [engine] = compare(fisFile, fldFile, delimiter, hasMetadata) -if (nargin < 3) - delimiter = ' '; -end -if (nargin < 4) - hasMetadata = true; -end - -engine = readfis(fisFile); -flMatrix = dlmread(fldFile, delimiter, hasMetadata ~ = 0, 0); - -if (length(engine.input) + length(engine.output) ~ = size(flMatrix, 2)) - error('fuzzylite:compare.m', 'Number of inputs and outputs in engine differ from FLD matrix'); -end - -if (isempty(engine.andMethod)) - engine.andMethod = 'min'; -end -if (isempty(engine.orMethod)) - engine.orMethod = 'max'; -end - -engine.inputValues = flMatrix(1 : end, 1 : length(engine.input)); -engine.outputValues = evalfis(engine.inputValues, engine); -engine.flOutputValues = flMatrix(1 : end, (length(engine.input) + 1) : (length(engine.input) + length(engine.output))); -engine.outputDiff = engine.outputValues - engine.flOutputValues; -engine.fld = [engine.inputValues engine.outputValues engine.flOutputValues engine.outputDiff]; -engine.nanfreeDiff = engine.outputDiff; -engine.nanfreeDiff(find(isnan(engine.nanfreeDiff))) = 0; -engine.mse = nansum(engine.outputDiff.^2) / size(engine.outputDiff, 1); -engine.quantiles = prctile(engine.nanfreeDiff, 0 : 25 : 100); - -end - - - diff --git a/fuzzylite/src/m/compare_examples.m b/fuzzylite/src/m/compare_examples.m deleted file mode 100755 index 1bf6818..0000000 --- a/fuzzylite/src/m/compare_examples.m +++ /dev/null @@ -1,45 +0,0 @@ -%{/*
- 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 (R) is a registered trademark of FuzzyLite Limited.
- */
-}%
-
-
-function [ engines ] = compare_examples(path, delimiter, hasMetadata)
-if (nargin < 2)
- delimiter = ' ';
-end
-if (nargin < 3)
- hasMetadata = true;
-end
-
-examples = {'\mamdani\SimpleDimmer', '\mamdani\matlab\mam21', '\mamdani\matlab\mam22', '\mamdani\matlab\shower', '\mamdani\matlab\tank', '\mamdani\matlab\tank2', '\mamdani\matlab\tipper', '\mamdani\matlab\tipper1', '\mamdani\octave\mamdani_tip_calculator', '\takagi-sugeno\SimpleDimmer', '\takagi-sugeno\matlab\fpeaks', '\takagi-sugeno\matlab\invkine1', '\takagi-sugeno\matlab\invkine2', '\takagi-sugeno\matlab\juggler', '\takagi-sugeno\matlab\membrn1', '\takagi-sugeno\matlab\membrn2', '\takagi-sugeno\matlab\slbb', '\takagi-sugeno\matlab\slcp', '\takagi-sugeno\matlab\slcp1', '\takagi-sugeno\matlab\slcpp1', '\takagi-sugeno\matlab\sltbu_fl', '\takagi-sugeno\matlab\sugeno1', '\takagi-sugeno\matlab\tanksg', '\takagi-sugeno\matlab\tippersg', '\takagi-sugeno\octave\cubic_approximator', '\takagi-sugeno\octave\heart_disease_risk', '\takagi-sugeno\octave\linear_tip_calculator'};
-pending = {'\mamdani\octave\investment_portfolio', '\takagi-sugeno\approximation', '\takagi-sugeno\octave\sugeno_tip_calculator', '\tsukamoto\tsukamoto'};
-engines = [];
-for i = 1 : length(examples)
- fisFile = strcat(path, examples{i}, '.fis')
- fldFile = strcat(path, examples{i}, '.fld');
- engines = [engines compare(fisFile, fldFile, delimiter, hasMetadata)];
- disp(strcat('Five number summary (', num2str(i), '): ', fisFile));
- engines(i).quantiles
-end
-end
-
diff --git a/fuzzylite/src/main.cpp b/fuzzylite/src/main.cpp index 6d8d701..06ca139 100644 --- a/fuzzylite/src/main.cpp +++ b/fuzzylite/src/main.cpp @@ -1,70 +1,49 @@ /* - 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/Headers.h" -#include <cstdlib> #include <fstream> -#include <iomanip> -#include <signal.h> -#include <typeinfo> - -using namespace fl; - -/* -#ifdef FL_WINDOWS -#include <windows.h> -BOOL WINAPI flSignalHandler(DWORD dwCtrlType) -{ - FL_LOG("Signal: " << dwCtrlType); - if (CTRL_C_EVENT == dwCtrlType){ - } - //return FALSE; - return TRUE; -} -#endif - */ +#include <csignal> -int main(int argc, char** argv) { - (void) argc; - (void) argv; +int main(int argc, const char* argv[]) { std::set_terminate(fl::Exception::terminate); std::set_unexpected(fl::Exception::terminate); - signal(SIGSEGV, fl::Exception::signalHandler); - signal(SIGABRT, fl::Exception::signalHandler); - signal(SIGILL, fl::Exception::signalHandler); - signal(SIGSEGV, fl::Exception::signalHandler); - signal(SIGFPE, fl::Exception::signalHandler); + ::signal(SIGSEGV, fl::Exception::signalHandler); + ::signal(SIGABRT, fl::Exception::signalHandler); + ::signal(SIGILL, fl::Exception::signalHandler); + ::signal(SIGSEGV, fl::Exception::signalHandler); + ::signal(SIGFPE, fl::Exception::signalHandler); #ifdef FL_UNIX - signal(SIGBUS, fl::Exception::signalHandler); - signal(SIGPIPE, fl::Exception::signalHandler); + ::signal(SIGBUS, fl::Exception::signalHandler); + ::signal(SIGPIPE, fl::Exception::signalHandler); #endif #ifdef FL_WINDOWS //SetConsoleCtrlHandler(flSignalHandler, TRUE); #endif - fuzzylite::setDebug(false); - return Console::main(argc, argv); + fl::fuzzylite::setDebugging(false); + + try { + fl::Console::main(argc, argv); + } catch (std::exception& ex) { + std::cout << ex.what() << "\nBACKTRACE:\n" << + fl::Exception::btCallStack() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/fuzzylite/src/norm/s/AlgebraicSum.cpp b/fuzzylite/src/norm/s/AlgebraicSum.cpp index e96c1a7..0497f23 100644 --- a/fuzzylite/src/norm/s/AlgebraicSum.cpp +++ b/fuzzylite/src/norm/s/AlgebraicSum.cpp @@ -1,25 +1,17 @@ /* - 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/norm/s/AlgebraicSum.h" @@ -30,6 +22,10 @@ namespace fl { return "AlgebraicSum"; } + Complexity AlgebraicSum::complexity() const { + return Complexity().arithmetic(3); + } + scalar AlgebraicSum::compute(scalar a, scalar b) const { return a + b - (a * b); } diff --git a/fuzzylite/src/norm/s/BoundedSum.cpp b/fuzzylite/src/norm/s/BoundedSum.cpp index 9c050c8..b1cb42b 100644 --- a/fuzzylite/src/norm/s/BoundedSum.cpp +++ b/fuzzylite/src/norm/s/BoundedSum.cpp @@ -1,35 +1,33 @@ /* - 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/norm/s/BoundedSum.h" +#include "fl/Operation.h" + namespace fl { std::string BoundedSum::className() const { return "BoundedSum"; } + Complexity BoundedSum::complexity() const { + return Complexity().arithmetic(1).function(1); + } + scalar BoundedSum::compute(scalar a, scalar b) const { return Op::min(scalar(1.0), a + b); } diff --git a/fuzzylite/src/norm/s/DrasticSum.cpp b/fuzzylite/src/norm/s/DrasticSum.cpp index c0629db..b0d9f1e 100644 --- a/fuzzylite/src/norm/s/DrasticSum.cpp +++ b/fuzzylite/src/norm/s/DrasticSum.cpp @@ -1,35 +1,33 @@ /* - 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/norm/s/DrasticSum.h" +#include "fl/Operation.h" + namespace fl { std::string DrasticSum::className() const { return "DrasticSum"; } + Complexity DrasticSum::complexity() const { + return Complexity().comparison(1).function(2); + } + scalar DrasticSum::compute(scalar a, scalar b) const { if (Op::isEq(Op::min(a, b), 0.0)) { return Op::max(a, b); diff --git a/fuzzylite/src/norm/s/EinsteinSum.cpp b/fuzzylite/src/norm/s/EinsteinSum.cpp index ec0fbd8..a638c7b 100644 --- a/fuzzylite/src/norm/s/EinsteinSum.cpp +++ b/fuzzylite/src/norm/s/EinsteinSum.cpp @@ -1,25 +1,17 @@ /* - 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/norm/s/EinsteinSum.h" @@ -30,6 +22,10 @@ namespace fl { return "EinsteinSum"; } + Complexity EinsteinSum::complexity() const { + return Complexity().arithmetic(4); + } + scalar EinsteinSum::compute(scalar a, scalar b) const { return (a + b) / (1.0 + a * b); } diff --git a/fuzzylite/src/norm/s/HamacherSum.cpp b/fuzzylite/src/norm/s/HamacherSum.cpp index f9c0994..41242ee 100644 --- a/fuzzylite/src/norm/s/HamacherSum.cpp +++ b/fuzzylite/src/norm/s/HamacherSum.cpp @@ -1,36 +1,35 @@ /* - 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/norm/s/HamacherSum.h" +#include "fl/Operation.h" + namespace fl { std::string HamacherSum::className() const { return "HamacherSum"; } + Complexity HamacherSum::complexity() const { + return Complexity().arithmetic(7); + } + scalar HamacherSum::compute(scalar a, scalar b) const { + if (Op::isEq(a * b, 1.0)) return 1.0; return (a + b - 2.0 * a * b) / (1.0 - a * b); } diff --git a/fuzzylite/src/norm/s/Maximum.cpp b/fuzzylite/src/norm/s/Maximum.cpp index ed8a839..5277d49 100644 --- a/fuzzylite/src/norm/s/Maximum.cpp +++ b/fuzzylite/src/norm/s/Maximum.cpp @@ -1,35 +1,33 @@ /* - 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/norm/s/Maximum.h" +#include "fl/Operation.h" + namespace fl { std::string Maximum::className() const { return "Maximum"; } + Complexity Maximum::complexity() const { + return Complexity().function(1); + } + scalar Maximum::compute(scalar a, scalar b) const { return Op::max(a, b); } diff --git a/fuzzylite/src/norm/s/NilpotentMaximum.cpp b/fuzzylite/src/norm/s/NilpotentMaximum.cpp index 0a09136..7c63999 100644 --- a/fuzzylite/src/norm/s/NilpotentMaximum.cpp +++ b/fuzzylite/src/norm/s/NilpotentMaximum.cpp @@ -1,38 +1,36 @@ /* - 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/norm/s/NilpotentMaximum.h" +#include "fl/Operation.h" + namespace fl { std::string NilpotentMaximum::className() const { return "NilpotentMaximum"; } + Complexity NilpotentMaximum::complexity() const { + return Complexity().comparison(1).arithmetic(1).function(1); + } + scalar NilpotentMaximum::compute(scalar a, scalar b) const { if (Op::isLt(a + b, 1.0)) { - return std::max(a, b); + return Op::max(a, b); } return 1.0; } @@ -45,6 +43,4 @@ namespace fl { return new NilpotentMaximum; } - } - diff --git a/fuzzylite/src/norm/s/NormalizedSum.cpp b/fuzzylite/src/norm/s/NormalizedSum.cpp index 94ad5ea..c420ed1 100644 --- a/fuzzylite/src/norm/s/NormalizedSum.cpp +++ b/fuzzylite/src/norm/s/NormalizedSum.cpp @@ -1,37 +1,35 @@ /* - 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/norm/s/NormalizedSum.h" +#include "fl/Operation.h" + namespace fl { std::string NormalizedSum::className() const { return "NormalizedSum"; } + Complexity NormalizedSum::complexity() const { + return Complexity().arithmetic(3).function(1); + } + scalar NormalizedSum::compute(scalar a, scalar b) const { - return a + b / Op::max(scalar(1.0), Op::max(a, b)); + return (a + b) / Op::max(scalar(1.0), a + b); } NormalizedSum* NormalizedSum::clone() const { @@ -42,5 +40,4 @@ namespace fl { return new NormalizedSum; } - } diff --git a/fuzzylite/src/norm/s/SNormFunction.cpp b/fuzzylite/src/norm/s/SNormFunction.cpp new file mode 100644 index 0000000..f8d9e42 --- /dev/null +++ b/fuzzylite/src/norm/s/SNormFunction.cpp @@ -0,0 +1,65 @@ +/* + 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 FuzzyLite License included with the software. + + 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/norm/s/SNormFunction.h" + +namespace fl { + + SNormFunction::SNormFunction(const std::string& formula) : SNorm() { + _function.variables["a"] = fl::nan; + _function.variables["b"] = fl::nan; + if (not formula.empty()) { + _function.load(formula); + } + } + + std::string SNormFunction::className() const { + return "SNormFunction"; + } + + Complexity SNormFunction::complexity() const { + if (_function.root()) + return _function.complexity().function(2 * std::log(scalar(_function.variables.size()))); + return _function.complexity(); + } + + scalar SNormFunction::compute(scalar a, scalar b) const { + _function.variables["a"] = a; + _function.variables["b"] = b; + return _function.evaluate(); + } + + Function& SNormFunction::function() { + return this->_function; + } + + void SNormFunction::setFormula(const std::string& formula) { + this->_function.load(formula); + } + + std::string SNormFunction::getFormula() const { + return _function.getFormula(); + } + + SNormFunction* SNormFunction::clone() const { + return new SNormFunction(*this); + } + + SNorm* SNormFunction::constructor() { + return new SNormFunction; + } + +} diff --git a/fuzzylite/src/norm/s/UnboundedSum.cpp b/fuzzylite/src/norm/s/UnboundedSum.cpp new file mode 100644 index 0000000..adee6ad --- /dev/null +++ b/fuzzylite/src/norm/s/UnboundedSum.cpp @@ -0,0 +1,43 @@ +/* + 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 FuzzyLite License included with the software. + + 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/norm/s/UnboundedSum.h" + +#include "fl/Operation.h" + +namespace fl { + + std::string UnboundedSum::className() const { + return "UnboundedSum"; + } + + Complexity UnboundedSum::complexity() const { + return Complexity().arithmetic(1); + } + + scalar UnboundedSum::compute(scalar a, scalar b) const { + return a + b; + } + + UnboundedSum* UnboundedSum::clone() const { + return new UnboundedSum(*this); + } + + SNorm* UnboundedSum::constructor() { + return new UnboundedSum; + } + +} diff --git a/fuzzylite/src/norm/t/AlgebraicProduct.cpp b/fuzzylite/src/norm/t/AlgebraicProduct.cpp index eee1061..eefe309 100644 --- a/fuzzylite/src/norm/t/AlgebraicProduct.cpp +++ b/fuzzylite/src/norm/t/AlgebraicProduct.cpp @@ -1,25 +1,17 @@ /* - 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/norm/t/AlgebraicProduct.h" @@ -30,6 +22,10 @@ namespace fl { return "AlgebraicProduct"; } + Complexity AlgebraicProduct::complexity() const { + return Complexity().arithmetic(1); + } + scalar AlgebraicProduct::compute(scalar a, scalar b) const { return a * b; } diff --git a/fuzzylite/src/norm/t/BoundedDifference.cpp b/fuzzylite/src/norm/t/BoundedDifference.cpp index aa4fead..3775aa4 100644 --- a/fuzzylite/src/norm/t/BoundedDifference.cpp +++ b/fuzzylite/src/norm/t/BoundedDifference.cpp @@ -1,29 +1,22 @@ /* - 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/norm/t/BoundedDifference.h" +#include "fl/Operation.h" namespace fl { @@ -31,6 +24,10 @@ namespace fl { return "BoundedDifference"; } + Complexity BoundedDifference::complexity() const { + return Complexity().arithmetic(2).function(1); + } + scalar BoundedDifference::compute(scalar a, scalar b) const { return Op::max(scalar(0.0), a + b - scalar(1.0)); } diff --git a/fuzzylite/src/norm/t/DrasticProduct.cpp b/fuzzylite/src/norm/t/DrasticProduct.cpp index 124b79d..5bfdc87 100644 --- a/fuzzylite/src/norm/t/DrasticProduct.cpp +++ b/fuzzylite/src/norm/t/DrasticProduct.cpp @@ -1,35 +1,33 @@ /* - 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/norm/t/DrasticProduct.h" +#include "fl/Operation.h" + namespace fl { std::string DrasticProduct::className() const { return "DrasticProduct"; } + Complexity DrasticProduct::complexity() const { + return Complexity().comparison(1).function(2); + } + scalar DrasticProduct::compute(scalar a, scalar b) const { if (Op::isEq(Op::max(a, b), 1.0)) { return Op::min(a, b); diff --git a/fuzzylite/src/norm/t/EinsteinProduct.cpp b/fuzzylite/src/norm/t/EinsteinProduct.cpp index ea79188..bbb3966 100644 --- a/fuzzylite/src/norm/t/EinsteinProduct.cpp +++ b/fuzzylite/src/norm/t/EinsteinProduct.cpp @@ -1,25 +1,17 @@ /* - 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/norm/t/EinsteinProduct.h" @@ -30,6 +22,10 @@ namespace fl { return "EinsteinProduct"; } + Complexity EinsteinProduct::complexity() const { + return Complexity().arithmetic(6); + } + scalar EinsteinProduct::compute(scalar a, scalar b) const { return (a * b) / (2.0 - (a + b - a * b)); } diff --git a/fuzzylite/src/norm/t/HamacherProduct.cpp b/fuzzylite/src/norm/t/HamacherProduct.cpp index e3f093e..9416084 100644 --- a/fuzzylite/src/norm/t/HamacherProduct.cpp +++ b/fuzzylite/src/norm/t/HamacherProduct.cpp @@ -1,29 +1,22 @@ /* - 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/norm/t/HamacherProduct.h" +#include "fl/Operation.h" namespace fl { @@ -31,7 +24,12 @@ namespace fl { return "HamacherProduct"; } + Complexity HamacherProduct::complexity() const { + return Complexity().arithmetic(5); + } + scalar HamacherProduct::compute(scalar a, scalar b) const { + if (Op::isEq(a + b, 0.0)) return 0.0; return (a * b) / (a + b - a * b); } diff --git a/fuzzylite/src/norm/t/Minimum.cpp b/fuzzylite/src/norm/t/Minimum.cpp index 34f348f..1a63658 100644 --- a/fuzzylite/src/norm/t/Minimum.cpp +++ b/fuzzylite/src/norm/t/Minimum.cpp @@ -1,35 +1,33 @@ /* - 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/norm/t/Minimum.h" +#include "fl/Operation.h" + namespace fl { std::string Minimum::className() const { return "Minimum"; } + Complexity Minimum::complexity() const { + return Complexity().function(1); + } + scalar Minimum::compute(scalar a, scalar b) const { return Op::min(a, b); } diff --git a/fuzzylite/src/norm/t/NilpotentMinimum.cpp b/fuzzylite/src/norm/t/NilpotentMinimum.cpp index b52a2fc..7ab906b 100644 --- a/fuzzylite/src/norm/t/NilpotentMinimum.cpp +++ b/fuzzylite/src/norm/t/NilpotentMinimum.cpp @@ -1,35 +1,33 @@ /* - 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/norm/t/NilpotentMinimum.h" +#include "fl/Operation.h" + namespace fl { std::string NilpotentMinimum::className() const { return "NilpotentMinimum"; } + Complexity NilpotentMinimum::complexity() const { + return Complexity().comparison(1).arithmetic(1).function(1); + } + scalar NilpotentMinimum::compute(scalar a, scalar b) const { if (Op::isGt(a + b, 1.0)) { return Op::min(a, b); @@ -47,4 +45,3 @@ namespace fl { } - diff --git a/fuzzylite/src/norm/t/TNormFunction.cpp b/fuzzylite/src/norm/t/TNormFunction.cpp new file mode 100644 index 0000000..1e0563e --- /dev/null +++ b/fuzzylite/src/norm/t/TNormFunction.cpp @@ -0,0 +1,66 @@ +/* + 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 FuzzyLite License included with the software. + + 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/norm/t/TNormFunction.h" + +namespace fl { + + TNormFunction::TNormFunction(const std::string& formula) : TNorm() { + _function.variables["a"] = fl::nan; + _function.variables["b"] = fl::nan; + if (not formula.empty()) { + _function.load(formula); + } + } + + std::string TNormFunction::className() const { + return "TNormFunction"; + } + + Complexity TNormFunction::complexity() const { + if (_function.root()) + return _function.complexity().function(2 * std::log(scalar(_function.variables.size()))); + return _function.complexity(); + } + + scalar TNormFunction::compute(scalar a, scalar b) const { + _function.variables["a"] = a; + _function.variables["b"] = b; + return _function.evaluate(); + } + + Function& TNormFunction::function() { + return this->_function; + } + + void TNormFunction::setFormula(const std::string& formula) { + this->_function.load(formula); + } + + std::string TNormFunction::getFormula() const { + return _function.getFormula(); + } + + TNormFunction* TNormFunction::clone() const { + return new TNormFunction(*this); + } + + TNorm* TNormFunction::constructor() { + return new TNormFunction; + } + + +} diff --git a/fuzzylite/src/rule/Antecedent.cpp b/fuzzylite/src/rule/Antecedent.cpp index aa18a68..5f0b0be 100644 --- a/fuzzylite/src/rule/Antecedent.cpp +++ b/fuzzylite/src/rule/Antecedent.cpp @@ -1,25 +1,17 @@ /* - 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/rule/Antecedent.h" @@ -28,29 +20,21 @@ #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/term/Aggregated.h" #include "fl/variable/InputVariable.h" #include "fl/variable/OutputVariable.h" -#include <algorithm> #include <stack> - namespace fl { Antecedent::Antecedent() - : _text(""), _expression(fl::null) { - } + : _text(""), _expression(fl::null) { } Antecedent::~Antecedent() { - unload(); + _expression.reset(fl::null); } void Antecedent::setText(const std::string& text) { @@ -62,24 +46,29 @@ namespace fl { } Expression* Antecedent::getExpression() const { - return this->_expression; + return this->_expression.get(); + } + + void Antecedent::setExpression(Expression* expression) { + this->_expression.reset(expression); } bool Antecedent::isLoaded() const { - return this->_expression != fl::null; + return _expression.get() != fl::null; } scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const { - return this->activationDegree(conjunction, disjunction, this->_expression); + return this->activationDegree(conjunction, disjunction, _expression.get()); } 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); + throw Exception("[antecedent error] antecedent <" + getText() + "> is not loaded", FL_AT); } - const Proposition* proposition = dynamic_cast<const Proposition*> (node); - if (proposition) { + const Expression::Type expression = node->type(); + if (expression == Expression::Proposition) { + const Proposition* proposition = static_cast<const Proposition*> (node); if (not proposition->variable->isEnabled()) { return 0.0; } @@ -96,62 +85,147 @@ namespace fl { } } 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); + Variable::Type variableType = proposition->variable->type(); + if (variableType == Variable::Input) { + result = proposition->term->membership(proposition->variable->getValue()); + } else if (variableType == Variable::Output) { + result = static_cast<OutputVariable*> (proposition->variable) + ->fuzzyOutput()->activationDegree(proposition->term); } - for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin(); - rit != proposition->hedges.rend(); ++rit) { - result = (*rit)->hedge(result); + + if (not proposition->hedges.empty()) { + 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)) { + if (expression == Expression::Operator) { + const Operator* fuzzyOperator = static_cast<const Operator*> (node); + if (not (fuzzyOperator->left and fuzzyOperator->right)) { + std::ostringstream ex; + ex << "[syntax error] left and right operands must exist"; + throw Exception(ex.str(), FL_AT); + } + if (fuzzyOperator->name == Rule::andKeyword()) { + if (not conjunction) throw 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 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] left and right operands must exist"; - throw fl::Exception(ex.str(), FL_AT); + ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized"; + throw Exception(ex.str(), FL_AT); + + } else { + std::ostringstream ss; + ss << "[antecedent error] expected a Proposition or Operator, but found <"; + if (node) ss << node->toString(); + ss << ">"; + throw Exception(ss.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)); + } + + + Complexity Antecedent::complexity(const TNorm* conjunction, const SNorm* disjunction) const { + return complexity(conjunction, disjunction, _expression.get()); + } + + Complexity Antecedent::complexity(const TNorm* conjunction, const SNorm* disjunction, + const Expression* node) const { + if (not isLoaded()) { + return Complexity(); } - 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)); + Complexity result; + const Expression::Type expression = node->type(); + if (expression == Expression::Proposition) { + const Proposition* proposition = static_cast<const Proposition*> (node); + if (not proposition->variable->isEnabled()) { + return result; + } + + 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)) { + result += (*rit)->complexity(); + while (++rit != proposition->hedges.rend()) { + result = (*rit)->complexity(); + } + return result; + } + } + Variable::Type variableType = proposition->variable->type(); + if (variableType == Variable::Input) { + result += proposition->term->complexity(); + } else if (variableType == Variable::Output) { + OutputVariable* outputVariable = static_cast<OutputVariable*> (proposition->variable); + result += outputVariable->fuzzyOutput()->complexityOfActivationDegree(); + } + + if (not proposition->hedges.empty()) { + for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin(); + rit != proposition->hedges.rend(); ++rit) { + result += (*rit)->complexity(); + } + } + return result; } - std::ostringstream ex; - ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized"; - throw fl::Exception(ex.str(), FL_AT); + //if node is an operator + if (expression == Expression::Operator) { + const Operator* fuzzyOperator = static_cast<const Operator*> (node); + if (not (fuzzyOperator->left and fuzzyOperator->right)) { + std::ostringstream ex; + ex << "[syntax error] left and right operands must exist"; + throw Exception(ex.str(), FL_AT); + } + if (fuzzyOperator->name == Rule::andKeyword()) { + if (conjunction) { + result += conjunction->complexity(); + } + result += complexity(conjunction, disjunction, fuzzyOperator->left) + + complexity(conjunction, disjunction, fuzzyOperator->right); + return result; + } + if (fuzzyOperator->name == Rule::orKeyword()) { + if (disjunction) { + result += disjunction->complexity(); + } + result += complexity(conjunction, disjunction, fuzzyOperator->left) + + complexity(conjunction, disjunction, fuzzyOperator->right); + return result; + } + } + return Complexity(); } void Antecedent::unload() { - if (_expression) { - delete _expression; - _expression = fl::null; - } + _expression.reset(fl::null); } - void Antecedent::load(fl::Rule* rule, const Engine* engine) { - load(_text, rule, engine); + void Antecedent::load(const Engine* engine) { + load(getText(), engine); } - void Antecedent::load(const std::string& antecedent, fl::Rule* rule, const Engine* engine) { + void Antecedent::load(const std::string& antecedent, 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); + setText(antecedent); + if (Op::trim(antecedent).empty()) { + throw Exception("[syntax error] antecedent is empty", FL_AT); } /* Builds an proposition tree from the antecedent of a fuzzy rule. @@ -179,8 +253,10 @@ namespace fl { 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 (engine->hasInputVariable(token)) + variable = engine->getInputVariable(token); + else if (engine->hasOutputVariable(token)) + variable = engine->getOutputVariable(token); if (variable) { proposition = new Proposition; proposition->variable = variable; @@ -201,15 +277,9 @@ namespace fl { } 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) { + HedgeFactory* factory = FactoryManager::instance()->hedge(); + if (factory->hasConstructor(token)) { + Hedge* hedge = factory->constructObject(token); proposition->hedges.push_back(hedge); if (dynamic_cast<Any*> (hedge)) { state = S_VARIABLE bitor S_AND_OR; @@ -236,7 +306,7 @@ namespace fl { 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); + throw Exception(ex.str(), FL_AT); } Operator* fuzzyOperator = new Operator; fuzzyOperator->name = token; @@ -258,33 +328,33 @@ namespace fl { 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); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> in antecedent"; - throw fl::Exception(ex.str(), FL_AT); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } } @@ -299,7 +369,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] unable to parse the following expressions in antecedent <" << Op::join(errors, " ") << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } } catch (...) { for (std::size_t i = 0; i < expressionStack.size(); ++i) { @@ -308,59 +378,68 @@ namespace fl { } throw; } - this->_expression = expressionStack.top(); + setExpression(expressionStack.top()); } std::string Antecedent::toString() const { - return toInfix(this->_expression); + return toInfix(getExpression()); } std::string Antecedent::toPrefix(const Expression* node) const { if (not isLoaded()) { - throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); 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) << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << fuzzyOperator->toString() << " " + << toPrefix(fuzzyOperator->left) << " " + << toPrefix(fuzzyOperator->right) << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } 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); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); 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) << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << toInfix(fuzzyOperator->left) << " " + << fuzzyOperator->toString() << " " + << toInfix(fuzzyOperator->right) << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } 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); + throw Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT); } - if (not node) node = this->_expression; + if (not node) node = getExpression(); 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() << " "; + if (const Operator * fuzzyOperator = dynamic_cast<const Operator*> (node)) { + ss << toPostfix(fuzzyOperator->left) << " " + << toPostfix(fuzzyOperator->right) << " " + << fuzzyOperator->toString() << " "; + } else { + ss << "[antecedent error] unknown class of Expression <" << (node ? node->toString() : "null") << ">"; + } return ss.str(); } diff --git a/fuzzylite/src/rule/Consequent.cpp b/fuzzylite/src/rule/Consequent.cpp index 28d3390..a90b1a9 100644 --- a/fuzzylite/src/rule/Consequent.cpp +++ b/fuzzylite/src/rule/Consequent.cpp @@ -1,25 +1,17 @@ /* - 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/rule/Consequent.h" @@ -27,24 +19,21 @@ #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/term/Aggregated.h" #include "fl/variable/OutputVariable.h" -#include <algorithm> - namespace fl { - Consequent::Consequent() { - } + Consequent::Consequent() { } Consequent::~Consequent() { - unload(); + for (std::size_t i = 0; i < _conclusions.size(); ++i) { + delete _conclusions.at(i); + } + _conclusions.clear(); } std::string Consequent::getText() const { @@ -59,9 +48,29 @@ namespace fl { return this->_conclusions; } - void Consequent::modify(scalar activationDegree, const TNorm* activation) { + std::vector<Proposition*>& Consequent::conclusions() { + return this->_conclusions; + } + + Complexity Consequent::complexity(const TNorm* implication) const { + Complexity result; + result.comparison(1); + + for (std::size_t i = 0; i < _conclusions.size(); ++i) { + Proposition* proposition = _conclusions.at(i); + result.comparison(2); + for (std::size_t h = 0; h < proposition->hedges.size(); ++h) { + result += proposition->hedges.at(h)->complexity(); + } + result += static_cast<OutputVariable*> (proposition->variable) + ->complexity(Activated(proposition->term, fl::nan, implication)); + } + return result; + } + + void Consequent::modify(scalar activationDegree, const TNorm* implication) { if (not isLoaded()) { - throw fl::Exception("[consequent error] consequent <" + _text + "> is not loaded", FL_AT); + throw Exception("[consequent error] consequent <" + getText() + "> is not loaded", FL_AT); } for (std::size_t i = 0; i < _conclusions.size(); ++i) { Proposition* proposition = _conclusions.at(i); @@ -72,10 +81,9 @@ namespace fl { 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()); + + static_cast<OutputVariable*> (proposition->variable)->fuzzyOutput() + ->addTerm(proposition->term, activationDegree, implication); } } } @@ -91,16 +99,16 @@ namespace fl { _conclusions.clear(); } - void Consequent::load(Rule* rule, const Engine* engine) { - load(_text, rule, engine); + void Consequent::load(const Engine* engine) { + load(getText(), engine); } - void Consequent::load(const std::string& consequent, Rule* rule, const Engine* engine) { + void Consequent::load(const std::string& consequent, const Engine* engine) { unload(); - this->_text = consequent; + setText(consequent); - if (fl::Op::trim(consequent).empty()) { - throw fl::Exception("[syntax error] consequent is empty", FL_AT); + if (Op::trim(consequent).empty()) { + throw Exception("[syntax error] consequent is empty", FL_AT); } /** @@ -129,8 +137,7 @@ namespace fl { if (engine->hasOutputVariable(token)) { proposition = new Proposition; proposition->variable = engine->getOutputVariable(token); - _conclusions.push_back(proposition); - + conclusions().push_back(proposition); state = S_IS; continue; } @@ -144,15 +151,9 @@ namespace fl { } 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) { + HedgeFactory* factory = FactoryManager::instance()->hedge(); + if (factory->hasConstructor(token)) { + Hedge* hedge = factory->constructObject(token); proposition->hedges.push_back(hedge); state = S_HEDGE bitor S_TERM; continue; @@ -178,19 +179,19 @@ namespace fl { 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); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } if ((state bitand S_AND) or (state bitand S_WITH)) { @@ -198,30 +199,30 @@ namespace fl { ex << "[syntax error] consequent expected operator <" << Rule::andKeyword() << "> " << "or keyword <" << Rule::withKeyword() << ">, " << "but found <" << token << ">"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } std::ostringstream ex; ex << "[syntax error] unexpected token <" << token << "> in consequent"; - throw fl::Exception(ex.str(), FL_AT); + throw 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); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } } } catch (...) { @@ -232,9 +233,9 @@ namespace fl { 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()) + 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 index ea7873e..9b0ab81 100644 --- a/fuzzylite/src/rule/Expression.cpp +++ b/fuzzylite/src/rule/Expression.cpp @@ -1,25 +1,17 @@ /* - 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/rule/Expression.h" @@ -31,18 +23,22 @@ namespace fl { - Expression::Expression() { - } + Expression::Expression() { } - Expression::~Expression() { - } + Expression::~Expression() { } - Proposition::Proposition() - : Expression(), variable(fl::null), term(fl::null) { - } + Proposition::Proposition() : Expression(), + variable(fl::null), term(fl::null) { } Proposition::~Proposition() { + for (std::size_t i = 0; i < hedges.size(); ++i) { + delete hedges.at(i); + } + hedges.clear(); + } + Expression::Type Proposition::type() const { + return Expression::Proposition; } std::string Proposition::toString() const { @@ -68,14 +64,18 @@ namespace fl { return ss.str(); } - Operator::Operator() : Expression(), name(""), left(fl::null), right(fl::null) { - } + Operator::Operator() : Expression(), + name(""), left(fl::null), right(fl::null) { } Operator::~Operator() { if (left) delete left; if (right) delete right; } + Expression::Type Operator::type() const { + return Expression::Operator; + } + std::string Operator::toString() const { return name; } diff --git a/fuzzylite/src/rule/Rule.cpp b/fuzzylite/src/rule/Rule.cpp index 446290b..0338cd9 100644 --- a/fuzzylite/src/rule/Rule.cpp +++ b/fuzzylite/src/rule/Rule.cpp @@ -1,55 +1,43 @@ /* - 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/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> +#include "fl/Operation.h" namespace fl { Rule::Rule(const std::string& text, scalar weight) - : _text(text), _weight(weight), _antecedent(new Antecedent), _consequent(new Consequent) { - } + : _enabled(true), _text(text), _weight(weight), _activationDegree(0.0), _triggered(false), + _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(const Rule& other) : _enabled(other._enabled), _text(other._text), + _weight(other._weight), _activationDegree(other._activationDegree), _triggered(false), + _antecedent(new Antecedent), _consequent(new Consequent) { } Rule& Rule::operator=(const Rule& other) { if (this != &other) { - unload(); - + _enabled = other._enabled; _text = other._text; _weight = other._weight; + _activationDegree = other._activationDegree; + _triggered = other._triggered; _antecedent.reset(new Antecedent); _consequent.reset(new Consequent); } @@ -57,7 +45,8 @@ namespace fl { } Rule::~Rule() { - unload(); + if (_antecedent.get()) _antecedent->unload(); + if (_consequent.get()) _consequent->unload(); } void Rule::setText(const std::string& text) { @@ -92,87 +81,93 @@ namespace fl { return this->_consequent.get(); } - /** - * Operations for std::vector _hedges - */ - void Rule::addHedge(Hedge* hedge) { - this->_hedges[hedge->name()] = hedge; + void Rule::setEnabled(bool active) { + this->_enabled = active; } - 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; + bool Rule::isEnabled() const { + return this->_enabled; } - 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; + void Rule::setActivationDegree(scalar activationDegree) { + this->_activationDegree = activationDegree; } - 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()); + scalar Rule::getActivationDegree() const { + return this->_activationDegree; } - int Rule::numberOfHedges() const { - return this->_hedges.size(); + void Rule::deactivate() { + _activationDegree = 0.0; + _triggered = false; } - void Rule::setHedges(const std::map<std::string, Hedge*>& hedges) { - this->_hedges = hedges; + scalar Rule::activateWith(const TNorm* conjunction, const SNorm* disjunction) { + if (not isLoaded()) { + throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT); + } + _activationDegree = _weight * _antecedent->activationDegree(conjunction, disjunction); + return _activationDegree; } - const std::map<std::string, Hedge*>& Rule::hedges() const { - return this->_hedges; + void Rule::trigger(const TNorm* implication) { + if (not isLoaded()) { + throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT); + } + if (_enabled and Op::isGt(_activationDegree, 0.0)) { + FL_DBG("[firing with " << Op::str(_activationDegree) << "] " << toString()); + _consequent->modify(_activationDegree, implication); + _triggered = true; + } } - std::map<std::string, Hedge*>& Rule::hedges() { - return this->_hedges; + bool Rule::isTriggered() const { + return this->_triggered; } - 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); + Complexity Rule::complexityOfActivation(const TNorm* conjunction, const SNorm* disjunction) const { + Complexity result; + result.comparison(1).arithmetic(1); + if (isLoaded()) { + result += _antecedent->complexity(conjunction, disjunction); } - return _weight * getAntecedent()->activationDegree(conjunction, disjunction); + return result; } - 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); + Complexity Rule::complexityOfFiring(const TNorm* implication) const { + Complexity result; + result.comparison(3); + if (isLoaded()) { + result += _consequent->complexity(implication); } - _consequent->modify(degree, activation); + return result; + } + + Complexity Rule::complexity(const TNorm* conjunction, const SNorm* disjunction, + const TNorm* implication) const { + return complexityOfActivation(conjunction, disjunction) + + complexityOfFiring(implication); } bool Rule::isLoaded() const { - return _antecedent->isLoaded() and _consequent->isLoaded(); + return _antecedent.get() and _consequent.get() + and _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(); + deactivate(); + if (getAntecedent()) getAntecedent()->unload(); + if (getConsequent()) getConsequent()->unload(); } void Rule::load(const Engine* engine) { - load(_text, engine); + load(getText(), engine); } void Rule::load(const std::string& rule, const Engine* engine) { - this->_text = rule; + deactivate(); + setEnabled(true); + setText(rule); std::istringstream tokenizer(rule.substr(0, rule.find_first_of('#'))); std::string token; std::ostringstream ossAntecedent, ossConsequent; @@ -192,7 +187,7 @@ namespace fl { std::ostringstream ex; ex << "[syntax error] expected keyword <" << Rule::ifKeyword() << ">, but found <" << token << "> in rule: " << rule; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } break; case S_IF: @@ -205,39 +200,46 @@ namespace fl { break; case S_WITH: try { - weight = fl::Op::toScalar(token); + weight = Op::toScalar(token); state = S_END; - } catch (fl::Exception& e) { + } catch (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; + throw; } 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); + throw Exception(ex.str(), FL_AT); + } + + default: + std::ostringstream ex; + ex << "[syntax error] unexpected state <" << state << ">"; + throw 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); + ex << "[syntax error] " << (rule.empty() ? "empty rule" : ("ignored rule: " + rule)); + throw 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); + throw 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); + throw Exception(ex.str(), FL_AT); } - _antecedent->load(ossAntecedent.str(), this, engine); - _consequent->load(ossConsequent.str(), this, engine); - _weight = weight; + getAntecedent()->load(ossAntecedent.str(), engine); + getConsequent()->load(ossConsequent.str(), engine); + setWeight(weight); } catch (...) { unload(); diff --git a/fuzzylite/src/rule/RuleBlock.cpp b/fuzzylite/src/rule/RuleBlock.cpp index 9ab813e..d4e2a82 100644 --- a/fuzzylite/src/rule/RuleBlock.cpp +++ b/fuzzylite/src/rule/RuleBlock.cpp @@ -1,44 +1,35 @@ /* - 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/rule/RuleBlock.h" +#include "fl/activation/General.h" #include "fl/imex/FllExporter.h" #include "fl/norm/TNorm.h" #include "fl/norm/SNorm.h" #include "fl/rule/Rule.h" - -#include <sstream> +#include "fl/Operation.h" namespace fl { RuleBlock::RuleBlock(const std::string& name) - : _name(name), _enabled(true) { - } + : _enabled(true), _name(name), _description("") { } - RuleBlock::RuleBlock(const RuleBlock& other) : _name(other._name), - _enabled(true) { + RuleBlock::RuleBlock(const RuleBlock& other) : _enabled(true), _name(other._name), + _description(other._description) { copyFrom(other); } @@ -50,6 +41,7 @@ namespace fl { _rules.clear(); _conjunction.reset(fl::null); _disjunction.reset(fl::null); + _implication.reset(fl::null); _activation.reset(fl::null); copyFrom(other); @@ -58,11 +50,13 @@ namespace fl { } void RuleBlock::copyFrom(const RuleBlock& source) { - _name = source._name; _enabled = source._enabled; - if (source._activation.get()) _activation.reset(source._activation->clone()); + _name = source._name; + _description = source._description; if (source._conjunction.get()) _conjunction.reset(source._conjunction->clone()); if (source._disjunction.get()) _disjunction.reset(source._disjunction->clone()); + if (source._implication.get()) _implication.reset(source._implication->clone()); + if (source._activation.get()) _activation.reset(source._activation->clone()); for (std::size_t i = 0; i < source._rules.size(); ++i) { _rules.push_back(source._rules.at(i)->clone()); } @@ -75,21 +69,25 @@ namespace fl { _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()); + Complexity RuleBlock::complexity() const { + Complexity result; + result.comparison(1); + if (_activation.get()) { + result += _activation->complexity(this); + } else { + for (std::size_t i = 0; i < _rules.size(); ++i) { + result += _rules.at(i)->complexity( + _conjunction.get(), _disjunction.get(), _implication.get()); } } + return result; + } + + void RuleBlock::activate() { + if (not _activation.get()) { + _activation.reset(new General); + } + _activation->activate(this); } void RuleBlock::unloadRules() const { @@ -114,7 +112,7 @@ namespace fl { } } if (throwException) { - fl::Exception exception("[ruleblock error] the following " + Exception exception("[ruleblock error] the following " "rules could not be loaded:\n" + exceptions.str(), FL_AT); throw exception; } @@ -133,6 +131,14 @@ namespace fl { return this->_name; } + void RuleBlock::setDescription(const std::string& description) { + this->_description = description; + } + + std::string RuleBlock::getDescription() const { + return this->_description; + } + void RuleBlock::setConjunction(TNorm* tnorm) { this->_conjunction.reset(tnorm); } @@ -149,11 +155,19 @@ namespace fl { return this->_disjunction.get(); } - void RuleBlock::setActivation(TNorm* activation) { + void RuleBlock::setImplication(TNorm* implication) { + this->_implication.reset(implication); + } + + TNorm* RuleBlock::getImplication() const { + return this->_implication.get(); + } + + void RuleBlock::setActivation(Activation* activation) { this->_activation.reset(activation); } - TNorm* RuleBlock::getActivation() const { + Activation* RuleBlock::getActivation() const { return this->_activation.get(); } @@ -173,25 +187,25 @@ namespace fl { * Operations for std::vector _rules */ void RuleBlock::addRule(Rule* rule) { - this->_rules.push_back(rule); + _rules.push_back(rule); } - void RuleBlock::insertRule(Rule* rule, int index) { - this->_rules.insert(this->_rules.begin() + index, rule); + void RuleBlock::insertRule(Rule* rule, std::size_t index) { + _rules.insert(_rules.begin() + index, rule); } - Rule* RuleBlock::getRule(int index) const { - return this->_rules.at(index); + Rule* RuleBlock::getRule(std::size_t index) const { + return _rules.at(index); } - Rule* RuleBlock::removeRule(int index) { - Rule* result = this->_rules.at(index); - this->_rules.erase(this->_rules.begin() + index); + Rule* RuleBlock::removeRule(std::size_t index) { + Rule* result = _rules.at(index); + _rules.erase(_rules.begin() + index); return result; } - int RuleBlock::numberOfRules() const { - return this->_rules.size(); + std::size_t RuleBlock::numberOfRules() const { + return _rules.size(); } const std::vector<Rule*>& RuleBlock::rules() const { @@ -206,5 +220,8 @@ namespace fl { return this->_rules; } + RuleBlock* RuleBlock::clone() const { + return new RuleBlock(*this); + } } diff --git a/fuzzylite/src/term/Accumulated.cpp b/fuzzylite/src/term/Accumulated.cpp deleted file mode 100644 index 979af9f..0000000 --- a/fuzzylite/src/term/Accumulated.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - 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/term/Accumulated.h" - -#include "fl/imex/FllExporter.h" -#include "fl/norm/SNorm.h" -#include "fl/norm/s/Maximum.h" -#include "fl/term/Activated.h" - - -namespace fl { - - Accumulated::Accumulated(const std::string& name, scalar minimum, scalar maximum, - SNorm* accumulation) - : Term(name), _minimum(minimum), _maximum(maximum), _accumulation(accumulation) { - } - - Accumulated::Accumulated(const Accumulated& other) : Term(other) { - copyFrom(other); - } - - Accumulated& Accumulated::operator=(const Accumulated& other) { - if (this != &other) { - clear(); - _accumulation.reset(fl::null); - - Term::operator=(other); - copyFrom(other); - } - return *this; - } - - Accumulated::~Accumulated() { - clear(); - } - - void Accumulated::copyFrom(const Accumulated& source) { - _minimum = source._minimum; - _maximum = source._maximum; - - if (source._accumulation.get()) - _accumulation.reset(source._accumulation->clone()); - - for (std::size_t i = 0; i < source._terms.size(); ++i) { - _terms.push_back(source._terms.at(i)->clone()); - } - } - - std::string Accumulated::className() const { - return "Accumulated"; - } - - scalar Accumulated::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - if (not (_terms.empty() or _accumulation.get())) { //Exception for IntegralDefuzzifiers - throw fl::Exception("[accumulation error] " - "accumulation operator needed to accumulate " + toString(), FL_AT); - } - scalar mu = 0.0; - for (std::size_t i = 0; i < _terms.size(); ++i) { - mu = _accumulation->compute(mu, _terms.at(i)->membership(x)); - } - return mu; - } - - scalar Accumulated::activationDegree(const Term* forTerm) const { - scalar result = 0.0; - for (std::size_t i = 0; i < _terms.size(); ++i) { - Activated* activatedTerm = _terms.at(i); - if (activatedTerm->getTerm() == forTerm) { - if (_accumulation.get()) result = _accumulation->compute(result, activatedTerm->getDegree()); - else result += activatedTerm->getDegree(); //Default for WeightDefuzzifier - } - } - return result; - } - - std::string Accumulated::parameters() const { - FllExporter exporter; - std::ostringstream ss; - ss << exporter.toString(_accumulation.get()); - ss << " " << Op::str(_minimum) << " " << Op::str(_maximum) << " "; - for (std::size_t i = 0; i < _terms.size(); ++i) { - ss << " " << exporter.toString(_terms.at(i)); - } - return ss.str(); - } - - void Accumulated::configure(const std::string& parameters) { - (void) parameters; - } - - Accumulated* Accumulated::clone() const { - return new Accumulated(*this); - } - - std::string Accumulated::toString() const { - std::vector<std::string> accumulate; - for (std::size_t i = 0; i < _terms.size(); ++i) { - accumulate.push_back(_terms.at(i)->toString()); - } - FllExporter exporter; - std::ostringstream ss; - ss << _name << ": " << className() << " " - << exporter.toString(_accumulation.get()) << "[" - << fl::Op::join(accumulate, ",") << "]"; - return ss.str(); - } - - void Accumulated::setMinimum(scalar minimum) { - this->_minimum = minimum; - } - - scalar Accumulated::getMinimum() const { - return this->_minimum; - } - - void Accumulated::setMaximum(scalar maximum) { - this->_maximum = maximum; - } - - scalar Accumulated::getMaximum() const { - return this->_maximum; - } - - void Accumulated::setRange(scalar minimum, scalar maximum) { - setMinimum(minimum); - setMaximum(maximum); - } - - scalar Accumulated::range() const { - return this->_maximum - this->_minimum; - } - - void Accumulated::setAccumulation(SNorm* accumulation) { - this->_accumulation.reset(accumulation); - } - - SNorm* Accumulated::getAccumulation() const { - return this->_accumulation.get(); - } - - /** - * Operations for std::vector _terms - */ - - - void Accumulated::addTerm(const Term* term, scalar degree, const TNorm* activation) { - this->_terms.push_back(new Activated(term, degree, activation)); - } - - void Accumulated::addTerm(Activated* term) { - this->_terms.push_back(term); - } - - Activated* Accumulated::removeTerm(int index) { - Activated* term = this->_terms.at(index); - this->_terms.erase(this->_terms.begin() + index); - return term; - } - - void Accumulated::clear() { - for (std::size_t i = 0; i < _terms.size(); ++i) { - delete _terms.at(i); - } - _terms.clear(); - } - - Activated* Accumulated::getTerm(int index) const { - return this->_terms.at(index); - } - - const std::vector<Activated*>& Accumulated::terms() const { - return this->_terms; - } - - std::vector<Activated*>& Accumulated::terms() { - return this->_terms; - } - - int Accumulated::numberOfTerms() const { - return _terms.size(); - } - - bool Accumulated::isEmpty() const { - return _terms.empty(); - } - -} diff --git a/fuzzylite/src/term/Activated.cpp b/fuzzylite/src/term/Activated.cpp index 9a27b4b..80eda6c 100644 --- a/fuzzylite/src/term/Activated.cpp +++ b/fuzzylite/src/term/Activated.cpp @@ -1,71 +1,81 @@ /* - 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/Activated.h" #include "fl/imex/FllExporter.h" -#include "fl/norm/TNorm.h" namespace fl { - Activated::Activated(const Term* term, scalar degree, const TNorm* activation) - : Term(""), _term(term), _degree(degree), _activation(activation) { - if (term) this->_name = term->getName(); + Activated::Activated(const Term* term, scalar degree, const TNorm* implication) + : Term(""), _term(term), _degree(degree), _implication(implication) { + if (term) setName(term->getName()); } - Activated::~Activated() { - } + Activated::~Activated() { } std::string Activated::className() const { return "Activated"; } + Complexity Activated::complexity() const { + Complexity result; + result.comparison(3); + if (_implication) { + result += _implication->complexity(); + } + if (_term) { + result += _term->complexity(); + } + return result; + } + scalar Activated::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - if (not _activation) throw fl::Exception("[activation error] " - "activation operator needed to activate " + _term->toString(), FL_AT); - return _activation->compute(this->_term->membership(x), _degree); + if (Op::isNaN(x)) return fl::nan; + if (not _term) + throw Exception("[activation error] no term available to activate", FL_AT); + if (not _implication) + throw Exception("[implication error] implication operator needed " + "to activate " + getTerm()->toString(), FL_AT); + return _implication->compute(_term->membership(x), _degree); } std::string Activated::parameters() const { FllExporter exporter; std::ostringstream ss; - ss << Op::str(_degree) << " " << exporter.toString(_activation) << " " - << exporter.toString(_term); + ss << Op::str(getDegree()) << " " << exporter.toString(getImplication()) << " " + << exporter.toString(getTerm()); return ss.str(); } void Activated::configure(const std::string& parameters) { - (void) parameters; + FL_IUNUSED(parameters); } std::string Activated::toString() const { FllExporter exporter; std::ostringstream ss; - ss << exporter.toString(_activation) << "(" - << Op::str(_degree) << "," - << _term->getName() << ")"; + if (getImplication()) { + ss << exporter.toString(getImplication()) << "(" + << Op::str(getDegree()) << "," + << getTerm()->getName() << ")"; + } else { + ss << "(" << Op::str(getDegree()) << "*" //"\u2297: (*)" + << getTerm()->getName() << ")"; + } return ss.str(); } @@ -85,12 +95,12 @@ namespace fl { return this->_degree; } - void Activated::setActivation(const TNorm* activation) { - this->_activation = activation; + void Activated::setImplication(const TNorm* implication) { + this->_implication = implication; } - const TNorm* Activated::getActivation() const { - return this->_activation; + const TNorm* Activated::getImplication() const { + return this->_implication; } Activated* Activated::clone() const { diff --git a/fuzzylite/src/term/Aggregated.cpp b/fuzzylite/src/term/Aggregated.cpp new file mode 100644 index 0000000..d1e2c89 --- /dev/null +++ b/fuzzylite/src/term/Aggregated.cpp @@ -0,0 +1,247 @@ +/* + 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 FuzzyLite License included with the software. + + 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/Aggregated.h" + +#include "fl/imex/FllExporter.h" +#include "fl/norm/s/Maximum.h" + +namespace fl { + + Aggregated::Aggregated(const std::string& name, scalar minimum, scalar maximum, + SNorm* aggregation) + : Term(name), _minimum(minimum), _maximum(maximum), _aggregation(aggregation) { } + + Aggregated::Aggregated(const Aggregated& other) : Term(other) { + copyFrom(other); + } + + Aggregated& Aggregated::operator=(const Aggregated& other) { + if (this != &other) { + clear(); + _aggregation.reset(fl::null); + + Term::operator=(other); + copyFrom(other); + } + return *this; + } + + Aggregated::~Aggregated() { } + + void Aggregated::copyFrom(const Aggregated& source) { + _minimum = source._minimum; + _maximum = source._maximum; + + if (source._aggregation.get()) + _aggregation.reset(source._aggregation->clone()); + + for (std::size_t i = 0; i < source._terms.size(); ++i) { + _terms.push_back(source._terms.at(i)); + } + } + + std::string Aggregated::className() const { + return "Aggregated"; + } + + Complexity Aggregated::complexity() const { + return complexityOfMembership(); + } + + Complexity Aggregated::complexityOfMembership() const { + Complexity result; + result.comparison(3); + if (_aggregation.get()) { + result += _aggregation->complexity().multiply(scalar(_terms.size())); + } + for (std::size_t i = 0; i < _terms.size(); ++i) { + result += _terms.at(i).complexity(); + } + return result; + } + + scalar Aggregated::membership(scalar x) const { + if (Op::isNaN(x)) return fl::nan; + if (not (_terms.empty() or _aggregation.get())) { //Exception for IntegralDefuzzifiers + throw Exception("[aggregation error] " + "aggregation operator needed to aggregate variable " + "<" + getName() + ">", FL_AT); + } + scalar mu = 0.0; + for (std::size_t i = 0; i < _terms.size(); ++i) { + mu = _aggregation->compute(mu, _terms.at(i).membership(x)); + } + return mu; + } + + Complexity Aggregated::complexityOfActivationDegree() const { + Complexity result; + result.comparison(2); + if (_aggregation.get()) { + result += _aggregation->complexity(); + } else result.arithmetic(1); + result.multiply(scalar(_terms.size())); + return result; + } + + scalar Aggregated::activationDegree(const Term* forTerm) const { + scalar result = 0.0; + for (std::size_t i = 0; i < _terms.size(); ++i) { + const Activated& activatedTerm = _terms.at(i); + if (activatedTerm.getTerm() == forTerm) { + if (_aggregation.get()) + result = _aggregation->compute(result, activatedTerm.getDegree()); + else + result += activatedTerm.getDegree(); //Default for WeightDefuzzifier + } + } + return result; + } + + const Activated* Aggregated::highestActivatedTerm() const { + const Activated* maximumTerm = fl::null; + scalar maximumActivation = -fl::inf; + for (std::size_t i = 0; i < _terms.size(); ++i) { + const Activated& activated = _terms.at(i); + if (Op::isGt(activated.getDegree(), maximumActivation)) { + maximumActivation = activated.getDegree(); + maximumTerm = &activated; + } + } + return maximumTerm; + } + + std::string Aggregated::parameters() const { + FllExporter exporter; + std::ostringstream ss; + ss << exporter.toString(getAggregation()); + ss << " " << Op::str(getMinimum()) << " " << Op::str(getMaximum()) << " "; + for (std::size_t i = 0; i < terms().size(); ++i) { + ss << " " << exporter.toString(&terms().at(i)); + } + return ss.str(); + } + + void Aggregated::configure(const std::string& parameters) { + FL_IUNUSED(parameters); + } + + Aggregated* Aggregated::clone() const { + return new Aggregated(*this); + } + + std::string Aggregated::toString() const { + std::vector<std::string> aggregate; + for (std::size_t i = 0; i < terms().size(); ++i) { + aggregate.push_back(terms().at(i).toString()); + } + FllExporter exporter; + std::ostringstream ss; + if (getAggregation()) { + ss << getName() << ": " << className() << " " + << exporter.toString(getAggregation()) << "[" + << Op::join(aggregate, ",") << "]"; + } else { + ss << getName() << ": " << className() << " " << "[" + << Op::join(aggregate, "+") << "]"; //\u2295: (+) + } + return ss.str(); + } + + void Aggregated::setMinimum(scalar minimum) { + this->_minimum = minimum; + } + + scalar Aggregated::getMinimum() const { + return this->_minimum; + } + + void Aggregated::setMaximum(scalar maximum) { + this->_maximum = maximum; + } + + scalar Aggregated::getMaximum() const { + return this->_maximum; + } + + void Aggregated::setRange(scalar minimum, scalar maximum) { + setMinimum(minimum); + setMaximum(maximum); + } + + scalar Aggregated::range() const { + return getMaximum() - getMinimum(); + } + + void Aggregated::setAggregation(SNorm* aggregation) { + this->_aggregation.reset(aggregation); + } + + SNorm* Aggregated::getAggregation() const { + return this->_aggregation.get(); + } + + /** + * Operations for std::vector _terms + */ + + + void Aggregated::addTerm(const Term* term, scalar degree, const TNorm* implication) { + _terms.push_back(Activated(term, degree, implication)); + FL_DBG("Aggregating " << _terms.back().toString()); + } + + void Aggregated::addTerm(const Activated& term) { + _terms.push_back(term); + FL_DBG("Aggregating " << _terms.back().toString()); + } + + const Activated& Aggregated::removeTerm(std::size_t index) { + const Activated& term = _terms.at(index); + _terms.erase(_terms.begin() + index); + return term; + } + + void Aggregated::clear() { + _terms.clear(); + } + + const Activated& Aggregated::getTerm(std::size_t index) const { + return _terms.at(index); + } + + void Aggregated::setTerms(const std::vector<Activated>& terms) { + this->_terms = terms; + } + + const std::vector<Activated>& Aggregated::terms() const { + return this->_terms; + } + + std::vector<Activated>& Aggregated::terms() { + return this->_terms; + } + + std::size_t Aggregated::numberOfTerms() const { + return _terms.size(); + } + + bool Aggregated::isEmpty() const { + return _terms.empty(); + } + +} diff --git a/fuzzylite/src/term/Bell.cpp b/fuzzylite/src/term/Bell.cpp index fc0b215..bd63753 100644 --- a/fuzzylite/src/term/Bell.cpp +++ b/fuzzylite/src/term/Bell.cpp @@ -1,25 +1,17 @@ /* - 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/Bell.h" @@ -27,24 +19,26 @@ namespace fl { Bell::Bell(const std::string& name, scalar center, scalar width, scalar slope, scalar height) - : Term(name, height), _center(center), _width(width), _slope(slope) { - } + : Term(name, height), _center(center), _width(width), _slope(slope) { } - Bell::~Bell() { - } + Bell::~Bell() { } std::string Bell::className() const { return "Bell"; } + Complexity Bell::complexity() const { + return Complexity().comparison(1).arithmetic(6).function(2); + } + scalar Bell::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - return _height * (1.0 / (1.0 + std::pow(std::abs((x - _center) / _width), 2 * _slope))); + if (Op::isNaN(x)) return fl::nan; + return Term::_height * (1.0 / (1.0 + std::pow(std::abs((x - _center) / _width), 2.0 * _slope))); } std::string Bell::parameters() const { return Op::join(3, " ", _center, _width, _slope) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Bell::configure(const std::string& parameters) { @@ -55,7 +49,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setCenter(Op::toScalar(values.at(0))); setWidth(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Binary.cpp b/fuzzylite/src/term/Binary.cpp new file mode 100644 index 0000000..368530b --- /dev/null +++ b/fuzzylite/src/term/Binary.cpp @@ -0,0 +1,96 @@ +/* + 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 FuzzyLite License included with the software. + + 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/Binary.h" + +namespace fl { + + Binary::Binary(const std::string& name, scalar start, scalar direction, scalar height) + : Term(name, height), _start(start), _direction(direction) { } + + Binary::~Binary() { } + + std::string Binary::className() const { + return "Binary"; + } + + Complexity Binary::complexity() const { + return Complexity().comparison(5).arithmetic(1); + } + + scalar Binary::membership(scalar x) const { + if (Op::isNaN(x)) return fl::nan; + if (_direction > _start and Op::isGE(x, _start)) { + return Term::_height * 1.0; + } + if (_direction < _start and Op::isLE(x, _start)) { + return Term::_height * 1.0; + } + return Term::_height * 0.0; + } + + std::string Binary::parameters() const { + return Op::join(2, " ", _start, _direction) + + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); + } + + void Binary::configure(const std::string& parameters) { + if (parameters.empty()) return; + std::vector<std::string> values = Op::split(parameters, " "); + std::size_t required = 2; + if (values.size() < required) { + std::ostringstream ex; + ex << "[configuration error] term <" << className() << ">" + << " requires <" << required << "> parameters"; + throw Exception(ex.str(), FL_AT); + } + setStart(Op::toScalar(values.at(0))); + setDirection(Op::toScalar(values.at(1))); + if (values.size() > required) + setHeight(Op::toScalar(values.at(required))); + } + + void Binary::setStart(scalar minimum) { + this->_start = minimum; + } + + scalar Binary::getStart() const { + return this->_start; + } + + void Binary::setDirection(scalar direction) { + this->_direction = direction; + } + + scalar Binary::getDirection() const { + return this->_direction; + } + + Binary::Direction Binary::direction() const { + if (this->_direction > _start) return Positive; + if (this->_direction < _start) return Negative; + return Undefined; + } + + Binary* Binary::clone() const { + return new Binary(*this); + } + + Term* Binary::constructor() { + return new Binary; + } + +} diff --git a/fuzzylite/src/term/Concave.cpp b/fuzzylite/src/term/Concave.cpp index 37679ae..c585edc 100644 --- a/fuzzylite/src/term/Concave.cpp +++ b/fuzzylite/src/term/Concave.cpp @@ -1,25 +1,17 @@ /* - 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/Concave.h" @@ -27,35 +19,47 @@ namespace fl { Concave::Concave(const std::string& name, scalar inflection, scalar end, scalar height) - : Term(name, height), _inflection(inflection), _end(end) { - - } + : Term(name, height), _inflection(inflection), _end(end) { } - Concave::~Concave() { - - } + Concave::~Concave() { } std::string Concave::className() const { return "Concave"; } + Complexity Concave::complexity() const { + return Complexity().comparison(1 + 3).arithmetic(1 + 5); + } + scalar Concave::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - if (fl::Op::isLE(_inflection, _end)) { //Concave increasing - if (fl::Op::isLt(x, _end)) { - return _height * (_end - _inflection) / (2 * _end - _inflection - x); + if (Op::isNaN(x)) return fl::nan; + if (Op::isLE(_inflection, _end)) { //Concave increasing + if (Op::isLt(x, _end)) { + return Term::_height * (_end - _inflection) / (2.0 * _end - _inflection - x); } } else { //Concave decreasing - if (fl::Op::isGt(x, _end)) { - return _height * (_inflection - _end) / (_inflection - 2 * _end + x); + if (Op::isGt(x, _end)) { + return Term::_height * (_inflection - _end) / (_inflection - 2.0 * _end + x); } } - return _height * 1.0; + return Term::_height * 1.0; + } + + scalar Concave::tsukamoto(scalar activationDegree, scalar minimum, scalar maximum) const { + FL_IUNUSED(minimum); + FL_IUNUSED(maximum); + scalar i = _inflection; + scalar e = _end; + return (i - e) / membership(activationDegree) + 2 * e - i; + } + + bool Concave::isMonotonic() const { + return true; } std::string Concave::parameters() const { return Op::join(2, " ", _inflection, _end) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } @@ -67,7 +71,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setInflection(Op::toScalar(values.at(0))); setEnd(Op::toScalar(values.at(1))); @@ -100,8 +104,4 @@ namespace fl { return new Concave; } - - - - } diff --git a/fuzzylite/src/term/Constant.cpp b/fuzzylite/src/term/Constant.cpp index d52f8ec..7c0422e 100644 --- a/fuzzylite/src/term/Constant.cpp +++ b/fuzzylite/src/term/Constant.cpp @@ -1,25 +1,17 @@ /* - 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/Constant.h" @@ -27,18 +19,20 @@ namespace fl { Constant::Constant(const std::string& name, scalar value) - : Term(name), _value(value) { - } + : Term(name), _value(value) { } - Constant::~Constant() { - } + Constant::~Constant() { } std::string Constant::className() const { return "Constant"; } + Complexity Constant::complexity() const { + return Complexity(); + } + scalar Constant::membership(scalar x) const { - (void) x; + FL_IUNUSED(x); return this->_value; } diff --git a/fuzzylite/src/term/Cosine.cpp b/fuzzylite/src/term/Cosine.cpp index a1402ad..78162ea 100644 --- a/fuzzylite/src/term/Cosine.cpp +++ b/fuzzylite/src/term/Cosine.cpp @@ -1,25 +1,17 @@ /* - 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/Cosine.h" @@ -27,13 +19,9 @@ namespace fl { Cosine::Cosine(const std::string& name, scalar center, scalar width, scalar height) - : Term(name, height), _center(center), _width(width) { + : Term(name, height), _center(center), _width(width) { } - } - - Cosine::~Cosine() { - - } + Cosine::~Cosine() { } std::string Cosine::className() const { return "Cosine"; @@ -41,7 +29,7 @@ namespace fl { std::string Cosine::parameters() const { return Op::join(2, " ", _center, _width) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Cosine::configure(const std::string& parameters) { @@ -52,7 +40,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setCenter(Op::toScalar(values.at(0))); setWidth(Op::toScalar(values.at(1))); @@ -61,13 +49,17 @@ namespace fl { } + Complexity Cosine::complexity() const { + return Complexity().comparison(3).arithmetic(4 + 1 + 7).function(2); + } + scalar Cosine::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - if (fl::Op::isLt(x, _center - _width / 2.0) - or fl::Op::isGt(x, _center + _width / 2.0)) - return _height * 0.0; + if (Op::isNaN(x)) return fl::nan; + if (Op::isLt(x, _center - 0.5 * _width) + or Op::isGt(x, _center + 0.5 * _width)) + return Term::_height * 0.0; const scalar pi = 4.0 * std::atan(1.0); - return _height * (0.5 * (1.0 + std::cos(2.0 / _width * pi * (x - _center)))); + return Term::_height * (0.5 * (1.0 + std::cos(2.0 / _width * pi * (x - _center)))); } void Cosine::setCenter(scalar center) { @@ -93,4 +85,5 @@ namespace fl { Term* Cosine::constructor() { return new Cosine; } + } diff --git a/fuzzylite/src/term/Discrete.cpp b/fuzzylite/src/term/Discrete.cpp index 212ada2..9951867 100644 --- a/fuzzylite/src/term/Discrete.cpp +++ b/fuzzylite/src/term/Discrete.cpp @@ -1,88 +1,89 @@ /* - 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/Discrete.h" -#include <cstdarg> - namespace fl { Discrete::Discrete(const std::string& name, const std::vector<Pair>& xy, scalar height) - : Term(name, height), _xy(xy) { - } + : Term(name, height), _xy(xy) { } - Discrete::~Discrete() { - } + Discrete::~Discrete() { } std::string Discrete::className() const { return "Discrete"; } - scalar Discrete::membership(scalar _x_) const { - if (fl::Op::isNaN(_x_)) return fl::nan; + bool compare(const Discrete::Pair& a, const Discrete::Pair& b) { + return a.first < b.first; + } + + void Discrete::sort(std::vector<Pair>& pairs) { + std::sort(pairs.begin(), pairs.end(), compare); + } + + void Discrete::sort() { + std::sort(_xy.begin(), _xy.end(), compare); + } + + Complexity Discrete::complexity() const { + return Complexity().comparison(1 + 4).arithmetic(1 + 1 + 1).function(1) + .function(2 * std::log(scalar(_xy.size()))); + } + + scalar Discrete::membership(scalar x) const { + if (Op::isNaN(x)) return fl::nan; if (_xy.empty()) - throw fl::Exception("[discrete error] term is empty", FL_AT); + throw Exception("[discrete error] term is empty", FL_AT); /* ______________________ - * / \ - * / \ - * ____________/ \____________ - * x[0] x[n-1] + / \ + / \ + ____________/ \____________ + x[0] x[n-1] */ + if (Op::isLE(x, _xy.front().first)) + return Term::_height * _xy.front().second; + if (Op::isGE(x, _xy.back().first)) + return Term::_height * _xy.back().second; - if (fl::Op::isLE(_x_, _xy.front().first)) return _height * _xy.front().second; - if (fl::Op::isGE(_x_, _xy.back().first)) return _height * _xy.back().second; - - int lower = -1, upper = -1; + const Pair value(x, fl::nan); + typedef std::vector<Discrete::Pair>::const_iterator Bound; + //std::lower_bound finds the first number greater than or equal to x + Bound lowerBound(std::lower_bound(_xy.begin(), _xy.end(), value, compare)); - for (std::size_t i = 0; i < _xy.size(); ++i) { - if (Op::isEq(_xy.at(i).first, _x_)) return _height * _xy.at(i).second; - //approximate on the left - if (Op::isLt(_xy.at(i).first, _x_)) { - lower = i; - } - //get the immediate next one on the right - if (Op::isGt(_xy.at(i).first, _x_)) { - upper = i; - break; - } + //if the lower bound is equal to x + if (Op::isEq(x, lowerBound->first)) { + return Term::_height * lowerBound->second; } - if (upper < 0) upper = _xy.size() - 1; - if (lower < 0) lower = 0; - - return _height * Op::scale(_x_, _xy.at(lower).first, _xy.at(upper).first, - _xy.at(lower).second, _xy.at(upper).second); + //find the upper bound starting from a copy of lowerBound + const Bound upperBound(std::upper_bound(_xy.begin(), _xy.end(), value, compare)); + --lowerBound; //One arithmetic + return Term::_height * Op::scale(x, lowerBound->first, upperBound->first, + lowerBound->second, upperBound->second); } std::string Discrete::parameters() const { std::ostringstream ss; for (std::size_t i = 0; i < _xy.size(); ++i) { - ss << fl::Op::str(_xy.at(i).first) << " " << fl::Op::str(_xy.at(i).second); + ss << Op::str(_xy.at(i).first) << " " << Op::str(_xy.at(i).second); if (i + 1 < _xy.size()) ss << " "; } - if (not Op::isEq(_height, 1.0)) ss << " " << Op::str(_height); + if (not Op::isEq(getHeight(), 1.0)) ss << " " << Op::str(getHeight()); return ss.str(); } @@ -102,36 +103,8 @@ namespace fl { this->_xy = toPairs(values); } - template <typename T> - Discrete* Discrete::create(const std::string& name, int argc, - T x1, T y1, ...) { // throw (fl::Exception) { - std::vector<scalar> xy(argc); - xy.at(0) = x1; - xy.at(1) = y1; - va_list args; - va_start(args, y1); - for (int i = 2; i < argc; ++i) { - xy.at(i) = (scalar) va_arg(args, T); - } - va_end(args); - - FL_unique_ptr<Discrete> result(new Discrete(name)); - if (xy.size() % 2 != 0) { - result->setHeight(xy.back()); - xy.pop_back(); - } - result->setXY(toPairs(xy)); - return result.release(); - } - - template FL_API Discrete* Discrete::create(const std::string& name, int argc, - double x1, double y1, ...); // throw (fl::Exception); - //double, not scalar because variadic promotes floats to double - template FL_API Discrete* Discrete::create(const std::string& name, int argc, - int x1, int y1, ...); // throw (fl::Exception); - - void Discrete::setXY(const std::vector<Pair>& pairs) { - this->_xy = pairs; + void Discrete::setXY(const std::vector<Pair>& xy) { + this->_xy = xy; } const std::vector<Discrete::Pair>& Discrete::xy() const { @@ -142,19 +115,51 @@ namespace fl { return this->_xy; } - const Discrete::Pair& Discrete::xy(int index) const { + const Discrete::Pair& Discrete::xy(std::size_t index) const { return this->_xy.at(index); } - Discrete::Pair& Discrete::xy(int index) { + Discrete::Pair& Discrete::xy(std::size_t index) { return this->_xy.at(index); } + std::vector<scalar> Discrete::x() const { + std::vector<scalar> result(_xy.size()); + for (std::size_t i = 0; i < result.size(); ++i) { + result.at(i) = _xy.at(i).first; + } + return result; + } + + std::vector<scalar> Discrete::y() const { + std::vector<scalar> result(_xy.size()); + for (std::size_t i = 0; i < result.size(); ++i) { + result.at(i) = _xy.at(i).second; + } + return result; + } + + scalar Discrete::x(std::size_t index) const { + return _xy.at(index).first; + } + + scalar& Discrete::x(std::size_t index) { + return _xy.at(index).first; + } + + scalar Discrete::y(std::size_t index) const { + return _xy.at(index).second; + } + + scalar& Discrete::y(std::size_t index) { + return _xy.at(index).second; + } + std::vector<Discrete::Pair> Discrete::toPairs(const std::vector<scalar>& xy) { if (xy.size() % 2 != 0) { std::ostringstream os; os << "[discrete error] missing value in set of pairs (|xy|=" << xy.size() << ")"; - throw fl::Exception(os.str(), FL_AT); + throw Exception(os.str(), FL_AT); } std::vector<Pair> result((xy.size() + 1) / 2); @@ -188,16 +193,32 @@ namespace fl { return result; } - std::string Discrete::formatXY(const std::vector<Pair>& xy, const std::string& prefix, const std::string& innerSeparator, const std::string& postfix, const std::string& outerSeparator) { + std::string Discrete::formatXY(const std::vector<Pair>& xy, const std::string& prefix, + const std::string& innerSeparator, const std::string& suffix, const std::string& outerSeparator) { std::ostringstream os; for (std::size_t i = 0; i < xy.size(); ++i) { - os << prefix << fl::Op::str(xy.at(i).first) << innerSeparator - << fl::Op::str(xy.at(i).second) << postfix; + os << prefix << Op::str(xy.at(i).first) << innerSeparator + << Op::str(xy.at(i).second) << suffix; if (i + 1 < xy.size()) os << outerSeparator; } return os.str(); } + Discrete* Discrete::discretize(const Term* term, scalar start, scalar end, int resolution, + bool boundedMembershipFunction) { + FL_unique_ptr<Discrete> result(new Discrete(term->getName())); + scalar dx = (end - start) / resolution; + scalar x, y; + for (int i = 0; i <= resolution; ++i) { + x = start + i * dx; + y = term->membership(x); + if (boundedMembershipFunction) + y = Op::bound(y, scalar(0.0), scalar(1.0)); + result->xy().push_back(Discrete::Pair(x, y)); + } + return result.release(); + } + Discrete* Discrete::clone() const { return new Discrete(*this); } @@ -206,5 +227,4 @@ namespace fl { return new Discrete; } - } 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()); - } - } - } diff --git a/fuzzylite/src/term/Gaussian.cpp b/fuzzylite/src/term/Gaussian.cpp index 5b709f9..8413b59 100644 --- a/fuzzylite/src/term/Gaussian.cpp +++ b/fuzzylite/src/term/Gaussian.cpp @@ -1,25 +1,17 @@ /* - 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/Gaussian.h" @@ -28,24 +20,26 @@ namespace fl { Gaussian::Gaussian(const std::string& name, scalar mean, scalar standardDeviation, scalar height) - : Term(name, height), _mean(mean), _standardDeviation(standardDeviation) { - } + : Term(name, height), _mean(mean), _standardDeviation(standardDeviation) { } - Gaussian::~Gaussian() { - } + Gaussian::~Gaussian() { } std::string Gaussian::className() const { return "Gaussian"; } + Complexity Gaussian::complexity() const { + return Complexity().comparison(1).arithmetic(7).function(1); + } + scalar Gaussian::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - return _height * std::exp((-(x - _mean) * (x - _mean)) / (2 * _standardDeviation * _standardDeviation)); + if (Op::isNaN(x)) return fl::nan; + return Term::_height * std::exp((-(x - _mean) * (x - _mean)) / (2.0 * _standardDeviation * _standardDeviation)); } std::string Gaussian::parameters() const { return Op::join(2, " ", _mean, _standardDeviation) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Gaussian::configure(const std::string& parameters) { @@ -56,7 +50,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setMean(Op::toScalar(values.at(0))); setStandardDeviation(Op::toScalar(values.at(1))); @@ -64,16 +58,16 @@ namespace fl { setHeight(Op::toScalar(values.at(required))); } - void Gaussian::setMean(scalar c) { - this->_mean = c; + void Gaussian::setMean(scalar mean) { + this->_mean = mean; } scalar Gaussian::getMean() const { return this->_mean; } - void Gaussian::setStandardDeviation(scalar sigma) { - this->_standardDeviation = sigma; + void Gaussian::setStandardDeviation(scalar standardDeviation) { + this->_standardDeviation = standardDeviation; } scalar Gaussian::getStandardDeviation() const { diff --git a/fuzzylite/src/term/GaussianProduct.cpp b/fuzzylite/src/term/GaussianProduct.cpp index b9652e1..791790c 100644 --- a/fuzzylite/src/term/GaussianProduct.cpp +++ b/fuzzylite/src/term/GaussianProduct.cpp @@ -1,25 +1,17 @@ /* - 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/GaussianProduct.h" @@ -30,32 +22,37 @@ namespace fl { scalar meanA, scalar standardDeviationA, scalar meanB, scalar standardDeviationB, scalar height) : Term(name, height), _meanA(meanA), _standardDeviationA(standardDeviationA), - _meanB(meanB), _standardDeviationB(standardDeviationB) { - } + _meanB(meanB), _standardDeviationB(standardDeviationB) { } - GaussianProduct::~GaussianProduct() { - } + GaussianProduct::~GaussianProduct() { } std::string GaussianProduct::className() const { return "GaussianProduct"; } + Complexity GaussianProduct::complexity() const { + return Complexity().comparison(1 + 2).arithmetic(9 + 9 + 2).function(2); + } + scalar GaussianProduct::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - bool xLEa = fl::Op::isLE(x, _meanA); - scalar a = (1 - xLEa) + xLEa * std::exp( - (-(x - _meanA) * (x - _meanA)) / (2 * _standardDeviationA * _standardDeviationA) - ); - bool xGEb = fl::Op::isGE(x, _meanB); - scalar b = (1 - xGEb) + xGEb * std::exp( - (-(x - _meanB) * (x - _meanB)) / (2 * _standardDeviationB * _standardDeviationB) - ); - return _height * a * b; + if (Op::isNaN(x)) return fl::nan; + + scalar a = 1.0, b = 1.0; + if (Op::isLt(x, _meanA)) { + a = std::exp((-(x - _meanA) * (x - _meanA)) / + (2.0 * _standardDeviationA * _standardDeviationA)); + } + if (Op::isGt(x, _meanB)) { + b = std::exp((-(x - _meanB) * (x - _meanB)) / + (2.0 * _standardDeviationB * _standardDeviationB)); + } + + return Term::_height * a * b; } std::string GaussianProduct::parameters() const { return Op::join(4, " ", _meanA, _standardDeviationA, _meanB, _standardDeviationB) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void GaussianProduct::configure(const std::string& parameters) { @@ -66,7 +63,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setMeanA(Op::toScalar(values.at(0))); setStandardDeviationA(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Linear.cpp b/fuzzylite/src/term/Linear.cpp index 4111a00..e6efb43 100644 --- a/fuzzylite/src/term/Linear.cpp +++ b/fuzzylite/src/term/Linear.cpp @@ -1,72 +1,72 @@ /* - 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/Linear.h" -#include "fl/Engine.h" #include "fl/variable/InputVariable.h" -#include <cstdarg> - namespace fl { Linear::Linear(const std::string& name, const std::vector<scalar>& coefficients, const Engine* engine) - : Term(name), _coefficients(coefficients), _engine(engine) { - } + : Term(name), _coefficients(coefficients), _engine(engine) { } - Linear::~Linear() { - } + Linear::~Linear() { } std::string Linear::className() const { return "Linear"; } + Complexity Linear::complexity() const { + Complexity result; + result.comparison(1 + 1); + if (_engine) { + result.arithmetic(scalar(_engine->variables().size())); + result.comparison(scalar(_engine->variables().size())); //if (i < coefficients) + } + return result; + } + scalar Linear::membership(scalar x) const { - (void) x; - if (not _engine) throw fl::Exception("[linear error] term <" + getName() + "> " + FL_IUNUSED(x); + if (not _engine) + throw Exception("[linear error] term <" + getName() + "> " "is missing a reference to the engine", FL_AT); scalar result = 0.0; - for (std::size_t i = 0; i < _engine->inputVariables().size(); ++i) { - if (i < _coefficients.size()) - result += _coefficients.at(i) * _engine->inputVariables().at(i)->getInputValue(); + const std::size_t numberOfInputVariables = _engine->inputVariables().size(); + const std::size_t numberOfCoefficients = _coefficients.size(); + for (std::size_t i = 0; i < numberOfInputVariables; ++i) { + if (i < numberOfCoefficients) + result += _coefficients.at(i) * _engine->inputVariables().at(i)->getValue(); } - if (_coefficients.size() > _engine->inputVariables().size()) { + if (numberOfCoefficients > numberOfInputVariables) { result += _coefficients.back(); } return result; } - void Linear::set(const std::vector<scalar>& coeffs, const Engine* engine) { - setCoefficients(coeffs); + void Linear::set(const std::vector<scalar>& coefficients, const Engine* engine) { + setCoefficients(coefficients); setEngine(engine); } - void Linear::setCoefficients(const std::vector<scalar>& coeffs) { - this->_coefficients = coeffs; + void Linear::setCoefficients(const std::vector<scalar>& coefficients) { + this->_coefficients = coefficients; } const std::vector<scalar>& Linear::coefficients() const { @@ -90,6 +90,7 @@ namespace fl { } void Linear::configure(const std::string& parameters) { + this->_coefficients.clear(); if (parameters.empty()) return; std::vector<std::string> strValues = Op::split(parameters, " "); std::vector<scalar> values; @@ -103,33 +104,12 @@ namespace fl { return new Linear(*this); } - Term* Linear::constructor() { - return new Linear; + void Linear::updateReference(const Engine* engine) { + setEngine(engine); } - template <typename T> - Linear* Linear::create(const std::string& name, - const Engine* engine, T firstCoefficient, ...) {// throw (fl::Exception) { - if (not engine) throw fl::Exception("[linear error] cannot create term <" + name + "> " - "without a reference to the engine", FL_AT); - std::vector<scalar> coefficients; - coefficients.push_back(firstCoefficient); - - va_list args; - va_start(args, firstCoefficient); - for (std::size_t i = 0; i < engine->inputVariables().size(); ++i) { - coefficients.push_back((scalar) va_arg(args, T)); - } - va_end(args); - - return new Linear(name, coefficients, engine); + Term* Linear::constructor() { + return new Linear; } - template FL_API Linear* Linear::create(const std::string& name, - const Engine* engine, - double firstCoefficient, ...); - - template FL_API Linear* Linear::create(const std::string& name, - const Engine* engine, - int firstCoefficient, ...); } diff --git a/fuzzylite/src/term/PiShape.cpp b/fuzzylite/src/term/PiShape.cpp index ba3e99f..6c91f54 100644 --- a/fuzzylite/src/term/PiShape.cpp +++ b/fuzzylite/src/term/PiShape.cpp @@ -1,25 +1,17 @@ /* - 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/PiShape.h" @@ -29,47 +21,45 @@ namespace fl { PiShape::PiShape(const std::string& name, scalar bottomLeft, scalar topLeft, scalar topRight, scalar bottomRight, scalar height) : Term(name, height), _bottomLeft(bottomLeft), _topLeft(topLeft), - _topRight(topRight), _bottomRight(bottomRight) { - } + _topRight(topRight), _bottomRight(bottomRight) { } - PiShape::~PiShape() { - } + PiShape::~PiShape() { } std::string PiShape::className() const { return "PiShape"; } - scalar PiShape::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - //from Octave smf.m - scalar a_b_ave = (_bottomLeft + _topLeft) / 2.0; - scalar b_minus_a = _topLeft - _bottomLeft; - scalar c_d_ave = (_topRight + _bottomRight) / 2.0; - scalar d_minus_c = _bottomRight - _topRight; - - if (Op::isLE(x, _bottomLeft)) return _height * 0.0; - - if (Op::isLE(x, a_b_ave)) - return _height * (2.0 * std::pow((x - _bottomLeft) / b_minus_a, 2)); - - if (Op::isLt(x, _topLeft)) - return _height * (1.0 - 2.0 * std::pow((x - _topLeft) / b_minus_a, 2)); + Complexity PiShape::complexity() const { + return Complexity().comparison(1 + 6).arithmetic(1 + 5 + 5).function(1 + 1); + } + scalar PiShape::membership(scalar x) const { + if (Op::isNaN(x)) return fl::nan; + + scalar sshape; + if (Op::isLE(x, _bottomLeft)) + sshape = 0.0; + else if (Op::isLE(x, 0.5 * (_bottomLeft + _topLeft))) + sshape = 2.0 * std::pow((x - _bottomLeft) / (_topLeft - _bottomLeft), 2); + else if (Op::isLt(x, _topLeft)) + sshape = 1.0 - 2.0 * std::pow((x - _topLeft) / (_topLeft - _bottomLeft), 2); + else sshape = 1.0; + + scalar zshape; if (Op::isLE(x, _topRight)) - return _height * 1.0; - - if (Op::isLE(x, c_d_ave)) - return _height * (1.0 - 2.0 * std::pow((x - _topRight) / d_minus_c, 2)); - - if (Op::isLt(x, _bottomRight)) - return _height * (2.0 * std::pow((x - _bottomRight) / d_minus_c, 2)); + zshape = 1.0; + else if (Op::isLE(x, 0.5 * (_topRight + _bottomRight))) + zshape = 1.0 - 2.0 * std::pow((x - _topRight) / (_bottomRight - _topRight), 2); + else if (Op::isLt(x, _bottomRight)) + zshape = 2.0 * std::pow((x - _bottomRight) / (_bottomRight - _topRight), 2); + else zshape = 0.0; - return _height * 0.0; + return Term::_height * sshape * zshape; } std::string PiShape::parameters() const { return Op::join(4, " ", _bottomLeft, _topLeft, _topRight, _bottomRight) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void PiShape::configure(const std::string& parameters) { @@ -80,7 +70,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setBottomLeft(Op::toScalar(values.at(0))); setTopLeft(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Ramp.cpp b/fuzzylite/src/term/Ramp.cpp index f224045..d29233d 100644 --- a/fuzzylite/src/term/Ramp.cpp +++ b/fuzzylite/src/term/Ramp.cpp @@ -1,25 +1,17 @@ /* - 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/Ramp.h" @@ -27,35 +19,52 @@ namespace fl { Ramp::Ramp(const std::string& name, scalar start, scalar end, scalar height) - : Term(name, height), _start(start), _end(end) { - } + : Term(name, height), _start(start), _end(end) { } - Ramp::~Ramp() { - } + Ramp::~Ramp() { } std::string Ramp::className() const { return "Ramp"; } + Complexity Ramp::complexity() const { + return Complexity().comparison(1 + 4).arithmetic(1 + 3); + } + scalar Ramp::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; + if (Op::isNaN(x)) return fl::nan; - if (Op::isEq(_start, _end)) return _height * 0.0; + if (Op::isEq(_start, _end)) + return Term::_height * 0.0; if (Op::isLt(_start, _end)) { - if (Op::isLE(x, _start)) return _height * 0.0; - if (Op::isGE(x, _end)) return _height * 1.0; - return _height * (x - _start) / (_end - _start); + if (Op::isLE(x, _start)) + return Term::_height * 0.0; + if (Op::isGE(x, _end)) + return Term::_height * 1.0; + return Term::_height * (x - _start) / (_end - _start); } else { - if (Op::isGE(x, _start)) return _height * 0.0; - if (Op::isLE(x, _end)) return _height * 1.0; - return _height * (_start - x) / (_start - _end); + if (Op::isGE(x, _start)) + return Term::_height * 0.0; + if (Op::isLE(x, _end)) + return Term::_height * 1.0; + return Term::_height * (_start - x) / (_start - _end); } } + scalar Ramp::tsukamoto(scalar activationDegree, scalar minimum, scalar maximum) const { + FL_IUNUSED(minimum); + FL_IUNUSED(maximum); + return Op::scale(activationDegree, 0, 1, _start, _end); + } + + bool Ramp::isMonotonic() const { + return true; + } + std::string Ramp::parameters() const { return Op::join(2, " ", _start, _end) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Ramp::configure(const std::string& parameters) { @@ -66,7 +75,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setStart(Op::toScalar(values.at(0))); setEnd(Op::toScalar(values.at(1))); @@ -92,11 +101,11 @@ namespace fl { Ramp::Direction Ramp::direction() const { scalar range = this->_end - this->_start; - if (not fl::Op::isFinite(range) or fl::Op::isEq(range, 0.0)) return ZERO; + if (not Op::isFinite(range) or Op::isEq(range, 0.0)) return Zero; - if (fl::Op::isGt(range, 0.0)) return POSITIVE; + if (Op::isGt(range, 0.0)) return Positive; - return NEGATIVE; + return Negative; } Ramp* Ramp::clone() const { diff --git a/fuzzylite/src/term/Rectangle.cpp b/fuzzylite/src/term/Rectangle.cpp index 363b15d..4f975d0 100644 --- a/fuzzylite/src/term/Rectangle.cpp +++ b/fuzzylite/src/term/Rectangle.cpp @@ -1,25 +1,17 @@ /* - 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/Rectangle.h" @@ -27,26 +19,28 @@ namespace fl { Rectangle::Rectangle(const std::string& name, scalar start, scalar end, scalar height) - : Term(name, height), _start(start), _end(end) { - } + : Term(name, height), _start(start), _end(end) { } - Rectangle::~Rectangle() { - } + Rectangle::~Rectangle() { } std::string Rectangle::className() const { return "Rectangle"; } + Complexity Rectangle::complexity() const { + return Complexity().comparison(1 + 2).arithmetic(1); + } + scalar Rectangle::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - if (fl::Op::isLt(x, _start) or fl::Op::isGt(x, _end)) - return _height * 0.0; - return _height * 1.0; + if (Op::isNaN(x)) return fl::nan; + if (Op::isGE(x, _start) and Op::isLE(x, _end)) + return Term::_height * 1.0; + return Term::_height * 0.0; } std::string Rectangle::parameters() const { return Op::join(2, " ", _start, _end) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Rectangle::configure(const std::string& parameters) { @@ -57,7 +51,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setStart(Op::toScalar(values.at(0))); setEnd(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/SShape.cpp b/fuzzylite/src/term/SShape.cpp index cd02be1..65dfd2b 100644 --- a/fuzzylite/src/term/SShape.cpp +++ b/fuzzylite/src/term/SShape.cpp @@ -1,25 +1,17 @@ /* - 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/SShape.h" @@ -27,36 +19,58 @@ namespace fl { SShape::SShape(const std::string& name, scalar start, scalar end, scalar height) - : Term(name, height), _start(start), _end(end) { - } + : Term(name, height), _start(start), _end(end) { } - SShape::~SShape() { - } + SShape::~SShape() { } std::string SShape::className() const { return "SShape"; } + Complexity SShape::complexity() const { + return Complexity().comparison(1 + 3).arithmetic(1 + 3 + 4).function(1); + } + scalar SShape::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - //from Octave smf.m - scalar average = (_start + _end) / 2.0; - scalar difference = _end - _start; + if (Op::isNaN(x)) return fl::nan; - if (Op::isLE(x, _start)) return _height * 0.0; + if (Op::isLE(x, _start)) + return Term::_height * 0.0; - if (Op::isLE(x, average)) - return _height * (2.0 * std::pow((x - _start) / difference, 2)); + if (Op::isLE(x, 0.5 * (_start + _end))) + return Term::_height * (2.0 * std::pow((x - _start) / (_end - _start), 2)); if (Op::isLt(x, _end)) - return _height * (1.0 - 2.0 * std::pow((x - _end) / difference, 2)); + return Term::_height * (1.0 - 2.0 * std::pow((x - _end) / (_end - _start), 2)); + + return Term::_height * 1.0; + } + + scalar SShape::tsukamoto(scalar activationDegree, scalar minimum, scalar maximum) const { + FL_IUNUSED(minimum); + FL_IUNUSED(maximum); + + scalar w = activationDegree; + scalar z = fl::nan; + + scalar difference = _end - _start; + scalar a = _start + std::sqrt(0.5 * w * difference * difference); + scalar b = _end + std::sqrt(-0.5 * (w - 1.0) * difference * difference); + if (std::abs(w - membership(a)) < std::abs(w - membership(b))) { + z = a; + } else { + z = b; + } + return z; + } - return _height * 1.0; + bool SShape::isMonotonic() const { + return true; } std::string SShape::parameters() const { return Op::join(2, " ", _start, _end) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void SShape::configure(const std::string& parameters) { @@ -67,7 +81,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setStart(Op::toScalar(values.at(0))); setEnd(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Sigmoid.cpp b/fuzzylite/src/term/Sigmoid.cpp index 77d7f82..fcf165e 100644 --- a/fuzzylite/src/term/Sigmoid.cpp +++ b/fuzzylite/src/term/Sigmoid.cpp @@ -1,25 +1,17 @@ /* - 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/Sigmoid.h" @@ -27,24 +19,58 @@ namespace fl { Sigmoid::Sigmoid(const std::string& name, scalar inflection, scalar slope, scalar height) - : Term(name, height), _inflection(inflection), _slope(slope) { - } + : Term(name, height), _inflection(inflection), _slope(slope) { } - Sigmoid::~Sigmoid() { - } + Sigmoid::~Sigmoid() { } std::string Sigmoid::className() const { return "Sigmoid"; } + Complexity Sigmoid::complexity() const { + return Complexity().comparison(1).arithmetic(1 + 4).function(1); + } + scalar Sigmoid::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - return _height * 1.0 / (1.0 + std::exp(-_slope * (x - _inflection))); + if (Op::isNaN(x)) return fl::nan; + return Term::_height * 1.0 / (1.0 + std::exp(-_slope * (x - _inflection))); + } + + scalar Sigmoid::tsukamoto(scalar activationDegree, + scalar minimum, scalar maximum) const { + + scalar w = activationDegree; + scalar z = fl::nan; + + if (Op::isEq(w, 1.0)) { + if (Op::isGE(_slope, 0.0)) { + z = maximum; + } else { + z = minimum; + } + + } else if (Op::isEq(w, 0.0)) { + if (Op::isGE(_slope, 0.0)) { + z = minimum; + } else { + z = maximum; + } + } else { + scalar a = _slope; + scalar b = _inflection; + z = b + (std::log(1.0 / w - 1.0) / -a); + } + + return z; + } + + bool Sigmoid::isMonotonic() const { + return true; } std::string Sigmoid::parameters() const { return Op::join(2, " ", _inflection, _slope) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Sigmoid::configure(const std::string& parameters) { @@ -55,7 +81,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setInflection(Op::toScalar(values.at(0))); setSlope(Op::toScalar(values.at(1))); @@ -80,11 +106,11 @@ namespace fl { } Sigmoid::Direction Sigmoid::direction() const { - if (not fl::Op::isFinite(_slope) or fl::Op::isEq(_slope, 0.0)) return ZERO; + if (not Op::isFinite(_slope) or Op::isEq(_slope, 0.0)) return Zero; - if (fl::Op::isGt(_slope, 0.0)) return POSITIVE; + if (Op::isGt(_slope, 0.0)) return Positive; - return NEGATIVE; + return Negative; } Sigmoid* Sigmoid::clone() const { diff --git a/fuzzylite/src/term/SigmoidDifference.cpp b/fuzzylite/src/term/SigmoidDifference.cpp index 55a586c..8f7ee80 100644 --- a/fuzzylite/src/term/SigmoidDifference.cpp +++ b/fuzzylite/src/term/SigmoidDifference.cpp @@ -1,25 +1,17 @@ /* - 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/SigmoidDifference.h" @@ -29,27 +21,29 @@ namespace fl { SigmoidDifference::SigmoidDifference(const std::string& name, scalar left, scalar rising, scalar falling, scalar right, scalar height) - : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) { - } + : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) { } - SigmoidDifference::~SigmoidDifference() { - } + SigmoidDifference::~SigmoidDifference() { } std::string SigmoidDifference::className() const { return "SigmoidDifference"; } + Complexity SigmoidDifference::complexity() const { + return Complexity().comparison(1).arithmetic(2 + 4 + 4).function(2 + 1); + } + scalar SigmoidDifference::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; + if (Op::isNaN(x)) return fl::nan; - scalar a = 1.0 / (1 + std::exp(-_rising * (x - _left))); - scalar b = 1.0 / (1 + std::exp(-_falling * (x - _right))); - return _height * std::abs(a - b); + const scalar a = 1.0 / (1.0 + std::exp(-_rising * (x - _left))); + const scalar b = 1.0 / (1.0 + std::exp(-_falling * (x - _right))); + return Term::_height * std::abs(a - b); } std::string SigmoidDifference::parameters() const { return Op::join(4, " ", _left, _rising, _falling, _right) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void SigmoidDifference::configure(const std::string& parameters) { @@ -60,7 +54,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setLeft(Op::toScalar(values.at(0))); setRising(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/SigmoidProduct.cpp b/fuzzylite/src/term/SigmoidProduct.cpp index 9e43ac9..5f35750 100644 --- a/fuzzylite/src/term/SigmoidProduct.cpp +++ b/fuzzylite/src/term/SigmoidProduct.cpp @@ -1,25 +1,17 @@ /* - 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/SigmoidProduct.h" @@ -29,25 +21,28 @@ namespace fl { SigmoidProduct::SigmoidProduct(const std::string& name, scalar left, scalar rising, scalar falling, scalar right, scalar height) - : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) { - } + : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) { } - SigmoidProduct::~SigmoidProduct() { - } + SigmoidProduct::~SigmoidProduct() { } std::string SigmoidProduct::className() const { return "SigmoidProduct"; } + Complexity SigmoidProduct::complexity() const { + return Complexity().comparison(1).arithmetic(2 + 4 + 4).function(2); + } + scalar SigmoidProduct::membership(scalar x) const { - scalar a = 1.0 / (1 + std::exp(-_rising * (x - _left))); - scalar b = 1.0 / (1 + std::exp(-_falling * (x - _right))); - return _height * a * b; + if (Op::isNaN(x)) return fl::nan; + const scalar a = 1.0 + std::exp(-_rising * (x - _left)); + const scalar b = 1.0 + std::exp(-_falling * (x - _right)); + return Term::_height * 1.0 / (a * b); } std::string SigmoidProduct::parameters() const { return Op::join(4, " ", _left, _rising, _falling, _right) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void SigmoidProduct::configure(const std::string& parameters) { @@ -58,7 +53,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setLeft(Op::toScalar(values.at(0))); setRising(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Spike.cpp b/fuzzylite/src/term/Spike.cpp index f4b73bf..a19810d 100644 --- a/fuzzylite/src/term/Spike.cpp +++ b/fuzzylite/src/term/Spike.cpp @@ -1,25 +1,17 @@ /* - 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/Spike.h" @@ -27,25 +19,26 @@ namespace fl { Spike::Spike(const std::string& name, scalar center, scalar width, scalar height) - : Term(name, height), _center(center), _width(width) { - } - - Spike::~Spike() { + : Term(name, height), _center(center), _width(width) { } - } + Spike::~Spike() { } std::string Spike::className() const { return "Spike"; } + Complexity Spike::complexity() const { + return Complexity().comparison(1).arithmetic(1 + 3).function(2); + } + scalar Spike::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - return _height * std::exp(-std::fabs(10.0 / _width * (x - _center))); + if (Op::isNaN(x)) return fl::nan; + return Term::_height * std::exp(-std::abs(10.0 / _width * (x - _center))); } std::string Spike::parameters() const { return Op::join(2, " ", _center, _width) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Spike::configure(const std::string& parameters) { @@ -56,7 +49,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setCenter(Op::toScalar(values.at(0))); setWidth(Op::toScalar(values.at(1))); @@ -87,4 +80,5 @@ namespace fl { Term* Spike::constructor() { return new Spike; } + } diff --git a/fuzzylite/src/term/Term.cpp b/fuzzylite/src/term/Term.cpp index 143a98f..bf7520d 100644 --- a/fuzzylite/src/term/Term.cpp +++ b/fuzzylite/src/term/Term.cpp @@ -1,25 +1,17 @@ /* - 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/Term.h" @@ -30,13 +22,9 @@ namespace fl { - Term::Term(const std::string& name, scalar height) : _name(name), _height(height) { - - } - - Term::~Term() { + Term::Term(const std::string& name, scalar height) : _name(name), _height(height) { } - } + Term::~Term() { } void Term::setName(const std::string& name) { this->_name = name; @@ -58,17 +46,20 @@ namespace fl { return FllExporter().toString(this); } - void Term::updateReference(Term* term, const Engine* engine) { - if (Linear * linear = dynamic_cast<Linear*> (term)) { - linear->setEngine(engine); - } else if (Function * function = dynamic_cast<Function*> (term)) { - function->setEngine(engine); - try { - function->load(); - } catch (...) { - //ignore - } - } + void Term::updateReference(const Engine* engine) { + FL_IUNUSED(engine); + //do nothing } + scalar Term::tsukamoto(scalar activationDegree, scalar minimum, scalar maximum) const { + FL_IUNUSED(minimum); + FL_IUNUSED(maximum); + return membership(activationDegree); + } + + bool Term::isMonotonic() const { + return false; + } + + } diff --git a/fuzzylite/src/term/Trapezoid.cpp b/fuzzylite/src/term/Trapezoid.cpp index 60abcc4..4773dbb 100644 --- a/fuzzylite/src/term/Trapezoid.cpp +++ b/fuzzylite/src/term/Trapezoid.cpp @@ -1,25 +1,17 @@ /* - 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/Trapezoid.h" @@ -30,7 +22,6 @@ namespace fl { scalar vertexA, scalar vertexB, scalar vertexC, scalar vertexD, scalar height) : Term(name, height), _vertexA(vertexA), _vertexB(vertexB), _vertexC(vertexC), _vertexD(vertexD) { if (Op::isNaN(vertexC) and Op::isNaN(vertexD)) { - //TODO: Modify FLL to allow passing two parameters only. this->_vertexD = _vertexB; scalar range = _vertexD - _vertexA; this->_vertexB = _vertexA + range * 1.0 / 5.0; @@ -38,34 +29,41 @@ namespace fl { } } - Trapezoid::~Trapezoid() { - } + Trapezoid::~Trapezoid() { } std::string Trapezoid::className() const { return "Trapezoid"; } + Complexity Trapezoid::complexity() const { + return Complexity().comparison(1 + 6).arithmetic(1 + 3).function(1); + } + scalar Trapezoid::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; + if (Op::isNaN(x)) return fl::nan; if (Op::isLt(x, _vertexA) or Op::isGt(x, _vertexD)) - return _height * 0.0; - - if (Op::isLt(x, _vertexB)) - return _height * Op::min(scalar(1.0), (x - _vertexA) / (_vertexB - _vertexA)); + return Term::_height * 0.0; + if (Op::isLt(x, _vertexB)) { + if (_vertexA == -fl::inf) return Term::_height * 1.0; + return Term::_height * Op::min(scalar(1.0), (x - _vertexA) / (_vertexB - _vertexA)); + } if (Op::isLE(x, _vertexC)) - return _height * 1.0; + return Term::_height * 1.0; - if (Op::isLt(x, _vertexD)) - return _height * (_vertexD - x) / (_vertexD - _vertexC); + if (Op::isLt(x, _vertexD)) { + if (_vertexD == fl::inf) return Term::_height * 1.0; + return Term::_height * (_vertexD - x) / (_vertexD - _vertexC); + } - return _height * 0.0; + if (_vertexD == fl::inf) return Term::_height * 1.0; + return Term::_height * 0.0; } std::string Trapezoid::parameters() const { return Op::join(4, " ", _vertexA, _vertexB, _vertexC, _vertexD)+ - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Trapezoid::configure(const std::string& parameters) { @@ -76,7 +74,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setVertexA(Op::toScalar(values.at(0))); setVertexB(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/Triangle.cpp b/fuzzylite/src/term/Triangle.cpp index 2fce1a6..9d1835c 100644 --- a/fuzzylite/src/term/Triangle.cpp +++ b/fuzzylite/src/term/Triangle.cpp @@ -1,25 +1,17 @@ /* - 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/Triangle.h" @@ -28,38 +20,44 @@ namespace fl { Triangle::Triangle(const std::string& name, scalar vertexA, scalar vertexB, scalar vertexC, scalar height) : Term(name, height), _vertexA(vertexA), _vertexB(vertexB), _vertexC(vertexC) { - if (fl::Op::isNaN(vertexC)) { - //TODO: Modify FLL to allow passing two parameters only. - this->_vertexC = vertexB; - this->_vertexB = (vertexA + vertexB) / 2.0; + if (Op::isNaN(vertexC)) { + this->_vertexC = _vertexB; + this->_vertexB = 0.5 * (_vertexA + _vertexB); } } - Triangle::~Triangle() { - } + Triangle::~Triangle() { } std::string Triangle::className() const { return "Triangle"; } + Complexity Triangle::complexity() const { + return Complexity().comparison(1 + 5).arithmetic(4); + } + scalar Triangle::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; + if (Op::isNaN(x)) return fl::nan; if (Op::isLt(x, _vertexA) or Op::isGt(x, _vertexC)) - return _height * 0.0; + return Term::_height * 0.0; if (Op::isEq(x, _vertexB)) - return _height * 1.0; - - if (Op::isLt(x, _vertexB)) - return _height * (x - _vertexA) / (_vertexB - _vertexA); + return Term::_height * 1.0; - return _height * (_vertexC - x) / (_vertexC - _vertexB); + if (Op::isLt(x, _vertexB)) { + if (_vertexA == -fl::inf) + return Term::_height * 1.0; + return Term::_height * (x - _vertexA) / (_vertexB - _vertexA); + } + if (_vertexC == fl::inf) + return Term::_height * 1.0; + return Term::_height * (_vertexC - x) / (_vertexC - _vertexB); } std::string Triangle::parameters() const { return Op::join(3, " ", _vertexA, _vertexB, _vertexC) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void Triangle::configure(const std::string& parameters) { @@ -70,7 +68,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setVertexA(Op::toScalar(values.at(0))); setVertexB(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/term/ZShape.cpp b/fuzzylite/src/term/ZShape.cpp index 86668b6..9054b20 100644 --- a/fuzzylite/src/term/ZShape.cpp +++ b/fuzzylite/src/term/ZShape.cpp @@ -1,25 +1,17 @@ /* - 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/ZShape.h" @@ -27,36 +19,58 @@ namespace fl { ZShape::ZShape(const std::string& name, scalar start, scalar end, scalar height) - : Term(name, height), _start(start), _end(end) { - } + : Term(name, height), _start(start), _end(end) { } - ZShape::~ZShape() { - } + ZShape::~ZShape() { } std::string ZShape::className() const { return "ZShape"; } + Complexity ZShape::complexity() const { + return Complexity().comparison(1 + 3).arithmetic(3 + 4).function(1); + } + scalar ZShape::membership(scalar x) const { - if (fl::Op::isNaN(x)) return fl::nan; - //from Octave zmf.m - scalar average = (_start + _end) / 2; - scalar difference = _end - _start; + if (Op::isNaN(x)) return fl::nan; - if (Op::isLE(x, _start)) return _height * 1.0; + if (Op::isLE(x, _start)) + return Term::_height * 1.0; - if (Op::isLE(x, average)) - return _height * (1.0 - 2.0 * std::pow((x - _start) / difference, 2)); + if (Op::isLE(x, 0.5 * (_start + _end))) + return Term::_height * (1.0 - 2.0 * std::pow((x - _start) / (_end - _start), 2)); if (Op::isLt(x, _end)) - return _height * (2.0 * std::pow((x - _end) / difference, 2)); + return Term::_height * (2.0 * std::pow((x - _end) / (_end - _start), 2)); + + return Term::_height * 0.0; + } + + scalar ZShape::tsukamoto(scalar activationDegree, scalar minimum, scalar maximum) const { + FL_IUNUSED(minimum); + FL_IUNUSED(maximum); + + scalar w = activationDegree; + scalar z = fl::nan; + + scalar difference = _end - _start; + scalar a = _start + std::sqrt(-0.5 * (w - 1.0) * difference * difference); + scalar b = _end + std::sqrt(0.5 * w * difference * difference); + if (std::abs(w - membership(a)) < std::abs(w - membership(b))) { + z = a; + } else { + z = b; + } + return z; + } - return _height * 0.0; + bool ZShape::isMonotonic() const { + return true; } std::string ZShape::parameters() const { return Op::join(2, " ", _start, _end) + - (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : ""); + (not Op::isEq(getHeight(), 1.0) ? " " + Op::str(getHeight()) : ""); } void ZShape::configure(const std::string& parameters) { @@ -67,7 +81,7 @@ namespace fl { std::ostringstream ex; ex << "[configuration error] term <" << className() << ">" << " requires <" << required << "> parameters"; - throw fl::Exception(ex.str(), FL_AT); + throw Exception(ex.str(), FL_AT); } setStart(Op::toScalar(values.at(0))); setEnd(Op::toScalar(values.at(1))); diff --git a/fuzzylite/src/variable/InputVariable.cpp b/fuzzylite/src/variable/InputVariable.cpp index 9fa2932..1364d8f 100644 --- a/fuzzylite/src/variable/InputVariable.cpp +++ b/fuzzylite/src/variable/InputVariable.cpp @@ -1,25 +1,17 @@ /* - 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/variable/InputVariable.h" @@ -29,26 +21,24 @@ namespace fl { InputVariable::InputVariable(const std::string& name, scalar minimum, scalar maximum) - : Variable(name, minimum, maximum), _inputValue(fl::nan) { - } - - InputVariable::~InputVariable() { - } + : Variable(name, minimum, maximum) { } - void InputVariable::setInputValue(scalar inputValue) { - this->_inputValue = inputValue; - } + InputVariable::~InputVariable() { } - scalar InputVariable::getInputValue() const { - return this->_inputValue; + std::string InputVariable::fuzzyInputValue() const { + return fuzzify(getValue()); } - std::string InputVariable::fuzzyInputValue() const { - return fuzzify(_inputValue); + Variable::Type InputVariable::type() const { + return Variable::Input; } std::string InputVariable::toString() const { return FllExporter().toString(this); } + InputVariable* InputVariable::clone() const { + return new InputVariable(*this); + } + } diff --git a/fuzzylite/src/variable/OutputVariable.cpp b/fuzzylite/src/variable/OutputVariable.cpp index 6e1d906..0f85dc4 100644 --- a/fuzzylite/src/variable/OutputVariable.cpp +++ b/fuzzylite/src/variable/OutputVariable.cpp @@ -1,44 +1,31 @@ /* - 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/variable/OutputVariable.h" -#include "fl/defuzzifier/Defuzzifier.h" #include "fl/imex/FllExporter.h" -#include "fl/norm/SNorm.h" -#include "fl/term/Accumulated.h" -#include "fl/term/Activated.h" namespace fl { OutputVariable::OutputVariable(const std::string& name, scalar minimum, scalar maximum) : Variable(name, minimum, maximum), - _fuzzyOutput(new Accumulated(name, minimum, maximum)), _outputValue(fl::nan), - _previousOutputValue(fl::nan), _defaultValue(fl::nan), - _lockOutputValueInRange(false), _lockPreviousOutputValue(false) { - } + _fuzzyOutput(new Aggregated(name, minimum, maximum)), + _previousValue(fl::nan), _defaultValue(fl::nan), + _lockPreviousValue(false) { } OutputVariable::OutputVariable(const OutputVariable& other) : Variable(other) { copyFrom(other); @@ -55,36 +42,33 @@ namespace fl { return *this; } - OutputVariable::~OutputVariable() { - } + OutputVariable::~OutputVariable() { } void OutputVariable::copyFrom(const OutputVariable& other) { _fuzzyOutput.reset(other._fuzzyOutput->clone()); if (other._defuzzifier.get()) _defuzzifier.reset(other._defuzzifier->clone()); - _outputValue = other._outputValue; - _previousOutputValue = other._previousOutputValue; + _previousValue = other._previousValue; _defaultValue = other._defaultValue; - _lockOutputValueInRange = other._lockOutputValueInRange; - _lockPreviousOutputValue = other._lockPreviousOutputValue; + _lockPreviousValue = other._lockPreviousValue; } void OutputVariable::setName(const std::string& name) { Variable::setName(name); - this->_fuzzyOutput->setName(name); + _fuzzyOutput->setName(name); } - Accumulated* OutputVariable::fuzzyOutput() const { + Aggregated* OutputVariable::fuzzyOutput() const { return this->_fuzzyOutput.get(); } void OutputVariable::setMinimum(scalar minimum) { Variable::setMinimum(minimum); - this->_fuzzyOutput->setMinimum(minimum); + _fuzzyOutput->setMinimum(minimum); } void OutputVariable::setMaximum(scalar maximum) { Variable::setMaximum(maximum); - this->_fuzzyOutput->setMaximum(maximum); + _fuzzyOutput->setMaximum(maximum); } void OutputVariable::setDefuzzifier(Defuzzifier* defuzzifier) { @@ -95,20 +79,20 @@ namespace fl { return this->_defuzzifier.get(); } - void OutputVariable::setOutputValue(scalar outputValue) { - this->_outputValue = outputValue; + void OutputVariable::setAggregation(SNorm* aggregation) { + this->_fuzzyOutput->setAggregation(aggregation); } - scalar OutputVariable::getOutputValue() const { - return this->_outputValue; + SNorm* OutputVariable::getAggregation() const { + return this->_fuzzyOutput->getAggregation(); } - void OutputVariable::setPreviousOutputValue(scalar previousOutputValue) { - this->_previousOutputValue = previousOutputValue; + void OutputVariable::setPreviousValue(scalar previousOutputValue) { + this->_previousValue = previousOutputValue; } - scalar OutputVariable::getPreviousOutputValue() const { - return this->_previousOutputValue; + scalar OutputVariable::getPreviousValue() const { + return this->_previousValue; } void OutputVariable::setDefaultValue(scalar defaultValue) { @@ -119,77 +103,124 @@ namespace fl { return this->_defaultValue; } - void OutputVariable::setLockOutputValueInRange(bool lockOutputValueInRange) { - this->_lockOutputValueInRange = lockOutputValueInRange; + void OutputVariable::setLockPreviousValue(bool lockPreviousValue) { + this->_lockPreviousValue = lockPreviousValue; } - bool OutputVariable::isLockedOutputValueInRange() const { - return this->_lockOutputValueInRange; + bool OutputVariable::isLockPreviousValue() const { + return this->_lockPreviousValue; } - void OutputVariable::setLockPreviousOutputValue(bool lockPreviousOutputValue) { - this->_lockPreviousOutputValue = lockPreviousOutputValue; + Variable::Type OutputVariable::type() const { + return Variable::Output; + } + + Complexity OutputVariable::complexity(const Activated& term) const { + Aggregated aggregated; + if (_fuzzyOutput->getAggregation()) { + aggregated.setAggregation(_fuzzyOutput->getAggregation()->clone()); + } + aggregated.addTerm(term); + if (_defuzzifier.get()) { + return _defuzzifier->complexity(&aggregated); + } + return aggregated.complexityOfMembership(); + } + + Complexity OutputVariable::complexityOfDefuzzification() const { + Aggregated term; + for (std::size_t i = 0; i < _terms.size(); ++i) { + term.addTerm(_terms.at(i), fl::nan, fl::null); + } + if (_defuzzifier.get()) { + return _defuzzifier->complexity(&term); + } + return term.complexityOfMembership(); } - bool OutputVariable::isLockedPreviousOutputValue() const { - return this->_lockPreviousOutputValue; + Complexity OutputVariable::currentComplexity() const { + if (_defuzzifier.get()) + return _defuzzifier->complexity(_fuzzyOutput.get()); + return _fuzzyOutput->complexity(); } void OutputVariable::defuzzify() { - if (fl::Op::isFinite(this->_outputValue)) { - this->_previousOutputValue = this->_outputValue; + if (not _enabled) return; + + if (Op::isFinite(_value)) { + _previousValue = _value; } + std::string exception; scalar result = fl::nan; - bool isValid = this->_enabled and not this->_fuzzyOutput->isEmpty(); + bool isValid = not _fuzzyOutput->isEmpty(); if (isValid) { - if (not _defuzzifier.get()) { - throw fl::Exception("[defuzzifier error] " - "defuzzifier needed to defuzzify output variable <" + _name + ">", FL_AT); + /* Checks whether the variable can be defuzzified without exceptions. + * If it cannot be defuzzified, be that due to a missing defuzzifier + * or aggregation operator, the expected behaviour is to leave the + * variable in a state that reflects an invalid defuzzification, + * that is, apply logic of default values and previous values.*/ + isValid = false; + if (_defuzzifier.get()) { + try { + result = _defuzzifier->defuzzify(_fuzzyOutput.get(), _minimum, _maximum); + isValid = true; + } catch (std::exception& ex) { + exception = ex.what(); + } + } else { + exception = "[defuzzifier error] " + "defuzzifier needed to defuzzify output variable <" + getName() + ">"; } - result = this->_defuzzifier->defuzzify(this->_fuzzyOutput.get(), _minimum, _maximum); - } else { + } + + if (not isValid) { //if a previous defuzzification was successfully performed and //and the output value is supposed not to change when the output is empty - if (_lockPreviousOutputValue and not Op::isNaN(_previousOutputValue)) { - result = _previousOutputValue; + if (_lockPreviousValue and not Op::isNaN(_previousValue)) { + result = _previousValue; } else { result = _defaultValue; } } - if (_lockOutputValueInRange) { - result = fl::Op::bound(result, _minimum, _maximum); - } + setValue(result); - this->_outputValue = result; + if (not exception.empty()) { + throw Exception(exception, FL_AT); + } } std::string OutputVariable::fuzzyOutputValue() const { std::ostringstream ss; - for (std::size_t i = 0; i < _terms.size(); ++i) { - scalar degree = _fuzzyOutput->activationDegree(_terms.at(i)); - if (i == 0) { - ss << fl::Op::str(degree); - } else { - if (fl::Op::isNaN(degree) or fl::Op::isGE(degree, 0.0)) - ss << " + " << fl::Op::str(degree); - else - ss << " - " << fl::Op::str(std::fabs(degree)); - } - ss << "/" << _terms.at(i)->getName(); + if (not _terms.empty()) { + Term* first = _terms.front(); + ss << Op::str(fuzzyOutput()->activationDegree(first)) + << "/" << first->getName(); + } + for (std::size_t i = 1; i < _terms.size(); ++i) { + scalar degree = fuzzyOutput()->activationDegree(_terms.at(i)); + if (Op::isNaN(degree) or Op::isGE(degree, 0.0)) + ss << " + " << Op::str(degree); + else + ss << " - " << Op::str(std::abs(degree)); + ss << "/" << terms().at(i)->getName(); } return ss.str(); } void OutputVariable::clear() { - _fuzzyOutput->clear(); - setPreviousOutputValue(fl::nan); - setOutputValue(fl::nan); + fuzzyOutput()->clear(); + setValue(fl::nan); + setPreviousValue(fl::nan); } std::string OutputVariable::toString() const { return FllExporter().toString(this); } + OutputVariable* OutputVariable::clone() const { + return new OutputVariable(*this); + } + } diff --git a/fuzzylite/src/variable/Variable.cpp b/fuzzylite/src/variable/Variable.cpp index 37cf25c..516b466 100644 --- a/fuzzylite/src/variable/Variable.cpp +++ b/fuzzylite/src/variable/Variable.cpp @@ -1,45 +1,34 @@ /* - 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/variable/Variable.h" -#include "fl/defuzzifier/Centroid.h" #include "fl/imex/FllExporter.h" #include "fl/norm/Norm.h" #include "fl/term/Constant.h" #include "fl/term/Linear.h" -#include "fl/term/Term.h" -#include <algorithm> -#include <map> -#include <sstream> +#include <queue> namespace fl { Variable::Variable(const std::string& name, scalar minimum, scalar maximum) - : _name(name), _minimum(minimum), _maximum(maximum), _enabled(true) { - } + : _name(name), _description(""), + _value(fl::nan), _minimum(minimum), _maximum(maximum), + _enabled(true), _lockValueInRange(false) { } Variable::Variable(const Variable& other) { copyFrom(other); @@ -58,9 +47,12 @@ namespace fl { void Variable::copyFrom(const Variable& other) { _name = other._name; - _enabled = other._enabled; + _description = other._description; + _value = other._value; _minimum = other._minimum; _maximum = other._maximum; + _enabled = other._enabled; + _lockValueInRange = other._lockValueInRange; for (std::size_t i = 0; i < other._terms.size(); ++i) { _terms.push_back(other._terms.at(i)->clone()); } @@ -80,13 +72,31 @@ namespace fl { return this->_name; } + void Variable::setDescription(const std::string& description) { + this->_description = description; + } + + std::string Variable::getDescription() const { + return this->_description; + } + + void Variable::setValue(scalar value) { + this->_value = _lockValueInRange + ? Op::bound(value, _minimum, _maximum) + : value; + } + + scalar Variable::getValue() const { + return this->_value; + } + void Variable::setRange(scalar minimum, scalar maximum) { setMinimum(minimum); setMaximum(maximum); } scalar Variable::range() const { - return this->_maximum - this->_minimum; + return getMaximum() - getMinimum(); } void Variable::setMinimum(scalar minimum) { @@ -113,24 +123,47 @@ namespace fl { return this->_enabled; } + void Variable::setLockValueInRange(bool lockValueInRange) { + this->_lockValueInRange = lockValueInRange; + } + + bool Variable::isLockValueInRange() const { + return this->_lockValueInRange; + } + + Variable::Type Variable::type() const { + return None; + } + + Complexity Variable::complexity() const { + Complexity result; + if (isEnabled()) { + for (std::size_t i = 0; i < _terms.size(); ++i) { + result += _terms.at(i)->complexity(); + } + } + return result; + } + std::string Variable::fuzzify(scalar x) const { std::ostringstream ss; - for (std::size_t i = 0; i < _terms.size(); ++i) { + for (std::size_t i = 0; i < terms().size(); ++i) { + Term* term = _terms.at(i); scalar fx = fl::nan; try { - fx = _terms.at(i)->membership(x); + fx = term->membership(x); } catch (...) { //ignore } if (i == 0) { - ss << fl::Op::str(fx); + ss << Op::str(fx); } else { - if (fl::Op::isNaN(fx) or fl::Op::isGE(fx, 0.0)) - ss << " + " << fl::Op::str(fx); + if (Op::isNaN(fx) or Op::isGE(fx, 0.0)) + ss << " + " << Op::str(fx); else - ss << " - " << fl::Op::str(std::fabs(fx)); + ss << " - " << Op::str(std::abs(fx)); } - ss << "/" << _terms.at(i)->getName(); + ss << "/" << term->getName(); } return ss.str(); } @@ -140,14 +173,15 @@ namespace fl { scalar ymax = 0.0; for (std::size_t i = 0; i < _terms.size(); ++i) { scalar y = fl::nan; + Term* term = _terms.at(i); try { - y = _terms.at(i)->membership(x); + y = term->membership(x); } catch (...) { //ignore } - if (fl::Op::isGt(y, ymax)) { + if (Op::isGt(y, ymax)) { ymax = y; - result = _terms.at(i); + result = term; } } if (yhighest) *yhighest = ymax; @@ -162,70 +196,83 @@ namespace fl { * Operations for datatype _terms */ - struct SortByCoG { - std::map<const Term*, scalar> centroids; + typedef std::pair<Term*, scalar> TermCentroid; + + struct Ascending { - bool operator()(const Term* a, const Term * b) { - return fl::Op::isLt( - centroids.find(a)->second, - centroids.find(b)->second); + bool operator()(const TermCentroid& a, const TermCentroid& b) const { + return a.second > b.second; } }; void Variable::sort() { + std::priority_queue <TermCentroid, std::vector<TermCentroid>, Ascending> termCentroids; Centroid defuzzifier; - std::map<const Term*, scalar> centroids; + FL_DBG("Sorting..."); for (std::size_t i = 0; i < _terms.size(); ++i) { Term* term = _terms.at(i); + scalar centroid = fl::inf; try { if (dynamic_cast<const Constant*> (term) or dynamic_cast<const Linear*> (term)) { - centroids[term] = term->membership(0); + centroid = term->membership(0); } else { - centroids[term] = defuzzifier.defuzzify(term, _minimum, _maximum); + centroid = defuzzifier.defuzzify(term, getMinimum(), getMaximum()); } } catch (...) { //ignore error possibly due to Function not loaded - centroids[term] = fl::inf; + centroid = fl::inf; } + termCentroids.push(TermCentroid(term, centroid)); + FL_DBG(term->toString() << " -> " << centroid) } - SortByCoG criterion; - criterion.centroids = centroids; - std::sort(_terms.begin(), _terms.end(), criterion); + + std::vector<Term*> sortedTerms; + while (termCentroids.size() > 0) { + sortedTerms.push_back(termCentroids.top().first); + FL_DBG(termCentroids.top().first->toString() << " -> " << termCentroids.top().second); + termCentroids.pop(); + } + setTerms(sortedTerms); } void Variable::addTerm(Term* term) { - this->_terms.push_back(term); + _terms.push_back(term); } - void Variable::insertTerm(Term* term, int index) { - this->_terms.insert(this->_terms.begin() + index, term); + void Variable::insertTerm(Term* term, std::size_t index) { + _terms.insert(_terms.begin() + index, term); } - Term* Variable::getTerm(int index) const { - return this->_terms.at(index); + Term* Variable::getTerm(std::size_t index) const { + return _terms.at(index); } Term* Variable::getTerm(const std::string& name) const { - for (std::size_t i = 0; i < _terms.size(); ++i) { + for (std::size_t i = 0; i < terms().size(); ++i) { if (_terms.at(i)->getName() == name) { - return _terms.at(i); + return terms().at(i); } } - throw fl::Exception("[variable error] term <" + name + "> " - "not found in variable <" + this->_name + ">", FL_AT); + throw Exception("[variable error] term <" + name + "> " + "not found in variable <" + getName() + ">", FL_AT); } bool Variable::hasTerm(const std::string& name) const { - return getTerm(name) != fl::null; + for (std::size_t i = 0; i < _terms.size(); ++i) { + if (_terms.at(i)->getName() == name) { + return true; + } + } + return false; } - Term* Variable::removeTerm(int index) { - Term* result = this->_terms.at(index); - this->_terms.erase(this->_terms.begin() + index); + Term* Variable::removeTerm(std::size_t index) { + Term* result = _terms.at(index); + _terms.erase(_terms.begin() + index); return result; } - int Variable::numberOfTerms() const { - return this->_terms.size(); + std::size_t Variable::numberOfTerms() const { + return _terms.size(); } const std::vector<Term*>& Variable::terms() const { @@ -240,5 +287,9 @@ namespace fl { return this->_terms; } + Variable* Variable::clone() const { + return new Variable(*this); + } + } |