diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 5c13d8c44..137b37fd3 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -12,10 +12,23 @@ app_objs += $(addprefix apps/regression/,\ graph_controller.o\ graph_view.o\ initialisation_parameter_controller.o\ + levenberg_marquardt.o\ prediction_parameter_controller.o\ + regression_context.o\ store.o\ store_controller.o\ - regression_context.o\ +) + +app_objs += $(addprefix apps/regression/model/,\ + cubic_model.o\ + exponential_model.o\ + linear_model.o\ + logarithmic_model.o\ + logistic_model.o\ + power_model.o\ + quadratic_model.o\ + quartic_model.o\ + trigonometric_model.o\ ) i18n_files += $(addprefix apps/regression/,\ diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 522eb77cd..bd200d79a 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -6,6 +6,7 @@ #include "../../poincare/src/layout/vertical_offset_layout.h" #include #include +#include "levenberg_marquardt.h" using namespace Poincare; using namespace Shared; @@ -47,6 +48,9 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { } void CalculationController::didBecomeFirstResponder() { + LevenbergMarquartd lm(m_store); + double modelCoefficients[] = {1.0, 1.0, 1.0, 1.0, 1.0}; + lm.fit(modelCoefficients, const_cast(static_cast(app()->container()))->globalContext()); if (selectedRow() == -1) { selectCellAtLocation(1, 0); } else { diff --git a/apps/regression/levenberg_marquardt.cpp b/apps/regression/levenberg_marquardt.cpp new file mode 100644 index 000000000..009622bbf --- /dev/null +++ b/apps/regression/levenberg_marquardt.cpp @@ -0,0 +1,133 @@ +#include "levenberg_marquardt.h" +#include +#include + +using namespace Poincare; +using namespace Shared; + +namespace Regression { + +void LevenbergMarquartd::fit(double * modelCoefficients, Context * context) { + double currentChi2 = chi2(modelCoefficients); + double lambda = k_initialLambda; + int n = m_model.numberOfCoefficients(); // n unknown coefficients + int smallChi2ChangeCounts = 0; + int iterationCount = 0; + while (smallChi2ChangeCounts < k_consecutiveSmallChi2ChangesLimit && iterationCount < k_maxIterations) { + // Compute modelCoefficients step + // Create the alpha matrix + Expression * coefficientsAPrime[RegressionModel::k_maxNumberOfCoefficients][RegressionModel::k_maxNumberOfCoefficients]; // TODO find a way not to use so much space, we only need Expression * coefficientsAPrime[n][n] + // TODO compute only half the matrix, it is symmetric. + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + coefficientsAPrime[i][j] = new Complex(Complex::Float((alphaPrimeCoefficient(modelCoefficients, i, j, lambda)))); + } + } + // Create the beta matrix + Expression ** operandsB = new Expression * [n]; + for (int j = 0; j < n; j++) { + operandsB[j] = new Complex(Complex::Float(betaCoefficient(modelCoefficients, j))); + } + double modelCoefficientSteps[n]; + solveLinearSystem(modelCoefficientSteps, coefficientsAPrime, operandsB, n, context); + + // Compare new chi2 with the previous value + double newModelCoefficients[n]; + for (int i = 0; i < n; i++) { + newModelCoefficients[i] = modelCoefficients[i] + modelCoefficientSteps[i]; + } + double newChi2 = chi2(newModelCoefficients); + smallChi2ChangeCounts = (fabs(currentChi2 - newChi2) > k_chi2ChangeCondition) ? 0 : smallChi2ChangeCounts + 1; + if (newChi2 >= currentChi2) { + lambda*= k_lambdaFactor; + } else { + lambda/= k_lambdaFactor; + for (int i = 0; i < n; i++) { + modelCoefficients[i] = newModelCoefficients[i]; + } + currentChi2 = newChi2; + } + iterationCount++; + } +} + +double LevenbergMarquartd::chi2(double * modelCoefficients) const { + double result = 0.0; + for (int i = 0; i < m_store->numberOfPairsOfSeries(m_series); i++) { + double xi = m_store->get(m_series, 0, i); + double yi = m_store->get(m_series, 1, i); + double difference = yi - m_model.evaluate(modelCoefficients, xi); + result += difference * difference; + } + return result; +} + +// a'(k,k) = a(k,k) * (1 + lambda) +// a'(k,l) = a(l,k) when (k != l) +double LevenbergMarquartd::alphaPrimeCoefficient(double * modelCoefficients, int k, int l, double lambda) const { + assert(k >= 0 && k < m_model.numberOfCoefficients()); + assert(l >= 0 && l < m_model.numberOfCoefficients()); + double result = k == l ? alphaCoefficient(modelCoefficients, k, l)*(1+lambda) : alphaCoefficient(modelCoefficients, l, k); + return result; +} + +// a(k,l) = sum(0, N-1, derivate(y(xi|a), ak) * derivate(y(xi|a), a)) +double LevenbergMarquartd::alphaCoefficient(double * modelCoefficients, int k, int l) const { + assert(k >= 0 && k < m_model.numberOfCoefficients()); + assert(l >= 0 && l < m_model.numberOfCoefficients()); + double result = 0.0; + int m = m_store->numberOfPairsOfSeries(m_series); + for (int i = 0; i < m; i++) { + double xi = m_store->get(m_series, 0, i); + result += m_model.partialDerivate(modelCoefficients, k, xi) * m_model.partialDerivate(modelCoefficients, l, xi); + } + return result; +} + +// b(k) = sum(0, N-1, (yi - y(xi|a)) * derivate(y(xi|a), ak)) +double LevenbergMarquartd::betaCoefficient(double * modelCoefficients, int k) const { + assert(k >= 0 && k < m_model.numberOfCoefficients()); + double result = 0.0; + int m = m_store->numberOfPairsOfSeries(m_series); // m equations + for (int i = 0; i < m; i++) { + double xi = m_store->get(m_series, 0, i); + double yi = m_store->get(m_series, 1, i); + result += (yi - m_model.evaluate(modelCoefficients, xi)) * m_model.partialDerivate(modelCoefficients, k, xi); + } + return result; +} + + +// TODO should return an error if no solution ? +void LevenbergMarquartd::solveLinearSystem(double * solutions, Expression * coefficients[RegressionModel::k_maxNumberOfCoefficients][RegressionModel::k_maxNumberOfCoefficients], Expression * * constants, int solutionDimension, Context * context) { + int n = solutionDimension; + const Expression ** operandsA = new const Expression * [n*n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + operandsA[i*n+j] = coefficients[i][j]; + } + } + Matrix * AMatrix = new Matrix(operandsA, n, n, false); + delete[] operandsA; + Matrix * BMatrix = new Matrix(constants, n, 1, false); + Expression::AngleUnit angleUnit = Preferences::sharedPreferences()->angleUnit(); + Matrix * matrixInverse = AMatrix->createApproximateInverse(); + assert(matrixInverse != nullptr); + Matrix * result = Multiplication::computeOnMatrices(const_cast(matrixInverse), const_cast(BMatrix)); + Expression * exactSolutions[n]; + for (int i = 0; i < n; i++) { + Expression * sol = result->matrixOperand(i,0); + exactSolutions[i] = sol; + result->detachOperand(sol); + Expression::Simplify(&exactSolutions[i], *context, angleUnit); + assert(exactSolutions[i] != nullptr); + solutions[i] = exactSolutions[i]->approximateToScalar(*context, angleUnit); + delete exactSolutions[i]; + } + delete result; + delete matrixInverse; + delete BMatrix; + delete AMatrix; +} + +} diff --git a/apps/regression/levenberg_marquardt.h b/apps/regression/levenberg_marquardt.h new file mode 100644 index 000000000..c214dc2c0 --- /dev/null +++ b/apps/regression/levenberg_marquardt.h @@ -0,0 +1,49 @@ +#ifndef REGRESSION_LEVENBERG_MARQUARDT_H +#define REGRESSION_LEVENBERG_MARQUARDT_H + +#include +#include "model/cubic_model.h" +#include "model/exponential_model.h" +#include "model/linear_model.h" +#include "model/logarithmic_model.h" +#include "model/logistic_model.h" +#include "model/power_model.h" +#include "model/quadratic_model.h" +#include "model/quartic_model.h" +#include "model/regression_model.h" +#include "model/trigonometric_model.h" +#include "store.h" + +namespace Regression { + +class LevenbergMarquartd { +public: + LevenbergMarquartd(Store * store) : + m_store(store), + m_series(0), + m_model() + { + } + void fit(double * modelCoefficients, Poincare::Context * context); +private: + static constexpr double k_maxIterations = 1000; + static constexpr double k_initialLambda = 0.001; + static constexpr double k_lambdaFactor = 10; + static constexpr double k_chi2ChangeCondition = 0.0005; + static constexpr int k_consecutiveSmallChi2ChangesLimit = 10; + + double chi2(double * modelCoefficients) const; + double alphaPrimeCoefficient(double * modelCoefficients, int k, int l, double lambda) const; + double alphaCoefficient(double * modelCoefficients, int k, int l) const; + double betaCoefficient(double * modelCoefficients, int k) const; + void solveLinearSystem(double * solutions, Poincare::Expression * coefficients[RegressionModel::k_maxNumberOfCoefficients][RegressionModel::k_maxNumberOfCoefficients], Poincare::Expression * * constants, int solutionDimension, Poincare::Context * context); + + Store * m_store; + int m_series; //TODO put as argument + LogisticModel m_model; //TODO put as argument +}; + +} + + +#endif diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp new file mode 100644 index 000000000..e6ad3d17d --- /dev/null +++ b/apps/regression/model/cubic_model.cpp @@ -0,0 +1,36 @@ +#include "cubic_model.h" +#include +#include + +namespace Regression { + +double CubicModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + double d = modelCoefficients[3]; + return a*x*x*x+b*x*x+c*x+d; +} + +double CubicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x^3 + return x*x*x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: x^2 + return x*x; + } + if (derivateCoefficientIndex == 2) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 3) { + // Derivate: 1 + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/cubic_model.h b/apps/regression/model/cubic_model.h new file mode 100644 index 000000000..8a23647c6 --- /dev/null +++ b/apps/regression/model/cubic_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_CUBIC_MODEL_H +#define REGRESSION_CUBIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class CubicModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 4; } +}; + +} + + +#endif diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp new file mode 100644 index 000000000..23a346e76 --- /dev/null +++ b/apps/regression/model/exponential_model.cpp @@ -0,0 +1,28 @@ +#include "exponential_model.h" +#include +#include + +namespace Regression { + +double ExponentialModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + return a*exp(b*x); +} + +double ExponentialModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + if (derivateCoefficientIndex == 0) { + // Derivate: exp(b*x) + return exp(b*x); + } + if (derivateCoefficientIndex == 1) { + // Derivate: a*x*exp(b*x) + return a*x*exp(b*x); + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/exponential_model.h b/apps/regression/model/exponential_model.h new file mode 100644 index 000000000..4d59a7137 --- /dev/null +++ b/apps/regression/model/exponential_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_EXPONENTIAL_MODEL_H +#define REGRESSION_EXPONENTIAL_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class ExponentialModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp new file mode 100644 index 000000000..3c65ed996 --- /dev/null +++ b/apps/regression/model/linear_model.cpp @@ -0,0 +1,26 @@ +#include "linear_model.h" +#include +#include + +namespace Regression { + +double LinearModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + return a*x+b; +} + +double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: 1; + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/linear_model.h b/apps/regression/model/linear_model.h new file mode 100644 index 000000000..ae8e2a25e --- /dev/null +++ b/apps/regression/model/linear_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_LINEAR_MODEL_H +#define REGRESSION_LINEAR_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class LinearModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp new file mode 100644 index 000000000..f9fae1358 --- /dev/null +++ b/apps/regression/model/logarithmic_model.cpp @@ -0,0 +1,26 @@ +#include "logarithmic_model.h" +#include +#include + +namespace Regression { + +double LogarithmicModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + return a*log(x)+b; +} + +double LogarithmicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: ln(x) + return log(x); + } + if (derivateCoefficientIndex == 1) { + // Derivate: 1 + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/logarithmic_model.h b/apps/regression/model/logarithmic_model.h new file mode 100644 index 000000000..1834c9590 --- /dev/null +++ b/apps/regression/model/logarithmic_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_LOGARITHMIC_MODEL_H +#define REGRESSION_LOGARITHMIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class LogarithmicModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp new file mode 100644 index 000000000..8bc35a140 --- /dev/null +++ b/apps/regression/model/logistic_model.cpp @@ -0,0 +1,35 @@ +#include "logistic_model.h" +#include +#include + +namespace Regression { + +double LogisticModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + return c/(1.0+a*exp(-b*x)); +} + +double LogisticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + double denominator = 1.0+a*exp(-b*x); + if (derivateCoefficientIndex == 0) { + // Derivate: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2) + return -exp(-b*x) * c/(denominator * denominator); + } + if (derivateCoefficientIndex == 1) { + // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) + return x*a*exp(-b*x)*c/(denominator * denominator); + } + if (derivateCoefficientIndex == 2) { + // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) + return 1.0/denominator; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/logistic_model.h b/apps/regression/model/logistic_model.h new file mode 100644 index 000000000..08e185384 --- /dev/null +++ b/apps/regression/model/logistic_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_LOGISTIC_MODEL_H +#define REGRESSION_LOGISTIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class LogisticModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 3; } +}; + +} + + +#endif diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp new file mode 100644 index 000000000..e546b2264 --- /dev/null +++ b/apps/regression/model/power_model.cpp @@ -0,0 +1,32 @@ +#include "power_model.h" +#include +#include + +namespace Regression { + +double PowerModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + return a*pow(x,b); +} + +double PowerModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + if (derivateCoefficientIndex == 0) { + // Derivate: pow(x,b) + return pow(x,b); + } + if (derivateCoefficientIndex == 1) { + assert(x >= 0); + /* We assume all xi are positive. + * For x = 0, a*pow(x,b) = 0, the partial derivate along b is 0 + * For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate along b is + * ln(x)*a*pow(x,b) */ + return x == 0 ? 0 : a*log(x)*exp(b*x); + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/power_model.h b/apps/regression/model/power_model.h new file mode 100644 index 000000000..f8a9c21f8 --- /dev/null +++ b/apps/regression/model/power_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_POWER_MODEL_H +#define REGRESSION_POWER_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class PowerModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp new file mode 100644 index 000000000..d0da368e8 --- /dev/null +++ b/apps/regression/model/quadratic_model.cpp @@ -0,0 +1,31 @@ +#include "quadratic_model.h" +#include +#include + +namespace Regression { + +double QuadraticModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + return a*x*x+b*x+c; +} + +double QuadraticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x^2 + return x*x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 2) { + // Derivate: 1 + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/quadratic_model.h b/apps/regression/model/quadratic_model.h new file mode 100644 index 000000000..c7730f82e --- /dev/null +++ b/apps/regression/model/quadratic_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_QUADRATIC_MODEL_H +#define REGRESSION_QUADRATIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class QuadraticModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 3; } +}; + +} + + +#endif diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp new file mode 100644 index 000000000..bf109f823 --- /dev/null +++ b/apps/regression/model/quartic_model.cpp @@ -0,0 +1,41 @@ +#include "quartic_model.h" +#include +#include + +namespace Regression { + +double QuarticModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + double d = modelCoefficients[3]; + double e = modelCoefficients[4]; + return a*x*x*x*x+b*x*x*x+c*x*x+d*x+e; +} + +double QuarticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x^4 + return x*x*x*x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: x^3 + return x*x*x; + } + if (derivateCoefficientIndex == 2) { + // Derivate: x^2 + return x*x; + } + if (derivateCoefficientIndex == 3) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 4) { + // Derivate: 1 + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/quartic_model.h b/apps/regression/model/quartic_model.h new file mode 100644 index 000000000..dbe16b467 --- /dev/null +++ b/apps/regression/model/quartic_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_QUARTIC_MODEL_H +#define REGRESSION_QUARTIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class QuarticModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 5; } +}; + +} + + +#endif diff --git a/apps/regression/model/regression_model.h b/apps/regression/model/regression_model.h new file mode 100644 index 000000000..1ae650d8a --- /dev/null +++ b/apps/regression/model/regression_model.h @@ -0,0 +1,16 @@ +#ifndef REGRESSION_REGRESSION_MODEL_H +#define REGRESSION_REGRESSION_MODEL_H + +namespace Regression { + +class RegressionModel { +public: + static constexpr int k_maxNumberOfCoefficients = 5; + virtual double evaluate(double * modelCoefficients, double x) const = 0; + virtual double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const = 0; + virtual double numberOfCoefficients() const = 0; +}; + +} + +#endif diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp new file mode 100644 index 000000000..f40aef511 --- /dev/null +++ b/apps/regression/model/trigonometric_model.cpp @@ -0,0 +1,34 @@ +#include "trigonometric_model.h" +#include +#include + +namespace Regression { + +double TrigonometricModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + return a*sin(b*x+c); +} + +double TrigonometricModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + double c = modelCoefficients[2]; + if (derivateCoefficientIndex == 0) { + // Derivate: sin(b*x+c) + return sin(b*x+c); + } + if (derivateCoefficientIndex == 1) { + // Derivate: x*a*cos(b*x+c); + return x*a*cos(b*x+c); + } + if (derivateCoefficientIndex == 2) { + // Derivate: a*cos(b*x+c) + return a*cos(b*x+c); + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/trigonometric_model.h b/apps/regression/model/trigonometric_model.h new file mode 100644 index 000000000..02df9f55f --- /dev/null +++ b/apps/regression/model/trigonometric_model.h @@ -0,0 +1,19 @@ +#ifndef REGRESSION_TRIGONOMETRIC_MODEL_H +#define REGRESSION_TRIGONOMETRIC_MODEL_H + +#include "regression_model.h" + +namespace Regression { + +class TrigonometricModel : public RegressionModel { +public: + using RegressionModel::RegressionModel; + double evaluate(double * modelCoefficients, double x) const override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + double numberOfCoefficients() const override { return 3; } +}; + +} + + +#endif