/* genetic_algorithm.c -- This belongs to gneural_network gneural_network is the GNU package which implements a programmable neural network. Copyright (C) 2016 Jean Michel Sellier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // performs a genetic search of the best weights during the training process #include "genetic_algorithm.h" typedef struct { double error; double* weights; } individual_t; int actual_weight_count() { int k = 0; int i, j; for (i = 0; i < NNUM; ++i) { for (j = 0; j < NEURON[i].nw; ++j) { k++; } } return k; } void crossover(double w1, double w2, double* n1, double* n2) { static int POSS = 1; double average = (w1 + w2) / 2; double mid = (WMAX + WMIN) / 2; *n1 = average; if (average > mid) { *n2 = average - (WMAX - WMIN) / 2; } else if (average < mid){ *n2 = average + (WMAX - WMIN) / 2; } else /* == mid*/{ if (POSS) { *n2 = WMAX; POSS = 0; } else { *n2 = WMIN; POSS = 1; } } } void mutation(double* weight, double rate) { if (rnd() > rate) return; if (rnd() > 0.5) { /* go plus */ *weight += (rnd() * (WMAX - WMIN) / 2); if (*weight > WMAX) *weight -= (WMAX - WMIN); } else { /* go minus */ *weight -= (rnd() * (WMAX - WMIN) / 2); if (*weight < WMIN) *weight += (WMAX - WMIN); } } int individual_compare(const void* a, const void* b) { individual_t* ia = *(individual_t**)a; individual_t* ib = *(individual_t**)b; if (ia->error > ib->error) return 1; if (ia->error < ib->error) return -1; return 0; } void init_individuals(individual_t** individuals, int size) { int n; for (n = 0; n < size; ++n) { int k = 0; int i, j; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { individuals[n]->weights[k] = rnd(); k++; } } } } void reproduce_next_generation(individual_t** individuals, int size, int weight_cout, double rate) { int pos = size; int i, j, k; for (i = 0; i < size; ++i) { for (j = i + 1; j < size; ++j) { for (k = 0; k < weight_cout; ++k) { double w1 = individuals[i]->weights[k]; double w2 = individuals[j]->weights[k]; crossover(w1, w2, individuals[pos]->weights + k, individuals[pos + 1]->weights + k); mutation(individuals[pos]->weights + k, rate); mutation(individuals[pos + 1]->weights + k, rate); } pos += 2; } } } void selection(individual_t** individuals, int size) { int pool_size = size * size; int n; for (n = 0; n < pool_size; ++n) { int k = 0; int i, j; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { NEURON[i].w[j] = individuals[n]->weights[k]; k++; } } individuals[n]->error = error(ERROR_TYPE); } qsort(individuals, pool_size, sizeof(individual_t*), individual_compare); } void genetic_algorithm2(int output /* screen output - on/off */, int nmax /* number of generations */, int npop /* number of individuals per generation */, double rate /* rate of change between one generation and the parent */, double eps /* numerical accuracy */) { int i, j, k, n; int pool_size = npop * npop; individual_t** individuals = malloc(pool_size * sizeof(individual_t*)); if (individuals == NULL) { printf("GA: Not enough memory to allocate individual index table\n"); exit(0); } for (i = 0; i < pool_size; ++i) { individuals[i] = malloc(sizeof(individual_t)); if (individuals[i] == NULL) { printf("GA: Not enough memory to allocate individuals\n"); exit(0); } individuals[i]->weights = malloc(NNUM * MAX_IN * sizeof(double)); if (individuals[i]->weights == NULL) { printf("GA: Not enough memory to allocate weights\n"); exit(0); } } init_individuals(individuals, npop); int weight_cout = actual_weight_count(); for (n = 0; n < nmax; ++n) { reproduce_next_generation(individuals, npop, weight_cout, rate); selection(individuals, npop); if (output == ON) printf("GA2: %d %.12g\n", n, individuals[0]->error); if (individuals[0]->error < eps) break; } if (individuals[0]->error > eps) { if (output == ON) printf("GA2: error still greater than %g after %d iterations", eps, nmax); } k = 0; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { NEURON[i].w[j] = individuals[0]->weights[k]; k++; } } for (i = 0; i < pool_size; ++i) { free(individuals[i]->weights); free(individuals[i]); } free(individuals); } void genetic_algorithm(int output /* screen output - on/off */, int nmax /* number of generations */, int npop /* number of individuals per generation */, double rate /* rate of change between one generation and the parent */, double eps /* numerical accuracy */) { int m, n; int m_best; register int i, j, k; double* err; double err_tmp; double err_best; double* w; double** wbest; err_tmp = err_best = 1.e8; // just a big number err = malloc((npop + 1) * sizeof(*err)); if (err == NULL) { printf("GA: Not enough memory to allocate\ndouble *err\n"); exit(0); } w = malloc((NNUM * MAX_IN + 1) * sizeof(*w)); if (w == NULL) { printf("GA: Not enough memory to allocate\ndouble *w\n"); exit(0); } wbest = (double**)malloc((npop + 1) * sizeof(double*)); if (wbest == NULL) { printf("GA: Not enough memory to allocate\ndouble *wbest[npop+1]\n"); exit(0); } for (i = 0; i <= npop; i++) { wbest[i] = (double*)malloc((NNUM * MAX_IN + 1) * sizeof(double)); if (wbest[i] == NULL) { printf("GA: Not enough memory to allocate\ndouble wbest[%d][NNUM* eps); n++) { // computes the error for every element for (m = 0; m < npop; m++) { k = 0; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { NEURON[i].w[j] = wbest[m][k]; k++; } } err[m] = error(ERROR_TYPE); } // checks for the best element m_best = -1000; for (m = 0; m < npop; m++) { if (err[m] < err_best) { m_best = m; err_best = err[m]; } } // updates the weights if (m_best != -1000) { k = 0; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { NEURON[i].w[j] = w[k] = wbest[m_best][k]; k++; } } err_tmp = err_best; } // creates a new generation from the best breed for (m = 0; m < npop; m++) { k = 0; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { wbest[m][k] = NEURON[i].w[j] + rate * rnd() * (WMAX - WMIN); k++; } } } if (output == ON) printf("GA: %d %g\n", n, err_best); } // final update of the weights k = 0; for (i = 0; i < NNUM; i++) { for (j = 0; j < NEURON[i].nw; j++) { NEURON[i].w[j] = w[k]; k++; } } if (output == ON) printf("\n"); free(err); free(w); for (i = 0; i <= npop; i++) { free(wbest[i]); } free(wbest); }