From cc4f1b5aef82bd2d9c6bb883264e841922465eea Mon Sep 17 00:00:00 2001 From: Jonathan Rampersad Date: Tue, 3 Oct 2023 14:48:38 -0400 Subject: [PATCH] Refactored Code --- Exponential/Exponential.h | 596 ++++++++++++++++++++++++ Exponential/Exponential.vcxproj | 2 +- Exponential/Exponential.vcxproj.filters | 2 +- Exponential/FunctionsTemplate.h | 496 -------------------- Exponential/Source.cpp | 93 +++- 5 files changed, 667 insertions(+), 522 deletions(-) create mode 100644 Exponential/Exponential.h delete mode 100644 Exponential/FunctionsTemplate.h diff --git a/Exponential/Exponential.h b/Exponential/Exponential.h new file mode 100644 index 0000000..6b61a8b --- /dev/null +++ b/Exponential/Exponential.h @@ -0,0 +1,596 @@ +#pragma once +#ifndef JONATHAN_RAMPERSAD_EXPONENTIAL_H_ +#define JONATHAN_RAMPERSAD_EXPONENTIAL_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace JRAMPERSAD +{ + namespace EXPONENTIAL + { + /** + * \brief Structure for options to be used when running one of the two genetic algorithms in a Function object + * + */ + struct GA_Options + { + /** \brief Minimum value you believe the answer can be */ + double min_range = -100; + /** \brief Maximum value you believe the answer can be */ + double max_range = 100; + /** \brief Number of times you'd like to run the algorithm (increasing this value causes the algorithm to take longer) */ + unsigned int num_of_generations = 10; + /** \brief Amount of approximate solutions you'd like to be returned */ + unsigned int sample_size = 1000; + /** \brief Amount of solutions you'd like the algorithm to generate (increasing this value causes the algorithm to take longer) */ + unsigned int data_size = 100000; + /** \brief How much you'd like the algorithm to mutate solutions (Leave this as default in most cases) */ + double mutation_percentage = 0.01; + }; + + namespace detail + { + template + [[nodiscard("MATH::ABS(T) returns a value of type T")]] T ABS(const T& n) noexcept + { + return n < 0 ? n * -1 : n; + } + + template + [[nodiscard("MATH::NEGATE(T) returns a value of type T")]] T NEGATE(const T& n) noexcept + { + return n * -1; + } + + template + [[nodiscard("MATH::POW(T, int) returns a value of type T")]] T POW(const T& n, const int& exp) noexcept + { + if (exp == 0) + return 1; + + T res = n; + for (int i = 1; i < exp; i++) + { + res *= n; + } + + return res; + } + + template + [[nodiscard("MATH::SUM(std::vector) returns a value of type T")]] T SUM(const std::vector& vec) noexcept + { + T res{}; + for (auto& val : vec) + res += val; + return res; + } + + template + [[nodiscard]] T MEDIAN(std::vector vec) noexcept + { + std::sort( + vec.begin(), + vec.end(), + [](const auto& lhs, const auto& rhs) { + return lhs < rhs; + }); + + return vec[vec.size() / 2]; + } + + template + [[nodiscard]] double MEAN(const std::vector& vec) noexcept + { + return SUM(vec) / vec.size(); + } + + template + [[noreturn]] void SortASC(std::vector& vec) + { + std::sort( + std::execution::par, + vec.begin(), vec.end(), + [](const auto& lhs, const auto& rhs) { + return lhs < rhs; + }); + } + + template + [[noreturn]] void SortDESC(std::vector& vec) + { + std::sort( + std::execution::par, + vec.begin(), vec.end(), + [](const auto& lhs, const auto& rhs) { + return lhs > rhs; + }); + } + + template // Genetic Algorithm helper struct + struct GA_Solution + { + double rank, x, y_val; + bool ranked; + + GA_Solution() : rank(0), x(0), y_val(0), ranked(false) {} + GA_Solution(double Rank, double x_val, double y = 0) : rank(Rank), x(x_val), y_val(y), ranked(false) {} + virtual ~GA_Solution() = default; + + void fitness(const std::vector& constants) + { + double ans = 0; + for (int i = lrgst_expo; i >= 0; i--) + ans += constants[i] * POW(x, (lrgst_expo - i)); + + ans -= y_val; + rank = (ans == 0) ? DBL_MAX : ABS(1 / ans); + } + }; + } + + using namespace detail; + /** + * \brief A class representing an Exponential Function (e.g 2x^2 + 4x - 1), + * \tparam lrgst_expo The largest exponent in the function (e.g 2 means largest exponent is x^2) + */ + template + class Function + { + private: + std::vector constants; + + public: + // Speicialty function to get the real roots of a Quadratic Function without relying on a Genetic Algorithm to approximate + friend std::vector QuadraticSolve(const Function<2>& f); + + public: + /** + * \brief Constructor for Function class + * \param constnts An array with the constants for the function (e.g 2, 1, 3 = 2x^2 + 1x - 3) size of array MUST be lrgst_expo + 1 + */ + Function(const std::vector& constnts); + /** + * \brief Constructor for Function class + * \param constnts An array with the constants for the function (e.g 2, 1, 3 = 2x^2 + 1x - 3) size of array MUST be lrgst_expo + 1 + */ + Function(std::vector&& constnts); + Function(const Function& other) = default; + Function(Function&& other) noexcept = default; + virtual ~Function(); + + Function& operator=(const Function& other) = default; + Function& operator=(Function&& other) noexcept = default; + + // Operator function to display function object in a human readable format + friend std::ostream& operator<<(std::ostream& os, const Function func) + { + if (lrgst_expo == 0) + { + os << func.constants[0]; + return os; + } + + if (func.constants[0] == 1) + os << "x"; + else if (func.constants[0] == -1) + os << "-x"; + else + os << func.constants[0] << "x"; + + if (lrgst_expo != 1) + os << "^" << lrgst_expo; + + for (int i = lrgst_expo - 1; i > 0; i--) + { + int n = func.constants[lrgst_expo - i]; + if (n == 0) continue; + + auto s = n > 0 ? " + " : " - "; + + if (n != 1) + os << s << ABS(n) << "x"; + else + os << s << "x"; + + if (i != 1) + os << "^" << i; + } + + int n = func.constants[lrgst_expo]; + if (n == 0) return os; + + auto s = n > 0 ? " + " : " - "; + os << s; + + os << ABS(n); + + return os; + } + + template + friend Function operator+(const Function& f1, const Function& f2); // Operator to add two functions + template + friend Function operator-(const Function& f1, const Function& f2); // Operator to subtract two functions + + // Operators to multiply a function by a constant (Scaling it) + friend Function operator*(const Function& f, const int& c) + { + if (c == 1) return f; + if (c == 0) throw std::logic_error("Cannot multiply a function by 0"); + + std::vector res; + for (auto& val : f.constants) + res.push_back(c * val); + + return Function(res); + } + Function& operator*=(const int& c) + { + if (c == 1) return *this; + if (c == 0) throw std::logic_error("Cannot multiply a function by 0"); + + for (auto& val : this->constants) + val *= c; + + return *this; + } + + /** + * \brief Calculates the differential (dy/dx) of the function + * \returns a function representing the differential (dy/dx) of the calling function object + */ + [[nodiscard("MATH::EXP::Function::differential() returns the differential, the calling object is not changed")]] + Function differential() const; + + /** + * \brief Function that uses a genetic algorithm to find the approximate roots of the function + * \param options GA_Options object specifying the options to run the algorithm + * \returns A vector containing a n number of approximate root values (n = sample_size as defined in options) + */ + [[nodiscard]] std::vector get_real_roots(const GA_Options& options = GA_Options()) const; + + /** + * \brief Function that solves for y when x = user value + * \param x_val the X Value you'd like the function to use + * \returns the Y value the function returns based on the entered X value + */ + [[nodiscard]] double solve_y(const double& x_val) const noexcept; + + /** + * \brief Function that uses a genetic algorithm to find the values of x where y = user value + * \param y_val The return value that you would like to find the approximate x values needed to solve when entered into the function + * \param options GA_Options object specifying the options to run the algorithm + * \returns A vector containing a n number of x values that cause the function to approximately equal the y_val (n = sample_size as defined in options) + */ + [[nodiscard]] std::vector solve_x(const double& y_val, const GA_Options& options = GA_Options()) const; + }; + + /** + * \brief Uses the quadratic function to solve the roots of an entered quadratic equation + * \param f Quadratic function you'd like to find the roots of (Quadratic Function object is a Function<2> object + * \returns a vector containing the roots + */ + std::vector QuadraticSolve(const Function<2>& f) + { + std::vector res; + + const int& a = f.constants[0]; + const int& b = f.constants[1]; + const int& c = f.constants[2]; + + const double sqr_val = static_cast(POW(b, 2) - (4 * a * c)); + + if (sqr_val < 0) + { + return res; + } + + res.push_back(((NEGATE(b) + sqrt(sqr_val)) / 2 * a)); + res.push_back(((NEGATE(b) - sqrt(sqr_val)) / 2 * a)); + return res; + } + + template e2 ? e1 : e2)> + Function operator+(const Function& f1, const Function& f2) + { + std::vector res; + if (e1 > e2) + { + for (auto& val : f1.constants) + res.push_back(val); + + int i = e1 - e2; + for (auto& val : f2.constants) + { + res[i] += val; + i++; + } + } + else + { + for (auto& val : f2.constants) + res.push_back(val); + + int i = e2 - e1; + for (auto& val : f1.constants) + { + res[i] += val; + i++; + } + } + + return Function{res}; + } + + template e2 ? e1 : e2)> + Function operator-(const Function& f1, const Function& f2) + { + std::vector res; + if (e1 > e2) + { + for (auto& val : f1.constants) + res.push_back(val); + + int i = e1 - e2; + for (auto& val : f2.constants) + { + res[i] -= val; + i++; + } + } + else + { + for (auto& val : f2.constants) + res.push_back(val); + + int i = e2 - e1; + + for (int j = 0; j < i; j++) + res[j] *= -1; + + for (auto& val : f1.constants) + { + res[i] = val - res[i]; + i++; + } + } + + return Function{res}; + } + + template + Function::Function(const std::vector& constnts) + { + if (lrgst_expo < 0) + throw std::logic_error("Function template argument must not be less than 0"); + + if (constnts.size() != lrgst_expo + 1) + throw std::logic_error("Function must be created with (n+1) integers in vector object"); + + if (constnts[0] == 0) + throw std::logic_error("First value should not be 0"); + + constants = constnts; + } + + template + Function::Function(std::vector&& constnts) + { + if (lrgst_expo < 0) + throw std::logic_error("Function template argument must not be less than 0"); + + if (constnts.size() != lrgst_expo + 1) + throw std::logic_error("Function must be created with (n+1) integers in vector object"); + + if (constnts[0] == 0) + throw std::logic_error("First value should not be 0"); + + constants = std::move(constnts); + } + + template + Function::~Function() + { + constants.clear(); + } + + template + Function Function::differential() const + { + if (lrgst_expo == 0) + throw std::logic_error("Cannot differentiate a number (Function<0>)"); + + std::vector result; + for (int i = 0; i < lrgst_expo; i++) + { + result.push_back(constants[i] * (lrgst_expo - i)); + } + + return Function{result}; + } + + template + std::vector Function::get_real_roots(const GA_Options& options) const + { + // Create initial random solutions + std::random_device device; + std::uniform_real_distribution unif(options.min_range, options.max_range); + std::vector> solutions; + + solutions.resize(options.data_size); + for (unsigned int i = 0; i < options.sample_size; i++) + solutions[i] = (GA_Solution{0, unif(device)}); + + float timer{ 0 }; + + for (unsigned int count = 0; count < options.num_of_generations; count++) + { + std::generate(std::execution::par, solutions.begin() + options.sample_size, solutions.end(), [&unif, &device]() { + return GA_Solution{0, unif(device)}; + }); + + // Run our fitness function + for (auto& s : solutions) { s.fitness(constants); } + + // Sort our solutions by rank + std::sort(std::execution::par, solutions.begin(), solutions.end(), + [](const auto& lhs, const auto& rhs) { + return lhs.rank > rhs.rank; + }); + + // Take top solutions + std::vector> sample; + std::copy( + solutions.begin(), + solutions.begin() + options.sample_size, + std::back_inserter(sample) + ); + solutions.clear(); + + if (count + 1 == options.num_of_generations) + { + std::copy( + sample.begin(), + sample.end(), + std::back_inserter(solutions) + ); + sample.clear(); + break; + } + + // Mutate the top solutions by % + std::uniform_real_distribution m((1 - options.mutation_percentage), (1 + options.mutation_percentage)); + std::for_each(sample.begin(), sample.end(), [&m, &device](auto& s) { + s.x *= m(device); + }); + + // Cross over not needed as it's only one value + + std::copy( + sample.begin(), + sample.end(), + std::back_inserter(solutions) + ); + sample.clear(); + solutions.resize(options.data_size); + } + + std::sort(solutions.begin(), solutions.end(), + [](const auto& lhs, const auto& rhs) { + return lhs.x < rhs.x; + }); + + std::vector ans; + for (auto& s : solutions) + { + ans.push_back(s.x); + } + return ans; + } + + template + double Function::solve_y(const double& x_val) const noexcept + { + std::vector exceptions; + + for (int i : constants) + exceptions.push_back(i != 0); + + double ans{ 0 }; + for (int i = lrgst_expo; i >= 0; i--) + { + if (exceptions[i]) + ans += constants[i] * POW(x_val, (lrgst_expo - i)); + } + + return ans; + } + + template + inline std::vector Function::solve_x(const double& y_val, const GA_Options& options) const + { + // Create initial random solutions + std::random_device device; + std::uniform_real_distribution unif(options.min_range, options.max_range); + std::vector> solutions; + + solutions.resize(options.data_size); + for (unsigned int i = 0; i < options.sample_size; i++) + solutions[i] = (GA_Solution{0, unif(device), y_val}); + + for (unsigned int count = 0; count < options.num_of_generations; count++) + { + std::generate(std::execution::par, solutions.begin() + options.sample_size, solutions.end(), [&unif, &device, &y_val]() { + return GA_Solution{0, unif(device), y_val}; + }); + + + // Run our fitness function + for (auto& s : solutions) { s.fitness(constants); } + + // Sort our solutions by rank + std::sort(std::execution::par, solutions.begin(), solutions.end(), + [](const auto& lhs, const auto& rhs) { + return lhs.rank > rhs.rank; + }); + + // Take top solutions + std::vector> sample; + std::copy( + solutions.begin(), + solutions.begin() + options.sample_size, + std::back_inserter(sample) + ); + solutions.clear(); + + if (count + 1 == options.num_of_generations) + { + std::copy( + sample.begin(), + sample.end(), + std::back_inserter(solutions) + ); + sample.clear(); + break; + } + + // Mutate the top solutions by % + std::uniform_real_distribution m((1 - options.mutation_percentage), (1 + options.mutation_percentage)); + std::for_each(sample.begin(), sample.end(), [&m, &device](auto& s) { + s.x *= m(device); + }); + + // Cross over not needed as it's only one value + + std::copy( + sample.begin(), + sample.end(), + std::back_inserter(solutions) + ); + sample.clear(); + solutions.resize(options.data_size); + } + + std::sort(solutions.begin(), solutions.end(), + [](const auto& lhs, const auto& rhs) { + return lhs.x < rhs.x; + }); + + std::vector ans; + for (auto& s : solutions) + { + ans.push_back(s.x); + } + return ans; + } + } +} + +#endif // !JONATHAN_RAMPERSAD_EXPONENTIAL_H_ \ No newline at end of file diff --git a/Exponential/Exponential.vcxproj b/Exponential/Exponential.vcxproj index 07d448b..a144d23 100644 --- a/Exponential/Exponential.vcxproj +++ b/Exponential/Exponential.vcxproj @@ -135,7 +135,7 @@ - + diff --git a/Exponential/Exponential.vcxproj.filters b/Exponential/Exponential.vcxproj.filters index b9ec802..1fc27ed 100644 --- a/Exponential/Exponential.vcxproj.filters +++ b/Exponential/Exponential.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files diff --git a/Exponential/FunctionsTemplate.h b/Exponential/FunctionsTemplate.h deleted file mode 100644 index ab6f446..0000000 --- a/Exponential/FunctionsTemplate.h +++ /dev/null @@ -1,496 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace MATH -{ - constexpr double GA_DEFAULT_MIN_RANGE = -100; - constexpr double GA_DEFAULT_MAX_RANGE = 100; - constexpr int GA_DEFAULT_NUM_OF_GENERATIONS = 100; - constexpr int GA_DEFAULT_SAMPLE_SIZE = 1000; - constexpr int GA_DEFAULT_DATA_SIZE = 100000; - constexpr double GA_DEFAULT_MUTATION_PERCENTAGE = 0.01; - - template - [[nodiscard("MATH::ABS(T) returns a value of type T")]] T ABS(const T& n) noexcept - { - return n < 0 ? n * -1 : n; - } - - template - [[nodiscard("MATH::NEGATE(T) returns a value of type T")]] T NEGATE(const T& n) noexcept - { - return n * -1; - } - - template - [[nodiscard("MATH::POW(T, int) returns a value of type T")]] T POW(const T& n, const int& exp) noexcept - { - if (exp == 0) - return 1; - - T res = n; - for (int i = 1; i < exp; i++) - { - res *= n; - } - - return res; - } - - template - [[nodiscard]] T SUM(const std::vector& vec) noexcept - { - T res{}; - for (auto& val : vec) - res += val; - return res; - } - - template - [[nodiscard]] T MEDIAN(std::vector vec) noexcept - { - std::sort( - vec.begin(), - vec.end(), - [](const auto& lhs, const auto& rhs){ - return lhs < rhs; - }); - - return vec[vec.size() / 2]; - } - - template - [[nodiscard]] double MEAN(const std::vector& vec) noexcept - { - return SUM(vec) / vec.size(); - } - - template - void SortASC(std::vector& vec) - { - std::sort( - vec.begin(), vec.end(), - [](const auto& lhs, const auto& rhs) { - return lhs < rhs; - }); - } - - template - void SortDESC(std::vector& vec) - { - std::sort( - vec.begin(), vec.end(), - [](const auto& lhs, const auto& rhs) { - return lhs > rhs; - }); - } - - class Coordinate2D - { - private: - double X, Y; - - public: - Coordinate2D() : X(0), Y(0) {} - Coordinate2D(double x) : X(x), Y(x) {} - Coordinate2D(double x, double y) : X(x), Y(y) {} - virtual ~Coordinate2D() = default; - - inline void set_x(const double val) noexcept { X = val; } - inline void set_y(const double val) noexcept { Y = val; } - - [[nodiscard]] inline double get_x() const noexcept { return X; } - [[nodiscard]] inline double get_y() const noexcept { return Y; } - - friend std::ostream& operator<<(std::ostream& os, const Coordinate2D& coord) - { - os << '(' << coord.X << ", " << coord.Y << ") "; - return os; - } - }; - - namespace INTERNAL - { - template // Genetic Algorithm helper struct - struct GA_Solution - { - double rank, x; - - GA_Solution(double Rank, double x_val) : rank(Rank), x(x_val) {} - virtual ~GA_Solution() = default; - - void fitness(const std::vector& constants) - { - std::vector exceptions; - for (int i : constants) - exceptions.push_back(i != 0); - - double ans = 0; - for (int i = lrgst_exp; i >= 0; i--) - { - if (exceptions[i]) - { - ans += constants[i] * POW(x, (lrgst_exp - i)); - } - } - rank = (ans == 0) ? DBL_MAX : ABS(1 / ans); - } - }; - } - - namespace EXP - { - template - class Function - { - private: - std::vector constants; - - public: - // Speicialty function to get the real roots of a Quadratic Function without relying on a Genetic Algorithm to approximate - friend std::vector QuadraticSolve(const Function<2>& f); - - public: - Function(const std::vector& constnts); - Function(std::vector&& constnts); - Function(const Function& other) = default; - Function(Function&& other) noexcept = default; - virtual ~Function(); - - Function& operator=(const Function& other) = default; - Function& operator=(Function&& other) noexcept = default; - - // Operator function to display function object in a human readable format - friend std::ostream& operator<<(std::ostream& os, const Function func) - { - if (lrgst_exp == 0) - { - os << func.constants[0]; - return os; - } - - if (func.constants[0] == 1) - os << "x"; - else if (func.constants[0] == -1) - os << "-x"; - else - os << func.constants[0] << "x"; - - if (lrgst_exp != 1) - os << "^" << lrgst_exp; - - for (int i = lrgst_exp - 1; i > 0; i--) - { - int n = func.constants[lrgst_exp - i]; - if (n == 0) continue; - - auto s = n > 0 ? " + " : " - "; - - if (n != 1) - os << s << ABS(n) << "x"; - else - os << s << "x"; - - if (i != 1) - os << "^" << i; - } - - int n = func.constants[lrgst_exp]; - if (n == 0) return os; - - auto s = n > 0 ? " + " : " - "; - os << s; - - os << ABS(n); - - return os; - } - - template - friend Function operator+(const Function& f1, const Function& f2); // Operator to add two functions - template - friend Function operator-(const Function& f1, const Function& f2); // Operator to subtract two functions - - // Operators to multiply a function by a constant (Scaling it) - friend Function operator*(const Function& f, const int& c) - { - if (c == 1) return f; - if (c == 0) throw std::logic_error("Cannot multiply a function by 0"); - - std::vector res; - for (auto& val : f.constants) - res.push_back(c * val); - - return Function(res); - } - Function& operator*=(const int& c) - { - if (c == 1) return *this; - if (c == 0) throw std::logic_error("Cannot multiply a function by 0"); - - for (auto& val : this->constants) - val *= c; - - return *this; - } - - [[nodiscard("MATH::EXP::Function::differential() returns the differential, the calling object is not changed")]] - Function differential() const; // This function returns the differential (dy/dx) of the Function object - - // Function that uses a genetic algorithm to find the approximate roots of the function - [[nodiscard]] std::vector get_real_roots_ga( - const double& min_range = GA_DEFAULT_MIN_RANGE, - const double& max_range = GA_DEFAULT_MAX_RANGE, - const int& num_of_generations = GA_DEFAULT_NUM_OF_GENERATIONS, - const int& sample_size = GA_DEFAULT_SAMPLE_SIZE, - const int& data_size = GA_DEFAULT_DATA_SIZE, - const double& mutation_percentage = GA_DEFAULT_MUTATION_PERCENTAGE) const; - - // Function that returns the y-intercept of the function i.e. where x = 0 - [[nodiscard]] inline Coordinate2D get_y_intrcpt() const noexcept { return Coordinate2D{ 0, (double)constants[lrgst_exp] }; } - - [[nodiscard]] double solve_y(const double& x_val) const noexcept; - - }; - - std::vector QuadraticSolve(const Function<2>& f) - { - std::vector res; - - const int& a = f.constants[0]; - const int& b = f.constants[1]; - const int& c = f.constants[2]; - - const double sqr_val = static_cast(POW(b, 2) - (4 * a * c)); - - if (sqr_val < 0) - { - return res; - } - - res.push_back( ((NEGATE(b) + sqrt(sqr_val)) / 2 * a) ); - res.push_back( ((NEGATE(b) - sqrt(sqr_val)) / 2 * a) ); - return res; - } - - template e2 ? e1 : e2)> - Function operator+(const Function& f1, const Function& f2) - { - std::vector res; - if (e1 > e2) - { - for (auto& val : f1.constants) - res.push_back(val); - - int i = e1 - e2; - for (auto& val : f2.constants) - { - res[i] += val; - i++; - } - } - else - { - for (auto& val : f2.constants) - res.push_back(val); - - int i = e2 - e1; - for (auto& val : f1.constants) - { - res[i] += val; - i++; - } - } - - return Function{res}; - } - - template e2 ? e1 : e2)> - Function operator-(const Function& f1, const Function& f2) - { - std::vector res; - if (e1 > e2) - { - for (auto& val : f1.constants) - res.push_back(val); - - int i = e1 - e2; - for (auto& val : f2.constants) - { - res[i] -= val; - i++; - } - } - else - { - for (auto& val : f2.constants) - res.push_back(val); - - int i = e2 - e1; - - for (int j = 0; j < i; j++) - res[j] *= -1; - - for (auto& val : f1.constants) - { - res[i] = val - res[i]; - i++; - } - } - - return Function{res}; - } - - template - Function::Function(const std::vector& constnts) - { - if (lrgst_exp < 0) - throw std::logic_error("Function template argument must not be less than 0"); - - if (constnts.size() != lrgst_exp + 1) - throw std::logic_error("Function must be created with (n+1) integers in vector object"); - - if (constnts[0] == 0) - throw std::logic_error("First value should not be 0"); - - constants = constnts; - } - - template - Function::Function(std::vector&& constnts) - { - if (lrgst_exp < 0) - throw std::logic_error("Function template argument must not be less than 0"); - - if (constnts.size() != lrgst_exp + 1) - throw std::logic_error("Function must be created with (n+1) integers in vector object"); - - if (constnts[0] == 0) - throw std::logic_error("First value should not be 0"); - - constants = std::move(constnts); - } - - template - Function::~Function() - { - constants.clear(); - } - - template - Function Function::differential() const - { - if (lrgst_exp == 0) - throw std::logic_error("Cannot differentiate a number (Function<0>)"); - - std::vector result; - for (int i = 0; i < lrgst_exp; i++) - { - result.push_back(constants[i] * (lrgst_exp - i)); - } - - return Function{result}; - } - - template - std::vector Function::get_real_roots_ga( - const double& min_range, const double& max_range, - const int& num_of_generations, - const int& sample_size, const int& data_size, - const double& mutation_percentage) const - { - // Create initial random solutions - std::random_device device; - std::uniform_real_distribution unif(static_cast(min_range), static_cast(max_range)); - std::vector> solutions; - - for (int i = 0; i < sample_size; i++) - solutions.push_back(INTERNAL::GA_Solution{0, unif(device)}); - - for(int count = 0; count < num_of_generations; count++) - { - for (int i = sample_size; i < data_size; i++) - solutions.push_back(INTERNAL::GA_Solution{0, unif(device)}); - - // Run our fitness function - for (auto& s : solutions) { s.fitness(constants); } - - // Sort our solutions by rank - std::sort(solutions.begin(), solutions.end(), - [](const auto& lhs, const auto& rhs) { - return lhs.rank > rhs.rank; - }); - - // Take top solutions - std::vector> sample; - std::copy( - solutions.begin(), - solutions.begin() + sample_size, - std::back_inserter(sample) - ); - solutions.clear(); - - if (count + 1 == num_of_generations) - { - std::copy( - sample.begin(), - sample.end(), - std::back_inserter(solutions) - ); - sample.clear(); - break; - } - - // Mutate the top solutions by % - std::uniform_real_distribution m((1 - mutation_percentage), (1 + mutation_percentage)); - std::for_each(sample.begin(), sample.end(), [&m, &device](auto& s) { - s.x *= m(device); - }); - - // Cross over not needed as it's only one value - - std::copy( - sample.begin(), - sample.end(), - std::back_inserter(solutions) - ); - sample.clear(); - } - - std::sort(solutions.begin(), solutions.end(), - [](const auto& lhs, const auto& rhs) { - return lhs.x < rhs.x; - }); - - std::vector ans; - for (auto& s : solutions) - { - ans.push_back(s.x); - } - return ans; - } - - template - double Function::solve_y(const double& x_val) const noexcept - { - std::vector exceptions; - - for (int i : constants) - exceptions.push_back(i != 0); - - double ans{ 0 }; - for (int i = lrgst_exp; i >= 0; i--) - { - if (exceptions[i]) - ans += constants[i] * POW(x_val, (lrgst_exp - i)); - } - - return ans; - } - } -} \ No newline at end of file diff --git a/Exponential/Source.cpp b/Exponential/Source.cpp index e7711a1..138de36 100644 --- a/Exponential/Source.cpp +++ b/Exponential/Source.cpp @@ -1,47 +1,92 @@ #include -#include -#include "FunctionsTemplate.h" +#include +#include +#include +#include "Exponential.h" #include "Timer.h" +using namespace JRAMPERSAD; + template -using Function = MATH::EXP::Function; +using Function = EXPONENTIAL::Function; typedef TIMER::Timer timer; +template +void CalcRoots(std::mutex& m, const Function& func, EXPONENTIAL::GA_Options options) +{ + m.lock(); + std::cout << "Starting calculation...\n"; + m.unlock(); + + timer t; + auto gr = func.get_real_roots(options); + t.SetEnd(); + + m.lock(); + std::cout << "Time took to calculate approx root values: " << t.GetTimeInS() << "s\n"; + std::cout << "Approximate values of x where y = 0 are: \n"; + std::for_each(gr.begin(), gr.end(), + [](const auto& val) { + std::cout << "x:" << val << '\n'; + }); + m.unlock(); +} + +template +void SolveX(std::mutex& m, const Function& func, EXPONENTIAL::GA_Options options, const double& y) +{ + timer t; + auto res = func.solve_x(y, options); + t.SetEnd(); + + m.lock(); + std::cout << "Time took to calculate approx x values: " << t.GetTimeInS() << "s\n"; + std::cout << "Approximate values of x where y = " << y << " are: \n"; + std::for_each(res.begin(), res.end(), + [](const auto& val) { + std::cout << "x:" << val << '\n'; + }); + m.unlock(); +} + int main() { std::vector vec{ 1, 5, 4 }; Function<2> f{ vec }; Function<3> g{ { 1, -6, 11, -6 } }; - timer t; + EXPONENTIAL::GA_Options options; + options.mutation_percentage = 0.005; + options.num_of_generations = 10; + options.sample_size = 1000; + options.data_size = 100000; + options.min_range = 4.9; + options.max_range = 5; - for (int i = 1; i < 2; i++) - { - t.Reset(); - auto gr = g.get_real_roots_ga(-100, 100, i, 1000, 100000, 0.005); - t.SetEnd(); + std::mutex m; + std::thread th(CalcRoots<3>, std::ref(m), std::cref(g), options); + //std::thread th1(SolveX<3>, std::ref(m), std::cref(g), options, 5); + //std::thread th2(SolveX<3>, std::ref(m), std::cref(g), options, 23); - std::cout << "Time took: " << t.GetTimeInS() << "s\n"; + //CalcRoots<3>(m, g); - - std::for_each(gr.begin(), gr.end(), - [](const auto& val) { - std::cout << "x:" << val << '\n'; - }); - } - - std::cout << g << " when x = 1\n" << "y = " << g.solve_y(1) << "\n\n"; - std::cout << g << " when x = 2\n" << "y = " << g.solve_y(2) << "\n\n"; - std::cout << g << " when x = 3\n" << "y = " << g.solve_y(3) << "\n\n"; + m.lock(); + std::cout << g << " when x = 4.961015\n" << "y = " << g.solve_y(4.961015) << "\n\n"; + //std::cout << g << " when x = 4.30891\n" << "y = " << g.solve_y(4.30891) << "\n\n"; + //std::cout << g << " when x = 2\n" << "y = " << g.solve_y(2) << "\n\n"; + //std::cout << g << " when x = 3\n" << "y = " << g.solve_y(3) << "\n\n"; //std::cout << "Median: " << MATH::MEDIAN(gr) << '\n'; //std::cout << "Mean: " << MATH::MEAN(gr) << '\n'; - //std::cout << g << '\n'; - //std::cout << fr[0] << ", " << fr[1] << '\n'; - //std::cout << f.get_y_intrcpt() << '\n'; - //std::cout << f.differential() << '\n'; + //std::cout << "Calculating Roots for function f(x) = " << g << '\n'; + //std::cout << "The y-intercept of the function f(x) is " << g.solve_y(0) << '\n'; + std::cout << "dy/dx of f(x) is " << g.differential() << '\n'; + m.unlock(); + th.join(); + //th1.join(); + //th2.join(); return 0; } \ No newline at end of file