示例#1
0
        /// <summary>
        ///     The map evaluator constructor.
        /// </summary>
        /// <param name="experimentParameters">The experiment definition and control parameters.</param>
        /// <param name="agentInputNeuronCount">The number of input neurons in the agent neural controller.</param>
        /// <param name="agentOutputNeuronCount">The number of output neurons in the agent neural controller.</param>
        public MapEvaluator(ExperimentParameters experimentParameters, int agentInputNeuronCount,
                            int agentOutputNeuronCount)
        {
            // Create the NEAT genome (agent) decoder - acyclic activation is always used
            _agentDecoder = new NeatGenomeDecoder(CreateActivationScheme(experimentParameters.ActivationScheme,
                                                                         experimentParameters.ActivationIters, experimentParameters.ActivationDeltaThreshold));

            // Create the maze decoder
            _mazeDecoder = new MazeDecoder(experimentParameters.MazeScaleMultiplier);

            // Initialize evaluation units
            EvaluationUnits = new List <MazeNavigatorEvaluationUnit>();

            // Create maze factory with default dimensions (NEAT factory will be set later based on structure of first
            // genome encountered)
            _mazeGenomeFactory = new MazeGenomeFactory(experimentParameters.MazeHeight, experimentParameters.MazeWidth,
                                                       experimentParameters.MazeQuadrantHeight, experimentParameters.MazeQuadrantWidth);
            _neatGenomeFactory = new NeatGenomeFactory(agentInputNeuronCount, agentOutputNeuronCount);

            // Set experiment parameters
            _experimentParameters = experimentParameters;

            // Create new agent ID list and maze ID/structure map
            _agentGenomeIds     = new List <int>();
            _mazeIdStructureMap = new Dictionary <int, MazeStructure>();
        }
        /// <summary>
        ///     Converts the maze genome into a maze structure and prints it to a bitmap file.
        /// </summary>
        /// <param name="mazeGenome">The maze genome to convert and print.</param>
        /// <param name="mazeImageName">The name of the maze output file.</param>
        private static void PrintMazeToFile(MazeGenome mazeGenome, string mazeImageName)
        {
            // Read in the maze decode parameters
            int    mazeScaleFactor           = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeScaleFactor]);
            string mazeBitmapOutputDirectory = _executionConfiguration[ExecutionParameter.BitmapOutputBaseDirectory];

            // Instantiate the maze genome decoder
            MazeDecoder mazeDecoder = new MazeDecoder(mazeScaleFactor);

            // Decode the maze to get a maze structure
            MazeStructure mazeStructure = mazeDecoder.Decode(mazeGenome);

            // Create pen and initialize bitmap canvas
            Pen    blackPen   = new Pen(Color.Black, 0.0001f);
            Bitmap mazeBitmap = new Bitmap(mazeStructure.ScaledMazeWidth + 1, mazeStructure.ScaledMazeHeight + 1);

            using (Graphics graphics = Graphics.FromImage(mazeBitmap))
            {
                // Fill with white
                Rectangle imageSize = new Rectangle(0, 0, mazeStructure.ScaledMazeWidth + 1,
                                                    mazeStructure.ScaledMazeHeight + 1);
                graphics.FillRectangle(Brushes.White, imageSize);

                // Draw start and end points
                graphics.FillEllipse(Brushes.Green, mazeStructure.StartLocation.X, mazeStructure.StartLocation.Y, 5, 5);
                graphics.FillEllipse(Brushes.Red, mazeStructure.TargetLocation.X, mazeStructure.TargetLocation.Y, 5, 5);

                // Draw all of the walls
                foreach (MazeStructureWall wall in mazeStructure.Walls)
                {
                    // Convert line start/end points to Point objects from drawing namespace
                    Point startPoint = new Point(wall.StartMazeStructurePoint.X, wall.StartMazeStructurePoint.Y);
                    Point endPoint   = new Point(wall.EndMazeStructurePoint.X, wall.EndMazeStructurePoint.Y);

                    // Draw wall
                    graphics.DrawLine(blackPen, startPoint, endPoint);
                }
            }

            // Save the bitmap image
            mazeBitmap.Save(Path.Combine(mazeBitmapOutputDirectory, mazeImageName));
        }
        public void MutatedGenomeDecodeTest()
        {
            int scaleMultiplier = 16;

            // Mock up maze genome (just use null genome factory)
            MazeGenome mazeGenome = new MazeGenome(new MazeGenomeFactory(), 1, 1);

            MazeDecoder mazeDecoder = new MazeDecoder(20, 20, scaleMultiplier);

            MazeStructure mazeGrid = mazeDecoder.Decode(mazeGenome);

            DisplayMaze(mazeGrid.MazeArray);

            mazeGrid.ConvertGridArrayToWalls(mazeGrid.MazeArray);

            uint birthGeneration = 1;

            do
            {
                // Generate an offspring (perform mutation)
                mazeGenome.CreateOffspring(++birthGeneration);

                if (birthGeneration%100 == 0)
                {
                    mazeGrid = mazeDecoder.Decode(mazeGenome);

                    PrintBitmapMaze(mazeGrid.Walls, 20*scaleMultiplier, 20*scaleMultiplier, (int) birthGeneration);
                }
            } while (birthGeneration < 1000000);

            // Create the maze decoder
            mazeDecoder = new MazeDecoder(20, 20, scaleMultiplier);

            mazeGrid = mazeDecoder.Decode(mazeGenome);

            DisplayMaze(mazeGrid.MazeArray);

            mazeGrid.ConvertGridArrayToWalls(mazeGrid.MazeArray);

            //PrintBitmapMaze(mazeGrid.Walls, 20 * scaleMultiplier, 20 * scaleMultiplier);
        }
        public void HardCodedGenomeDecodeTest()
        {
            // Mock up maze genome (just use null genome factory)
            MazeGenome mazeGenome = new MazeGenome((MazeGenomeFactory) null, 1, 1);

            // Add some genes
            mazeGenome.GeneList.Add(new MazeGene(1, 0.6, 0.3, false));
            mazeGenome.GeneList.Add(new MazeGene(2, 0.7, 0.4, false));
            mazeGenome.GeneList.Add(new MazeGene(3, 0.3, 0.8, true));
            mazeGenome.GeneList.Add(new MazeGene(4, 0.9, 0.2, false));
            mazeGenome.GeneList.Add(new MazeGene(5, 0.5, 0.3, false));
            mazeGenome.GeneList.Add(new MazeGene(6, 0.2, 0.5, false));
            mazeGenome.GeneList.Add(new MazeGene(7, 0.4, 0.1, true));
            mazeGenome.GeneList.Add(new MazeGene(8, 0.7, 0.8, true));
            mazeGenome.GeneList.Add(new MazeGene(9, 0.3, 0.2, false));

            // Create the maze decoder
            MazeDecoder mazeDecoder = new MazeDecoder(20, 20);

            MazeStructure mazeGrid = mazeDecoder.Decode(mazeGenome);

            //DisplayMaze(mazeGrid.MazeArray);
        }
示例#5
0
        /// <summary>
        ///     The map evaluator constructor.
        /// </summary>
        /// <param name="experimentParameters">The experiment definition and control parameters.</param>
        /// <param name="agentInputNeuronCount">The number of input neurons in the agent neural controller.</param>
        /// <param name="agentOutputNeuronCount">The number of output neurons in the agent neural controller.</param>
        public MapEvaluator(ExperimentParameters experimentParameters, int agentInputNeuronCount,
            int agentOutputNeuronCount)
        {
            // Create the NEAT genome (agent) decoder - acyclic activation is always used
            _agentDecoder = new NeatGenomeDecoder(NetworkActivationScheme.CreateAcyclicScheme());

            // Create the maze decoder
            _mazeDecoder = new MazeDecoder(experimentParameters.MazeHeight, experimentParameters.MazeWidth,
                experimentParameters.MazeScaleMultiplier);

            // Initialize evaluation units
            EvaluationUnits = new List<MazeNavigatorEvaluationUnit>();

            // Create default maze factory (NEAT factory will be set later based on structure of first genome encountered)
            _mazeGenomeFactory = new MazeGenomeFactory();
            _neatGenomeFactory = new NeatGenomeFactory(agentInputNeuronCount, agentOutputNeuronCount);

            // Set experiment parameters
            _experimentParameters = experimentParameters;

            // Create new agent ID list and maze ID/structure map
            _agentGenomeIds = new List<int>();
            _mazeIdStructureMap = new Dictionary<int, MazeStructure>();
        }
        private static void Main(string[] args)
        {
            // TODO: Refactor all of this to be more configurable - just hard-coding everything now to get results

            // Base path for maze bitmap output
            string mazeImageBase =
                @"F:\User Data\Jonathan\Documents\school\Jonathan\Graduate\PhD\Minimal Criteria Search\Analysis\Coevolution MCS\Images\Final Batch Mazes";

            // These are the experiments for which we want to get mazes in the final population
            List<string> experimentNames = new List<string>
            {
                "Coevolution MCS with Maze Initialization 9",
                "Coevolution MCS with Maze Initialization 10",
                "Coevolution MCS with Maze Initialization 11",
                "Coevolution MCS with Maze Initialization 12",
                "Coevolution MCS with Maze Initialization 13",
                "Coevolution MCS with Maze Initialization 14",
                "Coevolution MCS with Maze Initialization 15",
                "Coevolution MCS with Maze Initialization 16"
            };

            // Setup maze decoder with hard-coded height/width and scale multiplier
            MazeDecoder mazeDecoder = new MazeDecoder(10, 10, 32);

            // Create default maze genome factory
            MazeGenomeFactory mazeGenomeFactory = new MazeGenomeFactory();

            foreach (string experimentName in experimentNames)
            {
                // Get the current experiment configuration
                ExperimentDictionary curExperiment = ExperimentDataHandler.LookupExperimentConfiguration(experimentName);

                // Get the total number of runs of the experiment
                int numRuns = ExperimentDataHandler.GetNumRuns(curExperiment.ExperimentDictionaryID);

                for (int runIdx = 1; runIdx <= numRuns; runIdx++)
                {
                    // Get the total number of batches in the run
                    int numBatches = ExperimentDataHandler.GetNumBatchesForRun(curExperiment.ExperimentDictionaryID,
                        runIdx);

                    // Get the maze population extant in the last batch of the run
                    IList<string> mazePopulationGenomes =
                        ExperimentDataHandler.GetMazeGenomeXml(curExperiment.ExperimentDictionaryID, runIdx, numBatches);

                    // Build output directory
                    string imageOutputDirectory = Path.Combine(mazeImageBase,
                        string.Format("ExperimentName_{0}", curExperiment.ExperimentName),
                        string.Format("Run_{0}", runIdx));

                    if (Directory.Exists(imageOutputDirectory) == false)
                    {
                        Directory.CreateDirectory(imageOutputDirectory);
                    }

                    // Decode all genomes and render image of structure
                    foreach (string mazeGenomeStr in mazePopulationGenomes)
                    {
                        MazeGenome curMazeGenome;

                        // Unmarshall to maze genome object
                        using (XmlReader xmlReader = XmlReader.Create(new StringReader(mazeGenomeStr)))
                        {
                            curMazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, mazeGenomeFactory);
                        }

                        // Generate maze bitmap image
                        ImageGenerationHandler.GenerateMazeStructureImage(
                            Path.Combine(imageOutputDirectory,
                                string.Format("ExperimentID_{0}_Run_{1}_MazeID_{2}.bmp",
                                    curExperiment.ExperimentDictionaryID, runIdx, curMazeGenome.Id)),
                            mazeDecoder.Decode(curMazeGenome));
                    }
                }
            }
        }
        /// <summary>
        ///     Creates the evolution algorithm container using the given factories and genome lists.
        /// </summary>
        /// <param name="genomeFactory1">The agent genome factory.</param>
        /// <param name="genomeFactory2">The maze genome factory.</param>
        /// <param name="genomeList1">The agent genome list.</param>
        /// <param name="genomeList2">The maze genome list.</param>
        /// <returns>The instantiated coevolution algorithm container.</returns>
        public override ICoevolutionAlgorithmContainer<NeatGenome, MazeGenome> CreateCoevolutionAlgorithmContainer(
            IGenomeFactory<NeatGenome> genomeFactory1,
            IGenomeFactory<MazeGenome> genomeFactory2, List<NeatGenome> genomeList1, List<MazeGenome> genomeList2)
        {
            List<NeatGenome> seedAgentPopulation = new List<NeatGenome>();

            // Compute the maze max complexity
            ((MazeGenomeFactory) genomeFactory2).MaxComplexity = MazeUtils.DetermineMaxPartitions(_mazeHeight,
                _mazeWidth, 200);

            // Create maze decoder to decode initialization mazes
            MazeDecoder mazeDecoder = new MazeDecoder(_mazeHeight, _mazeWidth, _mazeScaleMultiplier);

            // Loop through every maze and evolve the requisite number of viable genomes that solve it
            for (int idx = 0; idx < genomeList2.Count; idx++)
            {
                Console.WriteLine(@"Evolving viable agents for maze population index {0} and maze ID {1}", idx,
                    genomeList2[idx].Id);

                // Evolve the number of agents required to meet the success MC for the current maze
                List<NeatGenome> viableMazeAgents = EvolveViableAgents(genomeFactory1, genomeList1.ToList(),
                    mazeDecoder.Decode(genomeList2[idx]));

                // Add the viable agent genomes who solve the current maze (but avoid adding duplicates, as identified by the genome ID)
                // Note that it's fine to have multiple mazes solved by the same agent, so in this case, we'll leave the agent
                // in the pool of seed agent genomes
                foreach (
                    NeatGenome viableMazeAgent in
                        viableMazeAgents.Where(
                            viableMazeAgent =>
                                seedAgentPopulation.Select(sap => sap.Id).Contains(viableMazeAgent.Id) == false))
                {
                    seedAgentPopulation.Add(viableMazeAgent);
                }
            }

            // If we still lack the genomes to fill out agent specie count while still satisfying the maze MC,
            // iteratively pick a random maze and evolve agents on that maze until we reach the requisite number
            while (seedAgentPopulation.ToList().Count < _numAgentSuccessCriteria*AgentNumSpecies)
            {
                FastRandom rndMazePicker = new FastRandom();

                // Pick a random maze on which to evolve agent(s)
                MazeGenome mazeGenome = genomeList2[rndMazePicker.Next(genomeList2.Count - 1)];

                Console.WriteLine(
                    @"Continuing viable agent evolution on maze {0}, with {1} of {2} required agents in place",
                    mazeGenome.Id, seedAgentPopulation.Count, (_numAgentSuccessCriteria*AgentNumSpecies));

                // Evolve the number of agents required to meet the success MC for the maze
                List<NeatGenome> viableMazeAgents = EvolveViableAgents(genomeFactory1, genomeList1.ToList(),
                    mazeDecoder.Decode(mazeGenome));

                // Iterate through each viable agent and remove them if they've already solved a maze or add them to the list
                // of viable agents if they have not
                foreach (NeatGenome viableMazeAgent in viableMazeAgents)
                {
                    // If they agent has already solved maze and is in the list of viable agents, remove that agent
                    // from the pool of seed genomes (this is done because here, we're interested in getting unique
                    // agents and want to avoid an endless loop wherein the same viable agents are returned)
                    if (seedAgentPopulation.Select(sap => sap.Id).Contains(viableMazeAgent.Id))
                    {
                        genomeList1.Remove(viableMazeAgent);
                    }
                    // Otherwise, add that agent to the list of viable agents
                    else
                    {
                        seedAgentPopulation.Add(viableMazeAgent);
                    }
                }
            }

            // Set dummy fitness so that seed maze(s) will be marked as evaluated
            foreach (MazeGenome mazeGenome in genomeList2)
            {
                mazeGenome.EvaluationInfo.SetFitness(0);
            }

            // Reset primary NEAT genome parameters on agent genome factory
            ((NeatGenomeFactory) genomeFactory1).ResetNeatGenomeParameters(NeatGenomeParameters);

            // Create the NEAT (i.e. navigator) queueing evolution algorithm
            AbstractEvolutionAlgorithm<NeatGenome> neatEvolutionAlgorithm =
                new MultiQueueNeatEvolutionAlgorithm<NeatGenome>(
                    new NeatEvolutionAlgorithmParameters
                    {
                        SpecieCount = AgentNumSpecies,
                        MaxSpecieSize = AgentDefaultPopulationSize/AgentNumSpecies
                    },
                    new ParallelKMeansClusteringStrategy<NeatGenome>(new ManhattanDistanceMetric(1.0, 0.0, 10.0),
                        ParallelOptions), null, NavigatorBatchSize, RunPhase.Primary, _navigatorEvolutionDataLogger,
                    _navigatorLogFieldEnableMap, _navigatorPopulationGenomesDataLogger, _populationLoggingBatchInterval);

            // Create the maze queueing evolution algorithm
            AbstractEvolutionAlgorithm<MazeGenome> mazeEvolutionAlgorithm =
                new MultiQueueNeatEvolutionAlgorithm<MazeGenome>(
                    new NeatEvolutionAlgorithmParameters
                    {
                        SpecieCount = MazeNumSpecies,
                        MaxSpecieSize = MazeDefaultPopulationSize/MazeNumSpecies
                    },
                    new ParallelKMeansClusteringStrategy<MazeGenome>(new ManhattanDistanceMetric(1.0, 0.0, 10.0),
                        ParallelOptions), null, MazeBatchSize, RunPhase.Primary, _mazeEvolutionDataLogger,
                    _mazeLogFieldEnableMap, _mazePopulationGenomesDataLogger, _populationLoggingBatchInterval);

            // Create the maze phenome evaluator
            IPhenomeEvaluator<MazeStructure, BehaviorInfo> mazeEvaluator = new MazeEnvironmentMCSEvaluator(
                _maxTimesteps, _minSuccessDistance, BehaviorCharacterizationFactory, _numAgentSuccessCriteria, 0);

            // Create navigator phenome evaluator
            IPhenomeEvaluator<IBlackBox, BehaviorInfo> navigatorEvaluator = new MazeNavigatorMCSEvaluator(
                _maxTimesteps, _minSuccessDistance, BehaviorCharacterizationFactory, _numMazeSuccessCriteria);

            // Create maze genome decoder
            IGenomeDecoder<MazeGenome, MazeStructure> mazeGenomeDecoder = new MazeDecoder(_mazeHeight, _mazeWidth,
                _mazeScaleMultiplier);

            // Create navigator genome decoder
            IGenomeDecoder<NeatGenome, IBlackBox> navigatorGenomeDecoder = new NeatGenomeDecoder(ActivationScheme);

            // Create the maze genome evaluator
            IGenomeEvaluator<MazeGenome> mazeFitnessEvaluator =
                new ParallelGenomeBehaviorEvaluator<MazeGenome, MazeStructure>(mazeGenomeDecoder, mazeEvaluator,
                    SelectionType.Queueing, SearchType.MinimalCriteriaSearch, ParallelOptions);

            // Create navigator genome evaluator
            IGenomeEvaluator<NeatGenome> navigatorFitnessEvaluator =
                new ParallelGenomeBehaviorEvaluator<NeatGenome, IBlackBox>(navigatorGenomeDecoder, navigatorEvaluator,
                    SelectionType.Queueing, SearchType.MinimalCriteriaSearch, ParallelOptions);

            // Create the coevolution container
            ICoevolutionAlgorithmContainer<NeatGenome, MazeGenome> coevolutionAlgorithmContainer =
                new CoevolutionAlgorithmContainer<NeatGenome, MazeGenome>(neatEvolutionAlgorithm, mazeEvolutionAlgorithm);

            // Initialize the container and component algorithms
            coevolutionAlgorithmContainer.Initialize(navigatorFitnessEvaluator, genomeFactory1, seedAgentPopulation,
                AgentDefaultPopulationSize, mazeFitnessEvaluator, genomeFactory2, genomeList2, MazeDefaultPopulationSize,
                MaxGenerations, MaxEvaluations);

            return coevolutionAlgorithmContainer;
        }
示例#8
0
        /// <summary>
        ///     Evolves the requisite number of agents to meet the MC for each maze in the initial population. This is performed
        ///     using a non-MCC based algorithm (such as fitness or novelty search).
        /// </summary>
        /// <param name="agentPopulation">The agents (NEAT genomes) in the initial, randomly generated population.</param>
        /// <param name="mazePopulation">The mazes in the initial population (either randomly generated or read from a file).</param>
        /// <param name="agentGenomeFactory">The factory class for producing agent (NEAT) genomes.</param>
        /// <param name="numAgents">The number of seed agents to evolve.</param>
        /// <returns>
        ///     The list of viable agents, each of whom is able to solve at least one of the initial mazes and, in totality,
        ///     meet the MC for solving each of the mazes.
        /// </returns>
        protected List <NeatGenome> EvolveSeedAgents(List <NeatGenome> agentPopulation, List <MazeGenome> mazePopulation,
                                                     IGenomeFactory <NeatGenome> agentGenomeFactory, int numAgents)
        {
            var seedAgentPopulation = new List <NeatGenome>();

            // Create maze decoder to decode initialization mazes
            var mazeDecoder = new MazeDecoder(MazeScaleMultiplier);

            // Loop through every maze and evolve the requisite number of viable genomes that solve it
            for (var idx = 0; idx < mazePopulation.Count; idx++)
            {
                Console.WriteLine(@"Evolving viable agents for maze population index {0} and maze ID {1}", idx,
                                  mazePopulation[idx].Id);

                // Evolve the number of agents required to meet the success MC for the current maze
                var viableMazeAgents = _mazeNavigationInitializer.EvolveViableAgents(agentGenomeFactory,
                                                                                     agentPopulation.ToList(), mazeDecoder.Decode(mazePopulation[idx]), _maxInitializationEvaluations,
                                                                                     ActivationScheme, ParallelOptions);

                // Add the viable agent genomes who solve the current maze (but avoid adding duplicates, as identified by the genome ID)
                // Note that it's fine to have multiple mazes solved by the same agent, so in this case, we'll leave the agent
                // in the pool of seed agent genomes
                foreach (
                    var viableMazeAgent in
                    viableMazeAgents.Where(
                        viableMazeAgent =>
                        seedAgentPopulation.Select(sap => sap.Id).Contains(viableMazeAgent.Id) == false))
                {
                    seedAgentPopulation.Add(viableMazeAgent);
                }
            }

            // If we still lack the genomes to fill out seed agent gnome count while still satisfying the maze MC,
            // iteratively pick a random maze and evolve agents on that maze until we reach the requisite number
            while (seedAgentPopulation.ToList().Count < numAgents)
            {
                var rndMazePicker = RandomDefaults.CreateRandomSource();

                // Pick a random maze on which to evolve agent(s)
                var mazeGenome = mazePopulation[rndMazePicker.Next(mazePopulation.Count - 1)];

                Console.WriteLine(
                    @"Continuing viable agent evolution on maze {0}, with {1} of {2} required agents in place",
                    mazeGenome.Id, seedAgentPopulation.Count, numAgents);

                // Evolve the number of agents required to meet the success MC for the maze
                var viableMazeAgents = _mazeNavigationInitializer.EvolveViableAgents(agentGenomeFactory,
                                                                                     agentPopulation.ToList(), mazeDecoder.Decode(mazeGenome), _maxInitializationEvaluations,
                                                                                     ActivationScheme, ParallelOptions);

                // Iterate through each viable agent and remove them if they've already solved a maze or add them to the list
                // of viable agents if they have not
                foreach (var viableMazeAgent in viableMazeAgents)
                {
                    // If they agent has already solved maze and is in the list of viable agents, remove that agent
                    // from the pool of seed genomes (this is done because here, we're interested in getting unique
                    // agents and want to avoid an endless loop wherein the same viable agents are returned)
                    if (seedAgentPopulation.Select(sap => sap.Id).Contains(viableMazeAgent.Id))
                    {
                        agentPopulation.Remove(viableMazeAgent);
                    }
                    // Otherwise, add that agent to the list of viable agents
                    else
                    {
                        seedAgentPopulation.Add(viableMazeAgent);
                    }
                }
            }

            return(seedAgentPopulation);
        }
        /// <inheritdoc />
        /// <summary>
        ///     Creates the evolution algorithm container using the given factories and genome lists.
        /// </summary>
        /// <param name="genomeFactory1">The agent genome factory.</param>
        /// <param name="genomeFactory2">The maze genome factory.</param>
        /// <param name="genomeList1">The agent genome list.</param>
        /// <param name="genomeList2">The maze genome list.</param>
        /// <param name="isAgentListPreevolved">
        ///     Indicates whether the given agents have been pre-evolved to satisfy the MC with
        ///     respect to the maze population.
        /// </param>
        /// <returns>The instantiated MCC algorithm container.</returns>
        public override IMCCAlgorithmContainer <NeatGenome, MazeGenome> CreateMCCAlgorithmContainer(
            IGenomeFactory <NeatGenome> genomeFactory1, IGenomeFactory <MazeGenome> genomeFactory2,
            List <NeatGenome> genomeList1, List <MazeGenome> genomeList2, bool isAgentListPreevolved)
        {
            // Either use pre-evolved agents or evolve the seed agents that meet the MC
            var seedAgentPopulation = isAgentListPreevolved
                ? genomeList1
                : EvolveSeedAgents(genomeList1, genomeList2, genomeFactory1, AgentSeedGenomeCount);

            // Set dummy fitness so that seed maze(s) will be marked as evaluated
            foreach (var mazeGenome in genomeList2)
            {
                mazeGenome.EvaluationInfo.SetFitness(0);
            }

            // Create the NEAT evolution algorithm parameters
            var neatEaParams = new EvolutionAlgorithmParameters
            {
                SpecieCount   = AgentNumSpecies,
                MaxSpecieSize =
                    AgentNumSpecies > 0
                        ? AgentDefaultPopulationSize / AgentNumSpecies
                        : AgentDefaultPopulationSize
            };

            // Create the maze evolution algorithm parameters
            var mazeEaParams = new EvolutionAlgorithmParameters
            {
                SpecieCount   = MazeNumSpecies,
                MaxSpecieSize =
                    MazeNumSpecies > 0 ? MazeDefaultPopulationSize / MazeNumSpecies : MazeDefaultPopulationSize
            };

            // Create the NEAT (i.e. navigator) queueing evolution algorithm
            AbstractEvolutionAlgorithm <NeatGenome> neatEvolutionAlgorithm = new QueueEvolutionAlgorithm <NeatGenome>(
                neatEaParams, new NeatAlgorithmStats(neatEaParams), null, NavigatorBatchSize, RunPhase.Primary,
                _navigatorEvolutionDataLogger, _navigatorLogFieldEnableMap, _navigatorPopulationDataLogger,
                _navigatorGenomeDataLogger, _populationLoggingBatchInterval);

            // Create the maze queueing evolution algorithm
            AbstractEvolutionAlgorithm <MazeGenome> mazeEvolutionAlgorithm = new QueueEvolutionAlgorithm <MazeGenome>(
                mazeEaParams, new MazeAlgorithmStats(mazeEaParams), null, MazeBatchSize, RunPhase.Primary,
                _mazeEvolutionDataLogger, _mazeLogFieldEnableMap, _mazePopulationDataLogger, _mazeGenomeDataLogger,
                _populationLoggingBatchInterval);

            // Create the maze phenome evaluator
            IPhenomeEvaluator <MazeStructure, BehaviorInfo> mazeEvaluator =
                new MazeEnvironmentMCCEvaluator(MinSuccessDistance, BehaviorCharacterizationFactory,
                                                NumAgentSuccessCriteria, NumAgentFailedCriteria);

            // Create navigator phenome evaluator
            IPhenomeEvaluator <IBlackBox, BehaviorInfo> navigatorEvaluator =
                new MazeNavigatorMCCEvaluator(MinSuccessDistance, BehaviorCharacterizationFactory,
                                              NumMazeSuccessCriteria);

            // Create maze genome decoder
            IGenomeDecoder <MazeGenome, MazeStructure> mazeGenomeDecoder = new MazeDecoder(MazeScaleMultiplier);

            // Create navigator genome decoder
            IGenomeDecoder <NeatGenome, IBlackBox> navigatorGenomeDecoder = new NeatGenomeDecoder(ActivationScheme);

            // Create the maze genome evaluator
            IGenomeEvaluator <MazeGenome> mazeFitnessEvaluator =
                new ParallelGenomeBehaviorEvaluator <MazeGenome, MazeStructure>(mazeGenomeDecoder, mazeEvaluator, SearchType.MinimalCriteriaSearch, ParallelOptions);

            // Create navigator genome evaluator
            IGenomeEvaluator <NeatGenome> navigatorFitnessEvaluator =
                new ParallelGenomeBehaviorEvaluator <NeatGenome, IBlackBox>(navigatorGenomeDecoder, navigatorEvaluator, SearchType.MinimalCriteriaSearch, ParallelOptions);

            // Verify that the seed agent population satisfies MC constraints of both populations so that MCC starts in
            // a valid state
            if (isAgentListPreevolved &&
                VerifyPreevolvedSeedAgents(genomeList1, genomeList2, navigatorFitnessEvaluator, mazeFitnessEvaluator) ==
                false)
            {
                throw new SharpNeatException("Seed agent population failed viability verification.");
            }

            // Create the MCC container
            IMCCAlgorithmContainer <NeatGenome, MazeGenome> mccAlgorithmContainer =
                new MCCAlgorithmContainer <NeatGenome, MazeGenome>(neatEvolutionAlgorithm, mazeEvolutionAlgorithm);

            // Initialize the container and component algorithms
            mccAlgorithmContainer.Initialize(navigatorFitnessEvaluator, genomeFactory1, seedAgentPopulation,
                                             AgentDefaultPopulationSize, mazeFitnessEvaluator, genomeFactory2, genomeList2,
                                             MazeDefaultPopulationSize,
                                             MaxGenerations, MaxEvaluations);

            return(mccAlgorithmContainer);
        }
        public SimulatorExperimentConfiguration ReadExperimentConfigurationFile(string configurationFile)
        {
            XmlDocument xmlExperimentConfig = new XmlDocument();

            // Parse experiment name out of configuration file
            string experimentName = Path.GetFileNameWithoutExtension(configurationFile);

            // Load experiment configuration file
            xmlExperimentConfig.Load(configurationFile);

            // Get the root element
            XmlElement rootElement = xmlExperimentConfig.DocumentElement;

            // Determine navigator ANN controller activation scheme
            NetworkActivationScheme navigatorActivationScheme = ExperimentUtils.CreateActivationScheme(rootElement,
                "Activation");

            // Create a new navigator genome decoder based on the activation scheme
            _navigatorGenomeDecoder = new NeatGenomeDecoder(navigatorActivationScheme);

            // Read in maze properties
            int mazeHeight = XmlUtils.GetValueAsInt(rootElement, "MazeHeight");
            int mazeWidth = XmlUtils.GetValueAsInt(rootElement, "MazeWidth");
            int mazeScaleMultiplier = XmlUtils.GetValueAsInt(rootElement, "MazeScaleMultiplier");

            // Create a new maze genome decoder based on the maze size
            _mazeGenomeDecoder = new MazeDecoder(mazeHeight, mazeWidth, mazeScaleMultiplier);

            // Read experiment parameteres and return experiment configuration
            return new SimulatorExperimentConfiguration(experimentName, mazeHeight, mazeWidth, mazeScaleMultiplier,
                navigatorActivationScheme, XmlUtils.GetValueAsInt(rootElement, "MaxTimesteps"),
                XmlUtils.GetValueAsInt(rootElement, "MinSuccessDistance"));
        }
        private static void Main(string[] args)
        {
            // Initialise log4net (log to console and file).
            XmlConfigurator.Configure(new FileInfo("log4net.properties"));

            // Instantiate the execution logger
            _executionLogger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

            // Extract the execution parameters and check for any errors (exit application if any found)
            if (ParseAndValidateConfiguration(args) == false)
                Environment.Exit(0);

            _executionLogger.Info("Invocation parameters validated - continuing with experiment execution.");

            // Get input and output neurons counts for navigator agent
            int inputNeuronCount = Int32.Parse(ExecutionConfiguration[ExecutionParameter.AgentNeuronInputCount]);
            int outputNeuronCount = Int32.Parse(ExecutionConfiguration[ExecutionParameter.AgentNeuronOutputCount]);

            // Get the reference experiment name
            string referenceExperimentName = ExecutionConfiguration[ExecutionParameter.ReferenceExperimentName];

            // Extract the experiment names
            string[] coEvoExperimentNames = ExecutionConfiguration[ExecutionParameter.ExperimentNames].Split(',');

            _executionLogger.Info(
                string.Format(
                    "Baseline experiment [{0}] specified for comparison against {1} coevolution experiments.",
                    referenceExperimentName, coEvoExperimentNames.Length));

            // Process each experiment
            foreach (string coEvoExperimentName in coEvoExperimentNames)
            {
                // Get the run from which to start execution (if specified)
                int startingRun = ExecutionConfiguration.ContainsKey(ExecutionParameter.StartFromRun)
                    ? Int32.Parse(ExecutionConfiguration[ExecutionParameter.StartFromRun])
                    : 1;

                // Lookup the reference experiment configuration
                ExperimentDictionary referenceExperimentConfiguration =
                    ExperimentDataHandler.LookupExperimentConfiguration(referenceExperimentName);

                // Ensure that reference experiment configuration was found
                if (referenceExperimentConfiguration == null)
                {
                    _executionLogger.Error(
                        string.Format("Unable to lookup reference experiment configuration with name [{0}]",
                            referenceExperimentName));
                    Environment.Exit(0);
                }

                // Lookup current coevolution experiment configuration
                // Lookup the current experiment configuration
                ExperimentDictionary curCoEvoExperimentConfiguration =
                    ExperimentDataHandler.LookupExperimentConfiguration(coEvoExperimentName);

                // Ensure that coevolution experiment configuration was found
                if (curCoEvoExperimentConfiguration == null)
                {
                    _executionLogger.Error(
                        string.Format("Unable to lookup coevolution experiment configuration with name [{0}]",
                            coEvoExperimentName));
                    Environment.Exit(0);
                }

                // Ensure that the experiment contains the necessary maze structure configuration parameters
                Debug.Assert(curCoEvoExperimentConfiguration.Primary_Maze_MazeHeight != null,
                    "Maze Height configuration parameter cannot be null.");
                Debug.Assert(curCoEvoExperimentConfiguration.Primary_Maze_MazeWidth != null,
                    "Maze Width configuration parameter cannot be null.");
                Debug.Assert(curCoEvoExperimentConfiguration.Primary_Maze_MazeScaleMultiplier != null,
                    "Maze Scale Multiplier configuration parameter cannot be null.");

                // Instantiate maze decoder
                MazeDecoder mazeDecoder = new MazeDecoder((int) curCoEvoExperimentConfiguration.Primary_Maze_MazeHeight,
                    (int) curCoEvoExperimentConfiguration.Primary_Maze_MazeWidth,
                    (int) curCoEvoExperimentConfiguration.Primary_Maze_MazeScaleMultiplier);

                // Get the number of runs in the coevolution experiment
                int numRuns = ExperimentDataHandler.GetNumRuns(curCoEvoExperimentConfiguration.ExperimentDictionaryID);

                _executionLogger.Info(
                    string.Format(
                        "Preparing to execute comparative analysis for [{0}] runs of coevolution experiment [{1}] against reference experiment [{2}]",
                        numRuns,
                        curCoEvoExperimentConfiguration.ExperimentName, referenceExperimentConfiguration.ExperimentName));

                // Get the number of batches (generations) in the experiment
                for (int curRun = startingRun; curRun <= numRuns; curRun++)
                {
                    // Open the file writer
                    ExperimentDataHandler.OpenFileWriter(
                        Path.Combine(ExecutionConfiguration[ExecutionParameter.DataFileOutputDirectory],
                            string.Format("{0} - Run{1}.csv", coEvoExperimentName, curRun)),
                        OutputFileType.NoveltySearchComparisonData);

                    // Get the number of primary batches in the current run
                    IList<int> batchesWithGenomeData =
                        ExperimentDataHandler.GetBatchesWithGenomeData(
                            curCoEvoExperimentConfiguration.ExperimentDictionaryID, curRun, RunPhase.Primary);

                    _executionLogger.Info(
                        string.Format("Executing comparative analysis for run [{0}/{1}] with [{2}] batches",
                            curRun,
                            numRuns, batchesWithGenomeData.Count));

                    // Begin comparative analysis
                    RunPerBatchComparativeAnalysis(batchesWithGenomeData,
                        curCoEvoExperimentConfiguration.ExperimentDictionaryID,
                        curCoEvoExperimentConfiguration.ExperimentName, curRun, numRuns, mazeDecoder,
                        referenceExperimentConfiguration);

                    // Close the file writer after the comparative analysis for the current run is complete
                    ExperimentDataHandler.CloseFileWriter(OutputFileType.NoveltySearchComparisonData);
                }
            }
        }
        /// <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>
        ///     Converts the maze genome into a maze structure and prints it to a bitmap file.
        /// </summary>
        /// <param name="mazeGenome">The maze genome to convert and print.</param>
        /// <param name="mazeImageName">The name of the maze output file.</param>
        private static void PrintMazeToFile(MazeGenome mazeGenome, string mazeImageName)
        {
            // Read in the maze decode parameters
            int mazeHeight = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeHeight]);
            int mazeWidth = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeWidth]);
            int mazeScaleFactor = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeScaleFactor]);
            string mazeBitmapOutputDirectory = _executionConfiguration[ExecutionParameter.BitmapOutputBaseDirectory];

            // Instantiate the maze genome decoder
            MazeDecoder mazeDecoder = new MazeDecoder(mazeHeight, mazeWidth, mazeScaleFactor);

            // Decode the maze to get a maze structure
            MazeStructure mazeStructure = mazeDecoder.Decode(mazeGenome);

            // Create pen and initialize bitmap canvas
            Pen blackPen = new Pen(Color.Black, 0.0001f);
            Bitmap mazeBitmap = new Bitmap(mazeStructure.ScaledMazeWidth + 1, mazeStructure.ScaledMazeHeight + 1);

            using (Graphics graphics = Graphics.FromImage(mazeBitmap))
            {
                // Fill with white
                Rectangle imageSize = new Rectangle(0, 0, mazeStructure.ScaledMazeWidth + 1,
                    mazeStructure.ScaledMazeHeight + 1);
                graphics.FillRectangle(Brushes.White, imageSize);

                // Draw start and end points
                graphics.FillEllipse(Brushes.Green, mazeStructure.StartLocation.X, mazeStructure.StartLocation.Y, 5, 5);
                graphics.FillEllipse(Brushes.Red, mazeStructure.TargetLocation.X, mazeStructure.TargetLocation.Y, 5, 5);

                // Draw all of the walls
                foreach (MazeStructureWall wall in mazeStructure.Walls)
                {
                    // Convert line start/end points to Point objects from drawing namespace
                    Point startPoint = new Point(wall.StartMazeStructurePoint.X, wall.StartMazeStructurePoint.Y);
                    Point endPoint = new Point(wall.EndMazeStructurePoint.X, wall.EndMazeStructurePoint.Y);

                    // Draw wall
                    graphics.DrawLine(blackPen, startPoint, endPoint);
                }
            }

            // Save the bitmap image
            mazeBitmap.Save(Path.Combine(mazeBitmapOutputDirectory, mazeImageName));
        }
        public void VerifyBootstrappedStateTest()
        {
            const string parentDirectory =
                "F:/User Data/Jonathan/Documents/school/Jonathan/Graduate/PhD/Development/C# NEAT/SharpNoveltyNeat/SharpNeatConsole/bin/Debug/";
            const string agentGenomeFile = "ViableSeedGenomes.xml";
            const string baseBitmapFilename = "AgentTrajectory";
            const int mazeHeight = 20;
            const int mazeWidth = 20;
            const int scaleMultiplier = 16;
            const int maxTimesteps = 400;
            const int minSuccessDistance = 5;

            // Setup stuff for the navigators
            List<NeatGenome> agentGenomes;
            NeatGenomeDecoder agentGenomeDecoder = new NeatGenomeDecoder(NetworkActivationScheme.CreateAcyclicScheme());
            NeatGenomeFactory agentGenomeFactory = new NeatGenomeFactory(10, 2);

            // Create new minimal maze (no barriers)
            MazeStructure mazeStructure = new MazeDecoder(mazeHeight, mazeWidth, scaleMultiplier).Decode(
                new MazeGenomeFactory(null, null, null).CreateGenome(0));

            // Create behavior characterization factory
            IBehaviorCharacterizationFactory behaviorCharacterizationFactory =
                new TrajectoryBehaviorCharacterizationFactory(null);

            // Create evaluator
            MazeNavigatorMCSEvaluator mazeNavigatorEvaluator = new MazeNavigatorMCSEvaluator(maxTimesteps,
                minSuccessDistance, behaviorCharacterizationFactory, 1);

            // Set maze within evaluator
            mazeNavigatorEvaluator.UpdateEvaluatorPhenotypes(new List<MazeStructure> {mazeStructure});

            // Read in agents
            using (XmlReader xr = XmlReader.Create(parentDirectory + agentGenomeFile))
            {
                agentGenomes = NeatGenomeXmlIO.ReadCompleteGenomeList(xr, false, agentGenomeFactory);
            }

            // Decode agent genomes to phenotype and run simulation
            for (int i = 0; i < agentGenomes.Count; i++)
            {
                // Decode navigator genome
                IBlackBox agentPhenome = agentGenomeDecoder.Decode(agentGenomes[i]);

                // Run simulation
                BehaviorInfo behaviorInfo = mazeNavigatorEvaluator.Evaluate(agentPhenome, 0, false, null, null);

                // Print the navigator trajectory through the maze
                DomainTestUtils.PrintMazeAndTrajectory(mazeStructure, behaviorInfo.Behaviors,
                    string.Format("{0}_{1}.bmp", baseBitmapFilename, i));
            }
        }