/* * File: NetworkEvolver.java * Author: Justin Basilico * Course: PO CS 152: Neural Networks * Assignment: Final Project * Updated: 2001.12.19 * Created: 2001.12.03 * * Description: * This file contains the NetworkEvolver abstract class, which implements the * main method for this project. It is where the genetic algorithm is * implemented and it handles the evolving of sigma-pi networks to act as * simulator networks. * * Copyright: Justin Basilico (2001). */ import java.util.Arrays; import java.util.Random; /** * NetworkEvolver abstract class * * This abstract class implements the main method for this project along with * most of the genetic algorithm used to evolve the networks. The basic idea * is that the user specifies the experiment number to run (1, 2, or 3, see * below or the write-up for what these mean), the parameters for the * genetic algorithm, and the file that contains dataset of networks to * simulate. The program then tries to evolve a sigma-pi network that * simulates the simple feed-forward networks in the given dataset. It * evaluates the fitness of each chromosome that corresponds to a sigma-pi * network by calculating its average mean squared error on all of the * examples in the dataset. It does the elite selection for the top 5% of the * population and rank selection for the rest. It does crossover and mutation * for the given rates. * * More specifically, it uses the dataset of NetworkInput that must be in a * file of the double dataset format that can be read in by the DatasetReader * class. It then uses the experiment number to create the proper * SigmaPiNetwork initial architecture with the proper inputs and outputs that * uses only LinearActivation functions for the activation of the network. * It then creates the SigmaPiFitness, which is the FitnessFunction that will * use this SigmaPiNetwork for evaluating Chromosomes. Next, it calculates the * size of the SigmaPiChromosomes needed, and then creates the Chromosomes * themselves. These are BinaryChromosomes so they are just a string of bits * that encode the connectivity of the SigmaPiNetwork. It creates all of these * Chromosomes and their corresponding Genomes (which just store the * Chromosome and the fitness). Once the initial population is created, the * program runs the genetic algorithm, which corresponds to first calculating * the fitness of each SigmaPiChromosome using the SigmaPiFitness. To create * the next generation, first the top 5% most fit Chromosomes are * automatically copied over unchanged. For the rest of the population, two * SigmaPiChromosomes are chosen with probability according to their rank (a * rank selection). They are crossed over on a bit level with the given * crossover rate, which is the chance that one bit will swap between the two * Chromosomes. Once this is done, one of the two crossed-over Chromosomes is * selected and put into the new population. After all of the population is * complete, the non-elite that were selected by the rank selection are * mutated with the given mutation rate, which is the chance per bit that the * bit will flip value. This corresponds to removing a connection in the * connectivity of the network that is there or adding one where there is * none. After each generation, the fitness of the best Chromosome for that * generation and the average fitness are output to System.out. Also, every * 100 epochs the architecture of the best SigmaPiChromosome loaded into a * SigmaPiNetwork is output as well. In addition, if it is specified, this * best Chromosome seen so far will be put into a SigmaPiNetwork and saved * into a specified file every given number of generations. Once the algorithm * has run for the specified number of generations, it outputs the best * Chromosome it has seen, loaded into a SigmaPiNetwork and then if a file is * specified, it saves that SigmaPiNetwork into a file. * * The dataset file for this program is assumed to have been created by the * DatasetCreator program. When the SigmaPiNetwork is saved into a file, it * can be tested against a dataset using the NetworkTester program. * * * To run this program, just do: * java NetworkEvolver * where the parameters as are follows: * * -e * This parameter specifies which experiment (1, 2, or 3) should be run. The * first experiment evolves a sigma-pi network that simulates a 2-1 network. * The second experiment evolves a simulator for a 2-2 network. The third * evolves one for a 2-2-1 network. The experiment number must be specified on * the command-line and it must be either 1,2, or 3. Also, make sure that * the experiment agrees with the dataset being used. * * -p * This parameter specifies the positive integer population size to use for * the genetic algorithm. This is the number of chromosomes in the population * that is being evolved. The default is 200. It must be a positive integer. * * -g * This parameter specifies the positive integer number of generations to run * the genetic algorithm for. The default is 100. Again, it must be a positive * integer. * * -m * This parameter specifies the value for the mutation rate per bit in the * chromosome. The mutation rate must be between 0.0 and 1.0. It is really * just the percent chance any one bit will flip at each generation. The * default value is 0.01. It is suggested that the rate be very low. * * -c * This parameter specifies the value for the crossover rate per bit in the * chromosome. The crossover rate must also be between 0.0 and 1.0. The * crossover between two chromosomes is done by just basically using a bit * mask, and this is the probability that any one bit will be crossed over. * The default is 0.1. * * -d * This parameter specifies the file that contains the dataset that the * program is going to use to test the evolved networks in the fitness * function. This parameter must be specified and the given file must be a * proper file that was created by the DatasetCreator program (or something * similar) that creates valid networks to be simulated. It is assumed that * the proper dataset is given with the experiment number. If it is not, * strange results might occur. Also, the dataset file should contain some * data. * * -r * This parameter specifies the seed value for the random number generator, * which is a long value. If no random number seed is specified, then the * value returned by System.currentTimeMillis() is used. * * -n * This parameter specifies where the SigmaPiNetwork corresponding to the * best member of the population will be stored at the end of the algorithm * and possibly every specified number of epochs. If no file is specified, * then it will not be saved. * * -s * This parameters specifies how many epochs should pass between each save of * the best SigmaPiNetwork that has been created yet, using the given network * save file name. It must be a positive integer The default is 100. * * -h * This parameter just makes the program display a usage message. * * The program runs for the given number of generations on the given * population. Every generation it prints out the fitness value of the best * Genome in the population along with the average fitness. Every 100 epochs * it will print out the fitness value, average mean squared error, and * the full network for the chromosome with the best fitness seen so far. * Also, if it is specified, it will save the best network seen so far into * the given file after every given number of generations (epochs). After * the algorithm has finished, it will print out the best seen once more and * then again save the best network (if a file name is given). * * The fitness of each chromosome is determined by the average mean squared * error of the SigmaPiNetwork created by that SigmaPiChromosome over the * dataset that the program is run for. Thus, a smaller fitness value means * a better network. * * @author Justin Basilico * @version 2001.12.19 * @see DatasetCreator * @see NetworkTester * @see SigmaPiNetwork * @see SigmaPiChromosome * @see SigmaPiFitness * @see FitnessFunction * @see LinearFunction * @see NetworkInput * @see DatasetReader */ public abstract class NetworkEvolver extends Object { /** * run * * This method runs the genetic algorithm using all of the needed * parameters. More specifically, it creates the proper SigmaPiNetwork * for the experiment number, creates the SigmaPiFitness which is the * FitnessFunction for evaluating the networks. It then creates the * initial population of Genomes that contain SigmaPiChromosomes. Once * this is done, it applies the genetic algorithm to each generation, * using nextGeneration() to create the next generation and * calculateFitness() to calculate the fitness of each Genome. The fitness * is how well the SigmaPiNetwork created from the Chromosome preforms * on the given dataset of NetworkInputs in terms of the average mean * squared error. This fitness is used for an elite selection of the top * 5% of the population along with a rank selection for the rest. It * uses the given mutation rate and crossover rate for each generation. * For each generation it prints out the fitness of the best Chromosome * for that generation along with the average fitness. It will save the * best network every given number of generations (epochs), if a file is * specified for saving. * * @param experinmentNum The integer experiment number to run. It must * be 1, 2, or 3. * @param popSize The positive integer size of the population to run * the genetic algorithm on. * @param numGens The positive integer number of generations to run the * genetic algorithm for. * @param mutationRate The double mutation rate per bit in the * chromosome, between 0.0 and 1.0. * @param crossoverRate The double crossover rate per bit in the * chromosome, between 0.0 and 1.0. * @param dataset The array of NetworkInputs that are the set of inputs * to test the evolved networks on for the SigmaPiFitness. * @param seed The long random number seed for the random number * generator. * @param networkSaveFile The String file name to save the best network * found in. If it is null, the network will not be saved. * @param networkSaveEpochs The positive integer that is the number of * epochs that pass between each time the best network so far * should be saved. * @see SigmaPiNetwork * @see SigmaPiChromosome * @see SigmaPiFitness * @see FitnessFunction * @see NetworkInput * @see NetworkEvolver.calculateFitness(Genome[], FitnessFunction) * @see NetworkEvolver.nextGeneration(Genome[], double, double, Random) * @see NetworkEvolver.select(Genome[], int) */ public static void run( int experimentNum, // Experiment to run. int popSize, // Population size. int numGens, // Number of generations. double mutationRate, // Mutation rate per bit. double crossoverRate, // Crossover rate per bit. NetworkInput[] dataset, // Dataset to test on. long seed, // Random number seed. String networkSaveFile, // File to save network in. int networkSaveEpochs) // Frequency to save network. { // Create the random number generator with the given seed. Random random = new Random(seed); // Create the SigmaPiNetwork that has the architecture that we are // trying to evolve. SigmaPiNetwork network = null; // Create the proper sized SigmaPiNetwork based on the experiment // number. if ( experimentNum == 1 ) // Create a 5-3-2 network. network = new SigmaPiNetwork( new DoubleArrayEncoder(InputOutputPair.PROPERTY_INPUT, 0, 5, 1), new DoubleArrayEncoder(InputOutputPair.PROPERTY_OUTPUT, 0, 1, 1), new int[] { 3 }, random); else if ( experimentNum == 2 ) // Create an 8-6-2 network. network = new SigmaPiNetwork( new DoubleArrayEncoder(InputOutputPair.PROPERTY_INPUT, 0, 8, 1), new DoubleArrayEncoder(InputOutputPair.PROPERTY_OUTPUT, 0, 2, 1), new int[] { 6 }, random); else if ( experimentNum == 3 ) // Create an 11-9-5-3 network. network = new SigmaPiNetwork( new DoubleArrayEncoder(InputOutputPair.PROPERTY_INPUT, 0, 11, 1), new DoubleArrayEncoder(InputOutputPair.PROPERTY_OUTPUT, 0, 2, 1), new int[] { 9, 5, 3 }, random); else { // Error: Bad experiment number. System.err.println("Unknown experiment number: " + experimentNum + ". Cannot generate network."); return; } // Create the activation functions for the network. All of which are // linear. ActivationFunction[][] functions = new ActivationFunction[network.getNumLayers() - 1][]; for (int i = 0; i < functions.length; i++) { functions[i] = new ActivationFunction[network.getLayerSize(i + 1) + 1]; for (int j = 0; j < functions[i].length; j++) functions[i][j] = LinearFunction.INSTANCE; } // Set the linear activation functions into the network. network.setActivationFunctions(functions); // Create the fitness function for evaluating chromosomes. SigmaPiFitness fitFunc = new SigmaPiFitness(network, dataset); // Figure out the sizes of the layers that we are encoding based on // which problem it is. int[] toSizes = null; int[] fromSizes = null; if ( experimentNum == 1 ) { // Evolve all weights in the network. toSizes = new int[] { 3, 1 }; fromSizes = new int[] { 5, 3 }; } else if ( experimentNum == 2 ) { // Evolve all weights in the network. toSizes = new int[] { 6, 2 }; fromSizes = new int[] { 8, 6 }; } else if ( experimentNum == 3 ) { // Only evolve some of the weights in the network. toSizes = new int[] { 6, 2, 3, 1 }; fromSizes = new int[] { 8, 6, 5, 3 }; // Set the rest of the weights properly. network.setWeight(1, 7, 9, 1.0); network.setWeight(1, 8, 10, 1.0); network.setWeight(1, 9, 11, 1.0); network.setWeight(2, 3, 7, 1.0); network.setWeight(2, 4, 8, 1.0); network.setWeight(2, 5, 9, 1.0); } // Calculate the length of the Chromosomes. int chromosomeSize = 0; for (int i = 0; i < toSizes.length; i++) chromosomeSize += toSizes[i] * (fromSizes[i] + 1); // Output the length of the chromosomes. System.out.println(" Chromosome length: " + chromosomeSize); // Create the index of which weights the bits in the chromosomes // correspond to for when we create the SigmaPiChromosomes. int[][] weightIndex = new int[chromosomeSize][3]; int offset = 0; for (int i = 1; i <= toSizes.length; i++) { for (int j = 1; j <= toSizes[i - 1]; j++) { for (int k = 0; k <= fromSizes[i - 1]; k++) { weightIndex[offset][0] = i; weightIndex[offset][1] = j; weightIndex[offset][2] = k; offset++; } } } // Create the population of Genomes by creating the Chromosomes. Genome[] population = new Genome[popSize]; for (int i = 0; i < popSize; i++) { // Create the SigmaPiChromosome. SigmaPiChromosome chrom = new SigmaPiChromosome(chromosomeSize, weightIndex, random); // Add it to the population of Genomes. population[i] = new Genome(chrom); } // Keep track of the best Genome we have seen so far. Genome bestOfAll = null; for (int g = 0; g <= numGens; g++) { // Print the generation number. System.out.print("" + g + ": "); // Calculate the fitness of the population and keep track of the // best Genome. Genome best = calculateFitness(population, fitFunc); if ( bestOfAll == null || best.getFitness() < bestOfAll.getFitness() ) // The current best is the best Genome we have seen so far. bestOfAll = best; // else - The best for this generation is not the best of all. if ( g != 0 && g != numGens && (g % 100) == 0 ) // Time to print the best we have found so far. printBestGenome(bestOfAll, network, dataset); // else - Not time to print. if ( g != 0 && (g % networkSaveEpochs) == 0 ) // Time to save the best we have found so far. saveGenome(bestOfAll, network, networkSaveFile); // else - Not time to save. if ( g < numGens ) // Create the next generation. population = nextGeneration(population, crossoverRate, mutationRate, random); // else - This is the last generation. Don't create a new one. } // Print the best Genome and save it to the specified file. printBestGenome(bestOfAll, network, dataset); saveGenome(bestOfAll, network, networkSaveFile); } /** * printBestGenome * * This method takes a Genome, a SigmaPiNetwork, and an array of * NetworkInputs that make up a dataset. It loads the SigmaPiChromosome * that is stored in the given Genome and then tests the network on all * of the given dataset, calculating its average mean squared error. * It outputs the fitness value for that Genome along with the average * mean square error to System.out then uses the print() method for * SigmaPiNetworks to output the information about the chromosome loaded * into the network to System.out. It is assumed that this Genome is the * best Genome so far. * * @param genome The Genome that contains the SigmaPiChromosome to print * to System.out. It is assumed that this Genome is the best * Genome so far. * @param network The SigmaPiNetwork to load the SigmaPiChromosome into * in order to test it. * @param dataset The array of NetworkInputs that make up the dataset to * test the chromosome on to see its average mean squared error. */ public static void printBestGenome( Genome genome, // Genome to print. SigmaPiNetwork network, // Network to load into. NetworkInput[] dataset) // Dataset to test with. { // Convert the Chromosome in the Genome to a SigmaPiChromosome. SigmaPiChromosome spc = (SigmaPiChromosome) genome.getChromosome(); // Load the chromosome into the SigmaPiNetwork. spc.loadIntoNetwork(network); // Calculate the average mean squared error across the dataset. double errorSum = 0.0; for (int i = 0; i < dataset.length; i++) errorSum += network.testOnInput(dataset[i]); // Output the fitness of the Genome. System.out.println("Best fitness: " + Utilities.NF7.format(genome.getFitness())); // Output the average mean squared error. System.out.println("Best average error: " + Utilities.NF7.format(errorSum / (double) dataset.length)); // Output the network itself. network.print(); } /** * saveGenome * * This method takes a Genome, a SigmaPiNetwork, and a String file name * and loads the SigmaPiChromosome in the given Genome into the * SigmaPiNetwork and then saves that network into the given file name, * as long as it is not null. * * @param genome The Genome that contains the SigmaPiChromosome to save * in the form of a SigmaPiNetwork. * @param network The SigmaPiNetwork that the chromosome is to be loaded * into so that it can be saved. * @param fileName The String file name to save the network into. * @see NeuralNetwork.writeNetworkToFile(String) */ public static void saveGenome( Genome genome, // Genome to save. SigmaPiNetwork network, // Network to save it in. String fileName) // File to save in. { if ( fileName == null ) // Error: No file to save it in. return; // Convert the Chromosome in the Genome to a SigmaPiChromosome. SigmaPiChromosome spc = (SigmaPiChromosome) genome.getChromosome(); // Load the SigmePiChromosome into the network. spc.loadIntoNetwork(network); System.out.println("Saving best network to file " + fileName); // Write the network into a file. network.writeNetworkToFile(fileName); } /** * calculateFitness * * This method takes an array of Genomes, which is the current population, * and a FitnessFunction and calculates the fitness of all of the Genomes * in the population. It prints the fitness of the best Genome in the * population along with the average fitness. It then returns the Genome * that had the best fitness value in the given population. * * It is assumed that FitnessFunction returns a smaller value for * Chromosomes with higher fitness values. * * @param population The array of Genomes that is the current population. * @param function The FitnessFunction for calculating the fitness of all * the Genomes. * @return The Genome in the given population that has the best fitness, * according to the given fitness function. This is just the first * such Genome found, if there is a tie. * @see Genome.updateFitness(FitnessFunction) * @see FitnessFunction */ public static Genome calculateFitness( Genome[] population, // The current population. FitnessFunction function) // A fitness function. { // We want to keep track of the best Genome. Genome best = null; double bestFitness = 0.0; // Keep track of the total fitness so we can average it. double totalFitness = 0.0; // Go through all of the population and calculate the fitness. for (int i = 0; i < population.length; i++) { // Calculate the fitness of the ith member of the population. double result = population[i].updateFitness(function); totalFitness += result; if ( best == null || result < bestFitness ) { // This is the best (or the first) Genome we've seen so far, // update the best information. best = population[i]; bestFitness = result; } // else - This Genome isn't the best. } // Output the information about the best and the average fitness. System.out.println("Best: " + Utilities.NF7.format(bestFitness) + ". " + "Average: " + Utilities.NF7.format(totalFitness / population.length)); // Return the best Genome. return best; } /** * nextGeneration * * This method implements the creation of a new generation of Genomes from * a given old generation. It takes all of the needed parameters to do * this. First it automatically transfers the top 5% of the Genomes into * the new population. Then it does a rank selection by using select() in * order to choose two members of the old population and then it does a * crossover between them and puts one of the children into the next * generation. Then it goes through again and does mutation to those not * selected by the elite selection. * * @param oldGeneration The array of Genomes which is the population to * create the next generation from. * @param crossoverRate The double crossover rate, per Genome. * @param mutationRate The double mutation rate, per Chromosome. * @return The next generation Genome array of the population. * @see Chromosome.mutate(double, random) * @see Chromosome.crossover(Chromosome, double, Random) * @see NetworkEvolver.select(Genome[], int) */ public static Genome[] nextGeneration( Genome[] oldGeneration, // Old population to create form. double crossoverRate, // Crossover rate per Genome. double mutationRate, // Mutation rate per Chromosome. Random random) // Random number generator. { // The population size stays the same. int popSize = oldGeneration.length; // Create the next generation. Genome[] newGeneration = new Genome[popSize]; // This is the total number of ranks in the population. int numRanks = (popSize * (popSize + 1)) / 2; // Sort the Genomes so the most fit are at the end. Arrays.sort(oldGeneration); // Automatically transfer the top 5% to the new population. int elite = popSize / 20 + 1; for (int i = 0; i < elite; i++) { newGeneration[i] = (Genome) oldGeneration[popSize - 1 - i].clone(); } // Rank Selection: for (int i = elite; i < popSize; i++) { // Pick two genomes randomly. Genome g = (Genome) select(oldGeneration, (int) (numRanks * random.nextDouble())).clone(); Genome h = (Genome) select(oldGeneration, (int) (numRanks * random.nextDouble())).clone(); // Do a crossover. g.crossover(h, crossoverRate, random); // Save g. newGeneration[i] = g; } // Mutate the non-elite Genomes. for (int i = elite; i < popSize; i++) newGeneration[i].mutate(mutationRate, random); // Finished the new generation. return newGeneration; } /** * select * * This method takes an array of Genomes that is the current population * an integer and selects the member of the population based on the * given rank. Rank selection is where each member of the population has a * probability of being chosen proportional to their rank number. So the * first Genome (that has the worst fitness) has the lowest probability * and the last Genome (that has the best fitness) has the highest * probability. The given integer is a number between 1 and the sum of all * the ranks in the population, so the member returned is the one where * the number falls into its rank, starting from the highest member. * * @param population The Genome population to select from. * @param n The selection number, between 0 and the sum of all the ranks * in the population. * @return The proper Genome based on rank selection. */ public static Genome select( Genome[] population, // Population to select from. int n) // Number to select. { // Try to find the member of the population with the given value. int index = n + 1; for (int i = population.length; i > 0; i--) if ( index <= i ) // Found it. return population[i - 1]; else // Haven't found it yet, keep looking. index -= i; // Error: Couldn't find it. Return the most fit by default. return population[population.length - 1]; } /** * main * * This is the main method where execution begins. It is supplied with * the array of Strings that are the command-line arguments. It parses * these arguments and as long as they are all fine it calls the run() * method to run the network evolution algorithm. * * @param args The array of Strings that are the command-line arguments. */ public static void main( String[] args) // Command-line arguments. { // These are the variables for the command-line arguments. boolean runProgram = true; int experimentNum = -1; int popSize = 200; int numGens = 100; double mutationRate = 0.01; double crossoverRate = 0.1; String datasetFile = null; NetworkInput[] dataset = null; long seed = System.currentTimeMillis(); String networkSaveFile = null; int networkSaveEpochs = 100; // Parse the command-line arguments. for (int i = 0; i < args.length && runProgram; i++) { if ( args[i].equalsIgnoreCase("-e") ) experimentNum = Integer.parseInt(args[++i]); else if ( args[i].equalsIgnoreCase("-p") ) popSize = Integer.parseInt(args[++i]); else if ( args[i].equalsIgnoreCase("-g") ) numGens = Integer.parseInt(args[++i]); else if ( args[i].equalsIgnoreCase("-m") ) mutationRate = Double.parseDouble(args[++i]); else if ( args[i].equalsIgnoreCase("-c") ) crossoverRate = Double.parseDouble(args[++i]); else if ( args[i].equalsIgnoreCase("-d") ) datasetFile = args[++i]; else if ( args[i].equalsIgnoreCase("-r") ) seed = Long.parseLong(args[++i]); else if ( args[i].equalsIgnoreCase("-n") ) networkSaveFile = args[++i]; else if ( args[i].equalsIgnoreCase("-s") ) networkSaveEpochs = Integer.parseInt(args[++i]); else if ( args[i].equalsIgnoreCase("-h") ) runProgram = false; else { // Error: Bad parameter System.err.println("Unknown parameter: " + args[i]); runProgram = false; } } if ( !runProgram ) { printUsage(); return; } if ( experimentNum != 1 && experimentNum != 2 && experimentNum != 3 ) { // Error: Bad experiment number. System.err.println("The experiment number must be 1, 2, or 3."); runProgram = false; } else if ( popSize < 1 ) { // Error: Bad population size. System.err.println("Population size must be at least 1."); runProgram = false; } else if ( numGens < 0 ) { // Error: Bad number of generations. System.err.println("Number of generations must be at least 0."); runProgram = false; } else if ( mutationRate < 0.0 ) { // Error: Bad mutation rate. System.err.println("Mutation rate must be at least 0.0."); runProgram = false; } else if ( crossoverRate < 0.0 ) { // Error: Bad crossover rate. System.err.println("Crossover rate must be at least 0.0."); runProgram = false; } else if ( networkSaveEpochs < 1 ) { // Error: Bad network save epoch interval. System.err.println("The network save epochs must be at lest 1."); runProgram = false; } else if ( datasetFile == null ) { // Error: No dataset file. System.err.println("You must specify a dataset file to read in " + "in order to test the evolved networks."); runProgram = false; } else if ( !Utilities.fileExists(datasetFile) ) { // Error: Dataset file does not exist. System.err.println("No such file: " + datasetFile); runProgram = false; } else { System.out.print("Reading dataset from file " + datasetFile + "..."); System.out.flush(); // Read in the dataset file. dataset = DatasetReader.readDoubleDataset(datasetFile); if ( dataset == null ) { // Error reading in the dataset file. System.err.println(" Error reading dataset file. " + "Aborting program."); runProgram = false; } else if ( dataset.length <= 0 ) { // Error: Dataset is empty. System.err.println(" Error reading dataset file: the dataset" + " is empty."); runProgram = false; } else // Reading in the dataset succeeded. System.out.println(" Done."); } if ( !runProgram ) { printUsage(); return; } // Output parameter information. System.out.println("Running the program with the following " + "parameters:"); System.out.println(" Experiment number: " + experimentNum); System.out.println(" Population size: " + popSize); System.out.println(" Generations: " + numGens); System.out.println(" Mutation rate: " + mutationRate); System.out.println(" Crossover rate: " + crossoverRate); System.out.println(" Dataset: " + datasetFile); System.out.println(" Random seed: " + seed); System.out.println(" Network save file: " + networkSaveFile); System.out.println(" Network saving: " + networkSaveEpochs + " " + "epochs"); // Run the experiment. run(experimentNum, popSize, numGens, mutationRate, crossoverRate, dataset, seed, networkSaveFile, networkSaveEpochs); } /** * printUsage * * This method just prints the usage message for this program to * System.out. */ public static void printUsage() { System.out.println("Usage: java NetworkEvolver "); System.out.println("Parameters:"); System.out.println(" -e "); System.out.println(" -p "); System.out.println(" -g "); System.out.println(" -m "); System.out.println(" -c "); System.out.println(" -d "); System.out.println(" -r "); System.out.println(" -n "); System.out.println(" -s "); System.out.println(" -h"); } }