COSC-4P82-Final-Project/lib/lilgp/kernel/mutate.c

533 lines
16 KiB
C
Raw Permalink Normal View History

2024-04-01 00:01:49 -04:00
/* 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 <lilgp.h>
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
}