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/imex | |
parent | bbefa170378553e5a6e0d72e4d52328b61f3e8ac (diff) |
new upstream version 6.0
Diffstat (limited to 'fuzzylite/src/imex')
-rw-r--r-- | fuzzylite/src/imex/CppExporter.cpp | 181 | ||||
-rw-r--r-- | fuzzylite/src/imex/Exporter.cpp | 32 | ||||
-rw-r--r-- | fuzzylite/src/imex/FclExporter.cpp | 126 | ||||
-rw-r--r-- | fuzzylite/src/imex/FclImporter.cpp | 164 | ||||
-rw-r--r-- | fuzzylite/src/imex/FisExporter.cpp | 180 | ||||
-rw-r--r-- | fuzzylite/src/imex/FisImporter.cpp | 235 | ||||
-rw-r--r-- | fuzzylite/src/imex/FldExporter.cpp | 246 | ||||
-rw-r--r-- | fuzzylite/src/imex/FllExporter.cpp | 85 | ||||
-rw-r--r-- | fuzzylite/src/imex/FllImporter.cpp | 193 | ||||
-rw-r--r-- | fuzzylite/src/imex/Importer.cpp | 32 | ||||
-rw-r--r-- | fuzzylite/src/imex/JavaExporter.cpp | 157 | ||||
-rw-r--r-- | fuzzylite/src/imex/RScriptExporter.cpp | 234 |
12 files changed, 1042 insertions, 823 deletions
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"; + } +} |