/// <summary> /// Create and return a SteadyStateNeatEvolutionAlgorithm object (specific to fitness-based evaluations) ready for /// running the /// NEAT algorithm/search based on the given genome factory and genome list. Various sub-parts of the algorithm are /// also constructed and connected up. /// </summary> /// <param name="genomeFactory">The genome factory from which to generate new genomes</param> /// <param name="genomeList">The current genome population</param> /// <param name="startingEvaluations">The number of evaluations that have been executed prior to the current run.</param> /// <returns>Constructed evolutionary algorithm</returns> public override INeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm( IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList, ulong startingEvaluations) { FileDataLogger logger = null; // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference. IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0); ISpeciationStrategy<NeatGenome> speciationStrategy = new ParallelKMeansClusteringStrategy<NeatGenome>(distanceMetric, ParallelOptions); // Create complexity regulation strategy. var complexityRegulationStrategy = ExperimentUtils.CreateComplexityRegulationStrategy(ComplexityRegulationStrategy, Complexitythreshold); // Initialize the logger if (_generationalLogFile != null) { logger = new FileDataLogger(_generationalLogFile); } // Create the evolution algorithm. var ea = new SteadyStateNeatEvolutionAlgorithm<NeatGenome>(NeatEvolutionAlgorithmParameters, speciationStrategy, complexityRegulationStrategy, _batchSize, _populationEvaluationFrequency, RunPhase.Primary, logger); // Create IBlackBox evaluator. var mazeNavigationEvaluator = new MazeNavigationMCNSEvaluator(MaxDistanceToTarget, MaxTimesteps, MazeVariant, MinSuccessDistance, _behaviorCharacterizationFactory); // Create genome decoder. var genomeDecoder = CreateGenomeDecoder(); // Create a novelty archive. AbstractNoveltyArchive<NeatGenome> archive = new BehavioralNoveltyArchive<NeatGenome>(_archiveAdditionThreshold, _archiveThresholdDecreaseMultiplier, _archiveThresholdIncreaseMultiplier, _maxGenerationArchiveAddition, _maxGenerationsWithoutArchiveAddition); // IGenomeEvaluator<NeatGenome> fitnessEvaluator = // new SerialGenomeBehaviorEvaluator<NeatGenome, IBlackBox>(genomeDecoder, mazeNavigationEvaluator, // _nearestNeighbors, archive); IGenomeEvaluator<NeatGenome> fitnessEvaluator = new ParallelGenomeBehaviorEvaluator<NeatGenome, IBlackBox>(genomeDecoder, mazeNavigationEvaluator, SelectionType.SteadyState, SearchType.MinimalCriteriaNoveltySearch, _nearestNeighbors, archive); // Initialize the evolution algorithm. ea.Initialize(fitnessEvaluator, genomeFactory, genomeList, 0, MaxEvaluations, archive); // Finished. Return the evolution algorithm return ea; }
/// <summary> /// Create and return a SteadyStateNeatEvolutionAlgorithm object (specific to fitness-based evaluations) ready for /// running the /// NEAT algorithm/search based on the given genome factory and genome list. Various sub-parts of the algorithm are /// also constructed and connected up. /// </summary> /// <param name="genomeFactory">The genome factory from which to generate new genomes</param> /// <param name="genomeList">The current genome population</param> /// <param name="startingEvaluations">The number of evaluations that have been executed prior to the current run.</param> /// <returns>Constructed evolutionary algorithm</returns> public override INeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm( IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList, ulong startingEvaluations) { FileDataLogger logger = null; // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference. IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0); ISpeciationStrategy<NeatGenome> speciationStrategy = new ParallelKMeansClusteringStrategy<NeatGenome>(distanceMetric, ParallelOptions); // Create complexity regulation strategy. var complexityRegulationStrategy = ExperimentUtils.CreateComplexityRegulationStrategy(ComplexityRegulationStrategy, Complexitythreshold); // Initialize the logger if (_generationalLogFile != null) { logger = new FileDataLogger(_generationalLogFile); } // Create the evolution algorithm. var ea = new SteadyStateNeatEvolutionAlgorithm<NeatGenome>(NeatEvolutionAlgorithmParameters, speciationStrategy, complexityRegulationStrategy, _batchSize, _populationEvaluationFrequency, RunPhase.Primary, logger); // Create IBlackBox evaluator. var mazeNavigationEvaluator = new MazeNavigationRandomEvaluator(MaxDistanceToTarget, MaxTimesteps, MazeVariant, MinSuccessDistance); // Create genome decoder. var genomeDecoder = CreateGenomeDecoder(); IGenomeEvaluator<NeatGenome> fitnessEvaluator = new ParallelGenomeFitnessEvaluator<NeatGenome, IBlackBox>(genomeDecoder, mazeNavigationEvaluator, ParallelOptions); // Initialize the evolution algorithm. ea.Initialize(fitnessEvaluator, genomeFactory, genomeList, null, MaxEvaluations); // Finished. Return the evolution algorithm return ea; }
/// <summary> /// Creates and returns a GenerationalNeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. /// Various sub-parts of the algorithm are also constructed and connected up. This overload accepts a pre-built genome /// population and their associated/parent genome factory. /// </summary> /// <param name="genomeFactory">The NEAT genome factory.</param> /// <param name="genomeList">The initial list of genomes.</param> /// <returns>The NEAT evolution algorithm.</returns> public INeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList) { FileDataLogger logger = null; // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0); ISpeciationStrategy<NeatGenome> speciationStrategy = new ParallelKMeansClusteringStrategy<NeatGenome>(distanceMetric, _parallelOptions); // Create complexity regulation strategy IComplexityRegulationStrategy complexityRegulationStrategy = ExperimentUtils.CreateComplexityRegulationStrategy(_complexityRegulationStr, _complexityThreshold); // Initialize the logger if (_generationalLogFile != null) { logger = new FileDataLogger(_generationalLogFile); } // Create the evolution algorithm GenerationalNeatEvolutionAlgorithm<NeatGenome> ea = new GenerationalNeatEvolutionAlgorithm<NeatGenome>(NeatEvolutionAlgorithmParameters, speciationStrategy, complexityRegulationStrategy, logger); // Create evalutor BinaryEvolvedAutoencoderEvaluator evaluator = new BinaryEvolvedAutoencoderEvaluator(_trainingImagesFilename, InputCount, _numImageSamples, _learningRate, _numBackpropIterations, _trainingSampleProportion); // Create genome decoder IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = CreateGenomeDecoder(); // Create a genome list evaluator. This packages up the genome decoder with the genome evaluator IGenomeEvaluator<NeatGenome> innerFitnessEvaluator = new ParallelGenomeFitnessEvaluator<NeatGenome, IBlackBox>(genomeDecoder, evaluator, _parallelOptions); // Wrap the list evaluator in a 'selective' evaulator that will only evaluate new genomes. That is, we skip re-evaluating any genomes // that were in the population in previous generations (elite genomes). This is determined by examining each genome's evaluation info object. IGenomeEvaluator<NeatGenome> selectiveFitnessEvaluator = new SelectiveGenomeFitnessEvaluator<NeatGenome>( innerFitnessEvaluator, SelectiveGenomeFitnessEvaluator<NeatGenome>.CreatePredicate_OnceOnly()); // Initialize the evolution algorithm ea.Initialize(selectiveFitnessEvaluator, genomeFactory, genomeList, null); // Finished. Return the evolution algorithm return ea; }
/// <summary> /// Constructs and returns a NEAT evolution algorithm, using the given genome factory and genome list. /// </summary> /// <param name="genomeFactory">The genome factory to use for generating offspring during evolution.</param> /// <param name="genomeList">The initial list of genomes.</param> /// <returns>The NEAT evolution algorithm.</returns> public INeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList) { FileDataLogger logger = null; // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference. IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0); ISpeciationStrategy<NeatGenome> speciationStrategy = new ParallelKMeansClusteringStrategy<NeatGenome>(distanceMetric, _parallelOptions); // Create complexity regulation strategy. IComplexityRegulationStrategy complexityRegulationStrategy = ExperimentUtils.CreateComplexityRegulationStrategy(_complexityRegulationStrategy, _complexityThreshold); // Initialize the logger if (_generationalLogFile != null) { logger = new FileDataLogger(_generationalLogFile); } // Create the evolution algorithm. GenerationalNeatEvolutionAlgorithm<NeatGenome> ea = new GenerationalNeatEvolutionAlgorithm<NeatGenome>(NeatEvolutionAlgorithmParameters, speciationStrategy, complexityRegulationStrategy, logger); // Create black box evaluator. ThreeParityEvaluator evaluator = new ThreeParityEvaluator(); // Create genome decoder. Decodes to a neural network packaged with an activation scheme. IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = CreateGenomeDecoder(); // Create a genome list evaluator. This packages up the genome decoder with the genome evaluator. IGenomeEvaluator<NeatGenome> innerFitnessEvaluator = new ParallelGenomeFitnessEvaluator<NeatGenome, IBlackBox>(genomeDecoder, evaluator, _parallelOptions); // Wrap the list evaluator in a 'selective' evaulator that will only evaluate new genomes. That is, we skip re-evaluating any genomes // that were in the population in previous generations (elite genomes). This is determiend by examining each genome's evaluation info object. IGenomeEvaluator<NeatGenome> selectiveFitnessEvaluator = new SelectiveGenomeFitnessEvaluator<NeatGenome>( innerFitnessEvaluator, SelectiveGenomeFitnessEvaluator<NeatGenome>.CreatePredicate_OnceOnly()); // Initialize the evolution algorithm. ea.Initialize(selectiveFitnessEvaluator, genomeFactory, genomeList); // Finished. Return the evolution algorithm return ea; }
/// <summary> /// Executes all runs of a given experiment using a configuration file as the configuration source and generated flat /// files as the result destination. /// </summary> /// <param name="experimentConfigurationDirectory">The directory containing the XML experiment configuration file.</param> /// <param name="logFileDirectory">The directory into which to write the evolution/evaluation log files.</param> /// <param name="experimentName">The name of the experiment to execute.</param> /// <param name="numRuns">The number of runs to execute for that experiment.</param> /// <param name="seedPopulationFiles">The seed population XML file names.</param> /// <param name="writeOrganismStateData">Flag indicating whether organism state data should be written to an output file.</param> private static void ExecuteFileBasedExperiment(string experimentConfigurationDirectory, string logFileDirectory, string experimentName, int numRuns, string[] seedPopulationFiles, bool writeOrganismStateData) { // Read in the configuration files that match the given experiment name (should only be 1 file) string[] experimentConfigurationFiles = Directory.GetFiles(experimentConfigurationDirectory, string.Format("{0}.*", experimentName)); // Make sure there's only one configuration file that matches the experiment name // (otherwise, we don't know for sure which configuration to use) if (experimentConfigurationFiles.Count() != 1) { _executionLogger.Error( string.Format( "Experiment configuration ambiguous: expeted a single possible configuration, but found {0} possible configurations", experimentConfigurationFiles.Count())); Environment.Exit(0); } // Instantiate XML reader for configuration file XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(experimentConfigurationFiles[0]); // Determine which experiment to execute BaseMazeNavigationExperiment experiment = DetermineMazeNavigationExperiment(xmlConfig.DocumentElement); // Execute the experiment for the specified number of runs for (int runIdx = 0; runIdx < numRuns; runIdx++) { // Initialize the data loggers for the given run IDataLogger evolutionDataLogger = new FileDataLogger(string.Format("{0}\\{1} - Run{2} - Evolution.csv", logFileDirectory, experimentName, runIdx + 1)); IDataLogger evaluationDataLogger = writeOrganismStateData ? new FileDataLogger(string.Format("{0}\\{1} - Run{2} - Evaluation.csv", logFileDirectory, experimentName, runIdx + 1)) : null; // Initialize new experiment experiment.Initialize(experimentName, xmlConfig.DocumentElement, evolutionDataLogger, evaluationDataLogger); _executionLogger.Info(string.Format("Initialized experiment {0}.", experiment.GetType())); // This will hold the number of evaluations executed for each "attempt" of the EA within the current run ulong curEvaluations = 0; // This will hold the number of restarts of the algorithm int curRestarts = 0; do { IGenomeFactory<NeatGenome> genomeFactory; List<NeatGenome> genomeList; // If there were seed population files specified, read them in if (seedPopulationFiles != null) { // Open and load population XML file. using (XmlReader xr = XmlReader.Create(seedPopulationFiles[runIdx])) { genomeList = experiment.LoadPopulation(xr); } // Grab the specified genome factory on the first genome in the list genomeFactory = genomeList[0].GenomeFactory; } // Otherwise, generate the starting population else { // Create a new genome factory genomeFactory = experiment.CreateGenomeFactory(); // Generate the initial population genomeList = genomeFactory.CreateGenomeList(experiment.SeedGenomeCount, 0); } _executionLogger.Info(string.Format("Loaded [{0}] genomes as initial population.", genomeList.Count)); _executionLogger.Info("Kicking off Experiment initialization/execution"); // Kick off the experiment run RunExperiment(genomeFactory, genomeList, experimentName, experiment, numRuns, runIdx, curEvaluations); // Increment the evaluations curEvaluations = _ea.CurrentEvaluations; // Increment the restart count curRestarts++; } while (_ea.StopConditionSatisfied == false && experiment.MaxRestarts >= curRestarts); // Close the data loggers evolutionDataLogger.Close(); evaluationDataLogger?.Close(); } }
/// <summary> /// Executes all runs of a given coevolution experiment using a configuration file as the configuration source and /// generated flat files as the result destination. /// </summary> /// <param name="experimentConfigurationDirectory">The directory containing the XML experiment configuration file.</param> /// <param name="logFileDirectory">The directory into which to write the evolution/evaluation log files.</param> /// <param name="seedMazePath">The path to the XML maze genome file or directory containing XML maze genome files.</param> /// <param name="experimentName">The name of the experiment to execute.</param> /// <param name="numRuns">The number of runs to execute for that experiment.</param> /// <param name="startFromRun">The run to start from (1 by default).</param> private static void ExecuteFileBasedCoevolutionExperiment(string experimentConfigurationDirectory, string logFileDirectory, string seedMazePath, string experimentName, int numRuns, int startFromRun) { // Read in the configuration files that match the given experiment name (should only be 1 file) string[] experimentConfigurationFiles = Directory.GetFiles(experimentConfigurationDirectory, string.Format("{0}.*", experimentName)); // Make sure there's only one configuration file that matches the experiment name // (otherwise, we don't know for sure which configuration to use) if (experimentConfigurationFiles.Count() != 1) { _executionLogger.Error( string.Format( "Experiment configuration ambiguous: expeted a single possible configuration, but found {0} possible configurations", experimentConfigurationFiles.Count())); Environment.Exit(0); } // Instantiate XML reader for configuration file XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(experimentConfigurationFiles[0]); // Determine which experiment to execute BaseCoevolutionMazeNavigationExperiment experiment = DetermineCoevolutionMazeNavigationExperiment(xmlConfig.DocumentElement); // Execute the experiment for the specified number of runs for (int runIdx = startFromRun; runIdx <= numRuns; runIdx++) { // Initialize the data loggers for the given run IDataLogger navigatorDataLogger = new FileDataLogger(string.Format("{0}\\{1} - Run{2} - NavigatorEvolution.csv", logFileDirectory, experimentName, runIdx)); IDataLogger navigatorGenomesLogger = new FileDataLogger(string.Format("{0}\\{1} - Run{2} - NavigatorGenomes.csv", logFileDirectory, experimentName, runIdx)); IDataLogger mazeDataLogger = new FileDataLogger(string.Format("{0}\\{1} - Run{2} - MazeEvolution.csv", logFileDirectory, experimentName, runIdx)); IDataLogger mazeGenomesLogger = new FileDataLogger(string.Format("{0}\\{1} - Run{2} - MazeGenomes.csv", logFileDirectory, experimentName, runIdx)); // Initialize new experiment experiment.Initialize(experimentName, xmlConfig.DocumentElement, navigatorDataLogger, navigatorGenomesLogger, mazeDataLogger, mazeGenomesLogger); _executionLogger.Info(string.Format("Initialized experiment {0}.", experiment.GetType())); // If there were seed population files specified, read them in if (Boolean.Parse(_executionConfiguration[ExecutionParameter.GeneratePopulation]) == false) { // TODO: Need to implement ability to load maze seed genomes throw new NotImplementedException("Currently unable to read in serialized maze genomes."); } // Otherwise, generate the starting population // Create a new agent genome factory IGenomeFactory<NeatGenome> agentGenomeFactory = experiment.CreateAgentGenomeFactory(); // Create a new maze genome factory IGenomeFactory<MazeGenome> mazeGenomeFactory = experiment.CreateMazeGenomeFactory(); // Generate the initial agent population List<NeatGenome> agentGenomeList = agentGenomeFactory.CreateGenomeList(experiment.AgentInitializationGenomeCount, 0); // Read in the seed population List<MazeGenome> mazeGenomeList = ExperimentUtils.ReadSeedMazeGenomes(seedMazePath, (MazeGenomeFactory) mazeGenomeFactory).Take(experiment.MazeSeedGenomeCount).ToList(); // Check for insufficient number of genomes in the seed maze file if (mazeGenomeList.Count < experiment.MazeSeedGenomeCount) { throw new SharpNeatException( string.Format( "The maze genome input file contains only {0} genomes while the experiment configuration requires {1} genomes.", mazeGenomeList.Count, experiment.MazeDefaultPopulationSize)); } _executionLogger.Info( string.Format("Loaded [{0}] navigator genomes and [{1}] seed maze genomes as initial populations.", agentGenomeList.Count, mazeGenomeList.Count)); _executionLogger.Info("Kicking off Experiment initialization/execution"); // Kick off the experiment run RunExperiment(agentGenomeFactory, mazeGenomeFactory, agentGenomeList, mazeGenomeList, experimentName, experiment, numRuns, runIdx); // Close the data loggers navigatorDataLogger.Close(); navigatorGenomesLogger.Close(); mazeDataLogger.Close(); mazeGenomesLogger.Close(); } }
/// <summary> /// Reads the experiment logging configuration. /// </summary> /// <param name="xmlConfig">The reference to the XML configuration file.</param> /// <param name="loggingType">The type of logging (evolution, evaluation, etc.).</param> /// <param name="parentElementName">The name of the top-level logging configuration element.</param> /// <returns>A constructed data logger.</returns> public static IDataLogger ReadDataLogger(XmlElement xmlConfig, LoggingType loggingType, string parentElementName) { IDataLogger dataLogger = null; XmlElement xmlLoggingConfig = null; int cnt = 0; // Get root of novelty configuration section XmlNodeList nodeList = xmlConfig.GetElementsByTagName(parentElementName, ""); // Iterate through the list of logging configurations, finding one that matches the specified logging type foreach (XmlElement curXmlLoggingConfig in nodeList) { if (loggingType == LoggingParameterUtils.ConvertStringToLoggingType(XmlUtils.TryGetValueAsString(curXmlLoggingConfig, "Type"))) { xmlLoggingConfig = curXmlLoggingConfig; break; } } // If no appropriate logger was found, just return null (meaning there won't be any logging for this type) if (xmlLoggingConfig == null) return null; // Get the logging destination LoggingDestination loggingDestination = LoggingParameterUtils.ConvertStringToLoggingDestination( XmlUtils.TryGetValueAsString(xmlLoggingConfig, "Destination")); // Configure a file-based logger if (LoggingDestination.File == loggingDestination) { // Read in the log file name string logFileName = XmlUtils.TryGetValueAsString(xmlLoggingConfig, "LogFile"); // Instantiate the file data logger dataLogger = new FileDataLogger(logFileName); } else if (LoggingDestination.Database == loggingDestination) { // Read in the experiment configuration and run number string experimentConfigurationName = XmlUtils.TryGetValueAsString(xmlLoggingConfig, "ExperimentConfigurationName"); if (LoggingType.Evolution == loggingType) { // Instantiate the evolution database data logger dataLogger = new NoveltyExperimentEvaluationEntityDataLogger(experimentConfigurationName); } else if (LoggingType.Evaluation == loggingType) { // Instantiate the evaluation database data logger dataLogger = new NoveltyExperimentOrganismStateEntityDataLogger(experimentConfigurationName); } } return dataLogger; }