/// <summary>
 ///     Novelty search runner constructor.
 /// </summary>
 /// <param name="comparisonExperimentConfig">
 ///     The experiment configuration against which to compare the given coevolution
 ///     experiment.
 /// </param>
 /// <param name="refExperimentName">The name of the reference (novelty search) experiment configuration.</param>
 /// <param name="mazeDomain">The maze on which the comparison will be executed.</param>
 /// <param name="currentRun">The current run being analyzed.</param>
 /// <param name="totalRuns">The total number of runs in the coevolution experiment.</param>
 /// <param name="executionLogger">Execution file logger.</param>
 public NoveltySearchRunner(ExperimentDictionary comparisonExperimentConfig, string refExperimentName,
     MazeStructure mazeDomain, int currentRun, int totalRuns, ILog executionLogger)
 {
     _comparisonExperimentConfig = comparisonExperimentConfig;
     _referenceExperimentName = refExperimentName;
     _evaluationMazeDomain = mazeDomain;
     _currentRun = currentRun;
     _totalRuns = totalRuns;
     _executionLogger = executionLogger;
 }
        public override void Initialize(ExperimentDictionary experimentDictionary)
        {
            base.Initialize(experimentDictionary);

            // Read in the behavior characterization
            _behaviorCharacterizationFactory = ExperimentUtils.ReadBehaviorCharacterizationFactory(
                experimentDictionary, true);

            // Read in number of offspring to produce in a single batch
            _batchSize = experimentDictionary.Primary_OffspringBatchSize ?? default(int);

            // Read in log file path/name
            _evolutionDataLogger = new McsExperimentEvaluationEntityDataLogger(experimentDictionary.ExperimentName);
            _evaluationDataLogger =
                new McsExperimentOrganismStateEntityDataLogger(experimentDictionary.ExperimentName);
        }
        public override void Initialize(ExperimentDictionary experimentDictionary)
        {
            base.Initialize(experimentDictionary);

            // Ensure the start position and minimum distance constraint are not null
            Debug.Assert(experimentDictionary.Primary_MCS_MinimalCriteriaStartX != null,
                "experimentDictionary.Primary_MCS_MinimalCriteriaStartX != null");
            Debug.Assert(experimentDictionary.Primary_MCS_MinimalCriteriaStartY != null,
                "experimentDictionary.Primary_MCS_MinimalCriteriaStartY != null");
            Debug.Assert(experimentDictionary.Primary_MCS_MinimalCriteriaThreshold != null,
                "experimentDictionary.Primary_MCS_MinimalCriteriaThreshold != null");

            // Read in the behavior characterization
            _behaviorCharacterizationFactory = ExperimentUtils.ReadBehaviorCharacterizationFactory(
                experimentDictionary, true);

            // Read in steady-state specific parameters
            _batchSize = experimentDictionary.Primary_OffspringBatchSize ?? default(int);
            _populationEvaluationFrequency = experimentDictionary.Primary_PopulationEvaluationFrequency ?? default(int);

            // Read in MCS selection method
            _mcsSelectionMethod = experimentDictionary.Primary_SelectionAlgorithmName;

            // Read in log file path/name
            _evolutionDataLogger = new NoveltyExperimentEvaluationEntityDataLogger(experimentDictionary.ExperimentName);
            _evaluationDataLogger =
                new NoveltyExperimentOrganismStateEntityDataLogger(experimentDictionary.ExperimentName);
        }
        public override void Initialize(ExperimentDictionary experimentDictionary)
        {
            base.Initialize(experimentDictionary);

            // Read in the behavior characterization
            _behaviorCharacterizationFactory = ExperimentUtils.ReadBehaviorCharacterizationFactory(
                experimentDictionary, true);

            // Read in number of offspring to produce in a single batch
            _batchSize = experimentDictionary.Primary_OffspringBatchSize ?? default(int);

            // Read in the bridging magnitude
            _bridgingMagnitude = experimentDictionary.Primary_MCS_BridgingMagnitude ?? default(int);
            _bridgingApplications = experimentDictionary.Primary_MCS_BridgingApplications ?? default(int);

            // TODO: Read seed genome count from the database

            // Read in log file path/name
            _evolutionDataLogger = new McsExperimentEvaluationEntityDataLogger(experimentDictionary.ExperimentName);
            _evaluationDataLogger =
                new McsExperimentOrganismStateEntityDataLogger(experimentDictionary.ExperimentName);

            // Initialize the initialization algorithm
            _mazeNavigationInitializer = new NoveltySearchMazeNavigationInitializer(MaxEvaluations, _evolutionDataLogger,
                _evaluationDataLogger,
                SerializeGenomeToXml);

            // Setup initialization algorithm
            _mazeNavigationInitializer.SetAlgorithmParameters(experimentDictionary, InputCount, OutputCount);

            // Pass in maze experiment specific parameters
            _mazeNavigationInitializer.SetEnvironmentParameters(MaxDistanceToTarget, MaxTimesteps, MazeVariant,
                MinSuccessDistance);
        }
        /// <summary>
        ///     Instantiates a new database context (opens the connection) and gets the experiment configuration key.
        /// </summary>
        public virtual void Open()
        {
            // Open the database connection
            DbContext = new ExperimentDataEntities();

            // Query for the experiment configuration entity
            ExperimentConfiguration =
                DbContext.ExperimentDictionaries.Single(expName => expName.ExperimentName == ExperimentConfigurationName);
        }
        /// <summary>
        ///     Constructs and initializes the MCS initialization algorithm (novelty search) using the database configuration.
        /// </summary>
        /// <param name="experimentDictionary">The reference to the experiment dictionary entity.</param>
        /// <param name="inputCount">The number of input neurons.</param>
        /// <param name="outputCount">The number of output neurons.</param>
        /// <returns>The constructed initialization algorithm.</returns>
        public void SetAlgorithmParameters(ExperimentDictionary experimentDictionary, int inputCount,
            int outputCount)
        {
            // Read NEAT parameters
            NeatGenomeParameters neatGenomeParameters =
                ExperimentUtils.ReadNeatGenomeParameters(experimentDictionary, false);

            // Read NEAT evolution parameters
            NeatEvolutionAlgorithmParameters =
                ExperimentUtils.ReadNeatEvolutionAlgorithmParameters(experimentDictionary, false);

            // Get complexity constraint parameters
            ComplexityRegulationStrategyDefinition =
                experimentDictionary.Initialization_ComplexityRegulationStrategy;
            ComplexityThreshold = experimentDictionary.Initialization_ComplexityThreshold;

            // Read in the behavior characterization
            _behaviorCharacterizationFactory =
                ExperimentUtils.ReadBehaviorCharacterizationFactory(experimentDictionary, false);

            // Read in the novelty archive parameters
            _archiveAdditionThreshold =
                experimentDictionary.Initialization_NoveltySearch_ArchiveAdditionThreshold ?? default(double);
            _archiveThresholdDecreaseMultiplier =
                experimentDictionary.Initialization_NoveltySearch_ArchiveThresholdDecreaseMultiplier ??
                default(double);
            _archiveThresholdIncreaseMultiplier =
                experimentDictionary.Initialization_NoveltySearch_ArchiveThresholdIncreaseMultiplier ??
                default(double);
            _maxGenerationArchiveAddition =
                experimentDictionary.Initialization_NoveltySearch_MaxGenerationsWithArchiveAddition ??
                default(int);
            _maxGenerationsWithoutArchiveAddition =
                experimentDictionary.Initialization_NoveltySearch_MaxGenerationsWithoutArchiveAddition ??
                default(int);

            // Read in nearest neighbors for behavior distance calculations
            _nearestNeighbors = experimentDictionary.Initialization_NoveltySearch_NearestNeighbors ?? default(int);

            // Read in steady-state specific parameters
            _batchSize = experimentDictionary.Initialization_OffspringBatchSize ?? default(int);
            _populationEvaluationFrequency = experimentDictionary.Initialization_PopulationEvaluationFrequency ??
                                             default(int);
        }
        /// <summary>
        ///     Handles per-batch comparison of coevolution-generated mazes on a reference algorithm (e.g. novelty search).
        /// </summary>
        /// <param name="batchesWithGenomeData">List of the batches that contain maze genome data.</param>
        /// <param name="curCoevoExperimentId">Identifier of the coevolution experiment.</param>
        /// <param name="curCoevoExperimentName">Name of the coevolution experiment.</param>
        /// <param name="curRun">The current run.</param>
        /// <param name="numRuns">The total number of runs in the experiment.</param>
        /// <param name="mazeDecoder">Reference to the maze genome decoder.</param>
        /// <param name="noveltySearchExperimentConfig">The experiment configuration for the reference (novelty search) experiment.</param>
        private static void RunPerBatchComparativeAnalysis(IList<int> batchesWithGenomeData,
            int curCoevoExperimentId, string curCoevoExperimentName, int curRun, int numRuns, MazeDecoder mazeDecoder,
            ExperimentDictionary noveltySearchExperimentConfig)
        {
            // Declare list to hold maze IDs that have already been evaluated using the comparison algorithm
            List<int> evaluatedMazeGenomeIds = new List<int>();

            // Get the total number of initialization evaluations for the current run
            int coEvoInitEvaluations = ExperimentDataHandler.GetInitializationEvaluationsForRun(curCoevoExperimentId,
                curRun);

            // Iterate through each batch and run comparative algorithm on each maze
            foreach (int curBatch in batchesWithGenomeData)
            {
                _executionLogger.Info(string.Format("Executing comparative analysis for batch [{0}] of run [{1}/{2}]",
                    curBatch, curRun, numRuns));

                // Get list of all extant maze genomes for this batch
                IList<string> mazeGenomeXml = ExperimentDataHandler.GetMazeGenomeXml(curCoevoExperimentId, curRun,
                    curBatch);

                foreach (string genomeXml in mazeGenomeXml)
                {
                    MazeStructure curMazeStructure = null;
                    MazeGenome curMazeGenome = null;

                    // Create a new (mostly dummy) maze genome factory
                    MazeGenomeFactory curMazeGenomeFactory = new MazeGenomeFactory();

                    // Convert each genome string to an equivalent genome object
                    using (XmlReader xr = XmlReader.Create(new StringReader(genomeXml)))
                    {
                        curMazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xr, curMazeGenomeFactory);
                    }

                    // If the maze has already been processed, continue on to the next one
                    // (also exclude the initialization maze as a special case)
                    if (evaluatedMazeGenomeIds.Contains((int) curMazeGenome.BirthGeneration) ||
                        curMazeGenome.BirthGeneration == 0)
                    {
                        continue;
                    }

                    // Get the total number of evaluations required to solve this maze
                    // (this amounts to the total number of evaluations from both queues at the time of maze birth)
                    int coEvoTotalEvaluations = coEvoInitEvaluations +
                                                ExperimentDataHandler.GetTotalPrimaryEvaluationsAtBatch(
                                                    curCoevoExperimentId, curRun, (int) curMazeGenome.BirthGeneration);

                    // Decode the maze structure
                    curMazeStructure =
                        mazeDecoder.Decode(curMazeGenome);

                    // Instantiate new novelty search experiment for the current experiment/batch/maze
                    NoveltySearchRunner nsRunner = new NoveltySearchRunner(noveltySearchExperimentConfig,
                        curCoevoExperimentName, curMazeStructure, curRun, numRuns, _executionLogger);

                    _executionLogger.Info(string.Format(
                        "Executing novelty search on maze [{0}] with birth batch [{1}]", curMazeGenome.Id,
                        curMazeGenome.BirthGeneration));

                    // Run novelty search on that maze until the maze is solved or the max evals have been reached
                    INeatEvolutionAlgorithm<NeatGenome> eaFinalState = nsRunner.RunNoveltySearch();

                    // Write comparison results to output file
                    ExperimentDataHandler.WriteNoveltySearchComparisonResults(curCoevoExperimentId,
                        noveltySearchExperimentConfig.ExperimentDictionaryID, curRun, curBatch,
                        (int) curMazeGenome.Id, (int) curMazeGenome.BirthGeneration,
                        (int) eaFinalState.Statistics._minComplexity,
                        (int) eaFinalState.Statistics._maxComplexity, eaFinalState.Statistics._meanComplexity,
                        coEvoTotalEvaluations, (int) eaFinalState.CurrentEvaluations,
                        !((int) eaFinalState.CurrentEvaluations >= noveltySearchExperimentConfig.MaxEvaluations), false);

                    // Add maze genome ID to the list of processed mazes to its not considered for evaluation again
                    evaluatedMazeGenomeIds.Add((int) curMazeGenome.Id);
                }
            }
        }
        /// <summary>
        ///     Runs post-hoc analysis for all batches in the given experiment/run.  This can be part of either the initialization
        ///     or primary run phase.
        /// </summary>
        /// <param name="batchesWithGenomeData">The total number of batches containing genome data.</param>
        /// <param name="runPhase">
        ///     Indicates whether this is part of the initialization or primary run phase.
        /// </param>
        /// <param name="experimentParameters">Experiment configuration parameters.</param>
        /// <param name="inputNeuronCount">Count of neurons in controller input layer.</param>
        /// <param name="outputNeuronCount">Count of neurons in controller output layer.</param>
        /// <param name="curRun">The run number.</param>
        /// <param name="numRuns">The total number of runs.</param>
        /// <param name="curExperimentConfiguration">The experiment configuration parameters.</param>
        /// <param name="generateSimulationResults">Indicates whether to write out the results of the batch simulation.</param>
        /// <param name="writeResultsToDatabase">
        ///     Indicates whether to write results directly into a database (if not, results are
        ///     written to a flat file).
        /// </param>
        /// <param name="generateTrajectoryData">Indicates whether the full navigator trajectory should be simulated and persisted.</param>
        /// <param name="generateTrajectoryDiversityScore">
        ///     Indicates whether quantification of navigator trajectory diversity
        ///     should be written out.
        /// </param>
        /// <param name="generateNaturalClustering">Indicates whether the natural population clusters should be analyzed.</param>
        /// <param name="generateMazeBitmaps">Indicates whether bitmap files of the distinct mazes should be written out.</param>
        /// <param name="generateTrajectoryBitmaps">
        ///     Indicates whether bitmap files depicting the navigator trajectory should be
        ///     written out.
        /// </param>
        /// <param name="baseImageOutputDirectory">The path to the output directory for the trajectory images.</param>
        /// <param name="clusterImprovementThreshold">
        ///     The number of cluster additions that are permitted without further maximization of silhouette width.
        /// </param>
        /// <param name="sampleSize">The number of genomes sampled from the extant species for clustering analysis.</param>
        /// <param name="sampleClusterObservationsFromSpecies">
        ///     Indicates whether to sample observations used in clustering analysis
        ///     from species or from the population as a whole.
        /// </param>
        private static void ProcessAndLogPerBatchResults(IList<int> batchesWithGenomeData, RunPhase runPhase,
            ExperimentParameters experimentParameters, int inputNeuronCount, int outputNeuronCount, int curRun,
            int numRuns, ExperimentDictionary curExperimentConfiguration, bool generateSimulationResults,
            bool generateTrajectoryData, bool generateTrajectoryDiversityScore, bool generateNaturalClustering,
            bool writeResultsToDatabase, bool generateMazeBitmaps, bool generateTrajectoryBitmaps,
            string baseImageOutputDirectory, int clusterImprovementThreshold, int sampleSize,
            bool sampleClusterObservationsFromSpecies)
        {
            IList<CoevolutionMCSMazeExperimentGenome> staticInitializationMazes = null;

            // If this invocation is processing initialization results, just get the maze up front as it will remain
            // the same throughout the initialization process
            if (runPhase == RunPhase.Initialization)
            {
                staticInitializationMazes =
                    ExperimentDataHandler.GetMazeGenomeData(curExperimentConfiguration.ExperimentDictionaryID,
                        curRun, 0);
            }

            // Iterate through each batch and evaluate maze/navigator combinations
            foreach (int curBatch in batchesWithGenomeData)
            {
                // Create the maze/navigator map
                MapEvaluator mapEvaluator =
                    new MapEvaluator(experimentParameters, inputNeuronCount, outputNeuronCount);

                _executionLogger.Info(string.Format("Executing {0} run phase analysis for batch [{1}] of run [{2}/{3}]",
                    runPhase, curBatch, curRun, numRuns));

                // TODO: Check first to see if trajectory evaluations already exist

                // Get any existing navigation results (this avoids rerunning failed combinations)
                var successfulNavigations =
                    ExperimentDataHandler.GetSuccessfulNavigations(curExperimentConfiguration.ExperimentDictionaryID,
                        curRun, curBatch);

                // If successful navigation results were found and we're not re-running the simulations,
                // initialize the map evaluator with only those combinations known to be successful
                if (generateSimulationResults == false && successfulNavigations != null &&
                    successfulNavigations.Count > 0)
                {
                    List<Tuple<CoevolutionMCSMazeExperimentGenome, CoevolutionMCSNavigatorExperimentGenome>>
                        successfulGenomeCombos =
                            new List<Tuple<CoevolutionMCSMazeExperimentGenome, CoevolutionMCSNavigatorExperimentGenome>>
                                (successfulNavigations.Count());

                    // Get distinct maze and navigator genomes
                    var mazeGenomeData =
                        ExperimentDataHandler.GetMazeGenomeData(curExperimentConfiguration.ExperimentDictionaryID,
                            curRun, curBatch, successfulNavigations.Select(n => n.MazeGenomeID).Distinct().ToList());
                    var navigatorGenomeData =
                        ExperimentDataHandler.GetNavigatorGenomeData(curExperimentConfiguration.ExperimentDictionaryID,
                            curRun, curBatch, runPhase,
                            successfulNavigations.Select(n => n.NavigatorGenomeID).Distinct().ToList());

                    // Build list of successful maze/navigator combinations
                    successfulGenomeCombos.AddRange(
                        successfulNavigations.Select(
                            successfulNav =>
                                new Tuple<CoevolutionMCSMazeExperimentGenome, CoevolutionMCSNavigatorExperimentGenome>(
                                    mazeGenomeData.First(gd => successfulNav.MazeGenomeID == gd.GenomeID),
                                    navigatorGenomeData.First(gd => successfulNav.NavigatorGenomeID == gd.GenomeID))));

                    // Initialize the maze/navigator map with combinations that are known to be successful
                    mapEvaluator.Initialize(successfulGenomeCombos);
                }
                // Otherwise, just initialize with all combinations
                else
                {
                    // Initialize the maze/navigator map with the serialized maze and navigator data (this does the parsing)
                    mapEvaluator.Initialize(
                        runPhase == RunPhase.Initialization
                            ? staticInitializationMazes
                            : ExperimentDataHandler.GetMazeGenomeData(curExperimentConfiguration.ExperimentDictionaryID,
                                curRun, curBatch), ExperimentDataHandler.GetNavigatorGenomeData(
                                    curExperimentConfiguration.ExperimentDictionaryID, curRun, curBatch, runPhase));
                }

                // Evaluate all of the maze/navigator combinations in the batch
                mapEvaluator.RunTrajectoryEvaluations();

                if (generateSimulationResults)
                {
                    // Save the evaluation results
                    ExperimentDataHandler.WriteNavigatorMazeEvaluationData(
                        curExperimentConfiguration.ExperimentDictionaryID, curRun, curBatch, runPhase,
                        mapEvaluator.EvaluationUnits, CommitPageSize, writeResultsToDatabase);
                }

                if (generateMazeBitmaps)
                {
                    // Generate bitmaps of distinct mazes extant at the current point in time
                    ImageGenerationHandler.GenerateMazeBitmaps(baseImageOutputDirectory,
                        curExperimentConfiguration.ExperimentName, curExperimentConfiguration.ExperimentDictionaryID,
                        curRun, curBatch, mapEvaluator.EvaluationUnits);
                }

                if (generateTrajectoryBitmaps)
                {
                    // Generate bitmaps of trajectory for all successful trials
                    ImageGenerationHandler.GenerateBitmapsForSuccessfulTrials(
                        baseImageOutputDirectory, curExperimentConfiguration.ExperimentName,
                        curExperimentConfiguration.ExperimentDictionaryID,
                        curRun, curBatch, mapEvaluator.EvaluationUnits, runPhase);
                }

                if (generateTrajectoryData && runPhase != RunPhase.Initialization)
                {
                    // Write out the full trajectory of all agents through all solved mazes
                    ExperimentDataHandler.WriteTrajectoryData(curExperimentConfiguration.ExperimentDictionaryID, curRun,
                        curBatch, mapEvaluator.EvaluationUnits, CommitPageSize, writeResultsToDatabase);
                }

                // Compare trajectories of agents through maze to get quantitative sense of solution diversity
                // Mean euclidean distance will be calculated for selected trajectory against:
                // 1. Other agent trajectories in the current maze only
                // 2. Other agent trajectories on *another* maze only
                // 3. All other agent trajectories (regardless of maze)
                if (generateTrajectoryDiversityScore && runPhase != RunPhase.Initialization)
                {
                    ExperimentDataHandler.WriteTrajectoryDiversityData(
                        curExperimentConfiguration.ExperimentDictionaryID, curRun, curBatch,
                        EvaluationHandler.CalculateTrajectoryDiversity(mapEvaluator.EvaluationUnits),
                        writeResultsToDatabase);
                }

                // Only write clustering results for primary runs when the number of trajectories have surpassed
                // the minimum cluster count of 3
                if (generateNaturalClustering && runPhase != RunPhase.Initialization)
                {
                    // Extract uniform samples of maze and navigator genomes from each extant specie
                    // on which to run clustering analysis
                    var evaluationSamples = sampleClusterObservationsFromSpecies
                        ? DataManipulationUtil.ExtractEvaluationUnitSamplesFromSpecies(
                            curExperimentConfiguration.ExperimentDictionaryID, curRun, curBatch,
                            mapEvaluator.EvaluationUnits.Where(eu => eu.IsMazeSolved).ToList(), sampleSize)
                        : DataManipulationUtil.ExtractEvaluationUnitSamplesFromPopulation(
                            mapEvaluator.EvaluationUnits.Where(eu => eu.IsMazeSolved).ToList(),
                            sampleSize);

                    // Calculate natural clustering of the population trajectories at each point in time and persist
                    ExperimentDataHandler.WriteClusteringDiversityData(
                        curExperimentConfiguration.ExperimentDictionaryID, curRun, curBatch,
                        EvaluationHandler.CalculateNaturalClustering(evaluationSamples, clusterImprovementThreshold),
                        writeResultsToDatabase);
                }
            }
        }
        /// <summary>
        ///     Runs post-hoc analysis for the entire experiment run in aggregate (i.e. not per batch).  This analyzes the primary
        ///     run phase only.
        /// </summary>
        /// <param name="batchesWithGenomeData">The total number of batches containing genome data.</param>
        /// <param name="experimentParameters">Experiment configuration parameters.</param>
        /// <param name="inputNeuronCount">Count of neurons in controller input layer.</param>
        /// <param name="outputNeuronCount">Count of neurons in controller output layer.</param>
        /// <param name="curRun">The run number.</param>
        /// <param name="numRuns">The total number of runs.</param>
        /// <param name="curExperimentConfiguration">The experiment configuration parameters.</param>
        /// <param name="generateSimulationResults">Indicates whether to write out the results of the batch simulation.</param>
        /// <param name="writeResultsToDatabase">
        ///     Indicates whether to write results directly into a database (if not, results are
        ///     written to a flat file).
        /// </param>
        /// <param name="generateTrajectoryData">Indicates whether the full navigator trajectory should be simulated and persisted.</param>
        /// <param name="generateTrajectoryDiversityScore">
        ///     Indicates whether quantification of navigator trajectory diversity
        ///     should be written out.
        /// </param>
        /// <param name="generateNaturalClustering">Indicates whether the natural population clusters should be analyzed.</param>
        /// <param name="generateMazeBitmaps">Indicates whether bitmap files of the distinct mazes should be written out.</param>
        /// <param name="generateTrajectoryBitmaps">
        ///     Indicates whether bitmap files depicting the navigator trajectory should be
        ///     written out.
        /// </param>
        /// <param name="baseImageOutputDirectory">The path to the output directory for the trajectory images.</param>
        /// <param name="clusterImprovementThreshold">
        ///     The number of cluster additions that are permitted without further maximization of silhouette width.
        /// </param>
        /// <param name="specieSampleSize">The number of genomes sampled from the extant species for clustering analysis.</param>
        private static void ProcessAndLogAggregateRunResults(IList<int> batchesWithGenomeData,
            ExperimentParameters experimentParameters,
            int inputNeuronCount, int outputNeuronCount, int curRun,
            int numRuns, ExperimentDictionary curExperimentConfiguration, bool generateSimulationResults,
            bool generateTrajectoryData, bool generateTrajectoryDiversityScore, bool generateNaturalClustering,
            bool writeResultsToDatabase, bool generateMazeBitmaps, bool generateTrajectoryBitmaps,
            string baseImageOutputDirectory, int clusterImprovementThreshold, int specieSampleSize)
        {
            // Create the maze/navigator map
            MapEvaluator mapEvaluator =
                new MapEvaluator(experimentParameters, inputNeuronCount, outputNeuronCount);

            _executionLogger.Info(string.Format("Executing aggregate run analysis for run [{0}/{1}]", curRun, numRuns));

            // TODO: Might need to consider the option of pinning the mazes in memory and batching through the agents
            // TODO: as pulling both into memory simultaneously is probably going to use too much memory

            // Initialize the evaluator with the maze genomes discovered throughout the entire run
            mapEvaluator.Initialize(
                ExperimentDataHandler.GetMazeGenomeData(curExperimentConfiguration.ExperimentDictionaryID, curRun));

            // Then evaluate each agent against each maze on a batch-by-batch basis
            foreach (int curBatch in batchesWithGenomeData)
            {
                _executionLogger.Info(string.Format("Executing trajectory for batch [{0}] and run [{1}/{2}]", curBatch,
                    curRun, numRuns));

                // Initialize with the agent genomes from the current batch
                mapEvaluator.Initialize(
                    ExperimentDataHandler.GetNavigatorGenomeData(curExperimentConfiguration.ExperimentDictionaryID,
                        curRun, curBatch, RunPhase.Primary));

                // Evaluate all of the maze/navigator combinations in the batch
                mapEvaluator.RunTrajectoryEvaluations();

                if (generateSimulationResults)
                {
                    // Save the evaluation results
                    // TODO: Implement - not sure if this adds a whole lot of value beyond per-batch approach
                }

                if (generateMazeBitmaps)
                {
                    // Generate bitmaps of distinct mazes extant throughout the run
                    // TODO: Implement - not sure if this adds a whole lot of value beyond per-batch approach
                }

                if (generateTrajectoryBitmaps)
                {
                    // Generate bitmaps of trajectory for all successful trials
                    // TODO: Implement - not sure if this adds a whole lot of value beyond per-batch approach
                }

                if (generateTrajectoryData)
                {
                    // Write out the full trajectory of all agents through all solved mazes
                    // TODO: Implement - may end up removing this as it takes up an enormous amount of space
                }

                // Compare trajectories of agents through maze to get quantitative sense of solution diversity
                // Mean euclidean distance will be calculated for selected trajectory against:
                // 1. Other agent trajectories in the current maze only
                // 2. Other agent trajectories on *another* maze only
                // 3. All other agent trajectories (regardless of maze)
                if (generateTrajectoryDiversityScore)
                {
                    // TODO: Implement
                    ExperimentDataHandler.WriteTrajectoryDiversityData(
                        curExperimentConfiguration.ExperimentDictionaryID, curRun,
                        EvaluationHandler.CalculateTrajectoryDiversity(mapEvaluator.EvaluationUnits),
                        writeResultsToDatabase);
                }

                if (generateNaturalClustering)
                {
                    // Calculate natural clustering of the population trajectories throughout the run and persist
                    ExperimentDataHandler.WriteClusteringDiversityData(
                        curExperimentConfiguration.ExperimentDictionaryID, curRun,
                        EvaluationHandler.CalculateNaturalClustering(mapEvaluator.EvaluationUnits,
                            clusterImprovementThreshold), writeResultsToDatabase);
                }
            }
        }
        public override void Initialize(ExperimentDictionary experimentDictionary)
        {
            base.Initialize(experimentDictionary);

            // Read in behavior characterization
            _behaviorCharacterizationFactory = ExperimentUtils.ReadBehaviorCharacterizationFactory(
                experimentDictionary, true);

            // Read in novelty archive parameters
            _archiveAdditionThreshold = experimentDictionary.Primary_NoveltySearch_ArchiveAdditionThreshold ??
                                        default(int);
            _archiveThresholdDecreaseMultiplier =
                experimentDictionary.Primary_NoveltySearch_ArchiveThresholdDecreaseMultiplier ?? default(double);
            _archiveThresholdIncreaseMultiplier =
                experimentDictionary.Primary_NoveltySearch_ArchiveThresholdIncreaseMultiplier ?? default(double);
            _maxGenerationArchiveAddition =
                experimentDictionary.Primary_NoveltySearch_MaxGenerationsWithArchiveAddition ?? default(int);
            _maxGenerationsWithoutArchiveAddition =
                experimentDictionary.Primary_NoveltySearch_MaxGenerationsWithoutArchiveAddition ?? default(int);

            // Read in nearest neighbors for behavior distance calculations
            _nearestNeighbors = experimentDictionary.Primary_NoveltySearch_NearestNeighbors ?? default(int);

            // Read in steady-state specific parameters
            _batchSize = experimentDictionary.Primary_OffspringBatchSize ?? default(int);
            _populationEvaluationFrequency = experimentDictionary.Primary_PopulationEvaluationFrequency ?? default(int);
        }
        /// <summary>
        ///     Initialize the experiment with database configuration parameters.
        /// </summary>
        /// <param name="experimentDictionary">The handle to the experiment dictionary row pulled from the database.</param>
        public virtual void Initialize(ExperimentDictionary experimentDictionary)
        {
            // Set all properties
            Name = experimentDictionary.ExperimentName;
            DefaultPopulationSize = experimentDictionary.Primary_PopulationSize;
            Description = experimentDictionary.ExperimentName;

            // Set all internal class variables
            _activationScheme = NetworkActivationScheme.CreateAcyclicScheme();
            ComplexityRegulationStrategy = experimentDictionary.Primary_ComplexityRegulationStrategy;
            Complexitythreshold = experimentDictionary.Primary_ComplexityThreshold;
            ParallelOptions = new ParallelOptions();
            SerializeGenomeToXml = experimentDictionary.SerializeGenomeToXml;
            MaxEvaluations = (ulong) experimentDictionary.MaxEvaluations;
            MaxRestarts = experimentDictionary.MaxRestarts;

            // Set evolution/genome parameters
            NeatEvolutionAlgorithmParameters = ExperimentUtils.ReadNeatEvolutionAlgorithmParameters(
                experimentDictionary, true);
            NeatGenomeParameters = ExperimentUtils.ReadNeatGenomeParameters(experimentDictionary, true);
            NeatGenomeParameters.FeedforwardOnly = _activationScheme.AcyclicNetwork;

            // Set experiment-specific parameters
            MaxTimesteps = experimentDictionary.MaxTimesteps;
            MinSuccessDistance = experimentDictionary.MinSuccessDistance;
            MaxDistanceToTarget = experimentDictionary.MaxDistanceToTarget ?? default(int);
            MazeVariant = MazeVariantUtil.convertStringToMazeVariant(experimentDictionary.ExperimentDomainName);
        }
 /// <summary>
 ///     Reads NEAT genome parameters from the database.
 /// </summary>
 /// <param name="experimentDictionary">Reference to experiment dictionary table.</param>
 /// <param name="isPrimary">Flag indicating whether this is the primary or an initialization algorithm.</param>
 /// <returns>Initialized NEAT genome parameters.</returns>
 public static NeatGenomeParameters ReadNeatGenomeParameters(ExperimentDictionary experimentDictionary,
     bool isPrimary)
 {
     return (isPrimary
         ? new NeatGenomeParameters
         {
             InitialInterconnectionsProportion = experimentDictionary.Primary_ConnectionProportion,
             ConnectionWeightMutationProbability =
                 experimentDictionary.Primary_MutateConnectionWeightsProbability,
             AddConnectionMutationProbability = experimentDictionary.Primary_MutateAddConnectionProbability,
             AddNodeMutationProbability = experimentDictionary.Primary_MutateAddNeuronProbability,
             DeleteConnectionMutationProbability = experimentDictionary.Primary_MutateDeleteConnectionProbability,
             ConnectionWeightRange = experimentDictionary.Primary_ConnectionWeightRange
         }
         : new NeatGenomeParameters
         {
             InitialInterconnectionsProportion =
                 experimentDictionary.Initialization_ConnectionProportion ?? default(double),
             ConnectionWeightMutationProbability =
                 experimentDictionary.Initialization_MutateConnectionWeightsProbability ?? default(double),
             AddConnectionMutationProbability =
                 experimentDictionary.Initialization_MutateAddConnectionProbability ?? default(double),
             AddNodeMutationProbability =
                 experimentDictionary.Initialization_MutateAddNeuronProbability ?? default(double),
             DeleteConnectionMutationProbability =
                 experimentDictionary.Initialization_MutateDeleteConnectionProbability ?? default(double),
             ConnectionWeightRange = experimentDictionary.Initialization_ConnectionWeightRange ?? default(double)
         });
 }
 /// <summary>
 ///     Reads NEAT evolution algorithm parameters from the database.
 /// </summary>
 /// <param name="experimentDictionary">Reference to the experiment dictionary table.</param>
 /// <param name="isPrimary">Flag indicating whether this is the primary or an initialization algorithm.</param>
 /// <returns>Initialized NEAT evolution algorithm parameters.</returns>
 public static NeatEvolutionAlgorithmParameters ReadNeatEvolutionAlgorithmParameters(
     ExperimentDictionary experimentDictionary,
     bool isPrimary)
 {
     return (isPrimary
         ? new NeatEvolutionAlgorithmParameters
         {
             SpecieCount = experimentDictionary.Primary_NumSpecies,
             InterspeciesMatingProportion = experimentDictionary.Primary_InterspeciesMatingProbability,
             ElitismProportion = experimentDictionary.Primary_ElitismProportion,
             SelectionProportion = experimentDictionary.Primary_SelectionProportion,
             OffspringAsexualProportion = experimentDictionary.Primary_AsexualProbability,
             OffspringSexualProportion = experimentDictionary.Primary_CrossoverProbability
         }
         : new NeatEvolutionAlgorithmParameters
         {
             SpecieCount = experimentDictionary.Initialization_NumSpecies ?? default(int),
             InterspeciesMatingProportion =
                 experimentDictionary.Initialization_InterspeciesMatingProbability ?? default(double),
             ElitismProportion = experimentDictionary.Initialization_ElitismProportion ?? default(double),
             SelectionProportion = experimentDictionary.Initialization_SelectionProportion ?? default(double),
             OffspringAsexualProportion =
                 experimentDictionary.Initialization_AsexualProbability ?? default(double),
             OffspringSexualProportion =
                 experimentDictionary.Initialization_CrossoverProbability ?? default(double)
         });
 }
        /// <summary>
        ///     Reads behavior characterization parameters from the database.
        /// </summary>
        /// <param name="experiment">The experiment dictionary entity.</param>
        /// <param name="isPrimary">
        ///     Boolean flag indicating whether this is the primary behavior characterization or the behavior
        ///     characterization used for experiment initialization.
        /// </param>
        /// <returns></returns>
        public static IBehaviorCharacterizationFactory ReadBehaviorCharacterizationFactory(
            ExperimentDictionary experiment,
            bool isPrimary)
        {
            // Read behavior characterization
            String behaviorCharacterizationName = isPrimary
                ? experiment.Primary_BehaviorCharacterizationName
                : experiment.Initialization_BehaviorCharacterizationName;

            // Ensure that the behavior was specified
            if (behaviorCharacterizationName == null)
            {
                throw new ArgumentException("Missing or invalid BehaviorConfig settings.");
            }

            IMinimalCriteria minimalCriteria = null;

            // Get the appropriate minimal criteria type
            MinimalCriteriaType mcType = BehaviorCharacterizationUtil.ConvertStringToMinimalCriteria(isPrimary
                ? experiment.Primary_MCS_MinimalCriteriaName
                : experiment.Initialization_MCS_MinimalCriteriaName);

            // Starting location used in most criterias
            double xStart, yStart;

            switch (mcType)
            {
                case MinimalCriteriaType.EuclideanLocation:

                    // TODO: Not implemented at the database layer yet

                    break;

                case MinimalCriteriaType.FixedPointEuclideanDistance:

                    // Read in the starting coordinates and the minimum required distance traveled
                    xStart = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaStartX ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaStartX ?? default(double);
                    yStart = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaStartY ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaStartY ?? default(double);
                    double minimumDistanceTraveled = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaThreshold ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaThreshold ?? default(double);

                    // Set the euclidean distance minimal criteria on the behavior characterization
                    minimalCriteria = new FixedPointEuclideanDistanceCriteria(xStart, yStart,
                        minimumDistanceTraveled);

                    break;

                case MinimalCriteriaType.Mileage:

                    // Read in the starting coordinates and minimum required total distance traveled (mileage)
                    xStart = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaStartX ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaStartX ?? default(double);
                    yStart = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaStartY ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaStartY ?? default(double);
                    double minimumMileage = isPrimary
                        ? experiment.Primary_MCS_MinimalCriteriaThreshold ?? default(double)
                        : experiment.Initialization_MCS_MinimalCriteriaThreshold ?? default(double);

                    // Set the mileage minimal criteria on the behavior characterization
                    minimalCriteria = new MileageCriteria(xStart, yStart, minimumMileage);

                    break;
            }

            // Parse and generate the appropriate behavior characterization factory
            IBehaviorCharacterizationFactory behaviorCharacterizationFactory =
                BehaviorCharacterizationUtil.GenerateBehaviorCharacterizationFactory(behaviorCharacterizationName,
                    minimalCriteria);

            return behaviorCharacterizationFactory;
        }