XCSF 1.4.8
XCSF learning classifier system
Loading...
Searching...
No Matches
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
47static const int MU_TYPE[N_MU] = { SAM_RATE_SELECT };
48
55static int
56tree_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
76static int
77tree_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
101void
102tree_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
118void
119tree_free(const struct GPTree *gp)
120{
121 free(gp->tree);
122 free(gp->mu);
123}
124
132double
133tree_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
165static const char *
166tree_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
188static int
189tree_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
214static int
215tree_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
252char *
253tree_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
272void
273tree_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
288void
289tree_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
301void
302tree_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
317void
318tree_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
354bool
355tree_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
382size_t
383tree_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
399size_t
400tree_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
421void
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
437void
439{
440 free(args->constants);
441}
442
448char *
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
468char *
469tree_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
500size_t
501tree_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
520size_t
521tree_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
538void
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
552void
553tree_param_set_max(struct ArgsGPTree *args, const double a)
554{
555 args->max = a;
556}
557
558void
559tree_param_set_min(struct ArgsGPTree *args, const double a)
560{
561 args->min = a;
562}
563
564void
565tree_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
575void
576tree_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
586void
587tree_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
597void
598tree_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
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_args_json_import(struct ArgsGPTree *args, cJSON *json)
Sets the GP tree parameters from a cJSON object.
Definition gp.c:469
#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
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
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
#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
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