#ifndef lint static char *rcsid = "$Header: /tmp_mnt/vida/disks/disk5/Users/terry/r/gassy/RCS/main.c,v 1.24 1992/10/09 06:43:50 terry Exp terry $"; #endif #include "types.h" #include "main.h" #include "new.h" #include "scaling.h" #include "survivors.h" #include "selectors.h" #include "crossover.h" #include "mutate.h" #include "alleles.h" #include "defaults.h" #include "syscalls.h" #include "options.h" #include "utility.h" #include "print.h" #include #include #include #include #include #include #include /* * Globals. The stuff in 'context' can be altered by an application, * though most applications will not want to. The context of the run * are the things that one can imagine an application wanting to change * under some circumstance. They are provided to give as much control * to each application as we dare. See types.h for the contents of * the context variable. */ STRING myname; BOOLEAN sysdie = TRUE; STRING gassy_version = "3.0"; CONTEXT context; LONG total_allocked; /* * Things that are global to this file. All of these are concerned with * the details of a single run and none of them should be alterable by the * application. */ static BOOLEAN run_was_restored = FALSE; static POPULATION pop1; static POPULATION pop2; static OUTPUT_FILE output_files[NUM_OUTPUT_FILES]; static LONG seed = (LONG) -1; static FILE *interrupt_fp; static STRING interrupt_file; static STRING initial_population_file = (STRING) 0; static STRING restore_file = (STRING) 0; static BOOLEAN dump_final_population = FALSE; static BOOLEAN verbose = FALSE; static BOOLEAN overwrite = FALSE; static BOOLEAN data_only = FALSE; static BOOLEAN unique = FALSE; static BOOLEAN show_something = FALSE; static INT show_start = (INT) 1; static INT show_stop = INT_MAX; /* from */ static BOOLEAN catch_interrupts = TRUE; static BOOLEAN stop_at_max_fitness = FALSE; static BOOLEAN max_possible_fitness_found = FALSE; static FITNESS max_possible_fitness = WORST_FITNESS; /* from application.h */ static INT interrupt_flag = (INT) 0; static INT report_interval = (INT) 1; static INT top_ranking = 0; static INT nsurvivors; static INT nreproducers; static INT *selections; static INT time_to_next_mutation; #if defined(APP_HASHING) static INT hash_table_size = (INT) 0; #endif #if ! defined(APP_HAS_CONSTANT_POPULATION_SIZE) static INT last_population_size; static INT last_nselections; #endif /* * Pointers to the functions that will be used to do * the various GA things in the main loop. */ static PFPV scaling; static PFPV survivors; static PFPV selection; static PFPV crossover; static PFPV mutate; static PFV scaling_init; static PFV survivors_init; static PFV selection_init; static PFV crossover_init; static PFV mutate_init; /* * The actual names of these functions, as far as the outside * world is concerned. */ #if defined(SCALING) static STRING scaling_function_name = (STRING) 0; #endif static STRING survivors_function_name = (STRING) 0; static STRING selection_function_name = (STRING) 0; static STRING crossover_function_name = (STRING) 0; static STRING mutation_function_name = (STRING) 0; /* * These files contain definitions of the functions that are * built in to Gassy, the options that are available to all * applications and the parameters that are dumped on interrupt. * They are a little untidy and I like to keep them separate, so * I include them here. The purists would probably vomit. */ #include "main_functions.c" #include "main_options.c" #include "main_dumps.c" /* * These are dependent on what's in main_functions.c and so * have to appear after the #include. */ #if defined(SCALING) static FUNCTION *scaling_functions = main_scaling_functions; #endif static FUNCTION *survivor_functions = main_survivor_functions; static FUNCTION *selection_functions = main_selection_functions; static FUNCTION *crossover_functions = main_crossover_functions; static FUNCTION *mutation_functions = main_mutation_functions; INT main(argc, argv) INT argc; CHAR **argv; { register INT i; FILE *restore_fp = NULL; extern INT next_prime(); INT optind; myname = set_name(argv[0]); init_context(&context); init_output_files(); optind = process_options(argc, argv, main_options); set_functions(); /* DO SOMETHING ABOUT PASSING ARGS TO APP. */ if (run_was_restored == FALSE){ extern LONG seed_random(); /* Open the appropriate optional output files. */ open_output_files(); #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) context.genome_size = app_individual_size(); #endif allocate_memory(); seed = seed_random(seed); check_main_options(); /* Did they set max_possible_fitness to something? */ if (max_possible_fitness != WORST_FITNESS){ stop_at_max_fitness = TRUE; } if (verbose == TRUE){ show_something = TRUE; if (output_files[SHOW_AVERAGES].enabled == FALSE){ output_files[SHOW_AVERAGES].enabled = TRUE; output_files[SHOW_AVERAGES].filename = STDOUT_INDICATOR; output_files[SHOW_AVERAGES].fp = stdout; } if (output_files[SHOW_BEST_OF_GENERATION].enabled == FALSE){ output_files[SHOW_BEST_OF_GENERATION].enabled = TRUE; output_files[SHOW_BEST_OF_GENERATION].filename = STDOUT_INDICATOR; output_files[SHOW_BEST_OF_GENERATION].fp = stdout; } if (output_files[SHOW_BEST_OF_RUN].enabled == FALSE){ output_files[SHOW_BEST_OF_RUN].enabled = TRUE; output_files[SHOW_BEST_OF_RUN].filename = STDOUT_INDICATOR; output_files[SHOW_BEST_OF_RUN].fp = stdout; } #if defined(APP_HASHING) if (output_files[SHOW_HASHING].enabled == FALSE){ output_files[SHOW_HASHING].enabled = TRUE; output_files[SHOW_HASHING].filename = STDOUT_INDICATOR; output_files[SHOW_HASHING].fp = stdout; } #endif if (output_files[SHOW_INTERMEDIATES].enabled == FALSE){ output_files[SHOW_INTERMEDIATES].enabled = TRUE; output_files[SHOW_INTERMEDIATES].filename = STDOUT_INDICATOR; output_files[SHOW_INTERMEDIATES].fp = stdout; } if (output_files[SHOW_FINAL].enabled == FALSE){ output_files[SHOW_FINAL].enabled = TRUE; output_files[SHOW_FINAL].filename = STDOUT_INDICATOR; output_files[SHOW_FINAL].fp = stdout; } if (output_files[SHOW_TOTALS].enabled == FALSE){ output_files[SHOW_TOTALS].enabled = TRUE; output_files[SHOW_TOTALS].filename = STDOUT_INDICATOR; output_files[SHOW_TOTALS].fp = stdout; } } /* Set up stdout for the normal and app output files if they are not already set up. */ if (output_files[MAIN_OUTPUT].enabled == FALSE){ output_files[MAIN_OUTPUT].enabled = TRUE; output_files[MAIN_OUTPUT].filename = STDOUT_INDICATOR; output_files[MAIN_OUTPUT].fp = stdout; } if (output_files[APP_OUTPUT].enabled == FALSE){ output_files[APP_OUTPUT].enabled = TRUE; output_files[APP_OUTPUT].filename = STDOUT_INDICATOR; output_files[APP_OUTPUT].fp = stdout; } #if defined(APP_WANTS_ADDITIONAL_OPTION_CHECKING) app_check_options(); #endif #if defined(APP_NEEDS_TO_INITIALIZE) app_initialize(&context); #endif create_initial_alleles(&context); check_alleles(&context); #if defined(APP_ALLELE_PROBS_CHANGE) create_mutation_alleles(&context); check_alleles(&context); #endif } #if defined(APP_HASHING) /* * Create the hash table if hashing is on. */ if (context.hashing == TRUE){ if (hash_table_size != (INT) 0){ context.ht = hash_create(next_prime(next_prime(hash_table_size))); } else { /* Is this a bit excessive? */ context.ht = hash_create(next_prime(context.population_size * context.ngenerations / 2)); } } #endif /* * How many individuals get replaced each generation. */ nreproducers = (INT) (context.generation_gap_prob * context.population_size); nsurvivors = context.population_size - nreproducers; if (nsurvivors == (INT) 0 && context.elitist == TRUE){ nsurvivors++; nreproducers--; } /* * Assume all selection functions will select the population * size individuals to operate on. This may well not be so, * it may be simpler to code scaling selectors if they can select * some number of individuals that is more than the size of * the population. If so, they can alter the value of * context.nselections in their intialization function. */ context.nselections = context.population_size; /* * Call the initialization functions for the various command line * functions, if they exist. */ if (scaling_init != (PFV) 0){ (*scaling_init)(&context); } if (survivors_init != (PFV) 0){ (*survivors_init)(&context); } if (selection_init != (PFV) 0){ (*selection_init)(&context); } if (crossover_init != (PFV) 0){ (*crossover_init)(&context); } if (mutate_init != (PFV) 0){ (*mutate_init)(&context); } /* * Make an array for the functions that do some sort of selecting * of population members to fill. */ selections = (INT *) Malloc(context.nselections * sizeof(INT)); #if ! defined(APP_HAS_CONSTANT_POPULATION_SIZE) context.max_population_size = context.population_size; last_population_size = context.population_size; last_nselections = context.nselections; #endif if (catch_interrupts == TRUE){ interrupt_file = prepare_for_interrupts(&interrupt_fp); } for (; context.run <= context.nruns; context.run++){ if (run_was_restored == FALSE){ if (show_something == TRUE){ /* BOOLEAN stdout_done = FALSE; if (output_files[SHOW_AVERAGES].enabled == TRUE){ if (output_files[SHOW_AVERAGES].fp == stdout && stdout_done == FALSE){ fprintf(output_files[SHOW_AVERAGES].fp, "Run %d\n", context.run); stdout_done = TRUE; } } if (output_files[SHOW_BEST_OF_GENERATION].enabled == TRUE){ if (output_files[SHOW_BEST_OF_RUN].enabled == TRUE){ #if defined(APP_HASHING) if (output_files[SHOW_HASHING].enabled == TRUE){ #endif if (output_files[SHOW_INTERMEDIATES].enabled == TRUE){ if (output_files[SHOW_FINAL].enabled == TRUE){ if (output_files[SHOW_TOTALS].enabled == TRUE){ */ fprintf(output_files[MAIN_OUTPUT].fp, "Run %d\n", context.run); } context.generation = (INT) 1; read_initial_population(); /* Initialize the population. */ for (i = 0; i < context.population_size; i++){ context.population[i].evaluated = FALSE; } if (stop_at_max_fitness == TRUE && max_possible_fitness_found == TRUE){ max_possible_fitness_found = FALSE; } context.best_fitness_of_generation = WORST_FITNESS; context.total_fitness = (FITNESS) 0; time_to_next_mutation = when_is_next_mutation(context.mutation_prob); } else { /* * Set this false, even though it is not strictly true. * If we restored, we want to avoid setting all the above stuff the * first time through this loop as it has been restored. * * The second time through this section, the population will * be re-initialized in the above. */ run_was_restored = FALSE; } for (; context.generation <= context.ngenerations; context.generation++){ BOOLEAN show_stats_this_generation; /* Work out if we will be displaying statistics this generation. */ if (show_something == TRUE){ if (context.generation >= show_start && context.generation <= show_stop && context.generation % report_interval == 0){ show_stats_this_generation = TRUE; } else { show_stats_this_generation = FALSE; } } #if defined(APP_NOISY_FITNESS_FUNCTION) || defined(APP_EVALUATES_ENTIRE_POPULATION) /* * Evaluate the whole population. Most of this code is written in * template_main.c. */ app_evaluate_population(&context); #else /* * Evaluate the fitness of each individual (if need be). We check * to see if that individual has a fitness which is already known. * This could happen for several reasons - not being crossed over, * being part of the copied survivors, etc. */ for (i = 0; i < context.population_size; i++){ register FITNESS this_fitness; if (context.population[i].evaluated == FALSE){ #if defined(SCALING) this_fitness = context.population[i].unscaled_fitness = app_evaluate_individual(i, &context); #else this_fitness = context.population[i].fitness = app_evaluate_individual(i, &context); #endif context.population[i].evaluated = TRUE; } else { #if defined(SCALING) this_fitness = context.population[i].unscaled_fitness; #else this_fitness = context.population[i].fitness; #endif } context.total_fitness += this_fitness; /* * Watch for the best individual of the generation. */ if (this_fitness #if defined(APP_LOW_FITNESS_IS_BETTER) < #else > #endif context.best_fitness_of_generation){ context.best_individual_of_generation = i; context.best_fitness_of_generation = this_fitness; } } #endif #if defined(SCALING) /* * Call the scaling function. This fills in the fitness from the * unscaled_fitness for each population member. */ (VOID) (*scaling)(&context); #endif /* * Watch for and record the best individual of this run. */ if (context.best_fitness_of_generation #if defined(APP_LOW_FITNESS_IS_BETTER) < #else > #endif context.stats[context.run].best_individual.fitness){ context.stats[context.run].best_generation = context.generation; copy_individual(&context.stats[context.run].best_individual, &context.population[context.best_individual_of_generation], &context); if (stop_at_max_fitness == TRUE && context.best_fitness_of_generation >= max_possible_fitness){ max_possible_fitness_found = TRUE; } if (show_stats_this_generation == TRUE && output_files[SHOW_INTERMEDIATES].enabled == TRUE){ FILE *fp = output_files[SHOW_INTERMEDIATES].fp; if (data_only == TRUE){ fprintf(fp, "%d ", context.generation); print_fitness(fp, 0, context.best_fitness_of_generation); putc(' ', fp); print_individual(fp, &context.population[context.best_individual_of_generation]); putc('\n', fp); } else { fprintf(fp, "Generation %d, with fitness ", context.generation); print_fitness(fp, 0, context.best_fitness_of_generation); putc('\n', fp); print_individual(fp, &context.population[context.best_individual_of_generation]); putc('\n', fp); } fflush(fp); } } /* * Display statistics if it is the right time. Note that if no * show_ options were selected, this will never be true. This test makes * for faster runs in this case and also for faster runs when something is * being shown. */ if (show_something == TRUE){ /* Do things which need to be done every generation. */ if (output_files[SHOW_TOTALS].enabled == TRUE){ if (data_only == TRUE){ fprintf(output_files[SHOW_TOTALS].fp, "%d %d\n", context.generation, context.total_fitness); } else { fprintf(output_files[SHOW_TOTALS].fp, "Total fitness in generation %d was %d\n", context.generation, context.total_fitness); } } if (output_files[SHOW_BEST_OF_GENERATION].enabled == TRUE){ if (data_only == TRUE){ fprintf(output_files[SHOW_BEST_OF_GENERATION].fp, "%d %d\n", context.generation, context.best_fitness_of_generation); } else { fprintf(output_files[SHOW_BEST_OF_GENERATION].fp, "Best fitness in generation %d was %d\n", context.generation, context.best_fitness_of_generation); } } if (output_files[SHOW_AVERAGES].enabled == TRUE){ if (data_only == TRUE){ fprintf(output_files[SHOW_AVERAGES].fp, "%d %.2f\n", context.generation, (DOUBLE) context.total_fitness / (DOUBLE) context.population_size); } else { fprintf(output_files[SHOW_AVERAGES].fp, "Average fitness in generation %d was %.2f\n", context.generation, (DOUBLE) context.total_fitness / (DOUBLE) context.population_size); } } /* * And now things which obey the show_start, show_stop and report_interval settings. * The population printing stuff does not obey data_only. It is unlikely that it can * be used directly (even with no words) for input into a plotting program. */ if (show_stats_this_generation == TRUE){ if (output_files[SHOW_POPULATIONS].enabled == TRUE){ FILE *fp = output_files[SHOW_POPULATIONS].fp; fprintf(fp, "The population at generation %d was:\n", context.generation); print_population(fp, NO_SORT, unique, top_ranking, &context); putc('\n', fp); } if (output_files[SHOW_POPULATIONS_SORTED_BY_G].enabled == TRUE){ FILE *fp = output_files[SHOW_POPULATIONS_SORTED_BY_G].fp; fprintf(fp, "The population (sorted by genome) at generation %d was:\n", context.generation); print_population(fp, BY_GENOME_THEN_BY_FITNESS, unique, top_ranking, &context); putc('\n', fp); } if (output_files[SHOW_POPULATIONS_SORTED_BY_F].enabled == TRUE){ FILE *fp = output_files[SHOW_POPULATIONS_SORTED_BY_F].fp; fprintf(fp, "The population (sorted by fitness) at generation %d was:\n", context.generation); print_population(fp, BY_FITNESS_THEN_BY_GENOME, unique, top_ranking, &context); putc('\n', fp); } #if defined(APP_PRINTS_STATS_EACH_GENERATION) app_generation(output_files[APP_OUTPUT].fp, &context); #endif } } if (max_possible_fitness_found == TRUE){ break; } /* * Check to see if we have been interrupted. If so, clean up, dump state and exit. */ if (catch_interrupts == TRUE && interrupt_flag && context.generation < context.ngenerations){ dump_state(interrupt_fp, interrupt_file, &context); Fflush(interrupt_fp); Fclose(interrupt_fp); close_output_files(); exit(1); } /* * If this wasn't the final generation, we'd better arrange for another one... * Do all the usual GA machinations to prepare a new population. */ if (context.generation != context.ngenerations){ INT nselected; /* * Choose those nsurvivors individuals that will survive intact into * the next generation. The function fills in the selection array. * If we are being elitist, we ask for one less and then put in the * best. If elitist is TRUE, we are guaranteed that nsurvivors is at * least one. */ if (context.elitist == TRUE){ if (nsurvivors - 1 > (INT) 0){ (VOID) (*survivors)(selections, nsurvivors - 1, &context); } selections[nsurvivors - 1] = context.best_individual_of_generation; } else { if (nsurvivors > (INT) 0){ (VOID) (*survivors)(selections, nsurvivors, &context); } } /* * Copy these nsurvivors survivors into the end of pop2. Putting * them at the end makes it easier for the application when (if) * it is doing crossover, then it can put them at the beginning. */ for (i = 0; i < nsurvivors; i++){ copy_individual(&pop2[nreproducers + i], &context.population[selections[i]], &context); } #if ! defined(APP_HAS_CONSTANT_POPULATION_SIZE) /* * Recalculate the generation gap if the population * has changed size. */ if (context.population_size != last_population_size){ last_population_size = context.population_size; nreproducers = (INT) (context.generation_gap_prob * context.population_size); nsurvivors = context.population_size - nreproducers; } /* * Make more room for selections if the number of selections wanted * has changed. */ if (last_nselections != context.nselections){ free(selections); selections = (INT *) Malloc(context.nselections * sizeof(INT)); last_nselections = context.nselections; } #endif /* * Call the selection function to choose individuals that will be * used in the recombination and mutation functions. These are the * genetic grist for the GA to chew on. This returns the number it * actually selected for reproducing. This cannot be more than the * population size. The function fills in the selection array. */ (VOID) (*selection)(selections, &nselected, &context); /* * Now cross them over, choosing from context->population and * moving into pop2. There are nselected to choose from (in * selections), and nreproducers must be produced (into pop2). * * This is the point at which an application with a non-constant * population size is most likely to change the size of the * population. We'll never know it (if they do things correctly). * It needs to change the population array in context and the * population_size variable in context. */ (VOID) (*crossover)(selections, nselected, nreproducers, pop2, &context); /* * Swap pointers around so that context.population has the new population * and context.former_population has the last population. */ context.former_population = context.population; context.population = pop2; pop2 = context.former_population; /* * The newly crossed over individuals are now at the start of * context.population. We can mutate them in place. The evaluated * flag for each individual is updated in this function if the * individual actually changes. */ time_to_next_mutation = (INT) ((*mutate)(nreproducers, &context, time_to_next_mutation)); } } /* * The run is over. Context.population contains the final population, and it has been evaluated. */ /* * If they wanted a dump of the final population, give it to them. */ if (output_files[DUMP_FINAL].enabled == TRUE){ dump_state(output_files[DUMP_FINAL].fp, output_files[DUMP_FINAL].filename, &context); } /* Print the final population if they asked for it. */ if (output_files[SHOW_FINAL].enabled == TRUE){ /* Print the final population. */ fprintf(output_files[SHOW_FINAL].fp, "\nThe final population was:\n"); print_population(output_files[SHOW_FINAL].fp, BY_FITNESS_THEN_BY_GENOME, unique, 0, &context); } /* * Print the statistics we have gathered. Have we gathered any statistics? */ if (output_files[SHOW_BEST_OF_RUN].enabled == TRUE){ print_run_stats(output_files[MAIN_OUTPUT].fp, max_possible_fitness, max_possible_fitness_found, &context); } #if defined(APP_HASHING) /* * Show what the hash table looks like. * We have a choice here - should we reinitialize the hash table before * the next run? From an efficiency point of view the answer is surely * no. But it might be confusing for them to look at the hash table next * time round. Oh well. Perhaps make this an option in new_app (yet another * #define... */ if (output_files[SHOW_HASHING_VERBOSE].enabled == TRUE){ hash_stats(output_files[SHOW_HASHING_VERBOSE].fp, context.ht, TRUE); } else if (output_files[SHOW_HASHING].enabled == TRUE){ hash_stats(output_files[SHOW_HASHING].fp, context.ht, FALSE); } #endif #if defined(APP_PRINTS_STATS_AT_END) /* * Give the application a chance to print out its own end of run statistics. */ app_end_of_run(output_files[APP_OUTPUT].fp, &context); #endif } /* All the runs are finished. */ /* * Show how we were invoked - this makes for easy mouse cutting and repeating etc. */ fprintf(output_files[MAIN_OUTPUT].fp, "Program invoked as: "); for (i = 0; i < argc; i++){ fprintf(output_files[MAIN_OUTPUT].fp, "%s ", argv[i]); } putc('\n', output_files[MAIN_OUTPUT].fp); fprintf(output_files[MAIN_OUTPUT].fp, "Population size = %d, Number of generations = %d\n", context.population_size, context.ngenerations); fprintf(output_files[MAIN_OUTPUT].fp, "Random seed = %ld\n\n", seed); print_final_stats(output_files[MAIN_OUTPUT].fp, &context); #if defined(APP_PRINTS_STATS_AT_END) /* * Give the application a chance to print out its own statistics. */ app_end(output_files[APP_OUTPUT].fp, &context); #endif /* * Clean up & go home. */ close_output_files(); if (catch_interrupts == TRUE){ Fclose(interrupt_fp); Unlink(interrupt_file); } return 0; } /* * Read a population from the file 'file' (which has been opened for * us and is readable from 'fp'. The population should be put into * the population in the provided context. */ INT read_population(fp, file, context) FILE *fp; STRING file; CONTEXT *context; { INT line_num = 0; while (line_num < context->population_size && read_individual(&context->population[line_num], fp, file, line_num + 1, context)){ line_num++; } if (line_num == context->population_size && getc(fp) != EOF){ extern INT sleep(); extern INT getpid(); /* Just give them a warning, this may have been intended. */ sysdie = FALSE; error("%cWARNING! '%s' contains more than %d (the population size) genomes.", BEEP, file, context->population_size); error("Use the command 'kill %d' to kill this process if this is an error.", getpid()); sysdie = TRUE; sleep(2); return context->population_size; } return line_num; } STRING prepare_for_interrupts(fp) FILE **fp; { STRING name; register INT count = 0; register INT limit = 1000; extern INT access(); name = Malloc(strlen(myname) + strlen(INT_STRING) + DIGITS + 1); do { count++; sprintf(name, "%s%s%0*d", myname, INT_STRING, DIGITS, count); if (count == limit){ error("could not create temporary file name %s%s(numeric suffix).", myname, INT_STRING); } } while (access(name, F_OK) == 0); /* * It's a bit sad that we have to open the file now, since it creates a file * that we will not use unless we are interrupted. So we have to unlink it when * we're done (if we exit normally). It may create confusion for the poor * user. But this is for the best I think, seeing as we don't want to screw * around doing too many system calls once the interrupt has actually been * taken. We can't find the filename now and open it later either, since in * the meantime (which may be hours or even days), who knows what might have * happened. */ *fp = Fopen(name, "w"); if (signal(SIGINT, flag_interrupt) == BADSIG){ error("could not arrange to catch SIGINT"); } if (signal(SIGHUP, flag_interrupt) == BADSIG){ error("could not arrange to catch SIGHUP"); } return name; } VOID flag_interrupt(n) INT n; { fprintf(stderr, "Signal %d received, waiting for interruptable state.\n", n); interrupt_flag = n; return; } VOID dump_state(fp, file, context) FILE *fp; STRING file; CONTEXT *context; { register INT parameter; register INT nparameters = sizeof(main_dump_parameters) / sizeof(DUMP_PARAMETER); fprintf(stderr, "Now dumping state to '%s'\n", file); for (parameter = 0; parameter < nparameters; parameter++){ register INT type = main_dump_parameters[parameter].type; register VOID *address = main_dump_parameters[parameter].address; register STRING name = main_dump_parameters[parameter].name; switch(type){ case DUMP_BOOLEAN:{ fprintf(fp, "%s%s%s\n", name, DUMP_SEPARATOR, *((BOOLEAN *)address) == TRUE ? "TRUE" : "FALSE"); break; } case DUMP_SHORT:{ fprintf(fp, "%s%s%d\n", name, DUMP_SEPARATOR, *((SHORT *)address)); break; } case DUMP_INT:{ fprintf(fp, "%s%s%d\n", name, DUMP_SEPARATOR, *((INT *)address)); break; } case DUMP_CHAR:{ fprintf(fp, "%s%s%c\n", name, DUMP_SEPARATOR, *((CHAR *)address)); break; } case DUMP_LONG:{ fprintf(fp, "%s%s%ld\n", name, DUMP_SEPARATOR, *((LONG *)address)); break; } case DUMP_FLOAT:{ fprintf(fp, "%s%s%f\n", name, DUMP_SEPARATOR, *((FLOAT *)address)); break; } case DUMP_DOUBLE:{ fprintf(fp, "%s%s%f\n", name, DUMP_SEPARATOR, *((DOUBLE *)address)); break; } case DUMP_FITNESS:{ fprintf(fp, "%s%s", name, DUMP_SEPARATOR); print_fitness(fp, 0, *((FITNESS *)address)); putc('\n', fp); break; } case DUMP_FILE:{ if ((*((OUTPUT_FILE *)address)).enabled == TRUE){ if ((*((OUTPUT_FILE *)address)).fp == stdout){ fprintf(fp, "%s%s%s\n", name, DUMP_SEPARATOR, STDOUT_INDICATOR); } else { fprintf(fp, "%s%s%s\n", name, DUMP_SEPARATOR, (*((OUTPUT_FILE *)address)).filename); } } else { fprintf(fp, "%s%s\n", name, DUMP_SEPARATOR); } break; } case DUMP_STRING:{ fprintf(fp, "%s%s%s\n", name, DUMP_SEPARATOR, *((STRING *)address)); break; } case DUMP_INDIVIDUAL:{ dump_individual(fp, (INDIVIDUAL *)address, context); print_fitness(fp, 0, ((INDIVIDUAL *)address)->fitness); putc('\n', fp); break; } case DUMP_HEADER:{ fprintf(fp, "%s\n", name); break; } case DUMP_DUMP_FUNCTION:{ ((void (*)()) address)(fp); break; } case DUMP_RESTORE_FUNCTION:{ /* We are not restoring, don't call it. */ break; } case DUMP_POPULATION:{ dump_population(fp, context); break; } default:{ error("unrecognized DUMP type (%d) in dump_state().", type); } } } fprintf(stderr, "Dump complete.\n"); return; } VOID dump_population(fp, context) FILE *fp; CONTEXT *context; { register INT i; /* Print the population, following each individual with its fitness. */ for (i = 0; i < context->population_size; i++){ dump_individual(fp, &context->population[i], context); print_fitness(fp, 0, context->population[i].fitness); putc('\n', fp); } return; } VOID restore_state(file, context) STRING file; CONTEXT *context; { FILE *fp = Fopen(file, "r"); CHAR line[BUF_SZ]; INT line_num = 1; STRING newline; register INT parameter; register INT nparameters = sizeof(main_dump_parameters) / sizeof(DUMP_PARAMETER); register INT sep_len = strlen(DUMP_SEPARATOR); for (parameter = 0; parameter < nparameters; parameter++){ register INT type = main_dump_parameters[parameter].type; register VOID *address = main_dump_parameters[parameter].address; register STRING name = main_dump_parameters[parameter].name; register INT start_pos = strlen(name) + sep_len; if (!(fgets(line, BUF_SZ, fp))){ error("fgets fails on line %d in restore_state().", line_num); } else { line_num++; } switch(type){ case DUMP_BOOLEAN:{ if (!strncmp(line + start_pos, "TRUE", 4)){ *((BOOLEAN *)address) = TRUE; } else { *((BOOLEAN *)address) = FALSE; } break; } case DUMP_SHORT:{ *((SHORT *)address) = (SHORT) atoi(line + start_pos); break; } case DUMP_INT:{ *((INT *)address) = atoi(line + start_pos); break; } case DUMP_CHAR:{ *((CHAR *)address) = *(line + start_pos); break; } case DUMP_LONG:{ *((LONG *)address) = atol(line + start_pos); break; } case DUMP_FLOAT:{ *((FLOAT *)address) = (FLOAT) atof(line + start_pos); break; } case DUMP_DOUBLE:{ *((DOUBLE *)address) = atof(line + start_pos); break; } case DUMP_FITNESS:{ *((FITNESS *)address) = (FITNESS) STR_TO_FITNESS(line + start_pos); break; } case DUMP_FILE:{ if (line[start_pos] == '\n'){ /* The file was not open. */ (*((OUTPUT_FILE *)address)).enabled = FALSE; } else { char *nl = index(line + start_pos, '\n'); if (!nl){ error("could not find newline on line %d of restore file '%s'.", line_num, file); } *nl = '\0'; (*((OUTPUT_FILE *)address)).enabled = TRUE; if (!strcmp(line + start_pos, STDOUT_INDICATOR)){ (*((OUTPUT_FILE *)address)).fp = stdout; } else { (*((OUTPUT_FILE *)address)).fp = fopen(line + start_pos, "a"); if (!(*((OUTPUT_FILE *)address)).fp){ error("the run cannot be resumed as the file '%s' cannot be opened.", line + start_pos); } } } break; } case DUMP_STRING:{ char *nl = index(line + start_pos, '\n'); if (!nl){ error("could not find newline on line %d of restore file '%s'.", line_num, file); } *nl = '\0'; *((STRING *)address) = strdup(line + start_pos); break; } case DUMP_INDIVIDUAL:{ #if defined(APP_DUMPS_INDIVIDUALS) app_restore_individual(fp, (INDIVIDUAL *)address, context); #else CHAR line[BUF_SZ]; read_individual((INDIVIDUAL *)address, fp, file, 0, context); if (!(fgets(line, BUF_SZ, fp))){ error("fgets fails in restore_state()."); } ((INDIVIDUAL *)address)->fitness = STR_TO_FITNESS(line); ((INDIVIDUAL *)address)->evaluated = TRUE; #endif break; } case DUMP_HEADER:{ /* Ignore - it was just an information line. */ break; } case DUMP_DUMP_FUNCTION:{ /* We are not dumping, don't call it. */ break; } case DUMP_RESTORE_FUNCTION:{ ((void (*)()) address)(); break; } case DUMP_POPULATION:{ read_dump_population(fp, file, context); break; } default:{ error("unrecognized DUMP type (%d) in dump_state().", type); } } } Fclose(fp); return; } VOID read_dump_population(fp, file, context) FILE *fp; STRING file; CONTEXT *context; { CHAR line[BUF_SZ]; register INT i; for (i = 0; i < context->population_size; i++){ #if defined(APP_DUMPS_INDIVIDUALS) app_restore_individual(fp, &context->population[i], context); #else read_individual(&context->population[i], fp, file, 0, context); #endif if (!(fgets(line, BUF_SZ, fp))){ error("fgets fails in read_dump_population()."); } context->population[i].fitness = STR_TO_FITNESS(line); context->population[i].evaluated = TRUE; } return; } STRING set_name(s) STRING s; { STRING slash = strrchr(s, '/'); return slash ? slash + 1 : s; } #if defined(ultrix) || defined(NeXT) STRING strdup(s) STRING s; { return strcpy(Malloc(strlen(s) + 1), s); } #endif VOID copy_individual(a, b, context) INDIVIDUAL *a; INDIVIDUAL *b; CONTEXT *context; { #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) /* Constant length. */ memcpy(a->genome, b->genome, context->genome_size); #else /* Non constant length. */ if (a->max_genome_size >= b->genome_size){ #if defined(APP_INDIVIDUALS_ARE_STRINGS) /* Overwrite the old memory. Include the NUL at the end of b. */ memcpy(a->genome, b->genome, b->genome_size + 1); #else /* Overwrite the old memory. */ memcpy(a->genome, b->genome, b->genome_size); #endif a->genome_size = b->genome_size; } else { free(a->genome); a->genome = (INDIVIDUAL) Malloc(b->genome_size * sizeof(CHAR) + 1); a->max_genome_size = b->genome_size; #if defined(APP_INDIVIDUALS_ARE_STRINGS) memcpy(a->genome, b->genome, b->genome_size + 1); #else memcpy(a->genome, b->genome, b->genome_size); #endif } #endif a->evaluated = b->evaluated; #if defined(SCALING) a->unscaled_fitness = b->unscaled_fitness; #endif a->fitness = b->fitness; return; } #if ! defined(APP_DUMPS_INDIVIDUALS) || ! defined(APP_READS_INIT_INDIVIDUALS) INT read_individual(individual, fp, file, line_num, context) INDIVIDUAL *individual; FILE *fp; STRING file; INT line_num; CONTEXT *context; { #ifndef ultrix extern INT fread(); #endif INT c; #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) static STRING line = (STRING) 0; if (line == (STRING) 0){ /* This will never be freed, but there will only be one of them. */ line = Malloc(context->genome_size * sizeof(CHAR) + 1); } if (fread(line, sizeof(CHAR), context->genome_size + 1, fp) != context->genome_size){ return 0; } if (line[context->genome_size] != '\n'){ error("line %d of file '%s' has a genome which appears to be too long (> %d characters)", line_num, file, context->genome_size); } memcpy((STRING) (individual->genome), line, context->genome_size); free(line); #else /* * We don't know the size of individuals. * We have to read them in as a pair, length and string. We look * for the length on a line and then the genome on the next * line(s) (the genome size is specified by the length). */ CHAR length_line[BUF_SZ]; /* Excessive, but who cares? */ CHAR line[BUF_SZ]; INT length; if (!fgets(length_line, BUF_SZ, fp)){ return 0; } length = (INT) atoi(length_line); if (length < (INT) 0){ error("the length for genome number %d in '%s' is negative! (%d).", line_num, file, length); } if (length > BUF_SZ){ error("the length for genome number %d in '%s' is too large (%d > %d).", line_num, file, length, BUF_SZ); } if (fread(line, sizeof(CHAR), length, fp) != length){ error("read_individual() could not read %d chars for genome %d in '%s'.", length, line_num, file); } individual->genome_size = length; individual->max_genome_size = length; individual->genome = (INDIVIDUAL_TYPE) Malloc(length * sizeof(CHAR) + 1); individual->genome[length] = '\0'; memcpy((STRING) (individual->genome), line, length); #endif /* Now read the rest of the line. */ while ((c = getc(fp)) != '\n'){ ; } if (c != '\n'){ error("read_individual() finds line (%d) with no terminating newline in '%s'.", line_num, file); } return 1; } #endif #if ! defined(APP_DUMPS_INDIVIDUALS) VOID write_individual(fp, individual, context) FILE *fp; INDIVIDUAL *individual; CONTEXT *context; { #ifndef ultrix extern INT fwrite(); #endif #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) INT size = context->genome_size; #else INT size = individual->genome_size; fprintf(fp, "%d\n", size); #endif if (fwrite(individual->genome, sizeof(CHAR), size, fp) != size){ error("fwrite() fails in write_individual()."); } putc('\n', fp); return; } #endif #if (defined(SCALING) && defined(APP_HAS_ITS_OWN_SCALING_METHODS)) || \ defined(APP_HAS_ITS_OWN_SURVIVOR_METHODS) || defined(APP_HAS_ITS_OWN_CROSSOVER_METHODS) || \ defined(APP_HAS_ITS_OWN_SELECTION_METHODS) || defined(APP_HAS_ITS_OWN_MUTATION_METHODS) FUNCTION * merge_functions(funcs1, count1, funcs2, count2) FUNCTION *funcs1; INT count1; FUNCTION *funcs2; INT count2; { /* * It's a funny thing, the names of the functions from the main * program and the application cannot be the same, or this code * would not be running, i.e., the compilation would fail! */ register INT i; register INT total = count1 + count2; register FUNCTION *merge; merge = (FUNCTION *) Malloc(total * sizeof(FUNCTION)); for (i = 0; i < count1; i++){ merge[i].name = funcs1[i].name; merge[i].initialization_function = funcs1[i].initialization_function; merge[i].function = funcs1[i].function; merge[i].explanation = funcs1[i].explanation; } for (; i < total; i++){ register INT pos = i - count1; merge[i].name = funcs2[pos].name; merge[i].initialization_function = funcs2[pos].initialization_function; merge[i].function = funcs2[pos].function; merge[i].explanation = funcs2[pos].explanation; } return merge; } #endif INT find_function(name, functions, nfunctions) STRING name; FUNCTION *functions; INT nfunctions; { register INT i; register INT location = (INT) -1; register INT matches = (INT) 0; for (i = 0; i < nfunctions; i++){ if (!strcmp(functions[i].name, name)){ return i; } } for (i = 0; i < nfunctions; i++){ if (!strncmp(functions[i].name, name, strlen(name))){ matches++; location = i; } } if (matches <= (INT) 1){ return location; } fprintf(stderr, "%s: function abbreviation '%s' is ambiguous, it matches\n\n", myname, name); for (i = 0; i < nfunctions; i++){ if (!strncmp(functions[i].name, name, strlen(name))){ fprintf(stderr, "\t%s\n", functions[i].name); } } fprintf(stderr, "\n%s: please use the -help option if you are confused.\n", myname); exit(1); } VOID set_functions() { register INT match; #if defined(SCALING) INT nscalers = sizeof(main_scaling_functions) / sizeof(FUNCTION); #endif INT nsurvivors = sizeof(main_survivor_functions) / sizeof(FUNCTION); INT nselectors = sizeof(main_selection_functions) / sizeof(FUNCTION); INT ncrossovers = sizeof(main_crossover_functions) / sizeof(FUNCTION); INT nmutators = sizeof(main_mutation_functions) / sizeof(FUNCTION); #if defined(SCALING) INT n_app_scalers = (INT) 0; #endif INT n_app_survivors = (INT) 0; INT n_app_selectors = (INT) 0; INT n_app_crossovers = (INT) 0; INT n_app_mutators = (INT) 0; #if defined(SCALING) #if defined(APP_HAS_ITS_OWN_SCALING_METHODS) while (app_scaling_functions[n_app_scalers].name){ n_app_scalers++; } scaling_functions = merge_functions(main_scaling_functions, nscalers, app_scaling_functions, n_app_scalers); #endif #endif #if defined(APP_HAS_ITS_OWN_SURVIVOR_METHODS) while (app_survivor_functions[n_app_survivors].name){ n_app_survivors++; } survivor_functions = merge_functions(main_survivor_functions, nsurvivors, app_survivor_functions, n_app_survivors); #endif #if defined(APP_HAS_ITS_OWN_SELECTION_METHODS) while (app_selection_functions[n_app_selectors].name){ n_app_selectors++; } selection_functions = merge_functions(main_selection_functions, nselectors, app_selection_functions, n_app_selectors); #endif #if defined(APP_HAS_ITS_OWN_CROSSOVER_METHODS) while (app_crossover_functions[n_app_crossovers].name){ n_app_crossovers++; } crossover_functions = merge_functions(main_crossover_functions, ncrossovers, app_crossover_functions, n_app_crossovers); #endif #if defined(APP_HAS_ITS_OWN_MUTATION_METHODS) while (app_mutation_functions[n_app_mutators].name){ n_app_mutators++; } mutation_functions = merge_functions(main_mutation_functions, nmutators, app_mutation_functions, n_app_mutators); #endif #if defined(SCALING) if (scaling_function_name == (STRING) 0){ scaling_function_name = DEFAULT_SCALING_FUNC; if ((match = find_function(scaling_function_name, scaling_functions, nscalers + n_app_scalers)) == -1){ error("could not find default scaling function '%s'!", DEFAULT_SCALING_FUNC); } } else { if ((match = find_function(scaling_function_name, scaling_functions, nscalers + n_app_scalers)) == -1){ sysdie = FALSE; error("could not find scaling function '%s'!", scaling_function_name); sysdie = TRUE; error("try the -functions option if you are confused."); } } scaling = scaling_functions[match].function; scaling_init = scaling_functions[match].initialization_function; #endif if (survivors_function_name == (STRING) 0){ survivors_function_name = DEFAULT_SURVIVOR_FUNC; if ((match = find_function(survivors_function_name, survivor_functions, nsurvivors + n_app_survivors)) == -1){ error("could not find default survivor function '%s'!", DEFAULT_SURVIVOR_FUNC); } } else { if ((match = find_function(survivors_function_name, survivor_functions, nsurvivors + n_app_survivors)) == -1){ sysdie = FALSE; error("could not find survivor function '%s'!", survivors_function_name); sysdie = TRUE; error("try the -functions option if you are confused."); } } survivors = survivor_functions[match].function; survivors_init = survivor_functions[match].initialization_function; if (selection_function_name == (STRING) 0){ selection_function_name = DEFAULT_SELECTION_FUNC; if ((match = find_function(selection_function_name, selection_functions, nselectors + n_app_selectors)) == -1){ error("could not find default selection function '%s'!", DEFAULT_SELECTION_FUNC); } } else { if ((match = find_function(selection_function_name, selection_functions, nselectors + n_app_selectors)) == -1){ sysdie = FALSE; error("could not find selection function '%s'!", selection_function_name); sysdie = TRUE; error("try the -functions option if you are confused."); } } selection = selection_functions[match].function; selection_init = selection_functions[match].initialization_function; if (crossover_function_name == (STRING) 0){ crossover_function_name = DEFAULT_CROSSOVER_FUNC; if ((match = find_function(crossover_function_name, crossover_functions, ncrossovers + n_app_crossovers)) == -1){ error("could not find default crossover function '%s'!", DEFAULT_CROSSOVER_FUNC); } } else { if ((match = find_function(crossover_function_name, crossover_functions, ncrossovers + n_app_crossovers)) == -1){ sysdie = FALSE; error("could not find crossover function '%s'!", crossover_function_name); sysdie = TRUE; error("try the -functions option if you are confused."); } } crossover = crossover_functions[match].function; crossover_init = crossover_functions[match].initialization_function; if (mutation_function_name == (STRING) 0){ mutation_function_name = DEFAULT_MUTATION_FUNC; if ((match = find_function(mutation_function_name, mutation_functions, nmutators + n_app_mutators)) == -1){ error("could not find default mutation function '%s'!", DEFAULT_MUTATION_FUNC); } } else { if ((match = find_function(mutation_function_name, mutation_functions, nmutators + n_app_mutators)) == -1){ sysdie = FALSE; error("could not find mutation function '%s'!", mutation_function_name); sysdie = TRUE; error("try the -functions option if you are confused."); } } mutate = mutation_functions[match].function; mutate_init = mutation_functions[match].initialization_function; return; } VOID show_functions() { VOID print_functions(); FUNCTION *tmp; /* * Give help about available functions. * Print it to stdout so that the average person * will know how to stop it scrolling off the screen. */ register INT i; printf("\ \n\ Gassy (version %s) Functions.\n\ \n\ The following is a list of the functions that are built in to Gassy for\n\ various GA things. Both Gassy and application functions (if any) are\n\ shown with a short description of what each does. For more help on Gassy\n\ options, use the -usage option.\n\n", gassy_version); #if defined(SCALING) tmp = main_scaling_functions; if (tmp == (FUNCTION *) 0){ printf("GASSY SCALING FUNCTIONS: none\n\n"); } else { INT i = 0; printf("GASSY SCALING FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #if defined(APP_HAS_ITS_OWN_SCALING_METHODS) tmp = app_scaling_functions; if (tmp == (FUNCTION *) 0){ printf("APPLICATION SCALING FUNCTIONS: none\n\n"); } else { INT i = 0; printf("APPLICATION SCALING FUNCTIONS:\n\n") while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #endif #endif putc('\n', stdout); tmp = main_survivor_functions; if (tmp == (FUNCTION *) 0){ printf("GASSY SURVIVOR FUNCTIONS: none\n\n"); } else { INT i = 0; printf("GASSY SURVIVOR FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #if defined(APP_HAS_ITS_OWN_SURVIVOR_METHODS) tmp = app_survivor_functions; if (tmp == (FUNCTION *) 0){ printf("APPLICATION SURVIVOR FUNCTIONS: none\n\n"); } else { INT i = 0; printf("APPLICATION SURVIVOR FUNCTIONS:\n\n") while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #endif putc('\n', stdout); tmp = main_crossover_functions; if (tmp == (FUNCTION *) 0){ printf("GASSY CROSSOVER FUNCTIONS: none\n\n"); } else { INT i = 0; printf("GASSY CROSSOVER FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #if defined(APP_HAS_ITS_OWN_CROSSOVER_METHODS) tmp = app_crossover_functions; if (tmp == (FUNCTION *) 0){ printf("APPLICATION CROSSOVER FUNCTIONS: none\n\n"); } else { INT i = 0; printf("APPLICATION CROSSOVER FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #endif putc('\n', stdout); tmp = main_selection_functions; if (tmp == (FUNCTION *) 0){ printf("GASSY SELECTION FUNCTIONS: none\n\n"); } else { INT i = 0; printf("GASSY SELECTION FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #if defined(APP_HAS_ITS_OWN_SELECTION_METHODS) tmp = app_selection_functions; if (tmp == (FUNCTION *) 0){ printf("APPLICATION SELECTION FUNCTIONS: none\n\n"); } else { INT i = 0; printf("APPLICATION SELECTION FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #endif putc('\n', stdout); tmp = main_mutation_functions; if (tmp == (FUNCTION *) 0){ printf("GASSY MUTATION FUNCTIONS: none\n\n"); } else { INT i = 0; printf("GASSY MUTATION FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #if defined(APP_HAS_ITS_OWN_MUTATION_METHODS) tmp = app_mutation_functions; if (tmp == (FUNCTION *) 0){ printf("APPLICATION MUTATION FUNCTIONS: none\n\n"); } else { INT i = 0; printf("APPLICATION MUTATION FUNCTIONS:\n\n"); while (tmp[i].name != (STRING) 0){ printf("Name : %s\n", tmp[i].name); printf("Description : \n%s\n\n", tmp[i].explanation); i++; } } #endif exit(0); } #if defined(APP_FITNESS_IS_A_CHAR) STRING char_to_string(c) char c; { static STRING str = (STRING) 0; if (str == (STRING) 0){ str = Malloc(2 * sizeof(CHAR)); str[1] = '\0'; } str[0] = c; return str; } #endif VOID version() { printf("Gassy version %s\n", gassy_version); exit(0); } VOID init_output_files() { register INT i; for (i = 0; i < NUM_OUTPUT_FILES; i++){ output_files[i].enabled = FALSE; output_files[i].filename = NULL; output_files[i].fp = NULL; } return; } VOID open_output_files() { /* * Open all the optional files. We watch for duplicated file names * and if we find them, just assign the same fp. */ register INT i; STRING output_file_open_mask = (overwrite == TRUE) ? "w" : "a"; for (i = 0; i < NUM_OUTPUT_FILES; i++){ if (output_files[i].enabled == TRUE){ show_something = TRUE; if (output_files[i].filename){ register INT j; for (j = i - 1; j >= 0; j--){ if (output_files[j].enabled == TRUE && !strcmp(output_files[j].filename, output_files[i].filename)){ /* Same file. */ output_files[i].fp = output_files[j].fp; break; } } /* If we didn't find it, do the open. */ if (!output_files[i].fp){ output_files[i].fp = Fopen(output_files[i].filename, output_file_open_mask); } } else { output_files[i].filename = STDOUT_INDICATOR; output_files[i].fp = stdout; } } } return; } VOID close_output_files() { /* * Close all the optional files. We watch for duplicated file names. */ register INT i; for (i = 0; i < NUM_OUTPUT_FILES; i++){ if (output_files[i].enabled == TRUE && output_files[i].fp != stdout){ register INT j; Fflush(output_files[i].fp); Fclose(output_files[i].fp); /* Check for other instances of this file and mark them as done. */ for (j = i + 1; j < NUM_OUTPUT_FILES; j++){ if (output_files[j].enabled == TRUE && !strcmp(output_files[j].filename, output_files[i].filename)){ /* Same file. */ output_files[j].enabled = FALSE; } } } } return; } VOID check_main_options() { /* * Check to see that we got sane option combinations. * Set sysdie to 0 so error() wont exit. */ sysdie = FALSE; #if defined(APP_HASHING) if (output_files[SHOW_HASHING].enabled == TRUE && context.hashing == FALSE){ error("you cannot use -show_hashing unless hashing is turned on with -hashing."); error("This program will give a usage message if invoked with -usage."); exit(1); } #else /* Is this right? should hashing be ON by default? see new.c */ if (output_files[SHOW_HASHING].enabled == TRUE || context.hashing == FALSE){ error("you have specified some hashing related option, but the program"); error("was not compiled with hashing enabled. Put \"#define APP_HASHING\""); error("in your application.h file and completely remake the program."); exit(1); } #endif if (show_stop < show_start){ error("you have specified a show_stop generation of %d,", show_stop); error("which is earlier than the show_start generation of %d.", show_start); exit(1); } sysdie = TRUE; return; } VOID read_initial_population() { /* Read in the initial population, or else create an initial random population. */ register INT i; if (initial_population_file != (STRING) 0){ FILE *fp = Fopen(initial_population_file, "r"); INT nread = read_population(fp, initial_population_file, &context); fprintf(stdout, "Read %d strings from '%s'\n", nread, initial_population_file); /* Create some random strings to fill the population, if necessary. */ #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) && defined(APP_INDIVIDUALS_ARE_STRINGS) for (i = nread; i < context.population_size; i++){ create_individual_from_alleles(&context.population[i], &context); } #else app_create_random_individuals(nread, context); #endif Fclose(fp); free(initial_population_file); } else { #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) && defined(APP_INDIVIDUALS_ARE_STRINGS) for (i = 0; i < context.population_size; i++){ create_individual_from_alleles(&context.population[i], &context); } #else app_create_random_individuals(0, &context); #endif } return; } VOID allocate_memory() { /* * Get memory for the run. This includes the populations, * the best individual and the alleles. This is all the memory * that restore will need to create in order to be able to * read in this stuff. There is no need to create space for * selections or the hash table as restore does not fill them * in. */ register INT i; /* * This will create and initialize a population. * Space will be allocated in the case that the population * members have constant size. If this is not so, we leave it * to the application (see below). */ pop1 = new_population(&context); pop2 = new_population(&context); context.population = pop1; /* * Make space for the run statistics. We make an array of RUN_STATS * structs and then fill in some of their details - doing what we * can given the type of application we're dealing with. We allocate one * more than we need - the first is unused. This makes indexing with * context.run simpler and faster. I know it's horrible, but it is * faster than constantly subtracting one from and index and simpler * than just making the index run up from 0 as then you are constantly * adding one when you want to print the run number or tell the * application what run number it is. So there. */ context.stats = (RUN_STATS *) Malloc((context.nruns + 1) * sizeof(RUN_STATS)); for (i = 1; i <= context.nruns; i++){ #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) #if defined(APP_INDIVIDUALS_ARE_STRINGS) /* They are constant sized strings. */ context.stats[i].best_individual.genome = (INDIVIDUAL_TYPE) Malloc(context.genome_size + 1); context.stats[i].best_individual.genome[context.genome_size] = '\0'; #else /* They are constant sized somethings. */ context.stats[i].best_individual.genome = (INDIVIDUAL_TYPE) Malloc(context.genome_size); #endif #else /* * They are not constant sized. We set it to 0 since there's * no point guessing. Things will be handled corectly when copy_individual * is called to fill in some values for it. */ context.stats[i].best_individual.genome = (INDIVIDUAL_TYPE) 0; context.stats[i].best_individual.genome_size = (INT) 0; context.stats[i].best_individual.max_genome_size = (INT) 0; #endif context.stats[i].best_individual.evaluated = FALSE; context.stats[i].best_individual.fitness = WORST_FITNESS; #if defined(SCALING) context.stats[i].best_individual.unscaled_fitness = WORST_FITNESS; #endif } #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) && defined(APP_INDIVIDUALS_ARE_STRINGS) if (context.alleles == (LOCUS *) 0){ context.alleles = (LOCUS *) Malloc(context.genome_size * sizeof(LOCUS)); } #endif return; } /* * Functions to catch people using other random number generators. * We have to do this because if people start getting random numbers * from elsewhere, we wont be able to repeat a run or restart a run. * This may seem a bit draconian, but it will at least work. * * The return()s here are window dressing to keep gcc -Wall quiet. In * fact, error() doesn't return. */ DOUBLE drand48() { error("drand48() should not be used - use knuth_random instead!"); return (DOUBLE) -1; } VOID srand48() { error("srand48() should not be used - see the README file for help."); return; } INT rand() { error("drand() should not be used - use knuth_random instead!"); return (INT) -1; } #ifdef ultrix VOID srand() { error("srand() should not be used - see the README file for help."); } #else INT srand() { error("srand() should not be used - see the README file for help."); return (INT) -1; } #endif LONG random() { error("random() should not be used - use uniform() instead!"); return (LONG) -1; } VOID srandom() { error("srandom() should not be used - see the README file for help."); return; } STRING initstate(seed, state, n) unsigned seed; STRING state; INT n; { error("initstate() should not be used - see the README file for help."); return (STRING) 0; } STRING setstate(state) STRING state; { error("setstate() should not be used - see the README file for help."); return (STRING) 0; }