summaryrefslogtreecommitdiff
path: root/fuzzylite/src
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzylite/src')
-rw-r--r--fuzzylite/src/Console.cpp820
-rw-r--r--fuzzylite/src/Engine.cpp698
-rw-r--r--fuzzylite/src/Exception.cpp183
-rw-r--r--fuzzylite/src/Operation.cpp477
-rw-r--r--fuzzylite/src/defuzzifier/Bisector.cpp85
-rw-r--r--fuzzylite/src/defuzzifier/Centroid.cpp80
-rw-r--r--fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp53
-rw-r--r--fuzzylite/src/defuzzifier/LargestOfMaximum.cpp75
-rw-r--r--fuzzylite/src/defuzzifier/MeanOfMaximum.cpp88
-rw-r--r--fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp77
-rw-r--r--fuzzylite/src/defuzzifier/WeightedAverage.cpp123
-rw-r--r--fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp178
-rw-r--r--fuzzylite/src/defuzzifier/WeightedSum.cpp121
-rw-r--r--fuzzylite/src/factory/CloningFactory.cpp135
-rw-r--r--fuzzylite/src/factory/ConstructionFactory.cpp114
-rw-r--r--fuzzylite/src/factory/DefuzzifierFactory.cpp78
-rw-r--r--fuzzylite/src/factory/FactoryManager.cpp127
-rw-r--r--fuzzylite/src/factory/FunctionFactory.cpp172
-rw-r--r--fuzzylite/src/factory/HedgeFactory.cpp51
-rw-r--r--fuzzylite/src/factory/SNormFactory.cpp55
-rw-r--r--fuzzylite/src/factory/TNormFactory.cpp53
-rw-r--r--fuzzylite/src/factory/TermFactory.cpp81
-rw-r--r--fuzzylite/src/fuzzylite.cpp128
-rw-r--r--fuzzylite/src/hedge/Any.cpp52
-rw-r--r--fuzzylite/src/hedge/Extremely.cpp50
-rw-r--r--fuzzylite/src/hedge/Not.cpp46
-rw-r--r--fuzzylite/src/hedge/Seldom.cpp50
-rw-r--r--fuzzylite/src/hedge/Somewhat.cpp45
-rw-r--r--fuzzylite/src/hedge/Very.cpp45
-rw-r--r--fuzzylite/src/imex/CppExporter.cpp234
-rw-r--r--fuzzylite/src/imex/Exporter.cpp49
-rw-r--r--fuzzylite/src/imex/FclExporter.cpp256
-rw-r--r--fuzzylite/src/imex/FclImporter.cpp607
-rw-r--r--fuzzylite/src/imex/FisExporter.cpp463
-rw-r--r--fuzzylite/src/imex/FisImporter.cpp501
-rw-r--r--fuzzylite/src/imex/FldExporter.cpp246
-rw-r--r--fuzzylite/src/imex/FllExporter.cpp189
-rw-r--r--fuzzylite/src/imex/FllImporter.cpp317
-rw-r--r--fuzzylite/src/imex/Importer.cpp54
-rw-r--r--fuzzylite/src/imex/JavaExporter.cpp228
-rwxr-xr-xfuzzylite/src/m/compare.m60
-rwxr-xr-xfuzzylite/src/m/compare_examples.m45
-rw-r--r--fuzzylite/src/main.cpp71
-rw-r--r--fuzzylite/src/norm/s/AlgebraicSum.cpp45
-rw-r--r--fuzzylite/src/norm/s/BoundedSum.cpp45
-rw-r--r--fuzzylite/src/norm/s/DrasticSum.cpp48
-rw-r--r--fuzzylite/src/norm/s/EinsteinSum.cpp45
-rw-r--r--fuzzylite/src/norm/s/HamacherSum.cpp45
-rw-r--r--fuzzylite/src/norm/s/Maximum.cpp45
-rw-r--r--fuzzylite/src/norm/s/NilpotentMaximum.cpp50
-rw-r--r--fuzzylite/src/norm/s/NormalizedSum.cpp46
-rw-r--r--fuzzylite/src/norm/t/AlgebraicProduct.cpp45
-rw-r--r--fuzzylite/src/norm/t/BoundedDifference.cpp46
-rw-r--r--fuzzylite/src/norm/t/DrasticProduct.cpp48
-rw-r--r--fuzzylite/src/norm/t/EinsteinProduct.cpp45
-rw-r--r--fuzzylite/src/norm/t/HamacherProduct.cpp46
-rw-r--r--fuzzylite/src/norm/t/Minimum.cpp46
-rw-r--r--fuzzylite/src/norm/t/NilpotentMinimum.cpp50
-rw-r--r--fuzzylite/src/rule/Antecedent.cpp368
-rw-r--r--fuzzylite/src/rule/Consequent.cpp243
-rw-r--r--fuzzylite/src/rule/Expression.cpp83
-rw-r--r--fuzzylite/src/rule/Rule.cpp262
-rw-r--r--fuzzylite/src/rule/RuleBlock.cpp210
-rw-r--r--fuzzylite/src/term/Accumulated.cpp211
-rw-r--r--fuzzylite/src/term/Activated.cpp100
-rw-r--r--fuzzylite/src/term/Bell.cpp99
-rw-r--r--fuzzylite/src/term/Concave.cpp107
-rw-r--r--fuzzylite/src/term/Constant.cpp70
-rw-r--r--fuzzylite/src/term/Cosine.cpp96
-rw-r--r--fuzzylite/src/term/Discrete.cpp210
-rw-r--r--fuzzylite/src/term/Function.cpp600
-rw-r--r--fuzzylite/src/term/Gaussian.cpp92
-rw-r--r--fuzzylite/src/term/GaussianProduct.cpp120
-rw-r--r--fuzzylite/src/term/Linear.cpp135
-rw-r--r--fuzzylite/src/term/PiShape.cpp133
-rw-r--r--fuzzylite/src/term/Ramp.cpp110
-rw-r--r--fuzzylite/src/term/Rectangle.cpp93
-rw-r--r--fuzzylite/src/term/SShape.cpp102
-rw-r--r--fuzzylite/src/term/Sigmoid.cpp98
-rw-r--r--fuzzylite/src/term/SigmoidDifference.cpp114
-rw-r--r--fuzzylite/src/term/SigmoidProduct.cpp112
-rw-r--r--fuzzylite/src/term/Spike.cpp90
-rw-r--r--fuzzylite/src/term/Term.cpp74
-rw-r--r--fuzzylite/src/term/Trapezoid.cpp130
-rw-r--r--fuzzylite/src/term/Triangle.cpp115
-rw-r--r--fuzzylite/src/term/ZShape.cpp102
-rw-r--r--fuzzylite/src/variable/InputVariable.cpp54
-rw-r--r--fuzzylite/src/variable/OutputVariable.cpp195
-rw-r--r--fuzzylite/src/variable/Variable.cpp244
89 files changed, 13127 insertions, 0 deletions
diff --git a/fuzzylite/src/Console.cpp b/fuzzylite/src/Console.cpp
new file mode 100644
index 0000000..3a55fc5
--- /dev/null
+++ b/fuzzylite/src/Console.cpp
@@ -0,0 +1,820 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/Console.h"
+
+#include "fl/Headers.h"
+
+#include <algorithm>
+#include <cctype>
+#include <fstream>
+#include <stdlib.h>
+#include <utility>
+#include <vector>
+
+#ifdef FL_UNIX
+#include <termios.h>
+#include <unistd.h>
+#elif defined(FL_WINDOWS)
+#include <conio.h>
+#endif
+
+#ifdef FL_CPP11
+#include <chrono>
+#endif
+
+namespace fl {
+ const std::string Console::KW_INPUT_FILE = "-i";
+ const std::string Console::KW_INPUT_FORMAT = "-if";
+ const std::string Console::KW_OUTPUT_FILE = "-o";
+ const std::string Console::KW_OUTPUT_FORMAT = "-of";
+ const std::string Console::KW_EXAMPLE = "-example";
+ const std::string Console::KW_DECIMALS = "-decimals";
+ const std::string Console::KW_DATA_INPUT = "-d";
+ const std::string Console::KW_DATA_MAXIMUM = "-dmaximum";
+ const std::string Console::KW_DATA_EXPORT_HEADER = "-dheader";
+ const std::string Console::KW_DATA_EXPORT_INPUTS = "-dinputs";
+
+ std::vector<Console::Option> Console::availableOptions() {
+ std::vector<Console::Option> options;
+ options.push_back(Option(KW_INPUT_FILE, "inputfile", "file to import your engine from"));
+ options.push_back(Option(KW_INPUT_FORMAT, "format", "format of the file to import (fll | fis | fcl)"));
+ options.push_back(Option(KW_OUTPUT_FILE, "outputfile", "file to export your engine to"));
+ options.push_back(Option(KW_OUTPUT_FORMAT, "format", "format of the file to export (fll | fld | cpp | java | fis | fcl)"));
+ options.push_back(Option(KW_EXAMPLE, "letter", "if not inputfile, built-in example to use as engine: (m)amdani or (t)akagi-sugeno"));
+ options.push_back(Option(KW_DECIMALS, "number", "number of decimals to write floating-poing values"));
+ options.push_back(Option(KW_DATA_INPUT, "datafile", "if exporting to fld, file of input values to evaluate your engine on"));
+ options.push_back(Option(KW_DATA_MAXIMUM, "number", "if exporting to fld without datafile, maximum number of results to export"));
+ options.push_back(Option(KW_DATA_EXPORT_HEADER, "boolean", "if true and exporting to fld, include headers"));
+ options.push_back(Option(KW_DATA_EXPORT_INPUTS, "boolean", "if true and exporting to fld, include input values"));
+ return options;
+ }
+
+ std::string Console::usage() {
+ std::vector<Console::Option> options = availableOptions();
+ std::ostringstream ss;
+
+ ss << "========================================\n";
+ ss << "fuzzylite: a fuzzy logic control library\n";
+ ss << "version: " << fuzzylite::longVersion() << "\n";
+ ss << "author: " << fuzzylite::author() << "\n";
+ ss << "license: " << fuzzylite::license() << "\n";
+ ss << "========================================\n\n";
+ ss << "usage: fuzzylite inputfile outputfile\n";
+ ss << " or: fuzzylite ";
+ for (std::size_t i = 0; i < options.size(); ++i) {
+ ss << "[" << options.at(i).key << " " << options.at(i).value << "] ";
+ }
+ ss << "\n\nwhere:\n";
+ for (std::size_t i = 0; i < options.size(); ++i) {
+ std::string spacedKey(12, ' ');
+ std::string key = options.at(i).key;
+ std::copy(key.begin(), key.end(), spacedKey.begin());
+
+ std::string spacedValue(13, ' ');
+ std::string value = options.at(i).value;
+ std::copy(value.begin(), value.end(), spacedValue.begin());
+
+ std::string description = options.at(i).description;
+
+ ss << spacedKey << spacedValue << description << "\n";
+ }
+
+ ss << "\n";
+ ss << "Visit " << fuzzylite::website() << " for more information.\n\n";
+ ss << "Copyright (C) 2010-2015 FuzzyLite Limited.\n";
+ ss << "All rights reserved.";
+
+ return ss.str();
+ }
+
+ std::map<std::string, std::string> Console::parse(int argc, char** argv) {
+ if ((argc - 1) % 2 != 0) {
+ throw fl::Exception("[option error] incomplete number of parameters [key value]", FL_AT);
+ }
+ std::map<std::string, std::string> options;
+ for (int i = 1; i < argc - 1; i += 2) {
+ std::string key = std::string(argv[i]);
+ std::string value = std::string(argv[i + 1]);
+ options[key] = value;
+ }
+ if (options.size() == 1) {
+ std::map<std::string, std::string>::const_iterator it = options.begin();
+ if (it->first.at(0) != '-') {
+ options[KW_INPUT_FILE] = it->first;
+ options[KW_OUTPUT_FILE] = it->second;
+ }
+ } else {
+ std::vector<Console::Option> validOptions = availableOptions();
+
+ for (std::map<std::string, std::string>::const_iterator it = options.begin();
+ it != options.end(); ++it) {
+ bool isValid = false;
+ for (std::size_t i = 0; i < validOptions.size(); ++i) {
+ std::string key = validOptions.at(i).key;
+ if (key == it->first) {
+ isValid = true;
+ break;
+ }
+ }
+ if (not isValid) {
+ throw fl::Exception("[option error] option <" + it->first + "> not recognized", FL_AT);
+ }
+ }
+ }
+ return options;
+ }
+
+ void Console::process(const std::map<std::string, std::string>& options) {
+ std::map<std::string, std::string>::const_iterator it;
+
+ it = options.find(KW_DECIMALS);
+ if (it != options.end()) {
+ fl::fuzzylite::setDecimals((int) fl::Op::toScalar(it->second));
+ }
+
+ std::string example;
+ std::string inputFormat;
+ std::ostringstream textEngine;
+
+ it = options.find(KW_EXAMPLE);
+
+ bool isExample = (it != options.end());
+
+ if (isExample) {
+ example = it->second;
+ Engine* engine;
+ if (example == "m" or example == "mamdani") {
+ engine = mamdani();
+ } else if (example == "t" or example == "ts" or example == "takagi-sugeno") {
+ engine = takagiSugeno();
+ } else {
+ throw fl::Exception("[option error] example <" + example + "> not available", FL_AT);
+ }
+ inputFormat = "fll";
+ textEngine << FllExporter().toString(engine);
+ delete engine;
+
+ } else {
+ it = options.find(KW_INPUT_FILE);
+ if (it == options.end()) {
+ throw fl::Exception("[option error] no input file specified", FL_AT);
+ }
+ std::string inputFilename = it->second;
+ std::ifstream inputFile(inputFilename.c_str());
+ if (not inputFile.is_open()) {
+ throw fl::Exception("[file error] file <" + inputFilename + "> could not be opened", FL_AT);
+ }
+ std::string line;
+ while (std::getline(inputFile, line)) {
+ textEngine << line << std::endl;
+ }
+ inputFile.close();
+
+ it = options.find(KW_INPUT_FORMAT);
+ if (it != options.end()) {
+ inputFormat = it->second;
+ } else {
+ std::size_t extensionIndex = inputFilename.find_last_of(".");
+ if (extensionIndex != std::string::npos) {
+ inputFormat = inputFilename.substr(extensionIndex + 1);
+ } else {
+ throw fl::Exception("[format error] unspecified format of input file", FL_AT);
+ }
+ }
+ }
+
+ std::string outputFilename;
+ it = options.find(KW_OUTPUT_FILE);
+ if (it != options.end()) {
+ outputFilename = it->second;
+ }
+
+ std::string outputFormat;
+ it = options.find(KW_OUTPUT_FORMAT);
+ if (it != options.end()) {
+ outputFormat = it->second;
+ } else {
+ std::size_t extensionIndex = outputFilename.find_last_of(".");
+ if (extensionIndex != std::string::npos) {
+ outputFormat = outputFilename.substr(extensionIndex + 1);
+ } else {
+ throw fl::Exception("[format error] unspecified format of output file", FL_AT);
+ }
+ }
+
+
+ if (outputFilename.empty()) {
+ process(textEngine.str(), std::cout, inputFormat, outputFormat, options);
+ } else {
+ std::ofstream writer(outputFilename.c_str());
+ if (not writer.is_open()) {
+ throw fl::Exception("[file error] file <" + outputFilename + "> could not be created", FL_AT);
+ }
+ process(textEngine.str(), writer, inputFormat, outputFormat, options);
+ writer.flush();
+ writer.close();
+ }
+ }
+
+ void Console::process(const std::string& input, std::ostream& writer,
+ const std::string& inputFormat, const std::string& outputFormat,
+ const std::map<std::string, std::string>& options) {
+ FL_unique_ptr<Importer> importer;
+ FL_unique_ptr<Exporter> exporter;
+ FL_unique_ptr<Engine> engine;
+
+ if ("fll" == inputFormat) {
+ importer.reset(new FllImporter);
+ } else if ("fcl" == inputFormat) {
+ importer.reset(new FclImporter);
+ } else if ("fis" == inputFormat) {
+ importer.reset(new FisImporter);
+ } else {
+ throw fl::Exception("[import error] format <" + inputFormat + "> "
+ "not supported", FL_AT);
+ }
+
+ engine.reset(importer->fromString(input));
+
+ if ("fld" == outputFormat) {
+ std::map<std::string, std::string>::const_iterator it;
+
+ FldExporter fldExporter;
+ fldExporter.setSeparator("\t");
+ bool exportHeaders = true;
+ if ((it = options.find(KW_DATA_EXPORT_HEADER)) != options.end()) {
+ exportHeaders = ("true" == it->second);
+ }
+ fldExporter.setExportHeader(exportHeaders);
+ bool exportInputValues = true;
+ if ((it = options.find(KW_DATA_EXPORT_INPUTS)) != options.end()) {
+ exportInputValues = ("true" == it->second);
+ }
+ fldExporter.setExportInputValues(exportInputValues);
+ if ((it = options.find(KW_DATA_INPUT)) != options.end()) {
+ std::ifstream dataFile(it->second.c_str());
+ if (not dataFile.is_open()) {
+ throw fl::Exception("[export error] file <" + it->second + "> could not be opened", FL_AT);
+ }
+ try {
+ fldExporter.write(engine.get(), writer, dataFile);
+ } catch (std::exception& ex) {
+ (void) ex;
+ dataFile.close();
+ throw;
+ }
+
+ } else {
+ if ((it = options.find(KW_DATA_MAXIMUM)) != options.end()) {
+ fldExporter.write(engine.get(), writer, (int) fl::Op::toScalar(it->second));
+ } else {
+ std::ostringstream buffer;
+ buffer << "#FuzzyLite Interactive Console (press H for help)\n";
+ buffer << fldExporter.header(engine.get()) << "\n";
+ bool showCout = &writer != &std::cout;
+ writer << buffer.str();
+ if (showCout) std::cout << buffer.str();
+ else writer.flush();
+ interactive(writer, engine.get());
+ }
+ }
+ } else {
+ if ("fll" == outputFormat) {
+ exporter.reset(new FllExporter);
+ } else if ("fcl" == outputFormat) {
+ exporter.reset(new FclExporter);
+ } else if ("fis" == outputFormat) {
+ exporter.reset(new FisExporter);
+ } else if ("cpp" == outputFormat) {
+ exporter.reset(new CppExporter);
+ } else if ("java" == outputFormat) {
+ exporter.reset(new JavaExporter);
+ } else throw fl::Exception("[export error] format <" + outputFormat + "> "
+ "not supported", FL_AT);
+ writer << exporter->toString(engine.get());
+ }
+ }
+
+ int Console::readCharacter() {
+ int ch = 0;
+#ifdef FL_UNIX
+ struct termios oldt, newt;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ ch = getchar();
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+#elif defined(FL_WINDOWS)
+ ch = _getch();
+#endif
+ return ch;
+ }
+
+ void Console::interactive(std::ostream& writer, Engine* engine) {
+ std::ostringstream buffer;
+ buffer << ">";
+ bool showCout = &writer != &std::cout;
+ const std::string space("\t");
+ std::vector<scalar> inputValues;
+ std::ostringstream inputValue;
+ int ch = 0;
+ do {
+ writer << buffer.str();
+ if (showCout) std::cout << buffer.str();
+ else writer.flush();
+ buffer.str("");
+
+ ch = readCharacter();
+
+ if (std::isspace(ch)) {
+ scalar value = engine->getInputVariable(inputValues.size())->getInputValue();
+ try {
+ value = fl::Op::toScalar(inputValue.str());
+ } catch (std::exception& ex) {
+ (void) ex;
+ buffer << "[" << fl::Op::str(value) << "]";
+ }
+ buffer << space;
+ inputValue.str("");
+ inputValues.push_back(value);
+ if (inputValues.size() == engine->inputVariables().size()) {
+ ch = 'P'; //fall through to process;
+ } else continue;
+ }
+
+ if (not std::isgraph(ch)) continue;
+
+ switch (ch) {
+ default:
+ inputValue << char(ch);
+ buffer << char(ch);
+ break;
+ case 'r':
+ case 'R': engine->restart();
+ buffer << "#[Restart]";
+ //fall through
+ case 'd':
+ case 'D': inputValues.clear();
+ buffer << "#[Discard]\n>";
+ inputValue.str("");
+ break;
+ case 'p':
+ case 'P': //Process
+ {
+ inputValue.str("");
+
+ for (std::size_t i = 0; i < inputValues.size(); ++i) {
+ InputVariable* inputVariable = engine->inputVariables().at(i);
+ inputVariable->setInputValue(inputValues.at(i));
+ }
+ std::vector<scalar> missingInputs;
+ for (std::size_t i = inputValues.size(); i < engine->inputVariables().size(); ++i) {
+ InputVariable* inputVariable = engine->inputVariables().at(i);
+ missingInputs.push_back(inputVariable->getInputValue());
+ }
+ inputValues.clear();
+ buffer << fl::Op::join(missingInputs, space);
+ if (not missingInputs.empty()) buffer << space;
+ buffer << "=" << space;
+ try {
+ engine->process();
+ std::vector<scalar> outputValues;
+ for (std::size_t i = 0; i < engine->outputVariables().size(); ++i) {
+ OutputVariable* outputVariable = engine->outputVariables().at(i);
+ outputVariable->defuzzify();
+ outputValues.push_back(outputVariable->getOutputValue());
+ }
+ buffer << fl::Op::join(outputValues, space) << "\n>";
+
+ } catch (std::exception& ex) {
+ buffer << "#[Error: " << ex.what() << "]";
+ }
+ break;
+ }
+ case 'q':
+ case 'Q': buffer << "#[Quit]\n";
+ break;
+ case 'h':
+ case 'H': buffer << "\n>" << interactiveHelp() << "\n>";
+ inputValue.str("");
+ break;
+ }
+ } while (not (ch == 'Q' or ch == 'q'));
+ writer << std::endl;
+ }
+
+ std::string Console::interactiveHelp() {
+ return
+ "#Special Keys\n"
+ "#=============\n"
+ "#\tR\tRestart engine and discard current inputs\n"
+ "#\tD\tDiscard current inputs\n"
+ "#\tP\tProcess engine\n"
+ "#\tQ\tQuit interactive console\n"
+ "#\tH\tShow this help\n"
+ "#=============\n";
+ }
+
+ Engine* Console::mamdani() {
+ Engine* engine = new Engine("simple-dimmer");
+
+ InputVariable* ambient = new InputVariable("Ambient", 0, 1);
+ ambient->addTerm(new Triangle("DARK", .0, .25, .5));
+ ambient->addTerm(new Triangle("MEDIUM", .25, .5, .75));
+ ambient->addTerm(new Triangle("BRIGHT", .5, .75, 1));
+ engine->addInputVariable(ambient);
+
+
+ OutputVariable* power = new OutputVariable("Power", 0, 2);
+ power->setDefaultValue(fl::nan);
+ power->addTerm(new Triangle("LOW", 0.0, 0.5, 1));
+ power->addTerm(new Triangle("MEDIUM", 0.5, 1, 1.5));
+ power->addTerm(new Triangle("HIGH", 1, 1.5, 2));
+ engine->addOutputVariable(power);
+
+ RuleBlock* ruleblock = new RuleBlock();
+ ruleblock->addRule(Rule::parse("if Ambient is DARK then Power is HIGH", engine));
+ ruleblock->addRule(Rule::parse("if Ambient is MEDIUM then Power is MEDIUM", engine));
+ ruleblock->addRule(Rule::parse("if Ambient is BRIGHT then Power is LOW", engine));
+
+ engine->addRuleBlock(ruleblock);
+
+ engine->configure("", "", "Minimum", "Maximum", "Centroid");
+
+ return engine;
+ }
+
+ Engine* Console::takagiSugeno() {
+ Engine* engine = new Engine("approximation of sin(x)/x");
+
+ fl::InputVariable* inputX = new fl::InputVariable("inputX");
+ inputX->setRange(0, 10);
+ inputX->addTerm(new fl::Triangle("NEAR_1", 0, 1, 2));
+ inputX->addTerm(new fl::Triangle("NEAR_2", 1, 2, 3));
+ inputX->addTerm(new fl::Triangle("NEAR_3", 2, 3, 4));
+ inputX->addTerm(new fl::Triangle("NEAR_4", 3, 4, 5));
+ inputX->addTerm(new fl::Triangle("NEAR_5", 4, 5, 6));
+ inputX->addTerm(new fl::Triangle("NEAR_6", 5, 6, 7));
+ inputX->addTerm(new fl::Triangle("NEAR_7", 6, 7, 8));
+ inputX->addTerm(new fl::Triangle("NEAR_8", 7, 8, 9));
+ inputX->addTerm(new fl::Triangle("NEAR_9", 8, 9, 10));
+ engine->addInputVariable(inputX);
+
+
+ fl::OutputVariable* outputFx = new fl::OutputVariable("outputFx");
+ outputFx->setRange(-1, 1);
+ outputFx->setDefaultValue(fl::nan);
+ outputFx->setLockPreviousOutputValue(true); //To use its value with diffFx
+ outputFx->addTerm(new Constant("f1", 0.84));
+ outputFx->addTerm(new Constant("f2", 0.45));
+ outputFx->addTerm(new Constant("f3", 0.04));
+ outputFx->addTerm(new Constant("f4", -0.18));
+ outputFx->addTerm(new Constant("f5", -0.19));
+ outputFx->addTerm(new Constant("f6", -0.04));
+ outputFx->addTerm(new Constant("f7", 0.09));
+ outputFx->addTerm(new Constant("f8", 0.12));
+ outputFx->addTerm(new Constant("f9", 0.04));
+ engine->addOutputVariable(outputFx);
+
+ fl::OutputVariable* trueFx = new fl::OutputVariable("trueFx");
+ trueFx->setRange(fl::nan, fl::nan);
+ trueFx->setLockPreviousOutputValue(true); //To use its value with diffFx
+ trueFx->addTerm(fl::Function::create("fx", "sin(inputX)/inputX", engine));
+ engine->addOutputVariable(trueFx);
+
+ fl::OutputVariable* diffFx = new fl::OutputVariable("diffFx");
+ diffFx->addTerm(fl::Function::create("diff", "fabs(outputFx-trueFx)", engine));
+ diffFx->setRange(fl::nan, fl::nan);
+ // diffFx->setLockValidOutput(true); //To use in input diffPreviousFx
+ engine->addOutputVariable(diffFx);
+
+ fl::RuleBlock* block = new fl::RuleBlock();
+ block->addRule(fl::Rule::parse("if inputX is NEAR_1 then outputFx is f1", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_2 then outputFx is f2", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_3 then outputFx is f3", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_4 then outputFx is f4", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_5 then outputFx is f5", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_6 then outputFx is f6", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_7 then outputFx is f7", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_8 then outputFx is f8", engine));
+ block->addRule(fl::Rule::parse("if inputX is NEAR_9 then outputFx is f9", engine));
+ block->addRule(fl::Rule::parse("if inputX is any then trueFx is fx and diffFx is diff", engine));
+ engine->addRuleBlock(block);
+
+ engine->configure("", "", "AlgebraicProduct", "AlgebraicSum", "WeightedAverage");
+
+ return engine;
+ }
+
+ void Console::exportAllExamples(const std::string& from, const std::string& to) {
+ Console::exportAllExamples(from, to, ".");
+ }
+
+ void Console::exportAllExamples(const std::string& from, const std::string& to, const std::string& path) {
+ std::vector<std::string> examples;
+ examples.push_back("/mamdani/AllTerms");
+ // examples.push_back("/mamdani/Laundry");
+ examples.push_back("/mamdani/SimpleDimmer");
+ // examples.push_back("/mamdani/SimpleDimmerInverse");
+ examples.push_back("/mamdani/matlab/mam21");
+ examples.push_back("/mamdani/matlab/mam22");
+ examples.push_back("/mamdani/matlab/shower");
+ examples.push_back("/mamdani/matlab/tank");
+ examples.push_back("/mamdani/matlab/tank2");
+ examples.push_back("/mamdani/matlab/tipper");
+ examples.push_back("/mamdani/matlab/tipper1");
+ examples.push_back("/mamdani/octave/investment_portfolio");
+ examples.push_back("/mamdani/octave/mamdani_tip_calculator");
+ examples.push_back("/takagi-sugeno/approximation");
+ examples.push_back("/takagi-sugeno/SimpleDimmer");
+ examples.push_back("/takagi-sugeno/matlab/fpeaks");
+ examples.push_back("/takagi-sugeno/matlab/invkine1");
+ examples.push_back("/takagi-sugeno/matlab/invkine2");
+ examples.push_back("/takagi-sugeno/matlab/juggler");
+ examples.push_back("/takagi-sugeno/matlab/membrn1");
+ examples.push_back("/takagi-sugeno/matlab/membrn2");
+ examples.push_back("/takagi-sugeno/matlab/slbb");
+ examples.push_back("/takagi-sugeno/matlab/slcp");
+ examples.push_back("/takagi-sugeno/matlab/slcp1");
+ examples.push_back("/takagi-sugeno/matlab/slcpp1");
+ examples.push_back("/takagi-sugeno/matlab/sltbu_fl");
+ examples.push_back("/takagi-sugeno/matlab/sugeno1");
+ examples.push_back("/takagi-sugeno/matlab/tanksg");
+ examples.push_back("/takagi-sugeno/matlab/tippersg");
+ examples.push_back("/takagi-sugeno/octave/cubic_approximator");
+ examples.push_back("/takagi-sugeno/octave/heart_disease_risk");
+ examples.push_back("/takagi-sugeno/octave/linear_tip_calculator");
+ examples.push_back("/takagi-sugeno/octave/sugeno_tip_calculator");
+ examples.push_back("/tsukamoto/tsukamoto");
+
+ std::string sourceBase = path + "/original";
+ std::string targetBase = path + "/tmp/";
+
+ FL_unique_ptr<Importer> importer;
+ if (from == "fll") importer.reset(new FllImporter);
+ else if (from == "fis") importer.reset(new FisImporter);
+ else if (from == "fcl") importer.reset(new FclImporter);
+ else throw fl::Exception("[examples error] unrecognized format <" + from + "> to import", FL_AT);
+
+ FL_unique_ptr<Exporter> exporter;
+ if (to == "fll") exporter.reset(new FllExporter);
+ else if (to == "fld") exporter.reset(new FldExporter(" "));
+ else if (to == "fcl") exporter.reset(new FclExporter);
+ else if (to == "fis") exporter.reset(new FisExporter);
+ else if (to == "cpp") exporter.reset(new CppExporter);
+ else if (to == "java") exporter.reset(new JavaExporter);
+ else throw fl::Exception("[examples error] unrecognized format <" + to + "> to export", FL_AT);
+
+ std::vector<std::pair<Exporter*, Importer*> > tests;
+ tests.push_back(std::pair<Exporter*, Importer*>(new FllExporter, new FllImporter));
+ tests.push_back(std::pair<Exporter*, Importer*>(new FisExporter, new FisImporter));
+ tests.push_back(std::pair<Exporter*, Importer*>(new FclExporter, new FclImporter));
+ for (std::size_t i = 0; i < examples.size(); ++i) {
+ FL_LOG("Processing " << (i + 1) << "/" << examples.size() << ": " << examples.at(i));
+ std::ostringstream ss;
+ std::string input = sourceBase + examples.at(i) + "." + from;
+ std::ifstream source(input.c_str());
+ if (source.is_open()) {
+ std::string line;
+ while (source.good()) {
+ std::getline(source, line);
+ ss << line << "\n";
+ }
+ source.close();
+ } else throw fl::Exception("[examples error] file not found: " + input, FL_AT);
+
+ FL_unique_ptr<Engine> engine(importer->fromString(ss.str()));
+
+ for (std::size_t t = 0; t < tests.size(); ++t) {
+ std::string out = tests.at(t).first->toString(engine.get());
+ FL_unique_ptr<Engine> copy(tests.at(t).second->fromString(out));
+ std::string out_copy = tests.at(t).first->toString(copy.get());
+
+ if (out != out_copy) {
+ std::ostringstream ss;
+ ss << "[imex error] different results <"
+ << importer->name() << "," << exporter->name() << "> "
+ "at " + examples.at(t) + "." + from + ":\n";
+ ss << "<Engine A>\n" << out << "\n\n" <<
+ "================================\n\n" <<
+ "<Engine B>\n" << out_copy;
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+ }
+
+ std::string output = targetBase + examples.at(i) + "." + to;
+ std::ofstream target(output.c_str());
+ if (target.is_open()) {
+ if (to == "cpp") {
+ target << "#include <fl/Headers.h>\n\n"
+ << "int main(int argc, char** argv){\n"
+ << exporter->toString(engine.get())
+ << "\n}\n";
+ } else if (to == "java") {
+ std::string className = examples.at(i).substr(examples.at(i).find_last_of('/') + 1);
+ target << "import com.fuzzylite.*;\n"
+ << "import com.fuzzylite.defuzzifier.*;\n"
+ << "import com.fuzzylite.factory.*;\n"
+ << "import com.fuzzylite.hedge.*;\n"
+ << "import com.fuzzylite.imex.*;\n"
+ << "import com.fuzzylite.norm.*;\n"
+ << "import com.fuzzylite.norm.s.*;\n"
+ << "import com.fuzzylite.norm.t.*;\n"
+ << "import com.fuzzylite.rule.*;\n"
+ << "import com.fuzzylite.term.*;\n"
+ << "import com.fuzzylite.variable.*;\n\n"
+ << "public class " << Op::validName(className) << "{\n"
+ << "public static void main(String[] args){\n"
+ << exporter->toString(engine.get())
+ << "\n}\n}\n";
+ } else {
+ target << exporter->toString(engine.get());
+ }
+ target.close();
+ }
+ Engine copyConstructor(*engine.get());
+ (void) copyConstructor;
+ Engine assignmentOperator = *engine.get();
+ (void) assignmentOperator;
+ }
+ for (std::size_t i = 0; i < tests.size(); ++i) {
+ delete tests.at(i).first;
+ delete tests.at(i).second;
+ }
+ }
+
+#ifdef FL_CPP11
+
+ void Console::benchmarkExamples(const std::string& path, int runs) {
+ std::string sourceBase = path + "/original";
+ typedef std::pair<std::string, int > Example;
+ std::vector<Example> examples;
+ examples.push_back(Example("/mamdani/AllTerms", 1e4));
+ examples.push_back(Example("/mamdani/SimpleDimmer", 1e5));
+ examples.push_back(Example("/mamdani/matlab/mam21", 128));
+ examples.push_back(Example("/mamdani/matlab/mam22", 128));
+ examples.push_back(Example("/mamdani/matlab/shower", 256));
+ examples.push_back(Example("/mamdani/matlab/tank", 256));
+ examples.push_back(Example("/mamdani/matlab/tank2", 512));
+ examples.push_back(Example("/mamdani/matlab/tipper", 256));
+ examples.push_back(Example("/mamdani/matlab/tipper1", 1e5));
+ examples.push_back(Example("/mamdani/octave/investment_portfolio", 256));
+ examples.push_back(Example("/mamdani/octave/mamdani_tip_calculator", 256));
+ examples.push_back(Example("/takagi-sugeno/approximation", 1e6));
+ examples.push_back(Example("/takagi-sugeno/SimpleDimmer", 2e6));
+ examples.push_back(Example("/takagi-sugeno/matlab/fpeaks", 512));
+ examples.push_back(Example("/takagi-sugeno/matlab/invkine1", 256));
+ examples.push_back(Example("/takagi-sugeno/matlab/invkine2", 256));
+ examples.push_back(Example("/takagi-sugeno/matlab/juggler", 512));
+ examples.push_back(Example("/takagi-sugeno/matlab/membrn1", 1024));
+ examples.push_back(Example("/takagi-sugeno/matlab/membrn2", 512));
+ examples.push_back(Example("/takagi-sugeno/matlab/slbb", 20));
+ examples.push_back(Example("/takagi-sugeno/matlab/slcp", 20));
+ examples.push_back(Example("/takagi-sugeno/matlab/slcp1", 15));
+ examples.push_back(Example("/takagi-sugeno/matlab/slcpp1", 9));
+ examples.push_back(Example("/takagi-sugeno/matlab/sltbu_fl", 128));
+ examples.push_back(Example("/takagi-sugeno/matlab/sugeno1", 2e6));
+ examples.push_back(Example("/takagi-sugeno/matlab/tanksg", 1024));
+ examples.push_back(Example("/takagi-sugeno/matlab/tippersg", 1024));
+ examples.push_back(Example("/takagi-sugeno/octave/cubic_approximator", 2e6));
+ examples.push_back(Example("/takagi-sugeno/octave/heart_disease_risk", 1024));
+ examples.push_back(Example("/takagi-sugeno/octave/linear_tip_calculator", 1024));
+ examples.push_back(Example("/takagi-sugeno/octave/sugeno_tip_calculator", 512));
+ examples.push_back(Example("/tsukamoto/tsukamoto", 1e6));
+
+ for (std::size_t i = 0; i < examples.size(); ++i) {
+ FL_LOG(examples.at(i).first << "\t" << examples.at(i).second);
+ }
+
+ std::vector<std::string> runNumbers(runs);
+ for (int i = 0; i < runs; ++i) {
+ runNumbers.at(i) = std::to_string(i + 1);
+ }
+ std::string spacedPath(40, ' ');
+ std::copy(path.begin(), path.end(), spacedPath.begin());
+ FL_LOG(spacedPath << "\t" << "mean\tstdev\n" << Op::join(runNumbers, "\t"));
+
+ FllImporter importer;
+ FldExporter exporter;
+ exporter.setExportHeader(false);
+ exporter.setExportInputValues(false);
+ exporter.setExportOutputValues(false);
+ std::ostream dummy(0);
+
+ for (std::size_t e = 0; e < examples.size(); ++e) {
+ FL_unique_ptr<Engine> engine(importer.fromFile(sourceBase + examples.at(e).first + ".fll"));
+
+ std::vector<scalar> seconds;
+ int results = std::pow(1.0 * examples.at(e).second, engine->numberOfInputVariables());
+
+ for (int r = 0; r < runs; ++r) {
+ auto start = std::chrono::system_clock::now();
+ exporter.write(engine.get(), dummy, results);
+ auto end = std::chrono::system_clock::now();
+
+ auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds> (end - start);
+
+ seconds.push_back(elapsed.count() / 1e3);
+ }
+ scalar mean = Op::mean(seconds);
+ scalar stdev = Op::standardDeviation(seconds, mean);
+
+ std::string spacedExample(40, ' ');
+ std::string exampleName = examples.at(e).first;
+ std::copy(exampleName.begin(), exampleName.end(), spacedExample.begin());
+ FL_LOG(spacedExample << "\t" << fl::Op::str(mean) << "\t" << fl::Op::str(stdev) << "\n" <<
+ Op::join(seconds, "\t"));
+ }
+ }
+#endif
+
+ int Console::main(int argc, char** argv) {
+ (void) argc;
+ (void) argv;
+ if (argc <= 2) {
+ std::cout << usage() << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ const std::string firstArgument = std::string(argv[1]);
+
+ if (firstArgument == "export-examples") {
+ std::string path = ".";
+ if (argc > 2) {
+ path = std::string(argv[2]);
+ }
+ FL_LOG("Path=" << path);
+ try {
+ fuzzylite::setDecimals(3);
+ FL_LOG("Processing fll->fll");
+ exportAllExamples("fll", "fll", path);
+ FL_LOG("Processing fll->fcl");
+ exportAllExamples("fll", "fcl", path);
+ FL_LOG("Processing fll->fis");
+ exportAllExamples("fll", "fis", path);
+ FL_LOG("Processing fll->cpp");
+ exportAllExamples("fll", "cpp", path);
+ FL_LOG("Processing fll->java");
+ exportAllExamples("fll", "java", path);
+ fuzzylite::setDecimals(8);
+ fuzzylite::setMachEps(1e-6);
+ FL_LOG("Processing fll->fld");
+ exportAllExamples("fll", "fld", path);
+ } catch (std::exception& ex) {
+ std::cout << ex.what() << "\nBACKTRACE:\n" <<
+ fl::Exception::btCallStack() << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ } else if (firstArgument == "benchmarks") {
+#ifdef FL_CPP11
+ std::string path = ".";
+ if (argc > 2) {
+ path = std::string(argv[2]);
+ }
+ int runs = 10;
+ if (argc > 3) {
+ runs = (int) Op::toScalar(argv[3]);
+ }
+ fuzzylite::setDecimals(3);
+ Console::benchmarkExamples(path, runs);
+ return EXIT_SUCCESS;
+#else
+ throw fl::Exception("[benchmarks error] implementation available only when built with C++11 (-DFL_CPP11)", FL_AT);
+#endif
+ }
+
+ try {
+ std::map<std::string, std::string> options = parse(argc, argv);
+ process(options);
+ } catch (std::exception& ex) {
+ std::cout << ex.what() << "\n" << std::endl;
+ // std::cout << fl::Exception::btCallStack() << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ }
+}
diff --git a/fuzzylite/src/Engine.cpp b/fuzzylite/src/Engine.cpp
new file mode 100644
index 0000000..f1e23c0
--- /dev/null
+++ b/fuzzylite/src/Engine.cpp
@@ -0,0 +1,698 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/Engine.h"
+
+#include "fl/defuzzifier/WeightedAverage.h"
+#include "fl/defuzzifier/WeightedSum.h"
+#include "fl/factory/DefuzzifierFactory.h"
+#include "fl/factory/FactoryManager.h"
+#include "fl/factory/SNormFactory.h"
+#include "fl/factory/TNormFactory.h"
+#include "fl/hedge/Hedge.h"
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/t/AlgebraicProduct.h"
+#include "fl/rule/Consequent.h"
+#include "fl/rule/Expression.h"
+#include "fl/rule/Rule.h"
+#include "fl/rule/RuleBlock.h"
+#include "fl/term/Accumulated.h"
+#include "fl/term/Constant.h"
+#include "fl/term/Linear.h"
+#include "fl/term/Function.h"
+#include "fl/term/Ramp.h"
+#include "fl/term/Sigmoid.h"
+#include "fl/term/SShape.h"
+#include "fl/term/ZShape.h"
+#include "fl/variable/InputVariable.h"
+#include "fl/variable/OutputVariable.h"
+
+
+namespace fl {
+
+ Engine::Engine(const std::string& name) : _name(name) {
+ }
+
+ Engine::Engine(const Engine& other) : _name("") {
+ copyFrom(other);
+ }
+
+ Engine& Engine::operator=(const Engine& other) {
+ if (this != &other) {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i)
+ delete _ruleblocks.at(i);
+ _ruleblocks.clear();
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i)
+ delete _outputVariables.at(i);
+ _outputVariables.clear();
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i)
+ delete _inputVariables.at(i);
+ _inputVariables.clear();
+
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ void Engine::copyFrom(const Engine& other) {
+ _name = other._name;
+ for (std::size_t i = 0; i < other._inputVariables.size(); ++i)
+ _inputVariables.push_back(new InputVariable(*other._inputVariables.at(i)));
+ for (std::size_t i = 0; i < other._outputVariables.size(); ++i)
+ _outputVariables.push_back(new OutputVariable(*other._outputVariables.at(i)));
+
+ updateReferences();
+
+ for (std::size_t i = 0; i < other._ruleblocks.size(); ++i) {
+ RuleBlock* ruleBlock = new RuleBlock(*other._ruleblocks.at(i));
+ try {
+ ruleBlock->loadRules(this);
+ } catch (...) {
+ }
+ _ruleblocks.push_back(ruleBlock);
+ }
+ }
+
+ void Engine::updateReferences() const {
+ std::vector<Variable*> myVariables = variables();
+ for (std::size_t i = 0; i < myVariables.size(); ++i) {
+ Variable* variable = myVariables.at(i);
+ for (int t = 0; t < variable->numberOfTerms(); ++t) {
+ Term::updateReference(variable->getTerm(t), this);
+ }
+ }
+ }
+
+ Engine::~Engine() {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) delete _ruleblocks.at(i);
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) delete _outputVariables.at(i);
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) delete _inputVariables.at(i);
+ }
+
+ void Engine::configure(const std::string& conjunctionT, const std::string& disjunctionS,
+ const std::string& activationT, const std::string& accumulationS,
+ const std::string& defuzzifierName, int resolution) {
+ TNormFactory* tnormFactory = FactoryManager::instance()->tnorm();
+ SNormFactory* snormFactory = FactoryManager::instance()->snorm();
+ DefuzzifierFactory* defuzzFactory = FactoryManager::instance()->defuzzifier();
+ TNorm* conjunction = tnormFactory->constructObject(conjunctionT);
+ SNorm* disjunction = snormFactory->constructObject(disjunctionS);
+ TNorm* activation = tnormFactory->constructObject(activationT);
+ SNorm* accumulation = snormFactory->constructObject(accumulationS);
+ Defuzzifier* defuzzifier = defuzzFactory->constructObject(defuzzifierName);
+ IntegralDefuzzifier* integralDefuzzifier = dynamic_cast<IntegralDefuzzifier*> (defuzzifier);
+ if (integralDefuzzifier) integralDefuzzifier->setResolution(resolution);
+
+ configure(conjunction, disjunction, activation, accumulation, defuzzifier);
+ }
+
+ void Engine::configure(TNorm* conjunction, SNorm* disjunction,
+ TNorm* activation, SNorm* accumulation, Defuzzifier* defuzzifier) {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ _ruleblocks.at(i)->setConjunction(conjunction ? conjunction->clone() : fl::null);
+ _ruleblocks.at(i)->setDisjunction(disjunction ? disjunction->clone() : fl::null);
+ _ruleblocks.at(i)->setActivation(activation ? activation->clone() : fl::null);
+ }
+
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ _outputVariables.at(i)->setDefuzzifier(defuzzifier ? defuzzifier->clone() : fl::null);
+ _outputVariables.at(i)->fuzzyOutput()->setAccumulation(
+ accumulation ? accumulation->clone() : fl::null);
+ }
+ if (defuzzifier) delete defuzzifier;
+ if (accumulation) delete accumulation;
+ if (activation) delete activation;
+ if (disjunction) delete disjunction;
+ if (conjunction) delete conjunction;
+ }
+
+ bool Engine::isReady(std::string* status) const {
+ std::ostringstream ss;
+ if (_inputVariables.empty()) {
+ ss << "- Engine <" << _name << "> has no input variables\n";
+ }
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ InputVariable* inputVariable = _inputVariables.at(i);
+ if (not inputVariable) {
+ ss << "- Engine <" << _name << "> has a fl::null input variable at index <" << i << ">\n";
+ } else if (inputVariable->terms().empty()) {
+ //ignore because sometimes inputs can be empty: takagi-sugeno/matlab/slcpp1.fis
+ // ss << "- Input variable <" << _inputVariables.at(i)->getName() << ">"
+ // << " has no terms\n";
+ }
+ }
+
+ if (_outputVariables.empty()) {
+ ss << "- Engine <" << _name << "> has no output variables\n";
+ }
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ if (not outputVariable) {
+ ss << "- Engine <" << _name << "> has a fl::null output variable at index <" << i << ">\n";
+ } else {
+ if (outputVariable->terms().empty()) {
+ ss << "- Output variable <" << outputVariable->getName() << ">"
+ << " has no terms\n";
+ }
+ Defuzzifier* defuzzifier = outputVariable->getDefuzzifier();
+ if (not defuzzifier) {
+ ss << "- Output variable <" << outputVariable->getName() << ">"
+ << " has no defuzzifier\n";
+ }
+ SNorm* accumulation = outputVariable->fuzzyOutput()->getAccumulation();
+ if (not accumulation and dynamic_cast<IntegralDefuzzifier*> (defuzzifier)) {
+ ss << "- Output variable <" << outputVariable->getName() << ">"
+ << " has no accumulation operator\n";
+ }
+ }
+ }
+
+ if (_ruleblocks.empty()) {
+ ss << "- Engine <" << _name << "> has no rule blocks\n";
+ }
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ RuleBlock* ruleblock = _ruleblocks.at(i);
+ if (not ruleblock) {
+ ss << "- Engine <" << _name << "> has a fl::null rule block at index <" << i << ">\n";
+ } else {
+ if (ruleblock->rules().empty()) {
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no rules\n";
+ }
+ int requiresConjunction = 0;
+ int requiresDisjunction = 0;
+ int requiresActivation = 0;
+ for (int r = 0; r < ruleblock->numberOfRules(); ++r) {
+ Rule* rule = ruleblock->getRule(r);
+ if (not rule) {
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName()
+ << "> has a fl::null rule at index <" << r << ">\n";
+ } else {
+ std::size_t thenIndex = rule->getText().find(" " + Rule::thenKeyword() + " ");
+ std::size_t andIndex = rule->getText().find(" " + Rule::andKeyword() + " ");
+ std::size_t orIndex = rule->getText().find(" " + Rule::orKeyword() + " ");
+ if (andIndex != std::string::npos and andIndex < thenIndex) {
+ ++requiresConjunction;
+ }
+ if (orIndex != std::string::npos and orIndex < thenIndex) {
+ ++requiresDisjunction;
+ }
+ if (rule->isLoaded()) {
+ Consequent* consequent = rule->getConsequent();
+ for (std::size_t c = 0; c < consequent->conclusions().size(); ++c) {
+ Proposition* proposition = consequent->conclusions().at(c);
+ const OutputVariable* outputVariable =
+ dynamic_cast<const OutputVariable*> (proposition->variable);
+ if (outputVariable and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier())) {
+ ++requiresActivation;
+ break;
+ }
+ }
+ }
+ }
+ }
+ const TNorm* conjunction = ruleblock->getConjunction();
+ if (requiresConjunction > 0 and not conjunction) {
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no conjunction operator\n";
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
+ << requiresConjunction << " rules that require conjunction operator\n";
+ }
+ const SNorm* disjunction = ruleblock->getDisjunction();
+ if (requiresDisjunction > 0 and not disjunction) {
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no disjunction operator\n";
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
+ << requiresDisjunction << " rules that require disjunction operator\n";
+ }
+ const TNorm* activation = ruleblock->getActivation();
+ if (requiresActivation > 0 and not activation) {
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no activation operator\n";
+ ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
+ << requiresActivation << " rules that require activation operator\n";
+ }
+ }
+ }
+ if (status) *status = ss.str();
+ return ss.str().empty();
+ }
+
+ void Engine::restart() {
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ _inputVariables.at(i)->setInputValue(fl::nan);
+ }
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ _outputVariables.at(i)->clear();
+ }
+ }
+
+ void Engine::process() {
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ _outputVariables.at(i)->fuzzyOutput()->clear();
+ }
+
+ FL_DEBUG_BEGIN;
+ FL_DBG("===============");
+ FL_DBG("CURRENT INPUTS:");
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ InputVariable* inputVariable = _inputVariables.at(i);
+ scalar inputValue = inputVariable->getInputValue();
+ if (inputVariable->isEnabled()) {
+ FL_DBG(inputVariable->getName() << ".input = " << Op::str(inputValue));
+ FL_DBG(inputVariable->getName() << ".fuzzy = " << inputVariable->fuzzify(inputValue));
+ } else {
+ FL_DBG(inputVariable->getName() << ".enabled = false");
+ }
+ }
+ FL_DEBUG_END;
+
+
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ RuleBlock* ruleBlock = _ruleblocks.at(i);
+ if (ruleBlock->isEnabled()) {
+ ruleBlock->activate();
+ }
+ }
+
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ _outputVariables.at(i)->defuzzify();
+ }
+
+ FL_DEBUG_BEGIN;
+ FL_DBG("===============");
+ FL_DBG("CURRENT OUTPUTS:");
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ if (outputVariable->isEnabled()) {
+ FL_DBG(outputVariable->getName() << ".default = "
+ << outputVariable->getDefaultValue());
+
+ FL_DBG(outputVariable->getName() << ".lockValueInRange = "
+ << outputVariable->isLockedOutputValueInRange());
+
+ FL_DBG(outputVariable->getName() << ".lockPreviousValue= "
+ << outputVariable->isLockedPreviousOutputValue());
+
+ scalar output = outputVariable->getOutputValue();
+ FL_DBG(outputVariable->getName() << ".output = " << output);
+ FL_DBG(outputVariable->getName() << ".fuzzy = " <<
+ outputVariable->fuzzify(output));
+ FL_DBG(outputVariable->fuzzyOutput()->toString());
+ } else {
+ FL_DBG(outputVariable->getName() << ".enabled = false");
+ }
+ }
+ FL_DBG("==============");
+ FL_DEBUG_END;
+ }
+
+ void Engine::setName(const std::string& name) {
+ this->_name = name;
+ }
+
+ std::string Engine::getName() const {
+ return this->_name;
+ }
+
+ std::string Engine::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ Engine::Type Engine::type(std::string* name, std::string* reason) const {
+ if (_outputVariables.empty()) {
+ if (name) *name = "Unknown";
+ if (reason) *reason = "- Engine has no output variables";
+ return Engine::Unknown;
+ }
+
+ //Mamdani
+ bool mamdani = true;
+ for (std::size_t i = 0; mamdani and i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ //Defuzzifier must be integral
+ mamdani = mamdani and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier());
+ }
+ //Larsen
+ bool larsen = mamdani and not _ruleblocks.empty();
+ //Larsen is Mamdani with AlgebraicProduct as Activation
+ if (mamdani) {
+ for (std::size_t i = 0; larsen and i < _ruleblocks.size(); ++i) {
+ RuleBlock* ruleBlock = _ruleblocks.at(i);
+ larsen = larsen and dynamic_cast<const AlgebraicProduct*> (ruleBlock->getActivation());
+ }
+ }
+ if (larsen) {
+ if (name) *name = "Larsen";
+ if (reason) *reason = "- Output variables have integral defuzzifiers\n"
+ "- Rule blocks activate using the algebraic product T-Norm";
+ return Engine::Larsen;
+ }
+ if (mamdani) {
+ if (name) *name = "Mamdani";
+ if (reason) *reason = "-Output variables have integral defuzzifiers";
+ return Engine::Mamdani;
+ }
+ //Else, keep checking
+
+ //TakagiSugeno
+ bool takagiSugeno = true;
+ for (std::size_t i = 0; takagiSugeno and i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ //Defuzzifier is Weighted
+ WeightedDefuzzifier* weightedDefuzzifier =
+ dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
+
+ takagiSugeno = takagiSugeno and weightedDefuzzifier and
+ (weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
+ weightedDefuzzifier->getType() == WeightedDefuzzifier::TakagiSugeno);
+
+ if (takagiSugeno) {
+ //Takagi-Sugeno has only Constant, Linear or Function terms
+ for (int t = 0; takagiSugeno and t < outputVariable->numberOfTerms(); ++t) {
+ Term* term = outputVariable->getTerm(t);
+ takagiSugeno = takagiSugeno and
+ weightedDefuzzifier->inferType(term) == WeightedDefuzzifier::TakagiSugeno;
+ }
+ }
+ }
+ if (takagiSugeno) {
+ if (name) *name = "Takagi-Sugeno";
+ if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
+ "- Output variables have constant, linear or function terms";
+ return Engine::TakagiSugeno;
+ }
+
+ //Tsukamoto
+ bool tsukamoto = true;
+ for (std::size_t i = 0; tsukamoto and i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ //Defuzzifier is Weighted
+ WeightedDefuzzifier* weightedDefuzzifier =
+ dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
+
+ tsukamoto = tsukamoto and weightedDefuzzifier and
+ (weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
+ weightedDefuzzifier->getType() == WeightedDefuzzifier::Tsukamoto);
+ if (tsukamoto) {
+ //Tsukamoto has only monotonic terms: Concave, Ramp, Sigmoid, SShape, or ZShape
+ for (int t = 0; tsukamoto and t < outputVariable->numberOfTerms(); ++t) {
+ Term* term = outputVariable->getTerm(t);
+ tsukamoto = tsukamoto and weightedDefuzzifier->isMonotonic(term);
+ }
+ }
+ }
+ if (tsukamoto) {
+ if (name) *name = "Tsukamoto";
+ if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
+ "- Output variables only have monotonic terms";
+ return Engine::Tsukamoto;
+ }
+
+ //Inverse Tsukamoto
+ bool inverseTsukamoto = true;
+ for (std::size_t i = 0; inverseTsukamoto and i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ //Defuzzifier cannot be integral
+ WeightedDefuzzifier* weightedDefuzzifier =
+ dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
+ inverseTsukamoto = inverseTsukamoto and weightedDefuzzifier;
+ }
+ if (inverseTsukamoto) {
+ if (name) *name = "Inverse Tsukamoto";
+ if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
+ "- Output variables do not only have constant, linear or function terms\n"
+ "- Output variables do not only have monotonic terms";
+ return Engine::InverseTsukamoto;
+ }
+
+ bool hybrid = true;
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ OutputVariable* outputVariable = _outputVariables.at(i);
+ //Output variables have non-fl::null defuzzifiers
+ hybrid = hybrid and outputVariable->getDefuzzifier();
+ }
+ if (hybrid) {
+ if (name) *name = "Hybrid";
+ if (reason) *reason = "- Output variables have different defuzzifiers";
+ return Engine::Hybrid;
+ }
+
+ if (name) *name = "Unknown";
+ if (reason) *reason = "- There are output variables without a defuzzifier";
+ return Engine::Unknown;
+ }
+
+ Engine* Engine::clone() const {
+ return new Engine(*this);
+ }
+
+ std::vector<Variable*> Engine::variables() const {
+ std::vector<Variable*> result;
+ result.reserve(_inputVariables.size() + _outputVariables.size());
+ result.insert(result.end(), _inputVariables.begin(), _inputVariables.end());
+ result.insert(result.end(), _outputVariables.begin(), _outputVariables.end());
+ return result;
+ }
+
+ /**
+ * Operations for InputVariables
+ */
+ void Engine::setInputValue(const std::string& name, scalar value) {
+ InputVariable* inputVariable = getInputVariable(name);
+ inputVariable->setInputValue(value);
+ }
+
+ void Engine::addInputVariable(InputVariable* inputVariable) {
+ this->_inputVariables.push_back(inputVariable);
+ }
+
+ InputVariable* Engine::setInputVariable(InputVariable* inputVariable, int index) {
+ InputVariable* result = this->_inputVariables.at(index);
+ this->_inputVariables.at(index) = inputVariable;
+ return result;
+ }
+
+ void Engine::insertInputVariable(InputVariable* inputVariable, int index) {
+ this->_inputVariables.insert(this->_inputVariables.begin() + index,
+ inputVariable);
+ }
+
+ InputVariable* Engine::getInputVariable(int index) const {
+ return this->_inputVariables.at(index);
+ }
+
+ InputVariable* Engine::getInputVariable(const std::string& name) const {
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ if (_inputVariables.at(i)->getName() == name)
+ return _inputVariables.at(i);
+ }
+ throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT);
+ }
+
+ bool Engine::hasInputVariable(const std::string& name) const {
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ if (_inputVariables.at(i)->getName() == name)
+ return true;
+ }
+ return false;
+ }
+
+ InputVariable* Engine::removeInputVariable(int index) {
+ InputVariable* result = this->_inputVariables.at(index);
+ this->_inputVariables.erase(this->_inputVariables.begin() + index);
+ return result;
+ }
+
+ InputVariable* Engine::removeInputVariable(const std::string& name) {
+ for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
+ if (_inputVariables.at(i)->getName() == name) {
+ InputVariable* result = this->_inputVariables.at(i);
+ this->_inputVariables.erase(this->_inputVariables.begin() + i);
+ return result;
+ }
+ }
+ throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT);
+ }
+
+ int Engine::numberOfInputVariables() const {
+ return this->_inputVariables.size();
+ }
+
+ const std::vector<InputVariable*>& Engine::inputVariables() const {
+ return this->_inputVariables;
+ }
+
+ void Engine::setInputVariables(const std::vector<InputVariable*>& inputVariables) {
+ this->_inputVariables = inputVariables;
+ }
+
+ std::vector<InputVariable*>& Engine::inputVariables() {
+ return this->_inputVariables;
+ }
+
+ /**
+ * Operations for OutputVariables
+ */
+ scalar Engine::getOutputValue(const std::string& name) {
+ OutputVariable* outputVariable = getOutputVariable(name);
+ return outputVariable->getOutputValue();
+ }
+
+ void Engine::addOutputVariable(OutputVariable* outputVariable) {
+ this->_outputVariables.push_back(outputVariable);
+ }
+
+ OutputVariable* Engine::setOutputVariable(OutputVariable* outputVariable, int index) {
+ OutputVariable* result = this->_outputVariables.at(index);
+ this->_outputVariables.at(index) = outputVariable;
+ return result;
+ }
+
+ void Engine::insertOutputVariable(OutputVariable* outputVariable, int index) {
+ this->_outputVariables.insert(this->_outputVariables.begin() + index,
+ outputVariable);
+ }
+
+ OutputVariable* Engine::getOutputVariable(int index) const {
+ return this->_outputVariables.at(index);
+ }
+
+ OutputVariable* Engine::getOutputVariable(const std::string& name) const {
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ if (_outputVariables.at(i)->getName() == name)
+ return _outputVariables.at(i);
+ }
+ throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT);
+ }
+
+ bool Engine::hasOutputVariable(const std::string& name) const {
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ if (_outputVariables.at(i)->getName() == name)
+ return true;
+ }
+ return false;
+ }
+
+ OutputVariable* Engine::removeOutputVariable(int index) {
+ OutputVariable* result = this->_outputVariables.at(index);
+ this->_outputVariables.erase(this->_outputVariables.begin() + index);
+ return result;
+ }
+
+ OutputVariable* Engine::removeOutputVariable(const std::string& name) {
+ for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
+ if (_outputVariables.at(i)->getName() == name) {
+ OutputVariable* result = this->_outputVariables.at(i);
+ this->_outputVariables.erase(this->_outputVariables.begin() + i);
+ return result;
+ }
+ }
+ throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT);
+ }
+
+ int Engine::numberOfOutputVariables() const {
+ return this->_outputVariables.size();
+ }
+
+ const std::vector<OutputVariable*>& Engine::outputVariables() const {
+ return this->_outputVariables;
+ }
+
+ void Engine::setOutputVariables(const std::vector<OutputVariable*>& outputVariables) {
+ this->_outputVariables = outputVariables;
+ }
+
+ std::vector<OutputVariable*>& Engine::outputVariables() {
+ return this->_outputVariables;
+ }
+
+ /**
+ * Operations for iterable datatype _ruleblocks
+ */
+ void Engine::addRuleBlock(RuleBlock* ruleblock) {
+ this->_ruleblocks.push_back(ruleblock);
+ }
+
+ RuleBlock* Engine::setRuleBlock(RuleBlock* ruleBlock, int index) {
+ RuleBlock* result = this->_ruleblocks.at(index);
+ this->_ruleblocks.at(index) = ruleBlock;
+ return result;
+ }
+
+ void Engine::insertRuleBlock(RuleBlock* ruleblock, int index) {
+ this->_ruleblocks.insert(this->_ruleblocks.begin() + index, ruleblock);
+ }
+
+ RuleBlock* Engine::getRuleBlock(int index) const {
+ return this->_ruleblocks.at(index);
+ }
+
+ RuleBlock* Engine::getRuleBlock(const std::string& name) const {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ if (_ruleblocks.at(i)->getName() == name)
+ return _ruleblocks.at(i);
+ }
+ throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT);
+ }
+
+ bool Engine::hasRuleBlock(const std::string& name) const {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ if (_ruleblocks.at(i)->getName() == name)
+ return true;
+ }
+ return false;
+ }
+
+ RuleBlock* Engine::removeRuleBlock(int index) {
+ RuleBlock* result = this->_ruleblocks.at(index);
+ this->_ruleblocks.erase(this->_ruleblocks.begin() + index);
+ return result;
+ }
+
+ RuleBlock* Engine::removeRuleBlock(const std::string& name) {
+ for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
+ if (_ruleblocks.at(i)->getName() == name) {
+ RuleBlock* result = this->_ruleblocks.at(i);
+ this->_ruleblocks.erase(this->_ruleblocks.begin() + i);
+ return result;
+ }
+ }
+ throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT);
+ }
+
+ int Engine::numberOfRuleBlocks() const {
+ return this->_ruleblocks.size();
+ }
+
+ const std::vector<RuleBlock*>& Engine::ruleBlocks() const {
+ return this->_ruleblocks;
+ }
+
+ void Engine::setRuleBlocks(const std::vector<RuleBlock*>& ruleBlocks) {
+ this->_ruleblocks = ruleBlocks;
+ }
+
+ std::vector<RuleBlock*>& Engine::ruleBlocks() {
+ return this->_ruleblocks;
+ }
+
+
+}
diff --git a/fuzzylite/src/Exception.cpp b/fuzzylite/src/Exception.cpp
new file mode 100644
index 0000000..d24adac
--- /dev/null
+++ b/fuzzylite/src/Exception.cpp
@@ -0,0 +1,183 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/Exception.h"
+
+
+#ifdef FL_BACKTRACE_OFF
+//do nothing
+
+#elif defined FL_UNIX
+#include <execinfo.h>
+
+#elif defined FL_WINDOWS
+#include <windows.h>
+#include <winbase.h>
+#ifndef __MINGW32__
+#include <dbghelp.h>
+#endif
+#endif
+
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace fl {
+
+ Exception::Exception(const std::string& what)
+ : std::exception(), _what(what) {
+ FL_DBG(this->what());
+ }
+
+ Exception::Exception(const std::string& what, const std::string& file, int line,
+ const std::string& function)
+ : std::exception(), _what(what) {
+ append(file, line, function);
+ FL_DBG(this->what());
+ }
+
+ Exception::~Exception() FL_INOEXCEPT {
+ }
+
+ void Exception::setWhat(const std::string& what) {
+ this->_what = what;
+ }
+
+ std::string Exception::getWhat() const {
+ return this->_what;
+ }
+
+ const char* Exception::what() const FL_INOEXCEPT {
+ return this->_what.c_str();
+ }
+
+ void Exception::append(const std::string& whatElse) {
+ this->_what += whatElse + "\n";
+ }
+
+ void Exception::append(const std::string& file, int line, const std::string& function) {
+ std::ostringstream ss;
+ ss << "\n{at " << file << "::" << function << "() [line:" << line << "]}";
+ _what += ss.str();
+ }
+
+ void Exception::append(const std::string& whatElse,
+ const std::string& file, int line, const std::string& function) {
+ append(whatElse);
+ append(file, line, function);
+ }
+
+ std::string Exception::btCallStack() {
+#ifdef FL_BACKTRACE_OFF
+ return "[backtrace disabled] fuzzylite was built with option -DFL_BACKTRACE_OFF";
+#elif defined FL_UNIX
+ std::ostringstream btStream;
+ const int bufferSize = 30;
+ void* buffer[bufferSize];
+ int backtraceSize = backtrace(buffer, bufferSize);
+ char **btSymbols = backtrace_symbols(buffer, backtraceSize);
+ if (btSymbols == fl::null) {
+ btStream << "[backtrace error] no symbols could be retrieved";
+ } else {
+ if (backtraceSize == 0) btStream << "[backtrace is empty]";
+ for (int i = 0; i < backtraceSize; ++i) {
+ btStream << btSymbols[i] << "\n";
+ }
+ }
+ free(btSymbols);
+ return btStream.str();
+
+
+#elif defined FL_WINDOWS && ! defined __MINGW32__
+ std::ostringstream btStream;
+ const int bufferSize = 30;
+ void* buffer[bufferSize];
+ SymInitialize(GetCurrentProcess(), fl::null, TRUE);
+
+ int backtraceSize = CaptureStackBackTrace(0, bufferSize, buffer, fl::null);
+ SYMBOL_INFO* btSymbol = (SYMBOL_INFO *) calloc(sizeof ( SYMBOL_INFO) + 256 * sizeof ( char), 1);
+ if (not btSymbol) {
+ btStream << "[backtrace error] no symbols could be retrieved";
+ } else {
+ btSymbol->MaxNameLen = 255;
+ btSymbol->SizeOfStruct = sizeof ( SYMBOL_INFO);
+ if (backtraceSize == 0) btStream << "[backtrace is empty]";
+ for (int i = 0; i < backtraceSize; ++i) {
+ SymFromAddr(GetCurrentProcess(), (DWORD64) (buffer[ i ]), 0, btSymbol);
+ btStream << (backtraceSize - i - 1) << ": " <<
+ btSymbol->Name << " at 0x" << btSymbol->Address << "\n";
+ }
+ }
+ free(btSymbol);
+ return btStream.str();
+#else
+ return "[backtrace missing] supported only in Unix and Windows platforms";
+#endif
+ }
+ //execinfo
+
+ void Exception::signalHandler(int signal) {
+ std::ostringstream ex;
+ ex << "[unexpected signal " << signal << "] ";
+#ifdef FL_UNIX
+ ex << strsignal(signal);
+#endif
+ ex << "\nBACKTRACE:\n" << btCallStack();
+ fl::Exception::catchException(fl::Exception(ex.str(), FL_AT));
+ exit(EXIT_FAILURE);
+ }
+
+ void Exception::convertToException(int signal) {
+ std::string signalDescription;
+#ifdef FL_UNIX
+ //Unblock the signal
+ sigset_t empty;
+ sigemptyset(&empty);
+ sigaddset(&empty, signal);
+ sigprocmask(SIG_UNBLOCK, &empty, fl::null);
+ signalDescription = strsignal(signal);
+#endif
+ std::ostringstream ex;
+ ex << "[signal " << signal << "] " << signalDescription << "\n";
+ ex << "BACKTRACE:\n" << btCallStack();
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ void Exception::terminate() {
+ fl::Exception::catchException(fl::Exception("[unexpected exception] BACKTRACE:\n" + btCallStack(), FL_AT));
+ exit(EXIT_FAILURE);
+ }
+
+ void Exception::catchException(const std::exception& exception) {
+ std::ostringstream ss;
+ ss << exception.what();
+ std::string backtrace = btCallStack();
+ if (not backtrace.empty()) {
+ ss << "\n\nBACKTRACE:\n" << backtrace;
+ }
+ FL_LOG(ss.str());
+ }
+
+}
diff --git a/fuzzylite/src/Operation.cpp b/fuzzylite/src/Operation.cpp
new file mode 100644
index 0000000..1596ea4
--- /dev/null
+++ b/fuzzylite/src/Operation.cpp
@@ -0,0 +1,477 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/Operation.h"
+
+#include "fl/defuzzifier/Defuzzifier.h"
+#include "fl/norm/Norm.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/TNorm.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <cstdarg>
+#include <cctype>
+
+namespace fl {
+
+ template <typename T>
+ T Operation::min(T a, T b) {
+ if (isNaN(a)) return b;
+ if (isNaN(b)) return a;
+ return a < b ? a : b;
+ }
+ template FL_API scalar Operation::min(scalar a, scalar b);
+ template FL_API int Operation::min(int a, int b);
+
+ template <typename T>
+ T Operation::max(T a, T b) {
+ if (isNaN(a)) return b;
+ if (isNaN(b)) return a;
+ return a > b ? a : b;
+ }
+ template FL_API scalar Operation::max(scalar a, scalar b);
+ template FL_API int Operation::max(int a, int b);
+
+ template <typename T>
+ T Operation::bound(T x, T min, T max) {
+ if (isGt(x, max)) return max;
+ if (isLt(x, min)) return min;
+ return x;
+ }
+ template FL_API scalar Operation::bound(scalar x, scalar min, scalar max);
+ template FL_API int Operation::bound(int x, int min, int max);
+
+ template <typename T>
+ bool Operation::in(T x, T min, T max, bool geq, bool leq) {
+ bool left = geq ? isGE(x, min) : isGt(x, min);
+ bool right = leq ? isLE(x, max) : isLt(x, max);
+ return (left and right);
+ }
+ template FL_API bool Operation::in(scalar x, scalar min, scalar max, bool geq, bool leq);
+ template FL_API bool Operation::in(int x, int min, int max, bool geq, bool leq);
+
+ template <typename T>
+ bool Operation::isInf(T x) {
+ return std::abs(x) == fl::inf;
+ }
+ template FL_API bool Operation::isInf(int x);
+ template FL_API bool Operation::isInf(scalar x);
+
+ template <typename T>
+ bool Operation::isNaN(T x) {
+ return not (x == x);
+ }
+ template FL_API bool Operation::isNaN(int x);
+ template FL_API bool Operation::isNaN(scalar x);
+
+ template<typename T>
+ bool Operation::isFinite(T x) {
+ return not (isNaN(x) or isInf(x));
+ }
+ template FL_API bool Operation::isFinite(int x);
+ template FL_API bool Operation::isFinite(scalar x);
+
+ bool Operation::isLt(scalar a, scalar b, scalar macheps) {
+ return not isEq(a, b, macheps) and a < b;
+ }
+
+ bool Operation::isLE(scalar a, scalar b, scalar macheps) {
+ return isEq(a, b, macheps) or a < b;
+ }
+
+ bool Operation::isEq(scalar a, scalar b, scalar macheps) {
+ return a == b or std::fabs(a - b) < macheps or (isNaN(a) and isNaN(b));
+ }
+
+ bool Operation::isGt(scalar a, scalar b, scalar macheps) {
+ return not isEq(a, b, macheps) and a > b;
+ }
+
+ bool Operation::isGE(scalar a, scalar b, scalar macheps) {
+ return isEq(a, b, macheps) or a > b;
+ }
+
+ scalar Operation::scale(scalar x, scalar fromMin, scalar fromMax, scalar toMin, scalar toMax, bool bounded) {
+ scalar result = (toMax - toMin) / (fromMax - fromMin) * (x - fromMin) + toMin;
+ return bounded ? fl::Op::bound(result, toMin, toMax) : result;
+ }
+
+ scalar Operation::add(scalar a, scalar b) {
+ return a + b;
+ }
+
+ scalar Operation::subtract(scalar a, scalar b) {
+ return a - b;
+ }
+
+ scalar Operation::multiply(scalar a, scalar b) {
+ return a * b;
+ }
+
+ scalar Operation::divide(scalar a, scalar b) {
+ return a / b;
+ }
+
+ scalar Operation::modulo(scalar a, scalar b) {
+ return fmod(a, b);
+ }
+
+ scalar Operation::logicalAnd(scalar a, scalar b) {
+ return (isEq(a, 1.0) and isEq(b, 1.0)) ? 1.0 : 0.0;
+ }
+
+ scalar Operation::logicalOr(scalar a, scalar b) {
+ return (isEq(a, 1.0) or isEq(b, 1.0)) ? 1.0 : 0.0;
+ }
+
+ scalar Operation::logicalNot(scalar a) {
+ return isEq(a, 1.0) ? 0.0 : 1.0;
+ }
+
+ scalar Operation::negate(scalar a) {
+ return -a;
+ }
+
+ scalar Operation::round(scalar x) {
+ return (x > 0.0) ? std::floor(x + 0.5) : std::ceil(x - 0.5);
+ }
+
+ scalar Operation::gt(scalar a, scalar b) {
+ return isGt(a, b);
+ }
+
+ scalar Operation::ge(scalar a, scalar b) {
+ return isGE(a, b);
+ }
+
+ scalar Operation::eq(scalar a, scalar b) {
+ return isEq(a, b);
+ }
+
+ scalar Operation::neq(scalar a, scalar b) {
+ return not isEq(a, b);
+ }
+
+ scalar Operation::le(scalar a, scalar b) {
+ return isLE(a, b);
+ }
+
+ scalar Operation::lt(scalar a, scalar b) {
+ return isLt(a, b);
+ }
+
+ bool Operation::increment(std::vector<int>& x, std::vector<int>& min, std::vector<int>& max) {
+ return increment(x, (int) x.size() - 1, min, max);
+ }
+
+ bool Operation::increment(std::vector<int>& x, int position, std::vector<int>& min, std::vector<int>& max) {
+ if (x.empty() or position < 0) return true;
+
+ bool overflow = false;
+ if (x.at(position) < max.at(position)) {
+ ++x.at(position);
+ } else {
+ overflow = (position == 0);
+ x.at(position) = min.at(position);
+ --position;
+ if (position >= 0) {
+ overflow = increment(x, position, min, max);
+ }
+ }
+ return overflow;
+ }
+
+ double Operation::mean(const std::vector<scalar>& x) {
+ if (x.size() == 0) return fl::nan;
+ scalar sum = 0.0;
+ for (std::size_t i = 0; i < x.size(); ++i) sum += x.at(i);
+ return sum / x.size();
+ }
+
+ double Operation::standardDeviation(const std::vector<scalar>& x) {
+ if (x.size() <= 1) return 0.0;
+ return standardDeviation(x, mean(x));
+ }
+
+ double Operation::standardDeviation(const std::vector<scalar>& x, scalar mean) {
+ if (x.size() <= 1) return 0.0;
+ return std::sqrt(variance(x, mean));
+ }
+
+ double Operation::variance(const std::vector<scalar>& x) {
+ if (x.size() <= 1) return 0.0;
+ return variance(x, mean(x));
+ }
+
+ double Operation::variance(const std::vector<scalar>& x, scalar mean) {
+ if (x.size() <= 1) return 0.0;
+ scalar result = 0;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ result += (x.at(i) - mean) * (x.at(i) - mean);
+ }
+ result /= -1 + x.size();
+ return result;
+ }
+
+
+
+ //Text Operations:
+
+ std::string Operation::validName(const std::string& name) {
+ if (trim(name).empty()) return "unnamed";
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < name.length(); ++i) {
+ char c = name[i];
+ if (c == '_' or c == '.' or isalnum(c)) {
+ ss << c;
+ }
+ }
+ return ss.str();
+ }
+
+ int Operation::isValidForName(int character) {
+ return character == '_' or character == '.' or isalnum(character);
+ }
+
+ std::string Operation::findReplace(const std::string& str, const std::string& find,
+ const std::string& replace, bool replaceAll) {
+ std::ostringstream result;
+ std::size_t fromIndex = 0, nextIndex;
+ do {
+ nextIndex = str.find(find, fromIndex);
+ result << str.substr(fromIndex, nextIndex - fromIndex);
+ if (nextIndex != std::string::npos)
+ result << replace;
+ fromIndex = nextIndex + find.size();
+ } while (replaceAll and nextIndex != std::string::npos);
+ return result.str();
+ }
+
+ std::vector<std::string> Operation::split(const std::string& str,
+ const std::string& delimiter, bool ignoreEmpty) {
+ std::vector<std::string> result;
+ if (str.empty() or delimiter.empty()) {
+ result.push_back(str);
+ return result;
+ }
+ std::string::const_iterator position = str.begin(), next = str.begin();
+ while (next != str.end()) {
+ next = std::search(position, str.end(), delimiter.begin(), delimiter.end());
+ std::string token(position, next);
+ if (not (token.empty() and ignoreEmpty)) {
+ result.push_back(token);
+ }
+ if (next != str.end()) {
+ position = next + delimiter.size();
+ }
+ }
+ return result;
+ }
+
+ std::string Operation::trim(const std::string& text) {
+ if (text.empty()) return text;
+ if (not (std::isspace(text.at(0)) or std::isspace(text.at(text.size() - 1))))
+ return text;
+ int start = 0, end = text.size() - 1;
+ while (start <= end and std::isspace(text.at(start))) {
+ ++start;
+ }
+ while (end >= start and std::isspace(text.at(end))) {
+ --end;
+ }
+ int length = end - start + 1;
+ if (length <= 0) return "";
+ return text.substr(start, length);
+ }
+
+ std::string Operation::format(const std::string& text, int matchesChar(int),
+ const std::string& replacement) {
+ std::ostringstream ss;
+ std::string::const_iterator it = text.begin();
+ while (it != text.end()) {
+ if (matchesChar(*it)) {
+ ss << *it;
+ } else {
+ ss << replacement;
+ }
+ ++it;
+ }
+ return ss.str();
+ }
+
+ scalar Operation::toScalar(const std::string& x) {
+ std::istringstream iss(x);
+ scalar result;
+ iss >> result;
+ char strict;
+ if (not (iss.fail() or iss.get(strict))) return result;
+
+ std::ostringstream nan, pInf, nInf;
+ nan << fl::nan;
+ pInf << fl::inf;
+ nInf << (-fl::inf);
+
+ if (x == nan.str() or x == "nan")
+ return fl::nan;
+ if (x == pInf.str() or x == "inf")
+ return fl::inf;
+ if (x == nInf.str() or x == "-inf")
+ return -fl::inf;
+
+ std::ostringstream ex;
+ ex << "[conversion error] from <" << x << "> to scalar";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ scalar Operation::toScalar(const std::string& x, scalar alternative) FL_INOEXCEPT {
+ std::istringstream iss(x);
+ scalar result;
+ iss >> result;
+ char strict;
+ if (not (iss.fail() or iss.get(strict))) return result;
+
+ std::ostringstream nan, pInf, nInf;
+ nan << fl::nan;
+ pInf << fl::inf;
+ nInf << (-fl::inf);
+
+ if (x == nan.str() or x == "nan")
+ return fl::nan;
+ if (x == pInf.str() or x == "inf")
+ return fl::inf;
+ if (x == nInf.str() or x == "-inf")
+ return -fl::inf;
+
+ return alternative;
+ }
+
+ bool Operation::isNumeric(const std::string& x) {
+ try {
+ fl::Op::toScalar(x);
+ return true;
+ } catch (std::exception& ex) {
+ (void) ex;
+ return false;
+ }
+ }
+
+ template <typename T>
+ std::string Operation::str(T x, int decimals) {
+ std::ostringstream ss;
+ ss << std::setprecision(decimals) << std::fixed;
+ if (fl::Op::isNaN(x)) {
+ ss << "nan";
+ } else if (fl::Op::isInf(x)) {
+ ss << (fl::Op::isLt(x, 0.0) ? "-inf" : "inf");
+ } else if (fl::Op::isEq(x, 0.0)) {
+ ss << 0.0;
+ } else ss << x;
+ return ss.str();
+ }
+ template FL_API std::string Operation::str(int x, int precision);
+ template FL_API std::string Operation::str(scalar x, int precision);
+
+ template <> FL_API std::string Operation::str(const std::string& x, int precision) {
+ (void) precision;
+ return x;
+ }
+
+ template <typename T>
+ std::string Operation::join(const std::vector<T>& x,
+ const std::string& separator) {
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ ss << str(x.at(i));
+ if (i + 1 < x.size()) ss << separator;
+ }
+ return ss.str();
+ }
+ template FL_API std::string Operation::join(const std::vector<int>& x,
+ const std::string& separator);
+ template FL_API std::string Operation::join(const std::vector<scalar>& x,
+ const std::string& separator);
+
+ template <> FL_API
+ std::string Operation::join(const std::vector<std::string>& x,
+ const std::string& separator) {
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ ss << x.at(i);
+ if (i + 1 < x.size()) ss << separator;
+ }
+ return ss.str();
+ }
+
+ template <typename T>
+ std::string Operation::join(int items, const std::string& separator, T first, ...) {
+ std::ostringstream ss;
+ ss << str(first);
+ if (items > 1) ss << separator;
+ va_list args;
+ va_start(args, first);
+ for (int i = 0; i < items - 1; ++i) {
+ ss << str(va_arg(args, T));
+ if (i + 1 < items - 1) ss << separator;
+ }
+ va_end(args);
+ return ss.str();
+ }
+
+ template FL_API std::string Operation::join(int items, const std::string& separator,
+ int first, ...);
+ template FL_API std::string Operation::join(int items, const std::string& separator,
+ double first, ...);
+
+ template <> FL_API std::string Operation::join(int items, const std::string& separator,
+ float first, ...) {
+ std::ostringstream ss;
+ ss << str(first);
+ if (items > 1) ss << separator;
+ va_list args;
+ va_start(args, first);
+ for (int i = 0; i < items - 1; ++i) {
+ ss << str(va_arg(args, double)); //automatic promotion
+ if (i + 1 < items - 1) ss << separator;
+ }
+ va_end(args);
+ return ss.str();
+ }
+
+ template <> FL_API
+ std::string Operation::join(int items, const std::string& separator, const char* first, ...) {
+ std::ostringstream ss;
+ ss << first;
+ if (items > 1) ss << separator;
+ va_list args;
+ va_start(args, first);
+ for (int i = 0; i < items - 1; ++i) {
+ ss << va_arg(args, const char*);
+ if (i + 1 < items - 1) ss << separator;
+ }
+ va_end(args);
+ return ss.str();
+ }
+
+}
diff --git a/fuzzylite/src/defuzzifier/Bisector.cpp b/fuzzylite/src/defuzzifier/Bisector.cpp
new file mode 100644
index 0000000..ee4d2fc
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/Bisector.cpp
@@ -0,0 +1,85 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/Bisector.h"
+
+#include "fl/term/Accumulated.h"
+#include "fl/term/Term.h"
+
+namespace fl {
+
+ Bisector::Bisector(int resolution)
+ : IntegralDefuzzifier(resolution) {
+ }
+
+ Bisector::~Bisector() {
+
+ }
+
+ std::string Bisector::className() const {
+ return "Bisector";
+ }
+
+ scalar Bisector::defuzzify(const Term* term, scalar minimum, scalar maximum) const {
+ if (not fl::Op::isFinite(minimum + maximum)) {
+ return fl::nan;
+ }
+ if (maximum - minimum > _resolution) {
+ FL_DBG("[accuracy warning] the resolution <" << _resolution << "> "
+ "is smaller than the range <" << minimum << ", " << maximum << ">. In order to "
+ "improve the accuracy, the resolution should be at least equal to the range.");
+ }
+ scalar dx = (maximum - minimum) / _resolution;
+
+ int counter = _resolution;
+ int left = 0, right = 0;
+ scalar leftArea = 0, rightArea = 0;
+ scalar xLeft = minimum, xRight = maximum;
+ while (counter-- > 0) {
+ if (fl::Op::isLE(leftArea, rightArea)) {
+ xLeft = minimum + (left + 0.5) * dx;
+ leftArea += term->membership(xLeft);
+ left++;
+ } else {
+ xRight = maximum - (right + 0.5) * dx;
+ rightArea += term->membership(xRight);
+ right++;
+ }
+ }
+
+ //Inverse weighted average to compensate
+ scalar bisector = (leftArea * xRight + rightArea * xLeft) / (leftArea + rightArea);
+ return bisector;
+ }
+
+ Bisector* Bisector::clone() const {
+ return new Bisector(*this);
+ }
+
+ Defuzzifier* Bisector::constructor() {
+ return new Bisector;
+ }
+
+
+}
diff --git a/fuzzylite/src/defuzzifier/Centroid.cpp b/fuzzylite/src/defuzzifier/Centroid.cpp
new file mode 100644
index 0000000..01490d3
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/Centroid.cpp
@@ -0,0 +1,80 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/Centroid.h"
+
+#include "fl/term/Accumulated.h"
+#include "fl/term/Term.h"
+
+
+namespace fl {
+
+ Centroid::Centroid(int resolution)
+ : IntegralDefuzzifier(resolution) {
+ }
+
+ Centroid::~Centroid() {
+
+ }
+
+ std::string Centroid::className() const {
+ return "Centroid";
+ }
+
+ scalar Centroid::defuzzify(const Term* term, scalar minimum, scalar maximum) const {
+ if (not fl::Op::isFinite(minimum + maximum)) {
+ return fl::nan;
+ }
+ if (maximum - minimum > _resolution) {
+ FL_DBG("[accuracy warning] the resolution <" << _resolution << "> "
+ "is smaller than the range <" << minimum << ", " << maximum << ">. In order to "
+ "improve the accuracy, the resolution should be at least equal to the range.");
+ }
+ scalar dx = (maximum - minimum) / _resolution;
+ scalar x, y;
+ scalar area = 0, xcentroid = 0, ycentroid = 0;
+ for (int i = 0; i < _resolution; ++i) {
+ x = minimum + (i + 0.5) * dx;
+ y = term->membership(x);
+
+ xcentroid += y * x;
+ ycentroid += y * y;
+ area += y;
+ }
+ xcentroid /= area;
+ ycentroid /= 2 * area;
+ area *= dx; //total area... unused, but for future reference.
+ return xcentroid;
+ }
+
+ Centroid* Centroid::clone() const {
+ return new Centroid(*this);
+ }
+
+ Defuzzifier* Centroid::constructor() {
+ return new Centroid;
+ }
+
+
+}
diff --git a/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp b/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp
new file mode 100644
index 0000000..2badf14
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/IntegralDefuzzifier.cpp
@@ -0,0 +1,53 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/IntegralDefuzzifier.h"
+
+namespace fl {
+
+ int IntegralDefuzzifier::_defaultResolution = 200;
+
+ void IntegralDefuzzifier::setDefaultResolution(int defaultResolution) {
+ _defaultResolution = defaultResolution;
+ }
+
+ int IntegralDefuzzifier::defaultResolution() {
+ return _defaultResolution;
+ }
+
+ IntegralDefuzzifier::IntegralDefuzzifier(int resolution)
+ : Defuzzifier(), _resolution(resolution) {
+ }
+
+ IntegralDefuzzifier::~IntegralDefuzzifier() {
+ }
+
+ void IntegralDefuzzifier::setResolution(int resolution) {
+ this->_resolution = resolution;
+ }
+
+ int IntegralDefuzzifier::getResolution() const {
+ return this->_resolution;
+ }
+}
diff --git a/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp b/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp
new file mode 100644
index 0000000..470af52
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/LargestOfMaximum.cpp
@@ -0,0 +1,75 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/LargestOfMaximum.h"
+
+#include "fl/Exception.h"
+#include "fl/term/Term.h"
+
+namespace fl {
+
+ LargestOfMaximum::LargestOfMaximum(int resolution)
+ : IntegralDefuzzifier(resolution) {
+ }
+
+ LargestOfMaximum::~LargestOfMaximum() {
+ }
+
+ std::string LargestOfMaximum::className() const {
+ return "LargestOfMaximum";
+ }
+
+ scalar LargestOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const {
+ if (not fl::Op::isFinite(minimum + maximum)) {
+ return fl::nan;
+ }
+ if (maximum - minimum > _resolution) {
+ FL_DBG("[accuracy warning] the resolution <" << _resolution << "> "
+ "is smaller than the range <" << minimum << ", " << maximum << ">. In order to "
+ "improve the accuracy, the resolution should be at least equal to the range.");
+ }
+ scalar dx = (maximum - minimum) / _resolution;
+ scalar x, y;
+ scalar ymax = -1.0, xlargest = maximum;
+ for (int i = 0; i < _resolution; ++i) {
+ x = minimum + (i + 0.5) * dx;
+ y = term->membership(x);
+
+ if (Op::isGE(y, ymax)) {
+ ymax = y;
+ xlargest = x;
+ }
+ }
+ return xlargest;
+ }
+
+ LargestOfMaximum* LargestOfMaximum::clone() const {
+ return new LargestOfMaximum(*this);
+ }
+
+ Defuzzifier* LargestOfMaximum::constructor() {
+ return new LargestOfMaximum;
+ }
+
+}
diff --git a/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp b/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp
new file mode 100644
index 0000000..7c40527
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/MeanOfMaximum.cpp
@@ -0,0 +1,88 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/MeanOfMaximum.h"
+
+#include "fl/Exception.h"
+#include "fl/term/Term.h"
+
+
+namespace fl {
+
+ MeanOfMaximum::MeanOfMaximum(int resolution)
+ : IntegralDefuzzifier(resolution) {
+ }
+
+ MeanOfMaximum::~MeanOfMaximum() {
+ }
+
+ std::string MeanOfMaximum::className() const {
+ return "MeanOfMaximum";
+ }
+
+ scalar MeanOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const {
+ if (not fl::Op::isFinite(minimum + maximum)) {
+ return fl::nan;
+ }
+ if (maximum - minimum > _resolution) {
+ FL_DBG("[accuracy warning] the resolution <" << _resolution << "> "
+ "is smaller than the range <" << minimum << ", " << maximum << ">. In order to "
+ "improve the accuracy, the resolution should be at least equal to the range.");
+ }
+ scalar dx = (maximum - minimum) / _resolution;
+ scalar x, y;
+ scalar ymax = -1.0;
+ scalar xsmallest = minimum;
+ scalar xlargest = maximum;
+ bool samePlateau = false;
+ for (int i = 0; i < _resolution; ++i) {
+ x = minimum + (i + 0.5) * dx;
+ y = term->membership(x);
+
+ if (Op::isGt(y, ymax)) {
+ ymax = y;
+
+ xsmallest = x;
+ xlargest = x;
+
+ samePlateau = true;
+ } else if (Op::isEq(y, ymax) and samePlateau) {
+ xlargest = x;
+ } else if (Op::isLt(y, ymax)) {
+ samePlateau = false;
+ }
+ }
+
+ return (xlargest + xsmallest) / 2.0;
+ }
+
+ MeanOfMaximum* MeanOfMaximum::clone() const {
+ return new MeanOfMaximum(*this);
+ }
+
+ Defuzzifier* MeanOfMaximum::constructor() {
+ return new MeanOfMaximum;
+ }
+
+}
diff --git a/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp b/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp
new file mode 100644
index 0000000..1e67395
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/SmallestOfMaximum.cpp
@@ -0,0 +1,77 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/SmallestOfMaximum.h"
+
+#include "fl/Exception.h"
+#include "fl/term/Term.h"
+
+
+namespace fl {
+
+ SmallestOfMaximum::SmallestOfMaximum(int resolution)
+ : IntegralDefuzzifier(resolution) {
+ }
+
+ SmallestOfMaximum::~SmallestOfMaximum() {
+ }
+
+ std::string SmallestOfMaximum::className() const {
+ return "SmallestOfMaximum";
+ }
+
+ scalar SmallestOfMaximum::defuzzify(const Term* term, scalar minimum, scalar maximum) const {
+ if (not fl::Op::isFinite(minimum + maximum)) {
+ return fl::nan;
+ }
+ if (maximum - minimum > _resolution) {
+ FL_DBG("[accuracy warning] the resolution <" << _resolution << "> "
+ "is smaller than the range <" << minimum << ", " << maximum << ">. In order to "
+ "improve the accuracy, the resolution should be at least equal to the range.");
+ }
+ scalar dx = (maximum - minimum) / _resolution;
+ scalar x, y;
+ scalar ymax = -1.0, xsmallest = minimum;
+ for (int i = 0; i < _resolution; ++i) {
+ x = minimum + (i + 0.5) * dx;
+ y = term->membership(x);
+
+ if (Op::isGt(y, ymax)) {
+ xsmallest = x;
+ ymax = y;
+ }
+ }
+ return xsmallest;
+ }
+
+ SmallestOfMaximum* SmallestOfMaximum::clone() const {
+ return new SmallestOfMaximum(*this);
+ }
+
+ Defuzzifier* SmallestOfMaximum::constructor() {
+ return new SmallestOfMaximum;
+ }
+
+}
+
diff --git a/fuzzylite/src/defuzzifier/WeightedAverage.cpp b/fuzzylite/src/defuzzifier/WeightedAverage.cpp
new file mode 100644
index 0000000..105c9d4
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/WeightedAverage.cpp
@@ -0,0 +1,123 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/WeightedAverage.h"
+
+#include "fl/term/Accumulated.h"
+#include "fl/term/Activated.h"
+#include "fl/norm/Norm.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/TNorm.h"
+
+#include <map>
+
+namespace fl {
+
+ WeightedAverage::WeightedAverage(Type type) : WeightedDefuzzifier(type) {
+ }
+
+ WeightedAverage::WeightedAverage(const std::string& type) : WeightedDefuzzifier(type) {
+ }
+
+ WeightedAverage::~WeightedAverage() {
+ }
+
+ std::string WeightedAverage::className() const {
+ return "WeightedAverage";
+ }
+
+ scalar WeightedAverage::defuzzify(const Term* term,
+ scalar minimum, scalar maximum) const {
+ const Accumulated* fuzzyOutput = dynamic_cast<const Accumulated*> (term);
+ if (not fuzzyOutput) {
+ std::ostringstream ss;
+ ss << "[defuzzification error]"
+ << "expected an Accumulated term instead of"
+ << "<" << term->toString() << ">";
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+
+ minimum = fuzzyOutput->getMinimum();
+ maximum = fuzzyOutput->getMaximum();
+
+ scalar sum = 0.0;
+ scalar weights = 0.0;
+
+ if (not fuzzyOutput->getAccumulation()) {
+ Type type = _type;
+ for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) {
+ Activated* activated = fuzzyOutput->getTerm(i);
+ scalar w = activated->getDegree();
+
+ if (type == Automatic) type = inferType(activated->getTerm());
+
+ scalar z = (type == TakagiSugeno)
+ //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work.
+ ? activated->getTerm()->membership(w) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions
+ : tsukamoto(activated->getTerm(), w, minimum, maximum);
+
+ sum += w * z;
+ weights += w;
+ }
+ } else {
+ typedef std::map<const Term*, std::vector<Activated*> > TermGroup;
+ TermGroup groups;
+ for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) {
+ Activated* value = fuzzyOutput->getTerm(i);
+ const Term* key = value->getTerm();
+ groups[key].push_back(value);
+ }
+ TermGroup::const_iterator it = groups.begin();
+ Type type = _type;
+ while (it != groups.end()) {
+ const Term* activatedTerm = it->first;
+ scalar accumulatedDegree = 0.0;
+ for (std::size_t i = 0; i < it->second.size(); ++i)
+ accumulatedDegree = fuzzyOutput->getAccumulation()->compute(
+ accumulatedDegree, it->second.at(i)->getDegree());
+
+ if (type == Automatic) type = inferType(activatedTerm);
+
+ scalar z = (type == TakagiSugeno)
+ //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work.
+ ? activatedTerm->membership(accumulatedDegree) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions
+ : tsukamoto(activatedTerm, accumulatedDegree, minimum, maximum);
+
+ sum += accumulatedDegree * z;
+ weights += accumulatedDegree;
+
+ ++it;
+ }
+ }
+ return sum / weights;
+ }
+
+ WeightedAverage* WeightedAverage::clone() const {
+ return new WeightedAverage(*this);
+ }
+
+ Defuzzifier* WeightedAverage::constructor() {
+ return new WeightedAverage;
+ }
+}
diff --git a/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp b/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp
new file mode 100644
index 0000000..760a5dc
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/WeightedDefuzzifier.cpp
@@ -0,0 +1,178 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/WeightedDefuzzifier.h"
+
+#include "fl/term/Activated.h"
+#include "fl/term/Concave.h"
+#include "fl/term/Constant.h"
+#include "fl/term/Function.h"
+#include "fl/term/Linear.h"
+#include "fl/term/Ramp.h"
+#include "fl/term/Sigmoid.h"
+#include "fl/term/SShape.h"
+#include "fl/term/ZShape.h"
+
+namespace fl {
+
+ WeightedDefuzzifier::WeightedDefuzzifier(Type type) : _type(type) {
+
+ }
+
+ WeightedDefuzzifier::WeightedDefuzzifier(const std::string& type) {
+ if (type == "Automatic") _type = Automatic;
+ else if (type == "TakagiSugeno") _type = TakagiSugeno;
+ else if (type == "Tsukamoto") _type = Tsukamoto;
+ else {
+ _type = Automatic;
+ FL_LOG("[warning] incorrect type <" + type + "> of WeightedDefuzzifier"
+ + " has been defaulted to <Automatic>");
+ }
+ }
+
+ WeightedDefuzzifier::~WeightedDefuzzifier() {
+
+ }
+
+ std::string WeightedDefuzzifier::typeName(Type type) {
+ switch (type) {
+ case Automatic: return "Automatic";
+ case TakagiSugeno: return "TakagiSugeno";
+ case Tsukamoto: return "Tsukamoto";
+ default: return "";
+ }
+ }
+
+ void WeightedDefuzzifier::setType(Type type) {
+ this->_type = type;
+ }
+
+ WeightedDefuzzifier::Type WeightedDefuzzifier::getType() const {
+ return this->_type;
+ }
+
+ std::string WeightedDefuzzifier::getTypeName() const {
+ return typeName(this->_type);
+ }
+
+ WeightedDefuzzifier::Type WeightedDefuzzifier::inferType(const Term* term) const {
+ if (dynamic_cast<const Constant*> (term)
+ or dynamic_cast<const Linear*> (term)
+ or dynamic_cast<const Function*> (term)) {
+ return TakagiSugeno;
+ }
+ return Tsukamoto;
+ }
+
+ bool WeightedDefuzzifier::isMonotonic(const Term* term) const {
+ return (dynamic_cast<const Concave*> (term)) or
+ (dynamic_cast<const Ramp*> (term)) or
+ (dynamic_cast<const Sigmoid*> (term)) or
+ (dynamic_cast<const SShape*> (term)) or
+ (dynamic_cast<const ZShape*> (term));
+ }
+
+ /**
+ * Instead of computing y=f(x), the goal of Tsukamoto is to find x=f(w),
+ * where f is monotonic.
+ */
+ scalar WeightedDefuzzifier::tsukamoto(const Term* monotonic, scalar activationDegree,
+ scalar minimum, scalar maximum) const {
+ scalar w = activationDegree;
+ scalar z = fl::nan; //result;
+ bool isTsukamoto = true;
+ if (const Ramp* ramp = dynamic_cast<const Ramp*> (monotonic)) {
+ z = Op::scale(w, 0, 1, ramp->getStart(), ramp->getEnd());
+
+ } else if (const Sigmoid* sigmoid = dynamic_cast<const Sigmoid*> (monotonic)) {
+ if (Op::isEq(w, 1.0)) {
+ if (Op::isGE(sigmoid->getSlope(), 0.0)) {
+ z = maximum;
+ } else {
+ z = minimum;
+ }
+
+ } else if (Op::isEq(w, 0.0)) {
+ if (Op::isGE(sigmoid->getSlope(), 0.0)) {
+ z = minimum;
+ } else {
+ z = maximum;
+ }
+ } else {
+ scalar a = sigmoid->getSlope();
+ scalar b = sigmoid->getInflection();
+ z = b + (std::log(1.0 / w - 1.0) / -a);
+ }
+
+ } else if (const SShape* sshape = dynamic_cast<const SShape*> (monotonic)) {
+ scalar difference = sshape->getEnd() - sshape->getStart();
+ scalar a = sshape->getStart() + std::sqrt(w * difference * difference / 2.0);
+ scalar b = sshape->getEnd() + std::sqrt(difference * difference * (w - 1.0) / -2.0);
+ if (std::fabs(w - monotonic->membership(a)) <
+ std::fabs(w - monotonic->membership(b))) {
+ z = a;
+ } else {
+ z = b;
+ }
+
+ } else if (const ZShape* zshape = dynamic_cast<const ZShape*> (monotonic)) {
+ scalar difference = zshape->getEnd() - zshape->getStart();
+ scalar a = zshape->getStart() + std::sqrt(difference * difference * (w - 1.0) / -2.0);
+ scalar b = zshape->getEnd() + std::sqrt(w * difference * difference / 2.0);
+ if (std::fabs(w - monotonic->membership(a)) <
+ std::fabs(w - monotonic->membership(b))) {
+ z = a;
+ } else {
+ z = b;
+ }
+
+ } else if (const Concave* concave = dynamic_cast<const Concave*> (monotonic)) {
+ scalar i = concave->getInflection();
+ scalar e = concave->getEnd();
+ z = (i - e) / concave->membership(w) + 2 * e - i;
+ } else {
+ isTsukamoto = false;
+ }
+
+ if (isTsukamoto) {
+ //Compare difference between estimated and true value
+ scalar fz = monotonic->membership(z);
+ if (not Op::isEq(w, fz, 1e-2)) {
+ FL_DBG("[tsukamoto warning] difference <" << Op::str(std::abs(w - fz)) << "> "
+ "might suggest an inaccurate computation of z because it is "
+ "expected w=f(z) in " << monotonic->className() <<
+ " term <" << monotonic->getName() << ">, but "
+ "w=" << w << " "
+ "f(z)=" << fz << " and "
+ "z=" << Op::str(z));
+ }
+ } else {
+ // else fallback to the regular Takagi-Sugeno or inverse Tsukamoto (according to term)
+ z = monotonic->membership(w);
+ }
+ return z;
+ }
+
+
+}
diff --git a/fuzzylite/src/defuzzifier/WeightedSum.cpp b/fuzzylite/src/defuzzifier/WeightedSum.cpp
new file mode 100644
index 0000000..fb3e2e3
--- /dev/null
+++ b/fuzzylite/src/defuzzifier/WeightedSum.cpp
@@ -0,0 +1,121 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/defuzzifier/WeightedSum.h"
+
+#include "fl/term/Accumulated.h"
+#include "fl/term/Activated.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/TNorm.h"
+
+#include <map>
+namespace fl {
+
+ WeightedSum::WeightedSum(Type type) : WeightedDefuzzifier(type) {
+ }
+
+ WeightedSum::WeightedSum(const std::string& type) : WeightedDefuzzifier(type) {
+
+ }
+
+ WeightedSum::~WeightedSum() {
+ }
+
+ std::string WeightedSum::className() const {
+ return "WeightedSum";
+ }
+
+ scalar WeightedSum::defuzzify(const Term* term,
+ scalar minimum, scalar maximum) const {
+ const Accumulated* fuzzyOutput = dynamic_cast<const Accumulated*> (term);
+ if (not fuzzyOutput) {
+ std::ostringstream ss;
+ ss << "[defuzzification error]"
+ << "expected an Accumulated term instead of"
+ << "<" << term->toString() << ">";
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+
+ minimum = fuzzyOutput->getMinimum();
+ maximum = fuzzyOutput->getMaximum();
+
+
+ scalar sum = 0.0;
+
+ if (not fuzzyOutput->getAccumulation()) {
+ Type type = _type;
+ for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) {
+ Activated* activated = fuzzyOutput->getTerm(i);
+ scalar w = activated->getDegree();
+
+ if (type == Automatic) type = inferType(activated->getTerm());
+
+ scalar z = (type == TakagiSugeno)
+ //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work.
+ ? activated->getTerm()->membership(w) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions
+ : tsukamoto(activated->getTerm(), w, minimum, maximum);
+
+ sum += w * z;
+ }
+ } else {
+ typedef std::map<const Term*, std::vector<Activated*> > TermGroup;
+ TermGroup groups;
+ for (int i = 0; i < fuzzyOutput->numberOfTerms(); ++i) {
+ Activated* value = fuzzyOutput->getTerm(i);
+ const Term* key = value->getTerm();
+ groups[key].push_back(value);
+ }
+ TermGroup::const_iterator it = groups.begin();
+ Type type = _type;
+ while (it != groups.end()) {
+ const Term* activatedTerm = it->first;
+ scalar accumulatedDegree = 0.0;
+ for (std::size_t i = 0; i < it->second.size(); ++i)
+ accumulatedDegree = fuzzyOutput->getAccumulation()->compute(
+ accumulatedDegree, it->second.at(i)->getDegree());
+
+ if (type == Automatic) type = inferType(activatedTerm);
+
+ scalar z = (type == TakagiSugeno)
+ //? activated.getTerm()->membership(fl::nan) Would ensure no Tsukamoto applies, but Inverse Tsukamoto with Functions would not work.
+ ? activatedTerm->membership(accumulatedDegree) //Provides Takagi-Sugeno and Inverse Tsukamoto of Functions
+ : tsukamoto(activatedTerm, accumulatedDegree, minimum, maximum);
+
+ sum += accumulatedDegree * z;
+
+ ++it;
+ }
+ }
+ return sum;
+ }
+
+ WeightedSum* WeightedSum::clone() const {
+ return new WeightedSum(*this);
+ }
+
+ Defuzzifier* WeightedSum::constructor() {
+ return new WeightedSum;
+ }
+
+}
diff --git a/fuzzylite/src/factory/CloningFactory.cpp b/fuzzylite/src/factory/CloningFactory.cpp
new file mode 100644
index 0000000..b187171
--- /dev/null
+++ b/fuzzylite/src/factory/CloningFactory.cpp
@@ -0,0 +1,135 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/CloningFactory.h"
+
+#include "fl/Exception.h"
+#include "fl/term/Function.h"
+
+namespace fl {
+
+ template<typename T>
+ CloningFactory<T>::CloningFactory(const std::string& name) : _name(name) {
+
+ }
+
+ template<typename T>
+ CloningFactory<T>::CloningFactory(const CloningFactory& other) {
+ typename std::map<std::string, T>::const_iterator it = other._objects.begin();
+ while (it != other._objects.end()) {
+ T clone = fl::null;
+ if (it->second) clone = it->second->clone();
+ this->_objects[it->first] = clone;
+ ++it;
+ }
+ }
+
+ template<typename T>
+ CloningFactory<T>& CloningFactory<T>::operator=(const CloningFactory& other) {
+ if (this != &other) {
+ typename std::map<std::string, T>::const_iterator it = this->_objects.begin();
+ while (it != this->_objects.end()) {
+ if (it->second) delete it->second;
+ ++it;
+ }
+ this->_objects.clear();
+
+ it = other._objects.begin();
+ while (it != other._objects.end()) {
+ T clone = fl::null;
+ if (it->second) clone = it->second->clone();
+ this->_objects[it->first] = clone;
+ ++it;
+ }
+ }
+ return *this;
+ }
+
+ template<typename T>
+ CloningFactory<T>::~CloningFactory() {
+ typename std::map<std::string, T>::const_iterator it = this->_objects.begin();
+ while (it != this->_objects.end()) {
+ if (it->second) delete it->second;
+ ++it;
+ }
+ }
+
+ template<typename T>
+ std::string CloningFactory<T>::name() const {
+ return this->_name;
+ }
+
+ template<typename T>
+ void CloningFactory<T>::registerObject(const std::string& key, T object) {
+ this->_objects[key] = object;
+ }
+
+ template<typename T>
+ void CloningFactory<T>::deregisterObject(const std::string& key) {
+ typename std::map<std::string, T>::iterator it = this->_objects.find(key);
+ if (it != this->_objects.end()) {
+ this->_objects.erase(it);
+ delete it->second;
+ }
+ }
+
+ template<typename T>
+ bool CloningFactory<T>::hasObject(const std::string& key) const {
+ typename std::map<std::string, T>::const_iterator it = this->_objects.find(key);
+ return (it != this->_objects.end());
+ }
+
+ template<typename T>
+ T CloningFactory<T>::getObject(const std::string& key) const {
+ typename std::map<std::string, T>::const_iterator it = this->_objects.find(key);
+ if (it != this->_objects.end()) {
+ if (it->second) return it->second;
+ }
+ return fl::null;
+ }
+
+ template<typename T>
+ T CloningFactory<T>::cloneObject(const std::string& key) const {
+ typename std::map<std::string, T>::const_iterator it = this->_objects.find(key);
+ if (it != this->_objects.end()) {
+ if (it->second) return it->second->clone();
+ return fl::null;
+ }
+ throw fl::Exception("[cloning error] " + _name + " object by name <" + key + "> not registered", FL_AT);
+ }
+
+ template<typename T>
+ std::vector<std::string> CloningFactory<T>::available() const {
+ std::vector<std::string> result;
+ typename std::map<std::string, T>::const_iterator it = this->_objects.begin();
+ while (it != this->_objects.end()) {
+ result.push_back(it->first);
+ }
+ return result;
+ }
+
+ template class fl::CloningFactory<fl::Function::Element*>;
+}
+
+
diff --git a/fuzzylite/src/factory/ConstructionFactory.cpp b/fuzzylite/src/factory/ConstructionFactory.cpp
new file mode 100644
index 0000000..b316e52
--- /dev/null
+++ b/fuzzylite/src/factory/ConstructionFactory.cpp
@@ -0,0 +1,114 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/ConstructionFactory.h"
+
+#include "fl/Exception.h"
+#include "fl/defuzzifier/Defuzzifier.h"
+#include "fl/hedge/Hedge.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/TNorm.h"
+#include "fl/term/Function.h"
+#include "fl/term/Term.h"
+
+
+
+namespace fl {
+
+ template <typename T>
+ ConstructionFactory<T>::ConstructionFactory(const std::string& name) : _name(name) {
+
+ }
+
+ template <typename T>
+ ConstructionFactory<T>::~ConstructionFactory() {
+ }
+
+ template<typename T>
+ std::string ConstructionFactory<T>::name() const {
+ return this->_name;
+ }
+
+ template <typename T>
+ void ConstructionFactory<T>::registerConstructor(const std::string& key, Constructor constructor) {
+ this->_constructors[key] = constructor;
+ }
+
+ template <typename T>
+ void ConstructionFactory<T>::deregisterConstructor(const std::string& key) {
+ typename std::map<std::string, Constructor>::iterator it = this->_constructors.find(key);
+ if (it != this->_constructors.end()) {
+ this->_constructors.erase(it);
+ }
+ }
+
+ template <typename T>
+ bool ConstructionFactory<T>::hasConstructor(const std::string& key) const {
+ typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key);
+ return (it != this->_constructors.end());
+ }
+
+ template <typename T>
+ typename ConstructionFactory<T>::Constructor ConstructionFactory<T>::getConstructor(const std::string& key) const {
+ typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key);
+ if (it != this->_constructors.end()) {
+ return it->second;
+ }
+ return fl::null;
+ }
+
+ template <typename T>
+ T ConstructionFactory<T>::constructObject(const std::string& key) const {
+ typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.find(key);
+ if (it != this->_constructors.end()) {
+ if (it->second) {
+ return it->second();
+ }
+ return fl::null;
+ }
+ std::ostringstream ss;
+ ss << "[factory error] constructor of " + _name + " <" << key << "> not registered";
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+
+ template <typename T>
+ std::vector<std::string> ConstructionFactory<T>::available() const {
+ std::vector<std::string> result;
+ typename std::map<std::string, Constructor>::const_iterator it = this->_constructors.begin();
+ while (it != this->_constructors.end()) {
+ result.push_back(it->first);
+ ++it;
+ }
+ return result;
+ }
+
+ template class ConstructionFactory<Defuzzifier*>;
+ template class ConstructionFactory<Hedge*>;
+ template class ConstructionFactory<SNorm*>;
+ template class ConstructionFactory<TNorm*>;
+ template class ConstructionFactory<Term*>;
+}
+
+
+
diff --git a/fuzzylite/src/factory/DefuzzifierFactory.cpp b/fuzzylite/src/factory/DefuzzifierFactory.cpp
new file mode 100644
index 0000000..8d89c2d
--- /dev/null
+++ b/fuzzylite/src/factory/DefuzzifierFactory.cpp
@@ -0,0 +1,78 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/DefuzzifierFactory.h"
+
+#include "fl/defuzzifier/Bisector.h"
+#include "fl/defuzzifier/Centroid.h"
+#include "fl/defuzzifier/SmallestOfMaximum.h"
+#include "fl/defuzzifier/LargestOfMaximum.h"
+#include "fl/defuzzifier/MeanOfMaximum.h"
+#include "fl/defuzzifier/WeightedAverage.h"
+#include "fl/defuzzifier/WeightedSum.h"
+
+namespace fl {
+
+ DefuzzifierFactory::DefuzzifierFactory() : ConstructionFactory<Defuzzifier*>("Defuzzifier") {
+ registerConstructor("", fl::null);
+ registerConstructor(Bisector().className(), &(Bisector::constructor));
+ registerConstructor(Centroid().className(), &(Centroid::constructor));
+ registerConstructor(LargestOfMaximum().className(), &(LargestOfMaximum::constructor));
+ registerConstructor(MeanOfMaximum().className(), &(MeanOfMaximum::constructor));
+ registerConstructor(SmallestOfMaximum().className(), &(SmallestOfMaximum::constructor));
+ registerConstructor(WeightedAverage().className(), &(WeightedAverage::constructor));
+ registerConstructor(WeightedSum().className(), &(WeightedSum::constructor));
+ }
+
+ DefuzzifierFactory::~DefuzzifierFactory() {
+
+ }
+
+ Defuzzifier* DefuzzifierFactory::constructDefuzzifier(const std::string& key,
+ int resolution, WeightedDefuzzifier::Type type) const {
+ Defuzzifier* result = constructObject(key);
+ if (IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<IntegralDefuzzifier*> (result)) {
+ integralDefuzzifier->setResolution(resolution);
+ } else if (WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (result)) {
+ weightedDefuzzifier->setType(type);
+ }
+ return result;
+ }
+
+ Defuzzifier* DefuzzifierFactory::constructDefuzzifier(const std::string& key, int resolution) const {
+ Defuzzifier* result = constructObject(key);
+ if (IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<IntegralDefuzzifier*> (result)) {
+ integralDefuzzifier->setResolution(resolution);
+ }
+ return result;
+ }
+
+ Defuzzifier* DefuzzifierFactory::constructDefuzzifier(const std::string& key, WeightedDefuzzifier::Type type) {
+ Defuzzifier* result = constructObject(key);
+ if (WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (result)) {
+ weightedDefuzzifier->setType(type);
+ }
+ return result;
+ }
+}
diff --git a/fuzzylite/src/factory/FactoryManager.cpp b/fuzzylite/src/factory/FactoryManager.cpp
new file mode 100644
index 0000000..c2374dc
--- /dev/null
+++ b/fuzzylite/src/factory/FactoryManager.cpp
@@ -0,0 +1,127 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/FactoryManager.h"
+
+#include "fl/factory/DefuzzifierFactory.h"
+#include "fl/factory/FunctionFactory.h"
+#include "fl/factory/HedgeFactory.h"
+#include "fl/factory/SNormFactory.h"
+#include "fl/factory/TermFactory.h"
+#include "fl/factory/TNormFactory.h"
+
+namespace fl {
+
+ FactoryManager FactoryManager::_instance;
+
+ FactoryManager* FactoryManager::instance() {
+ return &_instance;
+ }
+
+ FactoryManager::FactoryManager() :
+ _tnorm(new TNormFactory), _snorm(new SNormFactory), _defuzzifier(new DefuzzifierFactory),
+ _term(new TermFactory), _hedge(new HedgeFactory), _function(new FunctionFactory) {
+ }
+
+ FactoryManager::FactoryManager(TNormFactory* tnorm, SNormFactory* snorm,
+ DefuzzifierFactory* defuzzifier, TermFactory* term,
+ HedgeFactory* hedge, FunctionFactory* function) :
+ _tnorm(tnorm), _snorm(snorm), _defuzzifier(defuzzifier), _term(term), _hedge(hedge),
+ _function(function) {
+ }
+
+ FactoryManager::FactoryManager(const FactoryManager& other)
+ : _tnorm(fl::null), _snorm(fl::null), _defuzzifier(fl::null), _term(fl::null), _hedge(fl::null), _function(fl::null) {
+ if (other._tnorm.get()) this->_tnorm.reset(new TNormFactory(*other._tnorm.get()));
+ if (other._snorm.get()) this->_snorm.reset(new SNormFactory(*other._snorm.get()));
+ if (other._defuzzifier.get()) this->_defuzzifier.reset(new DefuzzifierFactory(*other._defuzzifier.get()));
+ if (other._term.get()) this->_term.reset(new TermFactory(*other._term.get()));
+ if (other._hedge.get()) this->_hedge.reset(new HedgeFactory(*other._hedge.get()));
+ if (other._function.get()) this->_function.reset(new FunctionFactory(*other._function.get()));
+ }
+
+ FactoryManager& FactoryManager::operator=(const FactoryManager& other) {
+ if (this != &other) {
+ if (other._tnorm.get()) this->_tnorm.reset(new TNormFactory(*other._tnorm.get()));
+ if (other._snorm.get()) this->_snorm.reset(new SNormFactory(*other._snorm.get()));
+ if (other._defuzzifier.get()) this->_defuzzifier.reset(new DefuzzifierFactory(*other._defuzzifier.get()));
+ if (other._term.get()) this->_term.reset(new TermFactory(*other._term.get()));
+ if (other._hedge.get()) this->_hedge.reset(new HedgeFactory(*other._hedge.get()));
+ if (other._function.get()) this->_function.reset(new FunctionFactory(*other._function.get()));
+ }
+ return *this;
+ }
+
+ FactoryManager::~FactoryManager() {
+ }
+
+ void FactoryManager::setTnorm(TNormFactory* tnorm) {
+ this->_tnorm.reset(tnorm);
+ }
+
+ TNormFactory* FactoryManager::tnorm() const {
+ return this->_tnorm.get();
+ }
+
+ void FactoryManager::setSnorm(SNormFactory* snorm) {
+ this->_snorm.reset(snorm);
+ }
+
+ SNormFactory* FactoryManager::snorm() const {
+ return this->_snorm.get();
+ }
+
+ void FactoryManager::setDefuzzifier(DefuzzifierFactory* defuzzifier) {
+ this->_defuzzifier.reset(defuzzifier);
+ }
+
+ DefuzzifierFactory* FactoryManager::defuzzifier() const {
+ return this->_defuzzifier.get();
+ }
+
+ void FactoryManager::setTerm(TermFactory* term) {
+ this->_term.reset(term);
+ }
+
+ TermFactory* FactoryManager::term() const {
+ return this->_term.get();
+ }
+
+ void FactoryManager::setHedge(HedgeFactory* hedge) {
+ this->_hedge.reset(hedge);
+ }
+
+ HedgeFactory* FactoryManager::hedge() const {
+ return this->_hedge.get();
+ }
+
+ void FactoryManager::setFunction(FunctionFactory* function) {
+ this->_function.reset(function);
+ }
+
+ FunctionFactory* FactoryManager::function() const {
+ return this->_function.get();
+ }
+
+}
diff --git a/fuzzylite/src/factory/FunctionFactory.cpp b/fuzzylite/src/factory/FunctionFactory.cpp
new file mode 100644
index 0000000..3c0718d
--- /dev/null
+++ b/fuzzylite/src/factory/FunctionFactory.cpp
@@ -0,0 +1,172 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/FunctionFactory.h"
+
+#include "fl/rule/Rule.h"
+
+namespace fl {
+
+ FunctionFactory::FunctionFactory() : CloningFactory<Function::Element*>("Function::Element") {
+ registerOperators();
+ registerFunctions();
+ }
+
+ FunctionFactory::~FunctionFactory() {
+
+ }
+
+ void FunctionFactory::registerOperators() {
+ //OPERATORS:
+ int p = 100;
+ //First order: not, negate:
+ registerObject("!", new Function::Element("!", "Logical NOT",
+ Function::Element::OPERATOR, &(fl::Op::logicalNot), p, 1)); //logical not
+ registerObject("~", new Function::Element("~", "Negation",
+ Function::Element::OPERATOR, &(fl::Op::negate), p, 1)); // ~ negates a number
+
+ p -= 10;
+ //Second order: power
+ registerObject("^", new Function::Element("^", "Power",
+ Function::Element::OPERATOR, &(std::pow), p, 1));
+
+ p -= 10;
+ //Third order: multiplication, division, modulo
+ registerObject("*", new Function::Element("*", "Multiplication",
+ Function::Element::OPERATOR, &(fl::Op::multiply), p));
+ registerObject("/", new Function::Element("/", "Division",
+ Function::Element::OPERATOR, &(fl::Op::divide), p));
+ registerObject("%", new Function::Element("%", "Modulo",
+ Function::Element::OPERATOR, &(fl::Op::modulo), p));
+
+ p -= 10;
+ //Fourth order: addition, subtraction
+ registerObject("+", new Function::Element("+", "Addition",
+ Function::Element::OPERATOR, &(fl::Op::add), p));
+ registerObject("-", new Function::Element("-", "Subtraction",
+ Function::Element::OPERATOR, &(fl::Op::subtract), p));
+
+ //Fifth order: logical and, logical or
+ p -= 10; //Logical AND
+ registerObject(fl::Rule::andKeyword(), new Function::Element(fl::Rule::andKeyword(), "Logical AND",
+ Function::Element::OPERATOR, &(fl::Op::logicalAnd), p));
+ p -= 10; //Logical OR
+ registerObject(fl::Rule::orKeyword(), new Function::Element(fl::Rule::orKeyword(), "Logical OR",
+ Function::Element::OPERATOR, &(fl::Op::logicalOr), p));
+ }
+
+ void FunctionFactory::registerFunctions() {
+ //FUNCTIONS
+ registerObject("gt", new Function::Element("gt", "Greater than (>)",
+ Function::Element::FUNCTION, &(fl::Op::gt)));
+ registerObject("ge", new Function::Element("ge", "Greater than or equal to (>=)",
+ Function::Element::FUNCTION, &(fl::Op::ge)));
+ registerObject("eq", new Function::Element("eq", "Equal to (==)",
+ Function::Element::FUNCTION, &(fl::Op::eq)));
+ registerObject("neq", new Function::Element("neq", "Not equal to (!=)",
+ Function::Element::FUNCTION, &(fl::Op::neq)));
+ registerObject("le", new Function::Element("le", "Less than or equal to (<=)",
+ Function::Element::FUNCTION, &(fl::Op::le)));
+ registerObject("lt", new Function::Element("lt", "Less than (<)",
+ Function::Element::FUNCTION, &(fl::Op::lt)));
+
+ registerObject("acos", new Function::Element("acos", "Inverse cosine",
+ Function::Element::FUNCTION, &(std::acos)));
+ registerObject("asin", new Function::Element("asin", "Inverse sine",
+ Function::Element::FUNCTION, &(std::asin)));
+ registerObject("atan", new Function::Element("atan", "Inverse tangent",
+ Function::Element::FUNCTION, &(std::atan)));
+
+ registerObject("ceil", new Function::Element("ceil", "Ceiling",
+ Function::Element::FUNCTION, &(std::ceil)));
+ registerObject("cos", new Function::Element("cos", "Cosine",
+ Function::Element::FUNCTION, &(std::cos)));
+ registerObject("cosh", new Function::Element("cosh", "Hyperbolic cosine",
+ Function::Element::FUNCTION, &(std::cosh)));
+ registerObject("exp", new Function::Element("exp", "Exponential",
+ Function::Element::FUNCTION, &(std::exp)));
+ registerObject("fabs", new Function::Element("fabs", "Absolute",
+ Function::Element::FUNCTION, &(std::fabs)));
+ registerObject("floor", new Function::Element("floor", "Floor",
+ Function::Element::FUNCTION, &(std::floor)));
+ registerObject("log", new Function::Element("log", "Natural logarithm",
+ Function::Element::FUNCTION, &(std::log)));
+ registerObject("log10", new Function::Element("log10", "Common logarithm",
+ Function::Element::FUNCTION, &(std::log10)));
+ registerObject("round", new Function::Element("round", "Round",
+ Function::Element::FUNCTION, &(fl::Op::round)));
+ registerObject("sin", new Function::Element("sin", "Sine",
+ Function::Element::FUNCTION, &(std::sin)));
+ registerObject("sinh", new Function::Element("sinh", "Hyperbolic sine",
+ Function::Element::FUNCTION, &(std::sinh)));
+ registerObject("sqrt", new Function::Element("sqrt", "Square root",
+ Function::Element::FUNCTION, &(std::sqrt)));
+ registerObject("tan", new Function::Element("tan", "Tangent",
+ Function::Element::FUNCTION, &(std::tan)));
+ registerObject("tanh", new Function::Element("tanh", "Hyperbolic tangent",
+ Function::Element::FUNCTION, &(std::tanh)));
+
+#if defined(FL_UNIX) && !defined(FL_USE_FLOAT)
+ //found in Unix when using double precision. not found in Windows.
+ registerObject("log1p", new Function::Element("log1p", "Natural logarithm plus one",
+ Function::Element::FUNCTION, &(log1p)));
+ registerObject("acosh", new Function::Element("acosh", "Inverse hyperbolic cosine",
+ Function::Element::FUNCTION, &(acosh)));
+ registerObject("asinh", new Function::Element("asinh", "Inverse hyperbolic sine",
+ Function::Element::FUNCTION, &(asinh)));
+ registerObject("atanh", new Function::Element("atanh", "Inverse hyperbolic tangent",
+ Function::Element::FUNCTION, &(atanh)));
+#endif
+
+ registerObject("pow", new Function::Element("pow", "Power",
+ Function::Element::FUNCTION, &(std::pow)));
+ registerObject("atan2", new Function::Element("atan2", "Inverse tangent (y,x)",
+ Function::Element::FUNCTION, &(std::atan2)));
+ registerObject("fmod", new Function::Element("fmod", "Floating-point remainder",
+ Function::Element::FUNCTION, &(std::fmod)));
+ }
+
+ std::vector<std::string> FunctionFactory::availableOperators() const {
+ std::vector<std::string> result;
+ std::map<std::string, Function::Element*>::const_iterator it = this->_objects.begin();
+ while (it != this->_objects.end()) {
+ if (it->second and it->second->type == Function::Element::OPERATOR)
+ result.push_back(it->first);
+ ++it;
+ }
+ return result;
+ }
+
+ std::vector<std::string> FunctionFactory::availableFunctions() const {
+ std::vector<std::string> result;
+ std::map<std::string, Function::Element*>::const_iterator it = this->_objects.begin();
+ while (it != this->_objects.end()) {
+ if (it->second and it->second->type == Function::Element::FUNCTION)
+ result.push_back(it->first);
+ ++it;
+ }
+ return result;
+ }
+
+}
diff --git a/fuzzylite/src/factory/HedgeFactory.cpp b/fuzzylite/src/factory/HedgeFactory.cpp
new file mode 100644
index 0000000..ea5f77a
--- /dev/null
+++ b/fuzzylite/src/factory/HedgeFactory.cpp
@@ -0,0 +1,51 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/HedgeFactory.h"
+
+#include "fl/hedge/Any.h"
+#include "fl/hedge/Extremely.h"
+#include "fl/hedge/Not.h"
+#include "fl/hedge/Seldom.h"
+#include "fl/hedge/Somewhat.h"
+#include "fl/hedge/Very.h"
+
+
+namespace fl {
+
+ HedgeFactory::HedgeFactory() : ConstructionFactory<Hedge*>("Hedge") {
+ registerConstructor("", fl::null);
+ registerConstructor(Any().name(), &(Any::constructor));
+ registerConstructor(Extremely().name(), &(Extremely::constructor));
+ registerConstructor(Not().name(), &(Not::constructor));
+ registerConstructor(Seldom().name(), &(Seldom::constructor));
+ registerConstructor(Somewhat().name(), &(Somewhat::constructor));
+ registerConstructor(Very().name(), &(Very::constructor));
+ }
+
+ HedgeFactory::~HedgeFactory() {
+
+ }
+
+}
diff --git a/fuzzylite/src/factory/SNormFactory.cpp b/fuzzylite/src/factory/SNormFactory.cpp
new file mode 100644
index 0000000..6e42551
--- /dev/null
+++ b/fuzzylite/src/factory/SNormFactory.cpp
@@ -0,0 +1,55 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/SNormFactory.h"
+
+#include "fl/norm/s/AlgebraicSum.h"
+#include "fl/norm/s/BoundedSum.h"
+#include "fl/norm/s/DrasticSum.h"
+#include "fl/norm/s/EinsteinSum.h"
+#include "fl/norm/s/HamacherSum.h"
+#include "fl/norm/s/Maximum.h"
+#include "fl/norm/s/NilpotentMaximum.h"
+#include "fl/norm/s/NormalizedSum.h"
+
+namespace fl {
+
+ SNormFactory::SNormFactory() : ConstructionFactory<SNorm*>("SNorm") {
+ registerConstructor("", fl::null);
+ registerConstructor(AlgebraicSum().className(), &(AlgebraicSum::constructor));
+ registerConstructor(BoundedSum().className(), &(BoundedSum::constructor));
+ registerConstructor(DrasticSum().className(), &(DrasticSum::constructor));
+ registerConstructor(EinsteinSum().className(), &(EinsteinSum::constructor));
+ registerConstructor(HamacherSum().className(), &(HamacherSum::constructor));
+ registerConstructor(Maximum().className(), &(Maximum::constructor));
+ registerConstructor(NilpotentMaximum().className(), &(NilpotentMaximum::constructor));
+ registerConstructor(NormalizedSum().className(), &(NormalizedSum::constructor));
+ }
+
+ SNormFactory::~SNormFactory() {
+
+ }
+
+
+}
diff --git a/fuzzylite/src/factory/TNormFactory.cpp b/fuzzylite/src/factory/TNormFactory.cpp
new file mode 100644
index 0000000..95628cc
--- /dev/null
+++ b/fuzzylite/src/factory/TNormFactory.cpp
@@ -0,0 +1,53 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/TNormFactory.h"
+
+#include "fl/norm/t/AlgebraicProduct.h"
+#include "fl/norm/t/BoundedDifference.h"
+#include "fl/norm/t/DrasticProduct.h"
+#include "fl/norm/t/EinsteinProduct.h"
+#include "fl/norm/t/HamacherProduct.h"
+#include "fl/norm/t/Minimum.h"
+#include "fl/norm/t/NilpotentMinimum.h"
+
+namespace fl {
+
+ TNormFactory::TNormFactory() : ConstructionFactory<TNorm*>("TNorm") {
+ registerConstructor("", fl::null);
+ registerConstructor(AlgebraicProduct().className(), &(AlgebraicProduct::constructor));
+ registerConstructor(BoundedDifference().className(), &(BoundedDifference::constructor));
+ registerConstructor(DrasticProduct().className(), &(DrasticProduct::constructor));
+ registerConstructor(EinsteinProduct().className(), &(EinsteinProduct::constructor));
+ registerConstructor(HamacherProduct().className(), &(HamacherProduct::constructor));
+ registerConstructor(Minimum().className(), &(Minimum::constructor));
+ registerConstructor(NilpotentMinimum().className(), &(NilpotentMinimum::constructor));
+ }
+
+ TNormFactory::~TNormFactory() {
+
+ }
+
+
+}
diff --git a/fuzzylite/src/factory/TermFactory.cpp b/fuzzylite/src/factory/TermFactory.cpp
new file mode 100644
index 0000000..10f3c15
--- /dev/null
+++ b/fuzzylite/src/factory/TermFactory.cpp
@@ -0,0 +1,81 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/factory/TermFactory.h"
+
+#include "fl/Exception.h"
+#include "fl/term/Term.h"
+#include "fl/term/Bell.h"
+#include "fl/term/Concave.h"
+#include "fl/term/Constant.h"
+#include "fl/term/Cosine.h"
+#include "fl/term/Discrete.h"
+#include "fl/term/Function.h"
+#include "fl/term/Gaussian.h"
+#include "fl/term/GaussianProduct.h"
+#include "fl/term/Linear.h"
+#include "fl/term/PiShape.h"
+#include "fl/term/Ramp.h"
+#include "fl/term/Rectangle.h"
+#include "fl/term/SShape.h"
+#include "fl/term/Sigmoid.h"
+#include "fl/term/SigmoidDifference.h"
+#include "fl/term/SigmoidProduct.h"
+#include "fl/term/Spike.h"
+#include "fl/term/Trapezoid.h"
+#include "fl/term/Triangle.h"
+#include "fl/term/ZShape.h"
+
+namespace fl {
+
+ TermFactory::TermFactory() : ConstructionFactory<Term*>("Term") {
+ registerConstructor("", fl::null);
+ registerConstructor(Bell().className(), &(Bell::constructor));
+ registerConstructor(Concave().className(), &(Concave::constructor));
+ registerConstructor(Constant().className(), &(Constant::constructor));
+ registerConstructor(Cosine().className(), &(Cosine::constructor));
+ registerConstructor(Discrete().className(), &(Discrete::constructor));
+ registerConstructor(Function().className(), &(Function::constructor));
+ registerConstructor(Gaussian().className(), &(Gaussian::constructor));
+ registerConstructor(GaussianProduct().className(), &(GaussianProduct::constructor));
+ registerConstructor(Linear().className(), &(Linear::constructor));
+ registerConstructor(PiShape().className(), &(PiShape::constructor));
+ registerConstructor(Ramp().className(), &(Ramp::constructor));
+ registerConstructor(Rectangle().className(), &(Rectangle::constructor));
+ registerConstructor(SShape().className(), &(SShape::constructor));
+ registerConstructor(Sigmoid().className(), &(Sigmoid::constructor));
+ registerConstructor(SigmoidDifference().className(), &(SigmoidDifference::constructor));
+ registerConstructor(SigmoidProduct().className(), &(SigmoidProduct::constructor));
+ registerConstructor(Spike().className(), &(Spike::constructor));
+ registerConstructor(Trapezoid().className(), &(Trapezoid::constructor));
+ registerConstructor(Triangle().className(), &(Triangle::constructor));
+ registerConstructor(ZShape().className(), &(ZShape::constructor));
+ }
+
+ TermFactory::~TermFactory() {
+
+ }
+
+
+}
diff --git a/fuzzylite/src/fuzzylite.cpp b/fuzzylite/src/fuzzylite.cpp
new file mode 100644
index 0000000..06b635a
--- /dev/null
+++ b/fuzzylite/src/fuzzylite.cpp
@@ -0,0 +1,128 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/fuzzylite.h"
+
+namespace fl {
+
+ int fuzzylite::_decimals = 3;
+ scalar fuzzylite::_macheps = 1e-6;
+ bool fuzzylite::_debug = false;
+ bool fuzzylite::_logging = true;
+
+ std::string fuzzylite::name() {
+ return "fuzzylite";
+ }
+
+ std::string fuzzylite::fullname() {
+ return name() + "-" + longVersion();
+ }
+
+ std::string fuzzylite::version() {
+ return FL_VERSION;
+ }
+
+ std::string fuzzylite::longVersion() {
+ return FL_VERSION "b" FL_DATE;
+ }
+
+ std::string fuzzylite::license() {
+ return "GNU Lesser General Public License v3.0";
+ }
+
+ std::string fuzzylite::author() {
+ return "Juan Rada-Vilela, Ph.D.";
+ }
+
+ std::string fuzzylite::company() {
+ return "FuzzyLite Limited";
+ }
+
+ std::string fuzzylite::website() {
+ return "http://www.fuzzylite.com/";
+ }
+
+ std::string fuzzylite::date() {
+ return FL_DATE;
+ }
+
+ std::string fuzzylite::platform() {
+#ifdef FL_UNIX
+ return "Unix";
+#elif defined FL_WINDOWS
+ return "Windows";
+#else
+ return "?";
+#endif
+ }
+
+ std::string fuzzylite::floatingPoint() {
+ scalar someScalar = 0;
+ (void) someScalar;
+ std::string type;
+
+ std::ostringstream ss;
+#ifdef FL_USE_FLOAT
+ type = "float";
+#else
+ type = "double";
+#endif
+ ss << "fl::scalar is defined as \'" << type << "\' using " <<
+ sizeof (someScalar) << " bytes";
+ return ss.str();
+ }
+
+ void fuzzylite::setDebug(bool debug) {
+ _debug = debug;
+ }
+
+ bool fuzzylite::debug() {
+ return _debug;
+ }
+
+ void fuzzylite::setDecimals(int decimals) {
+ _decimals = decimals;
+ }
+
+ int fuzzylite::decimals() {
+ return _decimals;
+ }
+
+ void fuzzylite::setMachEps(scalar macheps) {
+ _macheps = macheps;
+ }
+
+ scalar fuzzylite::macheps() {
+ return _macheps;
+ }
+
+ void fuzzylite::setLogging(bool logging) {
+ _logging = logging;
+ }
+
+ bool fuzzylite::logging() {
+ return _logging;
+ }
+
+}
diff --git a/fuzzylite/src/hedge/Any.cpp b/fuzzylite/src/hedge/Any.cpp
new file mode 100644
index 0000000..72724f1
--- /dev/null
+++ b/fuzzylite/src/hedge/Any.cpp
@@ -0,0 +1,52 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Any.h"
+
+namespace fl {
+
+ Any::Any() {
+ }
+
+ Any::~Any() {
+ }
+
+ std::string Any::name() const {
+ return "any";
+ }
+
+ scalar Any::hedge(scalar x) const {
+ (void) x;
+ return 1.0;
+ }
+
+ Any* Any::clone() const {
+ return new Any(*this);
+ }
+
+ Hedge* Any::constructor() {
+ return new Any;
+ }
+
+}
diff --git a/fuzzylite/src/hedge/Extremely.cpp b/fuzzylite/src/hedge/Extremely.cpp
new file mode 100644
index 0000000..eabbb60
--- /dev/null
+++ b/fuzzylite/src/hedge/Extremely.cpp
@@ -0,0 +1,50 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Extremely.h"
+
+#include "fl/Operation.h"
+
+namespace fl {
+
+ std::string Extremely::name() const {
+ return "extremely";
+ }
+
+ scalar Extremely::hedge(scalar x) const {
+ return Op::isLE(x, 0.5)
+ ? 2.0 * x * x
+ : 1.0 - 2.0 * (1.0 - x) * (1.0 - x);
+ }
+
+ Extremely* Extremely::clone() const {
+ return new Extremely(*this);
+ }
+
+ Hedge* Extremely::constructor() {
+ return new Extremely;
+ }
+
+
+}
diff --git a/fuzzylite/src/hedge/Not.cpp b/fuzzylite/src/hedge/Not.cpp
new file mode 100644
index 0000000..531213f
--- /dev/null
+++ b/fuzzylite/src/hedge/Not.cpp
@@ -0,0 +1,46 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Not.h"
+
+namespace fl {
+
+ std::string Not::name() const {
+ return "not";
+ }
+
+ scalar Not::hedge(scalar x) const {
+ return 1.0 - x;
+ }
+
+ Not* Not::clone() const {
+ return new Not(*this);
+ }
+
+ Hedge* Not::constructor() {
+ return new Not;
+ }
+
+
+}
diff --git a/fuzzylite/src/hedge/Seldom.cpp b/fuzzylite/src/hedge/Seldom.cpp
new file mode 100644
index 0000000..d87b48a
--- /dev/null
+++ b/fuzzylite/src/hedge/Seldom.cpp
@@ -0,0 +1,50 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Seldom.h"
+
+#include "fl/Operation.h"
+
+namespace fl {
+
+ std::string Seldom::name() const {
+ return "seldom";
+ }
+
+ scalar Seldom::hedge(scalar x) const {
+ return Op::isLE(x, 0.5)
+ ? std::sqrt(x / 2.0)
+ : 1.0 - std::sqrt((1.0 - x) / 2.0);
+ }
+
+ Seldom* Seldom::clone() const {
+ return new Seldom(*this);
+ }
+
+ Hedge* Seldom::constructor() {
+ return new Seldom;
+ }
+
+
+}
diff --git a/fuzzylite/src/hedge/Somewhat.cpp b/fuzzylite/src/hedge/Somewhat.cpp
new file mode 100644
index 0000000..16c371b
--- /dev/null
+++ b/fuzzylite/src/hedge/Somewhat.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Somewhat.h"
+
+namespace fl {
+
+ std::string Somewhat::name() const {
+ return "somewhat";
+ }
+
+ scalar Somewhat::hedge(scalar x) const {
+ return std::sqrt(x);
+ }
+
+ Somewhat* Somewhat::clone() const {
+ return new Somewhat(*this);
+ }
+
+ Hedge* Somewhat::constructor() {
+ return new Somewhat;
+ }
+
+}
diff --git a/fuzzylite/src/hedge/Very.cpp b/fuzzylite/src/hedge/Very.cpp
new file mode 100644
index 0000000..dc1fbec
--- /dev/null
+++ b/fuzzylite/src/hedge/Very.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/hedge/Very.h"
+
+namespace fl {
+
+ std::string Very::name() const {
+ return "very";
+ }
+
+ scalar Very::hedge(scalar x) const {
+ return x * x;
+ }
+
+ Very* Very::clone() const {
+ return new Very(*this);
+ }
+
+ Hedge* Very::constructor() {
+ return new Very;
+ }
+
+}
diff --git a/fuzzylite/src/imex/CppExporter.cpp b/fuzzylite/src/imex/CppExporter.cpp
new file mode 100644
index 0000000..7b21087
--- /dev/null
+++ b/fuzzylite/src/imex/CppExporter.cpp
@@ -0,0 +1,234 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/CppExporter.h"
+
+#include "fl/Headers.h"
+
+#include <algorithm>
+
+namespace fl {
+
+ CppExporter::CppExporter(bool prefixNamespace) : Exporter(),
+ _prefixNamespace(prefixNamespace) {
+ }
+
+ CppExporter::~CppExporter() {
+ }
+
+ std::string CppExporter::name() const {
+ return "CppExporter";
+ }
+
+ std::string CppExporter::fl(const std::string& clazz) const {
+ return _prefixNamespace ? "fl::" + clazz : clazz;
+ }
+
+ void CppExporter::setPrefixNamespace(bool prefixNamespace){
+ this->_prefixNamespace = prefixNamespace;
+ }
+
+ bool CppExporter::isPrefixNamespace() const{
+ return this->_prefixNamespace;
+ }
+
+ std::string CppExporter::toString(const Engine* engine) const {
+ std::ostringstream cpp;
+ if (not _prefixNamespace) cpp << "using namespace fl;\n\n";
+ cpp << fl("Engine* ") << "engine = new " << fl("Engine;\n");
+ cpp << "engine->setName(\"" << engine->getName() << "\");\n";
+
+ cpp << "\n";
+
+ for (int i = 0; i < engine->numberOfInputVariables(); ++i) {
+ cpp << toString(engine->getInputVariable(i), engine) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ cpp << toString(engine->getOutputVariable(i), engine) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) {
+ cpp << toString(engine->getRuleBlock(i), engine) << "\n";
+ }
+
+ return cpp.str();
+ }
+
+ 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);
+ }
+ ss << fl("InputVariable* ") << name << " = new " << fl("InputVariable;\n");
+ ss << name << "->setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << "->setName(\"" << inputVariable->getName() << "\");\n";
+ ss << name << "->setRange(" <<
+ toString(inputVariable->getMinimum()) << ", " <<
+ toString(inputVariable->getMaximum()) << ");\n";
+ for (int t = 0; t < inputVariable->numberOfTerms(); ++t) {
+ ss << name << "->addTerm(" << toString(inputVariable->getTerm(t)) << ");\n";
+ }
+ ss << "engine->addInputVariable(" << name << ");\n";
+ return ss.str();
+ }
+
+ 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);
+ }
+ ss << fl("OutputVariable* ") << name << " = new " << fl("OutputVariable;\n");
+ ss << name << "->setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << "->setName(\"" << outputVariable->getName() << "\");\n";
+ ss << name << "->setRange(" <<
+ toString(outputVariable->getMinimum()) << ", " <<
+ toString(outputVariable->getMaximum()) << ");\n";
+ ss << name << "->fuzzyOutput()->setAccumulation(" <<
+ toString(outputVariable->fuzzyOutput()->getAccumulation()) << ");\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 << "->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);
+ }
+ ss << fl("RuleBlock* ") << name << " = new " << fl("RuleBlock;\n");
+ ss << name << "->setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << "->setName(\"" << ruleBlock->getName() << "\");\n";
+ ss << name << "->setConjunction(" <<
+ toString(ruleBlock->getConjunction()) << ");\n";
+ ss << name << "->setDisjunction("
+ << toString(ruleBlock->getDisjunction()) << ");\n";
+ ss << name << "->setActivation("
+ << toString(ruleBlock->getActivation()) << ");\n";
+ for (int r = 0; r < ruleBlock->numberOfRules(); ++r) {
+ ss << name << "->addRule(" << "fl::Rule::parse(\"" <<
+ ruleBlock->getRule(r)->getText() << "\", engine));\n";
+ }
+ ss << "engine->addRuleBlock(" << name << ");\n";
+ return ss.str();
+ }
+
+ std::string CppExporter::toString(scalar value) const {
+ if (fl::Op::isNaN(value))
+ return "fl::nan";
+ if (fl::Op::isInf(value)){
+ return (value > 0 ? "fl::inf" : "-fl::inf");
+ }
+ return fl::Op::str(value);
+ }
+
+ std::string CppExporter::toString(const Term* term) const {
+ if (not term) return "fl::null";
+
+ 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()), ", ") << ")";
+ return ss.str();
+ }
+
+ if (const Function * function = dynamic_cast<const Function*> (term)) {
+ std::ostringstream ss;
+ ss << fl(term->className()) << "::create(\"" << term->getName() << "\", "
+ << "\"" << function->getFormula() << "\", engine)";
+ return ss.str();
+ }
+
+ 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(), ", ") << ")";
+ return ss.str();
+ }
+
+ std::ostringstream ss;
+ ss << "new " << fl(term->className()) << "(\"" << term->getName() << "\", "
+ << Op::findReplace(term->parameters(), " ", ", ") << ")";
+ return ss.str();
+ }
+
+ std::string CppExporter::toString(const Hedge * hedge) const {
+ if (hedge->name() == Any().name()) return "new " + fl("Any");
+ if (hedge->name() == Extremely().name()) return "new " + fl("Extremely");
+ if (hedge->name() == Not().name()) return "new " + fl("Not");
+ if (hedge->name() == Seldom().name()) return "new " + fl("Seldom");
+ if (hedge->name() == Somewhat().name()) return "new " + fl("Somewhat");
+ if (hedge->name() == Very().name()) return "new " + fl("Very");
+ return "new " + fl(hedge->name());
+ }
+
+ std::string CppExporter::toString(const Norm* op) const {
+ if (not op) return "fl::null";
+ return "new " + fl(op->className());
+ }
+
+ std::string CppExporter::toString(const Defuzzifier* defuzzifier) const {
+ if (not defuzzifier) return "fl::null";
+ if (const IntegralDefuzzifier * integralDefuzzifier =
+ dynamic_cast<const IntegralDefuzzifier*> (defuzzifier)) {
+ return "new " + fl(integralDefuzzifier->className()) + "("
+ + fl::Op::str(integralDefuzzifier->getResolution()) + ")";
+ }
+ if (const WeightedDefuzzifier * weightedDefuzzifier =
+ dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) {
+ return "new " + weightedDefuzzifier->className() +
+ "(\"" + weightedDefuzzifier->getTypeName() + "\")";
+ }
+ return "new " + fl(defuzzifier->className());
+ }
+
+ CppExporter* CppExporter::clone() const {
+ return new CppExporter(*this);
+ }
+
+}
diff --git a/fuzzylite/src/imex/Exporter.cpp b/fuzzylite/src/imex/Exporter.cpp
new file mode 100644
index 0000000..5b0fe1b
--- /dev/null
+++ b/fuzzylite/src/imex/Exporter.cpp
@@ -0,0 +1,49 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/Exporter.h"
+#include "fl/Exception.h"
+
+#include <fstream>
+
+namespace fl {
+
+ 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);
+ }
+ writer << toString(engine) << std::endl;
+ writer.close();
+ }
+
+}
diff --git a/fuzzylite/src/imex/FclExporter.cpp b/fuzzylite/src/imex/FclExporter.cpp
new file mode 100644
index 0000000..cf8ffb0
--- /dev/null
+++ b/fuzzylite/src/imex/FclExporter.cpp
@@ -0,0 +1,256 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+#include "fl/imex/FclExporter.h"
+
+#include "fl/Headers.h"
+
+#include <sstream>
+
+namespace fl {
+
+ FclExporter::FclExporter(const std::string& indent) : Exporter(), _indent(indent) {
+ }
+
+ FclExporter::~FclExporter() {
+ }
+
+ void FclExporter::setIndent(const std::string& indent) {
+ this->_indent = indent;
+ }
+
+ std::string FclExporter::getIndent() const {
+ return this->_indent;
+ }
+
+ std::string FclExporter::name() const {
+ return "FclExporter";
+ }
+
+ std::string FclExporter::toString(const Engine* engine) const {
+ std::ostringstream fcl;
+ fcl << "FUNCTION_BLOCK " << engine->getName() << "\n\n";
+
+ fcl << "VAR_INPUT\n";
+ for (int 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) {
+ fcl << _indent << Op::validName(engine->getOutputVariable(i)->getName()) << ": REAL;\n";
+ }
+ fcl << "END_VAR\n\n";
+
+ for (int i = 0; i < engine->numberOfInputVariables(); ++i) {
+ InputVariable* inputVariable = engine->getInputVariable(i);
+ fcl << toString(inputVariable) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* outputVariable = engine->getOutputVariable(i);
+ fcl << toString(outputVariable) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) {
+ RuleBlock* ruleblock = engine->getRuleBlock(i);
+ fcl << toString(ruleblock) << "\n";
+ }
+
+ fcl << "END_FUNCTION_BLOCK\n";
+ return fcl.str();
+ }
+
+ 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, " .. ",
+ inputVariable->getMinimum(), inputVariable->getMaximum())
+ << ");\n";
+
+ for (int t = 0; t < inputVariable->numberOfTerms(); ++t) {
+ Term* term = inputVariable->getTerm(t);
+ fcl << _indent << "TERM " << Op::validName(term->getName()) << " := " << toString(term)
+ << ";\n";
+ }
+ fcl << "END_FUZZIFY\n";
+ return fcl.str();
+ }
+
+ 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, " .. ",
+ outputVariable->getMinimum(), outputVariable->getMaximum())
+ << ");\n";
+
+ for (int t = 0; t < outputVariable->numberOfTerms(); ++t) {
+ Term* term = outputVariable->getTerm(t);
+ fcl << _indent << "TERM " << Op::validName(term->getName()) << " := " << toString(term)
+ << ";\n";
+ }
+ if (outputVariable->getDefuzzifier()) {
+ fcl << _indent << "METHOD : " << toString(outputVariable->getDefuzzifier()) << ";\n";
+ }
+ if (outputVariable->fuzzyOutput()->getAccumulation())
+ fcl << _indent << "ACCU : " << toString(outputVariable->fuzzyOutput()->getAccumulation()) << ";\n";
+
+ fcl << _indent << "DEFAULT := " << fl::Op::str(outputVariable->getDefaultValue());
+ if (outputVariable->isLockedPreviousOutputValue()) {
+ fcl << " | NC";
+ }
+ fcl << ";\n";
+
+ if (outputVariable->isLockedOutputValueInRange()) {
+ fcl << _indent << "LOCK : RANGE;\n";
+ }
+
+ fcl << "END_DEFUZZIFY\n";
+ return fcl.str();
+ }
+
+ 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";
+
+ for (int 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{
+ if (not norm) return "NONE";
+
+ std::string name = norm->className();
+ //TNorms
+ 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";
+
+ //SNorms
+ 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 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();
+ }
+
+ std::string FclExporter::toString(const Defuzzifier* defuzzifier) const {
+ if (not defuzzifier) return "NONE";
+ if (defuzzifier->className() == Centroid().className()) return "COG";
+ if (defuzzifier->className() == Bisector().className()) return "COA";
+ if (defuzzifier->className() == SmallestOfMaximum().className()) return "LM";
+ if (defuzzifier->className() == LargestOfMaximum().className()) return "RM";
+ if (defuzzifier->className() == MeanOfMaximum().className()) return "MM";
+ if (defuzzifier->className() == WeightedAverage().className()) return "COGS";
+ if (defuzzifier->className() == WeightedSum().className()) return "COGSS";
+ return defuzzifier->className();
+ }
+
+ std::string FclExporter::toString(const Term* term) const {
+ if (not term) return "";
+ 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) << ")";
+ 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());
+ }
+
+ std::ostringstream ss;
+ ss << term->className() << " " << term->parameters();
+ return ss.str();
+ }
+
+ FclExporter* FclExporter::clone() const {
+ return new FclExporter(*this);
+ }
+
+
+}
diff --git a/fuzzylite/src/imex/FclImporter.cpp b/fuzzylite/src/imex/FclImporter.cpp
new file mode 100644
index 0000000..a56a9e1
--- /dev/null
+++ b/fuzzylite/src/imex/FclImporter.cpp
@@ -0,0 +1,607 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FclImporter.h"
+
+#include "fl/Headers.h"
+
+#include <iostream>
+#include <sstream>
+
+namespace fl {
+
+ FclImporter::FclImporter() : Importer() {
+ }
+
+ FclImporter::~FclImporter() {
+ }
+
+ std::string FclImporter::name() const {
+ return "FclImporter";
+ }
+
+ Engine* FclImporter::fromString(const std::string& fcl) const {
+ FL_unique_ptr<Engine> engine(new Engine);
+
+ std::map<std::string, std::string> tags;
+ tags["VAR_INPUT"] = "END_VAR";
+ tags["VAR_OUTPUT"] = "END_VAR";
+ tags["FUZZIFY"] = "END_FUZZIFY";
+ tags["DEFUZZIFY"] = "END_DEFUZZIFY";
+ tags["RULEBLOCK"] = "END_RULEBLOCK";
+ std::map<std::string, std::string>::const_iterator tagFinder;
+
+ std::string currentTag = "", closingTag = "";
+ std::ostringstream block;
+ std::istringstream fclReader(fcl);
+ std::string line;
+
+ int 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) == "//")) {
+ continue;
+ }
+ line = fl::Op::findReplace(line, ";", "");
+ std::istringstream tokenizer(line);
+ std::string firstToken;
+ tokenizer >> firstToken;
+
+ if (firstToken == "FUNCTION_BLOCK") {
+ if (tokenizer.rdbuf()->in_avail() > 0) {
+ std::ostringstream name;
+ std::string token;
+ tokenizer >> token;
+ name << token;
+ while (tokenizer >> token) {
+ name << " " << token;
+ }
+ engine->setName(name.str());
+ }
+ continue;
+ }
+ if (firstToken == "END_FUNCTION_BLOCK") {
+ break;
+ }
+
+ if (currentTag.empty()) {
+ tagFinder = tags.find(firstToken);
+ if (tagFinder == tags.end()) {
+ std::ostringstream ex;
+ ex << "[syntax error] unknown block definition <" << firstToken
+ << "> " << " in line " << lineNumber << ": " << line;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ currentTag = tagFinder->first;
+ closingTag = tagFinder->second;
+ block.str("");
+ block.clear();
+ block << line << "\n";
+ continue;
+ }
+
+ if (not currentTag.empty()) {
+ if (firstToken == closingTag) {
+ processBlock(currentTag, block.str(), engine.get());
+ currentTag = "";
+ closingTag = "";
+ } else if (tags.find(firstToken) != tags.end()) {
+ //if opening new block without closing the previous one
+ std::ostringstream ex;
+ ex << "[syntax error] expected <" << closingTag << "> before <"
+ << firstToken << "> in line: " << line;
+ throw fl::Exception(ex.str(), FL_AT);
+ } else {
+ block << line << "\n";
+ }
+ continue;
+ }
+ }
+
+ if (not currentTag.empty()) {
+ std::ostringstream ex;
+ ex << "[syntax error] ";
+ if (block.rdbuf()->in_avail() > 0) {
+ ex << "expected <" << closingTag << "> for block:\n" << block.str();
+ } else {
+ ex << "expected <" << closingTag << ">, but not found";
+ }
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ return engine.release();
+ }
+
+ void FclImporter::processBlock(const std::string& tag, const std::string& block, Engine* engine) const {
+ if (tag == "VAR_INPUT" or tag == "VAR_OUTPUT") {
+ processVar(tag, block, engine);
+ } else if (tag == "FUZZIFY") {
+ processFuzzify(block, engine);
+ } else if (tag == "DEFUZZIFY") {
+ processDefuzzify(block, engine);
+ } else if (tag == "RULEBLOCK") {
+ processRuleBlock(block, engine);
+ } else {
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected tag <" << tag << "> for block:\n" << block;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+
+ void FclImporter::processVar(const std::string& tag, const std::string& block, Engine* engine)const {
+ std::istringstream blockReader(block);
+ std::string line;
+
+ std::getline(blockReader, line); //discard first line as it is VAR_INPUT
+ while (std::getline(blockReader, line)) {
+ std::vector<std::string> token = Op::split(line, ":");
+ 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);
+ }
+ std::string name = fl::Op::validName(token.at(0));
+ if (tag == "VAR_INPUT")
+ engine->addInputVariable(new InputVariable(name));
+ else if (tag == "VAR_OUTPUT")
+ engine->addOutputVariable(new OutputVariable(name));
+ else {
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected tag <" << tag << "> in line: " << line;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+ }
+
+ void FclImporter::processFuzzify(const std::string& block, Engine* engine)const {
+ std::istringstream blockReader(block);
+ std::string line;
+
+ std::getline(blockReader, line);
+ std::string name;
+ std::size_t index = line.find_first_of(' ');
+ if (index != std::string::npos) {
+ name = fl::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);
+ }
+ 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);
+ }
+
+ InputVariable* inputVariable = engine->getInputVariable(name);
+ while (std::getline(blockReader, line)) {
+ std::istringstream ss(line);
+ std::string firstToken;
+ ss >> firstToken;
+ try {
+ if (firstToken == "RANGE") {
+ std::pair<scalar, scalar> minmax = parseRange(line);
+ inputVariable->setMinimum(minmax.first);
+ inputVariable->setMaximum(minmax.second);
+ } else if (firstToken == "ENABLED") {
+ inputVariable->setEnabled(parseEnabled(line));
+ } else if (firstToken == "TERM") {
+ inputVariable->addTerm(parseTerm(line, engine));
+ } else throw fl::Exception("[syntax error] unexpected token "
+ "<" + firstToken + ">" + line, FL_AT);
+ } catch (fl::Exception& ex) {
+ ex.append("At line: <" + line + ">");
+ throw;
+ }
+ }
+
+ }
+
+ void FclImporter::processDefuzzify(const std::string& block, Engine* engine) const {
+ std::istringstream blockReader(block);
+ std::string line;
+
+ std::getline(blockReader, line);
+ std::string name;
+ std::size_t index = line.find_first_of(' ');
+ if (index != std::string::npos) {
+ name = fl::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);
+ }
+ 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);
+ }
+
+ OutputVariable* outputVariable = engine->getOutputVariable(name);
+ while (std::getline(blockReader, line)) {
+ line = fl::Op::trim(line);
+ std::istringstream tokenizer(line);
+ std::string firstToken;
+ tokenizer >> firstToken;
+ if (firstToken == "TERM") {
+ outputVariable->addTerm(parseTerm(line, engine));
+ } else if (firstToken == "METHOD") {
+ outputVariable->setDefuzzifier(parseDefuzzifier(line));
+ } else if (firstToken == "ACCU") {
+ outputVariable->fuzzyOutput()->setAccumulation(parseSNorm(line));
+ } else if (firstToken == "DEFAULT") {
+ std::pair<scalar, bool> defaultAndLock = parseDefaultValue(line);
+ outputVariable->setDefaultValue(defaultAndLock.first);
+ outputVariable->setLockPreviousOutputValue(defaultAndLock.second or
+ outputVariable->isLockedPreviousOutputValue());
+ } 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);
+ } 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);
+ }
+ }
+
+ }
+
+ void FclImporter::processRuleBlock(const std::string& block, Engine* engine)const {
+ std::istringstream blockReader(block);
+ std::string line;
+
+ std::string name;
+ std::getline(blockReader, line);
+ std::size_t index = line.find_last_of(' ');
+ if (index != std::string::npos) name = line.substr(index);
+ RuleBlock * ruleblock = new RuleBlock(name);
+ engine->addRuleBlock(ruleblock);
+
+ while (std::getline(blockReader, line)) {
+ std::string firstToken = line.substr(0, line.find_first_of(' '));
+ if (firstToken == "AND") {
+ ruleblock->setConjunction(parseTNorm(line));
+ } else if (firstToken == "OR") {
+ ruleblock->setDisjunction(parseSNorm(line));
+ } else if (firstToken == "ACT") {
+ ruleblock->setActivation(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);
+ Rule* rule = new Rule(ruleText);
+ try {
+ rule->load(engine);
+ } catch (...) {
+ //ignore
+ }
+ ruleblock->addRule(rule);
+ } else {
+ std::ostringstream ex;
+ ex << "[syntax error] keyword <" << firstToken
+ << "> not recognized in line: " << line;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+ }
+
+ TNorm* FclImporter::parseTNorm(const std::string& line) const {
+ std::vector<std::string> token = Op::split(line, ":");
+ 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);
+ }
+ std::string name = Op::trim(token.at(1));
+ std::string className = name;
+ if (name == "NONE") className = "";
+ else if (name == "MIN") className = Minimum().className();
+ else if (name == "PROD") className = AlgebraicProduct().className();
+ else if (name == "BDIF") className = BoundedDifference().className();
+ else if (name == "DPROD") className = DrasticProduct().className();
+ else if (name == "EPROD") className = EinsteinProduct().className();
+ else if (name == "HPROD") className = HamacherProduct().className();
+ else if (name == "NMIN") className = NilpotentMinimum().className();
+
+ return FactoryManager::instance()->tnorm()->constructObject(className);
+ }
+
+ SNorm* FclImporter::parseSNorm(const std::string& line) const {
+ std::vector<std::string> token = Op::split(line, ":");
+ 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);
+ }
+ std::string name = Op::trim(token.at(1));
+ std::string className = name;
+ if (name == "NONE") className = "";
+ else if (name == "MAX") className = Maximum().className();
+ else if (name == "ASUM") className = AlgebraicSum().className();
+ else if (name == "BSUM") className = BoundedSum().className();
+ else if (name == "NSUM") className = NormalizedSum().className();
+ else if (name == "DSUM") className = DrasticSum().className();
+ else if (name == "ESUM") className = EinsteinSum().className();
+ else if (name == "HSUM") className = HamacherSum().className();
+ else if (name == "NMAX") className = NilpotentMaximum().className();
+
+ return FactoryManager::instance()->snorm()->constructObject(className);
+ }
+
+ Term* FclImporter::parseTerm(const std::string& line, const Engine* engine) const {
+ std::ostringstream spacer;
+ for (std::size_t i = 0; i < line.size(); ++i) {
+ if (line.at(i) == '(' or line.at(i) == ')' or line.at(i) == ',') {
+ spacer << " " << line.at(i) << " ";
+ } else if (line.at(i) == ':') {
+ spacer << " :";
+ } else if (line.at(i) == '=') {
+ spacer << "= ";
+ } else
+ spacer << line.at(i);
+ }
+ std::string spacedLine = spacer.str();
+
+ enum FSM {
+ S_KWTERM, S_NAME, S_ASSIGN, S_TERMCLASS, S_PARAMETERS
+ };
+ FSM state = S_KWTERM;
+ std::istringstream tokenizer(spacedLine);
+ std::string token;
+ std::string name, termClass;
+ std::vector<std::string> parameters;
+ while (tokenizer >> token) {
+ if (state == S_KWTERM and token == "TERM") {
+ state = S_NAME;
+ continue;
+ }
+ if (state == S_NAME) {
+ name = token;
+ state = S_ASSIGN;
+ continue;
+ }
+ if (state == S_ASSIGN and token == ":=") {
+ state = S_TERMCLASS;
+ continue;
+ }
+ if (state == S_TERMCLASS) {
+ if (fl::Op::isNumeric(token)) {
+ termClass = Constant().className();
+ parameters.push_back(token);
+ } else if (token == "(") {
+ termClass = Discrete().className();
+ } else {
+ termClass = token;
+ }
+ state = S_PARAMETERS;
+ continue;
+ }
+ if (state == S_PARAMETERS) {
+ if (termClass != Function().className() and
+ (token == "(" or token == ")" or token == ",")) {
+ continue;
+ }
+ if (token == ";") break;
+ parameters.push_back(fl::Op::trim(token));
+ }
+ }
+ if (state <= S_TERMCLASS)
+ throw fl::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));
+ std::string separator;
+ if (not dynamic_cast<Function*> (term.get())) {
+ separator = " ";
+ }
+ term->configure(Op::join(parameters, separator)); //remove spaces for text of function
+ return term.release();
+ }
+
+ Defuzzifier* FclImporter::parseDefuzzifier(const std::string& line) const {
+ std::vector<std::string> token = Op::split(line, ":");
+ 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);
+ }
+
+ std::string name = fl::Op::trim(token.at(1));
+ std::string className = name;
+ if (name == "NONE") className = "";
+ else if (name == "COG") className = Centroid().className();
+ else if (name == "COA") className = Bisector().className();
+ else if (name == "LM") className = SmallestOfMaximum().className();
+ else if (name == "RM") className = LargestOfMaximum().className();
+ else if (name == "MM") className = MeanOfMaximum().className();
+ else if (name == "COGS") className = WeightedAverage().className();
+ else if (name == "COGSS") className = WeightedSum().className();
+
+ return FactoryManager::instance()->defuzzifier()->constructObject(className);
+ }
+
+ std::pair<scalar, bool> FclImporter::parseDefaultValue(const std::string& line) const {
+ std::vector<std::string> token = Op::split(line, ":=");
+ 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);
+ }
+
+ std::vector<std::string> values = Op::split(token.at(1), "|");
+
+ std::string defaultValue = values.front();
+ std::string nc;
+ if (values.size() == 2) nc = values.back();
+
+ defaultValue = fl::Op::trim(defaultValue);
+ nc = fl::Op::trim(nc);
+
+ scalar value;
+ try {
+ value = fl::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);
+ }
+
+ bool lockPreviousOutput = (nc == "NC");
+
+ if (not (lockPreviousOutput or nc.empty())) {
+ throw fl::Exception("[syntax error] expected keyword <NC>, "
+ "but found <" + nc + "> in line: " + line, FL_AT);
+ }
+
+ return std::pair<scalar, bool>(value, lockPreviousOutput);
+ }
+
+ std::pair<scalar, scalar> FclImporter::parseRange(const std::string& line) const {
+ std::vector<std::string> token = Op::split(line, ":=");
+ 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);
+ }
+
+ std::string rangeToken = token.at(1);
+
+ std::ostringstream range;
+ for (std::size_t i = 0; i < rangeToken.size(); ++i) {
+ char character = rangeToken.at(i);
+ if (character == '(' or character == ')' or character == ' ' or character == ';')
+ continue;
+ range << character;
+ }
+ token = Op::split(range.str(), "..");
+ if (token.size() != 2) {
+ 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);
+ }
+ scalar minimum, maximum;
+ int index;
+ try {
+ minimum = Op::toScalar(token.at(index = 0));
+ maximum = Op::toScalar(token.at(index = 1));
+ } catch (std::exception& ex) {
+ (void) 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);
+ }
+ return std::pair<scalar, scalar>(minimum, maximum);
+ }
+
+ 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 "
+ "'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, "|");
+ if (flags.size() == 1) {
+ std::string flag = fl::Op::trim(flags.front());
+ output = (flag == "PREVIOUS");
+ range = (flag == "RANGE");
+ if (not (output or range)) {
+ throw fl::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());
+ 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 "
+ "<PREVIOUS|RANGE>, but found "
+ "<" + flags.front() + "|" + flags.back() + "> in line: " + line, FL_AT);
+ }
+ } else {
+ throw fl::Exception("[syntax error] expected locking flags "
+ "<PREVIOUS|RANGE>, but found "
+ "<" + value + "> in line: " + line, FL_AT);
+ }
+ return std::pair<bool, bool>(output, range);
+ }
+
+ bool FclImporter::parseEnabled(const std::string& line) const {
+ std::vector<std::string> tokens = Op::split(line, ":");
+ if (tokens.size() != 2) {
+ std::ostringstream ex;
+ ex << "[syntax error] expected property of type (key : value) in "
+ << "line: " << line;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ std::string boolean = fl::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);
+ }
+
+ FclImporter* FclImporter::clone() const {
+ return new FclImporter(*this);
+ }
+
+
+}
diff --git a/fuzzylite/src/imex/FisExporter.cpp b/fuzzylite/src/imex/FisExporter.cpp
new file mode 100644
index 0000000..0934b33
--- /dev/null
+++ b/fuzzylite/src/imex/FisExporter.cpp
@@ -0,0 +1,463 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FisExporter.h"
+
+#include "fl/Headers.h"
+
+#include <queue>
+
+namespace fl {
+
+ FisExporter::FisExporter() : Exporter() {
+ }
+
+ FisExporter::~FisExporter() {
+ }
+
+ std::string FisExporter::name() const {
+ return "FisExporter";
+ }
+
+ std::string FisExporter::toString(const Engine* engine) const {
+ std::ostringstream fis;
+ fis << exportSystem(engine) << "\n";
+
+ fis << exportInputs(engine);
+
+ fis << exportOutputs(engine);
+
+ fis << exportRules(engine);
+
+ return fis.str();
+ }
+ //TODO: deal with multiple ruleblocks, merge them into one.
+ std::string FisExporter::exportSystem(const Engine* engine) const {
+ std::ostringstream fis;
+ fis << "[System]\n";
+ fis << "Name='" << engine->getName() << "'\n";
+ std::string type;
+ if (engine->type() == Engine::Mamdani or engine->type() == Engine::Larsen) {
+ type = "mamdani";
+ } else if (engine->type() == Engine::TakagiSugeno) {
+ type = "sugeno";
+ } else if (engine->type() == Engine::Tsukamoto) {
+ type = "tsukamoto";
+ } else if (engine->type() == Engine::InverseTsukamoto) {
+ type = "inverse tsukamoto";
+ }else if (engine->type() == Engine::Hybrid){
+ type = "hybrid";
+ } else {
+ type = "unknown";
+ }
+ fis << "Type='" << type << "'\n";
+ // fis << "Version=" << FL_VERSION << "\n";
+ fis << "NumInputs=" << engine->numberOfInputVariables() << "\n";
+ fis << "NumOutputs=" << engine->numberOfOutputVariables() << "\n";
+
+ int 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) {
+ 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();
+ }
+ fis << "NumRules=" << numberOfRules << "\n";
+ fis << "AndMethod='" << toString(conjunction) << "'\n";
+ fis << "OrMethod='" << toString(disjunction) << "'\n";
+ fis << "ImpMethod='" << toString(activation) << "'\n";
+
+ const SNorm* accumulation = fl::null;
+ Defuzzifier* defuzzifier = fl::null;
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* outputVariable = engine->getOutputVariable(i);
+ if (not accumulation) accumulation = outputVariable->fuzzyOutput()->getAccumulation();
+ if (not defuzzifier) defuzzifier = outputVariable->getDefuzzifier();
+ }
+
+ fis << "AggMethod='" << toString(accumulation) << "'\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) {
+ 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 << "NumMFs=" << var->numberOfTerms() << "\n";
+ for (int ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) {
+ fis << "MF" << (ixTerm + 1) << "='" << Op::validName(var->getTerm(ixTerm)->getName()) << "':"
+ << toString(var->getTerm(ixTerm)) << "\n";
+ }
+ fis << "\n";
+ }
+ return fis.str();
+ }
+
+ std::string FisExporter::exportOutputs(const Engine* engine) const {
+ std::ostringstream fis;
+ for (int 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 << "NumMFs=" << var->numberOfTerms() << "\n";
+ for (int ixTerm = 0; ixTerm < var->numberOfTerms(); ++ixTerm) {
+ fis << "MF" << (ixTerm + 1) << "='" << Op::validName(var->getTerm(ixTerm)->getName()) << "':"
+ << toString(var->getTerm(ixTerm)) << "\n";
+ }
+ fis << "\n";
+ }
+ return fis.str();
+ }
+
+ std::string FisExporter::exportRules(const Engine* engine) const {
+ std::ostringstream fis;
+ fis << "[Rules]\n";
+ for (int 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) {
+ Rule* rule = rb->getRule(ixRule);
+ if (rule->isLoaded()) {
+ fis << exportRule(rule, engine) << "\n";
+ }
+ }
+ }
+ return fis.str();
+ }
+
+ std::string FisExporter::exportRule(const Rule* rule, const Engine* engine) const {
+ if (not rule) return "";
+ std::vector<Proposition*> propositions;
+ std::vector<Operator*> operators;
+
+ std::queue<Expression*> bfsQueue;
+ bfsQueue.push(rule->getAntecedent()->getExpression());
+ while (not bfsQueue.empty()) {
+ Expression* front = bfsQueue.front();
+ bfsQueue.pop();
+ Operator* op = dynamic_cast<Operator*> (front);
+ if (op) {
+ bfsQueue.push(op->left);
+ bfsQueue.push(op->right);
+ operators.push_back(op);
+ } else {
+ propositions.push_back(dynamic_cast<Proposition*> (front));
+ }
+ }
+
+ bool equalOperators = true;
+ for (std::size_t i = 0; i + 1 < operators.size(); ++i) {
+ if (operators.at(i)->name != operators.at(i + 1)->name) {
+ equalOperators = false;
+ break;
+ }
+ }
+ if (not equalOperators) {
+ throw fl::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)
+ inputVariables.push_back(engine->getInputVariable(i));
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i)
+ outputVariables.push_back(engine->getOutputVariable(i));
+
+ fis << translate(propositions, inputVariables) << ", ";
+ fis << translate(rule->getConsequent()->conclusions(), outputVariables);
+ fis << "(" << Op::str(rule->getWeight()) << ") : ";
+ if (operators.size() == 0) fis << "1"; //does not matter
+ else {
+ if (operators.at(0)->name == Rule::andKeyword()) fis << "1";
+ else if (operators.at(0)->name == Rule::orKeyword()) fis << "2";
+ else fis << operators.at(0)->name;
+ }
+ return fis.str();
+ }
+
+ std::string FisExporter::translate(const std::vector<Proposition*>& propositions,
+ const std::vector<Variable*> variables) const {
+ std::ostringstream ss;
+ for (std::size_t ixVariable = 0; ixVariable < variables.size(); ++ixVariable) {
+ Variable* variable = variables.at(ixVariable);
+ int 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) {
+ if (variable->getTerm(termIndex) == proposition->term) {
+ termIndexPlusOne = termIndex + 1;
+ break;
+ }
+ }
+
+ std::vector<Hedge*> hedges = proposition->hedges;
+ if (hedges.size() > 1) {
+ FL_DBG("[exporter warning] only a few combinations of multiple "
+ "hedges are supported in fis files");
+ }
+ for (std::size_t ixHedge = 0; ixHedge < hedges.size(); ++ixHedge) {
+ Hedge* hedge = hedges.at(ixHedge);
+ if (hedge->name() == Not().name()) negated *= -1;
+ else if (hedge->name() == Extremely().name()) plusHedge += 0.3;
+ else if (hedge->name() == Very().name()) plusHedge += 0.2;
+ else if (hedge->name() == Somewhat().name()) plusHedge += 0.05;
+ else if (hedge->name() == Seldom().name()) plusHedge += 0.01;
+ else if (hedge->name() == Any().name()) plusHedge += 0.99;
+ else plusHedge = fl::nan; //Unreconized hedge combination (e.g. Any)
+ }
+
+ break;
+ }
+ if (negated < 0) ss << "-";
+ if (not fl::Op::isNaN(plusHedge)) {
+ ss << fl::Op::str(termIndexPlusOne + plusHedge);
+ } else {
+ ss << termIndexPlusOne << ".?"; // Unreconized hedge combination
+ }
+ ss << " ";
+ }
+ 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 "";
+ if (tnorm->className() == Minimum().className()) return "min";
+ if (tnorm->className() == AlgebraicProduct().className()) return "prod";
+ if (tnorm->className() == BoundedDifference().className()) return "bounded_difference";
+ if (tnorm->className() == DrasticProduct().className()) return "drastic_product";
+ if (tnorm->className() == EinsteinProduct().className()) return "einstein_product";
+ if (tnorm->className() == HamacherProduct().className()) return "hamacher_product";
+ if (tnorm->className() == NilpotentMinimum().className()) return "nilpotent_minimum";
+ return tnorm->className();
+ }
+
+ 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() == 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";
+ return snorm->className();
+ }
+
+ std::string FisExporter::toString(const Defuzzifier * defuzzifier) const {
+ if (not defuzzifier) return "";
+ if (defuzzifier->className() == Centroid().className()) return "centroid";
+ if (defuzzifier->className() == Bisector().className()) return "bisector";
+ if (defuzzifier->className() == LargestOfMaximum().className()) return "lom";
+ if (defuzzifier->className() == MeanOfMaximum().className()) return "mom";
+ if (defuzzifier->className() == SmallestOfMaximum().className()) return "som";
+ if (defuzzifier->className() == WeightedAverage().className()) return "wtaver";
+ if (defuzzifier->className() == WeightedSum().className()) return "wtsum";
+ return defuzzifier->className();
+ }
+
+ 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, " ",
+ x->getWidth(), x->getSlope(), x->getCenter()) << "]";
+ return ss.str();
+ }
+
+ if (const Concave * x = dynamic_cast<const Concave*> (term)) {
+ ss << "'concavemf',[" << fl::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()) << "]";
+ return ss.str();
+ }
+
+ if (const Cosine * x = dynamic_cast<const Cosine*> (term)) {
+ ss << "'cosinemf',[" << fl::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()), " ") << "]";
+ return ss.str();
+ }
+
+ if (const Function * x = dynamic_cast<const Function*> (term)) {
+ ss << "'function',[" << x->getFormula() << "]";
+ return ss.str();
+ }
+
+ if (const Gaussian * x = dynamic_cast<const Gaussian*> (term)) {
+ ss << "'gaussmf',[" << fl::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, " ",
+ 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(), " ") << "]";
+ return ss.str();
+ }
+
+
+ if (const PiShape * x = dynamic_cast<const PiShape*> (term)) {
+ ss << "'pimf',[" << fl::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, " ",
+ x->getStart(), x->getEnd()) << "]";
+ return ss.str();
+ }
+
+ if (const Rectangle * x = dynamic_cast<const Rectangle*> (term)) {
+ ss << "'rectmf',[" << fl::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, " ",
+ 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, " ",
+ x->getSlope(), x->getInflection()) << "]";
+ return ss.str();
+ }
+
+ if (const SigmoidProduct * x = dynamic_cast<const SigmoidProduct*> (term)) {
+ ss << "'psigmf',[" << fl::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, " ",
+ x->getStart(), x->getEnd()) << "]";
+ return ss.str();
+ }
+
+ if (const Spike * x = dynamic_cast<const Spike*> (term)) {
+ ss << "'spikemf',[" << fl::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, " ",
+ 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, " ",
+ x->getVertexA(), x->getVertexB(), x->getVertexC()) << "]";
+ return ss.str();
+ }
+
+ if (const ZShape * x = dynamic_cast<const ZShape*> (term)) {
+ ss << "'zmf',[" << fl::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);
+ }
+
+ FisExporter* FisExporter::clone() const {
+ return new FisExporter(*this);
+ }
+
+}
diff --git a/fuzzylite/src/imex/FisImporter.cpp b/fuzzylite/src/imex/FisImporter.cpp
new file mode 100644
index 0000000..741719c
--- /dev/null
+++ b/fuzzylite/src/imex/FisImporter.cpp
@@ -0,0 +1,501 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FisImporter.h"
+
+#include "fl/Headers.h"
+
+#include <sstream>
+#include <iostream>
+#include <cctype>
+
+namespace fl {
+
+ FisImporter::FisImporter() : Importer() {
+ }
+
+ FisImporter::~FisImporter() {
+ }
+
+ std::string FisImporter::name() const {
+ return "FisImporter";
+ }
+
+ Engine* FisImporter::fromString(const std::string& fis) const {
+ FL_unique_ptr<Engine> engine(new Engine);
+
+ std::istringstream fisReader(fis);
+ std::string line;
+ int 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();
+ }
+ line = Op::trim(line);
+ if (line.empty() or line.at(0) == '%' or line.at(0) == '#'
+ or (line.substr(0, 2) == "//")) {
+ continue;
+ }
+
+ line = fl::Op::findReplace(line, "'", "");
+
+ if ("[System]" == line.substr(0, std::string("[System]").size())
+ or "[Input" == line.substr(0, std::string("[Input").size())
+ or "[Output" == line.substr(0, std::string("[Output").size())
+ or "[Rules]" == line.substr(0, std::string("[Rules]").size())) {
+ sections.push_back(line);
+ } else {
+ if (not sections.empty()) {
+ sections.at(sections.size() - 1) += "\n" + line;
+ } else {
+ std::ostringstream ss;
+ ss << "[import error] line " << lineNumber << " <" << line + "> "
+ "does not belong to any section";
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+ }
+ }
+ std::string andMethod, orMethod, impMethod, aggMethod, defuzzMethod;
+ for (std::size_t i = 0; i < sections.size(); ++i) {
+ if ("[System]" == sections.at(i).substr(0, std::string("[System]").size()))
+ importSystem(sections.at(i), engine.get(),
+ andMethod, orMethod, impMethod, aggMethod, defuzzMethod);
+ else if ("[Input" == sections.at(i).substr(0, std::string("[Input").size()))
+ importInput(sections.at(i), engine.get());
+ else if ("[Output" == sections.at(i).substr(0, std::string("[Output").size()))
+ 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 <"
+ + sections.at(i) + "> not recognized", FL_AT);
+ }
+ engine->configure(extractTNorm(andMethod), extractSNorm(orMethod),
+ extractTNorm(impMethod), extractSNorm(aggMethod),
+ extractDefuzzifier(defuzzMethod));
+ return engine.release();
+ }
+
+ void FisImporter::importSystem(const std::string& section, Engine * engine,
+ std::string& andMethod, std::string& orMethod,
+ std::string& impMethod, std::string& aggMethod,
+ std::string& defuzzMethod) const {
+ std::istringstream reader(section);
+ 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::string key = fl::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);
+ if (key == "Name") engine->setName(value);
+ else if (key == "AndMethod") andMethod = value;
+ else if (key == "OrMethod") orMethod = value;
+ else if (key == "ImpMethod") impMethod = value;
+ else if (key == "AggMethod") aggMethod = value;
+ else if (key == "DefuzzMethod") defuzzMethod = value;
+ else if (key == "Type" or key == "Version"
+ 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);
+ }
+ }
+
+ void FisImporter::importInput(const std::string& section, Engine* engine) const {
+ std::istringstream reader(section);
+ std::string line;
+ std::getline(reader, line); //ignore first line [Input#]
+
+ InputVariable* input = new InputVariable;
+ engine->addInputVariable(input);
+
+ while (std::getline(reader, line)) {
+ std::vector<std::string> keyValue = fl::Op::split(line, "=");
+ if (keyValue.size() != 2)
+ throw fl::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));
+
+ if (key == "Name") input->setName(fl::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);
+ input->setMinimum(minmax.first);
+ input->setMaximum(minmax.second);
+ } else if (key.substr(0, 2) == "MF") {
+ input->addTerm(parseTerm(value, engine));
+ } else if (key == "NumMFs") {
+ //ignore
+ } else {
+ throw fl::Exception("[import error] token <" + key + "> not recognized", FL_AT);
+ }
+ }
+ }
+
+ void FisImporter::importOutput(const std::string& section, Engine* engine) const {
+ std::istringstream reader(section);
+ std::string line;
+ std::getline(reader, line); //ignore first line [Output#]
+
+ OutputVariable* output = new OutputVariable;
+ engine->addOutputVariable(output);
+
+
+ while (std::getline(reader, line)) {
+ std::vector<std::string> keyValue = fl::Op::split(line, "=");
+ if (keyValue.size() != 2)
+ throw fl::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));
+
+ if (key == "Name") output->setName(fl::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);
+ 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));
+ } else if (key == "LockPrevious") {
+ output->setLockPreviousOutputValue(fl::Op::isEq(fl::Op::toScalar(value), 1.0));
+ } else if (key == "LockRange") {
+ output->setLockOutputValueInRange(fl::Op::isEq(fl::Op::toScalar(value), 1.0));
+ } else if (key == "NumMFs") {
+ //ignore
+ } else {
+ throw fl::Exception("[import error] token <" + key + "> not recognized", FL_AT);
+ }
+ }
+ }
+
+ void FisImporter::importRules(const std::string& section, Engine* engine) const {
+ std::istringstream reader(section);
+ std::string line;
+ std::getline(reader, line); //ignore first line [Rules]
+
+ RuleBlock* ruleblock = new RuleBlock;
+ engine->addRuleBlock(ruleblock);
+
+ while (std::getline(reader, line)) {
+ std::vector<std::string> inputsAndRest = fl::Op::split(line, ",");
+ if (inputsAndRest.size() != 2)
+ throw fl::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), ":");
+ if (outputsAndRest.size() != 2)
+ throw fl::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::string weightInParenthesis = outputs.at(outputs.size() - 1);
+ outputs.erase(outputs.begin() + outputs.size() - 1);
+ std::string connector = fl::Op::trim(outputsAndRest.at(1));
+
+ if ((int) 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);
+ }
+ if ((int) 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);
+ }
+
+ 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;
+ std::ostringstream ss;
+ ss << engine->getInputVariable(i)->getName() << " "
+ << fl::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;
+ std::ostringstream ss;
+ ss << engine->getOutputVariable(i)->getName() << " "
+ << fl::Rule::isKeyword() << " "
+ << translateProposition(outputCode, engine->getOutputVariable(i));
+ consequent.push_back(ss.str());
+ }
+
+ std::ostringstream ruleText;
+
+ ruleText << fl::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 <"
+ + connector + "> not recognized", FL_AT);
+ }
+ }
+
+ ruleText << " " << fl::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() << " ";
+ }
+ }
+
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < weightInParenthesis.size(); ++i) {
+ if (weightInParenthesis.at(i) == '('
+ or weightInParenthesis.at(i) == ')'
+ or weightInParenthesis.at(i) == ' ') continue;
+ 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);
+ Rule* rule = new Rule(ruleText.str());
+ try {
+ rule->load(engine);
+ } catch (...) {
+ //ignore
+ }
+ ruleblock->addRule(rule);
+ }
+ }
+
+ 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()) {
+ 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);
+ }
+
+ 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 (not isAny) {
+ ss << variable->getTerm(intPart)->getName();
+ }
+ return ss.str();
+ }
+
+ std::string FisImporter::extractTNorm(const std::string & name) const {
+ if (name.empty()) return "";
+ if (name == "min") return Minimum().className();
+ if (name == "prod") return AlgebraicProduct().className();
+ if (name == "bounded_difference") return BoundedDifference().className();
+ if (name == "drastic_product") return DrasticProduct().className();
+ if (name == "einstein_product") return EinsteinProduct().className();
+ if (name == "hamacher_product") return HamacherProduct().className();
+ if (name == "nilpotent_minimum") return NilpotentMinimum().className();
+ return name;
+ }
+
+ std::string FisImporter::extractSNorm(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 == "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();
+ return name;
+ }
+
+ std::string FisImporter::extractDefuzzifier(const std::string & name) const {
+ if (name.empty()) return "";
+ if (name == "centroid") return Centroid().className();
+ if (name == "bisector") return Bisector().className();
+ if (name == "lom") return LargestOfMaximum().className();
+ if (name == "mom") return MeanOfMaximum().className();
+ if (name == "som") return SmallestOfMaximum().className();
+ if (name == "wtaver") return WeightedAverage().className();
+ if (name == "wtsum") return WeightedSum().className();
+ return name;
+ }
+
+ std::pair<scalar, scalar> FisImporter::range(const std::string& range) const {
+ std::vector<std::string> parts = fl::Op::split(range, " ");
+ if (parts.size() != 2)
+ throw fl::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]',"
+ " 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));
+ return result;
+ }
+
+ 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) == ']')) {
+ ss << fis.at(i);
+ }
+ }
+ std::string line = ss.str();
+
+ std::vector<std::string> nameTerm = fl::Op::split(line, ":");
+ if (nameTerm.size() != 2) {
+ throw fl::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), ",");
+ if (termParams.size() != 2) {
+ throw fl::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), " ");
+ for (std::size_t i = 0; i < parameters.size(); ++i) {
+ parameters.at(i) = fl::Op::trim(parameters.at(i));
+ }
+ return createInstance(
+ fl::Op::trim(termParams.at(0)),
+ fl::Op::trim(nameTerm.at(0)),
+ parameters, engine);
+ }
+
+ 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["concavemf"] = Concave().className();
+ mapping["constant"] = Constant().className();
+ mapping["cosinemf"] = Cosine().className();
+ mapping["function"] = Function().className();
+ mapping["gbellmf"] = Bell().className();
+ mapping["gaussmf"] = Gaussian().className();
+ mapping["gauss2mf"] = GaussianProduct().className();
+ mapping["linear"] = Linear().className();
+ mapping["pimf"] = PiShape().className();
+ mapping["rampmf"] = Ramp().className();
+ mapping["rectmf"] = Rectangle().className();
+ mapping["smf"] = SShape().className();
+ mapping["sigmf"] = Sigmoid().className();
+ mapping["dsigmf"] = SigmoidDifference().className();
+ mapping["psigmf"] = SigmoidProduct().className();
+ mapping["spikemf"] = Spike().className();
+ mapping["trapmf"] = Trapezoid().className();
+ mapping["trimf"] = Triangle().className();
+ mapping["zmf"] = ZShape().className();
+
+ std::vector<std::string> sortedParams = params;
+
+ if (mClass == "gbellmf" and params.size() >= 3) {
+ sortedParams.at(0) = params.at(2);
+ sortedParams.at(1) = params.at(0);
+ sortedParams.at(2) = params.at(1);
+ } else if (mClass == "gaussmf" and params.size() >= 2) {
+ sortedParams.at(0) = params.at(1);
+ sortedParams.at(1) = params.at(0);
+ } else if (mClass == "gauss2mf" and params.size() >= 4) {
+ sortedParams.at(0) = params.at(1);
+ sortedParams.at(1) = params.at(0);
+ sortedParams.at(2) = params.at(3);
+ sortedParams.at(3) = params.at(2);
+ } else if (mClass == "sigmf" and params.size() >= 2) {
+ sortedParams.at(0) = params.at(1);
+ sortedParams.at(1) = params.at(0);
+ } else if (mClass == "dsigmf" and params.size() >= 4) {
+ sortedParams.at(0) = params.at(1);
+ sortedParams.at(1) = params.at(0);
+ sortedParams.at(2) = params.at(2);
+ sortedParams.at(3) = params.at(3);
+ } else if (mClass == "psigmf" and params.size() >= 4) {
+ sortedParams.at(0) = params.at(1);
+ sortedParams.at(1) = params.at(0);
+ sortedParams.at(2) = params.at(2);
+ sortedParams.at(3) = params.at(3);
+ }
+
+ std::string flClass;
+ std::map<std::string, std::string>::const_iterator it = mapping.find(mClass);
+ if (it != mapping.end()) flClass = it->second;
+ else flClass = mClass;
+
+ FL_unique_ptr<Term> term;
+ term.reset(FactoryManager::instance()->term()->constructObject(flClass));
+ Term::updateReference(term.get(), engine);
+ term->setName(Op::validName(name));
+ std::string separator;
+ if (not dynamic_cast<Function*> (term.get())) {
+ separator = " ";
+ }
+ term->configure(Op::join(sortedParams, separator));
+ return term.release();
+ }
+
+ FisImporter* FisImporter::clone() const {
+ return new FisImporter(*this);
+ }
+
+}
diff --git a/fuzzylite/src/imex/FldExporter.cpp b/fuzzylite/src/imex/FldExporter.cpp
new file mode 100644
index 0000000..9064250
--- /dev/null
+++ b/fuzzylite/src/imex/FldExporter.cpp
@@ -0,0 +1,246 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FldExporter.h"
+
+#include "fl/Engine.h"
+#include "fl/Operation.h"
+#include "fl/variable/Variable.h"
+#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) {
+
+ }
+
+ FldExporter::~FldExporter() {
+ }
+
+ std::string FldExporter::name() const {
+ return "FldExporter";
+ }
+
+ void FldExporter::setSeparator(const std::string& separator) {
+ this->_separator = separator;
+ }
+
+ std::string FldExporter::getSeparator() const {
+ return this->_separator;
+ }
+
+ void FldExporter::setExportHeader(bool exportHeaders) {
+ this->_exportHeaders = exportHeaders;
+ }
+
+ bool FldExporter::exportsHeader() const {
+ return this->_exportHeaders;
+ }
+
+ void FldExporter::setExportInputValues(bool exportInputValues) {
+ this->_exportInputValues = exportInputValues;
+ }
+
+ bool FldExporter::exportsInputValues() const {
+ return this->_exportInputValues;
+ }
+
+ void FldExporter::setExportOutputValues(bool exportOutputValues) {
+ this->_exportOutputValues = exportOutputValues;
+ }
+
+ bool FldExporter::exportsOutputValues() const {
+ return this->_exportOutputValues;
+ }
+
+ std::string FldExporter::header(const Engine* engine) const {
+ std::vector<std::string> result;
+ if (_exportInputValues) {
+ for (int i = 0; i < engine->numberOfInputVariables(); ++i) {
+ InputVariable* inputVariable = engine->getInputVariable(i);
+ result.push_back("@InputVariable: " + inputVariable->getName() + ";");
+ }
+ }
+ if (_exportOutputValues) {
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* outputVariable = engine->getOutputVariable(i);
+ result.push_back("@OutputVariable: " + outputVariable->getName() + ";");
+ }
+ }
+ return "#@Engine: " + engine->getName() + ";\n#" + Op::join(result, _separator);
+ }
+
+ std::string FldExporter::toString(const Engine* engine) const {
+ return toString(const_cast<Engine*> (engine), 1024);
+ }
+
+ std::string FldExporter::toString(Engine* engine, int maximumNumberOfResults) const {
+ std::ostringstream result;
+ write(engine, result, maximumNumberOfResults);
+ return result.str();
+ }
+
+ 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, const std::string& inputData) const {
+ std::ostringstream writer;
+ if (_exportHeaders) writer << header(engine) << "\n";
+ std::istringstream reader(inputData);
+ std::string line;
+ while (std::getline(reader, 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 = parse(line);
+ write(engine, writer, inputValues);
+ writer.flush();
+ }
+ return writer.str();
+ }
+
+ void FldExporter::toFile(const std::string& path, Engine* engine, const std::string& inputData) 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);
+ }
+ if (_exportHeaders) writer << header(engine) << "\n";
+ std::istringstream reader(inputData);
+ std::string line;
+ while (std::getline(reader, 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 = parse(line);
+ write(engine, writer, inputValues);
+ writer.flush();
+ }
+ writer.close();
+ }
+
+ std::vector<scalar> FldExporter::parse(const std::string& x) 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));
+ }
+ return inputValues;
+ }
+
+ void FldExporter::write(Engine* engine, std::ostream& writer, int maximum) const {
+ if (_exportHeaders) writer << header(engine) << "\n";
+
+ int resolution = -1 + (int) std::max(1.0, std::pow(
+ maximum, 1.0 / engine->numberOfInputVariables()));
+ std::vector<int> sampleValues, minSampleValues, maxSampleValues;
+ for (int i = 0; i < engine->numberOfInputVariables(); ++i) {
+ sampleValues.push_back(0);
+ minSampleValues.push_back(0);
+ maxSampleValues.push_back(resolution);
+ }
+
+ engine->restart();
+
+ bool overflow = false;
+ std::vector<scalar> inputValues(engine->numberOfInputVariables());
+ while (not overflow) {
+ for (int 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);
+ }
+ write(engine, writer, inputValues);
+ overflow = 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;
+ while (std::getline(reader, line)) {
+ ++lineNumber;
+ std::vector<scalar> inputValues = parse(Op::trim(line));
+ try {
+ write(engine, writer, inputValues);
+ } catch (fl::Exception& ex) {
+ ex.append(" writing line <" + Op::str(lineNumber) + ">");
+ throw;
+ }
+ }
+ }
+
+ void FldExporter::write(Engine* engine, std::ostream& writer, const std::vector<scalar>& inputValues) const {
+ if (inputValues.empty()) {
+ writer << "\n";
+ return;
+ }
+ if (int(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);
+ }
+
+ std::vector<std::string> values;
+ for (int 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));
+ }
+
+ engine->process();
+
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* outputVariable = engine->getOutputVariable(i);
+ outputVariable->defuzzify();
+ if (_exportOutputValues)
+ values.push_back(Op::str(outputVariable->getOutputValue()));
+ }
+
+ writer << Op::join(values, _separator) << "\n";
+ }
+
+ FldExporter* FldExporter::clone() const {
+ return new FldExporter(*this);
+ }
+
+}
diff --git a/fuzzylite/src/imex/FllExporter.cpp b/fuzzylite/src/imex/FllExporter.cpp
new file mode 100644
index 0000000..0ffeb2f
--- /dev/null
+++ b/fuzzylite/src/imex/FllExporter.cpp
@@ -0,0 +1,189 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FllExporter.h"
+
+#include "fl/Headers.h"
+
+namespace fl {
+
+ FllExporter::FllExporter(const std::string& indent, const std::string& separator)
+ : Exporter(), _indent(indent), _separator(separator) {
+ }
+
+ FllExporter::~FllExporter() {
+ }
+
+ std::string FllExporter::name() const {
+ return "FllExporter";
+ }
+
+ void FllExporter::setIndent(const std::string& indent) {
+ this->_indent = indent;
+ }
+
+ std::string FllExporter::getIndent() const {
+ return this->_indent;
+ }
+
+ void FllExporter::setSeparator(const std::string& separator) {
+ this->_separator = separator;
+ }
+
+ std::string FllExporter::getSeparator() const {
+ return this->_separator;
+ }
+
+ 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()));
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const std::vector<Variable*>& variables) const {
+ std::vector<std::string> result;
+ for (std::size_t i = 0; i < variables.size(); ++i) {
+ result.push_back(toString(variables.at(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const std::vector<InputVariable*>& variables) const {
+ std::vector<std::string> result;
+ for (std::size_t i = 0; i < variables.size(); ++i) {
+ result.push_back(toString(variables.at(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const std::vector<OutputVariable*>& variables) const {
+ std::vector<std::string> result;
+ for (std::size_t i = 0; i < variables.size(); ++i) {
+ result.push_back(toString(variables.at(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const std::vector<RuleBlock*>& ruleBlocks) const {
+ std::vector<std::string> result;
+ for (std::size_t i = 0; i < ruleBlocks.size(); ++i) {
+ result.push_back(toString(ruleBlocks.at(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const Variable* variable) const {
+ std::vector<std::string> result;
+ result.push_back("Variable: " + Op::validName(variable->getName()));
+ 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 + toString(variable->getTerm(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const InputVariable* inputVariable) const {
+ std::vector<std::string> result;
+ result.push_back("InputVariable: " + Op::validName(inputVariable->getName()));
+ 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 + toString(inputVariable->getTerm(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const OutputVariable* outputVariable) const {
+ std::vector<std::string> result;
+ result.push_back("OutputVariable: " + Op::validName(outputVariable->getName()));
+ 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 + "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) {
+ result.push_back(_indent + toString(outputVariable->getTerm(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const RuleBlock* ruleBlock) const {
+ std::vector<std::string> result;
+ result.push_back("RuleBlock: " + ruleBlock->getName());
+ 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 + "activation: " + toString(ruleBlock->getActivation()));
+ for (int i = 0; i < ruleBlock->numberOfRules(); ++i) {
+ result.push_back(_indent + toString(ruleBlock->getRule(i)));
+ }
+ return Op::join(result, _separator);
+ }
+
+ std::string FllExporter::toString(const Rule* rule) const {
+ return "rule: " + rule->getText();
+ }
+
+ std::string FllExporter::toString(const Term* term) const {
+ return "term: " + Op::validName(term->getName()) + " " + term->className()
+ + " " + term->parameters();
+ }
+
+ std::string FllExporter::toString(const Norm* norm) const {
+ if (norm) return norm->className();
+ return "none";
+ }
+
+ 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());
+
+ } else if (const WeightedDefuzzifier * weightedDefuzzifier =
+ dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) {
+ return weightedDefuzzifier->className() + " " + weightedDefuzzifier->getTypeName();
+ }
+ return defuzzifier->className();
+ }
+
+ FllExporter* FllExporter::clone() const {
+ return new FllExporter(*this);
+ }
+
+}
diff --git a/fuzzylite/src/imex/FllImporter.cpp b/fuzzylite/src/imex/FllImporter.cpp
new file mode 100644
index 0000000..2443d10
--- /dev/null
+++ b/fuzzylite/src/imex/FllImporter.cpp
@@ -0,0 +1,317 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/FllImporter.h"
+
+#include "fl/Headers.h"
+
+#include <queue>
+
+namespace fl {
+
+ FllImporter::FllImporter(const std::string& separator) : Importer(),
+ _separator(separator) {
+ }
+
+ FllImporter::~FllImporter() {
+
+ }
+
+ std::string FllImporter::name() const {
+ return "FllImporter";
+ }
+
+ void FllImporter::setSeparator(const std::string& separator) {
+ this->_separator = separator;
+ }
+
+ std::string FllImporter::getSeparator() const {
+ return this->_separator;
+ }
+
+ Engine* FllImporter::fromString(const std::string& fll) const {
+ FL_unique_ptr<Engine> engine(new Engine);
+
+ std::string tag;
+ std::ostringstream block;
+ std::istringstream fclReader(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;
+ }
+ 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);
+ }
+ 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
+ block.clear(); //clear error flags
+ processPending = false;
+ tag = key;
+ }
+ block << key << ":" << value << "\n";
+ }
+ process(tag, block.str(), engine.get());
+ return engine.release();
+ }
+
+ void FllImporter::process(const std::string& tag, const std::string& block, Engine* engine) const {
+ if (tag.empty()) return;
+ if ("InputVariable" == tag) {
+ processInputVariable(block, engine);
+ } else if ("OutputVariable" == tag) {
+ processOutputVariable(block, engine);
+ } else if ("RuleBlock" == tag) {
+ processRuleBlock(block, engine);
+ } else {
+ throw fl::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);
+ 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 ("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 ("term" == keyValue.first) {
+ inputVariable->addTerm(parseTerm(keyValue.second, engine));
+ } else {
+ throw fl::Exception("[import error] key <" + keyValue.first + "> not "
+ "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT);
+ }
+ }
+ }
+
+ void FllImporter::processOutputVariable(const std::string& block, Engine* engine) const {
+ std::istringstream reader(block);
+ std::string line;
+ OutputVariable* outputVariable = new OutputVariable;
+ engine->addOutputVariable(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 ("enabled" == keyValue.first) {
+ outputVariable->setEnabled(parseBoolean(keyValue.second));
+ } else if ("range" == keyValue.first) {
+ std::pair<scalar, scalar> range = parseRange(keyValue.second);
+ outputVariable->setRange(range.first, range.second);
+ } 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));
+ } else if ("lock-range" == keyValue.first) {
+ outputVariable->setLockOutputValueInRange(parseBoolean(keyValue.second));
+ } else if ("defuzzifier" == keyValue.first) {
+ outputVariable->setDefuzzifier(parseDefuzzifier(keyValue.second));
+ } else if ("accumulation" == keyValue.first) {
+ outputVariable->fuzzyOutput()->setAccumulation(parseSNorm(keyValue.second));
+ } else if ("term" == keyValue.first) {
+ outputVariable->addTerm(parseTerm(keyValue.second, engine));
+ } else {
+ throw fl::Exception("[import error] key <" + keyValue.first + "> not "
+ "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT);
+ }
+ }
+ }
+
+ void FllImporter::processRuleBlock(const std::string& block, Engine* engine) const {
+ std::istringstream reader(block);
+ std::string line;
+ RuleBlock* ruleBlock = new RuleBlock;
+ engine->addRuleBlock(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 ("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 ("activation" == keyValue.first) {
+ ruleBlock->setActivation(parseTNorm(keyValue.second));
+ } else if ("rule" == keyValue.first) {
+ Rule* rule = new Rule;
+ rule->setText(keyValue.second);
+ try {
+ rule->load(engine);
+ } catch (std::exception& ex) {
+ FL_LOG(ex.what());
+ }
+ ruleBlock->addRule(rule);
+ } else {
+ throw fl::Exception("[import error] key <" + keyValue.first + "> not "
+ "recognized in pair <" + keyValue.first + ":" + keyValue.second + ">", FL_AT);
+ }
+ }
+ }
+
+ Term* FllImporter::parseTerm(const std::string& text, Engine* engine) const {
+ std::vector<std::string> tokens = Op::split(text, " ");
+
+ //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>, "
+ "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->setName(Op::validName(tokens.at(0)));
+ std::ostringstream parameters;
+ for (std::size_t i = 2; i < tokens.size(); ++i) {
+ parameters << tokens.at(i);
+ if (i + 1 < tokens.size()) parameters << " ";
+ }
+ term->configure(parameters.str());
+ return term.release();
+ }
+
+ TNorm* FllImporter::parseTNorm(const std::string& name) const {
+ if (name == "none") return FactoryManager::instance()->tnorm()->constructObject("");
+ return FactoryManager::instance()->tnorm()->constructObject(name);
+ }
+
+ SNorm* FllImporter::parseSNorm(const std::string& name) const {
+ if (name == "none") return FactoryManager::instance()->snorm()->constructObject("");
+ return FactoryManager::instance()->snorm()->constructObject(name);
+ }
+
+ Defuzzifier* FllImporter::parseDefuzzifier(const std::string& text) const {
+ std::vector<std::string> parameters = Op::split(text, " ");
+ std::string name = parameters.at(0);
+ if (name == "none") return FactoryManager::instance()->defuzzifier()->constructObject("");
+ Defuzzifier* defuzzifier = FactoryManager::instance()->defuzzifier()->constructObject(name);
+ if (parameters.size() > 1) {
+ std::string parameter(parameters.at(1));
+ if (IntegralDefuzzifier * integralDefuzzifier = dynamic_cast<IntegralDefuzzifier*> (defuzzifier)) {
+ integralDefuzzifier->setResolution((int) Op::toScalar(parameter));
+ } else if (WeightedDefuzzifier * weightedDefuzzifier = dynamic_cast<WeightedDefuzzifier*> (defuzzifier)) {
+ WeightedDefuzzifier::Type type = WeightedDefuzzifier::Automatic;
+ 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);
+ weightedDefuzzifier->setType(type);
+ }
+ }
+ return defuzzifier;
+ }
+
+ std::pair<scalar, scalar> FllImporter::parseRange(const std::string& text) const {
+ std::pair<std::string, std::string> range = parseKeyValue(text, ' ');
+ return std::pair<scalar, scalar>(Op::toScalar(range.first), Op::toScalar(range.second));
+ }
+
+ 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>, "
+ "but found <" + boolean + ">", FL_AT);
+ }
+
+ std::pair<std::string, std::string> FllImporter::parseKeyValue(const std::string& text,
+ char separator) const {
+ std::size_t half = text.find_first_of(separator);
+ if (half == std::string::npos) {
+ std::ostringstream ex;
+ ex << "[syntax error] expected pair in the form "
+ "<key" << separator << "value>, but found <" << text << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ std::pair<std::string, std::string> result;
+ result.first = text.substr(0, half);
+ result.second = text.substr(half + 1);
+ 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
new file mode 100644
index 0000000..faef71d
--- /dev/null
+++ b/fuzzylite/src/imex/Importer.cpp
@@ -0,0 +1,54 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/Importer.h"
+#include "fl/Exception.h"
+
+#include <fstream>
+#include <ostream>
+
+namespace fl {
+
+ 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);
+ }
+ std::ostringstream textEngine;
+ std::string line;
+ while (std::getline(reader, line)) {
+ textEngine << line << std::endl;
+ }
+ reader.close();
+ return fromString(textEngine.str());
+ }
+
+}
diff --git a/fuzzylite/src/imex/JavaExporter.cpp b/fuzzylite/src/imex/JavaExporter.cpp
new file mode 100644
index 0000000..a4948f8
--- /dev/null
+++ b/fuzzylite/src/imex/JavaExporter.cpp
@@ -0,0 +1,228 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/imex/JavaExporter.h"
+
+#include "fl/Headers.h"
+
+#include <algorithm>
+namespace fl {
+
+ JavaExporter::JavaExporter() : Exporter() {
+ }
+
+ JavaExporter::~JavaExporter() {
+
+ }
+
+ std::string JavaExporter::name() const {
+ return "JavaExporter";
+ }
+
+ std::string JavaExporter::toString(const Engine* engine) const {
+ std::ostringstream ss;
+ ss << "Engine engine = new Engine();\n";
+ ss << "engine.setName(\"" << engine->getName() << "\");\n";
+ ss << "\n";
+
+ for (int i = 0; i < engine->numberOfInputVariables(); ++i) {
+ ss << toString(engine->getInputVariable(i), engine) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfOutputVariables(); ++i) {
+ ss << toString(engine->getOutputVariable(i), engine) << "\n";
+ }
+
+ for (int i = 0; i < engine->numberOfRuleBlocks(); ++i) {
+ ss << toString(engine->getRuleBlock(i), engine) << "\n";
+ }
+
+ return ss.str();
+ }
+
+ 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);
+ }
+ ss << "InputVariable " << name << " = new InputVariable();\n";
+ ss << name << ".setEnabled(" << (inputVariable->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << ".setName(\"" << inputVariable->getName() << "\");\n";
+ ss << name << ".setRange("
+ << toString(inputVariable->getMinimum()) << ", "
+ << toString(inputVariable->getMaximum()) << ");\n";
+
+ for (int i = 0; i < inputVariable->numberOfTerms(); ++i) {
+ ss << name << ".addTerm(" <<
+ toString(inputVariable->getTerm(i)) << ");\n";
+ }
+ ss << "engine.addInputVariable(" << name << ");\n";
+ return ss.str();
+ }
+
+ 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);
+ }
+ ss << "OutputVariable " << name << " = new OutputVariable();\n";
+ ss << name << ".setEnabled(" << (outputVariable->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << ".setName(\"" << outputVariable->getName() << "\");\n";
+ ss << name << ".setRange("
+ << toString(outputVariable->getMinimum()) << ", "
+ << toString(outputVariable->getMaximum()) << ");\n";
+ ss << name << ".fuzzyOutput().setAccumulation(" <<
+ toString(outputVariable->fuzzyOutput()->getAccumulation()) << ");\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 << ".addTerm(" <<
+ toString(outputVariable->getTerm(i)) << ");\n";
+ }
+ ss << "engine.addOutputVariable(" << name << ");\n";
+ return ss.str();
+ }
+
+ 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);
+ }
+ ss << "RuleBlock " << name << " = new RuleBlock();\n";
+ ss << name << ".setEnabled(" << (ruleBlock->isEnabled() ? "true" : "false") << ");\n";
+ ss << name << ".setName(\"" << ruleBlock->getName() << "\");\n";
+ ss << name << ".setConjunction("
+ << toString(ruleBlock->getConjunction()) << ");\n";
+ ss << name << ".setDisjunction("
+ << toString(ruleBlock->getDisjunction()) << ");\n";
+ ss << name << ".setActivation("
+ << toString(ruleBlock->getActivation()) << ");\n";
+ for (int i = 0; i < ruleBlock->numberOfRules(); ++i) {
+ Rule* rule = ruleBlock->getRule(i);
+ ss << name << ".addRule(Rule.parse(\"" << rule->getText() << "\", engine));\n";
+ }
+ ss << "engine.addRuleBlock(" << name << ");\n";
+ return ss.str();
+ }
+
+ std::string JavaExporter::toString(const Term* term) const {
+ if (not term) {
+ return "null";
+ }
+
+ if (const Discrete * discrete = dynamic_cast<const Discrete*> (term)) {
+ std::ostringstream ss;
+ std::vector<scalar> xy;
+ ss << term->className() << ".create(\"" << term->getName() << "\", "
+ << Op::join(Discrete::toVector(discrete->xy()), ", ") << ")";
+ return ss.str();
+ }
+
+ if (const Function * function = dynamic_cast<const Function*> (term)) {
+ std::ostringstream ss;
+ ss << term->className() << ".create(\"" << term->getName() << "\", "
+ << "\"" << function->getFormula() << "\", engine)";
+ return ss.str();
+ }
+
+ if (const Linear * linear = dynamic_cast<const Linear*> (term)) {
+ std::ostringstream ss;
+ ss << term->className() << ".create(\"" << term->getName() << "\", "
+ << "engine, " << Op::join(linear->coefficients(), ", ") << ")";
+ return ss.str();
+ }
+
+ std::ostringstream ss;
+ ss << "new " << term->className() << "(\"" << term->getName() << "\", "
+ << Op::findReplace(term->parameters(), " ", ", ") << ")";
+ return ss.str();
+ }
+
+ std::string JavaExporter::toString(const Defuzzifier* defuzzifier) const {
+ if (not defuzzifier) return "null";
+
+ if (const IntegralDefuzzifier * integralDefuzzifier =
+ dynamic_cast<const IntegralDefuzzifier*> (defuzzifier)) {
+ return "new " + integralDefuzzifier->className() + "("
+ + fl::Op::str(integralDefuzzifier->getResolution()) + ")";
+ }
+ if (const WeightedDefuzzifier * weightedDefuzzifier =
+ dynamic_cast<const WeightedDefuzzifier*> (defuzzifier)) {
+ return "new " + weightedDefuzzifier->className() +
+ "(\"" + weightedDefuzzifier->getTypeName() + "\")";
+ }
+ 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 {
+ 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(scalar value) const {
+ if (Op::isNaN(value)) {
+ return "Double.NaN";
+ } else if (Op::isInf(value)) {
+ return (value > 0
+ ? "Double.POSITIVE_INFINITY"
+ : "Double.NEGATIVE_INFINITY");
+ }
+ return Op::str(value);
+ }
+
+ JavaExporter* JavaExporter::clone() const {
+ return new JavaExporter(*this);
+ }
+
+
+}
+
diff --git a/fuzzylite/src/m/compare.m b/fuzzylite/src/m/compare.m
new file mode 100755
index 0000000..56da68d
--- /dev/null
+++ b/fuzzylite/src/m/compare.m
@@ -0,0 +1,60 @@
+%{/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite (R) is a registered trademark of FuzzyLite Limited.
+ */
+}%
+
+function [engine] = compare(fisFile, fldFile, delimiter, hasMetadata)
+if (nargin < 3)
+ delimiter = ' ';
+end
+if (nargin < 4)
+ hasMetadata = true;
+end
+
+engine = readfis(fisFile);
+flMatrix = dlmread(fldFile, delimiter, hasMetadata ~ = 0, 0);
+
+if (length(engine.input) + length(engine.output) ~ = size(flMatrix, 2))
+ error('fuzzylite:compare.m', 'Number of inputs and outputs in engine differ from FLD matrix');
+end
+
+if (isempty(engine.andMethod))
+ engine.andMethod = 'min';
+end
+if (isempty(engine.orMethod))
+ engine.orMethod = 'max';
+end
+
+engine.inputValues = flMatrix(1 : end, 1 : length(engine.input));
+engine.outputValues = evalfis(engine.inputValues, engine);
+engine.flOutputValues = flMatrix(1 : end, (length(engine.input) + 1) : (length(engine.input) + length(engine.output)));
+engine.outputDiff = engine.outputValues - engine.flOutputValues;
+engine.fld = [engine.inputValues engine.outputValues engine.flOutputValues engine.outputDiff];
+engine.nanfreeDiff = engine.outputDiff;
+engine.nanfreeDiff(find(isnan(engine.nanfreeDiff))) = 0;
+engine.mse = nansum(engine.outputDiff.^2) / size(engine.outputDiff, 1);
+engine.quantiles = prctile(engine.nanfreeDiff, 0 : 25 : 100);
+
+end
+
+
+
diff --git a/fuzzylite/src/m/compare_examples.m b/fuzzylite/src/m/compare_examples.m
new file mode 100755
index 0000000..1bf6818
--- /dev/null
+++ b/fuzzylite/src/m/compare_examples.m
@@ -0,0 +1,45 @@
+%{/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite (R) is a registered trademark of FuzzyLite Limited.
+ */
+}%
+
+
+function [ engines ] = compare_examples(path, delimiter, hasMetadata)
+if (nargin < 2)
+ delimiter = ' ';
+end
+if (nargin < 3)
+ hasMetadata = true;
+end
+
+examples = {'\mamdani\SimpleDimmer', '\mamdani\matlab\mam21', '\mamdani\matlab\mam22', '\mamdani\matlab\shower', '\mamdani\matlab\tank', '\mamdani\matlab\tank2', '\mamdani\matlab\tipper', '\mamdani\matlab\tipper1', '\mamdani\octave\mamdani_tip_calculator', '\takagi-sugeno\SimpleDimmer', '\takagi-sugeno\matlab\fpeaks', '\takagi-sugeno\matlab\invkine1', '\takagi-sugeno\matlab\invkine2', '\takagi-sugeno\matlab\juggler', '\takagi-sugeno\matlab\membrn1', '\takagi-sugeno\matlab\membrn2', '\takagi-sugeno\matlab\slbb', '\takagi-sugeno\matlab\slcp', '\takagi-sugeno\matlab\slcp1', '\takagi-sugeno\matlab\slcpp1', '\takagi-sugeno\matlab\sltbu_fl', '\takagi-sugeno\matlab\sugeno1', '\takagi-sugeno\matlab\tanksg', '\takagi-sugeno\matlab\tippersg', '\takagi-sugeno\octave\cubic_approximator', '\takagi-sugeno\octave\heart_disease_risk', '\takagi-sugeno\octave\linear_tip_calculator'};
+pending = {'\mamdani\octave\investment_portfolio', '\takagi-sugeno\approximation', '\takagi-sugeno\octave\sugeno_tip_calculator', '\tsukamoto\tsukamoto'};
+engines = [];
+for i = 1 : length(examples)
+ fisFile = strcat(path, examples{i}, '.fis')
+ fldFile = strcat(path, examples{i}, '.fld');
+ engines = [engines compare(fisFile, fldFile, delimiter, hasMetadata)];
+ disp(strcat('Five number summary (', num2str(i), '): ', fisFile));
+ engines(i).quantiles
+end
+end
+
diff --git a/fuzzylite/src/main.cpp b/fuzzylite/src/main.cpp
new file mode 100644
index 0000000..6d8d701
--- /dev/null
+++ b/fuzzylite/src/main.cpp
@@ -0,0 +1,71 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/Headers.h"
+
+#include <cstdlib>
+#include <fstream>
+#include <iomanip>
+#include <signal.h>
+#include <typeinfo>
+
+using namespace fl;
+
+/*
+#ifdef FL_WINDOWS
+#include <windows.h>
+BOOL WINAPI flSignalHandler(DWORD dwCtrlType)
+{
+ FL_LOG("Signal: " << dwCtrlType);
+ if (CTRL_C_EVENT == dwCtrlType){
+ }
+ //return FALSE;
+ return TRUE;
+}
+#endif
+ */
+
+int main(int argc, char** argv) {
+ (void) argc;
+ (void) argv;
+ std::set_terminate(fl::Exception::terminate);
+ std::set_unexpected(fl::Exception::terminate);
+ signal(SIGSEGV, fl::Exception::signalHandler);
+ signal(SIGABRT, fl::Exception::signalHandler);
+ signal(SIGILL, fl::Exception::signalHandler);
+ signal(SIGSEGV, fl::Exception::signalHandler);
+ signal(SIGFPE, fl::Exception::signalHandler);
+#ifdef FL_UNIX
+ signal(SIGBUS, fl::Exception::signalHandler);
+ signal(SIGPIPE, fl::Exception::signalHandler);
+#endif
+#ifdef FL_WINDOWS
+ //SetConsoleCtrlHandler(flSignalHandler, TRUE);
+#endif
+ fuzzylite::setDebug(false);
+ return Console::main(argc, argv);
+}
+
+
+
diff --git a/fuzzylite/src/norm/s/AlgebraicSum.cpp b/fuzzylite/src/norm/s/AlgebraicSum.cpp
new file mode 100644
index 0000000..e96c1a7
--- /dev/null
+++ b/fuzzylite/src/norm/s/AlgebraicSum.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/AlgebraicSum.h"
+
+namespace fl {
+
+ std::string AlgebraicSum::className() const {
+ return "AlgebraicSum";
+ }
+
+ scalar AlgebraicSum::compute(scalar a, scalar b) const {
+ return a + b - (a * b);
+ }
+
+ AlgebraicSum* AlgebraicSum::clone() const {
+ return new AlgebraicSum(*this);
+ }
+
+ SNorm* AlgebraicSum::constructor() {
+ return new AlgebraicSum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/BoundedSum.cpp b/fuzzylite/src/norm/s/BoundedSum.cpp
new file mode 100644
index 0000000..9c050c8
--- /dev/null
+++ b/fuzzylite/src/norm/s/BoundedSum.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/BoundedSum.h"
+
+namespace fl {
+
+ std::string BoundedSum::className() const {
+ return "BoundedSum";
+ }
+
+ scalar BoundedSum::compute(scalar a, scalar b) const {
+ return Op::min(scalar(1.0), a + b);
+ }
+
+ BoundedSum* BoundedSum::clone() const {
+ return new BoundedSum(*this);
+ }
+
+ SNorm* BoundedSum::constructor() {
+ return new BoundedSum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/DrasticSum.cpp b/fuzzylite/src/norm/s/DrasticSum.cpp
new file mode 100644
index 0000000..c0629db
--- /dev/null
+++ b/fuzzylite/src/norm/s/DrasticSum.cpp
@@ -0,0 +1,48 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/DrasticSum.h"
+
+namespace fl {
+
+ std::string DrasticSum::className() const {
+ return "DrasticSum";
+ }
+
+ scalar DrasticSum::compute(scalar a, scalar b) const {
+ if (Op::isEq(Op::min(a, b), 0.0)) {
+ return Op::max(a, b);
+ }
+ return 1.0;
+ }
+
+ DrasticSum* DrasticSum::clone() const {
+ return new DrasticSum(*this);
+ }
+
+ SNorm* DrasticSum::constructor() {
+ return new DrasticSum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/EinsteinSum.cpp b/fuzzylite/src/norm/s/EinsteinSum.cpp
new file mode 100644
index 0000000..ec0fbd8
--- /dev/null
+++ b/fuzzylite/src/norm/s/EinsteinSum.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/EinsteinSum.h"
+
+namespace fl {
+
+ std::string EinsteinSum::className() const {
+ return "EinsteinSum";
+ }
+
+ scalar EinsteinSum::compute(scalar a, scalar b) const {
+ return (a + b) / (1.0 + a * b);
+ }
+
+ EinsteinSum* EinsteinSum::clone() const {
+ return new EinsteinSum(*this);
+ }
+
+ SNorm* EinsteinSum::constructor() {
+ return new EinsteinSum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/HamacherSum.cpp b/fuzzylite/src/norm/s/HamacherSum.cpp
new file mode 100644
index 0000000..f9c0994
--- /dev/null
+++ b/fuzzylite/src/norm/s/HamacherSum.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/HamacherSum.h"
+
+namespace fl {
+
+ std::string HamacherSum::className() const {
+ return "HamacherSum";
+ }
+
+ scalar HamacherSum::compute(scalar a, scalar b) const {
+ return (a + b - 2.0 * a * b) / (1.0 - a * b);
+ }
+
+ HamacherSum* HamacherSum::clone() const {
+ return new HamacherSum(*this);
+ }
+
+ SNorm* HamacherSum::constructor() {
+ return new HamacherSum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/Maximum.cpp b/fuzzylite/src/norm/s/Maximum.cpp
new file mode 100644
index 0000000..ed8a839
--- /dev/null
+++ b/fuzzylite/src/norm/s/Maximum.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/Maximum.h"
+
+namespace fl {
+
+ std::string Maximum::className() const {
+ return "Maximum";
+ }
+
+ scalar Maximum::compute(scalar a, scalar b) const {
+ return Op::max(a, b);
+ }
+
+ Maximum* Maximum::clone() const {
+ return new Maximum(*this);
+ }
+
+ SNorm* Maximum::constructor() {
+ return new Maximum;
+ }
+
+}
diff --git a/fuzzylite/src/norm/s/NilpotentMaximum.cpp b/fuzzylite/src/norm/s/NilpotentMaximum.cpp
new file mode 100644
index 0000000..0a09136
--- /dev/null
+++ b/fuzzylite/src/norm/s/NilpotentMaximum.cpp
@@ -0,0 +1,50 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/NilpotentMaximum.h"
+
+namespace fl {
+
+ std::string NilpotentMaximum::className() const {
+ return "NilpotentMaximum";
+ }
+
+ scalar NilpotentMaximum::compute(scalar a, scalar b) const {
+ if (Op::isLt(a + b, 1.0)) {
+ return std::max(a, b);
+ }
+ return 1.0;
+ }
+
+ NilpotentMaximum* NilpotentMaximum::clone() const {
+ return new NilpotentMaximum(*this);
+ }
+
+ SNorm* NilpotentMaximum::constructor() {
+ return new NilpotentMaximum;
+ }
+
+
+}
+
diff --git a/fuzzylite/src/norm/s/NormalizedSum.cpp b/fuzzylite/src/norm/s/NormalizedSum.cpp
new file mode 100644
index 0000000..94ad5ea
--- /dev/null
+++ b/fuzzylite/src/norm/s/NormalizedSum.cpp
@@ -0,0 +1,46 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/s/NormalizedSum.h"
+
+namespace fl {
+
+ std::string NormalizedSum::className() const {
+ return "NormalizedSum";
+ }
+
+ scalar NormalizedSum::compute(scalar a, scalar b) const {
+ return a + b / Op::max(scalar(1.0), Op::max(a, b));
+ }
+
+ NormalizedSum* NormalizedSum::clone() const {
+ return new NormalizedSum(*this);
+ }
+
+ SNorm* NormalizedSum::constructor() {
+ return new NormalizedSum;
+ }
+
+
+}
diff --git a/fuzzylite/src/norm/t/AlgebraicProduct.cpp b/fuzzylite/src/norm/t/AlgebraicProduct.cpp
new file mode 100644
index 0000000..eee1061
--- /dev/null
+++ b/fuzzylite/src/norm/t/AlgebraicProduct.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/AlgebraicProduct.h"
+
+namespace fl {
+
+ std::string AlgebraicProduct::className() const {
+ return "AlgebraicProduct";
+ }
+
+ scalar AlgebraicProduct::compute(scalar a, scalar b) const {
+ return a * b;
+ }
+
+ AlgebraicProduct* AlgebraicProduct::clone() const {
+ return new AlgebraicProduct(*this);
+ }
+
+ TNorm* AlgebraicProduct::constructor() {
+ return new AlgebraicProduct;
+ }
+
+}
diff --git a/fuzzylite/src/norm/t/BoundedDifference.cpp b/fuzzylite/src/norm/t/BoundedDifference.cpp
new file mode 100644
index 0000000..aa4fead
--- /dev/null
+++ b/fuzzylite/src/norm/t/BoundedDifference.cpp
@@ -0,0 +1,46 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/BoundedDifference.h"
+
+
+namespace fl {
+
+ std::string BoundedDifference::className() const {
+ return "BoundedDifference";
+ }
+
+ scalar BoundedDifference::compute(scalar a, scalar b) const {
+ return Op::max(scalar(0.0), a + b - scalar(1.0));
+ }
+
+ BoundedDifference* BoundedDifference::clone() const {
+ return new BoundedDifference(*this);
+ }
+
+ TNorm* BoundedDifference::constructor() {
+ return new BoundedDifference;
+ }
+
+}
diff --git a/fuzzylite/src/norm/t/DrasticProduct.cpp b/fuzzylite/src/norm/t/DrasticProduct.cpp
new file mode 100644
index 0000000..124b79d
--- /dev/null
+++ b/fuzzylite/src/norm/t/DrasticProduct.cpp
@@ -0,0 +1,48 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/DrasticProduct.h"
+
+namespace fl {
+
+ std::string DrasticProduct::className() const {
+ return "DrasticProduct";
+ }
+
+ scalar DrasticProduct::compute(scalar a, scalar b) const {
+ if (Op::isEq(Op::max(a, b), 1.0)) {
+ return Op::min(a, b);
+ }
+ return 0.0;
+ }
+
+ DrasticProduct* DrasticProduct::clone() const {
+ return new DrasticProduct(*this);
+ }
+
+ TNorm* DrasticProduct::constructor() {
+ return new DrasticProduct;
+ }
+
+}
diff --git a/fuzzylite/src/norm/t/EinsteinProduct.cpp b/fuzzylite/src/norm/t/EinsteinProduct.cpp
new file mode 100644
index 0000000..ea79188
--- /dev/null
+++ b/fuzzylite/src/norm/t/EinsteinProduct.cpp
@@ -0,0 +1,45 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/EinsteinProduct.h"
+
+namespace fl {
+
+ std::string EinsteinProduct::className() const {
+ return "EinsteinProduct";
+ }
+
+ scalar EinsteinProduct::compute(scalar a, scalar b) const {
+ return (a * b) / (2.0 - (a + b - a * b));
+ }
+
+ EinsteinProduct* EinsteinProduct::clone() const {
+ return new EinsteinProduct(*this);
+ }
+
+ TNorm* EinsteinProduct::constructor() {
+ return new EinsteinProduct;
+ }
+
+}
diff --git a/fuzzylite/src/norm/t/HamacherProduct.cpp b/fuzzylite/src/norm/t/HamacherProduct.cpp
new file mode 100644
index 0000000..e3f093e
--- /dev/null
+++ b/fuzzylite/src/norm/t/HamacherProduct.cpp
@@ -0,0 +1,46 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/HamacherProduct.h"
+
+
+namespace fl {
+
+ std::string HamacherProduct::className() const {
+ return "HamacherProduct";
+ }
+
+ scalar HamacherProduct::compute(scalar a, scalar b) const {
+ return (a * b) / (a + b - a * b);
+ }
+
+ HamacherProduct* HamacherProduct::clone() const {
+ return new HamacherProduct(*this);
+ }
+
+ TNorm* HamacherProduct::constructor() {
+ return new HamacherProduct;
+ }
+
+}
diff --git a/fuzzylite/src/norm/t/Minimum.cpp b/fuzzylite/src/norm/t/Minimum.cpp
new file mode 100644
index 0000000..34f348f
--- /dev/null
+++ b/fuzzylite/src/norm/t/Minimum.cpp
@@ -0,0 +1,46 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/Minimum.h"
+
+namespace fl {
+
+ std::string Minimum::className() const {
+ return "Minimum";
+ }
+
+ scalar Minimum::compute(scalar a, scalar b) const {
+ return Op::min(a, b);
+ }
+
+ Minimum* Minimum::clone() const {
+ return new Minimum(*this);
+ }
+
+ TNorm* Minimum::constructor() {
+ return new Minimum;
+ }
+
+
+}
diff --git a/fuzzylite/src/norm/t/NilpotentMinimum.cpp b/fuzzylite/src/norm/t/NilpotentMinimum.cpp
new file mode 100644
index 0000000..b52a2fc
--- /dev/null
+++ b/fuzzylite/src/norm/t/NilpotentMinimum.cpp
@@ -0,0 +1,50 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/norm/t/NilpotentMinimum.h"
+
+namespace fl {
+
+ std::string NilpotentMinimum::className() const {
+ return "NilpotentMinimum";
+ }
+
+ scalar NilpotentMinimum::compute(scalar a, scalar b) const {
+ if (Op::isGt(a + b, 1.0)) {
+ return Op::min(a, b);
+ }
+ return 0.0;
+ }
+
+ NilpotentMinimum* NilpotentMinimum::clone() const {
+ return new NilpotentMinimum(*this);
+ }
+
+ TNorm* NilpotentMinimum::constructor() {
+ return new NilpotentMinimum;
+ }
+
+
+}
+
diff --git a/fuzzylite/src/rule/Antecedent.cpp b/fuzzylite/src/rule/Antecedent.cpp
new file mode 100644
index 0000000..aa18a68
--- /dev/null
+++ b/fuzzylite/src/rule/Antecedent.cpp
@@ -0,0 +1,368 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Antecedent.h"
+
+#include "fl/Engine.h"
+#include "fl/factory/HedgeFactory.h"
+#include "fl/factory/FactoryManager.h"
+#include "fl/hedge/Any.h"
+#include "fl/hedge/Hedge.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/TNorm.h"
+#include "fl/rule/Expression.h"
+#include "fl/rule/Rule.h"
+#include "fl/term/Accumulated.h"
+#include "fl/term/Function.h"
+#include "fl/term/Term.h"
+#include "fl/variable/InputVariable.h"
+#include "fl/variable/OutputVariable.h"
+
+#include <algorithm>
+#include <stack>
+
+
+namespace fl {
+
+ Antecedent::Antecedent()
+ : _text(""), _expression(fl::null) {
+ }
+
+ Antecedent::~Antecedent() {
+ unload();
+ }
+
+ void Antecedent::setText(const std::string& text) {
+ this->_text = text;
+ }
+
+ std::string Antecedent::getText() const {
+ return this->_text;
+ }
+
+ Expression* Antecedent::getExpression() const {
+ return this->_expression;
+ }
+
+ bool Antecedent::isLoaded() const {
+ return this->_expression != fl::null;
+ }
+
+ scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const {
+ return this->activationDegree(conjunction, disjunction, this->_expression);
+ }
+
+ scalar Antecedent::activationDegree(const TNorm* conjunction, const SNorm* disjunction,
+ const Expression* node) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT);
+ }
+ const Proposition* proposition = dynamic_cast<const Proposition*> (node);
+ if (proposition) {
+ if (not proposition->variable->isEnabled()) {
+ return 0.0;
+ }
+
+ if (not proposition->hedges.empty()) {
+ //if last hedge is "Any", apply hedges in reverse order and return degree
+ std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin();
+ if (dynamic_cast<Any*> (*rit)) {
+ scalar result = (*rit)->hedge(fl::nan);
+ while (++rit != proposition->hedges.rend()) {
+ result = (*rit)->hedge(result);
+ }
+ return result;
+ }
+ }
+ scalar result = fl::nan;
+ if (InputVariable * inputVariable = dynamic_cast<InputVariable*> (proposition->variable)) {
+ result = proposition->term->membership(inputVariable->getInputValue());
+ } else if (OutputVariable * outputVariable = dynamic_cast<OutputVariable*> (proposition->variable)) {
+ result = outputVariable->fuzzyOutput()->activationDegree(proposition->term);
+ }
+ for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin();
+ rit != proposition->hedges.rend(); ++rit) {
+ result = (*rit)->hedge(result);
+ }
+ return result;
+ }
+ //if node is an operator
+ const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node);
+ if (not (fuzzyOperator->left and fuzzyOperator->right)) {
+ std::ostringstream ex;
+ ex << "[syntax error] left and right operands must exist";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if (fuzzyOperator->name == Rule::andKeyword()) {
+ if (not conjunction) throw fl::Exception("[conjunction error] "
+ "the following rule requires a conjunction operator:\n" + _text, FL_AT);
+ return conjunction->compute(
+ this->activationDegree(conjunction, disjunction, fuzzyOperator->left),
+ this->activationDegree(conjunction, disjunction, fuzzyOperator->right));
+ }
+
+ if (fuzzyOperator->name == Rule::orKeyword()) {
+ if (not disjunction) throw fl::Exception("[disjunction error] "
+ "the following rule requires a disjunction operator:\n" + _text, FL_AT);
+ return disjunction->compute(
+ this->activationDegree(conjunction, disjunction, fuzzyOperator->left),
+ this->activationDegree(conjunction, disjunction, fuzzyOperator->right));
+ }
+ std::ostringstream ex;
+ ex << "[syntax error] operator <" << fuzzyOperator->name << "> not recognized";
+ throw fl::Exception(ex.str(), FL_AT);
+
+ }
+
+ void Antecedent::unload() {
+ if (_expression) {
+ delete _expression;
+ _expression = fl::null;
+ }
+ }
+
+ void Antecedent::load(fl::Rule* rule, const Engine* engine) {
+ load(_text, rule, engine);
+ }
+
+ void Antecedent::load(const std::string& antecedent, fl::Rule* rule, const Engine* engine) {
+ FL_DBG("Antecedent: " << antecedent);
+ unload();
+ this->_text = antecedent;
+ if (fl::Op::trim(antecedent).empty()) {
+ throw fl::Exception("[syntax error] antecedent is empty", FL_AT);
+ }
+ /*
+ Builds an proposition tree from the antecedent of a fuzzy rule.
+ The rules are:
+ 1) After a variable comes 'is',
+ 2) After 'is' comes a hedge or a term
+ 3) After a hedge comes a hedge or a term
+ 4) After a term comes a variable or an operator
+ */
+
+ Function function;
+
+ std::string postfix = function.toPostfix(antecedent);
+ FL_DBG("Postfix: " << postfix);
+ std::stringstream tokenizer(postfix);
+ std::string token;
+
+ enum FSM {
+ S_VARIABLE = 1, S_IS = 2, S_HEDGE = 4, S_TERM = 8, S_AND_OR = 16
+ };
+ int state = S_VARIABLE;
+ std::stack<Expression*> expressionStack;
+ Proposition* proposition = fl::null;
+ try {
+ while (tokenizer >> token) {
+ if (state bitand S_VARIABLE) {
+ Variable* variable = fl::null;
+ if (engine->hasInputVariable(token)) variable = engine->getInputVariable(token);
+ else if (engine->hasOutputVariable(token)) variable = engine->getOutputVariable(token);
+ if (variable) {
+ proposition = new Proposition;
+ proposition->variable = variable;
+ expressionStack.push(proposition);
+
+ state = S_IS;
+ FL_DBG("Token <" << token << "> is variable");
+ continue;
+ }
+ }
+
+ if (state bitand S_IS) {
+ if (token == Rule::isKeyword()) {
+ state = S_HEDGE bitor S_TERM;
+ FL_DBG("Token <" << token << "> is keyword");
+ continue;
+ }
+ }
+
+ if (state bitand S_HEDGE) {
+ Hedge* hedge = rule->getHedge(token);
+ if (not hedge) {
+ HedgeFactory* factory = FactoryManager::instance()->hedge();
+ if (factory->hasConstructor(token)) {
+ hedge = factory->constructObject(token);
+ rule->addHedge(hedge);
+ }
+ }
+ if (hedge) {
+ proposition->hedges.push_back(hedge);
+ if (dynamic_cast<Any*> (hedge)) {
+ state = S_VARIABLE bitor S_AND_OR;
+ } else {
+ state = S_HEDGE bitor S_TERM;
+ }
+ FL_DBG("Token <" << token << "> is hedge");
+ continue;
+ }
+ }
+
+ if (state bitand S_TERM) {
+ if (proposition->variable->hasTerm(token)) {
+ proposition->term = proposition->variable->getTerm(token);
+ state = S_VARIABLE bitor S_AND_OR;
+ FL_DBG("Token <" << token << "> is term");
+ continue;
+ }
+ }
+
+ if (state bitand S_AND_OR) {
+ if (token == Rule::andKeyword() or token == Rule::orKeyword()) {
+ if (expressionStack.size() < 2) {
+ std::ostringstream ex;
+ ex << "[syntax error] logical operator <" << token << "> expects two operands,"
+ << "but found <" << expressionStack.size() << "> in antecedent";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ Operator* fuzzyOperator = new Operator;
+ fuzzyOperator->name = token;
+ fuzzyOperator->right = expressionStack.top();
+ expressionStack.pop();
+ fuzzyOperator->left = expressionStack.top();
+ expressionStack.pop();
+ expressionStack.push(fuzzyOperator);
+
+ state = S_VARIABLE bitor S_AND_OR;
+ FL_DBG("Subtree: " << fuzzyOperator->toString() <<
+ "(" << fuzzyOperator->left->toString() << ") " <<
+ "(" << fuzzyOperator->right->toString() << ")");
+ continue;
+ }
+ }
+
+ //If reached this point, there was an error
+ if ((state bitand S_VARIABLE) or (state bitand S_AND_OR)) {
+ std::ostringstream ex;
+ ex << "[syntax error] antecedent expected variable or logical operator, but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if (state bitand S_IS) {
+ std::ostringstream ex;
+ ex << "[syntax error] antecedent expected keyword <" << Rule::isKeyword() << ">, but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if ((state bitand S_HEDGE) or (state bitand S_TERM)) {
+ std::ostringstream ex;
+ ex << "[syntax error] antecedent expected hedge or term, but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected token <" << token << "> in antecedent";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ if (not ((state bitand S_VARIABLE) or (state bitand S_AND_OR))) { //only acceptable final state
+ if (state bitand S_IS) {
+ std::ostringstream ex;
+ ex << "[syntax error] antecedent expected keyword <" << Rule::isKeyword() << "> after <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if ((state bitand S_HEDGE) or (state bitand S_TERM)) {
+ std::ostringstream ex;
+ ex << "[syntax error] antecedent expected hedge or term after <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+
+ if (expressionStack.size() != 1) {
+ std::vector<std::string> errors;
+ while (expressionStack.size() > 1) {
+ Expression* expression = expressionStack.top();
+ expressionStack.pop();
+ errors.push_back(expression->toString());
+ delete expression;
+ }
+ std::ostringstream ex;
+ ex << "[syntax error] unable to parse the following expressions in antecedent <"
+ << Op::join(errors, " ") << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ } catch (...) {
+ for (std::size_t i = 0; i < expressionStack.size(); ++i) {
+ delete expressionStack.top();
+ expressionStack.pop();
+ }
+ throw;
+ }
+ this->_expression = expressionStack.top();
+ }
+
+ std::string Antecedent::toString() const {
+ return toInfix(this->_expression);
+ }
+
+ std::string Antecedent::toPrefix(const Expression* node) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT);
+ }
+ if (not node) node = this->_expression;
+
+ if (dynamic_cast<const Proposition*> (node)) {
+ return node->toString();
+ }
+ const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node);
+ std::stringstream ss;
+ ss << fuzzyOperator->toString() << " "
+ << toPrefix(fuzzyOperator->left) << " "
+ << toPrefix(fuzzyOperator->right) << " ";
+ return ss.str();
+ }
+
+ std::string Antecedent::toInfix(const Expression* node) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT);
+ }
+ if (not node) node = this->_expression;
+ if (dynamic_cast<const Proposition*> (node)) {
+ return node->toString();
+ }
+ const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node);
+ std::stringstream ss;
+ ss << toInfix(fuzzyOperator->left) << " "
+ << fuzzyOperator->toString() << " "
+ << toInfix(fuzzyOperator->right) << " ";
+ return ss.str();
+ }
+
+ std::string Antecedent::toPostfix(const Expression* node) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[antecedent error] antecedent <" + _text + "> is not loaded", FL_AT);
+ }
+ if (not node) node = this->_expression;
+ if (dynamic_cast<const Proposition*> (node)) {
+ return node->toString();
+ }
+ const Operator* fuzzyOperator = dynamic_cast<const Operator*> (node);
+ std::stringstream ss;
+ ss << toPostfix(fuzzyOperator->left) << " "
+ << toPostfix(fuzzyOperator->right) << " "
+ << fuzzyOperator->toString() << " ";
+ return ss.str();
+ }
+
+
+}
diff --git a/fuzzylite/src/rule/Consequent.cpp b/fuzzylite/src/rule/Consequent.cpp
new file mode 100644
index 0000000..28d3390
--- /dev/null
+++ b/fuzzylite/src/rule/Consequent.cpp
@@ -0,0 +1,243 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Consequent.h"
+
+#include "fl/Engine.h"
+#include "fl/factory/HedgeFactory.h"
+#include "fl/factory/FactoryManager.h"
+#include "fl/hedge/Hedge.h"
+#include "fl/hedge/Any.h"
+#include "fl/norm/TNorm.h"
+#include "fl/rule/Expression.h"
+#include "fl/rule/Rule.h"
+#include "fl/term/Accumulated.h"
+#include "fl/term/Activated.h"
+#include "fl/variable/OutputVariable.h"
+
+#include <algorithm>
+
+namespace fl {
+
+ Consequent::Consequent() {
+ }
+
+ Consequent::~Consequent() {
+ unload();
+ }
+
+ std::string Consequent::getText() const {
+ return this->_text;
+ }
+
+ void Consequent::setText(const std::string& text) {
+ this->_text = text;
+ }
+
+ const std::vector<Proposition*>& Consequent::conclusions() const {
+ return this->_conclusions;
+ }
+
+ void Consequent::modify(scalar activationDegree, const TNorm* activation) {
+ if (not isLoaded()) {
+ throw fl::Exception("[consequent error] consequent <" + _text + "> is not loaded", FL_AT);
+ }
+ for (std::size_t i = 0; i < _conclusions.size(); ++i) {
+ Proposition* proposition = _conclusions.at(i);
+ if (proposition->variable->isEnabled()) {
+ if (not proposition->hedges.empty()) {
+ for (std::vector<Hedge*>::const_reverse_iterator rit = proposition->hedges.rbegin();
+ rit != proposition->hedges.rend(); ++rit) {
+ activationDegree = (*rit)->hedge(activationDegree);
+ }
+ }
+ Activated* term = new Activated(_conclusions.at(i)->term, activationDegree, activation);
+ OutputVariable* outputVariable = dynamic_cast<OutputVariable*> (proposition->variable);
+ outputVariable->fuzzyOutput()->addTerm(term);
+ FL_DBG("Accumulating " << term->toString());
+ }
+ }
+ }
+
+ bool Consequent::isLoaded() {
+ return not _conclusions.empty();
+ }
+
+ void Consequent::unload() {
+ for (std::size_t i = 0; i < _conclusions.size(); ++i) {
+ delete _conclusions.at(i);
+ }
+ _conclusions.clear();
+ }
+
+ void Consequent::load(Rule* rule, const Engine* engine) {
+ load(_text, rule, engine);
+ }
+
+ void Consequent::load(const std::string& consequent, Rule* rule, const Engine* engine) {
+ unload();
+ this->_text = consequent;
+
+ if (fl::Op::trim(consequent).empty()) {
+ throw fl::Exception("[syntax error] consequent is empty", FL_AT);
+ }
+
+ /**
+ Extracts the list of propositions from the consequent
+ The rules are:
+ 1) After a variable comes 'is' or '=',
+ 2) After 'is' comes a hedge or a term
+ 3) After a hedge comes a hedge or a term
+ 4) After a term comes operators 'and' or 'with'
+ 5) After operator 'and' comes a variable
+ 6) After operator 'with' comes a float
+ */
+ enum FSM {
+ S_VARIABLE = 1, S_IS = 2, S_HEDGE = 4, S_TERM = 8,
+ S_AND = 16, S_WITH = 32
+ };
+ int state = S_VARIABLE;
+
+ Proposition* proposition = fl::null;
+
+ std::stringstream tokenizer(consequent);
+ std::string token;
+ try {
+ while (tokenizer >> token) {
+ if (state bitand S_VARIABLE) {
+ if (engine->hasOutputVariable(token)) {
+ proposition = new Proposition;
+ proposition->variable = engine->getOutputVariable(token);
+ _conclusions.push_back(proposition);
+
+ state = S_IS;
+ continue;
+ }
+ }
+
+ if (state bitand S_IS) {
+ if (token == Rule::isKeyword()) {
+ state = S_HEDGE bitor S_TERM;
+ continue;
+ }
+ }
+
+ if (state bitand S_HEDGE) {
+ Hedge* hedge = rule->getHedge(token);
+ if (not hedge) {
+ HedgeFactory* factory = FactoryManager::instance()->hedge();
+ if (factory->hasConstructor(token)){
+ hedge = factory->constructObject(token);
+ rule->addHedge(hedge);
+ }
+ }
+ if (hedge) {
+ proposition->hedges.push_back(hedge);
+ state = S_HEDGE bitor S_TERM;
+ continue;
+ }
+ }
+
+ if (state bitand S_TERM) {
+ if (proposition->variable->hasTerm(token)) {
+ proposition->term = proposition->variable->getTerm(token);
+ state = S_AND bitor S_WITH;
+ continue;
+ }
+ }
+
+ if (state bitand S_AND) {
+ if (token == Rule::andKeyword()) {
+ state = S_VARIABLE;
+ continue;
+ }
+ }
+
+ //if reached this point, there was an error:
+ if (state bitand S_VARIABLE) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected output variable, but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if (state bitand S_IS) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected keyword <" << Rule::isKeyword() << ">, "
+ "but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ if ((state bitand S_HEDGE) or (state bitand S_TERM)) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected hedge or term, but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ if ((state bitand S_AND) or (state bitand S_WITH)) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected operator <" << Rule::andKeyword() << "> "
+ << "or keyword <" << Rule::withKeyword() << ">, "
+ << "but found <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected token <" << token << "> in consequent";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ if (not ((state bitand S_AND) or (state bitand S_WITH))) { //only acceptable final state
+ if (state bitand S_VARIABLE) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected output variable after <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if (state bitand S_IS) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected keyword <" << Rule::isKeyword() << "> "
+ "after <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ if ((state bitand S_HEDGE) or (state bitand S_TERM)) {
+ std::ostringstream ex;
+ ex << "[syntax error] consequent expected hedge or term after <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+ } catch (...) {
+ unload();
+ throw;
+ }
+ }
+
+ std::string Consequent::toString() const {
+ std::stringstream ss;
+ for (std::size_t i = 0; i < _conclusions.size(); ++i) {
+ ss << _conclusions.at(i)->toString();
+ if (i + 1 < _conclusions.size())
+ ss << " " << Rule::andKeyword() << " ";
+ }
+ return ss.str();
+ }
+
+}
diff --git a/fuzzylite/src/rule/Expression.cpp b/fuzzylite/src/rule/Expression.cpp
new file mode 100644
index 0000000..ea7873e
--- /dev/null
+++ b/fuzzylite/src/rule/Expression.cpp
@@ -0,0 +1,83 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Expression.h"
+
+#include "fl/hedge/Hedge.h"
+#include "fl/term/Term.h"
+#include "fl/rule/Rule.h"
+#include "fl/variable/Variable.h"
+
+namespace fl {
+
+ Expression::Expression() {
+ }
+
+ Expression::~Expression() {
+ }
+
+ Proposition::Proposition()
+ : Expression(), variable(fl::null), term(fl::null) {
+ }
+
+ Proposition::~Proposition() {
+
+ }
+
+ std::string Proposition::toString() const {
+ std::ostringstream ss;
+ if (variable) {
+ ss << variable->getName();
+ } else {
+ ss << "?";
+ }
+ if (not hedges.empty()) {
+ ss << " " << Rule::isKeyword() << " ";
+ for (std::size_t i = 0; i < hedges.size(); ++i) {
+ ss << hedges.at(i)->name() << " ";
+ }
+ }
+
+ if (term) { //term is fl::null if hedge is any
+ if (hedges.empty()) {
+ ss << " " << Rule::isKeyword() << " ";
+ }
+ ss << term->getName();
+ }
+ return ss.str();
+ }
+
+ Operator::Operator() : Expression(), name(""), left(fl::null), right(fl::null) {
+ }
+
+ Operator::~Operator() {
+ if (left) delete left;
+ if (right) delete right;
+ }
+
+ std::string Operator::toString() const {
+ return name;
+ }
+
+}
diff --git a/fuzzylite/src/rule/Rule.cpp b/fuzzylite/src/rule/Rule.cpp
new file mode 100644
index 0000000..446290b
--- /dev/null
+++ b/fuzzylite/src/rule/Rule.cpp
@@ -0,0 +1,262 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/Rule.h"
+
+#include "fl/Exception.h"
+#include "fl/hedge/Hedge.h"
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/Norm.h"
+#include "fl/rule/Antecedent.h"
+#include "fl/rule/Consequent.h"
+
+#include <sstream>
+#include <vector>
+
+namespace fl {
+
+ Rule::Rule(const std::string& text, scalar weight)
+ : _text(text), _weight(weight), _antecedent(new Antecedent), _consequent(new Consequent) {
+ }
+
+ Rule::Rule(const Rule& other) : _text(other._text), _weight(other._weight),
+ _antecedent(new Antecedent), _consequent(new Consequent) {
+ }
+
+ Rule& Rule::operator=(const Rule& other) {
+ if (this != &other) {
+ unload();
+
+ _text = other._text;
+ _weight = other._weight;
+ _antecedent.reset(new Antecedent);
+ _consequent.reset(new Consequent);
+ }
+ return *this;
+ }
+
+ Rule::~Rule() {
+ unload();
+ }
+
+ void Rule::setText(const std::string& text) {
+ this->_text = text;
+ }
+
+ std::string Rule::getText() const {
+ return this->_text;
+ }
+
+ void Rule::setWeight(scalar weight) {
+ this->_weight = weight;
+ }
+
+ scalar Rule::getWeight() const {
+ return this->_weight;
+ }
+
+ void Rule::setAntecedent(Antecedent* antecedent) {
+ this->_antecedent.reset(antecedent);
+ }
+
+ Antecedent* Rule::getAntecedent() const {
+ return this->_antecedent.get();
+ }
+
+ void Rule::setConsequent(Consequent* consequent) {
+ this->_consequent.reset(consequent);
+ }
+
+ Consequent* Rule::getConsequent() const {
+ return this->_consequent.get();
+ }
+
+ /**
+ * Operations for std::vector _hedges
+ */
+ void Rule::addHedge(Hedge* hedge) {
+ this->_hedges[hedge->name()] = hedge;
+ }
+
+ Hedge* Rule::getHedge(const std::string& name) const {
+ std::map<std::string, Hedge*>::const_iterator it = this->_hedges.find(name);
+ if (it != this->_hedges.end()) {
+ if (it->second) return it->second;
+ }
+ return fl::null;
+ }
+
+ Hedge* Rule::removeHedge(const std::string& name) {
+ Hedge* result = fl::null;
+ std::map<std::string, Hedge*>::iterator it = this->_hedges.find(name);
+ if (it != this->_hedges.end()) {
+ result = it->second;
+ this->_hedges.erase(it);
+ }
+ return result;
+ }
+
+ bool Rule::hasHedge(const std::string& name) const {
+ std::map<std::string, Hedge*>::const_iterator it = this->_hedges.find(name);
+ return (it != this->_hedges.end());
+ }
+
+ int Rule::numberOfHedges() const {
+ return this->_hedges.size();
+ }
+
+ void Rule::setHedges(const std::map<std::string, Hedge*>& hedges) {
+ this->_hedges = hedges;
+ }
+
+ const std::map<std::string, Hedge*>& Rule::hedges() const {
+ return this->_hedges;
+ }
+
+ std::map<std::string, Hedge*>& Rule::hedges() {
+ return this->_hedges;
+ }
+
+ scalar Rule::activationDegree(const TNorm* conjunction, const SNorm* disjunction) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[rule error] the following rule is not loaded: " + _text, FL_AT);
+ }
+ return _weight * getAntecedent()->activationDegree(conjunction, disjunction);
+ }
+
+ void Rule::activate(scalar degree, const TNorm* activation) const {
+ if (not isLoaded()) {
+ throw fl::Exception("[rule error] the following rule is not loaded: " + _text, FL_AT);
+ }
+ _consequent->modify(degree, activation);
+ }
+
+ bool Rule::isLoaded() const {
+ return _antecedent->isLoaded() and _consequent->isLoaded();
+ }
+
+ void Rule::unload() {
+ _antecedent->unload();
+ _consequent->unload();
+
+ for (std::map<std::string, Hedge*>::const_iterator it = _hedges.begin();
+ it != _hedges.end(); ++it) {
+ delete it->second;
+ }
+ _hedges.clear();
+ }
+
+ void Rule::load(const Engine* engine) {
+ load(_text, engine);
+ }
+
+ void Rule::load(const std::string& rule, const Engine* engine) {
+ this->_text = rule;
+ std::istringstream tokenizer(rule.substr(0, rule.find_first_of('#')));
+ std::string token;
+ std::ostringstream ossAntecedent, ossConsequent;
+ scalar weight = 1.0;
+
+ enum FSM {
+ S_NONE, S_IF, S_THEN, S_WITH, S_END
+ };
+ FSM state = S_NONE;
+ try {
+ while (tokenizer >> token) {
+
+ switch (state) {
+ case S_NONE:
+ if (token == Rule::ifKeyword()) state = S_IF;
+ else {
+ std::ostringstream ex;
+ ex << "[syntax error] expected keyword <" << Rule::ifKeyword() <<
+ ">, but found <" << token << "> in rule: " << rule;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ break;
+ case S_IF:
+ if (token == Rule::thenKeyword()) state = S_THEN;
+ else ossAntecedent << token << " ";
+ break;
+ case S_THEN:
+ if (token == Rule::withKeyword()) state = S_WITH;
+ else ossConsequent << token << " ";
+ break;
+ case S_WITH:
+ try {
+ weight = fl::Op::toScalar(token);
+ state = S_END;
+ } catch (fl::Exception& e) {
+ std::ostringstream ex;
+ ex << "[syntax error] expected a numeric value as the weight of the rule: "
+ << rule;
+ e.append(ex.str(), FL_AT);
+ throw e;
+ }
+ break;
+ case S_END:
+ std::ostringstream ex;
+ ex << "[syntax error] unexpected token <" << token << "> at the end of rule";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+ if (state == S_NONE) {
+ std::ostringstream ex;
+ ex << "[syntax error] " << (rule.empty() ? "empty rule" : "ignored rule: " + rule);
+ throw fl::Exception(ex.str(), FL_AT);
+ } else if (state == S_IF) {
+ std::ostringstream ex;
+ ex << "[syntax error] keyword <" << Rule::thenKeyword() << "> not found in rule: " << rule;
+ throw fl::Exception(ex.str(), FL_AT);
+ } else if (state == S_WITH) {
+ std::ostringstream ex;
+ ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ _antecedent->load(ossAntecedent.str(), this, engine);
+ _consequent->load(ossConsequent.str(), this, engine);
+ _weight = weight;
+
+ } catch (...) {
+ unload();
+ throw;
+ }
+ }
+
+ std::string Rule::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ Rule* Rule::clone() const {
+ return new Rule(*this);
+ }
+
+ Rule* Rule::parse(const std::string& rule, const Engine* engine) {
+ FL_unique_ptr<Rule> result(new Rule);
+ result->load(rule, engine);
+ return result.release();
+ }
+
+}
diff --git a/fuzzylite/src/rule/RuleBlock.cpp b/fuzzylite/src/rule/RuleBlock.cpp
new file mode 100644
index 0000000..9ab813e
--- /dev/null
+++ b/fuzzylite/src/rule/RuleBlock.cpp
@@ -0,0 +1,210 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/rule/RuleBlock.h"
+
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/TNorm.h"
+#include "fl/norm/SNorm.h"
+#include "fl/rule/Rule.h"
+
+#include <sstream>
+
+namespace fl {
+
+ RuleBlock::RuleBlock(const std::string& name)
+ : _name(name), _enabled(true) {
+ }
+
+ RuleBlock::RuleBlock(const RuleBlock& other) : _name(other._name),
+ _enabled(true) {
+ copyFrom(other);
+ }
+
+ RuleBlock& RuleBlock::operator=(const RuleBlock& other) {
+ if (this != &other) {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ delete _rules.at(i);
+ }
+ _rules.clear();
+ _conjunction.reset(fl::null);
+ _disjunction.reset(fl::null);
+ _activation.reset(fl::null);
+
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ void RuleBlock::copyFrom(const RuleBlock& source) {
+ _name = source._name;
+ _enabled = source._enabled;
+ if (source._activation.get()) _activation.reset(source._activation->clone());
+ if (source._conjunction.get()) _conjunction.reset(source._conjunction->clone());
+ if (source._disjunction.get()) _disjunction.reset(source._disjunction->clone());
+ for (std::size_t i = 0; i < source._rules.size(); ++i) {
+ _rules.push_back(source._rules.at(i)->clone());
+ }
+ }
+
+ RuleBlock::~RuleBlock() {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ delete _rules.at(i);
+ }
+ _rules.clear();
+ }
+
+ void RuleBlock::activate() {
+ FL_DBG("===================");
+ FL_DBG("ACTIVATING RULEBLOCK " << _name);
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ Rule* rule = _rules.at(i);
+ if (rule->isLoaded()) {
+ scalar activationDegree = rule->activationDegree(_conjunction.get(), _disjunction.get());
+ FL_DBG("[degree=" << Op::str(activationDegree) << "] " << rule->toString());
+ if (Op::isGt(activationDegree, 0.0)) {
+ rule->activate(activationDegree, _activation.get());
+ }
+ } else {
+ FL_DBG("Rule not loaded: " << rule->toString());
+ }
+ }
+ }
+
+ void RuleBlock::unloadRules() const {
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ _rules.at(i)->unload();
+ }
+ }
+
+ void RuleBlock::loadRules(const Engine* engine) {
+ std::ostringstream exceptions;
+ bool throwException = false;
+ for (std::size_t i = 0; i < _rules.size(); ++i) {
+ Rule* rule = _rules.at(i);
+ if (rule->isLoaded()) {
+ rule->unload();
+ }
+ try {
+ rule->load(engine);
+ } catch (std::exception& ex) {
+ throwException = true;
+ exceptions << ex.what() << "\n";
+ }
+ }
+ if (throwException) {
+ fl::Exception exception("[ruleblock error] the following "
+ "rules could not be loaded:\n" + exceptions.str(), FL_AT);
+ throw exception;
+ }
+ }
+
+ void RuleBlock::reloadRules(const Engine* engine) {
+ unloadRules();
+ loadRules(engine);
+ }
+
+ void RuleBlock::setName(std::string name) {
+ this->_name = name;
+ }
+
+ std::string RuleBlock::getName() const {
+ return this->_name;
+ }
+
+ void RuleBlock::setConjunction(TNorm* tnorm) {
+ this->_conjunction.reset(tnorm);
+ }
+
+ TNorm* RuleBlock::getConjunction() const {
+ return this->_conjunction.get();
+ }
+
+ void RuleBlock::setDisjunction(SNorm* snorm) {
+ this->_disjunction.reset(snorm);
+ }
+
+ SNorm* RuleBlock::getDisjunction() const {
+ return this->_disjunction.get();
+ }
+
+ void RuleBlock::setActivation(TNorm* activation) {
+ this->_activation.reset(activation);
+ }
+
+ TNorm* RuleBlock::getActivation() const {
+ return this->_activation.get();
+ }
+
+ void RuleBlock::setEnabled(bool enabled) {
+ this->_enabled = enabled;
+ }
+
+ bool RuleBlock::isEnabled() const {
+ return this->_enabled;
+ }
+
+ std::string RuleBlock::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ /**
+ * Operations for std::vector _rules
+ */
+ void RuleBlock::addRule(Rule* rule) {
+ this->_rules.push_back(rule);
+ }
+
+ void RuleBlock::insertRule(Rule* rule, int index) {
+ this->_rules.insert(this->_rules.begin() + index, rule);
+ }
+
+ Rule* RuleBlock::getRule(int index) const {
+ return this->_rules.at(index);
+ }
+
+ Rule* RuleBlock::removeRule(int index) {
+ Rule* result = this->_rules.at(index);
+ this->_rules.erase(this->_rules.begin() + index);
+ return result;
+ }
+
+ int RuleBlock::numberOfRules() const {
+ return this->_rules.size();
+ }
+
+ const std::vector<Rule*>& RuleBlock::rules() const {
+ return this->_rules;
+ }
+
+ void RuleBlock::setRules(const std::vector<Rule*>& rules) {
+ this->_rules = rules;
+ }
+
+ std::vector<Rule*>& RuleBlock::rules() {
+ return this->_rules;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/Accumulated.cpp b/fuzzylite/src/term/Accumulated.cpp
new file mode 100644
index 0000000..979af9f
--- /dev/null
+++ b/fuzzylite/src/term/Accumulated.cpp
@@ -0,0 +1,211 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Accumulated.h"
+
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/SNorm.h"
+#include "fl/norm/s/Maximum.h"
+#include "fl/term/Activated.h"
+
+
+namespace fl {
+
+ Accumulated::Accumulated(const std::string& name, scalar minimum, scalar maximum,
+ SNorm* accumulation)
+ : Term(name), _minimum(minimum), _maximum(maximum), _accumulation(accumulation) {
+ }
+
+ Accumulated::Accumulated(const Accumulated& other) : Term(other) {
+ copyFrom(other);
+ }
+
+ Accumulated& Accumulated::operator=(const Accumulated& other) {
+ if (this != &other) {
+ clear();
+ _accumulation.reset(fl::null);
+
+ Term::operator=(other);
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ Accumulated::~Accumulated() {
+ clear();
+ }
+
+ void Accumulated::copyFrom(const Accumulated& source) {
+ _minimum = source._minimum;
+ _maximum = source._maximum;
+
+ if (source._accumulation.get())
+ _accumulation.reset(source._accumulation->clone());
+
+ for (std::size_t i = 0; i < source._terms.size(); ++i) {
+ _terms.push_back(source._terms.at(i)->clone());
+ }
+ }
+
+ std::string Accumulated::className() const {
+ return "Accumulated";
+ }
+
+ scalar Accumulated::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ if (not (_terms.empty() or _accumulation.get())) { //Exception for IntegralDefuzzifiers
+ throw fl::Exception("[accumulation error] "
+ "accumulation operator needed to accumulate " + toString(), FL_AT);
+ }
+ scalar mu = 0.0;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ mu = _accumulation->compute(mu, _terms.at(i)->membership(x));
+ }
+ return mu;
+ }
+
+ scalar Accumulated::activationDegree(const Term* forTerm) const {
+ scalar result = 0.0;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ Activated* activatedTerm = _terms.at(i);
+ if (activatedTerm->getTerm() == forTerm) {
+ if (_accumulation.get()) result = _accumulation->compute(result, activatedTerm->getDegree());
+ else result += activatedTerm->getDegree(); //Default for WeightDefuzzifier
+ }
+ }
+ return result;
+ }
+
+ std::string Accumulated::parameters() const {
+ FllExporter exporter;
+ std::ostringstream ss;
+ ss << exporter.toString(_accumulation.get());
+ ss << " " << Op::str(_minimum) << " " << Op::str(_maximum) << " ";
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ ss << " " << exporter.toString(_terms.at(i));
+ }
+ return ss.str();
+ }
+
+ void Accumulated::configure(const std::string& parameters) {
+ (void) parameters;
+ }
+
+ Accumulated* Accumulated::clone() const {
+ return new Accumulated(*this);
+ }
+
+ std::string Accumulated::toString() const {
+ std::vector<std::string> accumulate;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ accumulate.push_back(_terms.at(i)->toString());
+ }
+ FllExporter exporter;
+ std::ostringstream ss;
+ ss << _name << ": " << className() << " "
+ << exporter.toString(_accumulation.get()) << "["
+ << fl::Op::join(accumulate, ",") << "]";
+ return ss.str();
+ }
+
+ void Accumulated::setMinimum(scalar minimum) {
+ this->_minimum = minimum;
+ }
+
+ scalar Accumulated::getMinimum() const {
+ return this->_minimum;
+ }
+
+ void Accumulated::setMaximum(scalar maximum) {
+ this->_maximum = maximum;
+ }
+
+ scalar Accumulated::getMaximum() const {
+ return this->_maximum;
+ }
+
+ void Accumulated::setRange(scalar minimum, scalar maximum) {
+ setMinimum(minimum);
+ setMaximum(maximum);
+ }
+
+ scalar Accumulated::range() const {
+ return this->_maximum - this->_minimum;
+ }
+
+ void Accumulated::setAccumulation(SNorm* accumulation) {
+ this->_accumulation.reset(accumulation);
+ }
+
+ SNorm* Accumulated::getAccumulation() const {
+ return this->_accumulation.get();
+ }
+
+ /**
+ * Operations for std::vector _terms
+ */
+
+
+ void Accumulated::addTerm(const Term* term, scalar degree, const TNorm* activation) {
+ this->_terms.push_back(new Activated(term, degree, activation));
+ }
+
+ void Accumulated::addTerm(Activated* term) {
+ this->_terms.push_back(term);
+ }
+
+ Activated* Accumulated::removeTerm(int index) {
+ Activated* term = this->_terms.at(index);
+ this->_terms.erase(this->_terms.begin() + index);
+ return term;
+ }
+
+ void Accumulated::clear() {
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ delete _terms.at(i);
+ }
+ _terms.clear();
+ }
+
+ Activated* Accumulated::getTerm(int index) const {
+ return this->_terms.at(index);
+ }
+
+ const std::vector<Activated*>& Accumulated::terms() const {
+ return this->_terms;
+ }
+
+ std::vector<Activated*>& Accumulated::terms() {
+ return this->_terms;
+ }
+
+ int Accumulated::numberOfTerms() const {
+ return _terms.size();
+ }
+
+ bool Accumulated::isEmpty() const {
+ return _terms.empty();
+ }
+
+}
diff --git a/fuzzylite/src/term/Activated.cpp b/fuzzylite/src/term/Activated.cpp
new file mode 100644
index 0000000..9a27b4b
--- /dev/null
+++ b/fuzzylite/src/term/Activated.cpp
@@ -0,0 +1,100 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Activated.h"
+
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/TNorm.h"
+
+namespace fl {
+
+ Activated::Activated(const Term* term, scalar degree, const TNorm* activation)
+ : Term(""), _term(term), _degree(degree), _activation(activation) {
+ if (term) this->_name = term->getName();
+ }
+
+ Activated::~Activated() {
+ }
+
+ std::string Activated::className() const {
+ return "Activated";
+ }
+
+ scalar Activated::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ if (not _activation) throw fl::Exception("[activation error] "
+ "activation operator needed to activate " + _term->toString(), FL_AT);
+ return _activation->compute(this->_term->membership(x), _degree);
+ }
+
+ std::string Activated::parameters() const {
+ FllExporter exporter;
+ std::ostringstream ss;
+ ss << Op::str(_degree) << " " << exporter.toString(_activation) << " "
+ << exporter.toString(_term);
+ return ss.str();
+ }
+
+ void Activated::configure(const std::string& parameters) {
+ (void) parameters;
+ }
+
+ std::string Activated::toString() const {
+ FllExporter exporter;
+ std::ostringstream ss;
+ ss << exporter.toString(_activation) << "("
+ << Op::str(_degree) << ","
+ << _term->getName() << ")";
+ return ss.str();
+ }
+
+ void Activated::setTerm(const Term* term) {
+ this->_term = term;
+ }
+
+ const Term* Activated::getTerm() const {
+ return this->_term;
+ }
+
+ void Activated::setDegree(scalar degree) {
+ this->_degree = degree;
+ }
+
+ scalar Activated::getDegree() const {
+ return this->_degree;
+ }
+
+ void Activated::setActivation(const TNorm* activation) {
+ this->_activation = activation;
+ }
+
+ const TNorm* Activated::getActivation() const {
+ return this->_activation;
+ }
+
+ Activated* Activated::clone() const {
+ return new Activated(*this);
+ }
+
+}
diff --git a/fuzzylite/src/term/Bell.cpp b/fuzzylite/src/term/Bell.cpp
new file mode 100644
index 0000000..fc0b215
--- /dev/null
+++ b/fuzzylite/src/term/Bell.cpp
@@ -0,0 +1,99 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Bell.h"
+
+namespace fl {
+
+ Bell::Bell(const std::string& name, scalar center, scalar width, scalar slope, scalar height)
+ : Term(name, height), _center(center), _width(width), _slope(slope) {
+ }
+
+ Bell::~Bell() {
+ }
+
+ std::string Bell::className() const {
+ return "Bell";
+ }
+
+ scalar Bell::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ return _height * (1.0 / (1.0 + std::pow(std::abs((x - _center) / _width), 2 * _slope)));
+ }
+
+ std::string Bell::parameters() const {
+ return Op::join(3, " ", _center, _width, _slope) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Bell::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 3;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setCenter(Op::toScalar(values.at(0)));
+ setWidth(Op::toScalar(values.at(1)));
+ setSlope(Op::toScalar(values.at(2)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Bell::setWidth(scalar a) {
+ this->_width = a;
+ }
+
+ scalar Bell::getWidth() const {
+ return this->_width;
+ }
+
+ void Bell::setSlope(scalar b) {
+ this->_slope = b;
+ }
+
+ scalar Bell::getSlope() const {
+ return this->_slope;
+ }
+
+ void Bell::setCenter(scalar c) {
+ this->_center = c;
+ }
+
+ scalar Bell::getCenter() const {
+ return this->_center;
+ }
+
+ Bell* Bell::clone() const {
+ return new Bell(*this);
+ }
+
+ Term* Bell::constructor() {
+ return new Bell;
+ }
+
+}
diff --git a/fuzzylite/src/term/Concave.cpp b/fuzzylite/src/term/Concave.cpp
new file mode 100644
index 0000000..37679ae
--- /dev/null
+++ b/fuzzylite/src/term/Concave.cpp
@@ -0,0 +1,107 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Concave.h"
+
+namespace fl {
+
+ Concave::Concave(const std::string& name, scalar inflection, scalar end, scalar height)
+ : Term(name, height), _inflection(inflection), _end(end) {
+
+ }
+
+ Concave::~Concave() {
+
+ }
+
+ std::string Concave::className() const {
+ return "Concave";
+ }
+
+ scalar Concave::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ if (fl::Op::isLE(_inflection, _end)) { //Concave increasing
+ if (fl::Op::isLt(x, _end)) {
+ return _height * (_end - _inflection) / (2 * _end - _inflection - x);
+ }
+ } else { //Concave decreasing
+ if (fl::Op::isGt(x, _end)) {
+ return _height * (_inflection - _end) / (_inflection - 2 * _end + x);
+ }
+ }
+ return _height * 1.0;
+ }
+
+ std::string Concave::parameters() const {
+ return Op::join(2, " ", _inflection, _end) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+
+ }
+
+ void Concave::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setInflection(Op::toScalar(values.at(0)));
+ setEnd(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+
+ }
+
+ void Concave::setInflection(scalar start) {
+ this->_inflection = start;
+ }
+
+ scalar Concave::getInflection() const {
+ return this->_inflection;
+ }
+
+ void Concave::setEnd(scalar end) {
+ this->_end = end;
+ }
+
+ scalar Concave::getEnd() const {
+ return this->_end;
+ }
+
+ Concave* Concave::clone() const {
+ return new Concave(*this);
+ }
+
+ Term* Concave::constructor() {
+ return new Concave;
+ }
+
+
+
+
+
+}
diff --git a/fuzzylite/src/term/Constant.cpp b/fuzzylite/src/term/Constant.cpp
new file mode 100644
index 0000000..d52f8ec
--- /dev/null
+++ b/fuzzylite/src/term/Constant.cpp
@@ -0,0 +1,70 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Constant.h"
+
+namespace fl {
+
+ Constant::Constant(const std::string& name, scalar value)
+ : Term(name), _value(value) {
+ }
+
+ Constant::~Constant() {
+ }
+
+ std::string Constant::className() const {
+ return "Constant";
+ }
+
+ scalar Constant::membership(scalar x) const {
+ (void) x;
+ return this->_value;
+ }
+
+ std::string Constant::parameters() const {
+ return Op::str(_value);
+ }
+
+ void Constant::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ setValue(Op::toScalar(parameters));
+ }
+
+ void Constant::setValue(scalar value) {
+ this->_value = value;
+ }
+
+ scalar Constant::getValue() const {
+ return this->_value;
+ }
+
+ Constant* Constant::clone() const {
+ return new Constant(*this);
+ }
+
+ Term* Constant::constructor() {
+ return new Constant;
+ }
+
+}
diff --git a/fuzzylite/src/term/Cosine.cpp b/fuzzylite/src/term/Cosine.cpp
new file mode 100644
index 0000000..a1402ad
--- /dev/null
+++ b/fuzzylite/src/term/Cosine.cpp
@@ -0,0 +1,96 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Cosine.h"
+
+namespace fl {
+
+ Cosine::Cosine(const std::string& name, scalar center, scalar width, scalar height)
+ : Term(name, height), _center(center), _width(width) {
+
+ }
+
+ Cosine::~Cosine() {
+
+ }
+
+ std::string Cosine::className() const {
+ return "Cosine";
+ }
+
+ std::string Cosine::parameters() const {
+ return Op::join(2, " ", _center, _width) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Cosine::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setCenter(Op::toScalar(values.at(0)));
+ setWidth(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+
+ }
+
+ scalar Cosine::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ if (fl::Op::isLt(x, _center - _width / 2.0)
+ or fl::Op::isGt(x, _center + _width / 2.0))
+ return _height * 0.0;
+ const scalar pi = 4.0 * std::atan(1.0);
+ return _height * (0.5 * (1.0 + std::cos(2.0 / _width * pi * (x - _center))));
+ }
+
+ void Cosine::setCenter(scalar center) {
+ this->_center = center;
+ }
+
+ scalar Cosine::getCenter() const {
+ return this->_center;
+ }
+
+ void Cosine::setWidth(scalar width) {
+ this->_width = width;
+ }
+
+ scalar Cosine::getWidth() const {
+ return this->_width;
+ }
+
+ Cosine* Cosine::clone() const {
+ return new Cosine(*this);
+ }
+
+ Term* Cosine::constructor() {
+ return new Cosine;
+ }
+}
diff --git a/fuzzylite/src/term/Discrete.cpp b/fuzzylite/src/term/Discrete.cpp
new file mode 100644
index 0000000..212ada2
--- /dev/null
+++ b/fuzzylite/src/term/Discrete.cpp
@@ -0,0 +1,210 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Discrete.h"
+
+#include <cstdarg>
+
+namespace fl {
+
+ Discrete::Discrete(const std::string& name, const std::vector<Pair>& xy, scalar height)
+ : Term(name, height), _xy(xy) {
+ }
+
+ Discrete::~Discrete() {
+ }
+
+ std::string Discrete::className() const {
+ return "Discrete";
+ }
+
+ scalar Discrete::membership(scalar _x_) const {
+ if (fl::Op::isNaN(_x_)) return fl::nan;
+ if (_xy.empty())
+ throw fl::Exception("[discrete error] term is empty", FL_AT);
+
+ /* ______________________
+ * / \
+ * / \
+ * ____________/ \____________
+ * x[0] x[n-1]
+ */
+
+
+ if (fl::Op::isLE(_x_, _xy.front().first)) return _height * _xy.front().second;
+ if (fl::Op::isGE(_x_, _xy.back().first)) return _height * _xy.back().second;
+
+ int lower = -1, upper = -1;
+
+ for (std::size_t i = 0; i < _xy.size(); ++i) {
+ if (Op::isEq(_xy.at(i).first, _x_)) return _height * _xy.at(i).second;
+ //approximate on the left
+ if (Op::isLt(_xy.at(i).first, _x_)) {
+ lower = i;
+ }
+ //get the immediate next one on the right
+ if (Op::isGt(_xy.at(i).first, _x_)) {
+ upper = i;
+ break;
+ }
+ }
+ if (upper < 0) upper = _xy.size() - 1;
+ if (lower < 0) lower = 0;
+
+ return _height * Op::scale(_x_, _xy.at(lower).first, _xy.at(upper).first,
+ _xy.at(lower).second, _xy.at(upper).second);
+ }
+
+ std::string Discrete::parameters() const {
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < _xy.size(); ++i) {
+ ss << fl::Op::str(_xy.at(i).first) << " " << fl::Op::str(_xy.at(i).second);
+ if (i + 1 < _xy.size()) ss << " ";
+ }
+ if (not Op::isEq(_height, 1.0)) ss << " " << Op::str(_height);
+ return ss.str();
+ }
+
+ void Discrete::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> strValues = Op::split(parameters, " ");
+ std::vector<scalar> values(strValues.size());
+ for (std::size_t i = 0; i < strValues.size(); ++i) {
+ values.at(i) = Op::toScalar(strValues.at(i));
+ }
+ if (values.size() % 2 == 0) {
+ setHeight(1.0);
+ } else {
+ setHeight(values.back());
+ values.pop_back();
+ }
+ this->_xy = toPairs(values);
+ }
+
+ template <typename T>
+ Discrete* Discrete::create(const std::string& name, int argc,
+ T x1, T y1, ...) { // throw (fl::Exception) {
+ std::vector<scalar> xy(argc);
+ xy.at(0) = x1;
+ xy.at(1) = y1;
+ va_list args;
+ va_start(args, y1);
+ for (int i = 2; i < argc; ++i) {
+ xy.at(i) = (scalar) va_arg(args, T);
+ }
+ va_end(args);
+
+ FL_unique_ptr<Discrete> result(new Discrete(name));
+ if (xy.size() % 2 != 0) {
+ result->setHeight(xy.back());
+ xy.pop_back();
+ }
+ result->setXY(toPairs(xy));
+ return result.release();
+ }
+
+ template FL_API Discrete* Discrete::create(const std::string& name, int argc,
+ double x1, double y1, ...); // throw (fl::Exception);
+ //double, not scalar because variadic promotes floats to double
+ template FL_API Discrete* Discrete::create(const std::string& name, int argc,
+ int x1, int y1, ...); // throw (fl::Exception);
+
+ void Discrete::setXY(const std::vector<Pair>& pairs) {
+ this->_xy = pairs;
+ }
+
+ const std::vector<Discrete::Pair>& Discrete::xy() const {
+ return this->_xy;
+ }
+
+ std::vector<Discrete::Pair>& Discrete::xy() {
+ return this->_xy;
+ }
+
+ const Discrete::Pair& Discrete::xy(int index) const {
+ return this->_xy.at(index);
+ }
+
+ Discrete::Pair& Discrete::xy(int index) {
+ return this->_xy.at(index);
+ }
+
+ std::vector<Discrete::Pair> Discrete::toPairs(const std::vector<scalar>& xy) {
+ if (xy.size() % 2 != 0) {
+ std::ostringstream os;
+ os << "[discrete error] missing value in set of pairs (|xy|=" << xy.size() << ")";
+ throw fl::Exception(os.str(), FL_AT);
+ }
+
+ std::vector<Pair> result((xy.size() + 1) / 2);
+ for (std::size_t i = 0; i + 1 < xy.size(); i += 2) {
+ result.at(i / 2).first = xy.at(i);
+ result.at(i / 2).second = xy.at(i + 1);
+ }
+ return result;
+ }
+
+ std::vector<Discrete::Pair> Discrete::toPairs(const std::vector<scalar>& xy,
+ scalar missingValue) FL_INOEXCEPT {
+ std::vector<Pair> result((xy.size() + 1) / 2);
+ for (std::size_t i = 0; i + 1 < xy.size(); i += 2) {
+ result.at(i / 2).first = xy.at(i);
+ result.at(i / 2).second = xy.at(i + 1);
+ }
+ if (xy.size() % 2 != 0) {
+ result.back().first = xy.back();
+ result.back().second = missingValue;
+ }
+ return result;
+ }
+
+ std::vector<scalar> Discrete::toVector(const std::vector<Pair>& xy) {
+ std::vector<scalar> result(xy.size() * 2);
+ for (std::size_t i = 0; i < xy.size(); ++i) {
+ result.at(2 * i) = xy.at(i).first;
+ result.at(2 * i + 1) = xy.at(i).second;
+ }
+ return result;
+ }
+
+ std::string Discrete::formatXY(const std::vector<Pair>& xy, const std::string& prefix, const std::string& innerSeparator, const std::string& postfix, const std::string& outerSeparator) {
+ std::ostringstream os;
+ for (std::size_t i = 0; i < xy.size(); ++i) {
+ os << prefix << fl::Op::str(xy.at(i).first) << innerSeparator
+ << fl::Op::str(xy.at(i).second) << postfix;
+ if (i + 1 < xy.size()) os << outerSeparator;
+ }
+ return os.str();
+ }
+
+ Discrete* Discrete::clone() const {
+ return new Discrete(*this);
+ }
+
+ Term* Discrete::constructor() {
+ return new Discrete;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/Function.cpp b/fuzzylite/src/term/Function.cpp
new file mode 100644
index 0000000..c0f54b8
--- /dev/null
+++ b/fuzzylite/src/term/Function.cpp
@@ -0,0 +1,600 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Function.h"
+
+#include "fl/Engine.h"
+#include "fl/factory/FactoryManager.h"
+#include "fl/factory/FunctionFactory.h"
+#include "fl/rule/Rule.h"
+#include "fl/variable/InputVariable.h"
+#include "fl/variable/OutputVariable.h"
+
+#include <cctype>
+#include <functional>
+#include <queue>
+#include <signal.h>
+#include <stack>
+
+
+namespace fl {
+
+ /**
+ * Parsing elements
+ */
+
+
+ Function::Element::Element(const std::string& name, const std::string& description, Type type)
+ : name(name), description(description), type(type), unary(fl::null), binary(fl::null), arity(0),
+ precedence(0), associativity(-1) {
+
+ }
+
+ Function::Element::Element(const std::string& name, const std::string& description,
+ Type type, Unary unary, int precedence, int associativity)
+ : name(name), description(description), type(type), unary(unary), binary(fl::null), arity(1),
+ precedence(precedence), associativity(associativity) {
+ }
+
+ Function::Element::Element(const std::string& name, const std::string& description,
+ Type type, Binary binary, int precedence, int associativity)
+ : name(name), description(description), type(type), unary(fl::null), binary(binary), arity(2),
+ precedence(precedence), associativity(associativity) {
+ }
+
+ Function::Element::~Element() {
+
+ }
+
+ bool Function::Element::isOperator() const {
+ return type == OPERATOR;
+ }
+
+ bool Function::Element::isFunction() const {
+ return type == FUNCTION;
+ }
+
+ Function::Element* Function::Element::clone() const {
+ return new Element(*this);
+ }
+
+ std::string Function::Element::toString() const {
+ std::ostringstream ss;
+
+ if (type == OPERATOR) {
+ ss << "Operator (name=" << name << ", "
+ << "description=" << description << ", "
+ << "precedence=" << precedence << ", "
+ << "arity=" << arity << ", "
+ << "associativity=" << associativity << ", ";
+ if (arity == 1) ss << "pointer=" << unary;
+ else if (arity == 2) ss << "pointer=" << binary;
+ else ss << "pointer=error";
+ ss << ")";
+ } else if (type == FUNCTION) {
+ ss << "Function (name=" << name << ", "
+ << "description=" << description << ", "
+ << "arity=" << arity << ", "
+ << "associativity=" << associativity << ", ";
+ if (arity == 1) ss << "pointer=" << unary;
+ else if (arity == 2) ss << "pointer=" << binary;
+ else ss << "pointer=error";
+ ss << ")";
+ }
+ return ss.str();
+ }
+
+ /******************************
+ * Tree Node Elements
+ ******************************/
+
+ Function::Node::Node(Element* element, Node* left, Node* right)
+ : element(element), left(left), right(right), variable(""), value(fl::nan) {
+ }
+
+ Function::Node::Node(const std::string& variable)
+ : element(fl::null), left(fl::null), right(fl::null), variable(variable), value(fl::nan) {
+ }
+
+ Function::Node::Node(scalar value)
+ : element(fl::null), left(fl::null), right(fl::null), variable(""), value(value) {
+ }
+
+ Function::Node::Node(const Node& other)
+ : element(fl::null), left(fl::null), right(fl::null), variable(""), value(fl::nan) {
+ copyFrom(other);
+ }
+
+ Function::Node& Function::Node::operator=(const Node& other) {
+ if (this != &other) {
+ element.reset(fl::null);
+ left.reset(fl::null);
+ right.reset(fl::null);
+
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ void Function::Node::copyFrom(const Node& other) {
+ if (other.element.get()) element.reset(other.element->clone());
+ if (other.left.get()) left.reset(other.left->clone());
+ if (other.right.get()) right.reset(other.right->clone());
+ variable = other.variable;
+ value = other.value;
+ }
+
+ Function::Node::~Node() {
+ }
+
+ scalar Function::Node::evaluate(const std::map<std::string, scalar>* variables) const {
+ scalar result = fl::nan;
+ if (element.get()) {
+ if (element->unary) {
+ result = element->unary(left->evaluate(variables));
+ } else if (element->binary) {
+ result = element->binary(right->evaluate(variables), left->evaluate(variables));
+ } else {
+ std::ostringstream ex;
+ ex << "[function error] arity <" << element->arity << "> of "
+ << (element->isOperator() ? "operator" : "function") <<
+ " <" << element->name << "> is fl::null";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ } else if (not variable.empty()) {
+ if (not variables) {
+ throw fl::Exception("[function error] "
+ "expected a map of variables, but none was provided", FL_AT);
+ }
+ std::map<std::string, scalar>::const_iterator it = variables->find(variable);
+ if (it != variables->end()) result = it->second;
+ else throw fl::Exception("[function error] "
+ "unknown variable <" + variable + ">", FL_AT);
+ } else {
+ result = value;
+ }
+ return result;
+ }
+
+ Function::Node* Function::Node::clone() const {
+ return new Node(*this);
+ }
+
+ std::string Function::Node::toString() const {
+ std::ostringstream ss;
+ if (element.get()) ss << element->name;
+ else if (not variable.empty()) ss << variable;
+ else ss << fl::Op::str(value);
+ return ss.str();
+ }
+
+ std::string Function::Node::toPrefix(const Node* node) const {
+ if (not node) node = this;
+ if (not fl::Op::isNaN(node->value)) { //is terminal
+ return fl::Op::str(node->value);
+ }
+ if (not node->variable.empty()) {
+ return node->variable;
+ }
+
+ std::ostringstream ss;
+ ss << node->toString();
+ if (node->left.get())
+ ss << " " << this->toPrefix(node->left.get());
+ if (node->right.get())
+ ss << " " << this->toPrefix(node->right.get());
+ return ss.str();
+ }
+
+ std::string Function::Node::toInfix(const Node* node) const {
+ if (not node) node = this;
+ if (not fl::Op::isNaN(node->value)) { //is proposition
+ return fl::Op::str(node->value);
+ }
+ if (not node->variable.empty()) {
+ return node->variable;
+ }
+
+ std::ostringstream ss;
+ if (node->left.get())
+ ss << this->toInfix(node->left.get()) << " ";
+ ss << node->toString();
+ if (node->right.get())
+ ss << " " << this->toInfix(node->right.get());
+ return ss.str();
+ }
+
+ std::string Function::Node::toPostfix(const Node* node) const {
+ if (not node) node = this;
+ if (not fl::Op::isNaN(node->value)) { //is proposition
+ return fl::Op::str(node->value);
+ }
+ if (not node->variable.empty()) {
+ return node->variable;
+ }
+
+ std::ostringstream ss;
+ if (node->left.get())
+ ss << this->toPostfix(node->left.get()) << " ";
+ if (node->right.get())
+ ss << this->toPostfix(node->right.get()) << " ";
+ ss << node->toString();
+ return ss.str();
+ }
+
+ /**********************************
+ * Function class.
+ **********************************/
+ Function::Function(const std::string& name,
+ const std::string& formula, const Engine* engine)
+ : Term(name), _root(fl::null), _formula(formula), _engine(engine) {
+ }
+
+ Function::Function(const Function& other) : Term(other),
+ _root(fl::null), _formula(other._formula), _engine(other._engine) {
+ if (other._root.get()) _root.reset(other._root->clone());
+ variables = other.variables;
+ }
+
+ Function& Function::operator=(const Function& other) {
+ if (this != &other) {
+ _root.reset(fl::null);
+
+ Term::operator=(other);
+ _formula = other._formula;
+ _engine = other._engine;
+ if (other._root.get()) _root.reset(other._root->clone());
+ variables = other.variables;
+ }
+ return *this;
+ }
+
+ Function::~Function() {
+ }
+
+ std::string Function::className() const {
+ return "Function";
+ }
+
+ scalar Function::membership(scalar x) const {
+ if (not this->_root.get()) {
+ throw fl::Exception("[function error] function <" + _formula + "> not loaded.", FL_AT);
+ }
+ if (this->_engine) {
+ for (int i = 0; i < this->_engine->numberOfInputVariables(); ++i) {
+ InputVariable* input = this->_engine->getInputVariable(i);
+ this->variables[input->getName()] = input->getInputValue();
+ }
+ for (int i = 0; i < this->_engine->numberOfOutputVariables(); ++i) {
+ OutputVariable* output = this->_engine->getOutputVariable(i);
+ this->variables[output->getName()] = output->getOutputValue();
+ }
+ }
+ this->variables["x"] = x;
+ return this->evaluate(&this->variables);
+ }
+
+ scalar Function::evaluate(const std::map<std::string, scalar>* localVariables) const {
+ if (not this->_root.get())
+ throw fl::Exception("[function error] evaluation failed because the function is not loaded", FL_AT);
+ if (localVariables)
+ return this->_root->evaluate(localVariables);
+ return this->_root->evaluate(&this->variables);
+ }
+
+ std::string Function::parameters() const {
+ return _formula;
+ }
+
+ void Function::configure(const std::string& parameters) {
+ load(parameters);
+ }
+
+ Function* Function::create(const std::string& name,
+ const std::string& infix, const Engine* engine) {
+ FL_unique_ptr<Function> result(new Function(name));
+ result->load(infix, engine);
+ return result.release();
+ }
+
+ bool Function::isLoaded() const {
+ return this->_root.get() != fl::null;
+ }
+
+ void Function::unload() {
+ this->_root.reset(fl::null);
+ this->variables.clear();
+ }
+
+ void Function::load() {
+ load(this->_formula);
+ }
+
+ void Function::load(const std::string& formula) {
+ load(formula, this->_engine);
+ }
+
+ void Function::load(const std::string& formula,
+ const Engine* engine) {
+ unload();
+ this->_formula = formula;
+ this->_engine = engine;
+ this->_root.reset(parse(formula));
+ membership(0.0); //make sure function evaluates without throwing exception.
+ }
+
+ void Function::setFormula(const std::string& formula) {
+ this->_formula = formula;
+ }
+
+ std::string Function::getFormula() const {
+ return this->_formula;
+ }
+
+ void Function::setEngine(const Engine* engine) {
+ this->_engine = engine;
+ }
+
+ const Engine* Function::getEngine() const {
+ return this->_engine;
+ }
+
+ Function::Node* Function::root() const {
+ return this->_root.get();
+ }
+
+ Function* Function::clone() const {
+ return new Function(*this);
+ }
+
+ Term* Function::constructor() {
+ return new Function;
+ }
+
+ std::string Function::space(const std::string& formula) const {
+ std::vector<std::string> chars;
+ chars.push_back("(");
+ chars.push_back(")");
+ chars.push_back(",");
+
+ std::vector<std::string> operators = fl::FactoryManager::instance()->function()->availableOperators();
+ for (std::size_t i = 0; i < operators.size(); ++i) {
+ if (not (operators.at(i) == fl::Rule::andKeyword() or
+ operators.at(i) == fl::Rule::orKeyword())) {
+ chars.push_back(operators.at(i));
+ }
+ }
+
+ std::string result = formula;
+ for (std::size_t i = 0; i < chars.size(); ++i) {
+ result = fl::Op::findReplace(result, chars.at(i), " " + chars.at(i) + " ");
+ }
+ return result;
+ }
+
+ /****************************************
+ * The Glorious Parser
+ * Shunting-yard algorithm
+ * TODO: Maybe change it for http://en.wikipedia.org/wiki/Operator-precedence_parser
+ ***************************************/
+
+ std::string Function::toPostfix(const std::string& formula) const {
+ std::string spacedFormula = space(formula);
+
+ std::queue<std::string> queue;
+ std::stack<std::string> stack;
+
+ std::stringstream tokenizer(spacedFormula);
+ std::string token;
+ FunctionFactory* factory = fl::FactoryManager::instance()->function();
+ while (tokenizer >> token) {
+ Element* element = factory->getObject(token);
+ bool isOperand = not element and token != "(" and token != ")" and token != ",";
+
+ if (isOperand) {
+ queue.push(token);
+
+ } else if (element and element->isFunction()) {
+ stack.push(token);
+
+ } else if (token == ",") {
+ while (not stack.empty() and stack.top() != "(") {
+ queue.push(stack.top());
+ stack.pop();
+ }
+ if (stack.empty() or stack.top() != "(") {
+ std::ostringstream ex;
+ ex << "[parsing error] mismatching parentheses in: " << formula;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+
+ } else if (element and element->isOperator()) {
+ Element* op1 = element;
+ for (;;) {
+ Element* op2 = fl::null;
+ if (not stack.empty()) op2 = factory->getObject(stack.top());
+ if (not op2) break;
+
+ if ((op1->associativity < 0 and op1->precedence == op2->precedence)
+ or op1->precedence < op2->precedence) {
+ queue.push(stack.top());
+ stack.pop();
+ } else
+ break;
+ }
+ stack.push(token);
+
+ } else if (token == "(") {
+ stack.push(token);
+
+ } else if (token == ")") {
+ while (not stack.empty() and stack.top() != "(") {
+ queue.push(stack.top());
+ stack.pop();
+ }
+ if (stack.empty() or stack.top() != "(") {
+ std::ostringstream ex;
+ ex << "[parsing error] mismatching parentheses in: " << formula;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ stack.pop(); //get rid of "("
+
+ Element* top = fl::null;
+ if (not stack.empty()) top = factory->getObject(stack.top());
+ if (top and top->isFunction()) {
+ queue.push(stack.top());
+ stack.pop();
+ }
+ } else {
+ std::ostringstream ex;
+ ex << "[parsing error] unexpected error with token <" << token << ">";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ }
+
+ while (not stack.empty()) {
+ if (stack.top() == "(" or stack.top() == ")") {
+ std::ostringstream ex;
+ ex << "[parsing error] mismatching parentheses in: " << formula;
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ queue.push(stack.top());
+ stack.pop();
+ }
+
+ std::stringstream ssPostfix;
+ while (not queue.empty()) {
+ ssPostfix << queue.front();
+ queue.pop();
+ if (not queue.empty()) ssPostfix << " ";
+ }
+ // FL_DBG("postfix=" << ssPostfix.str());
+ return ssPostfix.str();
+ }
+
+ // bool FunctionFactory::isOperand(const std::string& name) const {
+ // //An operand is not a parenthesis...
+ // if (name == "(" or name == ")" or name == ",") return false;
+ // //nor an operator...
+ // if (isOperator(name)) return false;
+ // //nor a function...
+ // if (isFunction(name)) return false;
+ // //...it is everything else :)
+ // return true;
+ // }
+
+ Function::Node* Function::parse(const std::string& formula) {
+ if (formula.empty())
+ throw fl::Exception("[function error] formula is empty", FL_AT);
+ std::string postfix = toPostfix(formula);
+
+ std::stack<Node*> stack;
+
+ std::istringstream tokenizer(postfix);
+ std::string token;
+ FunctionFactory* factory = fl::FactoryManager::instance()->function();
+ while (tokenizer >> token) {
+ Element* element = factory->getObject(token);
+ bool isOperand = not element and token != "(" and token != ")" and token != ",";
+
+ if (element) {
+ if (element->arity > (int) stack.size()) {
+ std::ostringstream ss;
+ ss << "[function error] " << (element->isOperator() ? "operator" : "function") <<
+ " <" << element->name << "> has arity <" << element->arity << ">, "
+ "but found <" << stack.size() << "> element" <<
+ (stack.size() == 1 ? "" : "s");
+ throw fl::Exception(ss.str(), FL_AT);
+ }
+
+ Node* node = new Node(element->clone());
+ node->left.reset(stack.top());
+ stack.pop();
+ if (element->arity == 2) {
+ node->right.reset(stack.top());
+ stack.pop();
+ }
+ stack.push(node);
+
+ } else if (isOperand) {
+ Node* node;
+ try {
+ scalar value = fl::Op::toScalar(token);
+ node = new Node(value);
+ } catch (std::exception& ex) {
+ (void) ex;
+ node = new Node(token);
+ }
+ stack.push(node);
+ }
+ }
+
+ if (stack.size() != 1)
+ throw fl::Exception("[function error] ill-formed formula <" + formula + ">", FL_AT);
+
+ return stack.top();
+ }
+
+ void Function::main() {
+ Function f;
+ std::string text = "3+4*2/(1-5)^2^3";
+ FL_LOG(f.toPostfix(text));
+ FL_LOG("P: " << f.parse(text)->toInfix());
+ FL_LOG(">" << f.parse(text)->evaluate());
+ //3 4 2 * 1 5 - 2 3 ^ ^ / +
+
+ f.variables["y"] = 1.0;
+ text = "sin(y*x)^2/x";
+ FL_LOG("pre: " << f.parse(text)->toPrefix());
+ FL_LOG("in: " << f.parse(text)->toInfix());
+ FL_LOG("pos: " << f.parse(text)->toPostfix());
+ f.load(text);
+ FL_LOG("Result: " << f.membership(1));
+ //y x * sin 2 ^ x /
+
+
+ text = "(Temperature is High and Oxygen is Low) or "
+ "(Temperature is Low and (Oxygen is Low or Oxygen is High))";
+ FL_LOG(f.toPostfix(text));
+
+ f.variables["pi"] = 3.14;
+ text = "-5 *4/sin(-pi/2)";
+ FL_LOG(f.toPostfix(text));
+ try {
+ FL_LOG(f.parse(text)->evaluate());
+ } catch (std::exception& e) {
+ FL_LOG(e.what());
+ }
+ f.variables["pi"] = 3.14;
+ text = "~5 *4/sin(~pi/2)";
+ FL_LOG(f.toPostfix(text));
+ try {
+ FL_LOG(f.parse(text)->evaluate(&f.variables));
+ } catch (std::exception& e) {
+ FL_LOG(e.what());
+ }
+ }
+
+}
diff --git a/fuzzylite/src/term/Gaussian.cpp b/fuzzylite/src/term/Gaussian.cpp
new file mode 100644
index 0000000..5b709f9
--- /dev/null
+++ b/fuzzylite/src/term/Gaussian.cpp
@@ -0,0 +1,92 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Gaussian.h"
+
+namespace fl {
+
+ Gaussian::Gaussian(const std::string& name,
+ scalar mean, scalar standardDeviation, scalar height)
+ : Term(name, height), _mean(mean), _standardDeviation(standardDeviation) {
+ }
+
+ Gaussian::~Gaussian() {
+ }
+
+ std::string Gaussian::className() const {
+ return "Gaussian";
+ }
+
+ scalar Gaussian::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ return _height * std::exp((-(x - _mean) * (x - _mean)) / (2 * _standardDeviation * _standardDeviation));
+ }
+
+ std::string Gaussian::parameters() const {
+ return Op::join(2, " ", _mean, _standardDeviation) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Gaussian::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setMean(Op::toScalar(values.at(0)));
+ setStandardDeviation(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Gaussian::setMean(scalar c) {
+ this->_mean = c;
+ }
+
+ scalar Gaussian::getMean() const {
+ return this->_mean;
+ }
+
+ void Gaussian::setStandardDeviation(scalar sigma) {
+ this->_standardDeviation = sigma;
+ }
+
+ scalar Gaussian::getStandardDeviation() const {
+ return this->_standardDeviation;
+ }
+
+ Gaussian* Gaussian::clone() const {
+ return new Gaussian(*this);
+ }
+
+ Term* Gaussian::constructor() {
+ return new Gaussian;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/GaussianProduct.cpp b/fuzzylite/src/term/GaussianProduct.cpp
new file mode 100644
index 0000000..b9652e1
--- /dev/null
+++ b/fuzzylite/src/term/GaussianProduct.cpp
@@ -0,0 +1,120 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/GaussianProduct.h"
+
+namespace fl {
+
+ GaussianProduct::GaussianProduct(const std::string& name,
+ scalar meanA, scalar standardDeviationA, scalar meanB, scalar standardDeviationB,
+ scalar height)
+ : Term(name, height), _meanA(meanA), _standardDeviationA(standardDeviationA),
+ _meanB(meanB), _standardDeviationB(standardDeviationB) {
+ }
+
+ GaussianProduct::~GaussianProduct() {
+ }
+
+ std::string GaussianProduct::className() const {
+ return "GaussianProduct";
+ }
+
+ scalar GaussianProduct::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ bool xLEa = fl::Op::isLE(x, _meanA);
+ scalar a = (1 - xLEa) + xLEa * std::exp(
+ (-(x - _meanA) * (x - _meanA)) / (2 * _standardDeviationA * _standardDeviationA)
+ );
+ bool xGEb = fl::Op::isGE(x, _meanB);
+ scalar b = (1 - xGEb) + xGEb * std::exp(
+ (-(x - _meanB) * (x - _meanB)) / (2 * _standardDeviationB * _standardDeviationB)
+ );
+ return _height * a * b;
+ }
+
+ std::string GaussianProduct::parameters() const {
+ return Op::join(4, " ", _meanA, _standardDeviationA, _meanB, _standardDeviationB) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void GaussianProduct::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 4;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setMeanA(Op::toScalar(values.at(0)));
+ setStandardDeviationA(Op::toScalar(values.at(1)));
+ setMeanB(Op::toScalar(values.at(2)));
+ setStandardDeviationB(Op::toScalar(values.at(3)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void GaussianProduct::setMeanA(scalar meanA) {
+ this->_meanA = meanA;
+ }
+
+ scalar GaussianProduct::getMeanA() const {
+ return this->_meanA;
+ }
+
+ void GaussianProduct::setStandardDeviationA(scalar sigmaA) {
+ this->_standardDeviationA = sigmaA;
+ }
+
+ scalar GaussianProduct::getStandardDeviationA() const {
+ return this->_standardDeviationA;
+ }
+
+ void GaussianProduct::setMeanB(scalar meanB) {
+ this->_meanB = meanB;
+ }
+
+ scalar GaussianProduct::getMeanB() const {
+ return this->_meanB;
+ }
+
+ void GaussianProduct::setStandardDeviationB(scalar sigmaB) {
+ this->_standardDeviationB = sigmaB;
+ }
+
+ scalar GaussianProduct::getStandardDeviationB() const {
+ return this->_standardDeviationB;
+ }
+
+ GaussianProduct* GaussianProduct::clone() const {
+ return new GaussianProduct(*this);
+ }
+
+ Term* GaussianProduct::constructor() {
+ return new GaussianProduct;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/Linear.cpp b/fuzzylite/src/term/Linear.cpp
new file mode 100644
index 0000000..4111a00
--- /dev/null
+++ b/fuzzylite/src/term/Linear.cpp
@@ -0,0 +1,135 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Linear.h"
+
+#include "fl/Engine.h"
+#include "fl/variable/InputVariable.h"
+
+#include <cstdarg>
+
+namespace fl {
+
+ Linear::Linear(const std::string& name,
+ const std::vector<scalar>& coefficients,
+ const Engine* engine)
+ : Term(name), _coefficients(coefficients), _engine(engine) {
+ }
+
+ Linear::~Linear() {
+ }
+
+ std::string Linear::className() const {
+ return "Linear";
+ }
+
+ scalar Linear::membership(scalar x) const {
+ (void) x;
+ if (not _engine) throw fl::Exception("[linear error] term <" + getName() + "> "
+ "is missing a reference to the engine", FL_AT);
+
+ scalar result = 0.0;
+ for (std::size_t i = 0; i < _engine->inputVariables().size(); ++i) {
+ if (i < _coefficients.size())
+ result += _coefficients.at(i) * _engine->inputVariables().at(i)->getInputValue();
+ }
+ if (_coefficients.size() > _engine->inputVariables().size()) {
+ result += _coefficients.back();
+ }
+ return result;
+ }
+
+ void Linear::set(const std::vector<scalar>& coeffs, const Engine* engine) {
+ setCoefficients(coeffs);
+ setEngine(engine);
+ }
+
+ void Linear::setCoefficients(const std::vector<scalar>& coeffs) {
+ this->_coefficients = coeffs;
+ }
+
+ const std::vector<scalar>& Linear::coefficients() const {
+ return this->_coefficients;
+ }
+
+ std::vector<scalar>& Linear::coefficients() {
+ return this->_coefficients;
+ }
+
+ void Linear::setEngine(const Engine* engine) {
+ this->_engine = engine;
+ }
+
+ const Engine* Linear::getEngine() const {
+ return this->_engine;
+ }
+
+ std::string Linear::parameters() const {
+ return Op::join(this->_coefficients, " ");
+ }
+
+ void Linear::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> strValues = Op::split(parameters, " ");
+ std::vector<scalar> values;
+ for (std::size_t i = 0; i < strValues.size(); ++i) {
+ values.push_back(Op::toScalar(strValues.at(i)));
+ }
+ this->_coefficients = values;
+ }
+
+ Linear* Linear::clone() const {
+ return new Linear(*this);
+ }
+
+ Term* Linear::constructor() {
+ return new Linear;
+ }
+
+ template <typename T>
+ Linear* Linear::create(const std::string& name,
+ const Engine* engine, T firstCoefficient, ...) {// throw (fl::Exception) {
+ if (not engine) throw fl::Exception("[linear error] cannot create term <" + name + "> "
+ "without a reference to the engine", FL_AT);
+ std::vector<scalar> coefficients;
+ coefficients.push_back(firstCoefficient);
+
+ va_list args;
+ va_start(args, firstCoefficient);
+ for (std::size_t i = 0; i < engine->inputVariables().size(); ++i) {
+ coefficients.push_back((scalar) va_arg(args, T));
+ }
+ va_end(args);
+
+ return new Linear(name, coefficients, engine);
+ }
+
+ template FL_API Linear* Linear::create(const std::string& name,
+ const Engine* engine,
+ double firstCoefficient, ...);
+
+ template FL_API Linear* Linear::create(const std::string& name,
+ const Engine* engine,
+ int firstCoefficient, ...);
+}
diff --git a/fuzzylite/src/term/PiShape.cpp b/fuzzylite/src/term/PiShape.cpp
new file mode 100644
index 0000000..ba3e99f
--- /dev/null
+++ b/fuzzylite/src/term/PiShape.cpp
@@ -0,0 +1,133 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/PiShape.h"
+
+namespace fl {
+
+ PiShape::PiShape(const std::string& name, scalar bottomLeft, scalar topLeft,
+ scalar topRight, scalar bottomRight, scalar height)
+ : Term(name, height), _bottomLeft(bottomLeft), _topLeft(topLeft),
+ _topRight(topRight), _bottomRight(bottomRight) {
+ }
+
+ PiShape::~PiShape() {
+ }
+
+ std::string PiShape::className() const {
+ return "PiShape";
+ }
+
+ scalar PiShape::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ //from Octave smf.m
+ scalar a_b_ave = (_bottomLeft + _topLeft) / 2.0;
+ scalar b_minus_a = _topLeft - _bottomLeft;
+ scalar c_d_ave = (_topRight + _bottomRight) / 2.0;
+ scalar d_minus_c = _bottomRight - _topRight;
+
+ if (Op::isLE(x, _bottomLeft)) return _height * 0.0;
+
+ if (Op::isLE(x, a_b_ave))
+ return _height * (2.0 * std::pow((x - _bottomLeft) / b_minus_a, 2));
+
+ if (Op::isLt(x, _topLeft))
+ return _height * (1.0 - 2.0 * std::pow((x - _topLeft) / b_minus_a, 2));
+
+ if (Op::isLE(x, _topRight))
+ return _height * 1.0;
+
+ if (Op::isLE(x, c_d_ave))
+ return _height * (1.0 - 2.0 * std::pow((x - _topRight) / d_minus_c, 2));
+
+ if (Op::isLt(x, _bottomRight))
+ return _height * (2.0 * std::pow((x - _bottomRight) / d_minus_c, 2));
+
+ return _height * 0.0;
+ }
+
+ std::string PiShape::parameters() const {
+ return Op::join(4, " ", _bottomLeft, _topLeft, _topRight, _bottomRight) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void PiShape::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 4;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setBottomLeft(Op::toScalar(values.at(0)));
+ setTopLeft(Op::toScalar(values.at(1)));
+ setTopRight(Op::toScalar(values.at(2)));
+ setBottomRight(Op::toScalar(values.at(3)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void PiShape::setBottomLeft(scalar a) {
+ this->_bottomLeft = a;
+ }
+
+ scalar PiShape::getBottomLeft() const {
+ return this->_bottomLeft;
+ }
+
+ void PiShape::setTopLeft(scalar b) {
+ this->_topLeft = b;
+ }
+
+ scalar PiShape::getTopLeft() const {
+ return this->_topLeft;
+ }
+
+ void PiShape::setTopRight(scalar d) {
+ this->_topRight = d;
+ }
+
+ scalar PiShape::getTopRight() const {
+ return this->_topRight;
+ }
+
+ void PiShape::setBottomRight(scalar c) {
+ this->_bottomRight = c;
+ }
+
+ scalar PiShape::getBottomRight() const {
+ return this->_bottomRight;
+ }
+
+ PiShape* PiShape::clone() const {
+ return new PiShape(*this);
+ }
+
+ Term* PiShape::constructor() {
+ return new PiShape;
+ }
+
+}
diff --git a/fuzzylite/src/term/Ramp.cpp b/fuzzylite/src/term/Ramp.cpp
new file mode 100644
index 0000000..f224045
--- /dev/null
+++ b/fuzzylite/src/term/Ramp.cpp
@@ -0,0 +1,110 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Ramp.h"
+
+namespace fl {
+
+ Ramp::Ramp(const std::string& name, scalar start, scalar end, scalar height)
+ : Term(name, height), _start(start), _end(end) {
+ }
+
+ Ramp::~Ramp() {
+ }
+
+ std::string Ramp::className() const {
+ return "Ramp";
+ }
+
+ scalar Ramp::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+
+ if (Op::isEq(_start, _end)) return _height * 0.0;
+
+ if (Op::isLt(_start, _end)) {
+ if (Op::isLE(x, _start)) return _height * 0.0;
+ if (Op::isGE(x, _end)) return _height * 1.0;
+ return _height * (x - _start) / (_end - _start);
+ } else {
+ if (Op::isGE(x, _start)) return _height * 0.0;
+ if (Op::isLE(x, _end)) return _height * 1.0;
+ return _height * (_start - x) / (_start - _end);
+ }
+ }
+
+ std::string Ramp::parameters() const {
+ return Op::join(2, " ", _start, _end) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Ramp::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setStart(Op::toScalar(values.at(0)));
+ setEnd(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Ramp::setStart(scalar start) {
+ this->_start = start;
+ }
+
+ scalar Ramp::getStart() const {
+ return this->_start;
+ }
+
+ void Ramp::setEnd(scalar end) {
+ this->_end = end;
+ }
+
+ scalar Ramp::getEnd() const {
+ return this->_end;
+ }
+
+ Ramp::Direction Ramp::direction() const {
+ scalar range = this->_end - this->_start;
+ if (not fl::Op::isFinite(range) or fl::Op::isEq(range, 0.0)) return ZERO;
+
+ if (fl::Op::isGt(range, 0.0)) return POSITIVE;
+
+ return NEGATIVE;
+ }
+
+ Ramp* Ramp::clone() const {
+ return new Ramp(*this);
+ }
+
+ Term* Ramp::constructor() {
+ return new Ramp;
+ }
+
+}
diff --git a/fuzzylite/src/term/Rectangle.cpp b/fuzzylite/src/term/Rectangle.cpp
new file mode 100644
index 0000000..363b15d
--- /dev/null
+++ b/fuzzylite/src/term/Rectangle.cpp
@@ -0,0 +1,93 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Rectangle.h"
+
+namespace fl {
+
+ Rectangle::Rectangle(const std::string& name, scalar start, scalar end, scalar height)
+ : Term(name, height), _start(start), _end(end) {
+ }
+
+ Rectangle::~Rectangle() {
+ }
+
+ std::string Rectangle::className() const {
+ return "Rectangle";
+ }
+
+ scalar Rectangle::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ if (fl::Op::isLt(x, _start) or fl::Op::isGt(x, _end))
+ return _height * 0.0;
+ return _height * 1.0;
+ }
+
+ std::string Rectangle::parameters() const {
+ return Op::join(2, " ", _start, _end) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Rectangle::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setStart(Op::toScalar(values.at(0)));
+ setEnd(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Rectangle::setStart(scalar minimum) {
+ this->_start = minimum;
+ }
+
+ scalar Rectangle::getStart() const {
+ return this->_start;
+ }
+
+ void Rectangle::setEnd(scalar maximum) {
+ this->_end = maximum;
+ }
+
+ scalar Rectangle::getEnd() const {
+ return this->_end;
+ }
+
+ Rectangle* Rectangle::clone() const {
+ return new Rectangle(*this);
+ }
+
+ Term* Rectangle::constructor() {
+ return new Rectangle;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/SShape.cpp b/fuzzylite/src/term/SShape.cpp
new file mode 100644
index 0000000..cd02be1
--- /dev/null
+++ b/fuzzylite/src/term/SShape.cpp
@@ -0,0 +1,102 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/SShape.h"
+
+namespace fl {
+
+ SShape::SShape(const std::string& name, scalar start, scalar end, scalar height)
+ : Term(name, height), _start(start), _end(end) {
+ }
+
+ SShape::~SShape() {
+ }
+
+ std::string SShape::className() const {
+ return "SShape";
+ }
+
+ scalar SShape::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ //from Octave smf.m
+ scalar average = (_start + _end) / 2.0;
+ scalar difference = _end - _start;
+
+ if (Op::isLE(x, _start)) return _height * 0.0;
+
+ if (Op::isLE(x, average))
+ return _height * (2.0 * std::pow((x - _start) / difference, 2));
+
+ if (Op::isLt(x, _end))
+ return _height * (1.0 - 2.0 * std::pow((x - _end) / difference, 2));
+
+ return _height * 1.0;
+ }
+
+ std::string SShape::parameters() const {
+ return Op::join(2, " ", _start, _end) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void SShape::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setStart(Op::toScalar(values.at(0)));
+ setEnd(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void SShape::setStart(scalar start) {
+ this->_start = start;
+ }
+
+ scalar SShape::getStart() const {
+ return this->_start;
+ }
+
+ void SShape::setEnd(scalar end) {
+ this->_end = end;
+ }
+
+ scalar SShape::getEnd() const {
+ return this->_end;
+ }
+
+ SShape* SShape::clone() const {
+ return new SShape(*this);
+ }
+
+ Term* SShape::constructor() {
+ return new SShape;
+ }
+
+}
diff --git a/fuzzylite/src/term/Sigmoid.cpp b/fuzzylite/src/term/Sigmoid.cpp
new file mode 100644
index 0000000..77d7f82
--- /dev/null
+++ b/fuzzylite/src/term/Sigmoid.cpp
@@ -0,0 +1,98 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Sigmoid.h"
+
+namespace fl {
+
+ Sigmoid::Sigmoid(const std::string& name, scalar inflection, scalar slope, scalar height)
+ : Term(name, height), _inflection(inflection), _slope(slope) {
+ }
+
+ Sigmoid::~Sigmoid() {
+ }
+
+ std::string Sigmoid::className() const {
+ return "Sigmoid";
+ }
+
+ scalar Sigmoid::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ return _height * 1.0 / (1.0 + std::exp(-_slope * (x - _inflection)));
+ }
+
+ std::string Sigmoid::parameters() const {
+ return Op::join(2, " ", _inflection, _slope) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Sigmoid::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setInflection(Op::toScalar(values.at(0)));
+ setSlope(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Sigmoid::setSlope(scalar a) {
+ this->_slope = a;
+ }
+
+ scalar Sigmoid::getSlope() const {
+ return this->_slope;
+ }
+
+ void Sigmoid::setInflection(scalar c) {
+ this->_inflection = c;
+ }
+
+ scalar Sigmoid::getInflection() const {
+ return this->_inflection;
+ }
+
+ Sigmoid::Direction Sigmoid::direction() const {
+ if (not fl::Op::isFinite(_slope) or fl::Op::isEq(_slope, 0.0)) return ZERO;
+
+ if (fl::Op::isGt(_slope, 0.0)) return POSITIVE;
+
+ return NEGATIVE;
+ }
+
+ Sigmoid* Sigmoid::clone() const {
+ return new Sigmoid(*this);
+ }
+
+ Term* Sigmoid::constructor() {
+ return new Sigmoid;
+ }
+
+}
diff --git a/fuzzylite/src/term/SigmoidDifference.cpp b/fuzzylite/src/term/SigmoidDifference.cpp
new file mode 100644
index 0000000..55a586c
--- /dev/null
+++ b/fuzzylite/src/term/SigmoidDifference.cpp
@@ -0,0 +1,114 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/SigmoidDifference.h"
+
+namespace fl {
+
+ SigmoidDifference::SigmoidDifference(const std::string& name,
+ scalar left, scalar rising,
+ scalar falling, scalar right, scalar height)
+ : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) {
+ }
+
+ SigmoidDifference::~SigmoidDifference() {
+ }
+
+ std::string SigmoidDifference::className() const {
+ return "SigmoidDifference";
+ }
+
+ scalar SigmoidDifference::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+
+ scalar a = 1.0 / (1 + std::exp(-_rising * (x - _left)));
+ scalar b = 1.0 / (1 + std::exp(-_falling * (x - _right)));
+ return _height * std::abs(a - b);
+ }
+
+ std::string SigmoidDifference::parameters() const {
+ return Op::join(4, " ", _left, _rising, _falling, _right) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void SigmoidDifference::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 4;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setLeft(Op::toScalar(values.at(0)));
+ setRising(Op::toScalar(values.at(1)));
+ setFalling(Op::toScalar(values.at(2)));
+ setRight(Op::toScalar(values.at(3)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void SigmoidDifference::setLeft(scalar leftInflection) {
+ this->_left = leftInflection;
+ }
+
+ scalar SigmoidDifference::getLeft() const {
+ return this->_left;
+ }
+
+ void SigmoidDifference::setRising(scalar risingSlope) {
+ this->_rising = risingSlope;
+ }
+
+ scalar SigmoidDifference::getRising() const {
+ return this->_rising;
+ }
+
+ void SigmoidDifference::setFalling(scalar fallingSlope) {
+ this->_falling = fallingSlope;
+ }
+
+ scalar SigmoidDifference::getFalling() const {
+ return this->_falling;
+ }
+
+ void SigmoidDifference::setRight(scalar rightInflection) {
+ this->_right = rightInflection;
+ }
+
+ scalar SigmoidDifference::getRight() const {
+ return this->_right;
+ }
+
+ SigmoidDifference* SigmoidDifference::clone() const {
+ return new SigmoidDifference(*this);
+ }
+
+ Term* SigmoidDifference::constructor() {
+ return new SigmoidDifference;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/SigmoidProduct.cpp b/fuzzylite/src/term/SigmoidProduct.cpp
new file mode 100644
index 0000000..9e43ac9
--- /dev/null
+++ b/fuzzylite/src/term/SigmoidProduct.cpp
@@ -0,0 +1,112 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/SigmoidProduct.h"
+
+namespace fl {
+
+ SigmoidProduct::SigmoidProduct(const std::string& name,
+ scalar left, scalar rising,
+ scalar falling, scalar right, scalar height)
+ : Term(name, height), _left(left), _rising(rising), _falling(falling), _right(right) {
+ }
+
+ SigmoidProduct::~SigmoidProduct() {
+ }
+
+ std::string SigmoidProduct::className() const {
+ return "SigmoidProduct";
+ }
+
+ scalar SigmoidProduct::membership(scalar x) const {
+ scalar a = 1.0 / (1 + std::exp(-_rising * (x - _left)));
+ scalar b = 1.0 / (1 + std::exp(-_falling * (x - _right)));
+ return _height * a * b;
+ }
+
+ std::string SigmoidProduct::parameters() const {
+ return Op::join(4, " ", _left, _rising, _falling, _right) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void SigmoidProduct::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 4;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setLeft(Op::toScalar(values.at(0)));
+ setRising(Op::toScalar(values.at(1)));
+ setFalling(Op::toScalar(values.at(2)));
+ setRight(Op::toScalar(values.at(3)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+
+ }
+
+ void SigmoidProduct::setRising(scalar risingSlope) {
+ this->_rising = risingSlope;
+ }
+
+ scalar SigmoidProduct::getRising() const {
+ return this->_rising;
+ }
+
+ void SigmoidProduct::setLeft(scalar leftInflection) {
+ this->_left = leftInflection;
+ }
+
+ scalar SigmoidProduct::getLeft() const {
+ return this->_left;
+ }
+
+ void SigmoidProduct::setRight(scalar rightInflection) {
+ this->_right = rightInflection;
+ }
+
+ scalar SigmoidProduct::getRight() const {
+ return this->_right;
+ }
+
+ void SigmoidProduct::setFalling(scalar fallingSlope) {
+ this->_falling = fallingSlope;
+ }
+
+ scalar SigmoidProduct::getFalling() const {
+ return this->_falling;
+ }
+
+ SigmoidProduct* SigmoidProduct::clone() const {
+ return new SigmoidProduct(*this);
+ }
+
+ Term* SigmoidProduct::constructor() {
+ return new SigmoidProduct;
+ }
+
+}
diff --git a/fuzzylite/src/term/Spike.cpp b/fuzzylite/src/term/Spike.cpp
new file mode 100644
index 0000000..f4b73bf
--- /dev/null
+++ b/fuzzylite/src/term/Spike.cpp
@@ -0,0 +1,90 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Spike.h"
+
+namespace fl {
+
+ Spike::Spike(const std::string& name, scalar center, scalar width, scalar height)
+ : Term(name, height), _center(center), _width(width) {
+ }
+
+ Spike::~Spike() {
+
+ }
+
+ std::string Spike::className() const {
+ return "Spike";
+ }
+
+ scalar Spike::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ return _height * std::exp(-std::fabs(10.0 / _width * (x - _center)));
+ }
+
+ std::string Spike::parameters() const {
+ return Op::join(2, " ", _center, _width) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Spike::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setCenter(Op::toScalar(values.at(0)));
+ setWidth(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Spike::setCenter(scalar center) {
+ this->_center = center;
+ }
+
+ scalar Spike::getCenter() const {
+ return this->_center;
+ }
+
+ void Spike::setWidth(scalar width) {
+ this->_width = width;
+ }
+
+ scalar Spike::getWidth() const {
+ return this->_width;
+ }
+
+ Spike* Spike::clone() const {
+ return new Spike(*this);
+ }
+
+ Term* Spike::constructor() {
+ return new Spike;
+ }
+}
diff --git a/fuzzylite/src/term/Term.cpp b/fuzzylite/src/term/Term.cpp
new file mode 100644
index 0000000..143a98f
--- /dev/null
+++ b/fuzzylite/src/term/Term.cpp
@@ -0,0 +1,74 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Term.h"
+
+#include "fl/imex/FllExporter.h"
+#include "fl/term/Linear.h"
+#include "fl/term/Function.h"
+
+namespace fl {
+
+ Term::Term(const std::string& name, scalar height) : _name(name), _height(height) {
+
+ }
+
+ Term::~Term() {
+
+ }
+
+ void Term::setName(const std::string& name) {
+ this->_name = name;
+ }
+
+ std::string Term::getName() const {
+ return this->_name;
+ }
+
+ void Term::setHeight(scalar height) {
+ this->_height = height;
+ }
+
+ scalar Term::getHeight() const {
+ return this->_height;
+ }
+
+ std::string Term::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ void Term::updateReference(Term* term, const Engine* engine) {
+ if (Linear * linear = dynamic_cast<Linear*> (term)) {
+ linear->setEngine(engine);
+ } else if (Function * function = dynamic_cast<Function*> (term)) {
+ function->setEngine(engine);
+ try {
+ function->load();
+ } catch (...) {
+ //ignore
+ }
+ }
+ }
+
+}
diff --git a/fuzzylite/src/term/Trapezoid.cpp b/fuzzylite/src/term/Trapezoid.cpp
new file mode 100644
index 0000000..60abcc4
--- /dev/null
+++ b/fuzzylite/src/term/Trapezoid.cpp
@@ -0,0 +1,130 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Trapezoid.h"
+
+namespace fl {
+
+ Trapezoid::Trapezoid(const std::string& name,
+ scalar vertexA, scalar vertexB, scalar vertexC, scalar vertexD, scalar height)
+ : Term(name, height), _vertexA(vertexA), _vertexB(vertexB), _vertexC(vertexC), _vertexD(vertexD) {
+ if (Op::isNaN(vertexC) and Op::isNaN(vertexD)) {
+ //TODO: Modify FLL to allow passing two parameters only.
+ this->_vertexD = _vertexB;
+ scalar range = _vertexD - _vertexA;
+ this->_vertexB = _vertexA + range * 1.0 / 5.0;
+ this->_vertexC = _vertexA + range * 4.0 / 5.0;
+ }
+ }
+
+ Trapezoid::~Trapezoid() {
+ }
+
+ std::string Trapezoid::className() const {
+ return "Trapezoid";
+ }
+
+ scalar Trapezoid::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+
+ if (Op::isLt(x, _vertexA) or Op::isGt(x, _vertexD))
+ return _height * 0.0;
+
+ if (Op::isLt(x, _vertexB))
+ return _height * Op::min(scalar(1.0), (x - _vertexA) / (_vertexB - _vertexA));
+
+ if (Op::isLE(x, _vertexC))
+ return _height * 1.0;
+
+ if (Op::isLt(x, _vertexD))
+ return _height * (_vertexD - x) / (_vertexD - _vertexC);
+
+ return _height * 0.0;
+ }
+
+ std::string Trapezoid::parameters() const {
+ return Op::join(4, " ", _vertexA, _vertexB, _vertexC, _vertexD)+
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Trapezoid::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 4;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setVertexA(Op::toScalar(values.at(0)));
+ setVertexB(Op::toScalar(values.at(1)));
+ setVertexC(Op::toScalar(values.at(2)));
+ setVertexD(Op::toScalar(values.at(3)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Trapezoid::setVertexA(scalar a) {
+ this->_vertexA = a;
+ }
+
+ scalar Trapezoid::getVertexA() const {
+ return this->_vertexA;
+ }
+
+ void Trapezoid::setVertexB(scalar b) {
+ this->_vertexB = b;
+ }
+
+ scalar Trapezoid::getVertexB() const {
+ return this->_vertexB;
+ }
+
+ void Trapezoid::setVertexC(scalar c) {
+ this->_vertexC = c;
+ }
+
+ scalar Trapezoid::getVertexC() const {
+ return this->_vertexC;
+ }
+
+ void Trapezoid::setVertexD(scalar d) {
+ this->_vertexD = d;
+ }
+
+ scalar Trapezoid::getVertexD() const {
+ return this->_vertexD;
+ }
+
+ Trapezoid* Trapezoid::clone() const {
+ return new Trapezoid(*this);
+ }
+
+ Term* Trapezoid::constructor() {
+ return new Trapezoid;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/Triangle.cpp b/fuzzylite/src/term/Triangle.cpp
new file mode 100644
index 0000000..2fce1a6
--- /dev/null
+++ b/fuzzylite/src/term/Triangle.cpp
@@ -0,0 +1,115 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/Triangle.h"
+
+namespace fl {
+
+ Triangle::Triangle(const std::string& name, scalar vertexA, scalar vertexB, scalar vertexC, scalar height)
+ : Term(name, height), _vertexA(vertexA), _vertexB(vertexB), _vertexC(vertexC) {
+ if (fl::Op::isNaN(vertexC)) {
+ //TODO: Modify FLL to allow passing two parameters only.
+ this->_vertexC = vertexB;
+ this->_vertexB = (vertexA + vertexB) / 2.0;
+ }
+ }
+
+ Triangle::~Triangle() {
+ }
+
+ std::string Triangle::className() const {
+ return "Triangle";
+ }
+
+ scalar Triangle::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+
+ if (Op::isLt(x, _vertexA) or Op::isGt(x, _vertexC))
+ return _height * 0.0;
+
+ if (Op::isEq(x, _vertexB))
+ return _height * 1.0;
+
+ if (Op::isLt(x, _vertexB))
+ return _height * (x - _vertexA) / (_vertexB - _vertexA);
+
+ return _height * (_vertexC - x) / (_vertexC - _vertexB);
+ }
+
+ std::string Triangle::parameters() const {
+ return Op::join(3, " ", _vertexA, _vertexB, _vertexC) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void Triangle::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 3;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setVertexA(Op::toScalar(values.at(0)));
+ setVertexB(Op::toScalar(values.at(1)));
+ setVertexC(Op::toScalar(values.at(2)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void Triangle::setVertexA(scalar a) {
+ this->_vertexA = a;
+ }
+
+ scalar Triangle::getVertexA() const {
+ return this->_vertexA;
+ }
+
+ void Triangle::setVertexB(scalar b) {
+ this->_vertexB = b;
+ }
+
+ scalar Triangle::getVertexB() const {
+ return this->_vertexB;
+ }
+
+ void Triangle::setVertexC(scalar c) {
+ this->_vertexC = c;
+ }
+
+ scalar Triangle::getVertexC() const {
+ return this->_vertexC;
+ }
+
+ Triangle* Triangle::clone() const {
+ return new Triangle(*this);
+ }
+
+ Term* Triangle::constructor() {
+ return new Triangle;
+ }
+
+
+}
diff --git a/fuzzylite/src/term/ZShape.cpp b/fuzzylite/src/term/ZShape.cpp
new file mode 100644
index 0000000..86668b6
--- /dev/null
+++ b/fuzzylite/src/term/ZShape.cpp
@@ -0,0 +1,102 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/term/ZShape.h"
+
+namespace fl {
+
+ ZShape::ZShape(const std::string& name, scalar start, scalar end, scalar height)
+ : Term(name, height), _start(start), _end(end) {
+ }
+
+ ZShape::~ZShape() {
+ }
+
+ std::string ZShape::className() const {
+ return "ZShape";
+ }
+
+ scalar ZShape::membership(scalar x) const {
+ if (fl::Op::isNaN(x)) return fl::nan;
+ //from Octave zmf.m
+ scalar average = (_start + _end) / 2;
+ scalar difference = _end - _start;
+
+ if (Op::isLE(x, _start)) return _height * 1.0;
+
+ if (Op::isLE(x, average))
+ return _height * (1.0 - 2.0 * std::pow((x - _start) / difference, 2));
+
+ if (Op::isLt(x, _end))
+ return _height * (2.0 * std::pow((x - _end) / difference, 2));
+
+ return _height * 0.0;
+ }
+
+ std::string ZShape::parameters() const {
+ return Op::join(2, " ", _start, _end) +
+ (not Op::isEq(_height, 1.0) ? " " + Op::str(_height) : "");
+ }
+
+ void ZShape::configure(const std::string& parameters) {
+ if (parameters.empty()) return;
+ std::vector<std::string> values = Op::split(parameters, " ");
+ std::size_t required = 2;
+ if (values.size() < required) {
+ std::ostringstream ex;
+ ex << "[configuration error] term <" << className() << ">"
+ << " requires <" << required << "> parameters";
+ throw fl::Exception(ex.str(), FL_AT);
+ }
+ setStart(Op::toScalar(values.at(0)));
+ setEnd(Op::toScalar(values.at(1)));
+ if (values.size() > required)
+ setHeight(Op::toScalar(values.at(required)));
+ }
+
+ void ZShape::setStart(scalar start) {
+ this->_start = start;
+ }
+
+ scalar ZShape::getStart() const {
+ return this->_start;
+ }
+
+ void ZShape::setEnd(scalar end) {
+ this->_end = end;
+ }
+
+ scalar ZShape::getEnd() const {
+ return this->_end;
+ }
+
+ ZShape* ZShape::clone() const {
+ return new ZShape(*this);
+ }
+
+ Term* ZShape::constructor() {
+ return new ZShape;
+ }
+
+}
diff --git a/fuzzylite/src/variable/InputVariable.cpp b/fuzzylite/src/variable/InputVariable.cpp
new file mode 100644
index 0000000..9fa2932
--- /dev/null
+++ b/fuzzylite/src/variable/InputVariable.cpp
@@ -0,0 +1,54 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/variable/InputVariable.h"
+
+#include "fl/imex/FllExporter.h"
+
+namespace fl {
+
+ InputVariable::InputVariable(const std::string& name, scalar minimum, scalar maximum)
+ : Variable(name, minimum, maximum), _inputValue(fl::nan) {
+ }
+
+ InputVariable::~InputVariable() {
+ }
+
+ void InputVariable::setInputValue(scalar inputValue) {
+ this->_inputValue = inputValue;
+ }
+
+ scalar InputVariable::getInputValue() const {
+ return this->_inputValue;
+ }
+
+ std::string InputVariable::fuzzyInputValue() const {
+ return fuzzify(_inputValue);
+ }
+
+ std::string InputVariable::toString() const {
+ return FllExporter().toString(this);
+ }
+
+}
diff --git a/fuzzylite/src/variable/OutputVariable.cpp b/fuzzylite/src/variable/OutputVariable.cpp
new file mode 100644
index 0000000..6e1d906
--- /dev/null
+++ b/fuzzylite/src/variable/OutputVariable.cpp
@@ -0,0 +1,195 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/variable/OutputVariable.h"
+
+#include "fl/defuzzifier/Defuzzifier.h"
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/SNorm.h"
+#include "fl/term/Accumulated.h"
+#include "fl/term/Activated.h"
+
+namespace fl {
+
+ OutputVariable::OutputVariable(const std::string& name,
+ scalar minimum, scalar maximum)
+ : Variable(name, minimum, maximum),
+ _fuzzyOutput(new Accumulated(name, minimum, maximum)), _outputValue(fl::nan),
+ _previousOutputValue(fl::nan), _defaultValue(fl::nan),
+ _lockOutputValueInRange(false), _lockPreviousOutputValue(false) {
+ }
+
+ OutputVariable::OutputVariable(const OutputVariable& other) : Variable(other) {
+ copyFrom(other);
+ }
+
+ OutputVariable& OutputVariable::operator=(const OutputVariable& other) {
+ if (this != &other) {
+ _fuzzyOutput.reset(fl::null);
+ _defuzzifier.reset(fl::null);
+
+ Variable::operator=(other);
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ OutputVariable::~OutputVariable() {
+ }
+
+ void OutputVariable::copyFrom(const OutputVariable& other) {
+ _fuzzyOutput.reset(other._fuzzyOutput->clone());
+ if (other._defuzzifier.get()) _defuzzifier.reset(other._defuzzifier->clone());
+ _outputValue = other._outputValue;
+ _previousOutputValue = other._previousOutputValue;
+ _defaultValue = other._defaultValue;
+ _lockOutputValueInRange = other._lockOutputValueInRange;
+ _lockPreviousOutputValue = other._lockPreviousOutputValue;
+ }
+
+ void OutputVariable::setName(const std::string& name) {
+ Variable::setName(name);
+ this->_fuzzyOutput->setName(name);
+ }
+
+ Accumulated* OutputVariable::fuzzyOutput() const {
+ return this->_fuzzyOutput.get();
+ }
+
+ void OutputVariable::setMinimum(scalar minimum) {
+ Variable::setMinimum(minimum);
+ this->_fuzzyOutput->setMinimum(minimum);
+ }
+
+ void OutputVariable::setMaximum(scalar maximum) {
+ Variable::setMaximum(maximum);
+ this->_fuzzyOutput->setMaximum(maximum);
+ }
+
+ void OutputVariable::setDefuzzifier(Defuzzifier* defuzzifier) {
+ this->_defuzzifier.reset(defuzzifier);
+ }
+
+ Defuzzifier* OutputVariable::getDefuzzifier() const {
+ return this->_defuzzifier.get();
+ }
+
+ void OutputVariable::setOutputValue(scalar outputValue) {
+ this->_outputValue = outputValue;
+ }
+
+ scalar OutputVariable::getOutputValue() const {
+ return this->_outputValue;
+ }
+
+ void OutputVariable::setPreviousOutputValue(scalar previousOutputValue) {
+ this->_previousOutputValue = previousOutputValue;
+ }
+
+ scalar OutputVariable::getPreviousOutputValue() const {
+ return this->_previousOutputValue;
+ }
+
+ void OutputVariable::setDefaultValue(scalar defaultValue) {
+ this->_defaultValue = defaultValue;
+ }
+
+ scalar OutputVariable::getDefaultValue() const {
+ return this->_defaultValue;
+ }
+
+ void OutputVariable::setLockOutputValueInRange(bool lockOutputValueInRange) {
+ this->_lockOutputValueInRange = lockOutputValueInRange;
+ }
+
+ bool OutputVariable::isLockedOutputValueInRange() const {
+ return this->_lockOutputValueInRange;
+ }
+
+ void OutputVariable::setLockPreviousOutputValue(bool lockPreviousOutputValue) {
+ this->_lockPreviousOutputValue = lockPreviousOutputValue;
+ }
+
+ bool OutputVariable::isLockedPreviousOutputValue() const {
+ return this->_lockPreviousOutputValue;
+ }
+
+ void OutputVariable::defuzzify() {
+ if (fl::Op::isFinite(this->_outputValue)) {
+ this->_previousOutputValue = this->_outputValue;
+ }
+
+ scalar result = fl::nan;
+ bool isValid = this->_enabled and not this->_fuzzyOutput->isEmpty();
+ if (isValid) {
+ if (not _defuzzifier.get()) {
+ throw fl::Exception("[defuzzifier error] "
+ "defuzzifier needed to defuzzify output variable <" + _name + ">", FL_AT);
+ }
+ result = this->_defuzzifier->defuzzify(this->_fuzzyOutput.get(), _minimum, _maximum);
+ } else {
+ //if a previous defuzzification was successfully performed and
+ //and the output value is supposed not to change when the output is empty
+ if (_lockPreviousOutputValue and not Op::isNaN(_previousOutputValue)) {
+ result = _previousOutputValue;
+ } else {
+ result = _defaultValue;
+ }
+ }
+
+ if (_lockOutputValueInRange) {
+ result = fl::Op::bound(result, _minimum, _maximum);
+ }
+
+ this->_outputValue = result;
+ }
+
+ std::string OutputVariable::fuzzyOutputValue() const {
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ scalar degree = _fuzzyOutput->activationDegree(_terms.at(i));
+ if (i == 0) {
+ ss << fl::Op::str(degree);
+ } else {
+ if (fl::Op::isNaN(degree) or fl::Op::isGE(degree, 0.0))
+ ss << " + " << fl::Op::str(degree);
+ else
+ ss << " - " << fl::Op::str(std::fabs(degree));
+ }
+ ss << "/" << _terms.at(i)->getName();
+ }
+ return ss.str();
+ }
+
+ void OutputVariable::clear() {
+ _fuzzyOutput->clear();
+ setPreviousOutputValue(fl::nan);
+ setOutputValue(fl::nan);
+ }
+
+ std::string OutputVariable::toString() const {
+ return FllExporter().toString(this);
+ }
+
+}
diff --git a/fuzzylite/src/variable/Variable.cpp b/fuzzylite/src/variable/Variable.cpp
new file mode 100644
index 0000000..37cf25c
--- /dev/null
+++ b/fuzzylite/src/variable/Variable.cpp
@@ -0,0 +1,244 @@
+/*
+ Author: Juan Rada-Vilela, Ph.D.
+ Copyright (C) 2010-2014 FuzzyLite Limited
+ All rights reserved
+
+ This file is part of fuzzylite.
+
+ fuzzylite is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ fuzzylite is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with fuzzylite. If not, see <http://www.gnu.org/licenses/>.
+
+ fuzzylite™ is a trademark of FuzzyLite Limited.
+
+ */
+
+#include "fl/variable/Variable.h"
+
+#include "fl/defuzzifier/Centroid.h"
+#include "fl/imex/FllExporter.h"
+#include "fl/norm/Norm.h"
+#include "fl/term/Constant.h"
+#include "fl/term/Linear.h"
+#include "fl/term/Term.h"
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+
+namespace fl {
+
+ Variable::Variable(const std::string& name, scalar minimum, scalar maximum)
+ : _name(name), _minimum(minimum), _maximum(maximum), _enabled(true) {
+ }
+
+ Variable::Variable(const Variable& other) {
+ copyFrom(other);
+ }
+
+ Variable& Variable::operator=(const Variable& other) {
+ if (this != &other) {
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ delete _terms.at(i);
+ }
+ _terms.clear();
+ copyFrom(other);
+ }
+ return *this;
+ }
+
+ void Variable::copyFrom(const Variable& other) {
+ _name = other._name;
+ _enabled = other._enabled;
+ _minimum = other._minimum;
+ _maximum = other._maximum;
+ for (std::size_t i = 0; i < other._terms.size(); ++i) {
+ _terms.push_back(other._terms.at(i)->clone());
+ }
+ }
+
+ Variable::~Variable() {
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ delete _terms.at(i);
+ }
+ }
+
+ void Variable::setName(const std::string& name) {
+ this->_name = name;
+ }
+
+ std::string Variable::getName() const {
+ return this->_name;
+ }
+
+ void Variable::setRange(scalar minimum, scalar maximum) {
+ setMinimum(minimum);
+ setMaximum(maximum);
+ }
+
+ scalar Variable::range() const {
+ return this->_maximum - this->_minimum;
+ }
+
+ void Variable::setMinimum(scalar minimum) {
+ this->_minimum = minimum;
+ }
+
+ scalar Variable::getMinimum() const {
+ return this->_minimum;
+ }
+
+ void Variable::setMaximum(scalar maximum) {
+ this->_maximum = maximum;
+ }
+
+ scalar Variable::getMaximum() const {
+ return this->_maximum;
+ }
+
+ void Variable::setEnabled(bool enabled) {
+ this->_enabled = enabled;
+ }
+
+ bool Variable::isEnabled() const {
+ return this->_enabled;
+ }
+
+ std::string Variable::fuzzify(scalar x) const {
+ std::ostringstream ss;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ scalar fx = fl::nan;
+ try {
+ fx = _terms.at(i)->membership(x);
+ } catch (...) {
+ //ignore
+ }
+ if (i == 0) {
+ ss << fl::Op::str(fx);
+ } else {
+ if (fl::Op::isNaN(fx) or fl::Op::isGE(fx, 0.0))
+ ss << " + " << fl::Op::str(fx);
+ else
+ ss << " - " << fl::Op::str(std::fabs(fx));
+ }
+ ss << "/" << _terms.at(i)->getName();
+ }
+ return ss.str();
+ }
+
+ Term* Variable::highestMembership(scalar x, scalar* yhighest) const {
+ Term* result = fl::null;
+ scalar ymax = 0.0;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ scalar y = fl::nan;
+ try {
+ y = _terms.at(i)->membership(x);
+ } catch (...) {
+ //ignore
+ }
+ if (fl::Op::isGt(y, ymax)) {
+ ymax = y;
+ result = _terms.at(i);
+ }
+ }
+ if (yhighest) *yhighest = ymax;
+ return result;
+ }
+
+ std::string Variable::toString() const {
+ return FllExporter().toString(this);
+ }
+
+ /**
+ * Operations for datatype _terms
+ */
+
+ struct SortByCoG {
+ std::map<const Term*, scalar> centroids;
+
+ bool operator()(const Term* a, const Term * b) {
+ return fl::Op::isLt(
+ centroids.find(a)->second,
+ centroids.find(b)->second);
+ }
+ };
+
+ void Variable::sort() {
+ Centroid defuzzifier;
+ std::map<const Term*, scalar> centroids;
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ Term* term = _terms.at(i);
+ try {
+ if (dynamic_cast<const Constant*> (term) or dynamic_cast<const Linear*> (term)) {
+ centroids[term] = term->membership(0);
+ } else {
+ centroids[term] = defuzzifier.defuzzify(term, _minimum, _maximum);
+ }
+ } catch (...) { //ignore error possibly due to Function not loaded
+ centroids[term] = fl::inf;
+ }
+ }
+ SortByCoG criterion;
+ criterion.centroids = centroids;
+ std::sort(_terms.begin(), _terms.end(), criterion);
+ }
+
+ void Variable::addTerm(Term* term) {
+ this->_terms.push_back(term);
+ }
+
+ void Variable::insertTerm(Term* term, int index) {
+ this->_terms.insert(this->_terms.begin() + index, term);
+ }
+
+ Term* Variable::getTerm(int index) const {
+ return this->_terms.at(index);
+ }
+
+ Term* Variable::getTerm(const std::string& name) const {
+ for (std::size_t i = 0; i < _terms.size(); ++i) {
+ if (_terms.at(i)->getName() == name) {
+ return _terms.at(i);
+ }
+ }
+ throw fl::Exception("[variable error] term <" + name + "> "
+ "not found in variable <" + this->_name + ">", FL_AT);
+ }
+
+ bool Variable::hasTerm(const std::string& name) const {
+ return getTerm(name) != fl::null;
+ }
+
+ Term* Variable::removeTerm(int index) {
+ Term* result = this->_terms.at(index);
+ this->_terms.erase(this->_terms.begin() + index);
+ return result;
+ }
+
+ int Variable::numberOfTerms() const {
+ return this->_terms.size();
+ }
+
+ const std::vector<Term*>& Variable::terms() const {
+ return this->_terms;
+ }
+
+ void Variable::setTerms(const std::vector<Term*>& terms) {
+ this->_terms = terms;
+ }
+
+ std::vector<Term*>& Variable::terms() {
+ return this->_terms;
+ }
+
+}
+