XCSF  1.4.7
XCSF learning classifier system
gp.c
Go to the documentation of this file.
1 /*
2  * This program is free software: you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation, either version 3 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program. If not, see <http://www.gnu.org/licenses/>.
14  */
15 
25 #include "gp.h"
26 #include "sam.h"
27 #include "utils.h"
28 
29 #define GP_NUM_FUNC (4)
30 #define ADD (0)
31 #define SUB (1)
32 #define MUL (2)
33 #define DIV (3)
34 
35 #define STRING_ADD ("+\0")
36 #define STRING_SUB ("-\0")
37 #define STRING_MUL ("*\0")
38 #define STRING_DIV ("/\0")
39 
40 #define N_MU (1)
41 #define RET_MIN (-1000)
42 #define RET_MAX (1000)
43 
47 static const int MU_TYPE[N_MU] = { SAM_RATE_SELECT };
48 
55 static int
56 tree_traverse(int *tree, int pos)
57 {
58  if (tree[pos] >= GP_NUM_FUNC) {
59  ++pos;
60  return pos;
61  }
62  ++pos;
63  return tree_traverse(tree, tree_traverse(tree, pos));
64 }
65 
76 static int
77 tree_grow(const struct ArgsGPTree *args, int *tree, const int pos,
78  const int max, const int depth)
79 {
80  if (pos >= max) {
81  return -1;
82  }
83  if (depth == 0 || (pos != 0 && rand_uniform(0, 1) < 0.5)) {
84  const int max_term = GP_NUM_FUNC + args->n_constants + args->n_inputs;
85  tree[pos] = rand_uniform_int(GP_NUM_FUNC, max_term);
86  return pos + 1;
87  }
88  tree[pos] = rand_uniform_int(0, GP_NUM_FUNC);
89  const int child = tree_grow(args, tree, pos + 1, max, depth - 1);
90  if (child < 0) {
91  return -1;
92  }
93  return tree_grow(args, tree, child, max, depth - 1);
94 }
95 
101 void
102 tree_rand(struct GPTree *gp, const struct ArgsGPTree *args)
103 {
104  gp->tree = malloc(sizeof(int) * args->max_len);
105  gp->len = 0;
106  while (gp->len < 1) {
107  gp->len = tree_grow(args, gp->tree, 0, args->max_len, args->init_depth);
108  }
109  gp->tree = realloc(gp->tree, sizeof(int) * gp->len);
110  gp->mu = malloc(sizeof(double) * N_MU);
111  sam_init(gp->mu, N_MU, MU_TYPE);
112 }
113 
118 void
119 tree_free(const struct GPTree *gp)
120 {
121  free(gp->tree);
122  free(gp->mu);
123 }
124 
132 double
133 tree_eval(struct GPTree *gp, const struct ArgsGPTree *args, const double *x)
134 {
135  const int node = gp->tree[gp->pos];
136  ++(gp->pos);
137  if (node >= GP_NUM_FUNC + args->n_constants) {
138  return x[node - GP_NUM_FUNC - args->n_constants];
139  }
140  if (node >= GP_NUM_FUNC) {
141  return args->constants[node - GP_NUM_FUNC];
142  }
143  const double a = clamp(tree_eval(gp, args, x), RET_MIN, RET_MAX);
144  const double b = clamp(tree_eval(gp, args, x), RET_MIN, RET_MAX);
145  switch (node) {
146  case ADD:
147  return a + b;
148  case SUB:
149  return a - b;
150  case MUL:
151  return a * b;
152  case DIV:
153  return (b != 0) ? (a / b) : a;
154  default:
155  printf("tree_eval() invalid function: %d\n", node);
156  exit(EXIT_FAILURE);
157  }
158 }
159 
165 static const char *
166 tree_function_string(const int node)
167 {
168  switch (node) {
169  case ADD:
170  return STRING_ADD;
171  case SUB:
172  return STRING_SUB;
173  case MUL:
174  return STRING_MUL;
175  case DIV:
176  return STRING_DIV;
177  default:
178  printf("tree_function_string() invalid function: %d\n", node);
179  exit(EXIT_FAILURE);
180  }
181 }
182 
188 static int
189 tree_function_int(const char *node)
190 {
191  if (strncmp(node, STRING_ADD, 2) == 0) {
192  return ADD;
193  }
194  if (strncmp(node, STRING_SUB, 2) == 0) {
195  return SUB;
196  }
197  if (strncmp(node, STRING_MUL, 2) == 0) {
198  return MUL;
199  }
200  if (strncmp(node, STRING_DIV, 2) == 0) {
201  return DIV;
202  }
203  return -1;
204 }
205 
214 static int
215 tree_string(const struct GPTree *gp, const struct ArgsGPTree *args, int pos,
216  cJSON *json)
217 {
218  const int node = gp->tree[pos];
219  if (node >= GP_NUM_FUNC) {
220  if (node >= GP_NUM_FUNC + args->n_constants) {
221  const int val = node - GP_NUM_FUNC - args->n_constants;
222  char buff[256];
223  snprintf(buff, 256, "feature_%d", val);
224  cJSON *input = cJSON_CreateString(buff);
225  cJSON_AddItemToArray(json, input);
226  } else {
227  const double val = args->constants[node - GP_NUM_FUNC];
228  cJSON *constant = cJSON_CreateNumber(val);
229  cJSON_AddItemToArray(json, constant);
230  }
231  ++pos;
232  return pos;
233  }
234  cJSON *leftp = cJSON_CreateString("(");
235  cJSON_AddItemToArray(json, leftp);
236  ++pos;
237  const int a1 = tree_string(gp, args, pos, json);
238  cJSON *func = cJSON_CreateString(tree_function_string(node));
239  cJSON_AddItemToArray(json, func);
240  const int a2 = tree_string(gp, args, a1, json);
241  cJSON *rightp = cJSON_CreateString(")");
242  cJSON_AddItemToArray(json, rightp);
243  return a2;
244 }
245 
252 char *
253 tree_json_export(const struct GPTree *gp, const struct ArgsGPTree *args)
254 {
255  cJSON *json = cJSON_CreateObject();
256  cJSON *tree = cJSON_CreateArray();
257  cJSON_AddItemToObject(json, "array", tree);
258  tree_string(gp, args, 0, tree);
259  cJSON *mutation = cJSON_CreateDoubleArray(gp->mu, N_MU);
260  cJSON_AddItemToObject(json, "mutation", mutation);
261  char *string = cJSON_Print(json);
262  cJSON_Delete(json);
263  return string;
264 }
265 
272 void
273 tree_json_import(struct GPTree *gp, const struct ArgsGPTree *args,
274  const cJSON *json)
275 {
276  (void) gp;
277  (void) args;
278  (void) json;
279  printf("Import error: GP trees not yet implemented\n");
280  exit(EXIT_FAILURE);
281 }
282 
288 void
289 tree_print(const struct GPTree *gp, const struct ArgsGPTree *args)
290 {
291  char *json_str = tree_json_export(gp, args);
292  printf("%s\n", json_str);
293  free(json_str);
294 }
295 
301 void
302 tree_copy(struct GPTree *dest, const struct GPTree *src)
303 {
304  dest->len = src->len;
305  dest->tree = malloc(sizeof(int) * src->len);
306  memcpy(dest->tree, src->tree, sizeof(int) * src->len);
307  dest->pos = src->pos;
308  dest->mu = malloc(sizeof(double) * N_MU);
309  memcpy(dest->mu, src->mu, sizeof(double) * N_MU);
310 }
311 
317 void
318 tree_crossover(struct GPTree *p1, struct GPTree *p2)
319 {
320  const int len1 = p1->len;
321  const int len2 = p2->len;
322  const int start1 = rand_uniform_int(0, len1);
323  const int end1 = tree_traverse(p1->tree, start1);
324  const int start2 = rand_uniform_int(0, len2);
325  const int end2 = tree_traverse(p2->tree, start2);
326  const int nlen1 = start1 + (end2 - start2) + (len1 - end1);
327  int *new1 = malloc(sizeof(int) * nlen1);
328  memcpy(&new1[0], &p1->tree[0], sizeof(int) * start1);
329  memcpy(&new1[start1], &p2->tree[start2], sizeof(int) * (end2 - start2));
330  memcpy(&new1[start1 + (end2 - start2)], &p1->tree[end1],
331  sizeof(int) * (len1 - end1));
332  const int nlen2 = start2 + (end1 - start1) + (len2 - end2);
333  int *new2 = malloc(sizeof(int) * nlen2);
334  memcpy(&new2[0], &p2->tree[0], sizeof(int) * start2);
335  memcpy(&new2[start2], &p1->tree[start1], sizeof(int) * (end1 - start1));
336  memcpy(&new2[start2 + (end1 - start1)], &p2->tree[end2],
337  sizeof(int) * (len2 - end2));
338  free(p1->tree);
339  free(p2->tree);
340  p1->tree = new1;
341  p2->tree = new2;
342  p1->len = tree_traverse(p1->tree, 0);
343  p2->len = tree_traverse(p2->tree, 0);
344 }
345 
354 bool
355 tree_mutate(struct GPTree *gp, const struct ArgsGPTree *args)
356 {
357  bool changed = false;
358  sam_adapt(gp->mu, N_MU, MU_TYPE);
359  const int max_term = GP_NUM_FUNC + args->n_constants + args->n_inputs;
360  for (int i = 0; i < gp->len; ++i) {
361  if (rand_uniform(0, 1) < gp->mu[0]) {
362  const int orig = gp->tree[i];
363  if (gp->tree[i] >= GP_NUM_FUNC) {
364  gp->tree[i] = rand_uniform_int(GP_NUM_FUNC, max_term);
365  } else {
366  gp->tree[i] = rand_uniform_int(0, GP_NUM_FUNC);
367  }
368  if (gp->tree[i] != orig) {
369  changed = true;
370  }
371  }
372  }
373  return changed;
374 }
375 
382 size_t
383 tree_save(const struct GPTree *gp, FILE *fp)
384 {
385  size_t s = 0;
386  s += fwrite(&gp->pos, sizeof(int), 1, fp);
387  s += fwrite(&gp->len, sizeof(int), 1, fp);
388  s += fwrite(gp->tree, sizeof(int), gp->len, fp);
389  s += fwrite(gp->mu, sizeof(double), N_MU, fp);
390  return s;
391 }
392 
399 size_t
400 tree_load(struct GPTree *gp, FILE *fp)
401 {
402  size_t s = 0;
403  s += fread(&gp->pos, sizeof(int), 1, fp);
404  s += fread(&gp->len, sizeof(int), 1, fp);
405  if (gp->len < 1) {
406  printf("tree_load(): read error\n");
407  gp->len = 1;
408  exit(EXIT_FAILURE);
409  }
410  gp->tree = malloc(sizeof(int) * gp->len);
411  gp->mu = malloc(sizeof(double) * N_MU);
412  s += fread(gp->tree, sizeof(int), gp->len, fp);
413  s += fread(gp->mu, sizeof(double), N_MU, fp);
414  return s;
415 }
416 
421 void
423 {
424  args->max = 0;
425  args->min = 0;
426  args->n_inputs = 0;
427  args->n_constants = 0;
428  args->init_depth = 0;
429  args->max_len = 0;
430  args->constants = NULL;
431 }
432 
437 void
439 {
440  free(args->constants);
441 }
442 
448 char *
450 {
451  cJSON *json = cJSON_CreateObject();
452  cJSON_AddNumberToObject(json, "min_constant", args->min);
453  cJSON_AddNumberToObject(json, "max_constant", args->max);
454  cJSON_AddNumberToObject(json, "n_constants", args->n_constants);
455  cJSON_AddNumberToObject(json, "init_depth", args->init_depth);
456  cJSON_AddNumberToObject(json, "max_len", args->max_len);
457  char *string = cJSON_Print(json);
458  cJSON_Delete(json);
459  return string;
460 }
461 
468 char *
469 tree_args_json_import(struct ArgsGPTree *args, cJSON *json)
470 {
471  for (cJSON *iter = json; iter != NULL; iter = iter->next) {
472  if (strncmp(iter->string, "min_constant\0", 13) == 0 &&
473  cJSON_IsNumber(iter)) {
474  tree_param_set_min(args, iter->valuedouble);
475  } else if (strncmp(iter->string, "max_constant\0", 13) == 0 &&
476  cJSON_IsNumber(iter)) {
477  tree_param_set_max(args, iter->valuedouble);
478  } else if (strncmp(iter->string, "n_constants\0", 12) == 0 &&
479  cJSON_IsNumber(iter)) {
480  tree_param_set_n_constants(args, iter->valueint);
481  } else if (strncmp(iter->string, "init_depth\0", 11) == 0 &&
482  cJSON_IsNumber(iter)) {
483  tree_param_set_init_depth(args, iter->valueint);
484  } else if (strncmp(iter->string, "max_len\0", 8) == 0 &&
485  cJSON_IsNumber(iter)) {
486  tree_param_set_max_len(args, iter->valueint);
487  } else {
488  return iter->string;
489  }
490  }
491  return NULL;
492 }
493 
500 size_t
501 tree_args_save(const struct ArgsGPTree *args, FILE *fp)
502 {
503  size_t s = 0;
504  s += fwrite(&args->max, sizeof(double), 1, fp);
505  s += fwrite(&args->min, sizeof(double), 1, fp);
506  s += fwrite(&args->n_inputs, sizeof(int), 1, fp);
507  s += fwrite(&args->n_constants, sizeof(int), 1, fp);
508  s += fwrite(&args->init_depth, sizeof(int), 1, fp);
509  s += fwrite(&args->max_len, sizeof(int), 1, fp);
510  s += fwrite(args->constants, sizeof(double), args->n_constants, fp);
511  return s;
512 }
513 
520 size_t
521 tree_args_load(struct ArgsGPTree *args, FILE *fp)
522 {
523  size_t s = 0;
524  s += fread(&args->max, sizeof(double), 1, fp);
525  s += fread(&args->min, sizeof(double), 1, fp);
526  s += fread(&args->n_inputs, sizeof(int), 1, fp);
527  s += fread(&args->n_constants, sizeof(int), 1, fp);
528  s += fread(&args->init_depth, sizeof(int), 1, fp);
529  s += fread(&args->max_len, sizeof(int), 1, fp);
530  s += fread(args->constants, sizeof(double), args->n_constants, fp);
531  return s;
532 }
533 
538 void
540 {
541  if (args->constants != NULL) {
542  free(args->constants);
543  }
544  args->constants = malloc(sizeof(double) * args->n_constants);
545  for (int i = 0; i < args->n_constants; ++i) {
546  args->constants[i] = rand_uniform(args->min, args->max);
547  }
548 }
549 
550 /* parameter setters */
551 
552 void
553 tree_param_set_max(struct ArgsGPTree *args, const double a)
554 {
555  args->max = a;
556 }
557 
558 void
559 tree_param_set_min(struct ArgsGPTree *args, const double a)
560 {
561  args->min = a;
562 }
563 
564 void
565 tree_param_set_n_inputs(struct ArgsGPTree *args, const int a)
566 {
567  if (a < 1) {
568  printf("Warning: tried to set GP N_INPUTS too small\n");
569  args->n_inputs = 1;
570  } else {
571  args->n_inputs = a;
572  }
573 }
574 
575 void
576 tree_param_set_n_constants(struct ArgsGPTree *args, const int a)
577 {
578  if (a < 1) {
579  printf("Warning: tried to set GP N_CONSTANTS too small\n");
580  args->n_constants = 1;
581  } else {
582  args->n_constants = a;
583  }
584 }
585 
586 void
587 tree_param_set_init_depth(struct ArgsGPTree *args, const int a)
588 {
589  if (a < 1) {
590  printf("Warning: tried to set GP INIT_DEPTH too small\n");
591  args->init_depth = 1;
592  } else {
593  args->init_depth = a;
594  }
595 }
596 
597 void
598 tree_param_set_max_len(struct ArgsGPTree *args, const int a)
599 {
600  if (a < 1) {
601  printf("Warning: tried to set GP MAX_LEN too small\n");
602  args->max_len = 1;
603  } else {
604  args->max_len = a;
605  }
606 }
void tree_json_import(struct GPTree *gp, const struct ArgsGPTree *args, const cJSON *json)
Creates a GP tree from a cJSON object.
Definition: gp.c:273
#define STRING_SUB
Subtraction.
Definition: gp.c:36
static const int MU_TYPE[(1)]
Self-adaptation method for mutating GP trees.
Definition: gp.c:47
void tree_args_free(struct ArgsGPTree *args)
Frees memory used by GP tree parameters.
Definition: gp.c:438
static const char * tree_function_string(const int node)
Returns a string representation of a node function.
Definition: gp.c:166
size_t tree_load(struct GPTree *gp, FILE *fp)
Reads a GP tree from a file.
Definition: gp.c:400
void tree_param_set_max_len(struct ArgsGPTree *args, const int a)
Definition: gp.c:598
static int tree_grow(const struct ArgsGPTree *args, int *tree, const int pos, const int max, const int depth)
Grows a random GP tree of specified max length and depth.
Definition: gp.c:77
#define RET_MAX
Maximum tree return value.
Definition: gp.c:42
#define SUB
Subtraction function.
Definition: gp.c:31
#define STRING_ADD
Addition.
Definition: gp.c:35
void tree_crossover(struct GPTree *p1, struct GPTree *p2)
Performs sub-tree crossover.
Definition: gp.c:318
size_t tree_save(const struct GPTree *gp, FILE *fp)
Writes the GP tree to a file.
Definition: gp.c:383
#define RET_MIN
Minimum tree return value.
Definition: gp.c:41
void tree_print(const struct GPTree *gp, const struct ArgsGPTree *args)
Prints a GP tree.
Definition: gp.c:289
#define N_MU
Number of tree-GP mutation rates.
Definition: gp.c:40
void tree_param_set_max(struct ArgsGPTree *args, const double a)
Definition: gp.c:553
void tree_param_set_n_inputs(struct ArgsGPTree *args, const int a)
Definition: gp.c:565
static int tree_string(const struct GPTree *gp, const struct ArgsGPTree *args, int pos, cJSON *json)
Returns a json formatted string representation of a GP tree.
Definition: gp.c:215
char * tree_args_json_import(struct ArgsGPTree *args, cJSON *json)
Sets the GP tree parameters from a cJSON object.
Definition: gp.c:469
#define DIV
Division function.
Definition: gp.c:33
double tree_eval(struct GPTree *gp, const struct ArgsGPTree *args, const double *x)
Evaluates a GP tree.
Definition: gp.c:133
#define ADD
Addition function.
Definition: gp.c:30
void tree_param_set_n_constants(struct ArgsGPTree *args, const int a)
Definition: gp.c:576
static int tree_traverse(int *tree, int pos)
Traverses a GP tree.
Definition: gp.c:56
#define STRING_DIV
Division.
Definition: gp.c:38
void tree_args_init_constants(struct ArgsGPTree *args)
Builds global constants used by GP trees.
Definition: gp.c:539
#define MUL
Multiplication function.
Definition: gp.c:32
void tree_param_set_init_depth(struct ArgsGPTree *args, const int a)
Definition: gp.c:587
#define STRING_MUL
Multiplication.
Definition: gp.c:37
bool tree_mutate(struct GPTree *gp, const struct ArgsGPTree *args)
Performs point mutation on a GP tree.
Definition: gp.c:355
void tree_args_init(struct ArgsGPTree *args)
Sets tree GP parameters to default values.
Definition: gp.c:422
size_t tree_args_load(struct ArgsGPTree *args, FILE *fp)
Loads Tree GP parameters.
Definition: gp.c:521
void tree_param_set_min(struct ArgsGPTree *args, const double a)
Definition: gp.c:559
char * tree_args_json_export(const struct ArgsGPTree *args)
Returns a json formatted string of the GP tree parameters.
Definition: gp.c:449
char * tree_json_export(const struct GPTree *gp, const struct ArgsGPTree *args)
Returns a json formatted string representation of a GP tree.
Definition: gp.c:253
static int tree_function_int(const char *node)
Returns an integer representation of a node function.
Definition: gp.c:189
#define GP_NUM_FUNC
Number of selectable GP functions.
Definition: gp.c:29
void tree_copy(struct GPTree *dest, const struct GPTree *src)
Copies a GP tree.
Definition: gp.c:302
void tree_free(const struct GPTree *gp)
Frees a GP tree.
Definition: gp.c:119
size_t tree_args_save(const struct ArgsGPTree *args, FILE *fp)
Saves Tree GP parameters.
Definition: gp.c:501
void tree_rand(struct GPTree *gp, const struct ArgsGPTree *args)
Creates a random GP tree.
Definition: gp.c:102
An implementation of GP trees based upon TinyGP.
void sam_init(double *mu, const int N, const int *type)
Initialises self-adaptive mutation rates.
Definition: sam.c:43
void sam_adapt(double *mu, const int N, const int *type)
Self-adapts mutation rates.
Definition: sam.c:68
Self-adaptive mutation functions.
#define SAM_RATE_SELECT
Ten normally distributed rates.
Definition: sam.h:29
Parameters for initialising GP trees.
Definition: gp.h:31
double * constants
Constants available for GP trees.
Definition: gp.h:38
int n_constants
Number of constants available.
Definition: gp.h:35
int init_depth
Initial depth.
Definition: gp.h:36
int max_len
Maximum initial length.
Definition: gp.h:37
double min
Minimum value of a constant.
Definition: gp.h:33
int n_inputs
Number of inputs.
Definition: gp.h:34
double max
Maximum value of a constant.
Definition: gp.h:32
GP tree data structure.
Definition: gp.h:44
int * tree
Flattened tree representation of functions and terminals.
Definition: gp.h:45
int pos
Current position in the tree.
Definition: gp.h:47
double * mu
Mutation rates.
Definition: gp.h:48
int len
Size of the tree.
Definition: gp.h:46
int rand_uniform_int(const int min, const int max)
Returns a uniform random integer [min,max] not inclusive of max.
Definition: utils.c:74
double rand_uniform(const double min, const double max)
Returns a uniform random float [min,max].
Definition: utils.c:62
Utility functions for random number handling, etc.
static double clamp(const double a, const double min, const double max)
Returns a float clamped within the specified range.
Definition: utils.h:60