/* lil-gp Genetic Programming System, version 1.0, 11 July 1995 * Copyright (C) 1995 Michigan State University * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Douglas Zongker (zongker@isl.cps.msu.edu) * Dr. Bill Punch (punch@isl.cps.msu.edu) * * Computer Science Department * A-714 Wells Hall * Michigan State University * East Lansing, Michigan 48824 * USA * */ #include /* Read in an individual from a file, skipping any text in between */ void mod_read_tree_recurse ( int space, ephem_const **eind, FILE *fil, int tree, char *string, int skip ) { function *f; int i, j; ephem_const *ep; if( skip ) { i = 0; /*Read up until we get the first parenthesies */ while( i != '(' ) { if( feof(fil) ) { fseek( fil, 0L, SEEK_SET ); } i = fgetc(fil); } if( i != '(' ) { error( E_FATAL_ERROR, "Ran out of file space while reading in saved tree"); } } /* read up until a nonwhitespace character in file. the nonwhitespace character is saved in string[0]. */ while ( isspace(string[0]=fgetc(fil)) ); /* get the next character. */ i = fgetc ( fil ); if ( isspace(i) ) /* if the next character is whitespace, then string[0] is a one-character function name. null-terminate the string. */ string[1] = 0; else { /** if the next character is not whitespace, then string[0] is either an open parenthesis or the first character of a multi-character function name. **/ /* push the next character back. */ ungetc ( i, fil ); /* read the function name. skip over an open parenthesis, if there is one. */ fscanf ( fil, "%s ", string+(string[0]!='(') ); } #ifdef DEBUG_READTREE fprintf ( stderr, "function name is [%s]\n", string ); #endif /* look up the function name in this tree's function set. if the function is an ERC terminal (the name is of the form "name:ERCindex"), then place the ERC address in ep. */ f = get_function_by_name ( tree, string, &ep, eind ); /* add an lnode to the tree. */ gensp_next(space)->f = f; switch ( f->type ) { case TERM_NORM: case TERM_ARG: case EVAL_TERM: break; case TERM_ERC: /* record the ERC address as the next lnode in the array. */ gensp_next(space)->d = ep; break; case FUNC_DATA: case EVAL_DATA: /** recursively read child functions, no skip nodes needed. **/ for ( i = 0; i < f->arity; ++i ) mod_read_tree_recurse ( space, eind, fil, tree, string, 0 ); break; case FUNC_EXPR: case EVAL_EXPR: /** recursively read child functions, recording skip values. **/ for ( i = 0; i < f->arity; ++i ) { /* save an lnode for the skip value. */ j = gensp_next_int ( space ); /* read the child tree. */ mod_read_tree_recurse ( space, eind, fil, tree, string, 0); /* figure out how big the child tree was, and save that number in the skip node. */ gensp[space].data[j].s = gensp[space].used-j-1; } break; } } /* generate_random_population() * * fills a population with randomly generated members. */ void generate_random_population ( population *p, int *mindepth, int *maxdepth, int *method, FILE **dataum ) { int i, j, k, m; int attempts; int totalattempts = 0; int depth; int attempts_generation; tree *temp; int totalnodes = 0; int flag; char buf[2048]; int ignore_limits; /* how many consecutive rejected trees we will tolerate before giving up. */ attempts_generation = atoi ( get_parameter ( "init.random_attempts" ) ); if ( attempts_generation <= 0 ) error ( E_FATAL_ERROR, "\"init.random_attempts\" must be positive." ); ignore_limits = (get_parameter("init.ignore_limits")!=NULL); temp = (tree *)MALLOC ( tree_count * sizeof ( tree ) ); k = 0; attempts = attempts_generation; while ( k < p->size ) { /* total nodes in the individual being generated. */ totalnodes = 0; for ( j = 0; j < tree_count; ++j ) { if ( attempts <= 0 ) error ( E_FATAL_ERROR, "The last %d trees generated have been bad. Giving up.", attempts_generation ); --attempts; ++totalattempts; /* pick a depth on the depth ramp. */ depth = mindepth[j] + random_int ( &globrand, maxdepth[j] - mindepth[j] + 1 ); /* clear a generation space. */ gensp_reset ( 0 ); /** generate the tree. **/ switch ( method[j] ) { case GENERATE_FULL: generate_random_full_tree ( 0, depth, fset+tree_map[j].fset, tree_map[j].return_type); break; case GENERATE_GROW: generate_random_grow_tree ( 0, depth, fset+tree_map[j].fset, tree_map[j].return_type); break; case GENERATE_HALF_AND_HALF: if ( random_double(&globrand) < 0.5 ) generate_random_full_tree ( 0, depth, fset+tree_map[j].fset, tree_map[j].return_type); else generate_random_grow_tree ( 0, depth, fset+tree_map[j].fset, tree_map[j].return_type); break; case LOAD_FILE: gensp_reset(0); mod_read_tree_recurse( 0, NULL, dataum[j], j, buf, 1); /* need to do something here about limit checking */ break; } /** throw away the tree if it's too big. **/ /*printf("Attempted Tree: "); print_tree(gensp[0].data,stdout);*/ /* first check the node limits. */ flag = 0; m = tree_nodes ( gensp[0].data ); if (method[j]!=LOAD_FILE) { if ( tree_map[j].nodelimit > -1 && m > tree_map[j].nodelimit ) { --j; continue; } /* now change the depth limits. */ if ( tree_map[j].depthlimit > -1 && tree_depth ( gensp[0].data ) > tree_map[j].depthlimit ) { --j; continue; } /* count total nodes in the individual being created. */ } totalnodes += m; gensp_dup_tree ( 0, temp+j ); } if (!ignore_limits) { /* is the individual over the total node limit (if one is set)? */ if ( ind_nodelimit > -1 && totalnodes > ind_nodelimit ) { #ifdef DEBUG printf ( "overall node limit violated.\n" ); #endif /* yes, so delete it and try again. */ for ( j = 0; j < tree_count; ++j ) free_tree ( temp+j ); continue; } /* throw away the individual if it's a duplicate. */ for ( i = 0; i < k; ++i ) { flag = 0; for ( j = 0; j < tree_count && !flag; ++j ) { if ( temp[j].size == p->ind[i].tr[j].size ) { if ( memcmp ( temp[j].data, p->ind[i].tr[j].data, temp[j].size * sizeof ( lnode ) ) ) { flag = 1; } } else flag = 1; } if ( !flag ) break; } if ( i < k ) { #ifdef DEBUG printf ( "duplicate individual: (same as %d)\n", i ); for ( j = 0; j < tree_count; ++j ) { printf ( " tree %d: ", j ); print_tree ( temp[j].data, stdout ); } #endif /* individual is a duplicate, throw it away. */ --attempts; for ( j = 0; j < tree_count; ++j ) free_tree ( temp+j ); continue; } } /** we now have a good individual to put in the population. */ /* copy the tree array. */ memcpy ( p->ind[k].tr, temp, tree_count * sizeof ( tree ) ); /* reference ERCs. */ for ( j = 0; j < tree_count; ++j ) reference_ephem_constants ( p->ind[k].tr[j].data, 1 ); #ifdef DUMP_POPULATION printf ( "individual %5d:\n", k ); print_individual ( p->ind+k, stdout ); #endif /* mark individual as unevaluated. */ p->ind[k].evald = EVAL_CACHE_INVALID; p->ind[k].flags = FLAG_NONE; attempts = attempts_generation; ++k; } FREE ( temp ); oprintf ( OUT_SYS, 10, " %d trees were generated to fill the population of %d (%d trees).\n", totalattempts, p->size, p->size * tree_count ); } /* allocate_population() * * allocates a population structure with the given size. */ population *allocate_population ( int size ) { int i; population *p = (population *)MALLOC ( sizeof ( population ) ); p->size = size; p->next = 0; /* allocate the array of individuals. */ p->ind = (individual *)MALLOC ( size * sizeof ( individual ) ); for ( i = 0; i < size; ++i ) { /* each individual has a block of tree structures, allocate those here. */ p->ind[i].tr = (tree *)MALLOC ( tree_count * sizeof ( tree ) ); p->ind[i].evald = EVAL_CACHE_INVALID; p->ind[i].flags = FLAG_NONE; } return p; } /* free_multi_population() * * frees the populations in a multipop structure. */ void free_multi_population ( multipop *mp ) { int i; for ( i = 0; i < mp->size; ++i ) free_population ( mp->pop[i] ); FREE ( mp->pop ); FREE ( mp ); } /* free_population() * * frees a population and all the individuals in it. */ void free_population ( population *p ) { int i, j; for ( i = 0; i < p->size; ++i ) { for ( j = 0; j < tree_count; ++j ) { /* dereference ERCs. */ reference_ephem_constants ( p->ind[i].tr[j].data, -1 ); free_tree ( &(p->ind[i].tr[j]) ); } FREE ( p->ind[i].tr ); } FREE ( p->ind ); FREE ( p ); } /* initial_multi_population() * * randomly fills a multipop strcture with individuals. */ multipop *initial_multi_population ( void ) { char *param; char temp_name[256]; char **tree_replace; multipop *mpop; int i; int *mindepth, *maxdepth, *method; char *cp; char pnamebuf[100]; FILE **tree_data; FILE *fp; oputs ( OUT_SYS, 10, "creating initial population(s):\n" ); mpop = (multipop *)MALLOC ( sizeof ( multipop ) ); /* how many subpops are we supposed to have? */ param = get_parameter ( "multiple.subpops" ); mpop->size = atoi ( param ); if ( mpop->size <= 0 ) error ( E_FATAL_ERROR, "\"%s\" is not a valid value for \"multiple.subpops\".", param ); /* allocate that many population pointers. */ mpop->pop = (population **)MALLOC ( sizeof ( population* ) * mpop->size ); /* read the depth ramp and generation method(s), which can be different for each tree. */ tree_replace = (char **)MALLOC(sizeof(char *)*tree_count); tree_data = (FILE **)MALLOC( sizeof(FILE *)*tree_count); for( i = 0; i < tree_count; i++ ) { sprintf( temp_name, "tree-replace[%d]", i ); tree_replace[i] = get_parameter( temp_name ); } for( i = 0; i < tree_count; i++ ) { if( tree_replace[i] != NULL ) { tree_data[i] = fopen( tree_replace[i], "r" ); if( fp == NULL ) { /* Error Handler */ error( E_FATAL_ERROR, "Unable to open file %s\n", tree_replace[i] ); } } else { tree_data[i] = NULL; } } mindepth = (int *)MALLOC ( tree_count * sizeof ( int ) ); maxdepth = (int *)MALLOC ( tree_count * sizeof ( int ) ); method = (int *)MALLOC ( tree_count * sizeof ( int ) ); for ( i = 0; i < tree_count; ++i ) { mindepth[i] = -1; maxdepth[i] = -1; /** read the depth ramp. **/ sprintf ( pnamebuf, "init.tree[%d].depth", i ); param = get_parameter ( pnamebuf ); if ( !param ) param = get_parameter ( "init.depth" ); if ( param == NULL ) error ( E_FATAL_ERROR, "\"init.tree[%d].depth\" must be specified.", i ); /** parse the depth ramp ("min-max" or just "val"). **/ mindepth[i] = strtol ( param, &cp, 10 ); if ( *cp ) { if ( *cp == '-' ) { maxdepth[i] = strtol ( cp+1, &cp, 10 ); } if ( *cp ) { error ( E_FATAL_ERROR, "\"init.tree[%d].depth\" is malformed.", i ); } } if ( maxdepth[i] == -1 ) maxdepth[i] = mindepth[i]; if ( mindepth[i] > maxdepth[i] ) error ( E_FATAL_ERROR, "\"init.tree[%d].depth\" is malformed.", i ); /** read the method. **/ sprintf ( pnamebuf, "init.tree[%d].method", i ); param = get_parameter ( pnamebuf ); if ( !param ) param = get_parameter ( "init.method" ); if ( param == NULL ) error ( E_FATAL_ERROR, "\"init.tree[%d].method\" must be set.", i ); if ( strcmp ( param, "half_and_half" ) == 0 ) method[i] = GENERATE_HALF_AND_HALF; else if ( strcmp ( param, "full" ) == 0 ) method[i] = GENERATE_FULL; else if ( strcmp ( param, "grow" ) == 0 ) method[i] = GENERATE_GROW; else if ( strcmp ( param, "load" ) == 0 ) method[i]= LOAD_FILE; else error ( E_FATAL_ERROR, "\"init.tree[%d].method\": \"%s\" is not a known generation method.", i, param ); } /* generate each population. */ for ( i = 0; i < mpop->size; ++i ) mpop->pop[i] = initial_population ( mindepth, maxdepth, method, tree_data ); for( i = 0; i < tree_count; i++ ) { if( tree_data[i] != NULL ) fclose(tree_data[i]); } FREE ( mindepth ); FREE ( maxdepth ); FREE ( method ); FREE ( tree_replace ); FREE ( tree_data ); oputs ( OUT_SYS, 10, " initial population(s) complete.\n" ); return mpop; } /* initial_population() * * creates a population structure and fills it with randomly * generated individuals. */ population *initial_population ( int *mindepth, int *maxdepth, int *method, FILE **dataum ) { population *pop; int pop_size; char *param; /* get the population size and allocate. */ param = get_parameter ( "pop_size" ); if ( param == NULL ) error ( E_FATAL_ERROR, "no value specified for \"pop_size\"." ); pop_size = atoi ( param ); pop = allocate_population ( pop_size ); /* get the generation method and create the random population. */ generate_random_population ( pop, mindepth, maxdepth, method, dataum ); return pop; }