/* 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 typedef struct { int keep_trying; int num_times; /* Number of times we should keep trying. If 0, infinity. --Sean */ double internal; double external; double *tree; double treetotal; char *sname; sel_context *sc; int method; int mindepth, maxdepth; } mutate_data; /* operator_mutate_init() * * initialize a breedphase record for mutation. much of this is cut and * paste from the crossover operator; see that for (a few) more comments. */ int operator_mutate_init ( char *options, breedphase *bp ) { int errors = 0; mutate_data *md; int i, j, k, m; double r; char **argv, **targv; int internalset = 0, externalset = 0; char *cp; md = (mutate_data *)MALLOC ( sizeof ( mutate_data ) ); /* fill in the breedphase record. */ bp->gp_operator = OPERATOR_MUTATE; bp->data = (void *)md; bp->operator_free = operator_mutate_free; bp->operator_start = operator_mutate_start; bp->operator_end = operator_mutate_end; bp->operator_operate = operator_mutate; /* default values for the mutation-specific data structure. */ md->keep_trying = 0; md->internal = 0.9; md->external = 0.1; md->tree = (double *)MALLOC ( tree_count * sizeof ( double ) ); for ( j = 0; j < tree_count; ++j ) md->tree[j] = 0.0; md->treetotal = 0.0; md->sname = NULL; md->method = GENERATE_HALF_AND_HALF; md->mindepth = -1; md->maxdepth = -1; j = parse_o_rama ( options, &argv ); for ( i = 0; i < j; ++i ) { if ( strcmp ( "keep_trying", argv[i] ) == 0 ) { md->keep_trying = translate_binary ( argv[++i] ); if ( md->keep_trying == -1 ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a valid setting for \"keep_trying\".", argv[i] ); } } /* parse number of times to try */ else if (strcmp ("num_times", argv[i]) == 0) { md->num_times = translate_binary(argv[++i]); if (md->num_times < 0 ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a valid setting for \"num_times\".", argv[i] ); } } else if ( strcmp ( "internal", argv[i] ) == 0 ) { internalset = 1; md->internal = strtod ( argv[++i], NULL ); if ( md->internal < 0.0 ) { ++errors; error ( E_ERROR, "mutation: \"internal\" must be nonnegative." ); } } else if ( strcmp ( "external", argv[i] ) == 0 ) { externalset = 1; md->external = strtod ( argv[++i], NULL ); if ( md->external < 0.0 ) { ++errors; error ( E_ERROR, "mutation: \"external\" must be nonnegative." ); } } else if ( strcmp ( "select", argv[i] ) == 0 ) { if ( !exists_select_method ( argv[++i] ) ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a known selection method.", argv[i] ); } FREE ( md->sname ); md->sname = (char *)MALLOC ( (strlen(argv[i])+1) * sizeof ( char ) ); strcpy ( md->sname, argv[i] ); } else if ( strcmp ( "method", argv[i] ) == 0 ) { ++i; if ( strcmp ( argv[i], "half_and_half" ) == 0 ) md->method = GENERATE_HALF_AND_HALF; else if ( strcmp ( argv[i], "grow" ) == 0 ) md->method = GENERATE_GROW; else if ( strcmp ( argv[i], "full" ) == 0 ) md->method = GENERATE_FULL; else { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a known generation method.", argv[i] ); } } else if ( strcmp ( "depth", argv[i] ) == 0 ) { md->mindepth = strtol ( argv[++i], &cp, 10 ); if ( *cp == 0 ) md->maxdepth = md->mindepth; else if ( *cp == '-' ) { md->maxdepth = strtol ( cp+1, &cp, 10 ); if ( *cp ) { ++errors; error ( E_ERROR, "mutation: malformed depth string \"%s\".", argv[i] ); } } else { ++errors; error ( E_ERROR, "mutation: malformed depth string \"%s\".", argv[i] ); } } else if ( strcmp ( "tree", argv[i] ) == 0 ) { k = parse_o_rama ( argv[++i], &targv ); if ( k != tree_count ) { ++errors; error ( E_ERROR, "mutation: wrong number of tree fields: \"%s\".", argv[i] ); } else { for ( m = 0; m < k; ++m ) { md->tree[m] = strtod ( targv[m], &cp ); if ( *cp ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a number.", targv[m] ); } } } free_o_rama ( k, &targv ); } else if ( strncmp ( "tree", argv[i], 4 ) == 0 ) { k = strtol ( argv[i]+4, &cp, 10 ); if ( *cp ) { ++errors; error ( E_ERROR, "mutation: unknown option \"%s\".", argv[i] ); } if ( k < 0 || k >= tree_count ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is out of range.", argv[i] ); } else { md->tree[k] = strtod ( argv[++i], &cp ); if ( *cp ) { ++errors; error ( E_ERROR, "mutation: \"%s\" is not a number.", argv[i] ); } } } else { ++errors; error ( E_ERROR, "mutation: unknown option \"%s\".", argv[i] ); } } free_o_rama ( j, &argv ); if ( internalset && !externalset ) md->external = 0.0; else if ( !internalset && externalset ) md->internal = 0.0; if ( md->sname == NULL ) { ++errors; error ( E_ERROR, "mutation: no selection method specified." ); } if ( md->mindepth == -1 && md->maxdepth == -1 ) { md->mindepth = 0; md->maxdepth = 4; } if ( md->mindepth < 0 || md->maxdepth < 0 || md->maxdepth < md->mindepth ) { ++errors; error ( E_ERROR, "mutation: bad depth range.\n" ); } for ( j = 0; j < tree_count; ++j ) md->treetotal += md->tree[j]; if ( md->treetotal == 0.0 ) { for ( j = 0; j < tree_count; ++j ) md->tree[j] = 1.0; md->treetotal = tree_count; } r = 0.0; for ( j = 0; j < tree_count; ++j ) r = (md->tree[j] += r); #ifdef DEBUG if ( !errors ) { printf ( "mutation options:\n" ); printf ( " internal: %lf external: %lf\n", md->internal, md->external ); printf ( " keep_trying: %d\n", md->keep_trying ); printf ( " selection: %s\n", md->sname==NULL?"NULL":md->sname ); printf ( " method: %d mindepth: %d maxdepth: %d\n", md->method, md->mindepth, md->maxdepth ); printf ( " tree total: %lf\n", md->treetotal ); for ( j = 0; j < tree_count; ++j ) printf ( " tree %d: %lf\n", j, md->tree[j] ); } #endif return errors; } /* operator_mutate_free() * * free mutation stuff. */ void operator_mutate_free ( void *data ) { mutate_data * md; md = (mutate_data *)data; FREE ( md->sname ); FREE ( md->tree ); FREE ( md ); } /* operator_mutate_start() * * get selection context for mutation operator. */ void operator_mutate_start ( population *oldpop, void *data ) { mutate_data * md; select_context_func_ptr select_con; md = (mutate_data *)data; select_con = get_select_context ( md->sname ); md->sc = select_con ( SELECT_INIT, NULL, oldpop, md->sname ); } /* operator_mutate_end() * * free selection context for mutation operator. */ void operator_mutate_end ( void *data ) { mutate_data * md; md = (mutate_data *)data; md->sc->context_method ( SELECT_CLEAN, md->sc, NULL, NULL ); } /* operator_mutate() * * do the mutation. */ void operator_mutate ( population *oldpop, population *newpop, void *data ) { int i; int ps; lnode *replace[2]; int l, ns; int badtree; int repcount; mutate_data * md; int t; double r; int depth; int totalnodes; int p; int forceany; double total; int count=0; md = (mutate_data *)data; total = md->internal + md->external; /* choose a tree to mutate. */ r = random_double(&globrand) * md->treetotal; for ( t = 0; r >= md->tree[t]; ++t ); /* select an individual to mutate. */ p = md->sc->select_method ( md->sc ); ps = tree_nodes ( oldpop->ind[p].tr[t].data ); forceany = (ps==1||total==0.0); #ifdef DEBUG_MUTATE fprintf ( stderr, "the parent size is %d\n", ps ); fprintf ( stderr, " parent %4d: ", p ); print_tree ( oldpop->ind[p].tr[t].data, stderr ); #endif while(1) { count++; if ( forceany ) { /* choose any point. */ l = random_int ( &globrand, ps ); replace[0] = get_subtree ( oldpop->ind[p].tr[t].data, l ); } else if ( total*random_double(&globrand) < md->internal ) { /* choose an internal point. */ l = random_int ( &globrand, tree_nodes_internal ( oldpop->ind[p].tr[t].data ) ); replace[0] = get_subtree_internal ( oldpop->ind[p].tr[t].data, l ); } else { /* choose an external point. */ l = random_int ( &globrand, tree_nodes_external ( oldpop->ind[p].tr[t].data ) ); replace[0] = get_subtree_external ( oldpop->ind[p].tr[t].data, l ); } #ifdef DEBUG_MUTATE fprintf ( stderr, "selected for replacement: " ); print_tree ( replace[0], stderr ); #endif gensp_reset ( 1 ); /* pick a value from the depth ramp. */ depth = md->mindepth + random_int ( &globrand, md->maxdepth - md->mindepth + 1 ); /* grow the tree. */ switch ( md->method ) { case GENERATE_GROW: /* Look up the return type of the parent */ generate_random_grow_tree ( 1, depth, fset+tree_map[t].fset, replace[0]->f->return_type); break; case GENERATE_FULL: generate_random_full_tree ( 1, depth, fset+tree_map[t].fset, replace[0]->f->return_type); break; case GENERATE_HALF_AND_HALF: if ( random_double(&globrand) < 0.5 ) generate_random_grow_tree ( 1, depth, fset+tree_map[t].fset, replace[0]->f->return_type); else generate_random_full_tree ( 1, depth, fset+tree_map[t].fset , replace[0]->f->return_type); break; } #ifdef DEBUG_MUTATE fprintf ( stderr, "the new subtree is: " ); print_tree ( gensp[1].data, stderr ); #endif /* count the nodes in the new tree. */ ns = ps - tree_nodes ( replace[0] ) + tree_nodes ( gensp[1].data ); totalnodes = ns; /* check the mutated tree against node count and/or size limits. */ badtree = 0; if ( tree_map[t].nodelimit > -1 && ns > tree_map[t].nodelimit ) badtree = 1; else if ( tree_map[t].depthlimit > -1 ) { ns = tree_depth_to_subtree ( oldpop->ind[p].tr[t].data, replace[0] ) + tree_depth ( gensp[1].data ); if ( ns > tree_map[t].depthlimit ) badtree = 1; } /* if tree is too big and keep_trying is set, then skip to the stop and choose a new mutation point/mutant subtree. */ if ( md->keep_trying && badtree && (md->num_times==0 || md->num_times > count) ) continue; /* check mutated tree against whole-individual node limits. */ if ( ind_nodelimit > -1 ) { for ( i = 0; i < tree_count; ++i ) if ( i != t ) totalnodes += oldpop->ind[p].tr[i].nodes; badtree |= (totalnodes > ind_nodelimit); } /* if tree is too big and keep_trying is set, then skip to the stop and choose a new mutation point/mutant subtree. */ if ( md->keep_trying && badtree && (md->num_times==0 || md->num_times > count)) continue; if ( badtree ) { #ifdef DEBUG_MUTATE fprintf ( stderr, "new tree is too big; reproducing parent.\n" ); #endif /* tree too big but keep_trying not set, just reproduce parent tree. */ duplicate_individual ( (newpop->ind)+newpop->next, (oldpop->ind)+p ); } else { #ifdef DEBUG_MUTATE fprintf ( stderr, "new tree is permissible.\n" ); #endif /* copy the parent tree to the offspring position. */ duplicate_individual ( (newpop->ind)+newpop->next, (oldpop->ind)+p ); /* free the tree selected for mutation. */ free_tree ( newpop->ind[newpop->next].tr+t ); /* copy the selected tree, replacing the subtree at the mutation point with the randomly generated tree. */ replace[1] = gensp[1].data; copy_tree_replace_many ( 0, oldpop->ind[p].tr[t].data, replace, replace+1, 1, &repcount ); if ( repcount != 1 ) { error ( E_FATAL_ERROR, "botched mutation: this can't happen." ); } /* copy the tree to the new individual. */ gensp_dup_tree ( 0, newpop->ind[newpop->next].tr+t ); newpop->ind[newpop->next].evald = EVAL_CACHE_INVALID; newpop->ind[newpop->next].flags = FLAG_NONE; #ifdef DEBUG_MUTATE fprintf ( stderr, " the mutated individual is: " ); print_individual ( newpop->ind+newpop->next, stderr ); #endif } ++newpop->next; break; } #ifdef DEBUG_MUTATE printf ( "MUTATION COMPLETE.\n\n\n" ); #endif }