diff options
Diffstat (limited to 'fuzzylite/test/term')
-rw-r--r-- | fuzzylite/test/term/AggregatedTest.cpp | 52 | ||||
-rw-r--r-- | fuzzylite/test/term/DiscreteTest.cpp | 125 | ||||
-rw-r--r-- | fuzzylite/test/term/FunctionTest.cpp | 115 | ||||
-rw-r--r-- | fuzzylite/test/term/TrapezoidTest.cpp | 53 | ||||
-rw-r--r-- | fuzzylite/test/term/TriangleTest.cpp | 66 |
5 files changed, 411 insertions, 0 deletions
diff --git a/fuzzylite/test/term/AggregatedTest.cpp b/fuzzylite/test/term/AggregatedTest.cpp new file mode 100644 index 0000000..bb3f0e4 --- /dev/null +++ b/fuzzylite/test/term/AggregatedTest.cpp @@ -0,0 +1,52 @@ +/* + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> + + This file is part of fuzzylite. + + fuzzylite is free software: you can redistribute it and/or modify it under + the terms of the FuzzyLite License included with the software. + + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + + fuzzylite is a registered trademark of FuzzyLite Limited. + */ + +#include "test/catch.hpp" +#include "fl/Headers.h" + +namespace fl { + + /** + * Tests: term/Aggregated + * + * @author Juan Rada-Vilela, Ph.D. + * + */ + + TEST_CASE("highest term in aggregated", "[term][aggregated]") { + + FL_unique_ptr<Term> dark(new Triangle("DARK", 0.000, 0.250, 0.500)); + FL_unique_ptr<Term> medium(new Triangle("MEDIUM", 0.250, 0.500, 0.750)); + FL_unique_ptr<Term> bright(new Triangle("BRIGHT", 0.500, 0.750, 1.000)); + + Aggregated aggregated; + aggregated.addTerm(dark.get(), 0.5, fl::null); + aggregated.addTerm(medium.get(), 0.1, fl::null); + aggregated.addTerm(bright.get(), 0.6, fl::null); + + REQUIRE(aggregated.highestActivatedTerm()->getTerm() == bright.get()); + + aggregated.terms().at(1).setDegree(0.7); + REQUIRE(aggregated.highestActivatedTerm()->getTerm() == medium.get()); + + aggregated.terms().front().setDegree(0.9); + REQUIRE(aggregated.highestActivatedTerm()->getTerm() == dark.get()); + + aggregated.clear(); + REQUIRE(aggregated.highestActivatedTerm() == fl::null); + } + +} diff --git a/fuzzylite/test/term/DiscreteTest.cpp b/fuzzylite/test/term/DiscreteTest.cpp new file mode 100644 index 0000000..2fb32aa --- /dev/null +++ b/fuzzylite/test/term/DiscreteTest.cpp @@ -0,0 +1,125 @@ +/* + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> + + This file is part of fuzzylite. + + fuzzylite is free software: you can redistribute it and/or modify it under + the terms of the FuzzyLite License included with the software. + + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + + fuzzylite is a registered trademark of FuzzyLite Limited. + */ + +#include "test/catch.hpp" +#include "fl/Headers.h" + +namespace fl { + + /** + * Tests: term/Discrete + * + * @author Juan Rada-Vilela, Ph.D. + * + */ + + TEST_CASE("discrete finds elements using binary search", "[term][discrete]") { + fuzzylite::setLogging(true); + fuzzylite::setDebugging(false); + Rectangle rectangle("rectangle", 0, 1); + FL_unique_ptr<Discrete> discrete(Discrete::discretize(&rectangle, rectangle.getStart(), rectangle.getEnd(), 10)); + FL_LOG(discrete->toString()); + + CHECK(discrete->membership(.25) == 1.0); + CHECK(discrete->membership(0.0) == 1.0); + CHECK(discrete->membership(-1.0) == 1.0); + CHECK(discrete->membership(1.0) == 1.0); + CHECK(discrete->membership(2.0) == 1.0); + CHECK(Op::isNaN(discrete->membership(fl::nan))); + CHECK(discrete->membership(fl::inf) == 1.0); + CHECK(discrete->membership(-fl::inf) == 1.0); + } + + TEST_CASE("discrete still finds elements using binary search", "[term][discrete]") { + fuzzylite::setLogging(true); + fuzzylite::setDebugging(false); + Triangle triangle("triangle", 0, 1); + FL_unique_ptr<Discrete> discrete(Discrete::discretize(&triangle, triangle.getVertexA(), triangle.getVertexC(), 100)); + FL_LOG(discrete->toString()); + for (int i = 0; i < 200; ++i) { + scalar x = Op::scale(i, 0, 200, -1, 1); + if (not Op::isEq(triangle.membership(x), discrete->membership(x))) { + fuzzylite::setDebugging(true); + CHECK(Op::isEq(triangle.membership(x), discrete->membership(x))); + fuzzylite::setDebugging(false); + } + } + } + + TEST_CASE("discrete finds all elements using binary search", "[term][discrete]") { + fuzzylite::setLogging(true); + fuzzylite::setDebugging(false); + scalar min = -1.0; + scalar max = 1.0; + scalar range = max - min; + scalar mean = 0.5 * (max + min); + + + std::vector<Term*> terms; + terms.push_back(new Triangle("triangle", min, mean, max)); + terms.push_back(new Trapezoid("trapezoid", min, min + .25 * range, min + .75 * range, max)); + terms.push_back(new Rectangle("rectangle", min, max)); + terms.push_back(new Bell("bell", mean, range / 4, 3.0)); + terms.push_back(new Cosine("cosine", mean, range)); + terms.push_back(new Gaussian("gaussian", mean, range / 4)); + terms.push_back(new GaussianProduct("gaussianProduct", mean, range / 4, mean, range / 4)); + terms.push_back(new PiShape("piShape", min, mean, mean, max)); + terms.push_back(new SigmoidDifference("sigmoidDifference", min + .25 * range, 20 / range, 20 / range, max - .25 * range)); + terms.push_back(new SigmoidProduct("sigmoidProduct", min + .25 * range, 20 / range, 20 / range, max - .25 * range)); + terms.push_back(new Spike("spike", mean, range)); + + terms.push_back(new Binary("binary", min, max)); + terms.push_back(new Concave("concave", mean, max)); + terms.push_back(new Ramp("ramp", min, max)); + terms.push_back(new Sigmoid("sigmoid", mean, 20 / range)); + terms.push_back(new SShape("sshape", min, max)); + terms.push_back(new ZShape("zshape", min, max)); + + for (std::size_t t = 0; t < terms.size(); ++t) { + Term* term = terms.at(t); + std::vector<Discrete::Pair> pairs; + srand(0); + for (int i = 0; i < 1000; ++i) { + int randomX = std::rand(); + scalar x = Op::scale(randomX % 100, 0, 100, -1, 1); + pairs.push_back(Discrete::Pair(x, term->membership(x))); + } + Discrete::sort(pairs); + + Discrete discrete("discrete", pairs); + for (std::size_t i = 0; i < pairs.size(); ++i) { + Discrete::Pair pair = pairs.at(i); + scalar x = pair.first; + if (not Op::isEq(discrete.membership(x), term->membership(x))) { + fuzzylite::setDebugging(true); + CHECK(discrete.membership(x) == term->membership(x)); + fuzzylite::setDebugging(false); + } + } + for (int i = 0 ; i < 100 ; i++) { + scalar x = Op::scale(i, 0, 100, -1, 1); + if (not Op::isEq(discrete.membership(x), term->membership(x))) { + fuzzylite::setDebugging(true); + CHECK(discrete.membership(x) == term->membership(x)); + fuzzylite::setDebugging(false); + } + } + } + for (std::size_t i = 0 ; i < terms.size(); ++i){ + delete terms.at(i); + } + } +} diff --git a/fuzzylite/test/term/FunctionTest.cpp b/fuzzylite/test/term/FunctionTest.cpp new file mode 100644 index 0000000..a42e37b --- /dev/null +++ b/fuzzylite/test/term/FunctionTest.cpp @@ -0,0 +1,115 @@ +/* + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> + + This file is part of fuzzylite. + + fuzzylite is free software: you can redistribute it and/or modify it under + the terms of the FuzzyLite License included with the software. + + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + + fuzzylite is a registered trademark of FuzzyLite Limited. + */ + +#include "test/catch.hpp" +#include "fl/Headers.h" + +namespace fl { + + /** + * Tests: term/Function + * + * @author Juan Rada-Vilela, Ph.D. + * + */ + + TEST_CASE("function parses basic arithmetic", "[term][function]") { + Function f; + std::string text = "3+4*2/(1-5)^2^3"; + CHECK(f.toPostfix(text) == "3 4 2 * 1 5 - 2 3 ^ ^ / +"); + CHECK(f.parse(text)->toInfix() == "3.000 ^ 2.000 ^ 5.000 - 1.000 / 2.000 * 4.000 + 3.000"); + CHECK(f.parse(text)->toPrefix() == "+ / ^ ^ 3.000 2.000 - 5.000 1.000 * 2.000 4.000 3.000"); + } + + TEST_CASE("function parses basic trigonometry", "[term][function]") { + Function f; + std::string text = "sin(y*x)^2/x"; + + CHECK_THROWS(f.load(text)); + + f.variables["y"] = 1.0; + f.load(text); + + CHECK(f.toPostfix(text) == "y x * sin 2 ^ x /"); + CHECK(f.parse(text)->toInfix() == "x / 2.000 ^ x * y sin"); + CHECK(f.parse(text)->toPrefix() == "/ x ^ 2.000 sin * x y"); + } + + TEST_CASE("function parses propositions", "[term][function]") { + Function f; + + std::string text = "(Temperature is High and Oxygen is Low) or " + "(Temperature is Low and (Oxygen is Low or Oxygen is High))"; + + CHECK(f.toPostfix(text) == "Temperature is High Oxygen is Low " + "and Temperature is Low Oxygen is Low Oxygen is High or and or"); + } + + TEST_CASE("function cannot deal with negative numbers", "[term][function]") { + Function f; + std::string text = "-5 *4/sin(-pi/2)"; + + SECTION("function throws exception") { + CHECK_THROWS(f.parse(text)->evaluate()); + } + + f.variables["pi"] = 3.14; + CHECK_THROWS(f.parse(text)->evaluate(&f.variables)); + + text = "~5 *4/sin(~pi/2)"; + CHECK(f.parse(text)->evaluate(&f.variables) == Approx(20)); + + f.load(text); + + f.variables["pi"] = 3.14; + + CHECK(f.toPostfix(text) == "5 ~ 4 * pi ~ 2 / sin /"); + CHECK(f.parse(text)->toInfix() == "2.000 / pi ~ sin / 4.000 * 5.000 ~"); + CHECK(f.parse(text)->toPrefix() == "/ sin / 2.000 ~ pi * 4.000 ~ 5.000"); + } + + TEST_CASE("Function is clonable", "[term][function]") { + Function* f = new Function; + std::string text = "2+2"; + f->load(text); + CHECK(Op::isEq(f->membership(fl::nan), 4)); + Function* clone = f->clone(); + delete f; + CHECK(Op::isEq(clone->membership(fl::nan), 4)); + delete clone; + } + + TEST_CASE("Function is constructor copyable", "[term][function]") { + Function* f = new Function; + std::string text = "2+2"; + f->load(text); + CHECK(Op::isEq(f->membership(fl::nan), 4)); + Function* clone = new Function(*f); + delete f; + CHECK(Op::isEq(clone->membership(fl::nan), 4)); + delete clone; + } + + TEST_CASE("Function computes tree size correctly", "[term][function]"){ + Function f("f", "x*x+(x-x)/x+log(x)"); + f.load(); + CHECK(f.root()->treeSize() == 6); + CHECK(f.root()->treeSize(Function::Element::Function) == 1); + CHECK(f.root()->treeSize(Function::Element::Operator) == 5); + FL_LOG(f.complexity().toString()); + } + +} diff --git a/fuzzylite/test/term/TrapezoidTest.cpp b/fuzzylite/test/term/TrapezoidTest.cpp new file mode 100644 index 0000000..8581ac1 --- /dev/null +++ b/fuzzylite/test/term/TrapezoidTest.cpp @@ -0,0 +1,53 @@ +/* + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> + + This file is part of fuzzylite. + + fuzzylite is free software: you can redistribute it and/or modify it under + the terms of the FuzzyLite License included with the software. + + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + + fuzzylite is a registered trademark of FuzzyLite Limited. + */ + +#include "test/catch.hpp" +#include "fl/Headers.h" + +namespace fl { + + /** + * Tests: term/Trapezoid + * + * @author Juan Rada-Vilela, Ph.D. + * + */ + + TEST_CASE("trapezoid can be open ended with -infinity", "[term][trapezoid]") { + Trapezoid trapezoid("A", -fl::inf, 0, 1, 2); + Ramp ramp("a", 2, 1); + //(-inf, inf) + for (scalar i = -10.0; Op::isLE(i, 10.0); i += .2) { + FL_DBG("A(" << i << ")=" << trapezoid.membership(i)); + FL_DBG("a(" << i << ")=" << ramp.membership(i)); + REQUIRE(Op::isEq(trapezoid.membership(i), ramp.membership(i))); + } + REQUIRE(Op::isEq(trapezoid.membership(-fl::inf), 1.0)); + REQUIRE(Op::isEq(trapezoid.membership(fl::inf), 0.0)); + } + + TEST_CASE("trapezoid can be open ended with +infinity", "[term][trapezoid]") { + Trapezoid trapezoid("A", 0, 1, 2, fl::inf); + Ramp ramp("a", 0, 1); + //(-inf, inf) + for (scalar i = -10.0; Op::isLE(i, 10.0); i += .2) { + REQUIRE(Op::isEq(trapezoid.membership(i), ramp.membership(i))); + } + REQUIRE(Op::isEq(trapezoid.membership(fl::inf), 1.0)); + REQUIRE(Op::isEq(trapezoid.membership(-fl::inf), 0.0)); + } + +} diff --git a/fuzzylite/test/term/TriangleTest.cpp b/fuzzylite/test/term/TriangleTest.cpp new file mode 100644 index 0000000..bd1459d --- /dev/null +++ b/fuzzylite/test/term/TriangleTest.cpp @@ -0,0 +1,66 @@ +/* + fuzzylite (R), a fuzzy logic control library in C++. + Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved. + Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com> + + This file is part of fuzzylite. + + fuzzylite is free software: you can redistribute it and/or modify it under + the terms of the FuzzyLite License included with the software. + + You should have received a copy of the FuzzyLite License along with + fuzzylite. If not, see <http://www.fuzzylite.com/license/>. + + fuzzylite is a registered trademark of FuzzyLite Limited. + */ + +#include "test/catch.hpp" +#include "fl/Headers.h" + +namespace fl { + + /** + * Tests: term/Triangle + * + * @author Juan Rada-Vilela, Ph.D. + * + */ + + TEST_CASE("triangle can be open ended with -infinity", "[term][triangle]") { + Triangle triangle("A", -fl::inf, 0, 1); + Ramp ramp("a", 1, 0); + for (int i = -10; i < 2; ++i) { + FL_DBG("A(" << i << ")=" << triangle.membership(i)); + FL_DBG("a(" << i << ")=" << ramp.membership(i)); + REQUIRE(Op::isEq(triangle.membership(i), ramp.membership(i))); + } + REQUIRE(Op::isEq(triangle.membership(-fl::inf), 1.0)); + REQUIRE(Op::isEq(triangle.membership(fl::inf), 0.0)); + } + + TEST_CASE("triangle can be open ended with +infinity", "[term][triangle]") { + Triangle triangle("A", 0, 1, fl::inf); + Ramp ramp("a", 0, 1); + for (int i = 10; i >= -2; --i) { + FL_DBG("A(" << i << ")=" << triangle.membership(i)); + FL_DBG("a(" << i << ")=" << ramp.membership(i)); + REQUIRE(Op::isEq(triangle.membership(i), ramp.membership(i))); + } + REQUIRE(Op::isEq(triangle.membership(fl::inf), 1.0)); + REQUIRE(Op::isEq(triangle.membership(-fl::inf), 0.0)); + } + + TEST_CASE("triangle defuzzification is correct", "[term][triangle]") { + Triangle a("A", 0, 1, 2); + Triangle b("B", 1, 2, 3); + AlgebraicProduct times; + Aggregated x("X", 0, 3, new AlgebraicSum); + x.addTerm(&a, 1, ×); + x.addTerm(&b, 1, ×); + Centroid c(101); + FL_LOG(c.defuzzify(&x, 0, 3)); + + Triangle t("T", 0, 0, 1); + FL_LOG(c.defuzzify(&t, 0, 1)); + } +} |