/// <summary>
        ///     Retrieves the batches during a given run of a given experiment that have associated genome data.
        /// </summary>
        /// <param name="experimentId">The experiment that was executed.</param>
        /// <param name="run">The run number of the given experiment.</param>
        /// <param name="runPhase">The run phase (i.e. initialization or primary) for which to get the associated batches.</param>
        /// <returns>The collection of batch numbers in the given experiment/run that have associated genome data.</returns>
        public static IList<int> GetBatchesWithGenomeData(int experimentId, int run, RunPhase runPhase)
        {
            IList<int> batchesWithGenomeData = null;
            bool querySuccess = false;
            int retryCnt = 0;

            while (querySuccess == false && retryCnt <= MaxQueryRetryCnt)
            {
                try
                {
                    // Query for the distinct batches in the current run of the given experiment
                    using (ExperimentDataEntities context = new ExperimentDataEntities())
                    {
                        batchesWithGenomeData = context.CoevolutionMCSNavigatorExperimentGenomes.Where(
                            expData =>
                                expData.ExperimentDictionaryID == experimentId && expData.Run == run &&
                                expData.RunPhase.RunPhaseName == runPhase.ToString())
                            .Select(row => row.Generation)
                            .Distinct().OrderBy(row => row).ToList();
                    }

                    querySuccess = true;
                }
                catch (Exception e)
                {
                    HandleQueryException(MethodBase.GetCurrentMethod().ToString(), retryCnt++, e);
                }
            }

            return batchesWithGenomeData;
        }
        /// <summary>
        ///     Retrieves the primary key associated with the given run phase.
        /// </summary>
        /// <param name="runPhase">The run phase for which to lookup the key.</param>
        /// <returns>The key (in the "RunPhase" database table) for the given run phase object.</returns>
        private static int GetRunPhaseKey(RunPhase runPhase)
        {
            int runPhaseKey = 0;
            bool querySuccess = false;
            int retryCnt = 0;

            while (querySuccess == false && retryCnt <= MaxQueryRetryCnt)
            {
                try
                {
                    // Query for the run phase key
                    using (ExperimentDataEntities context = new ExperimentDataEntities())
                    {
                        runPhaseKey =
                            context.RunPhases.First(runPhaseData => runPhaseData.RunPhaseName == runPhase.ToString())
                                .RunPhaseID;
                    }

                    querySuccess = true;
                }
                catch (Exception e)
                {
                    HandleQueryException(MethodBase.GetCurrentMethod().ToString(), retryCnt++, e);
                }
            }

            return runPhaseKey;
        }
        /// <summary>
        ///     Retrieves specie assignments for the given navigator genome IDs and experiment, run, and batch.
        /// </summary>
        /// <param name="experimentId">The experiment that was executed.</param>
        /// <param name="run">The run number of the given experiment.</param>
        /// <param name="batch">The batch number of the given run.</param>
        /// <param name="runPhase">Indicates whether this is part of the initialization or primary experiment phase.</param>
        /// <param name="navigatorGenomeIds">The navigator genome IDs to group into species.</param>
        /// <returns>Specie assignments for the given navigator genome IDs and experiment, run, and batch.</returns>
        public static List<SpecieGenomesGroup> GetSpecieAssignmentsForNavigatorGenomeIds(int experimentId, int run,
            int batch, RunPhase runPhase, IList<int> navigatorGenomeIds)
        {
            var navigatorSpecieGenomesGroups = new List<SpecieGenomesGroup>();
            bool querySuccess = false;
            int retryCnt = 0;

            while (querySuccess == false && retryCnt <= MaxQueryRetryCnt)
            {
                try
                {
                    using (ExperimentDataEntities context = new ExperimentDataEntities())
                    {
                        // Get the species to which the navigators are assigned and group by species
                        var specieGroupedNavigators =
                            context.CoevolutionMCSNavigatorExperimentGenomes.Where(
                                nav =>
                                    experimentId == nav.ExperimentDictionaryID && run == nav.Run &&
                                    batch == nav.Generation && runPhase.ToString().Equals(nav.RunPhase.RunPhaseName) &&
                                    navigatorGenomeIds.Contains(nav.GenomeID))
                                .Select(nav => new {nav.SpecieID, nav.GenomeID})
                                .GroupBy(nav => nav.SpecieID)
                                .ToList();

                        // Build list of navigator specie genome groups
                        navigatorSpecieGenomesGroups.AddRange(
                            specieGroupedNavigators.Select(
                                specieGenomesGroup =>
                                    new SpecieGenomesGroup((int) specieGenomesGroup.Key,
                                        specieGenomesGroup.Select(gg => gg.GenomeID).ToList())));
                    }

                    querySuccess = true;
                }
                catch (Exception e)
                {
                    HandleQueryException(MethodBase.GetCurrentMethod().ToString(), retryCnt++, e);
                }
            }

            return navigatorSpecieGenomesGroups;
        }
        /// <summary>
        ///     Retrieves the navigator genome data (i.e. evaluation statistics and XML) for a particular batch of a given
        ///     run/experiemnt.
        /// </summary>
        /// <param name="experimentId">The experiment that was executed.</param>
        /// <param name="run">The run number of the given experiment.</param>
        /// <param name="batch">The batch number of the given run.</param>
        /// <param name="runPhase">
        ///     Indicates whether this is part of the initialization or primary experiment phase.
        /// </param>
        /// <param name="navigatorGenomeIds">The navigator genome IDs by which to filter the query result set (optional).</param>
        /// <returns>The navigator genome data.</returns>
        public static IList<CoevolutionMCSNavigatorExperimentGenome> GetNavigatorGenomeData(int experimentId, int run,
            int batch, RunPhase runPhase, IList<int> navigatorGenomeIds = null)
        {
            IList<CoevolutionMCSNavigatorExperimentGenome> navigatorGenomes = null;
            bool querySuccess = false;
            int retryCnt = 0;

            while (querySuccess == false && retryCnt <= MaxQueryRetryCnt)
            {
                try
                {
                    using (ExperimentDataEntities context = new ExperimentDataEntities())
                    {
                        // Query for navigator genomes logged during the current batch and constrained by the given set of genome IDs
                        if (navigatorGenomeIds != null)
                        {
                            navigatorGenomes =
                                context.CoevolutionMCSNavigatorExperimentGenomes.Where(
                                    expData =>
                                        navigatorGenomeIds.Contains(expData.GenomeID) &&
                                        expData.ExperimentDictionaryID == experimentId && expData.Run == run &&
                                        expData.Generation == batch &&
                                        expData.RunPhase.RunPhaseName == runPhase.ToString())
                                    .ToList();
                        }
                        // Otherwise, query for all navigator genomes logged during the current batch
                        else
                        {
                            navigatorGenomes =
                                context.CoevolutionMCSNavigatorExperimentGenomes.Where(
                                    expData =>
                                        expData.ExperimentDictionaryID == experimentId && expData.Run == run &&
                                        expData.Generation == batch &&
                                        expData.RunPhase.RunPhaseName == runPhase.ToString())
                                    .ToList();
                        }
                    }

                    querySuccess = true;
                }
                catch (Exception e)
                {
                    HandleQueryException(MethodBase.GetCurrentMethod().ToString(), retryCnt++, e);
                }
            }

            return navigatorGenomes;
        }
        /// <summary>
        ///     Retrieves the navigator genome data (i.e. evaluation statistics and XML) for the entirety of a given
        ///     run/experiment.
        /// </summary>
        /// <param name="experimentId">The experiment that was executed.</param>
        /// <param name="run">The run number of the given experiment.</param>
        /// <param name="runPhase">
        ///     Indicates whether this is part of the initialization or primary experiment phase.
        /// </param>
        /// <returns>The navigator genome data.</returns>
        public static IList<CoevolutionMCSNavigatorExperimentGenome> GetNavigatorGenomeData(int experimentId, int run,
            RunPhase runPhase)
        {
            IList<CoevolutionMCSNavigatorExperimentGenome> navigatorGenomes = null;
            bool querySuccess = false;
            int retryCnt = 0;

            while (querySuccess == false && retryCnt <= MaxQueryRetryCnt)
            {
                try
                {
                    // Query for the distinct navigator genomes logged during the run
                    using (ExperimentDataEntities context = new ExperimentDataEntities())
                    {
                        navigatorGenomes = context.CoevolutionMCSNavigatorExperimentGenomes.Where(
                            expData =>
                                expData.ExperimentDictionaryID == experimentId && expData.Run == run &&
                                expData.RunPhase.RunPhaseName == runPhase.ToString())
                            .GroupBy(expData => expData.GenomeID)
                            .Select(expDataGroup => expDataGroup.OrderBy(expData => expData.Generation).FirstOrDefault())
                            .ToList();
                    }

                    querySuccess = true;
                }
                catch (Exception e)
                {
                    HandleQueryException(MethodBase.GetCurrentMethod().ToString(), retryCnt++, e);
                }
            }

            return navigatorGenomes;
        }
        /// <summary>
        ///     Writes the given evaluation results to a flat file.
        /// </summary>
        /// <param name="experimentId">The experiment that was executed.</param>
        /// <param name="run">The run number of the given experiment.</param>
        /// <param name="batch">The batch number of the given run.</param>
        /// <param name="runPhase">
        ///     Indicates whether this is part of the initialization or primary experiment phase.
        /// </param>
        /// <param name="evaluationUnits">The evaluation results to persist.</param>
        private static void WriteNavigatorMazeEvaluationDataToFile(int experimentId, int run, int batch,
            RunPhase runPhase, IList<MazeNavigatorEvaluationUnit> evaluationUnits)
        {
            // Make sure the file writer actually exists before attempting to write to it
            if (FileWriters.ContainsKey(OutputFileType.NavigatorMazeEvaluationData) == false)
            {
                throw new Exception(
                    string.Format("Cannot write to output stream as no file writer of type {0} has been created.",
                        OutputFileType.NavigatorMazeEvaluationData));
            }

            // Loop through the evaluation units and write each row
            foreach (MazeNavigatorEvaluationUnit evaluationUnit in evaluationUnits)
            {
                FileWriters[OutputFileType.NavigatorMazeEvaluationData].WriteLine(string.Join(FileDelimiter,
                    new List<string>
                    {
                        experimentId.ToString(),
                        run.ToString(),
                        batch.ToString(),
                        runPhase.ToString(),
                        evaluationUnit.MazeId.ToString(),
                        evaluationUnit.AgentId.ToString(),
                        evaluationUnit.IsMazeSolved.ToString(),
                        evaluationUnit.NumTimesteps.ToString()
                    }));
            }

            // Immediately flush to the output file
            FileWriters[OutputFileType.NavigatorMazeEvaluationData].Flush();
        }