/// <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); }
/// <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; }
/// <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)); } }