/// <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();
            }
        }
Пример #7
0
        /// <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;
        }