107 lines
3.7 KiB
C
107 lines
3.7 KiB
C
#ifndef INCLUDED_C_LINEARREGRESSION_H
|
|||
|
|
#define INCLUDED_C_LINEARREGRESSION_H
|
||
|
|
|
||
|
|
#ifndef INCLUDED_C_BASE_H
|
||
|
|
#include <c_Base.h>
|
||
|
|
#endif /*INCLUDED_C_BASE_H*/
|
||
|
|
|
||
|
|
|
||
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||
|
|
/* */
|
||
|
|
|
||
|
|
// Linear Regression Model Context Structure
|
||
|
|
typedef struct {
|
||
|
|
double slope; // Slope coefficient (Beta)
|
||
|
|
double intercept; // Y-Intercept coefficient (Alpha)
|
||
|
|
c_bool_t is_trained;// State flag tracking model training status
|
||
|
|
} c_LinearRegression_t;
|
||
|
|
|
||
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||
|
|
/* */
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Initialize the Linear Regression Model context block.
|
||
|
|
*/
|
||
|
|
C_STATIC_FORCE_INLINE
|
||
|
|
c_err_t c_LinearRegression_Init(c_LinearRegression_t* model) {
|
||
|
|
if (model == NULL) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
model->slope = 0.0;
|
||
|
|
model->intercept = 0.0;
|
||
|
|
model->is_trained = C_FALSE;
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Train the model using Ordinary Least Squares (OLS) linear derivation math.
|
||
|
|
* Time Complexity: O(N) | Auxiliary Space: O(1) in-place
|
||
|
|
* @param model Pointer to the linear regression model context instance.
|
||
|
|
* @param x Contiguous array tracking independent variable observations.
|
||
|
|
* @param y Contiguous array tracking dependent variable target features.
|
||
|
|
* @param num Total number of data points inside the training array sets.
|
||
|
|
* @return C_ERR_OK if successful, C_ERR_PARAM for NULL targets,
|
||
|
|
* or C_ERR_INVALID if variance evaluates to zero (vertical line slope anomaly).
|
||
|
|
*/
|
||
|
|
C_STATIC_FORCE_INLINE
|
||
|
|
c_err_t c_LinearRegression_Fit(c_LinearRegression_t* model, const double* x, const double* y, c_size_t num) {
|
||
|
|
if (model == NULL || x == NULL || y == NULL) return C_ERR_PARAM;
|
||
|
|
if (num < 2) return C_ERR_PARAM; // Mandate at least two distinct points to draw a trend line
|
||
|
|
|
||
|
|
double sum_x = 0.0;
|
||
|
|
double sum_y = 0.0;
|
||
|
|
|
||
|
|
// Step 1: Calculate the arithmetic mean values for features X and Y
|
||
|
|
for (c_size_t i = 0; i < num; i++) {
|
||
|
|
sum_x += x[i];
|
||
|
|
sum_y += y[i];
|
||
|
|
}
|
||
|
|
double mean_x = sum_x / (double)num;
|
||
|
|
double mean_y = sum_y / (double)num;
|
||
|
|
|
||
|
|
double num_covariance = 0.0;
|
||
|
|
double den_variance = 0.0;
|
||
|
|
|
||
|
|
// Step 2: Accumulate sample covariance and independent feature variance maps
|
||
|
|
for (c_size_t i = 0; i < num; i++) {
|
||
|
|
double diff_x = x[i] - mean_x;
|
||
|
|
num_covariance += diff_x * (y[i] - mean_y);
|
||
|
|
den_variance += diff_x * diff_x;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 3: Guard against division-by-zero on perfectly vertical data layouts
|
||
|
|
if (den_variance == 0.0) {
|
||
|
|
model->is_trained = C_FALSE;
|
||
|
|
return C_ERR_INVALID;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 4: Map final slope and intercept boundary coefficients
|
||
|
|
model->slope = num_covariance / den_variance;
|
||
|
|
model->intercept = mean_y - (model->slope * mean_x);
|
||
|
|
model->is_trained = C_TRUE;
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Inference Lookahead: Predict the output target value for a specific input feature.
|
||
|
|
* @param model Pointer to the constant trained model instance.
|
||
|
|
* @param x The input independent scalar variable point.
|
||
|
|
* @param out_val Pointer to the destination variable where the predicted Y value is written.
|
||
|
|
* @return C_ERR_OK if successful, or C_ERR_INVALID if the model is untrained.
|
||
|
|
*/
|
||
|
|
C_STATIC_FORCE_INLINE
|
||
|
|
c_err_t c_LinearRegression_Predict(const c_LinearRegression_t* model, double x, double* out_val) {
|
||
|
|
if (model == NULL || out_val == NULL) return C_ERR_PARAM;
|
||
|
|
if (!model->is_trained) return C_ERR_INVALID;
|
||
|
|
|
||
|
|
// Execute standard linear function lookup: y = alpha + beta * x
|
||
|
|
*out_val = model->intercept + (model->slope * x);
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#endif /*INCLUDED_C_LINEARREGRESSION_H*/
|