Moved my extension files from subproject to main project

This commit is contained in:
Themis Demetriades 2024-06-18 08:26:08 +01:00
parent 1c13cfa2f2
commit e21ff75bad
10 changed files with 412 additions and 1 deletions

@ -1 +0,0 @@
Subproject commit 2dc6ce7eabce9d86c0f5f44606d0cb18fc64d983

18
extension/src/Makefile Normal file
View File

@ -0,0 +1,18 @@
CC = gcc
CFLAGS = -std=c17 -g\
-D_POSIX_SOURCE -D_DEFAULT_SOURCE\
-Wall -Werror -pedantic\
LDLIBS = -lm
.SUFFIXES: .c .o
.PHONY: all clean
all: test/test_math test/test_ann
test/test_math: test/test_math.o mymath.o
test/test_ann: test/test_ann.o ann.o mymath.o
clean:
$(RM) *.o test/*.o

81
extension/src/ann.c Normal file
View File

@ -0,0 +1,81 @@
#include <stdlib.h>
#include "ann.h"
// Helper function that simulate 'bias' term by setting a neuron that always outputs 1.0
static void network_fillbias(Network network, unsigned int layerIndex) {
for (int j = 0; j < network->trainingWidth; j++) {
*(network->outputs[layerIndex]->data + network->layers[layerIndex].neuronCount * network->trainingWidth + j) = 1.0;
}
}
// Defines a sequence of random numbers sampled from a standard normal distribution
double normalseq(unsigned int i) {
return stdnormal();
}
// Creates a new network instance given an array of layers and the size of the array
Network network_create(Layer *layers, unsigned int noLayers, unsigned int trainingWidth) {
// Initialize network struct and its fields
Network network = malloc(sizeof(struct Network));
if (network == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate sufficient memory for Network struct!\n");
abort();
}
network->layers = layers;
network->noLayers = noLayers;
network->trainingWidth = trainingWidth;
network->outputs = malloc(sizeof(struct Matrix) * noLayers);
if (network->outputs == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate sufficient memory for Network output matrix array!\n");
abort();
}
network->weights = malloc(sizeof(struct Matrix) * (noLayers - 1));
if (network->weights == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate sufficient memory for Network weight matrix array!\n");
abort();
}
// Initialize output and weight matrix array data
for (unsigned int i = 0; i < noLayers; i++) {
network->outputs[i] = matrix_create(layers[i].neuronCount + 1, trainingWidth);
network_fillbias(network, i);
if (i < noLayers - 1) {
network->weights[i] = matrix_seqcreate(layers[i + 1].neuronCount + 1, layers[i].neuronCount + 1, &normalseq);
}
}
return network;
}
// Returns the output of the ANN given the specified input
Matrix network_pass(Network network, Matrix features) {
Matrix currentOut = features;
for (int i = 0; i < network->noLayers; i++) {
// Update current layer's outputs
matrix_free(network->outputs[i]);
network->outputs[i] = currentOut;
network_fillbias(network, i);
// Calculate outputs of next layer if not in last layer
if (i < network->noLayers - 1) {
currentOut = matrix_apply(matrix_multiply(network->weights[i], currentOut), network->layers[i].activation);
}
}
return currentOut;
}
Matrix log_loss(Matrix result, Matrix expected) {
}

41
extension/src/ann.h Normal file
View File

@ -0,0 +1,41 @@
/* @file ann.h
@brief Data structures and functions for feedforward neural networks.
@author Themis Demetriades */
#ifndef __ANN__
#define __ANN__
#include "mymath.h"
// Defines function pointer type for activation functions
typedef double (*activationFunc)(double);
// Specifies structure of a given layer within a network
typedef struct {
unsigned int neuronCount;
activationFunc activation;
} Layer;
// Defines a specific instance of an active network
struct Network {
Layer *layers; // Array of layers specifying network structure
unsigned int noLayers;
Matrix *weights; // Array of matrices specifying weights of *outputs* to each layer
Matrix *outputs;
unsigned int trainingWidth; // Number of training examples to train per gradient descent step
};
typedef struct Network *Network;
// Creates a new network instance given an array of layers and the size of the array
Network network_create(Layer *layers, unsigned int noLayers, unsigned int trainingWidth);
// Returns the output of the ANN given the specified input
Matrix network_pass(Network network, Matrix features);
// Calculates the log loss of a result wrt to the expected result
Matrix log_loss(Matrix result, Matrix expected);
#endif

166
extension/src/mymath.c Normal file
View File

@ -0,0 +1,166 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "mymath.h"
#define LRELU_SFACTOR 0.001
// Maximum number of characters that would ever be required to represent a valid matrix entry
#define MAX_ENTRY_LENGTH 100
// Defines the format used to print matrix entries
#define VALUE_PRINT_FORMAT "%.2lf"
// Pretty print matrix data to the given output stream
void matrix_print(Matrix m, FILE *stream) {
double (*data)[m->cols] = (double (*)[m->cols]) m->data;
char valstr[MAX_ENTRY_LENGTH];
unsigned int maxlen = 1;
// Get maximum length of a matrix entry
for (int i = 0; i < m->rows; i++) {
for (int j = 0; j < m->cols; j++) {
sprintf(valstr, VALUE_PRINT_FORMAT, data[i][j]);
unsigned int curlen = strlen(valstr);
if (curlen > maxlen) maxlen = curlen;
}
}
// Print matrix entries
for (int i = 0; i < m->rows; i++) {
fputc('[', stream);
for (int j = 0; j < m->cols; j++) {
sprintf(valstr, VALUE_PRINT_FORMAT, data[i][j]);
fprintf(stream, "%s", valstr);
for (int k = 0; k < maxlen - strlen(valstr); k++) fputc(' ', stream);
if (j < m->cols - 1) fputc(' ', stream);
}
fprintf(stream, "]\n");
}
}
// Return matrix with specified dimensions and random values as its entries
Matrix matrix_create(unsigned int rows, unsigned int cols) {
// Allocate memory for matrix struct, aborting if couldn't allocate memory
Matrix m = malloc(sizeof(struct Matrix));
if (m == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate sufficient memory for matrix struct!\n");
abort();
}
// Allocate memory for matrix data (values), aborting if couldn't allocate memory
m->data = malloc(rows * cols * sizeof(double));
if (m->data == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate sufficient memory for matrix data!\n");
abort();
}
// Initialize matrix struct data
m->rows = rows;
m->cols = cols;
return m;
}
// Free data associated with the specified matrix
void matrix_free(Matrix m) {
free(m->data);
free(m);
}
// Return matrix with specified dimensions and entries whose values are the first values of the specified matrix sequence
Matrix matrix_seqcreate(unsigned int rows, unsigned int cols, matrix_sequence seq) {
// Create matrix with specified dimensions containing entries with random values
Matrix m = matrix_create(rows, cols);
double (*data)[cols] = (double (*)[cols]) m->data;
// Fill matrix with correct entries
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
data[i][j] = seq(i * cols + j);
}
}
return m;
}
// Multiply left matrix (l) with right matrix (r), returning a new instance representing the result
Matrix matrix_multiply(Matrix l, Matrix r) {
// Ensure that dimensions of matrices are compatible for multiplication
if (l->cols != r->rows) {
fprintf(stderr, "ERROR: Attempting to multiply matrices with incompatible dimensions!\n");
abort();
}
// Create matrix instance to store result of product
Matrix prod = matrix_create(l->rows, r->cols);
double (*prod_data)[prod->cols] = (double (*)[prod->cols]) prod->data;
double (*l_data)[l->cols] = (double (*)[l->cols]) l->data;
double (*r_data)[r->cols] = (double (*)[r->cols]) r->data;
// Perform matrix multiplication, storing result in prod
for (int i = 0; i < l->rows; i++) {
for (int j = 0; j < r->cols; j++) {
prod_data[i][j] = 0.0;
for (int k = 0; k < l->cols; k++) {
prod_data[i][j] += l_data[i][k] * r_data[k][j];
}
}
}
// Return result
return prod;
}
// Return result of applying given function to each entry in the matrix independently
Matrix matrix_apply(Matrix m, matrix_entrytrans f) {
Matrix result = matrix_create(m->rows, m->cols);
double (*result_data)[result->cols] = (double (*)[result->cols])result->data;
double (*m_data)[m->cols] = (double (*)[m->cols])m->data;
for (int i = 0; i < m->rows; i++) {
for (int j = 0; j < m->cols; j++) {
result_data[i][j] = f(m_data[i][j]);
}
}
return result;
}
// Draws a value from the standard normal distribution
double stdnormal() {
// Generate 2 random numbers in the interval [0, 1)
double s1 = ((double)rand() / RAND_MAX);
double s2 = ((double)rand() / RAND_MAX);
// Peform box-muller transform
return sqrt(-2 * log(s1)) * cos(2 * PI * s2);
}
// Defines the leaky rectified linear unit activation function
double lrelu(double x) {
if (x > 0) {
return x;
} else {
return LRELU_SFACTOR * x;
}
}
// Defines the identity activation function f(x) = x
double identity(double x) {
return x;
}
// Defines a simple counting sequence
double countseq(unsigned int i) {
return i + 1;
}

58
extension/src/mymath.h Normal file
View File

@ -0,0 +1,58 @@
/* @file math.h
@brief Math functions and types for feedforward ANNs.
@author Themis Demetriades */
#ifndef __MATH__
#define __MATH__
#include <stdio.h>
#define PI 3.1415926535
// Definition of matrix types
struct Matrix {
unsigned int rows;
unsigned int cols;
double *data;
};
typedef struct Matrix *Matrix;
// Pretty print matrix data to the given output stream
void matrix_print(Matrix m, FILE *stream);
// Defines a sequence that maps matrix entry indices to floating point values
typedef double (*matrix_sequence)(unsigned int);
// Defines a function that transforms matrix entries
typedef double (*matrix_entrytrans)(double);
// Return matrix with specified dimensions and random values as its entries
Matrix matrix_create(unsigned int rows, unsigned int cols);
// Free data associated with the specified matrix
void matrix_free(Matrix m);
// Return matrix with specified dimensions and entries whose values are the first values of the specified matrix sequence
Matrix matrix_seqcreate(unsigned int rows, unsigned int cols, matrix_sequence seq);
// Multiply left matrix (l) with right matrix (r), returning a new instance representing the result
Matrix matrix_multiply(Matrix l, Matrix r);
// Return result of applying given function to each entry in the matrix independently
Matrix matrix_apply(Matrix m, matrix_entrytrans f);
// Draws a value from the standard normal distribution
double stdnormal();
// Defines the identity activation function f(x) = x
double identity(double x);
// Defines the rectified linear unit activation function
double lrelu(double x);
// Defines a simple counting sequence
double countseq(unsigned int i);
#endif

BIN
extension/src/test/test_ann Executable file

Binary file not shown.

View File

@ -0,0 +1,28 @@
#include "../ann.h"
#define TEST1_LAYERS 4
int main(int argc, char **argv) {
Layer layers[] = {{1, &lrelu}, {3, &lrelu}, {3, &lrelu}, {2, &identity}};
Network network = network_create(layers, TEST1_LAYERS, 1);
for (int i = 0; i < TEST1_LAYERS; i++) {
matrix_print(network->outputs[i], stdout);
printf("\n");
if (i < TEST1_LAYERS - 1) {
matrix_print(network->weights[i], stdout);
printf("\n ------------- \n");
}
}
printf("===========================\n");
Matrix features = matrix_create(2, 1);
*(features->data + 0) = 1;
Matrix out = network_pass(network, features);
matrix_print(out, stdout);
return 0;
}

BIN
extension/src/test/test_math Executable file

Binary file not shown.

View File

@ -0,0 +1,20 @@
#include "../mymath.h"
int main(int argc, char **argv) {
Matrix l = matrix_seqcreate(4, 3, &countseq);
Matrix r = matrix_seqcreate(3, 2, &countseq);
matrix_print(l, stdout);
printf("\n");
matrix_print(r, stdout);
printf("\n");
Matrix p = matrix_multiply(l, r);
matrix_print(p, stdout);
matrix_free(l);
matrix_free(r);
matrix_free(p);
return 0;
}