diff options
Diffstat (limited to 'fuzzylite/src/Console.cpp')
-rw-r--r-- | fuzzylite/src/Console.cpp | 809 |
1 files changed, 506 insertions, 303 deletions
diff --git a/fuzzylite/src/Console.cpp b/fuzzylite/src/Console.cpp index 3a55fc5..5a00c50 100644 --- a/fuzzylite/src/Console.cpp +++ b/fuzzylite/src/Console.cpp @@ -1,37 +1,24 @@ /* - Author: Juan Rada-Vilela, Ph.D. - Copyright (C) 2010-2014 FuzzyLite Limited - All rights reserved + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> This file is part of fuzzylite. fuzzylite is free software: you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. + the terms of the FuzzyLite License included with the software. - fuzzylite is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - for more details. - - You should have received a copy of the GNU Lesser General Public License - along with fuzzylite. If not, see <http://www.gnu.org/licenses/>. - - fuzzyliteâ„¢ is a trademark of FuzzyLite Limited. + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + fuzzylite is a registered trademark of FuzzyLite Limited. */ #include "fl/Console.h" #include "fl/Headers.h" -#include <algorithm> -#include <cctype> #include <fstream> -#include <stdlib.h> -#include <utility> -#include <vector> #ifdef FL_UNIX #include <termios.h> @@ -40,9 +27,6 @@ #include <conio.h> #endif -#ifdef FL_CPP11 -#include <chrono> -#endif namespace fl { const std::string Console::KW_INPUT_FILE = "-i"; @@ -51,11 +35,16 @@ namespace fl { const std::string Console::KW_OUTPUT_FORMAT = "-of"; const std::string Console::KW_EXAMPLE = "-example"; const std::string Console::KW_DECIMALS = "-decimals"; - const std::string Console::KW_DATA_INPUT = "-d"; - const std::string Console::KW_DATA_MAXIMUM = "-dmaximum"; + const std::string Console::KW_DATA_INPUT_FILE = "-d"; + const std::string Console::KW_DATA_VALUES = "-values"; + const std::string Console::KW_DATA_VALUES_SCOPE = "-scope"; + const std::string Console::KW_DATA_EXPORT_HEADER = "-dheader"; const std::string Console::KW_DATA_EXPORT_INPUTS = "-dinputs"; + Console::Option::Option(const std::string& key, const std::string& value, const std::string& description) : + key(key), value(value), description(description) { } + std::vector<Console::Option> Console::availableOptions() { std::vector<Console::Option> options; options.push_back(Option(KW_INPUT_FILE, "inputfile", "file to import your engine from")); @@ -64,8 +53,9 @@ namespace fl { options.push_back(Option(KW_OUTPUT_FORMAT, "format", "format of the file to export (fll | fld | cpp | java | fis | fcl)")); options.push_back(Option(KW_EXAMPLE, "letter", "if not inputfile, built-in example to use as engine: (m)amdani or (t)akagi-sugeno")); options.push_back(Option(KW_DECIMALS, "number", "number of decimals to write floating-poing values")); - options.push_back(Option(KW_DATA_INPUT, "datafile", "if exporting to fld, file of input values to evaluate your engine on")); - options.push_back(Option(KW_DATA_MAXIMUM, "number", "if exporting to fld without datafile, maximum number of results to export")); + options.push_back(Option(KW_DATA_INPUT_FILE, "file", "if exporting to fld, FLD file of input values to evaluate your engine on")); + options.push_back(Option(KW_DATA_VALUES, "number", "if exporting to fld without datafile, number of results to export within scope (default: EachVariable)")); + options.push_back(Option(KW_DATA_VALUES_SCOPE, "scope", "if exporting to fld without datafile, scope of " + KW_DATA_VALUES + ": [EachVariable|AllVariables]")); options.push_back(Option(KW_DATA_EXPORT_HEADER, "boolean", "if true and exporting to fld, include headers")); options.push_back(Option(KW_DATA_EXPORT_INPUTS, "boolean", "if true and exporting to fld, include input values")); return options; @@ -74,14 +64,16 @@ namespace fl { std::string Console::usage() { std::vector<Console::Option> options = availableOptions(); std::ostringstream ss; - + ss << "========================================\n"; ss << "fuzzylite: a fuzzy logic control library\n"; - ss << "version: " << fuzzylite::longVersion() << "\n"; + ss << "version: " << fuzzylite::version() << "\n"; ss << "author: " << fuzzylite::author() << "\n"; ss << "license: " << fuzzylite::license() << "\n"; ss << "========================================\n\n"; ss << "usage: fuzzylite inputfile outputfile\n"; + ss << " or: fuzzylite benchmark engine.fll input.fld runs [output.tsv]\n"; + ss << " or: fuzzylite benchmarks fllFiles.txt fldFiles.txt runs [output.tsv]\n"; ss << " or: fuzzylite "; for (std::size_t i = 0; i < options.size(); ++i) { ss << "[" << options.at(i).key << " " << options.at(i).value << "] "; @@ -103,15 +95,15 @@ namespace fl { ss << "\n"; ss << "Visit " << fuzzylite::website() << " for more information.\n\n"; - ss << "Copyright (C) 2010-2015 FuzzyLite Limited.\n"; + ss << "Copyright (C) 2010-2017 by FuzzyLite Limited.\n"; ss << "All rights reserved."; - + return ss.str(); } - std::map<std::string, std::string> Console::parse(int argc, char** argv) { + std::map<std::string, std::string> Console::parse(int argc, const char* argv[]) { if ((argc - 1) % 2 != 0) { - throw fl::Exception("[option error] incomplete number of parameters [key value]", FL_AT); + throw Exception("[option error] incomplete number of parameters [key value]", FL_AT); } std::map<std::string, std::string> options; for (int i = 1; i < argc - 1; i += 2) { @@ -139,7 +131,7 @@ namespace fl { } } if (not isValid) { - throw fl::Exception("[option error] option <" + it->first + "> not recognized", FL_AT); + throw Exception("[option error] option <" + it->first + "> not recognized", FL_AT); } } } @@ -151,7 +143,7 @@ namespace fl { it = options.find(KW_DECIMALS); if (it != options.end()) { - fl::fuzzylite::setDecimals((int) fl::Op::toScalar(it->second)); + fuzzylite::setDecimals((int) Op::toScalar(it->second)); } std::string example; @@ -170,7 +162,7 @@ namespace fl { } else if (example == "t" or example == "ts" or example == "takagi-sugeno") { engine = takagiSugeno(); } else { - throw fl::Exception("[option error] example <" + example + "> not available", FL_AT); + throw Exception("[option error] example <" + example + "> not available", FL_AT); } inputFormat = "fll"; textEngine << FllExporter().toString(engine); @@ -179,12 +171,12 @@ namespace fl { } else { it = options.find(KW_INPUT_FILE); if (it == options.end()) { - throw fl::Exception("[option error] no input file specified", FL_AT); + throw Exception("[option error] no input file specified", FL_AT); } std::string inputFilename = it->second; std::ifstream inputFile(inputFilename.c_str()); if (not inputFile.is_open()) { - throw fl::Exception("[file error] file <" + inputFilename + "> could not be opened", FL_AT); + throw Exception("[file error] file <" + inputFilename + "> could not be opened", FL_AT); } std::string line; while (std::getline(inputFile, line)) { @@ -200,7 +192,7 @@ namespace fl { if (extensionIndex != std::string::npos) { inputFormat = inputFilename.substr(extensionIndex + 1); } else { - throw fl::Exception("[format error] unspecified format of input file", FL_AT); + throw Exception("[format error] unspecified format of input file", FL_AT); } } } @@ -220,7 +212,7 @@ namespace fl { if (extensionIndex != std::string::npos) { outputFormat = outputFilename.substr(extensionIndex + 1); } else { - throw fl::Exception("[format error] unspecified format of output file", FL_AT); + throw Exception("[format error] unspecified format of output file", FL_AT); } } @@ -230,7 +222,7 @@ namespace fl { } else { std::ofstream writer(outputFilename.c_str()); if (not writer.is_open()) { - throw fl::Exception("[file error] file <" + outputFilename + "> could not be created", FL_AT); + throw Exception("[file error] file <" + outputFilename + "> could not be created", FL_AT); } process(textEngine.str(), writer, inputFormat, outputFormat, options); writer.flush(); @@ -252,7 +244,7 @@ namespace fl { } else if ("fis" == inputFormat) { importer.reset(new FisImporter); } else { - throw fl::Exception("[import error] format <" + inputFormat + "> " + throw Exception("[import error] format <" + inputFormat + "> " "not supported", FL_AT); } @@ -262,7 +254,7 @@ namespace fl { std::map<std::string, std::string>::const_iterator it; FldExporter fldExporter; - fldExporter.setSeparator("\t"); + fldExporter.setSeparator(" "); bool exportHeaders = true; if ((it = options.find(KW_DATA_EXPORT_HEADER)) != options.end()) { exportHeaders = ("true" == it->second); @@ -273,22 +265,32 @@ namespace fl { exportInputValues = ("true" == it->second); } fldExporter.setExportInputValues(exportInputValues); - if ((it = options.find(KW_DATA_INPUT)) != options.end()) { + if ((it = options.find(KW_DATA_INPUT_FILE)) != options.end()) { std::ifstream dataFile(it->second.c_str()); if (not dataFile.is_open()) { - throw fl::Exception("[export error] file <" + it->second + "> could not be opened", FL_AT); + throw Exception("[export error] file <" + it->second + "> could not be opened", FL_AT); } try { fldExporter.write(engine.get(), writer, dataFile); } catch (std::exception& ex) { - (void) ex; + FL_IUNUSED(ex); dataFile.close(); throw; } } else { - if ((it = options.find(KW_DATA_MAXIMUM)) != options.end()) { - fldExporter.write(engine.get(), writer, (int) fl::Op::toScalar(it->second)); + if ((it = options.find(KW_DATA_VALUES)) != options.end()) { + int values = (int) Op::toScalar(it->second); + FldExporter::ScopeOfValues scope = FldExporter::EachVariable; + if ((it = options.find(KW_DATA_VALUES_SCOPE)) != options.end()) { + if ("AllVariables" == it->second) + scope = FldExporter::AllVariables; + else if ("EachVariable" == it->second) + scope = FldExporter::EachVariable; + else throw Exception("[export error] unknown scope of values <" + + it->second + ">", FL_AT); + } + fldExporter.write(engine.get(), writer, values, scope); } else { std::ostringstream buffer; buffer << "#FuzzyLite Interactive Console (press H for help)\n"; @@ -311,7 +313,7 @@ namespace fl { exporter.reset(new CppExporter); } else if ("java" == outputFormat) { exporter.reset(new JavaExporter); - } else throw fl::Exception("[export error] format <" + outputFormat + "> " + } else throw Exception("[export error] format <" + outputFormat + "> " "not supported", FL_AT); writer << exporter->toString(engine.get()); } @@ -321,14 +323,14 @@ namespace fl { int ch = 0; #ifdef FL_UNIX struct termios oldt, newt; - tcgetattr(STDIN_FILENO, &oldt); + ::tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - ch = getchar(); - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + ::tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ch = ::getchar(); + ::tcsetattr(STDIN_FILENO, TCSANOW, &oldt); #elif defined(FL_WINDOWS) - ch = _getch(); + ch = ::_getch(); #endif return ch; } @@ -349,13 +351,17 @@ namespace fl { ch = readCharacter(); + if (ch == EOF) { + break; + } + if (std::isspace(ch)) { - scalar value = engine->getInputVariable(inputValues.size())->getInputValue(); + scalar value = engine->getInputVariable(inputValues.size())->getValue(); try { - value = fl::Op::toScalar(inputValue.str()); + value = Op::toScalar(inputValue.str()); } catch (std::exception& ex) { - (void) ex; - buffer << "[" << fl::Op::str(value) << "]"; + FL_IUNUSED(ex); + buffer << "[" << Op::str(value) << "]"; } buffer << space; inputValue.str(""); @@ -375,7 +381,7 @@ namespace fl { case 'r': case 'R': engine->restart(); buffer << "#[Restart]"; - //fall through + continue; //fall through case 'd': case 'D': inputValues.clear(); buffer << "#[Discard]\n>"; @@ -388,15 +394,15 @@ namespace fl { for (std::size_t i = 0; i < inputValues.size(); ++i) { InputVariable* inputVariable = engine->inputVariables().at(i); - inputVariable->setInputValue(inputValues.at(i)); + inputVariable->setValue(inputValues.at(i)); } std::vector<scalar> missingInputs; for (std::size_t i = inputValues.size(); i < engine->inputVariables().size(); ++i) { InputVariable* inputVariable = engine->inputVariables().at(i); - missingInputs.push_back(inputVariable->getInputValue()); + missingInputs.push_back(inputVariable->getValue()); } inputValues.clear(); - buffer << fl::Op::join(missingInputs, space); + buffer << Op::join(missingInputs, space); if (not missingInputs.empty()) buffer << space; buffer << "=" << space; try { @@ -405,9 +411,9 @@ namespace fl { for (std::size_t i = 0; i < engine->outputVariables().size(); ++i) { OutputVariable* outputVariable = engine->outputVariables().at(i); outputVariable->defuzzify(); - outputValues.push_back(outputVariable->getOutputValue()); + outputValues.push_back(outputVariable->getValue()); } - buffer << fl::Op::join(outputValues, space) << "\n>"; + buffer << Op::join(outputValues, space) << "\n>"; } catch (std::exception& ex) { buffer << "#[Error: " << ex.what() << "]"; @@ -422,7 +428,7 @@ namespace fl { inputValue.str(""); break; } - } while (not (ch == 'Q' or ch == 'q')); + } while (not (ch == 'Q' or ch == 'q' or ch == 4)); writer << std::endl; } @@ -439,145 +445,283 @@ namespace fl { } Engine* Console::mamdani() { - Engine* engine = new Engine("simple-dimmer"); - - InputVariable* ambient = new InputVariable("Ambient", 0, 1); - ambient->addTerm(new Triangle("DARK", .0, .25, .5)); - ambient->addTerm(new Triangle("MEDIUM", .25, .5, .75)); - ambient->addTerm(new Triangle("BRIGHT", .5, .75, 1)); + Engine* engine = new Engine; + engine->setName("simple-dimmer"); + engine->setDescription(""); + + InputVariable* ambient = new InputVariable; + ambient->setName("ambient"); + ambient->setDescription(""); + ambient->setEnabled(true); + ambient->setRange(0.000, 1.000); + ambient->setLockValueInRange(false); + ambient->addTerm(new Triangle("DARK", 0.000, 0.250, 0.500)); + ambient->addTerm(new Triangle("MEDIUM", 0.250, 0.500, 0.750)); + ambient->addTerm(new Triangle("BRIGHT", 0.500, 0.750, 1.000)); engine->addInputVariable(ambient); - - OutputVariable* power = new OutputVariable("Power", 0, 2); + OutputVariable* power = new OutputVariable; + power->setName("power"); + power->setDescription(""); + power->setEnabled(true); + power->setRange(0.000, 2.000); + power->setLockValueInRange(false); + power->setAggregation(new Maximum); + power->setDefuzzifier(new Centroid(200)); power->setDefaultValue(fl::nan); - power->addTerm(new Triangle("LOW", 0.0, 0.5, 1)); - power->addTerm(new Triangle("MEDIUM", 0.5, 1, 1.5)); - power->addTerm(new Triangle("HIGH", 1, 1.5, 2)); + power->setLockPreviousValue(false); + power->addTerm(new Triangle("LOW", 0.000, 0.500, 1.000)); + power->addTerm(new Triangle("MEDIUM", 0.500, 1.000, 1.500)); + power->addTerm(new Triangle("HIGH", 1.000, 1.500, 2.000)); engine->addOutputVariable(power); - RuleBlock* ruleblock = new RuleBlock(); - ruleblock->addRule(Rule::parse("if Ambient is DARK then Power is HIGH", engine)); - ruleblock->addRule(Rule::parse("if Ambient is MEDIUM then Power is MEDIUM", engine)); - ruleblock->addRule(Rule::parse("if Ambient is BRIGHT then Power is LOW", engine)); - - engine->addRuleBlock(ruleblock); - - engine->configure("", "", "Minimum", "Maximum", "Centroid"); + RuleBlock* ruleBlock = new RuleBlock; + ruleBlock->setName(""); + ruleBlock->setDescription(""); + ruleBlock->setEnabled(true); + ruleBlock->setConjunction(fl::null); + ruleBlock->setDisjunction(fl::null); + ruleBlock->setImplication(new Minimum); + ruleBlock->setActivation(new General); + ruleBlock->addRule(Rule::parse("if ambient is DARK then power is HIGH", engine)); + ruleBlock->addRule(Rule::parse("if ambient is MEDIUM then power is MEDIUM", engine)); + ruleBlock->addRule(Rule::parse("if ambient is BRIGHT then power is LOW", engine)); + engine->addRuleBlock(ruleBlock); return engine; } Engine* Console::takagiSugeno() { - Engine* engine = new Engine("approximation of sin(x)/x"); - - fl::InputVariable* inputX = new fl::InputVariable("inputX"); - inputX->setRange(0, 10); - inputX->addTerm(new fl::Triangle("NEAR_1", 0, 1, 2)); - inputX->addTerm(new fl::Triangle("NEAR_2", 1, 2, 3)); - inputX->addTerm(new fl::Triangle("NEAR_3", 2, 3, 4)); - inputX->addTerm(new fl::Triangle("NEAR_4", 3, 4, 5)); - inputX->addTerm(new fl::Triangle("NEAR_5", 4, 5, 6)); - inputX->addTerm(new fl::Triangle("NEAR_6", 5, 6, 7)); - inputX->addTerm(new fl::Triangle("NEAR_7", 6, 7, 8)); - inputX->addTerm(new fl::Triangle("NEAR_8", 7, 8, 9)); - inputX->addTerm(new fl::Triangle("NEAR_9", 8, 9, 10)); + Engine* engine = new Engine; + engine->setName("approximation"); + engine->setDescription("approximation of sin(x)/x"); + + InputVariable* inputX = new InputVariable; + inputX->setName("inputX"); + inputX->setDescription("value of x"); + inputX->setEnabled(true); + inputX->setRange(0.000, 10.000); + inputX->setLockValueInRange(false); + inputX->addTerm(new Triangle("NEAR_1", 0.000, 1.000, 2.000)); + inputX->addTerm(new Triangle("NEAR_2", 1.000, 2.000, 3.000)); + inputX->addTerm(new Triangle("NEAR_3", 2.000, 3.000, 4.000)); + inputX->addTerm(new Triangle("NEAR_4", 3.000, 4.000, 5.000)); + inputX->addTerm(new Triangle("NEAR_5", 4.000, 5.000, 6.000)); + inputX->addTerm(new Triangle("NEAR_6", 5.000, 6.000, 7.000)); + inputX->addTerm(new Triangle("NEAR_7", 6.000, 7.000, 8.000)); + inputX->addTerm(new Triangle("NEAR_8", 7.000, 8.000, 9.000)); + inputX->addTerm(new Triangle("NEAR_9", 8.000, 9.000, 10.000)); engine->addInputVariable(inputX); - - fl::OutputVariable* outputFx = new fl::OutputVariable("outputFx"); - outputFx->setRange(-1, 1); + OutputVariable* outputFx = new OutputVariable; + outputFx->setName("outputFx"); + outputFx->setDescription("value of the approximation of x"); + outputFx->setEnabled(true); + outputFx->setRange(-1.000, 1.000); + outputFx->setLockValueInRange(false); + outputFx->setAggregation(fl::null); + outputFx->setDefuzzifier(new WeightedAverage("Automatic")); outputFx->setDefaultValue(fl::nan); - outputFx->setLockPreviousOutputValue(true); //To use its value with diffFx - outputFx->addTerm(new Constant("f1", 0.84)); - outputFx->addTerm(new Constant("f2", 0.45)); - outputFx->addTerm(new Constant("f3", 0.04)); - outputFx->addTerm(new Constant("f4", -0.18)); - outputFx->addTerm(new Constant("f5", -0.19)); - outputFx->addTerm(new Constant("f6", -0.04)); - outputFx->addTerm(new Constant("f7", 0.09)); - outputFx->addTerm(new Constant("f8", 0.12)); - outputFx->addTerm(new Constant("f9", 0.04)); + outputFx->setLockPreviousValue(true); + outputFx->addTerm(new Constant("f1", 0.840)); + outputFx->addTerm(new Constant("f2", 0.450)); + outputFx->addTerm(new Constant("f3", 0.040)); + outputFx->addTerm(new Constant("f4", -0.180)); + outputFx->addTerm(new Constant("f5", -0.190)); + outputFx->addTerm(new Constant("f6", -0.040)); + outputFx->addTerm(new Constant("f7", 0.090)); + outputFx->addTerm(new Constant("f8", 0.120)); + outputFx->addTerm(new Constant("f9", 0.040)); engine->addOutputVariable(outputFx); - fl::OutputVariable* trueFx = new fl::OutputVariable("trueFx"); - trueFx->setRange(fl::nan, fl::nan); - trueFx->setLockPreviousOutputValue(true); //To use its value with diffFx - trueFx->addTerm(fl::Function::create("fx", "sin(inputX)/inputX", engine)); - engine->addOutputVariable(trueFx); - - fl::OutputVariable* diffFx = new fl::OutputVariable("diffFx"); - diffFx->addTerm(fl::Function::create("diff", "fabs(outputFx-trueFx)", engine)); - diffFx->setRange(fl::nan, fl::nan); - // diffFx->setLockValidOutput(true); //To use in input diffPreviousFx - engine->addOutputVariable(diffFx); - - fl::RuleBlock* block = new fl::RuleBlock(); - block->addRule(fl::Rule::parse("if inputX is NEAR_1 then outputFx is f1", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_2 then outputFx is f2", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_3 then outputFx is f3", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_4 then outputFx is f4", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_5 then outputFx is f5", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_6 then outputFx is f6", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_7 then outputFx is f7", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_8 then outputFx is f8", engine)); - block->addRule(fl::Rule::parse("if inputX is NEAR_9 then outputFx is f9", engine)); - block->addRule(fl::Rule::parse("if inputX is any then trueFx is fx and diffFx is diff", engine)); - engine->addRuleBlock(block); - - engine->configure("", "", "AlgebraicProduct", "AlgebraicSum", "WeightedAverage"); + OutputVariable* trueValue = new OutputVariable; + trueValue->setName("trueValue"); + trueValue->setDescription("value of f(x)=sin(x)/x"); + trueValue->setEnabled(true); + trueValue->setRange(-1.060, 1.000); + trueValue->setLockValueInRange(false); + trueValue->setAggregation(fl::null); + trueValue->setDefuzzifier(new WeightedAverage("Automatic")); + trueValue->setDefaultValue(fl::nan); + trueValue->setLockPreviousValue(true); + trueValue->addTerm(Function::create("fx", "sin(inputX)/inputX", engine)); + engine->addOutputVariable(trueValue); + + OutputVariable* difference = new OutputVariable; + difference->setName("difference"); + difference->setDescription("error e=f(x) - f'(x)"); + difference->setEnabled(true); + difference->setRange(-1.000, 1.000); + difference->setLockValueInRange(false); + difference->setAggregation(fl::null); + difference->setDefuzzifier(new WeightedAverage("Automatic")); + difference->setDefaultValue(fl::nan); + difference->setLockPreviousValue(false); + difference->addTerm(Function::create("error", "outputFx-trueValue", engine)); + engine->addOutputVariable(difference); + + RuleBlock* ruleBlock = new RuleBlock; + ruleBlock->setName(""); + ruleBlock->setDescription(""); + ruleBlock->setEnabled(true); + ruleBlock->setConjunction(fl::null); + ruleBlock->setDisjunction(fl::null); + ruleBlock->setImplication(new AlgebraicProduct); + ruleBlock->setActivation(new General); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_1 then outputFx is f1", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_2 then outputFx is f2", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_3 then outputFx is f3", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_4 then outputFx is f4", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_5 then outputFx is f5", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_6 then outputFx is f6", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_7 then outputFx is f7", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_8 then outputFx is f8", engine)); + ruleBlock->addRule(Rule::parse("if inputX is NEAR_9 then outputFx is f9", engine)); + ruleBlock->addRule(Rule::parse("if inputX is any then trueValue is fx and difference is error", engine)); + engine->addRuleBlock(ruleBlock); + + return engine; + } + + Engine* Console::hybrid() { + Engine* engine = new Engine; + engine->setName("tipper"); + engine->setDescription("(service and food) -> (tip)"); + + InputVariable* service = new InputVariable; + service->setName("service"); + service->setDescription("quality of service"); + service->setEnabled(true); + service->setRange(0.000, 10.000); + service->setLockValueInRange(true); + service->addTerm(new Trapezoid("poor", 0.000, 0.000, 2.500, 5.000)); + service->addTerm(new Triangle("good", 2.500, 5.000, 7.500)); + service->addTerm(new Trapezoid("excellent", 5.000, 7.500, 10.000, 10.000)); + engine->addInputVariable(service); + + InputVariable* food = new InputVariable; + food->setName("food"); + food->setDescription("quality of food"); + food->setEnabled(true); + food->setRange(0.000, 10.000); + food->setLockValueInRange(true); + food->addTerm(new Trapezoid("rancid", 0.000, 0.000, 2.500, 7.500)); + food->addTerm(new Trapezoid("delicious", 2.500, 7.500, 10.000, 10.000)); + engine->addInputVariable(food); + + OutputVariable* mTip = new OutputVariable; + mTip->setName("mTip"); + mTip->setDescription("tip based on Mamdani inference"); + mTip->setEnabled(true); + mTip->setRange(0.000, 30.000); + mTip->setLockValueInRange(false); + mTip->setAggregation(new Maximum); + mTip->setDefuzzifier(new Centroid(100)); + mTip->setDefaultValue(fl::nan); + mTip->setLockPreviousValue(false); + mTip->addTerm(new Triangle("cheap", 0.000, 5.000, 10.000)); + mTip->addTerm(new Triangle("average", 10.000, 15.000, 20.000)); + mTip->addTerm(new Triangle("generous", 20.000, 25.000, 30.000)); + engine->addOutputVariable(mTip); + + OutputVariable* tsTip = new OutputVariable; + tsTip->setName("tsTip"); + tsTip->setDescription("tip based on Takagi-Sugeno inference"); + tsTip->setEnabled(true); + tsTip->setRange(0.000, 30.000); + tsTip->setLockValueInRange(false); + tsTip->setAggregation(fl::null); + tsTip->setDefuzzifier(new WeightedAverage("TakagiSugeno")); + tsTip->setDefaultValue(fl::nan); + tsTip->setLockPreviousValue(false); + tsTip->addTerm(new Constant("cheap", 5.000)); + tsTip->addTerm(new Constant("average", 15.000)); + tsTip->addTerm(new Constant("generous", 25.000)); + engine->addOutputVariable(tsTip); + + RuleBlock* mamdaniRuleBlock = new RuleBlock; + mamdaniRuleBlock->setName("mamdani"); + mamdaniRuleBlock->setDescription("Mamdani inference"); + mamdaniRuleBlock->setEnabled(true); + mamdaniRuleBlock->setConjunction(new AlgebraicProduct); + mamdaniRuleBlock->setDisjunction(new AlgebraicSum); + mamdaniRuleBlock->setImplication(new Minimum); + mamdaniRuleBlock->setActivation(new General); + mamdaniRuleBlock->addRule(Rule::parse("if service is poor or food is rancid then mTip is cheap", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is good then mTip is average", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is excellent or food is delicious then mTip is generous with 0.5", engine)); + mamdaniRuleBlock->addRule(Rule::parse("if service is excellent and food is delicious then mTip is generous with 1.0", engine)); + engine->addRuleBlock(mamdaniRuleBlock); + + RuleBlock* takagiSugenoRuleBlock = new RuleBlock; + takagiSugenoRuleBlock->setName("takagiSugeno"); + takagiSugenoRuleBlock->setDescription("Takagi-Sugeno inference"); + takagiSugenoRuleBlock->setEnabled(true); + takagiSugenoRuleBlock->setConjunction(new AlgebraicProduct); + takagiSugenoRuleBlock->setDisjunction(new AlgebraicSum); + takagiSugenoRuleBlock->setImplication(fl::null); + takagiSugenoRuleBlock->setActivation(new General); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is poor or food is rancid then tsTip is cheap", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is good then tsTip is average", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is excellent or food is delicious then tsTip is generous with 0.5", engine)); + takagiSugenoRuleBlock->addRule(Rule::parse("if service is excellent and food is delicious then tsTip is generous with 1.0", engine)); + engine->addRuleBlock(takagiSugenoRuleBlock); return engine; } void Console::exportAllExamples(const std::string& from, const std::string& to) { - Console::exportAllExamples(from, to, "."); + Console::exportAllExamples(from, to, "./", "/tmp/"); } - void Console::exportAllExamples(const std::string& from, const std::string& to, const std::string& path) { + void Console::exportAllExamples(const std::string& from, const std::string& to, + const std::string& sourcePath, const std::string& targetPath) { std::vector<std::string> examples; - examples.push_back("/mamdani/AllTerms"); - // examples.push_back("/mamdani/Laundry"); - examples.push_back("/mamdani/SimpleDimmer"); - // examples.push_back("/mamdani/SimpleDimmerInverse"); - examples.push_back("/mamdani/matlab/mam21"); - examples.push_back("/mamdani/matlab/mam22"); - examples.push_back("/mamdani/matlab/shower"); - examples.push_back("/mamdani/matlab/tank"); - examples.push_back("/mamdani/matlab/tank2"); - examples.push_back("/mamdani/matlab/tipper"); - examples.push_back("/mamdani/matlab/tipper1"); - examples.push_back("/mamdani/octave/investment_portfolio"); - examples.push_back("/mamdani/octave/mamdani_tip_calculator"); - examples.push_back("/takagi-sugeno/approximation"); - examples.push_back("/takagi-sugeno/SimpleDimmer"); - examples.push_back("/takagi-sugeno/matlab/fpeaks"); - examples.push_back("/takagi-sugeno/matlab/invkine1"); - examples.push_back("/takagi-sugeno/matlab/invkine2"); - examples.push_back("/takagi-sugeno/matlab/juggler"); - examples.push_back("/takagi-sugeno/matlab/membrn1"); - examples.push_back("/takagi-sugeno/matlab/membrn2"); - examples.push_back("/takagi-sugeno/matlab/slbb"); - examples.push_back("/takagi-sugeno/matlab/slcp"); - examples.push_back("/takagi-sugeno/matlab/slcp1"); - examples.push_back("/takagi-sugeno/matlab/slcpp1"); - examples.push_back("/takagi-sugeno/matlab/sltbu_fl"); - examples.push_back("/takagi-sugeno/matlab/sugeno1"); - examples.push_back("/takagi-sugeno/matlab/tanksg"); - examples.push_back("/takagi-sugeno/matlab/tippersg"); - examples.push_back("/takagi-sugeno/octave/cubic_approximator"); - examples.push_back("/takagi-sugeno/octave/heart_disease_risk"); - examples.push_back("/takagi-sugeno/octave/linear_tip_calculator"); - examples.push_back("/takagi-sugeno/octave/sugeno_tip_calculator"); - examples.push_back("/tsukamoto/tsukamoto"); - - std::string sourceBase = path + "/original"; - std::string targetBase = path + "/tmp/"; + examples.push_back("mamdani/AllTerms"); + examples.push_back("mamdani/SimpleDimmer"); + examples.push_back("mamdani/Laundry"); + examples.push_back("mamdani/ObstacleAvoidance"); + examples.push_back("mamdani/SimpleDimmerChained"); + examples.push_back("mamdani/SimpleDimmerInverse"); + examples.push_back("mamdani/matlab/mam21"); + examples.push_back("mamdani/matlab/mam22"); + examples.push_back("mamdani/matlab/shower"); + examples.push_back("mamdani/matlab/tank"); + examples.push_back("mamdani/matlab/tank2"); + examples.push_back("mamdani/matlab/tipper"); + examples.push_back("mamdani/matlab/tipper1"); + examples.push_back("mamdani/octave/investment_portfolio"); + examples.push_back("mamdani/octave/mamdani_tip_calculator"); + examples.push_back("takagi-sugeno/approximation"); + examples.push_back("takagi-sugeno/ObstacleAvoidance"); + examples.push_back("takagi-sugeno/SimpleDimmer"); + examples.push_back("takagi-sugeno/matlab/fpeaks"); + examples.push_back("takagi-sugeno/matlab/invkine1"); + examples.push_back("takagi-sugeno/matlab/invkine2"); + examples.push_back("takagi-sugeno/matlab/juggler"); + examples.push_back("takagi-sugeno/matlab/membrn1"); + examples.push_back("takagi-sugeno/matlab/membrn2"); + examples.push_back("takagi-sugeno/matlab/slbb"); + examples.push_back("takagi-sugeno/matlab/slcp"); + examples.push_back("takagi-sugeno/matlab/slcp1"); + examples.push_back("takagi-sugeno/matlab/slcpp1"); + examples.push_back("takagi-sugeno/matlab/sltbu_fl"); + examples.push_back("takagi-sugeno/matlab/sugeno1"); + examples.push_back("takagi-sugeno/matlab/tanksg"); + examples.push_back("takagi-sugeno/matlab/tippersg"); + examples.push_back("takagi-sugeno/octave/cubic_approximator"); + examples.push_back("takagi-sugeno/octave/heart_disease_risk"); + examples.push_back("takagi-sugeno/octave/linear_tip_calculator"); + examples.push_back("takagi-sugeno/octave/sugeno_tip_calculator"); + examples.push_back("tsukamoto/tsukamoto"); + examples.push_back("hybrid/tipper"); + examples.push_back("hybrid/ObstacleAvoidance"); FL_unique_ptr<Importer> importer; if (from == "fll") importer.reset(new FllImporter); else if (from == "fis") importer.reset(new FisImporter); else if (from == "fcl") importer.reset(new FclImporter); - else throw fl::Exception("[examples error] unrecognized format <" + from + "> to import", FL_AT); + else throw Exception("[examples error] unrecognized format <" + from + "> to import", FL_AT); FL_unique_ptr<Exporter> exporter; if (to == "fll") exporter.reset(new FllExporter); @@ -586,16 +730,19 @@ namespace fl { else if (to == "fis") exporter.reset(new FisExporter); else if (to == "cpp") exporter.reset(new CppExporter); else if (to == "java") exporter.reset(new JavaExporter); - else throw fl::Exception("[examples error] unrecognized format <" + to + "> to export", FL_AT); + else if (to == "R") exporter.reset(new RScriptExporter()); + else throw Exception("[examples error] unrecognized format <" + to + "> to export", FL_AT); std::vector<std::pair<Exporter*, Importer*> > tests; tests.push_back(std::pair<Exporter*, Importer*>(new FllExporter, new FllImporter)); tests.push_back(std::pair<Exporter*, Importer*>(new FisExporter, new FisImporter)); tests.push_back(std::pair<Exporter*, Importer*>(new FclExporter, new FclImporter)); for (std::size_t i = 0; i < examples.size(); ++i) { - FL_LOG("Processing " << (i + 1) << "/" << examples.size() << ": " << examples.at(i)); + std::string example = examples.at(i); + FL_LOG((i + 1) << "/" << examples.size()); + FL_LOG("Importing from: " << sourcePath << "/" << example << "." << from); std::ostringstream ss; - std::string input = sourceBase + examples.at(i) + "." + from; + std::string input = sourcePath + "/" + example + "." + from; std::ifstream source(input.c_str()); if (source.is_open()) { std::string line; @@ -604,29 +751,40 @@ namespace fl { ss << line << "\n"; } source.close(); - } else throw fl::Exception("[examples error] file not found: " + input, FL_AT); + } else throw Exception("[examples error] file not found: " + input, FL_AT); FL_unique_ptr<Engine> engine(importer->fromString(ss.str())); for (std::size_t t = 0; t < tests.size(); ++t) { - std::string out = tests.at(t).first->toString(engine.get()); - FL_unique_ptr<Engine> copy(tests.at(t).second->fromString(out)); - std::string out_copy = tests.at(t).first->toString(copy.get()); - - if (out != out_copy) { - std::ostringstream ss; - ss << "[imex error] different results <" - << importer->name() << "," << exporter->name() << "> " - "at " + examples.at(t) + "." + from + ":\n"; - ss << "<Engine A>\n" << out << "\n\n" << + if ("mamdani/Laundry" == example + or "mamdani/SimpleDimmerInverse" == example + or "mamdani/SimpleDimmerChained" == example + or "hybrid/tipper" == example + or "hybrid/ObstacleAvoidance" == example) { + if (tests.at(t).second->name() == FisImporter().name()) { + continue; + } + } + + std::string exported = tests.at(t).first->toString(engine.get()); + FL_unique_ptr<Engine> engineFromExport(tests.at(t).second->fromString(exported)); + std::string imported = tests.at(t).first->toString(engineFromExport.get()); + + if (exported != imported) { + std::ostringstream msg; + msg << "[imex error] different results <" + << tests.at(t).first->name() << "," << tests.at(t).second->name() << "> " + "at " << example << "." << from << ":\n"; + msg << "<Engine A>\n" << exported << "\n\n" << "================================\n\n" << - "<Engine B>\n" << out_copy; - throw fl::Exception(ss.str(), FL_AT); + "<Engine B>\n" << imported; + throw Exception(msg.str(), FL_AT); } } - std::string output = targetBase + examples.at(i) + "." + to; + std::string output = targetPath + "/" + example + "." + to; std::ofstream target(output.c_str()); + FL_LOG("Exporting to: " << output << "\n"); if (target.is_open()) { if (to == "cpp") { target << "#include <fl/Headers.h>\n\n" @@ -634,8 +792,9 @@ namespace fl { << exporter->toString(engine.get()) << "\n}\n"; } else if (to == "java") { - std::string className = examples.at(i).substr(examples.at(i).find_last_of('/') + 1); + std::string className = example.substr(example.find_last_of('/') + 1); target << "import com.fuzzylite.*;\n" + << "import com.fuzzylite.activation.*\n" << "import com.fuzzylite.defuzzifier.*;\n" << "import com.fuzzylite.factory.*;\n" << "import com.fuzzylite.hedge.*;\n" @@ -650,171 +809,215 @@ namespace fl { << "public static void main(String[] args){\n" << exporter->toString(engine.get()) << "\n}\n}\n"; + } else if (to == "R") { + RScriptExporter* rScript = dynamic_cast<RScriptExporter*> (exporter.get()); + InputVariable* a = engine->getInputVariable(0); + InputVariable* b = engine->getInputVariable(1 % engine->numberOfInputVariables()); + std::string pathToDF = example.substr(example.find_last_of('/') + 1) + ".fld"; + rScript->writeScriptImportingDataFrame(engine.get(), target, + a, b, pathToDF, engine->outputVariables()); } else { target << exporter->toString(engine.get()); } target.close(); } Engine copyConstructor(*engine.get()); - (void) copyConstructor; + FL_IUNUSED(copyConstructor); Engine assignmentOperator = *engine.get(); - (void) assignmentOperator; + FL_IUNUSED(assignmentOperator); } + FL_LOG("Please, make sure the output contains the following structure:\n" + "mkdir -p " << targetPath << "mamdani/matlab; " + "mkdir -p " << targetPath << "mamdani/octave; " + "mkdir -p " << targetPath << "takagi-sugeno/matlab; " + "mkdir -p " << targetPath << "takagi-sugeno/octave; " + "mkdir -p " << targetPath << "tsukamoto; " + "mkdir -p " << targetPath << "hybrid;"); for (std::size_t i = 0; i < tests.size(); ++i) { delete tests.at(i).first; delete tests.at(i).second; } } -#ifdef FL_CPP11 - - void Console::benchmarkExamples(const std::string& path, int runs) { - std::string sourceBase = path + "/original"; - typedef std::pair<std::string, int > Example; - std::vector<Example> examples; - examples.push_back(Example("/mamdani/AllTerms", 1e4)); - examples.push_back(Example("/mamdani/SimpleDimmer", 1e5)); - examples.push_back(Example("/mamdani/matlab/mam21", 128)); - examples.push_back(Example("/mamdani/matlab/mam22", 128)); - examples.push_back(Example("/mamdani/matlab/shower", 256)); - examples.push_back(Example("/mamdani/matlab/tank", 256)); - examples.push_back(Example("/mamdani/matlab/tank2", 512)); - examples.push_back(Example("/mamdani/matlab/tipper", 256)); - examples.push_back(Example("/mamdani/matlab/tipper1", 1e5)); - examples.push_back(Example("/mamdani/octave/investment_portfolio", 256)); - examples.push_back(Example("/mamdani/octave/mamdani_tip_calculator", 256)); - examples.push_back(Example("/takagi-sugeno/approximation", 1e6)); - examples.push_back(Example("/takagi-sugeno/SimpleDimmer", 2e6)); - examples.push_back(Example("/takagi-sugeno/matlab/fpeaks", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/invkine1", 256)); - examples.push_back(Example("/takagi-sugeno/matlab/invkine2", 256)); - examples.push_back(Example("/takagi-sugeno/matlab/juggler", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/membrn1", 1024)); - examples.push_back(Example("/takagi-sugeno/matlab/membrn2", 512)); - examples.push_back(Example("/takagi-sugeno/matlab/slbb", 20)); - examples.push_back(Example("/takagi-sugeno/matlab/slcp", 20)); - examples.push_back(Example("/takagi-sugeno/matlab/slcp1", 15)); - examples.push_back(Example("/takagi-sugeno/matlab/slcpp1", 9)); - examples.push_back(Example("/takagi-sugeno/matlab/sltbu_fl", 128)); - examples.push_back(Example("/takagi-sugeno/matlab/sugeno1", 2e6)); - examples.push_back(Example("/takagi-sugeno/matlab/tanksg", 1024)); - examples.push_back(Example("/takagi-sugeno/matlab/tippersg", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/cubic_approximator", 2e6)); - examples.push_back(Example("/takagi-sugeno/octave/heart_disease_risk", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/linear_tip_calculator", 1024)); - examples.push_back(Example("/takagi-sugeno/octave/sugeno_tip_calculator", 512)); - examples.push_back(Example("/tsukamoto/tsukamoto", 1e6)); - - for (std::size_t i = 0; i < examples.size(); ++i) { - FL_LOG(examples.at(i).first << "\t" << examples.at(i).second); + void Console::benchmark(const std::string& fllFile, const std::string& fldFile, + int runs, std::ofstream* writer) const { + FL_unique_ptr<Engine> engine(FllImporter().fromFile(fllFile)); + std::ifstream reader(fldFile.c_str()); + if (not reader.is_open()) { + throw Exception("File <" + fldFile + "> could not be opened"); + } + Benchmark benchmark(engine->getName(), engine.get()); + benchmark.prepare(reader); + if (writer) { + FL_LOG("\tEvaluating on " << benchmark.getExpected().size() << + " values read from " << fldFile << " ..."); } - - std::vector<std::string> runNumbers(runs); for (int i = 0; i < runs; ++i) { - runNumbers.at(i) = std::to_string(i + 1); + benchmark.runOnce(); } - std::string spacedPath(40, ' '); - std::copy(path.begin(), path.end(), spacedPath.begin()); - FL_LOG(spacedPath << "\t" << "mean\tstdev\n" << Op::join(runNumbers, "\t")); - - FllImporter importer; - FldExporter exporter; - exporter.setExportHeader(false); - exporter.setExportInputValues(false); - exporter.setExportOutputValues(false); - std::ostream dummy(0); + if (writer) { + FL_LOG("\tMean(t)=" << Op::mean(benchmark.getTimes()) << " nanoseconds"); + *writer << benchmark.format(benchmark.results(), + Benchmark::Horizontal, Benchmark::Body) << "\n"; + } else { + FL_LOGP(benchmark.format(benchmark.results(), + Benchmark::Horizontal, Benchmark::Body)); + } + } - for (std::size_t e = 0; e < examples.size(); ++e) { - FL_unique_ptr<Engine> engine(importer.fromFile(sourceBase + examples.at(e).first + ".fll")); + void Console::benchmarks(const std::string& fllFileList, + const std::string& fldFileList, int runs, std::ofstream* writer) const { + std::vector<std::string> fllFiles, fldFiles; - std::vector<scalar> seconds; - int results = std::pow(1.0 * examples.at(e).second, engine->numberOfInputVariables()); + { + std::ifstream fllReader(fllFileList.c_str()); + if (not fllReader.is_open()) { + throw Exception("[error] file <" + fllFileList + "> could not be opened"); + } + std::ifstream fldReader(fldFileList.c_str()); + if (not fldReader.is_open()) { + throw Exception("[error] file <" + fldFileList + "> could not be opened"); + } - for (int r = 0; r < runs; ++r) { - auto start = std::chrono::system_clock::now(); - exporter.write(engine.get(), dummy, results); - auto end = std::chrono::system_clock::now(); + std::string fllLine, fldLine; + while (std::getline(fllReader, fllLine) and std::getline(fldReader, fldLine)) { + fllLine = Op::trim(fllLine); + fldLine = Op::trim(fldLine); + if (fllLine.empty() or fllLine[0] == '#') + continue; + fllFiles.push_back(fllLine); + fldFiles.push_back(fldLine); + } + } - auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds> (end - start); + if (writer) { + *writer << Op::join(Benchmark().header(runs, true), "\t") << "\n"; + } else { + FL_LOGP(Op::join(Benchmark().header(runs, true), "\t")); + } - seconds.push_back(elapsed.count() / 1e3); + for (std::size_t i = 0; i < fllFiles.size(); ++i) { + if (writer) { + FL_LOG("Benchmark " << (i + 1) << "/" << fllFiles.size() << ": " + << fllFiles.at(i)); } - scalar mean = Op::mean(seconds); - scalar stdev = Op::standardDeviation(seconds, mean); - - std::string spacedExample(40, ' '); - std::string exampleName = examples.at(e).first; - std::copy(exampleName.begin(), exampleName.end(), spacedExample.begin()); - FL_LOG(spacedExample << "\t" << fl::Op::str(mean) << "\t" << fl::Op::str(stdev) << "\n" << - Op::join(seconds, "\t")); + benchmark(fllFiles.at(i), fldFiles.at(i), runs, writer); } } -#endif - int Console::main(int argc, char** argv) { - (void) argc; - (void) argv; + int Console::main(int argc, const char* argv[]) { + fuzzylite::setLogging(true); + + Console console; if (argc <= 2) { - std::cout << usage() << std::endl; + FL_LOGP(console.usage() << "\n"); return EXIT_SUCCESS; } - const std::string firstArgument = std::string(argv[1]); - + const std::string firstArgument(argv[1]); if (firstArgument == "export-examples") { std::string path = "."; if (argc > 2) { path = std::string(argv[2]); } - FL_LOG("Path=" << path); + std::string outputPath = "/tmp/"; + if (argc > 3) { + outputPath = std::string(argv[3]); + } + FL_LOG("Origin=" << path); + FL_LOG("Target=" << outputPath); + fuzzylite::setDecimals(3); try { - fuzzylite::setDecimals(3); FL_LOG("Processing fll->fll"); - exportAllExamples("fll", "fll", path); + console.exportAllExamples("fll", "fll", path, outputPath); FL_LOG("Processing fll->fcl"); - exportAllExamples("fll", "fcl", path); + console.exportAllExamples("fll", "fcl", path, outputPath); FL_LOG("Processing fll->fis"); - exportAllExamples("fll", "fis", path); + console.exportAllExamples("fll", "fis", path, outputPath); FL_LOG("Processing fll->cpp"); - exportAllExamples("fll", "cpp", path); + console.exportAllExamples("fll", "cpp", path, outputPath); FL_LOG("Processing fll->java"); - exportAllExamples("fll", "java", path); - fuzzylite::setDecimals(8); - fuzzylite::setMachEps(1e-6); + console.exportAllExamples("fll", "java", path, outputPath); + FL_LOG("Processing fll->R"); + console.exportAllExamples("fll", "R", path, outputPath); + fuzzylite::setDecimals(9); FL_LOG("Processing fll->fld"); - exportAllExamples("fll", "fld", path); + console.exportAllExamples("fll", "fld", path, outputPath); + FL_LOG("Origin=" << path); + FL_LOG("Target=" << outputPath); + } catch (std::exception& ex) { + FL_LOGP(ex.what() << "\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + + } else if (firstArgument == "benchmark") { + if (argc < 5) { + FL_LOG("[error] not enough parameters"); + return EXIT_FAILURE; + } + std::string fllFile(argv[2]); + std::string fldFile(argv[3]); + try { + int runs = (int) Op::toScalar(argv[4]); + if (argc > 5) { + std::string filename(argv[5]); + std::ofstream outputFile; + outputFile.open(filename.c_str()); + if (not outputFile.is_open()) { + FL_LOG("[error] cannot create file <" << filename << ">"); + return EXIT_FAILURE; + } + outputFile << Op::join(Benchmark().header(runs, true), "\t") << "\n"; + console.benchmark(fllFile, fldFile, runs, &outputFile); + } else { + FL_LOGP(Op::join(Benchmark().header(runs, true), "\t")); + console.benchmark(fllFile, fldFile, runs, fl::null); + } } catch (std::exception& ex) { - std::cout << ex.what() << "\nBACKTRACE:\n" << - fl::Exception::btCallStack() << std::endl; + FL_LOGP(ex.what() << "\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; + } else if (firstArgument == "benchmarks") { -#ifdef FL_CPP11 - std::string path = "."; - if (argc > 2) { - path = std::string(argv[2]); + if (argc < 5) { + FL_LOG("[error] not enough parameters"); + return EXIT_FAILURE; } - int runs = 10; - if (argc > 3) { - runs = (int) Op::toScalar(argv[3]); + std::string fllFiles(argv[2]); + std::string fldFiles(argv[3]); + try { + int runs = (int) Op::toScalar(argv[4]); + if (argc > 5) { + std::string filename(argv[5]); + std::ofstream outputFile; + outputFile.open(filename.c_str()); + if (not outputFile.is_open()) { + FL_LOG("[error] cannot create file <" << filename << ">"); + return EXIT_FAILURE; + } + console.benchmarks(fllFiles, fldFiles, runs, &outputFile); + } else { + console.benchmarks(fllFiles, fldFiles, runs, fl::null); + } + } catch (std::exception& ex) { + FL_LOGP(ex.what() << "\n"); + return EXIT_FAILURE; } - fuzzylite::setDecimals(3); - Console::benchmarkExamples(path, runs); return EXIT_SUCCESS; -#else - throw fl::Exception("[benchmarks error] implementation available only when built with C++11 (-DFL_CPP11)", FL_AT); -#endif } + //MAIN: try { - std::map<std::string, std::string> options = parse(argc, argv); - process(options); + std::map<std::string, std::string> options = console.parse(argc, argv); + console.process(options); + } catch (std::exception& ex) { - std::cout << ex.what() << "\n" << std::endl; - // std::cout << fl::Exception::btCallStack() << std::endl; + FL_LOGP(ex.what() << "\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } + } |