diff options
Diffstat (limited to 'fuzzylite/src/Console.cpp')
-rw-r--r-- | fuzzylite/src/Console.cpp | 820 |
1 files changed, 820 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; + } +} |