0xC000041D: An unhandled exception was encountered during a user callback - c

I am encountering the error '0xC000041D: An unhandled exception was encountered during a user callback', when I run the OpenGL code. Th debugger indicates that the error occurs here:
The function where the error occurs and calling functions are given below.
Could you please help me identify the cause for this error.
double maxDistance(Point_2D* bez, int deg)
{
int i;
double maxheight;
double height[30];
// Computing baseline vector and its magnitude
Point_2D baselineVector;
baselineVector.x = bez[deg].x - bez[0].x;
baselineVector.y = bez[deg].y - bez[0].y;
double baselineMag = sqrt((baselineVector.x*baselineVector.x) + (baselineVector.y*baselineVector.y));
double crossprod[3];
double crossprodMag;
// Computing height of a intermediate control points from baseline
Point_2D cpVector; // Vector from first control point to intermediate control point
for (i = 1; i < deg; i++)
{
cpVector.x = bez[i].x - bez[0].x;
cpVector.y = bez[i].y - bez[0].y;
// Computing cross product of baseline vector and control point vector
//z coordinates of baseline and control point vectors are 0
crossprod[0] = ((baselineVector.y * 0) - (0 * cpVector.y));
crossprod[1] = -((baselineVector.x * 0) - (0 * cpVector.x));
crossprod[2] = ((baselineVector.x * cpVector.y) - (baselineVector.y * cpVector.x));
crossprodMag = sqrt((crossprod[0] * crossprod[0]) + (crossprod[1] * crossprod[1]) + (crossprod[2] * crossprod[2]));
height[i] = crossprodMag / baselineMag;
}
// Finding maximum height of a control point from baseline
maxheight = height[0];
for (i = 0; i < deg; i++)
{
if (maxheight < height[i])
maxheight = height[i];
}
return maxheight;
}
void plotBezier(Point_2D* bez, int deg)
{
Point_2D* leftBez= (Point_2D*)malloc((deg + 1)*sizeof(Point_2D));
Point_2D* rightBez=(Point_2D*)malloc((deg + 1)*sizeof(Point_2D));;
double height = maxDistance(bez, deg);
if (height < flat_thresh)
{
drawLine(bez[0], bez[deg]);
return;
}
else
{
midSubdivideBezier(bez, deg, leftBez, rightBez);
plotBezier(leftBez, deg);
plotBezier(rightBez, deg);
}
free(leftBez);
free(rightBez);
}
//============================================================
void adaptiveRender()
{
Point_2D* bez =(Point_2D*) malloc(30 * sizeof(Point_2D)); // assume the degree is not greater than 29.
int i;
for (i = bcr.degree; i< bcr.deBoor_count; i++) // Determining segments between the kth knot and the (n+1)th knot
{
if (!(fabs(bcr.knots[i] - bcr.knots[i + 1]) < 0.00001)) // No segment when adjacent knots are equal
{
extractBezier(bez, i); // Extract the i-th Bezier curve
plotBezier(bez, bcr.degree); // Adaptively plot a Bezier curve
}
}
free(bez);
}

Related

Writing a wave generator with SDL

I've coded a simple sequencer in C with SDL 1.2 and SDL_mixer(to play .wav file). It works well and I want to add some audio synthesis to this program. I've look up the and I found this sinewave code using SDL2(https://github.com/lundstroem/synth-samples-sdl2/blob/master/src/synth_samples_sdl2_2.c)
Here's how the sinewave is coded in the program:
static void build_sine_table(int16_t *data, int wave_length)
{
/*
Build sine table to use as oscillator:
Generate a 16bit signed integer sinewave table with 1024 samples.
This table will be used to produce the notes.
Different notes will be created by stepping through
the table at different intervals (phase).
*/
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static double get_pitch(double note) {
/*
Calculate pitch from note value.
offset note by 57 halfnotes to get correct pitch from the range we have chosen for the notes.
*/
double p = pow(chromatic_ratio, note - 57);
p *= 440;
return p;
}
static void audio_callback(void *unused, Uint8 *byte_stream, int byte_stream_length) {
/*
This function is called whenever the audio buffer needs to be filled to allow
for a continuous stream of audio.
Write samples to byteStream according to byteStreamLength.
The audio buffer is interleaved, meaning that both left and right channels exist in the same
buffer.
*/
// zero the buffer
memset(byte_stream, 0, byte_stream_length);
if(quit) {
return;
}
// cast buffer as 16bit signed int.
Sint16 *s_byte_stream = (Sint16*)byte_stream;
// buffer is interleaved, so get the length of 1 channel.
int remain = byte_stream_length / 2;
// split the rendering up in chunks to make it buffersize agnostic.
long chunk_size = 64;
int iterations = remain/chunk_size;
for(long i = 0; i < iterations; i++) {
long begin = i*chunk_size;
long end = (i*chunk_size) + chunk_size;
write_samples(s_byte_stream, begin, end, chunk_size);
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
/*
get correct phase increment for note depending on sample rate and table length.
*/
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
/*
loop through the buffer and write samples.
*/
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_wave_table[phase_int];
sample *= 0.6; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
I don't understand how I could change the sinewave formula to genrate other waveform like square/triangle/saw ect...
EDIT:
Because I forgot to explain it, here's what I tried.
I followed the example I've seen on this video series(https://www.youtube.com/watch?v=tgamhuQnOkM). The source code of the method provided by the video is on github, and the wave generation code is looking like this:
double w(double dHertz)
{
return dHertz * 2.0 * PI;
}
// General purpose oscillator
double osc(double dHertz, double dTime, int nType = OSC_SINE)
{
switch (nType)
{
case OSC_SINE: // Sine wave bewteen -1 and +1
return sin(w(dHertz) * dTime);
case OSC_SQUARE: // Square wave between -1 and +1
return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
case OSC_TRIANGLE: // Triangle wave between -1 and +1
return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
}
Because the C++ code here uses windows soun api I could not copy/paste this method to make it work on the piece of code I've found using SDL2.
So I tried to this in order to obtain a square wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = ((2.0f * pi) / (double)wave_length) > 0 ? 1.0 : -1.0;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This didn't gave me a square wave but more a saw wave.
Here's what I tried to get a triangle wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(asin(sin(current_phase) * INT16_MAX)) * (2 / pi);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This also gave me another type of waveform, not triangle.
You’d replace the sin function call with call to one of the following:
// this is a helper function only
double normalize(double phase)
{
double cycles = phase/(2.0*M_PI);
phase -= trunc(cycles) * 2.0 * M_PI;
if (phase < 0) phase += 2.0*M_PI;
return phase;
}
double square(double phase)
{ return (normalize(phase) < M_PI) ? 1.0 : -1.0; }
double sawtooth(double phase)
{ return -1.0 + normalize(phase) / M_PI; }
double triangle(double phase)
{
phase = normalize(phase);
if (phase >= M_PI)
phase = 2*M_PI - phase;
return -1.0 + 2.0 * phase / M_PI;
}
You’d be building tables just like you did for the sine, except they’d be the square, sawtooth and triangle tables, respectively.

Backpropagation algorithm doesn't guess MNIST data at all

I have been trying to write an artificial neural network that uses backpropagation and I have been trying to train it to recognize digits by using MNIST data. For xor training, this program can complete the training with 63000 epochs. I use 0.5 learning rate for both the weights and biases. For xor, I make only one hidden layer, and 2 neurons for that layer. It seems to work. But when it coems to MNIST, I use 20 neurons and 1 hidden layer, 3 learning rate for both weights and biases (I also tried smaller or higher learning rates but this looked like a good number) but even with 60000 images, it does not come close to guessing numbers. Sometimes it guesses only one number, like 1 4 9, sometimes it doesn't guess anything. I trained it for 450 epochs but it still didn't change anything. The full source code is here: https://github.com/Fethbita/perceptron/blob/master/backprop_mnist_rewrite.c
I thought that there was a problem with my backpropagation algorithm but I couldn't find any after a weeks search. Can you help me finding my problem?
// testing the images on the network
// I test by looking at all the outputs and check every number to see if any of them is > 0.9 and if it is, I compare it with the known output of the image.
double outputs_test[number_of_outputs + hidden_layers * hidden];
int correct = 0;
for (int i = 0; i < training_data_size; i++)
{
feed_forward(train_x + inputs * i, outputs_test, w, bias);
int maxind = -1;
int num = 0;
for (int j = 0; j < number_of_outputs; j++)
{
int output_n = hidden_layers * hidden + j;
if (outputs_test[output_n] > 0.9 && maxind == -1)
{
maxind = j;
}
else if(outputs_test[output_n] > 0.9 && maxind > -1)
{
maxind = -2;
}
if (train_t[i][j])
{
num = j;
}
}
if (num == maxind)
{
correct++;
}
}
printf("Total correct guesses in this epoch = %d\n\n", correct);
////////////////
I have these function protoypes and functions.
void feed_forward(double* train_input, double* outputs, double* w, double* bias);
int train(double* train_x, double** train_t, double* w, double* bias);
double activation_function(int number_of_weights, const double* w, const double* x, const double* bias);
double sigmoid(double x);
I won't write the train function in it's fullest but here is the part that does the backpropagation;
int train(double* train_x, double** train_t, double* w, double* bias)
// train_x: the address of the inputs. For example for the MNIST data the first 784 double corresponds to the first image, the second to the second image etc.
// train_t: the address that corresponds to the outputs. Each pointer in the array points to an array of doubles that corresponds to the outputs. For the MNIST data the first pointer points to 10 doubles and if the corresponding image is 4, the double array is like this: [0] = 0.0 [1] = 0.0 [2] = 0.0 [3] = 0.0 [4] = 1.0 [5] = 0.0 [6] = 0.0 [7] = 0.0 [8] = 0.0 [9] = 0.0
// w: the addresses of the weights
// bias: the addresses of the biases
{
total_error = 0;
int train_out_index = 0;
double* train_output;
for (double* train_input = train_x; train_input < train_x + training_data_size * inputs; train_input += inputs)
{
train_output = train_t[train_out_index];
feed_forward(train_input, outputs, w, bias);
for (int i = 0; i < number_of_outputs; i++)
{
// calculates the error but I don't understand why we multiply it with 0.5
// If the network guesses every number wrong, the maximum this total_error can be (0.5 for each output neuron, and since there are 10 output neurons, it can be 5 for each input. Since there are 60000 images, it can be 300000 in total. And since we divide it by the number of inputs at the end,) it can be 300000/60000 = 5. But what does this 5 mean? It is 5 if every output the network guesses is wrong.
int output_n = hidden_layers * hidden + i;
total_error += .5 * (train_output[i] - outputs[output_n]) * (train_output[i] - outputs[output_n]);
}
//================= calculates the deltas
// backward pass
memset(deltas, 0, (size_t)total_weights * sizeof(double));
// output layer (hidden to output)
for (int i = number_of_outputs * hidden; i > 0; i--)
{
int a_index = (hidden_layers * hidden) + number_of_outputs - 1 - (i - 1) / hidden;
int target_index = number_of_outputs - 1 - (i - 1) / hidden;
deltas[total_weights - i] = (outputs[a_index] - train_output[target_index]) * outputs[a_index] * (1 - outputs[a_index]);
}
// rest of the layers
for (int i = total_weights - number_of_outputs * hidden - 1; i >= 0; i--)
{
// first hidden layer (input to hidden)
if (i < inputs * hidden && hidden_layers > 1)
{
for (int j = 0; j < hidden; j++)
{
int w_index = inputs * hidden + j * hidden + (i / inputs);
deltas[i] += deltas[w_index] * w[w_index];
}
deltas[i] *= outputs[i / inputs] * (1 - outputs[i / inputs]);
}
// last hidden layer (hidden to hidden) or first hidden layer when there's only one hidden layer
else if ((i > inputs * hidden + hidden * hidden * (hidden_layers - 2) && hidden_layers > 1) || (i < inputs * hidden && hidden_layers == 1))
{
for (int j = 0; j < number_of_outputs; j++)
{
int w_index = (inputs * hidden + hidden * hidden * (hidden_layers - 1)) + j * hidden + (i / inputs);
deltas[i] += deltas[w_index] * w[w_index];
}
int a_j_index;
if (hidden_layers == 1)
{
a_j_index = i / inputs;
}
else
{
a_j_index = (i - inputs * hidden) / hidden + hidden;
}
deltas[i] *= outputs[a_j_index] * (1 - outputs[a_j_index]);
}
// other hidden layers (hidden to hidden)
else
{
for (int j = 0; j < hidden; j++)
{
int next_layer_first_index = ((i - inputs * hidden) / (hidden * hidden) + 1) * hidden * hidden + (inputs * hidden);
int w_index = next_layer_first_index + j * hidden + ((i - inputs * hidden) / hidden) % hidden;
deltas[i] += deltas[w_index] * w[w_index];
}
int a_j_index = (i - inputs * hidden) / hidden + hidden;
deltas[i] *= outputs[a_j_index] * (1 - outputs[a_j_index]);
}
}
//=================
// update the weights
for (int i = 0; i < total_weights; i++)
{
// update biases
if (i <= hidden_layers)
{
if (i == 0)
{
bias[i] -= bias_learning_rate * deltas[0];
}
else
{
bias[i] -= bias_learning_rate * deltas[hidden * inputs + (i - 1) * hidden * hidden];
}
}
// first hidden layer (input to hidden)
if (i < inputs * hidden)
{
deltas[i] *= train_input[i % inputs];
}
// output layer
else if (i >= total_weights - number_of_outputs * hidden)
{
deltas[i] *= outputs[hidden * (hidden_layers - 1) + i % hidden];
}
// other hidden layers (hidden to hidden)
else
{
deltas[i] *= outputs[(((i - hidden * inputs) / hidden) / hidden) * hidden + i % hidden];
}
w[i] -= learning_rate * deltas[i];
}
train_out_index++;
}
}
// Feeds one input to the network
void feed_forward(double* train_input, double* outputs, double* w, double* bias)
// train_input: the address of the input to be feeded
// outputs: outputs of each neuron will be written here
// w: weights of the network
// bias: biases of the network
{
// forward pass
for (int i = 0; i < hidden * hidden_layers; i++)
{
// first layer
if (i < hidden)
{
outputs[i] = sigmoid(activation_function(inputs, w + i * inputs, train_input, bias));
}
// hidden layers
else
{
int w_index = inputs * hidden + (i / hidden - 1) * (hidden * hidden) + (i % hidden) * hidden;
int x_index = (i / hidden - 1) * hidden;
outputs[i] = sigmoid(activation_function(hidden, w + w_index, &outputs[x_index], &bias[i / hidden]));
}
}
// output layer
for (int i = 0; i < number_of_outputs; i++)
{
int output_n = hidden_layers * hidden + i;
int w_index = total_weights + hidden * (i - number_of_outputs);
outputs[output_n] = sigmoid(activation_function(hidden, w + w_index, &outputs[(hidden_layers - 1) * hidden], &bias[hidden_layers]));
}
}
// This function activates one neuron.
double activation_function(int number_of_weights, const double* w, const double* x, const double* bias)
// number_of_weights: the number of weights that is connected to the neuron to be activated
// w: the address of the first weight that is connected to the neuron
// x: the address of the first output of the previous layer
// bias: the address of the bias that is to be added to the weight
{
double a = 0;
for (int i = 0; i < number_of_weights; i++)
{
a += w[i] * x[i];
}
a += *bias;
return a;
}
// Sigmoid function
double sigmoid(double a)
{
return 1 / (1 + exp(-a));
}

Check whether the polygon is convex

I need to check whether the polygon is convex
I know that there were questions here about it, but I need to check the code, whether it is right
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int check_figure(float* x_points[], float* y_points[]);
int main(void) {
int n;
scanf("%i", &n);
int i = 0;
float **x_points = NULL, **y_points = NULL;
x_points = (float**) malloc(sizeof(float*) * (n + 1));
if (x_points == NULL) {
return 0;
}
y_points = (float**) malloc(sizeof(float*) * (n + 1));
if (y_points == NULL) {
return 0;
}
for (i = 0; i < n; i++) {
x_points[i] = (float*) malloc((n + 1) * sizeof(float));
scanf("%f", x_points[i]);
y_points[i] = (float*) malloc((n + 1) * sizeof(float));
scanf("%f", y_points[i]);
}
for (i = 0; i < n - 1; i++) {
if ((x_points[i] == NULL) || (y_points[i] == NULL)) {
return 0;
}
}
x_points[n] = NULL;
y_points[n] = NULL;
int convex = check_figure(x_points, y_points);
if (convex == 1) {
printf("%s", "true");
} else {
printf("%s", "false");
}
free(x_points);
free(y_points);
//free(convex);
return 0;
}
int check_figure(float *x_points[], float *y_points[]) {
float first = 0, booll = 1, sign = 0, result = 0;
int i = 0;
//int *convex = (int*)malloc(sizeof(int));
int convex;
while (1) {
if (x_points[i] != NULL) {
i++;
} else {
break;
}
}
first = *x_points[i - 1] * *y_points[0] - *y_points[i - 1] * *x_points[0];
sign = first / fabsf(first);
int k;
for (k = 0; k < i - 2; k++) {
result = *x_points[k] * *y_points[k + 1] - *x_points[k + 1] * *y_points[k];
booll = booll * sign * result / fabsf(result);
if (booll < 0) {
convex = 0;
return convex;
} else {
convex = 1;
return convex;
}
}
}
there is a sample, for example I input 4 and then I input 0,2; 2,-2; 0,0; -2,-2; and it returns me true, but the polygon is not convex...I really can't get it
A polygon is convex if all of its angles are acute, or 180 degrees (well, then it's not an angle, but depending what your doing, sometimes they happen.) So, just make sure all the angles are acute or 180 degrees.
On 2 dimensions this is not that hard, you just make sure your polygons are always wound the same way, then to test an angle between segments ab and bc, you create another vector by rotating vec2(a - b) 90 degrees towards the middle of the polygon (always the same rotation, because it's always wound the same way. A 90 degree rotation here can be accomplished by flipping the x and y values, and then negating one of the values, based on which way you are rotating.) Then, if the dot product of that rotated vector and the the vector of vec2(c-b) is positive, it is acute, if it is negative, it is obtuse, and if it is 0 it is a straight line. In three dimensions it is also not that hard, but you have to rotate along the same plane as the original angle.
Looking at your code I just have no idea how that is suppose to determine whether or not an angle is acute. At some point you need an angle, or a sine of an angle, or a cosine of an angle (obtained in this method via a dot product), or something somehow related to an angle. There are more direct ways to do this also, this is just a relatively performant one.

Writing a simple Discrete Fourier Transform for real inputs in C

So I'm trying to write the Discrete Fourier Transform in C to work with real 32-bit float wav files. It reads in 2 frames at a time (one for each channel, but for my purposes I'm assuming they are both the same and so I use frame[0]). This code is supposed to write out the amplitude spectrum for an input file by probing it with frequencies 20,40,60,...,10000. I am using a Hanning window on the input frames. I want to avoid using complex numbers if I can. When I run this, it gives me some very strange amplitudes (most of which are extremely small, and are not associated with the correct frequencies), which makes me believe I am making a fundamental mistake in my computation. Can somebody offer some insight into what is happening here? Here is my code:
int windowSize = 2205;
int probe[500];
float hann[2205];
int j, n;
// initialize probes to 20,40,60,...,10000
for (j=0; j< len(probe); j++) {
probe[j] = j*20 + 20;
fprintf(f, "%d\n", probe[j]);
}
fprintf(f, "-1\n");
// setup the Hann window
for (n=0; n< len(hann); n++) {
hann[n] = 0.5*(cos((2*M_PI*n/(float)windowSize) + M_PI))+0.5;
}
float angle = 0.0;
float w = 0.0; // windowed sample
float realSum[len(probe)]; // stores the real part of the probe[j] within a window
float imagSum[len(probe)]; // stores the imaginary part of probe[j] within window
float mag[len(probe)]; // stores the calculated amplitude of probe[j] within a window
for (j=0; j<len(probe);j++) {
realSum[j] = 0.0;
imagSum[j] = 0.0;
mag[j] = 0.0;
}
n=0; //count number of samples within current window
framesread = psf_sndReadFloatFrames(ifd,frame,1);
totalread = 0;
while (framesread == 1){
totalread++;
// window the frame with hann value at current sample
w = frame[0]*hann[n];
// determine both real and imag product values at sample n for all probe freqs times the windowed signal
for (j=0; j<len(probe);j++) {
angle = (2.0 * M_PI * probe[j] * n) / windowSize;
realSum[j] = realSum[j] + (w * cos(angle));
imagSum[j] = imagSum[j] + (w * sin(angle));
}
n++;
// checks to see if current window has ended
if (totalread % windowSize == 0) {
fprintf(f, "B(%f)\n", totalread/44100.0);
printf("%f breakpoint written\n", totalread/44100.0);
for (j=0; j < len(mag); j++) { // print out the amplitudes
realSum[j] = realSum[j]/windowSize;
imagSum[j] = imagSum[j]/windowSize;
mag[j] = sqrt(pow((double)realSum[j],2)+pow((double)imagSum[j],2))/windowSize;
fprintf(f, "%d\t%f\n", probe[j], mag[j]);
realSum[j] = 0.0;
imagSum[j] = 0.0;
}
n=0;
}
framesread = psf_sndReadFloatFrames(ifd,frame,1);
}
I think the error is in the calculation of the angle. The increment of the angle for each sample is dependent on the sampling frequency.
Something like this (you seem to have 44100Hz):
angle = (2.0 * M_PI * probe[j] * n) / 44100;
Your sample window will contain one full cycle for your lowest probed frequency 20Hz. If you loop n up to 2205 then that angle would be 2*M_PI.
What you saw was probably aliasing because your reference had the frequency 2205Hz and all frequencies above 1102Hz was aliased to lower frequencies.
With code below - only slightly reorganised to compile and create a fake sample, I do not get all zeroes. I have changed the output call to at the end from:
fprintf(f, "%d\t%f\n", probe[j], mag[j] );
to
if (mag[j] > 1e-7)
fprintf(f, "%d\t%f\n", probe[j], mag[j] * 10000);
This just makes it easier to see the non-zero data. Maybe the only issue is understanding the scale factor? Note how I faked input to generate a pure tone as a test case.
#include <math.h>
#include <stdio.h>
#define M_PI 3.1415926535
#define SAMPLE_RATE 44100.0f
#define len(array) (sizeof array/sizeof *array)
unsigned psf_sndReadFloatFrames(FILE* inFile,float* frame,int framesToRead)
{
static float counter = 0;
float frequency = 1000;
float time = counter++;
float phase = time/SAMPLE_RATE*frequency;
*frame = (float)sin(phase);
return counter < SAMPLE_RATE;
}
void discreteFourier(FILE* f)
{
FILE* ifd = 0;
float frame[1];
int windowSize = 2205;
int probe[500];
float hann[2205];
float angle = 0.0;
float w = 0.0; // windowed sample
float realSum[len(probe)]; // stores the real part of the probe[j] within a window
float imagSum[len(probe)]; // stores the imaginary part of probe[j] within window
float mag[len(probe)]; // stores the calculated amplitude of probe[j] within a window
int j, n;
unsigned framesread = 0;
unsigned totalread = 0;
for (j=0; j<len(probe);j++) {
realSum[j] = 0.0;
imagSum[j] = 0.0;
mag[j] = 0.0;
}
// initialize probes to 20,40,60,...,10000
for (j=0; j< len(probe); j++) {
probe[j] = j*20 + 20;
fprintf(f, "%d\n", probe[j]);
}
fprintf(f, "-1\n");
// setup the Hann window
for (n=0; n< len(hann); n++)
{
hann[n] = 0.5*(cos((2*M_PI*n/(float)windowSize) + M_PI))+0.5;
}
n=0; //count number of samples within current window
framesread = psf_sndReadFloatFrames(ifd,frame,1);
totalread = 0;
while (framesread == 1){
totalread++;
// window the frame with hann value at current sample
w = frame[0]*hann[n];
// determine both real and imag product values at sample n for all probe freqs times the windowed signal
for (j=0; j<len(probe);j++) {
angle = (2.0 * M_PI * probe[j] * n) / windowSize;
realSum[j] = realSum[j] + (w * cos(angle));
imagSum[j] = imagSum[j] + (w * sin(angle));
}
n++;
// checks to see if current window has ended
if (totalread % windowSize == 0) {
fprintf(f, "B(%f)\n", totalread/SAMPLE_RATE);
printf("%f breakpoint written\n", totalread/SAMPLE_RATE);
for (j=0; j < len(mag); j++) { // print out the amplitudes
realSum[j] = realSum[j]/windowSize;
imagSum[j] = imagSum[j]/windowSize;
mag[j] = sqrt(pow((double)realSum[j],2)+pow((double)imagSum[j],2))/windowSize;
if (mag[j] > 1e-7)
fprintf(f, "%d\t%f\n", probe[j], mag[j] * 10000);
realSum[j] = 0.0;
imagSum[j] = 0.0;
}
n=0;
}
framesread = psf_sndReadFloatFrames(ifd,frame,1);
}
}

fast & efficient least squares fit algorithm in C?

I am trying to implement a linear least squares fit onto 2 arrays of data: time vs amplitude. The only technique I know so far is to test all of the possible m and b points in (y = m*x+b) and then find out which combination fits my data best so that it has the least error. However, I think iterating so many combinations is sometimes useless because it tests out everything. Are there any techniques to speed up the process that I don't know about? Thanks.
Try this code. It fits y = mx + b to your (x,y) data.
The arguments to linreg are
linreg(int n, REAL x[], REAL y[], REAL* b, REAL* m, REAL* r)
n = number of data points
x,y = arrays of data
*b = output intercept
*m = output slope
*r = output correlation coefficient (can be NULL if you don't want it)
The return value is 0 on success, !=0 on failure.
Here's the code
#include "linreg.h"
#include <stdlib.h>
#include <math.h> /* math functions */
//#define REAL float
#define REAL double
inline static REAL sqr(REAL x) {
return x*x;
}
int linreg(int n, const REAL x[], const REAL y[], REAL* m, REAL* b, REAL* r){
REAL sumx = 0.0; /* sum of x */
REAL sumx2 = 0.0; /* sum of x**2 */
REAL sumxy = 0.0; /* sum of x * y */
REAL sumy = 0.0; /* sum of y */
REAL sumy2 = 0.0; /* sum of y**2 */
for (int i=0;i<n;i++){
sumx += x[i];
sumx2 += sqr(x[i]);
sumxy += x[i] * y[i];
sumy += y[i];
sumy2 += sqr(y[i]);
}
REAL denom = (n * sumx2 - sqr(sumx));
if (denom == 0) {
// singular matrix. can't solve the problem.
*m = 0;
*b = 0;
if (r) *r = 0;
return 1;
}
*m = (n * sumxy - sumx * sumy) / denom;
*b = (sumy * sumx2 - sumx * sumxy) / denom;
if (r!=NULL) {
*r = (sumxy - sumx * sumy / n) / /* compute correlation coeff */
sqrt((sumx2 - sqr(sumx)/n) *
(sumy2 - sqr(sumy)/n));
}
return 0;
}
Example
You can run this example online.
int main()
{
int n = 6;
REAL x[6]= {1, 2, 4, 5, 10, 20};
REAL y[6]= {4, 6, 12, 15, 34, 68};
REAL m,b,r;
linreg(n,x,y,&m,&b,&r);
printf("m=%g b=%g r=%g\n",m,b,r);
return 0;
}
Here is the output
m=3.43651 b=-0.888889 r=0.999192
Here is the Excel plot and linear fit (for verification).
All values agree exactly with the C code above (note C code returns r while Excel returns R**2).
There are efficient algorithms for least-squares fitting; see Wikipedia for details. There are also libraries that implement the algorithms for you, likely more efficiently than a naive implementation would do; the GNU Scientific Library is one example, but there are others under more lenient licenses as well.
From Numerical Recipes: The Art of Scientific Computing in (15.2) Fitting Data to a Straight Line:
Linear Regression:
Consider the problem of fitting a set of N data points (xi, yi) to a straight-line model:
Assume that the uncertainty: sigmai associated with each yi and that the xi’s (values of the dependent variable) are known exactly. To measure how well the model agrees with the data, we use the chi-square function, which in this case is:
The above equation is minimized to determine a and b. This is done by finding the derivative of the above equation with respect to a and b, equate them to zero and solve for a and b. Then we estimate the probable uncertainties in the estimates of a and b, since obviously the measurement errors in the data must introduce some uncertainty in the determination of those parameters. Additionally, we must estimate the goodness-of-fit of the data to the
model. Absent this estimate, we have not the slightest indication that the parameters a and b in the model have any meaning at all.
The below struct performs the mentioned calculations:
struct Fitab {
// Object for fitting a straight line y = a + b*x to a set of
// points (xi, yi), with or without available
// errors sigma i . Call one of the two constructors to calculate the fit.
// The answers are then available as the variables:
// a, b, siga, sigb, chi2, and either q or sigdat.
int ndata;
double a, b, siga, sigb, chi2, q, sigdat; // Answers.
vector<double> &x, &y, &sig;
// Constructor.
Fitab(vector<double> &xx, vector<double> &yy, vector<double> &ssig)
: ndata(xx.size()), x(xx), y(yy), sig(ssig), chi2(0.), q(1.), sigdat(0.)
{
// Given a set of data points x[0..ndata-1], y[0..ndata-1]
// with individual standard deviations sig[0..ndata-1],
// sets a,b and their respective probable uncertainties
// siga and sigb, the chi-square: chi2, and the goodness-of-fit
// probability: q
Gamma gam;
int i;
double ss=0., sx=0., sy=0., st2=0., t, wt, sxoss; b=0.0;
for (i=0;i < ndata; i++) { // Accumulate sums ...
wt = 1.0 / SQR(sig[i]); //...with weights
ss += wt;
sx += x[i]*wt;
sy += y[i]*wt;
}
sxoss = sx/ss;
for (i=0; i < ndata; i++) {
t = (x[i]-sxoss) / sig[i];
st2 += t*t;
b += t*y[i]/sig[i];
}
b /= st2; // Solve for a, b, sigma-a, and simga-b.
a = (sy-sx*b) / ss;
siga = sqrt((1.0+sx*sx/(ss*st2))/ss);
sigb = sqrt(1.0/st2); // Calculate chi2.
for (i=0;i<ndata;i++) chi2 += SQR((y[i]-a-b*x[i])/sig[i]);
if (ndata>2) q=gam.gammq(0.5*(ndata-2),0.5*chi2); // goodness of fit
}
// Constructor.
Fitab(vector<double> &xx, vector<double> &yy)
: ndata(xx.size()), x(xx), y(yy), sig(xx), chi2(0.), q(1.), sigdat(0.)
{
// As above, but without known errors (sig is not used).
// The uncertainties siga and sigb are estimated by assuming
// equal errors for all points, and that a straight line is
// a good fit. q is returned as 1.0, the normalization of chi2
// is to unit standard deviation on all points, and sigdat
// is set to the estimated error of each point.
int i;
double ss,sx=0.,sy=0.,st2=0.,t,sxoss;
b=0.0; // Accumulate sums ...
for (i=0; i < ndata; i++) {
sx += x[i]; // ...without weights.
sy += y[i];
}
ss = ndata;
sxoss = sx/ss;
for (i=0;i < ndata; i++) {
t = x[i]-sxoss;
st2 += t*t;
b += t*y[i];
}
b /= st2; // Solve for a, b, sigma-a, and sigma-b.
a = (sy-sx*b)/ss;
siga=sqrt((1.0+sx*sx/(ss*st2))/ss);
sigb=sqrt(1.0/st2); // Calculate chi2.
for (i=0;i<ndata;i++) chi2 += SQR(y[i]-a-b*x[i]);
if (ndata > 2) sigdat=sqrt(chi2/(ndata-2));
// For unweighted data evaluate typical
// sig using chi2, and adjust
// the standard deviations.
siga *= sigdat;
sigb *= sigdat;
}
};
where struct Gamma:
struct Gamma : Gauleg18 {
// Object for incomplete gamma function.
// Gauleg18 provides coefficients for Gauss-Legendre quadrature.
static const Int ASWITCH=100; When to switch to quadrature method.
static const double EPS; // See end of struct for initializations.
static const double FPMIN;
double gln;
double gammp(const double a, const double x) {
// Returns the incomplete gamma function P(a,x)
if (x < 0.0 || a <= 0.0) throw("bad args in gammp");
if (x == 0.0) return 0.0;
else if ((Int)a >= ASWITCH) return gammpapprox(a,x,1); // Quadrature.
else if (x < a+1.0) return gser(a,x); // Use the series representation.
else return 1.0-gcf(a,x); // Use the continued fraction representation.
}
double gammq(const double a, const double x) {
// Returns the incomplete gamma function Q(a,x) = 1 - P(a,x)
if (x < 0.0 || a <= 0.0) throw("bad args in gammq");
if (x == 0.0) return 1.0;
else if ((Int)a >= ASWITCH) return gammpapprox(a,x,0); // Quadrature.
else if (x < a+1.0) return 1.0-gser(a,x); // Use the series representation.
else return gcf(a,x); // Use the continued fraction representation.
}
double gser(const Doub a, const Doub x) {
// Returns the incomplete gamma function P(a,x) evaluated by its series representation.
// Also sets ln (gamma) as gln. User should not call directly.
double sum,del,ap;
gln=gammln(a);
ap=a;
del=sum=1.0/a;
for (;;) {
++ap;
del *= x/ap;
sum += del;
if (fabs(del) < fabs(sum)*EPS) {
return sum*exp(-x+a*log(x)-gln);
}
}
}
double gcf(const Doub a, const Doub x) {
// Returns the incomplete gamma function Q(a, x) evaluated
// by its continued fraction representation.
// Also sets ln (gamma) as gln. User should not call directly.
int i;
double an,b,c,d,del,h;
gln=gammln(a);
b=x+1.0-a; // Set up for evaluating continued fraction
// by modified Lentz’s method with with b0 = 0.
c=1.0/FPMIN;
d=1.0/b;
h=d;
for (i=1;;i++) {
// Iterate to convergence.
an = -i*(i-a);
b += 2.0;
d=an*d+b;
if (fabs(d) < FPMIN) d=FPMIN;
c=b+an/c;
if (fabs(c) < FPMIN) c=FPMIN;
d=1.0/d;
del=d*c;
h *= del;
if (fabs(del-1.0) <= EPS) break;
}
return exp(-x+a*log(x)-gln)*h; Put factors in front.
}
double gammpapprox(double a, double x, int psig) {
// Incomplete gamma by quadrature. Returns P(a,x) or Q(a, x),
// when psig is 1 or 0, respectively. User should not call directly.
int j;
double xu,t,sum,ans;
double a1 = a-1.0, lna1 = log(a1), sqrta1 = sqrt(a1);
gln = gammln(a);
// Set how far to integrate into the tail:
if (x > a1) xu = MAX(a1 + 11.5*sqrta1, x + 6.0*sqrta1);
else xu = MAX(0.,MIN(a1 - 7.5*sqrta1, x - 5.0*sqrta1));
sum = 0;
for (j=0;j<ngau;j++) { // Gauss-Legendre.
t = x + (xu-x)*y[j];
sum += w[j]*exp(-(t-a1)+a1*(log(t)-lna1));
}
ans = sum*(xu-x)*exp(a1*(lna1-1.)-gln);
return (psig?(ans>0.0? 1.0-ans:-ans):(ans>=0.0? ans:1.0+ans));
}
double invgammp(Doub p, Doub a);
// Inverse function on x of P(a,x) .
};
const Doub Gamma::EPS = numeric_limits<Doub>::epsilon();
const Doub Gamma::FPMIN = numeric_limits<Doub>::min()/EPS
and stuct Gauleg18:
struct Gauleg18 {
// Abscissas and weights for Gauss-Legendre quadrature.
static const Int ngau = 18;
static const Doub y[18];
static const Doub w[18];
};
const Doub Gauleg18::y[18] = {0.0021695375159141994,
0.011413521097787704,0.027972308950302116,0.051727015600492421,
0.082502225484340941, 0.12007019910960293,0.16415283300752470,
0.21442376986779355, 0.27051082840644336, 0.33199876341447887,
0.39843234186401943, 0.46931971407375483, 0.54413605556657973,
0.62232745288031077, 0.70331500465597174, 0.78649910768313447,
0.87126389619061517, 0.95698180152629142};
const Doub Gauleg18::w[18] = {0.0055657196642445571,
0.012915947284065419,0.020181515297735382,0.027298621498568734,
0.034213810770299537,0.040875750923643261,0.047235083490265582,
0.053244713977759692,0.058860144245324798,0.064039797355015485
0.068745323835736408,0.072941885005653087,0.076598410645870640,
0.079687828912071670,0.082187266704339706,0.084078218979661945,
0.085346685739338721,0.085983275670394821};
and, finally fuinction Gamma::invgamp():
double Gamma::invgammp(double p, double a) {
// Returns x such that P(a,x) = p for an argument p between 0 and 1.
int j;
double x,err,t,u,pp,lna1,afac,a1=a-1;
const double EPS=1.e-8; // Accuracy is the square of EPS.
gln=gammln(a);
if (a <= 0.) throw("a must be pos in invgammap");
if (p >= 1.) return MAX(100.,a + 100.*sqrt(a));
if (p <= 0.) return 0.0;
if (a > 1.) {
lna1=log(a1);
afac = exp(a1*(lna1-1.)-gln);
pp = (p < 0.5)? p : 1. - p;
t = sqrt(-2.*log(pp));
x = (2.30753+t*0.27061)/(1.+t*(0.99229+t*0.04481)) - t;
if (p < 0.5) x = -x;
x = MAX(1.e-3,a*pow(1.-1./(9.*a)-x/(3.*sqrt(a)),3));
} else {
t = 1.0 - a*(0.253+a*0.12); and (6.2.9).
if (p < t) x = pow(p/t,1./a);
else x = 1.-log(1.-(p-t)/(1.-t));
}
for (j=0;j<12;j++) {
if (x <= 0.0) return 0.0; // x too small to compute accurately.
err = gammp(a,x) - p;
if (a > 1.) t = afac*exp(-(x-a1)+a1*(log(x)-lna1));
else t = exp(-x+a1*log(x)-gln);
u = err/t;
// Halley’s method.
x -= (t = u/(1.-0.5*MIN(1.,u*((a-1.)/x - 1))));
// Halve old value if x tries to go negative.
if (x <= 0.) x = 0.5*(x + t);
if (fabs(t) < EPS*x ) break;
}
return x;
}
Here is my version of a C/C++ function that does simple linear regression. The calculations follow the wikipedia article on simple linear regression. This is published as a single-header public-domain (MIT) library on github: simple_linear_regression. The library (.h file) is tested to work on Linux and Windows, and from C and C++ using -Wall -Werror and all -std versions supported by clang/gcc.
#define SIMPLE_LINEAR_REGRESSION_ERROR_INPUT_VALUE -2
#define SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC -3
int simple_linear_regression(const double * x, const double * y, const int n, double * slope_out, double * intercept_out, double * r2_out) {
double sum_x = 0.0;
double sum_xx = 0.0;
double sum_xy = 0.0;
double sum_y = 0.0;
double sum_yy = 0.0;
double n_real = (double)(n);
int i = 0;
double slope = 0.0;
double denominator = 0.0;
if (x == NULL || y == NULL || n < 2) {
return SIMPLE_LINEAR_REGRESSION_ERROR_INPUT_VALUE;
}
for (i = 0; i < n; ++i) {
sum_x += x[i];
sum_xx += x[i] * x[i];
sum_xy += x[i] * y[i];
sum_y += y[i];
sum_yy += y[i] * y[i];
}
denominator = n_real * sum_xx - sum_x * sum_x;
if (denominator == 0.0) {
return SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC;
}
slope = (n_real * sum_xy - sum_x * sum_y) / denominator;
if (slope_out != NULL) {
*slope_out = slope;
}
if (intercept_out != NULL) {
*intercept_out = (sum_y - slope * sum_x) / n_real;
}
if (r2_out != NULL) {
denominator = ((n_real * sum_xx) - (sum_x * sum_x)) * ((n_real * sum_yy) - (sum_y * sum_y));
if (denominator == 0.0) {
return SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC;
}
*r2_out = ((n_real * sum_xy) - (sum_x * sum_y)) * ((n_real * sum_xy) - (sum_x * sum_y)) / denominator;
}
return 0;
}
Usage example:
#define SIMPLE_LINEAR_REGRESSION_IMPLEMENTATION
#include "simple_linear_regression.h"
#include <stdio.h>
/* Some data that we want to find the slope, intercept and r2 for */
static const double x[] = { 1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83 };
static const double y[] = { 52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46 };
int main() {
double slope = 0.0;
double intercept = 0.0;
double r2 = 0.0;
int res = 0;
res = simple_linear_regression(x, y, sizeof(x) / sizeof(x[0]), &slope, &intercept, &r2);
if (res < 0) {
printf("Error: %s\n", simple_linear_regression_error_string(res));
return res;
}
printf("slope: %f\n", slope);
printf("intercept: %f\n", intercept);
printf("r2: %f\n", r2);
return 0;
}
The original example above worked well for me with slope and offset but I had a hard time with the corr coef. Maybe I don't have my parenthesis working the same as the assumed precedence? Anyway, with some help of other web pages I finally got values that match the linear trend-line in Excel. Thought I would share my code using Mark Lakata's variable names. Hope this helps.
double slope = ((n * sumxy) - (sumx * sumy )) / denom;
double intercept = ((sumy * sumx2) - (sumx * sumxy)) / denom;
double term1 = ((n * sumxy) - (sumx * sumy));
double term2 = ((n * sumx2) - (sumx * sumx));
double term3 = ((n * sumy2) - (sumy * sumy));
double term23 = (term2 * term3);
double r2 = 1.0;
if (fabs(term23) > MIN_DOUBLE) // Define MIN_DOUBLE somewhere as 1e-9 or similar
r2 = (term1 * term1) / term23;
as an assignment I had to code in C a simple linear regression using RMSE loss function. The program is dynamic and you can enter your own values and choose your own loss function which is for now limited to Root Mean Square Error. But first here are the algorithms I used:
now the code... you need gnuplot to display the chart, sudo apt install gnuplot
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#define BUFFSIZE 64
#define MAXSIZE 100
static double vector_x[MAXSIZE] = {0};
static double vector_y[MAXSIZE] = {0};
static double vector_predict[MAXSIZE] = {0};
static double max_x;
static double max_y;
static double mean_x;
static double mean_y;
static double teta_0_intercept;
static double teta_1_grad;
static double RMSE;
static double r_square;
static double prediction;
static char intercept[BUFFSIZE];
static char grad[BUFFSIZE];
static char xrange[BUFFSIZE];
static char yrange[BUFFSIZE];
static char lossname_RMSE[BUFFSIZE] = "Simple Linear Regression using RMSE'";
static char cmd_gnu_0[BUFFSIZE] = "set title '";
static char cmd_gnu_1[BUFFSIZE] = "intercept = ";
static char cmd_gnu_2[BUFFSIZE] = "grad = ";
static char cmd_gnu_3[BUFFSIZE] = "set xrange [0:";
static char cmd_gnu_4[BUFFSIZE] = "set yrange [0:";
static char cmd_gnu_5[BUFFSIZE] = "f(x) = (grad * x) + intercept";
static char cmd_gnu_6[BUFFSIZE] = "plot f(x), 'data.temp' with points pointtype 7";
static char const *commands_gnuplot[] = {
cmd_gnu_0,
cmd_gnu_1,
cmd_gnu_2,
cmd_gnu_3,
cmd_gnu_4,
cmd_gnu_5,
cmd_gnu_6,
};
static size_t size;
static void user_input()
{
printf("Enter x,y vector size, MAX = 100\n");
scanf("%lu", &size);
if (size > MAXSIZE) {
printf("Wrong input size is too big\n");
user_input();
}
printf("vector's size is %lu\n", size);
size_t i;
for (i = 0; i < size; i++) {
printf("Enter vector_x[%ld] values\n", i);
scanf("%lf", &vector_x[i]);
}
for (i = 0; i < size; i++) {
printf("Enter vector_y[%ld] values\n", i);
scanf("%lf", &vector_y[i]);
}
}
static void display_vector()
{
size_t i;
for (i = 0; i < size; i++){
printf("vector_x[%lu] = %lf\t", i, vector_x[i]);
printf("vector_y[%lu] = %lf\n", i, vector_y[i]);
}
}
static void concatenate(char p[], char q[]) {
int c;
int d;
c = 0;
while (p[c] != '\0') {
c++;
}
d = 0;
while (q[d] != '\0') {
p[c] = q[d];
d++;
c++;
}
p[c] = '\0';
}
static void compute_mean_x_y()
{
size_t i;
double tmp_x = 0.0;
double tmp_y = 0.0;
for (i = 0; i < size; i++) {
tmp_x += vector_x[i];
tmp_y += vector_y[i];
}
mean_x = tmp_x / size;
mean_y = tmp_y / size;
printf("mean_x = %lf\n", mean_x);
printf("mean_y = %lf\n", mean_y);
}
static void compute_teta_1_grad()
{
double numerator = 0.0;
double denominator = 0.0;
double tmp1 = 0.0;
double tmp2 = 0.0;
size_t i;
for (i = 0; i < size; i++) {
numerator += (vector_x[i] - mean_x) * (vector_y[i] - mean_y);
}
for (i = 0; i < size; i++) {
tmp1 = vector_x[i] - mean_x;
tmp2 = tmp1 * tmp1;
denominator += tmp2;
}
teta_1_grad = numerator / denominator;
printf("teta_1_grad = %lf\n", teta_1_grad);
}
static void compute_teta_0_intercept()
{
teta_0_intercept = mean_y - (teta_1_grad * mean_x);
printf("teta_0_intercept = %lf\n", teta_0_intercept);
}
static void compute_prediction()
{
size_t i;
for (i = 0; i < size; i++) {
vector_predict[i] = teta_0_intercept + (teta_1_grad * vector_x[i]);
printf("y^[%ld] = %lf\n", i, vector_predict[i]);
}
printf("\n");
}
static void compute_RMSE()
{
compute_prediction();
double error = 0;
size_t i;
for (i = 0; i < size; i++) {
error = (vector_predict[i] - vector_y[i]) * (vector_predict[i] - vector_y[i]);
printf("error y^[%ld] = %lf\n", i, error);
RMSE += error;
}
/* mean */
RMSE = RMSE / size;
/* square root mean */
RMSE = sqrt(RMSE);
printf("\nRMSE = %lf\n", RMSE);
}
static void compute_loss_function()
{
int input = 0;
printf("Which loss function do you want to use?\n");
printf(" 1 - RMSE\n");
scanf("%d", &input);
switch(input) {
case 1:
concatenate(cmd_gnu_0, lossname_RMSE);
compute_RMSE();
printf("\n");
break;
default:
printf("Wrong input try again\n");
compute_loss_function(size);
}
}
static void compute_r_square(size_t size)
{
double num_err = 0.0;
double den_err = 0.0;
size_t i;
for (i = 0; i < size; i++) {
num_err += (vector_y[i] - vector_predict[i]) * (vector_y[i] - vector_predict[i]);
den_err += (vector_y[i] - mean_y) * (vector_y[i] - mean_y);
}
r_square = 1 - (num_err/den_err);
printf("R_square = %lf\n", r_square);
}
static void compute_predict_for_x()
{
double x = 0.0;
printf("Please enter x value\n");
scanf("%lf", &x);
prediction = teta_0_intercept + (teta_1_grad * x);
printf("y^ if x = %lf -> %lf\n",x, prediction);
}
static void compute_max_x_y()
{
size_t i;
double tmp1= 0.0;
double tmp2= 0.0;
for (i = 0; i < size; i++) {
if (vector_x[i] > tmp1) {
tmp1 = vector_x[i];
max_x = vector_x[i];
}
if (vector_y[i] > tmp2) {
tmp2 = vector_y[i];
max_y = vector_y[i];
}
}
printf("vector_x max value %lf\n", max_x);
printf("vector_y max value %lf\n", max_y);
}
static void display_model_line()
{
sprintf(intercept, "%0.7lf", teta_0_intercept);
sprintf(grad, "%0.7lf", teta_1_grad);
sprintf(xrange, "%0.7lf", max_x + 1);
sprintf(yrange, "%0.7lf", max_y + 1);
concatenate(cmd_gnu_1, intercept);
concatenate(cmd_gnu_2, grad);
concatenate(cmd_gnu_3, xrange);
concatenate(cmd_gnu_3, "]");
concatenate(cmd_gnu_4, yrange);
concatenate(cmd_gnu_4, "]");
printf("grad = %s\n", grad);
printf("intercept = %s\n", intercept);
printf("xrange = %s\n", xrange);
printf("yrange = %s\n", yrange);
printf("cmd_gnu_0: %s\n", cmd_gnu_0);
printf("cmd_gnu_1: %s\n", cmd_gnu_1);
printf("cmd_gnu_2: %s\n", cmd_gnu_2);
printf("cmd_gnu_3: %s\n", cmd_gnu_3);
printf("cmd_gnu_4: %s\n", cmd_gnu_4);
printf("cmd_gnu_5: %s\n", cmd_gnu_5);
printf("cmd_gnu_6: %s\n", cmd_gnu_6);
/* print plot */
FILE *gnuplot_pipe = (FILE*)popen("gnuplot -persistent", "w");
FILE *temp = (FILE*)fopen("data.temp", "w");
/* create data.temp */
size_t i;
for (i = 0; i < size; i++)
{
fprintf(temp, "%f %f \n", vector_x[i], vector_y[i]);
}
/* display gnuplot */
for (i = 0; i < 7; i++)
{
fprintf(gnuplot_pipe, "%s \n", commands_gnuplot[i]);
}
}
int main(void)
{
printf("===========================================\n");
printf("INPUT DATA\n");
printf("===========================================\n");
user_input();
display_vector();
printf("\n");
printf("===========================================\n");
printf("COMPUTE MEAN X:Y, TETA_1 TETA_0\n");
printf("===========================================\n");
compute_mean_x_y();
compute_max_x_y();
compute_teta_1_grad();
compute_teta_0_intercept();
printf("\n");
printf("===========================================\n");
printf("COMPUTE LOSS FUNCTION\n");
printf("===========================================\n");
compute_loss_function();
printf("===========================================\n");
printf("COMPUTE R_square\n");
printf("===========================================\n");
compute_r_square(size);
printf("\n");
printf("===========================================\n");
printf("COMPUTE y^ according to x\n");
printf("===========================================\n");
compute_predict_for_x();
printf("\n");
printf("===========================================\n");
printf("DISPLAY LINEAR REGRESSION\n");
printf("===========================================\n");
display_model_line();
printf("\n");
return 0;
}
Look at Section 1 of this paper. This section expresses a 2D linear regression as a matrix multiplication exercise. As long as your data is well-behaved, this technique should permit you to develop a quick least squares fit.
Depending on the size of your data, it might be worthwhile to algebraically reduce the matrix multiplication to simple set of equations, thereby avoiding the need to write a matmult() function. (Be forewarned, this is completely impractical for more than 4 or 5 data points!)
The fastest, most efficient way to solve least squares, as far as I am aware, is to subtract (the gradient)/(the 2nd order gradient) from your parameter vector. (2nd order gradient = i.e. the diagonal of the Hessian.)
Here is the intuition:
Let's say you want to optimize least squares over a single parameter. This is equivalent to finding the vertex of a parabola. Then, for any random initial parameter, x0, the vertex of the loss function is located at x0 - f(1) / f(2). That's because adding - f(1) / f(2) to x will always zero out the derivative, f(1).
Side note: Implementing this in Tensorflow, the solution appeared at w0 - f(1) / f(2) / (number of weights), but I'm not sure if that's due to Tensorflow or if it's due to something else..

Resources