aboutsummaryrefslogtreecommitdiffstats
path: root/test/monniaux/genann
diff options
context:
space:
mode:
authorDavid Monniaux <david.monniaux@univ-grenoble-alpes.fr>2019-06-07 15:48:49 +0200
committerDavid Monniaux <david.monniaux@univ-grenoble-alpes.fr>2019-06-07 15:48:49 +0200
commitc441b86ea994d65f50c5dec30c9c97250d88ac98 (patch)
treef5a917239b300ef727ecdb6e56f4940a5a477ba4 /test/monniaux/genann
parent02c22aa772338b24fc5797f57e584e428230e563 (diff)
downloadcompcert-kvx-c441b86ea994d65f50c5dec30c9c97250d88ac98.tar.gz
compcert-kvx-c441b86ea994d65f50c5dec30c9c97250d88ac98.zip
réseau de neurones
Diffstat (limited to 'test/monniaux/genann')
-rw-r--r--test/monniaux/genann/example/iris.data150
-rw-r--r--test/monniaux/genann/example4shorter.c141
-rw-r--r--test/monniaux/genann/genann.c409
-rw-r--r--test/monniaux/genann/genann.h109
-rw-r--r--test/monniaux/genann/make.proto2
5 files changed, 811 insertions, 0 deletions
diff --git a/test/monniaux/genann/example/iris.data b/test/monniaux/genann/example/iris.data
new file mode 100644
index 00000000..a3490e0e
--- /dev/null
+++ b/test/monniaux/genann/example/iris.data
@@ -0,0 +1,150 @@
+5.1,3.5,1.4,0.2,Iris-setosa
+4.9,3.0,1.4,0.2,Iris-setosa
+4.7,3.2,1.3,0.2,Iris-setosa
+4.6,3.1,1.5,0.2,Iris-setosa
+5.0,3.6,1.4,0.2,Iris-setosa
+5.4,3.9,1.7,0.4,Iris-setosa
+4.6,3.4,1.4,0.3,Iris-setosa
+5.0,3.4,1.5,0.2,Iris-setosa
+4.4,2.9,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.4,3.7,1.5,0.2,Iris-setosa
+4.8,3.4,1.6,0.2,Iris-setosa
+4.8,3.0,1.4,0.1,Iris-setosa
+4.3,3.0,1.1,0.1,Iris-setosa
+5.8,4.0,1.2,0.2,Iris-setosa
+5.7,4.4,1.5,0.4,Iris-setosa
+5.4,3.9,1.3,0.4,Iris-setosa
+5.1,3.5,1.4,0.3,Iris-setosa
+5.7,3.8,1.7,0.3,Iris-setosa
+5.1,3.8,1.5,0.3,Iris-setosa
+5.4,3.4,1.7,0.2,Iris-setosa
+5.1,3.7,1.5,0.4,Iris-setosa
+4.6,3.6,1.0,0.2,Iris-setosa
+5.1,3.3,1.7,0.5,Iris-setosa
+4.8,3.4,1.9,0.2,Iris-setosa
+5.0,3.0,1.6,0.2,Iris-setosa
+5.0,3.4,1.6,0.4,Iris-setosa
+5.2,3.5,1.5,0.2,Iris-setosa
+5.2,3.4,1.4,0.2,Iris-setosa
+4.7,3.2,1.6,0.2,Iris-setosa
+4.8,3.1,1.6,0.2,Iris-setosa
+5.4,3.4,1.5,0.4,Iris-setosa
+5.2,4.1,1.5,0.1,Iris-setosa
+5.5,4.2,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.0,3.2,1.2,0.2,Iris-setosa
+5.5,3.5,1.3,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+4.4,3.0,1.3,0.2,Iris-setosa
+5.1,3.4,1.5,0.2,Iris-setosa
+5.0,3.5,1.3,0.3,Iris-setosa
+4.5,2.3,1.3,0.3,Iris-setosa
+4.4,3.2,1.3,0.2,Iris-setosa
+5.0,3.5,1.6,0.6,Iris-setosa
+5.1,3.8,1.9,0.4,Iris-setosa
+4.8,3.0,1.4,0.3,Iris-setosa
+5.1,3.8,1.6,0.2,Iris-setosa
+4.6,3.2,1.4,0.2,Iris-setosa
+5.3,3.7,1.5,0.2,Iris-setosa
+5.0,3.3,1.4,0.2,Iris-setosa
+7.0,3.2,4.7,1.4,Iris-versicolor
+6.4,3.2,4.5,1.5,Iris-versicolor
+6.9,3.1,4.9,1.5,Iris-versicolor
+5.5,2.3,4.0,1.3,Iris-versicolor
+6.5,2.8,4.6,1.5,Iris-versicolor
+5.7,2.8,4.5,1.3,Iris-versicolor
+6.3,3.3,4.7,1.6,Iris-versicolor
+4.9,2.4,3.3,1.0,Iris-versicolor
+6.6,2.9,4.6,1.3,Iris-versicolor
+5.2,2.7,3.9,1.4,Iris-versicolor
+5.0,2.0,3.5,1.0,Iris-versicolor
+5.9,3.0,4.2,1.5,Iris-versicolor
+6.0,2.2,4.0,1.0,Iris-versicolor
+6.1,2.9,4.7,1.4,Iris-versicolor
+5.6,2.9,3.6,1.3,Iris-versicolor
+6.7,3.1,4.4,1.4,Iris-versicolor
+5.6,3.0,4.5,1.5,Iris-versicolor
+5.8,2.7,4.1,1.0,Iris-versicolor
+6.2,2.2,4.5,1.5,Iris-versicolor
+5.6,2.5,3.9,1.1,Iris-versicolor
+5.9,3.2,4.8,1.8,Iris-versicolor
+6.1,2.8,4.0,1.3,Iris-versicolor
+6.3,2.5,4.9,1.5,Iris-versicolor
+6.1,2.8,4.7,1.2,Iris-versicolor
+6.4,2.9,4.3,1.3,Iris-versicolor
+6.6,3.0,4.4,1.4,Iris-versicolor
+6.8,2.8,4.8,1.4,Iris-versicolor
+6.7,3.0,5.0,1.7,Iris-versicolor
+6.0,2.9,4.5,1.5,Iris-versicolor
+5.7,2.6,3.5,1.0,Iris-versicolor
+5.5,2.4,3.8,1.1,Iris-versicolor
+5.5,2.4,3.7,1.0,Iris-versicolor
+5.8,2.7,3.9,1.2,Iris-versicolor
+6.0,2.7,5.1,1.6,Iris-versicolor
+5.4,3.0,4.5,1.5,Iris-versicolor
+6.0,3.4,4.5,1.6,Iris-versicolor
+6.7,3.1,4.7,1.5,Iris-versicolor
+6.3,2.3,4.4,1.3,Iris-versicolor
+5.6,3.0,4.1,1.3,Iris-versicolor
+5.5,2.5,4.0,1.3,Iris-versicolor
+5.5,2.6,4.4,1.2,Iris-versicolor
+6.1,3.0,4.6,1.4,Iris-versicolor
+5.8,2.6,4.0,1.2,Iris-versicolor
+5.0,2.3,3.3,1.0,Iris-versicolor
+5.6,2.7,4.2,1.3,Iris-versicolor
+5.7,3.0,4.2,1.2,Iris-versicolor
+5.7,2.9,4.2,1.3,Iris-versicolor
+6.2,2.9,4.3,1.3,Iris-versicolor
+5.1,2.5,3.0,1.1,Iris-versicolor
+5.7,2.8,4.1,1.3,Iris-versicolor
+6.3,3.3,6.0,2.5,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+7.1,3.0,5.9,2.1,Iris-virginica
+6.3,2.9,5.6,1.8,Iris-virginica
+6.5,3.0,5.8,2.2,Iris-virginica
+7.6,3.0,6.6,2.1,Iris-virginica
+4.9,2.5,4.5,1.7,Iris-virginica
+7.3,2.9,6.3,1.8,Iris-virginica
+6.7,2.5,5.8,1.8,Iris-virginica
+7.2,3.6,6.1,2.5,Iris-virginica
+6.5,3.2,5.1,2.0,Iris-virginica
+6.4,2.7,5.3,1.9,Iris-virginica
+6.8,3.0,5.5,2.1,Iris-virginica
+5.7,2.5,5.0,2.0,Iris-virginica
+5.8,2.8,5.1,2.4,Iris-virginica
+6.4,3.2,5.3,2.3,Iris-virginica
+6.5,3.0,5.5,1.8,Iris-virginica
+7.7,3.8,6.7,2.2,Iris-virginica
+7.7,2.6,6.9,2.3,Iris-virginica
+6.0,2.2,5.0,1.5,Iris-virginica
+6.9,3.2,5.7,2.3,Iris-virginica
+5.6,2.8,4.9,2.0,Iris-virginica
+7.7,2.8,6.7,2.0,Iris-virginica
+6.3,2.7,4.9,1.8,Iris-virginica
+6.7,3.3,5.7,2.1,Iris-virginica
+7.2,3.2,6.0,1.8,Iris-virginica
+6.2,2.8,4.8,1.8,Iris-virginica
+6.1,3.0,4.9,1.8,Iris-virginica
+6.4,2.8,5.6,2.1,Iris-virginica
+7.2,3.0,5.8,1.6,Iris-virginica
+7.4,2.8,6.1,1.9,Iris-virginica
+7.9,3.8,6.4,2.0,Iris-virginica
+6.4,2.8,5.6,2.2,Iris-virginica
+6.3,2.8,5.1,1.5,Iris-virginica
+6.1,2.6,5.6,1.4,Iris-virginica
+7.7,3.0,6.1,2.3,Iris-virginica
+6.3,3.4,5.6,2.4,Iris-virginica
+6.4,3.1,5.5,1.8,Iris-virginica
+6.0,3.0,4.8,1.8,Iris-virginica
+6.9,3.1,5.4,2.1,Iris-virginica
+6.7,3.1,5.6,2.4,Iris-virginica
+6.9,3.1,5.1,2.3,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+6.8,3.2,5.9,2.3,Iris-virginica
+6.7,3.3,5.7,2.5,Iris-virginica
+6.7,3.0,5.2,2.3,Iris-virginica
+6.3,2.5,5.0,1.9,Iris-virginica
+6.5,3.0,5.2,2.0,Iris-virginica
+6.2,3.4,5.4,2.3,Iris-virginica
+5.9,3.0,5.1,1.8,Iris-virginica
diff --git a/test/monniaux/genann/example4shorter.c b/test/monniaux/genann/example4shorter.c
new file mode 100644
index 00000000..ff4ce402
--- /dev/null
+++ b/test/monniaux/genann/example4shorter.c
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+#include "genann.h"
+
+#define VERIMAG
+#ifdef VERIMAG
+#include "../clock.h"
+#endif
+
+/* This example is to illustrate how to use GENANN.
+ * It is NOT an example of good machine learning techniques.
+ */
+
+const char *iris_data = "example/iris.data";
+
+double *input, *class;
+int samples;
+const char *class_names[] = {"Iris-setosa", "Iris-versicolor", "Iris-virginica"};
+
+void load_data() {
+ /* Load the iris data-set. */
+ FILE *in = fopen("example/iris.data", "r");
+ if (!in) {
+ printf("Could not open file: %s\n", iris_data);
+ exit(1);
+ }
+
+ /* Loop through the data to get a count. */
+ char line[1024];
+ while (!feof(in) && fgets(line, 1024, in)) {
+ ++samples;
+ }
+ fseek(in, 0, SEEK_SET);
+
+ printf("Loading %d data points from %s\n", samples, iris_data);
+
+ /* Allocate memory for input and output data. */
+ input = malloc(sizeof(double) * samples * 4);
+ class = malloc(sizeof(double) * samples * 3);
+
+ /* Read the file into our arrays. */
+ int i, j;
+ for (i = 0; i < samples; ++i) {
+ double *p = input + i * 4;
+ double *c = class + i * 3;
+ c[0] = c[1] = c[2] = 0.0;
+
+ if (fgets(line, 1024, in) == NULL) {
+ perror("fgets");
+ exit(1);
+ }
+
+ char *split = strtok(line, ",");
+ for (j = 0; j < 4; ++j) {
+ p[j] = atof(split);
+ split = strtok(0, ",");
+ }
+
+ split[strlen(split)-1] = 0;
+ if (strcmp(split, class_names[0]) == 0) {c[0] = 1.0;}
+ else if (strcmp(split, class_names[1]) == 0) {c[1] = 1.0;}
+ else if (strcmp(split, class_names[2]) == 0) {c[2] = 1.0;}
+ else {
+ printf("Unknown class %s.\n", split);
+ exit(1);
+ }
+
+ /* printf("Data point %d is %f %f %f %f -> %f %f %f\n", i, p[0], p[1], p[2], p[3], c[0], c[1], c[2]); */
+ }
+
+ fclose(in);
+}
+
+
+int main(int argc, char *argv[])
+{
+ printf("GENANN example 4.\n");
+ printf("Train an ANN on the IRIS dataset using backpropagation.\n");
+
+#ifdef VERIMAG
+ srand(42);
+#else
+ srand(time(0));
+#endif
+
+ /* Load the data from file. */
+ load_data();
+
+ /* 4 inputs.
+ * 1 hidden layer(s) of 4 neurons.
+ * 3 outputs (1 per class)
+ */
+ genann *ann = genann_init(4, 1, 4, 3);
+
+ int i, j;
+#ifdef VERIMAG
+ int loops = 500;
+#else
+ int loops = 5000;
+#endif
+
+ /* Train the network with backpropagation. */
+ printf("Training for %d loops over data.\n", loops);
+#ifdef VERIMAG
+ clock_prepare();
+ clock_start();
+#endif
+ for (i = 0; i < loops; ++i) {
+ for (j = 0; j < samples; ++j) {
+ genann_train(ann, input + j*4, class + j*3, .01);
+ }
+ /* printf("%1.2f ", xor_score(ann)); */
+ }
+
+ int correct = 0;
+ for (j = 0; j < samples; ++j) {
+ const double *guess = genann_run(ann, input + j*4);
+ if (class[j*3+0] == 1.0) {if (guess[0] > guess[1] && guess[0] > guess[2]) ++correct;}
+ else if (class[j*3+1] == 1.0) {if (guess[1] > guess[0] && guess[1] > guess[2]) ++correct;}
+ else if (class[j*3+2] == 1.0) {if (guess[2] > guess[0] && guess[2] > guess[1]) ++correct;}
+ else {printf("Logic error.\n"); exit(1);}
+ }
+#ifdef VERIMAG
+ clock_stop();
+#endif
+
+ printf("%d/%d correct (%0.1f%%).\n", correct, samples, (double)correct / samples * 100.0);
+
+#ifdef VERIMAG
+ print_total_clock();
+#endif
+
+ genann_free(ann);
+ free(input);
+ free(class);
+
+ return 0;
+}
diff --git a/test/monniaux/genann/genann.c b/test/monniaux/genann/genann.c
new file mode 100644
index 00000000..609c06a1
--- /dev/null
+++ b/test/monniaux/genann/genann.c
@@ -0,0 +1,409 @@
+/*
+ * GENANN - Minimal C Artificial Neural Network
+ *
+ * Copyright (c) 2015-2018 Lewis Van Winkle
+ *
+ * http://CodePlea.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+#define VERIMAG
+
+#include "genann.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef genann_act
+#define genann_act_hidden genann_act_hidden_indirect
+#define genann_act_output genann_act_output_indirect
+#else
+#define genann_act_hidden genann_act
+#define genann_act_output genann_act
+#endif
+
+#define LOOKUP_SIZE 4096
+
+double genann_act_hidden_indirect(const struct genann *ann, double a) {
+ return ann->activation_hidden(ann, a);
+}
+
+double genann_act_output_indirect(const struct genann *ann, double a) {
+ return ann->activation_output(ann, a);
+}
+
+const double sigmoid_dom_min = -15.0;
+const double sigmoid_dom_max = 15.0;
+double interval;
+double lookup[LOOKUP_SIZE];
+
+#ifdef __GNUC__
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define unused __attribute__((unused))
+#else
+#define likely(x) x
+#define unlikely(x) x
+#define unused
+#pragma warning(disable : 4996) /* For fscanf */
+#endif
+
+
+double static inline genann_act_sigmoid(const genann *ann unused, double a) {
+ if (a < -45.0) return 0;
+ if (a > 45.0) return 1;
+ return 1.0 / (1 + exp(-a));
+}
+
+void genann_init_sigmoid_lookup(const genann *ann) {
+ const double f = (sigmoid_dom_max - sigmoid_dom_min) / LOOKUP_SIZE;
+ int i;
+
+ interval = LOOKUP_SIZE / (sigmoid_dom_max - sigmoid_dom_min);
+ for (i = 0; i < LOOKUP_SIZE; ++i) {
+ lookup[i] = genann_act_sigmoid(ann, sigmoid_dom_min + f * i);
+ }
+}
+
+double static inline genann_act_sigmoid_cached(const genann *ann unused, double a) {
+#ifndef VERIMAG
+ assert(!isnan(a));
+#endif
+
+ if (a < sigmoid_dom_min) return lookup[0];
+ if (a >= sigmoid_dom_max) return lookup[LOOKUP_SIZE - 1];
+
+ size_t j = (size_t)((a-sigmoid_dom_min)*interval+0.5);
+
+ /* Because floating point... */
+ if (unlikely(j >= LOOKUP_SIZE)) return lookup[LOOKUP_SIZE - 1];
+
+ return lookup[j];
+}
+
+double static inline genann_act_linear(const struct genann *ann unused, double a) {
+ return a;
+}
+
+double static inline genann_act_threshold(const struct genann *ann unused, double a) {
+ return a > 0;
+}
+
+genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs) {
+ if (hidden_layers < 0) return 0;
+ if (inputs < 1) return 0;
+ if (outputs < 1) return 0;
+ if (hidden_layers > 0 && hidden < 1) return 0;
+
+
+ const int hidden_weights = hidden_layers ? (inputs+1) * hidden + (hidden_layers-1) * (hidden+1) * hidden : 0;
+ const int output_weights = (hidden_layers ? (hidden+1) : (inputs+1)) * outputs;
+ const int total_weights = (hidden_weights + output_weights);
+
+ const int total_neurons = (inputs + hidden * hidden_layers + outputs);
+
+ /* Allocate extra size for weights, outputs, and deltas. */
+ const int size = sizeof(genann) + sizeof(double) * (total_weights + total_neurons + (total_neurons - inputs));
+ genann *ret = malloc(size);
+ if (!ret) return 0;
+
+ ret->inputs = inputs;
+ ret->hidden_layers = hidden_layers;
+ ret->hidden = hidden;
+ ret->outputs = outputs;
+
+ ret->total_weights = total_weights;
+ ret->total_neurons = total_neurons;
+
+ /* Set pointers. */
+ ret->weight = (double*)((char*)ret + sizeof(genann));
+ ret->output = ret->weight + ret->total_weights;
+ ret->delta = ret->output + ret->total_neurons;
+
+ genann_randomize(ret);
+
+ ret->activation_hidden = genann_act_sigmoid_cached;
+ ret->activation_output = genann_act_sigmoid_cached;
+
+ genann_init_sigmoid_lookup(ret);
+
+ return ret;
+}
+
+
+genann *genann_read(FILE *in) {
+ int inputs, hidden_layers, hidden, outputs;
+ int rc;
+
+ errno = 0;
+ rc = fscanf(in, "%d %d %d %d", &inputs, &hidden_layers, &hidden, &outputs);
+ if (rc < 4 || errno != 0) {
+ perror("fscanf");
+ return NULL;
+ }
+
+ genann *ann = genann_init(inputs, hidden_layers, hidden, outputs);
+
+ int i;
+ for (i = 0; i < ann->total_weights; ++i) {
+ errno = 0;
+ rc = fscanf(in, " %le", ann->weight + i);
+ if (rc < 1 || errno != 0) {
+ perror("fscanf");
+ genann_free(ann);
+
+ return NULL;
+ }
+ }
+
+ return ann;
+}
+
+
+genann *genann_copy(genann const *ann) {
+ const int size = sizeof(genann) + sizeof(double) * (ann->total_weights + ann->total_neurons + (ann->total_neurons - ann->inputs));
+ genann *ret = malloc(size);
+ if (!ret) return 0;
+
+ memcpy(ret, ann, size);
+
+ /* Set pointers. */
+ ret->weight = (double*)((char*)ret + sizeof(genann));
+ ret->output = ret->weight + ret->total_weights;
+ ret->delta = ret->output + ret->total_neurons;
+
+ return ret;
+}
+
+
+void genann_randomize(genann *ann) {
+ int i;
+ for (i = 0; i < ann->total_weights; ++i) {
+ double r = GENANN_RANDOM();
+ /* Sets weights from -0.5 to 0.5. */
+ ann->weight[i] = r - 0.5;
+ }
+}
+
+
+void genann_free(genann *ann) {
+ /* The weight, output, and delta pointers go to the same buffer. */
+ free(ann);
+}
+
+
+double const *genann_run(genann const *ann, double const *inputs) {
+ double const *w = ann->weight;
+ double *o = ann->output + ann->inputs;
+ double const *i = ann->output;
+
+ /* Copy the inputs to the scratch area, where we also store each neuron's
+ * output, for consistency. This way the first layer isn't a special case. */
+ memcpy(ann->output, inputs, sizeof(double) * ann->inputs);
+
+ int h, j, k;
+
+ if (!ann->hidden_layers) {
+ double *ret = o;
+ for (j = 0; j < ann->outputs; ++j) {
+ double sum = *w++ * -1.0;
+ for (k = 0; k < ann->inputs; ++k) {
+ sum += *w++ * i[k];
+ }
+ *o++ = genann_act_output(ann, sum);
+ }
+
+ return ret;
+ }
+
+ /* Figure input layer */
+ for (j = 0; j < ann->hidden; ++j) {
+ double sum = *w++ * -1.0;
+ for (k = 0; k < ann->inputs; ++k) {
+ sum += *w++ * i[k];
+ }
+ *o++ = genann_act_hidden(ann, sum);
+ }
+
+ i += ann->inputs;
+
+ /* Figure hidden layers, if any. */
+ for (h = 1; h < ann->hidden_layers; ++h) {
+ for (j = 0; j < ann->hidden; ++j) {
+ double sum = *w++ * -1.0;
+ for (k = 0; k < ann->hidden; ++k) {
+ sum += *w++ * i[k];
+ }
+ *o++ = genann_act_hidden(ann, sum);
+ }
+
+ i += ann->hidden;
+ }
+
+ double const *ret = o;
+
+ /* Figure output layer. */
+ for (j = 0; j < ann->outputs; ++j) {
+ double sum = *w++ * -1.0;
+ for (k = 0; k < ann->hidden; ++k) {
+ sum += *w++ * i[k];
+ }
+ *o++ = genann_act_output(ann, sum);
+ }
+
+ /* Sanity check that we used all weights and wrote all outputs. */
+ assert(w - ann->weight == ann->total_weights);
+ assert(o - ann->output == ann->total_neurons);
+
+ return ret;
+}
+
+
+void genann_train(genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate) {
+ /* To begin with, we must run the network forward. */
+ genann_run(ann, inputs);
+
+ int h, j, k;
+
+ /* First set the output layer deltas. */
+ {
+ double const *o = ann->output + ann->inputs + ann->hidden * ann->hidden_layers; /* First output. */
+ double *d = ann->delta + ann->hidden * ann->hidden_layers; /* First delta. */
+ double const *t = desired_outputs; /* First desired output. */
+
+
+ /* Set output layer deltas. */
+ if (genann_act_output == genann_act_linear ||
+ ann->activation_output == genann_act_linear) {
+ for (j = 0; j < ann->outputs; ++j) {
+ *d++ = *t++ - *o++;
+ }
+ } else {
+ for (j = 0; j < ann->outputs; ++j) {
+ *d++ = (*t - *o) * *o * (1.0 - *o);
+ ++o; ++t;
+ }
+ }
+ }
+
+
+ /* Set hidden layer deltas, start on last layer and work backwards. */
+ /* Note that loop is skipped in the case of hidden_layers == 0. */
+ for (h = ann->hidden_layers - 1; h >= 0; --h) {
+
+ /* Find first output and delta in this layer. */
+ double const *o = ann->output + ann->inputs + (h * ann->hidden);
+ double *d = ann->delta + (h * ann->hidden);
+
+ /* Find first delta in following layer (which may be hidden or output). */
+ double const * const dd = ann->delta + ((h+1) * ann->hidden);
+
+ /* Find first weight in following layer (which may be hidden or output). */
+ double const * const ww = ann->weight + ((ann->inputs+1) * ann->hidden) + ((ann->hidden+1) * ann->hidden * (h));
+
+ for (j = 0; j < ann->hidden; ++j) {
+
+ double delta = 0;
+
+ for (k = 0; k < (h == ann->hidden_layers-1 ? ann->outputs : ann->hidden); ++k) {
+ const double forward_delta = dd[k];
+ const int windex = k * (ann->hidden + 1) + (j + 1);
+ const double forward_weight = ww[windex];
+ delta += forward_delta * forward_weight;
+ }
+
+ *d = *o * (1.0-*o) * delta;
+ ++d; ++o;
+ }
+ }
+
+
+ /* Train the outputs. */
+ {
+ /* Find first output delta. */
+ double const *d = ann->delta + ann->hidden * ann->hidden_layers; /* First output delta. */
+
+ /* Find first weight to first output delta. */
+ double *w = ann->weight + (ann->hidden_layers
+ ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * ann->hidden * (ann->hidden_layers-1))
+ : (0));
+
+ /* Find first output in previous layer. */
+ double const * const i = ann->output + (ann->hidden_layers
+ ? (ann->inputs + (ann->hidden) * (ann->hidden_layers-1))
+ : 0);
+
+ /* Set output layer weights. */
+ for (j = 0; j < ann->outputs; ++j) {
+ *w++ += *d * learning_rate * -1.0;
+ for (k = 1; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k) {
+ *w++ += *d * learning_rate * i[k-1];
+ }
+
+ ++d;
+ }
+
+ assert(w - ann->weight == ann->total_weights);
+ }
+
+
+ /* Train the hidden layers. */
+ for (h = ann->hidden_layers - 1; h >= 0; --h) {
+
+ /* Find first delta in this layer. */
+ double const *d = ann->delta + (h * ann->hidden);
+
+ /* Find first input to this layer. */
+ double const *i = ann->output + (h
+ ? (ann->inputs + ann->hidden * (h-1))
+ : 0);
+
+ /* Find first weight to this layer. */
+ double *w = ann->weight + (h
+ ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * (ann->hidden) * (h-1))
+ : 0);
+
+
+ for (j = 0; j < ann->hidden; ++j) {
+ *w++ += *d * learning_rate * -1.0;
+ for (k = 1; k < (h == 0 ? ann->inputs : ann->hidden) + 1; ++k) {
+ *w++ += *d * learning_rate * i[k-1];
+ }
+ ++d;
+ }
+
+ }
+
+}
+
+
+void genann_write(genann const *ann, FILE *out) {
+ fprintf(out, "%d %d %d %d", ann->inputs, ann->hidden_layers, ann->hidden, ann->outputs);
+
+ int i;
+ for (i = 0; i < ann->total_weights; ++i) {
+ fprintf(out, " %.20e", ann->weight[i]);
+ }
+}
+
+
diff --git a/test/monniaux/genann/genann.h b/test/monniaux/genann/genann.h
new file mode 100644
index 00000000..7eeb1cdc
--- /dev/null
+++ b/test/monniaux/genann/genann.h
@@ -0,0 +1,109 @@
+/*
+ * GENANN - Minimal C Artificial Neural Network
+ *
+ * Copyright (c) 2015-2018 Lewis Van Winkle
+ *
+ * http://CodePlea.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+
+#ifndef GENANN_H
+#define GENANN_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef GENANN_RANDOM
+/* We use the following for uniform random numbers between 0 and 1.
+ * If you have a better function, redefine this macro. */
+#define GENANN_RANDOM() (((double)rand())/RAND_MAX)
+#endif
+
+struct genann;
+
+typedef double (*genann_actfun)(const struct genann *ann, double a);
+
+typedef struct genann {
+ /* How many inputs, outputs, and hidden neurons. */
+ int inputs, hidden_layers, hidden, outputs;
+
+ /* Which activation function to use for hidden neurons. Default: gennann_act_sigmoid_cached*/
+ genann_actfun activation_hidden;
+
+ /* Which activation function to use for output. Default: gennann_act_sigmoid_cached*/
+ genann_actfun activation_output;
+
+ /* Total number of weights, and size of weights buffer. */
+ int total_weights;
+
+ /* Total number of neurons + inputs and size of output buffer. */
+ int total_neurons;
+
+ /* All weights (total_weights long). */
+ double *weight;
+
+ /* Stores input array and output of each neuron (total_neurons long). */
+ double *output;
+
+ /* Stores delta of each hidden and output neuron (total_neurons - inputs long). */
+ double *delta;
+
+} genann;
+
+/* Creates and returns a new ann. */
+genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs);
+
+/* Creates ANN from file saved with genann_write. */
+genann *genann_read(FILE *in);
+
+/* Sets weights randomly. Called by init. */
+void genann_randomize(genann *ann);
+
+/* Returns a new copy of ann. */
+genann *genann_copy(genann const *ann);
+
+/* Frees the memory used by an ann. */
+void genann_free(genann *ann);
+
+/* Runs the feedforward algorithm to calculate the ann's output. */
+double const *genann_run(genann const *ann, double const *inputs);
+
+/* Does a single backprop update. */
+void genann_train(genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate);
+
+/* Saves the ann. */
+void genann_write(genann const *ann, FILE *out);
+
+void genann_init_sigmoid_lookup(const genann *ann);
+#ifndef VERIMAG
+double genann_act_sigmoid(const genann *ann, double a);
+double genann_act_sigmoid_cached(const genann *ann, double a);
+double genann_act_threshold(const genann *ann, double a);
+double genann_act_linear(const genann *ann, double a);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*GENANN_H*/
diff --git a/test/monniaux/genann/make.proto b/test/monniaux/genann/make.proto
new file mode 100644
index 00000000..7c4248bf
--- /dev/null
+++ b/test/monniaux/genann/make.proto
@@ -0,0 +1,2 @@
+sources: example4shorter.c genann.c
+target: genann4 \ No newline at end of file