From 5e568c95fba476fb8123ea17dd2975293d47a6a8 Mon Sep 17 00:00:00 2001 From: Jonathan Rampersad Date: Tue, 5 Sep 2023 09:54:19 -0400 Subject: [PATCH] Add project files. --- Exponential.sln | 31 ++ Exponential/Exponential.vcxproj | 147 ++++++++ Exponential/Exponential.vcxproj.filters | 30 ++ Exponential/FunctionsTemplate.h | 475 ++++++++++++++++++++++++ Exponential/Source.cpp | 45 +++ Exponential/Timer.h | 42 +++ 6 files changed, 770 insertions(+) create mode 100644 Exponential.sln create mode 100644 Exponential/Exponential.vcxproj create mode 100644 Exponential/Exponential.vcxproj.filters create mode 100644 Exponential/FunctionsTemplate.h create mode 100644 Exponential/Source.cpp create mode 100644 Exponential/Timer.h diff --git a/Exponential.sln b/Exponential.sln new file mode 100644 index 0000000..781745c --- /dev/null +++ b/Exponential.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Exponential", "Exponential\Exponential.vcxproj", "{74C04891-9509-4EA4-BC52-6D86492B203A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x64.ActiveCfg = Debug|x64 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x64.Build.0 = Debug|x64 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x86.ActiveCfg = Debug|Win32 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x86.Build.0 = Debug|Win32 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x64.ActiveCfg = Release|x64 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x64.Build.0 = Release|x64 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x86.ActiveCfg = Release|Win32 + {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C33279A4-898B-46DE-9C0E-87EE72B702AF} + EndGlobalSection +EndGlobal diff --git a/Exponential/Exponential.vcxproj b/Exponential/Exponential.vcxproj new file mode 100644 index 0000000..07d448b --- /dev/null +++ b/Exponential/Exponential.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {74c04891-9509-4ea4-bc52-6d86492b203a} + Exponential + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + false + $(SolutionDir) + NoListing + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + false + $(SolutionDir) + NoListing + + + Console + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/Exponential/Exponential.vcxproj.filters b/Exponential/Exponential.vcxproj.filters new file mode 100644 index 0000000..b9ec802 --- /dev/null +++ b/Exponential/Exponential.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Exponential/FunctionsTemplate.h b/Exponential/FunctionsTemplate.h new file mode 100644 index 0000000..49fcb7b --- /dev/null +++ b/Exponential/FunctionsTemplate.h @@ -0,0 +1,475 @@ +#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: + 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; + + [[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 + + // 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; + } + + // 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); + + 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) + { + for (auto& val : this->constants) + val *= c; + + return *this; + } + + // 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] }; } + + }; + + 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; + } + + 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; + } + + const double root1 = (NEGATE(b) + sqrt(sqr_val)) / 2 * a; + const double root2 = (NEGATE(b) - sqrt(sqr_val)) / 2 * a; + + res.push_back(root1); + res.push_back(root2); + 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}; + } + } +} \ No newline at end of file diff --git a/Exponential/Source.cpp b/Exponential/Source.cpp new file mode 100644 index 0000000..a161957 --- /dev/null +++ b/Exponential/Source.cpp @@ -0,0 +1,45 @@ +#include +#include +#include "FunctionsTemplate.h" +#include "Timer.h" + +template +using Function = MATH::EXP::Function; + +typedef TIMER::Timer timer; + +int main() +{ + std::vector vec{ 1, 5, 4 }; + Function<2> f{ vec }; + Function<3> g{ { 1, -6, 11, -6 } }; + + auto res = f * 2; + std::cout << res << '\n'; + + timer t; + + for (int i = 11; i < 2; i++) + { + t.Reset(); + auto gr = g.get_real_roots_ga(-100, 100, i, 1000, 100000, 0.005); + t.SetEnd(); + + std::cout << "Time took: " << t.GetTimeInS() << "s\n"; + + + std::for_each(gr.begin(), gr.end(), + [](const auto& val) { + std::cout << "x:" << val << '\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'; + + return 0; +} \ No newline at end of file diff --git a/Exponential/Timer.h b/Exponential/Timer.h new file mode 100644 index 0000000..1ec849e --- /dev/null +++ b/Exponential/Timer.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +namespace TIMER{ + struct Timer + { + std::chrono::time_point start, end; + std::chrono::duration duration; + + Timer() + { + Reset(); + } + + ~Timer() + { + + } + + inline void Reset() noexcept + { + start = std::chrono::high_resolution_clock::now(); + } + + void SetEnd() noexcept + { + end = std::chrono::high_resolution_clock::now(); + duration = end - start; + } + + inline float GetTimeInMS() const noexcept + { + return float(duration.count() * 1000.f); + } + + inline float GetTimeInS() const noexcept + { + return float(duration.count()); + } + }; +} \ No newline at end of file