/// <summary> /// Preps for running the maze navigation simulations by decoding the given maze/navigator combinations genomes. These /// are presumably combinations that were determined to be successful in solving the respective maze. /// </summary> /// <param name="navigationCombos">The combinations of mazes and navigators.</param> public void Initialize( IEnumerable <Tuple <MccexperimentMazeGenome, MccexperimentNavigatorGenome> > navigationCombos) { foreach (var navigationCombo in navigationCombos) { MazeGenome mazeGenome; NeatGenome navigatorGenome; // Deserialize the maze XML into a maze genome using (var xmlReader = XmlReader.Create(new StringReader(navigationCombo.Item1.GenomeXml))) { mazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, _mazeGenomeFactory); } // Deserialize the navigator XML into a NEAT genome using (var xmlReader = XmlReader.Create(new StringReader(navigationCombo.Item2.GenomeXml))) { navigatorGenome = NeatGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, false, _neatGenomeFactory); } // Decode to maze and navigator phenomes and add to the evaluation units list EvaluationUnits.Add(new MazeNavigatorEvaluationUnit(_mazeDecoder.Decode(mazeGenome), _agentDecoder.Decode(navigatorGenome), navigationCombo.Item1.GenomeId, navigationCombo.Item2.GenomeId)); } }
/// <summary> /// Preps for running the maze navigation simulations by constructing all combinations of mazes/navigators that need to /// be evaluated in tandem. /// </summary> /// <param name="mazes">The mazes that were in the maze queue during the given batch.</param> /// <param name="navigators">The navigators that were in the navigator queue during the given batch.</param> public void Initialize(IEnumerable <MccexperimentMazeGenome> mazes, IList <MccexperimentNavigatorGenome> navigators) { IList <NeatGenome> cachedAgents = new List <NeatGenome>(navigators.Count); foreach (var serializedMaze in mazes) { MazeGenome mazeGenome; // Deserialize the maze XML into a maze genome using (var xmlReader = XmlReader.Create(new StringReader(serializedMaze.GenomeXml))) { mazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, _mazeGenomeFactory); } // If no agents have been deserialized yet, we need to parse the XML and load the genotype list if (cachedAgents.Count < 1) { // Go through every serialized navigator genome, deserialize it, and use it with the maze to build // a new evaluation unit foreach (var serializedNavigator in navigators) { NeatGenome agentGenome; // Read in the current navigator agent genome using (var xmlReader = XmlReader.Create(new StringReader(serializedNavigator.GenomeXml))) { agentGenome = NeatGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, false, _neatGenomeFactory); } // Decode to maze and navigator phenomes and add to the evaluation units list EvaluationUnits.Add(new MazeNavigatorEvaluationUnit(_mazeDecoder.Decode(mazeGenome), _agentDecoder.Decode(agentGenome), serializedMaze.GenomeId, serializedNavigator.GenomeId)); // Also add to the list of cached genomes cachedAgents.Add(agentGenome); } } // Otherwise, skip the deserialization process else { // Add each genome with the current maze to create new evaluation units foreach (var cachedAgent in cachedAgents) { EvaluationUnits.Add(new MazeNavigatorEvaluationUnit(_mazeDecoder.Decode(mazeGenome), _agentDecoder.Decode(cachedAgent), serializedMaze.GenomeId, (int)cachedAgent.Id)); } } } }
/// <summary> /// Preps for generating the maze structures by decoding the given list of maze genomes. This is in support of /// post-hoc analyses that doesn't consider navigator trajectories. /// </summary> /// <param name="mazes">The mazes that were in the maze queue during the given batch.</param> public void Initialize(IEnumerable <MccexperimentMazeGenome> mazes) { foreach (var serializedMaze in mazes) { MazeGenome mazeGenome; // Deserialize the maze XML into a maze genome using (var xmlReader = XmlReader.Create(new StringReader(serializedMaze.GenomeXml))) { mazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xmlReader, _mazeGenomeFactory); } // Decode to maze phenome and add to the maze/id map _mazeIdStructureMap.Add(serializedMaze.GenomeId, _mazeDecoder.Decode(mazeGenome)); } }
/// <summary> /// Reads in seed maze genomes used to bootstrap MCC experiments. /// </summary> /// <param name="seedMazePath"> /// The path of the single maze genome or a directory containing multiple XML genome /// definitions. /// </param> /// <param name="mazeGenomeFactory">The maze genome factory to assign to each genome.</param> /// <returns>The list of seed maze genomes.</returns> public static IEnumerable <MazeGenome> ReadSeedMazeGenomes(string seedMazePath, MazeGenomeFactory mazeGenomeFactory) { var mazeGenomes = new List <MazeGenome>(); // Get the maze genome files in the given path var mazeGenomeFiles = GetGenomeFiles(seedMazePath); // Read in all maze genomes and add them to the list foreach (var mazeGenomeFile in mazeGenomeFiles) { using (var xr = XmlReader.Create(mazeGenomeFile)) { // Read in the maze genomes var curMazeGenomes = MazeGenomeXmlIO.ReadCompleteGenomeList(xr, mazeGenomeFactory); // Add the genomes to the overall genome list mazeGenomes.AddRange(curMazeGenomes); } } return(mazeGenomes); }
/// <summary> /// Handles the process of rendering a bitmap file of an existing maze genome. /// </summary> private static void HandleMazeImageReproduction() { MazeGenome mazeGenome = null; // Get the maze genome file path and image output path string mazeGenomeFile = _executionConfiguration[ExecutionParameter.MazeGenomeFile]; string imageOutputPath = _executionConfiguration[ExecutionParameter.BitmapOutputBaseDirectory]; // Create a new (mostly dummy) maze genome factory MazeGenomeFactory mazeGenomeFactory = new MazeGenomeFactory(); // Read in the genome using (XmlReader xr = XmlReader.Create(mazeGenomeFile)) { mazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xr, mazeGenomeFactory); } // Get the maze genome ID uint mazeGenomeId = mazeGenome.Id; // Render the maze phenotype (i.e. the graphical structure0 and print to a bitmap file PrintMazeToFile(mazeGenome, Path.Combine(imageOutputPath, string.Format("EvolvedMaze_ID_{0}.bmp", mazeGenomeId))); }
/// <inheritdoc /> /// <summary> /// Save a population of maze genomes to an XmlWriter. /// </summary> public void SaveMazePopulation(XmlWriter xw, IList <MazeGenome> mazeGenomeList) { MazeGenomeXmlIO.WriteComplete(xw, mazeGenomeList); }
/// <summary> /// Handles the process of generating maze genomes and rendering bitmap files of their structure. /// </summary> private static void HandleMazeGeneration() { IRandomSource rand = RandomDefaults.CreateRandomSource(); // Get the evolved maze height and width int mazeHeight = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeHeight]); int mazeWidth = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeWidth]); int mazeQuadrantHeight = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeQuadrantHeight]); int mazeQuadrantWidth = Int32.Parse(_executionConfiguration[ExecutionParameter.MazeQuadrantWidth]); // Get the number of interior walls and maze genome output directory int numWaypoints = Int32.Parse(_executionConfiguration[ExecutionParameter.NumWaypoints]); int numInteriorWalls = Int32.Parse(_executionConfiguration[ExecutionParameter.NumWalls]); string mazeGenomeOutputDirectory = _executionConfiguration[ExecutionParameter.MazeGenomeOutputBaseDirectory]; // Get the number of sample mazes to generate (or just 1 maze if not specified) int numMazes = _executionConfiguration.ContainsKey(ExecutionParameter.NumMazes) ? Int32.Parse(_executionConfiguration[ExecutionParameter.NumMazes]) : 1; // Get whether images are being generated for the sample mazes bool generateMazeImages = _executionConfiguration.ContainsKey(ExecutionParameter.OutputMazeBitmap) && Boolean.Parse(_executionConfiguration[ExecutionParameter.OutputMazeBitmap]); // Get whether maze genomes are being serialized to the same file (defaults to false) bool isSingleOutputFile = _executionConfiguration.ContainsKey(ExecutionParameter.SingleGenomeOutputFile) && Boolean.Parse(_executionConfiguration[ExecutionParameter.SingleGenomeOutputFile]); // Create a new maze genome factory MazeGenomeFactory mazeGenomeFactory = new MazeGenomeFactory(mazeHeight, mazeWidth, mazeQuadrantHeight, mazeQuadrantWidth); // Instantiate list to hold generated maze genomes // (only really used when we're writing everything out to one file) List <MazeGenome> mazeGenomeList = new List <MazeGenome>(numMazes); for (int curMazeCnt = 0; curMazeCnt < numMazes; curMazeCnt++) { MazeGenome mazeGenome; // Lay out the base file name string fileBaseName = string.Format("GeneratedMazeGenome_{0}_Height_{1}_Width_{2}_Waypoints_{3}_Walls_{4}", mazeHeight, mazeWidth, numWaypoints, numInteriorWalls, curMazeCnt); // With a single output file, the genomes are likely being used for separate experiments, so we // reset the innovation IDs and assign the maze a constant identifier if (isSingleOutputFile == false) { // Reset innovation IDs mazeGenomeFactory.InnovationIdGenerator.Reset(); // Create a new genome and pass in the requisite factory mazeGenome = new MazeGenome(mazeGenomeFactory, 0, 0); } // Otherwise, we leave the innovation ID generator alone and create a new maze genome with // an identifier that's incremented by one else { mazeGenome = new MazeGenome(mazeGenomeFactory, (uint)curMazeCnt, 0); } // Add the specified number of waypoints (less one because center waypoint is created on maze initialization) for (int cnt = 0; cnt < numWaypoints - 1; cnt++) { Point2DInt waypoint; // Randomly select an orientation IntersectionOrientation newPointOrientation = mazeGenomeFactory.Rng.NextBool() ? IntersectionOrientation.Horizontal : IntersectionOrientation.Vertical; // Iterate until we get a waypoint that's valid do { waypoint = new Point2DInt(mazeGenomeFactory.Rng.Next(mazeGenome.MazeBoundaryWidth - 1), mazeGenomeFactory.Rng.Next(mazeGenome.MazeBoundaryHeight - 1)); } while ( MazeUtils.IsValidWaypointLocation(mazeGenome, waypoint, UInt32.MaxValue, newPointOrientation) == false); mazeGenome.PathGeneList.Add(new PathGene(mazeGenomeFactory.InnovationIdGenerator.NextId, waypoint, newPointOrientation)); } // Create the specified number of interior walls (i.e. maze genes) for (int cnt = 0; cnt < numInteriorWalls; cnt++) { // Create new maze gene and add to genome mazeGenome.WallGeneList.Add(new WallGene(mazeGenomeFactory.InnovationIdGenerator.NextId, rand.NextDouble(), rand.NextDouble(), rand.NextDouble() < 0.5)); } // Only serialize genomes to separate files if single output file option is turned off if (isSingleOutputFile == false) { // Serialize the genome to XML using ( XmlWriter xmlWriter = XmlWriter.Create( Path.Combine(mazeGenomeOutputDirectory, string.Format("{0}.xml", fileBaseName)), new XmlWriterSettings { Indent = true })) { // Get genome XML MazeGenomeXmlIO.WriteComplete(xmlWriter, mazeGenome); } } // Otherwise, just add genome to list to be serialized to single file in bulk else { mazeGenomeList.Add(mazeGenome); } // Print the maze to a bitmap file if that option has been specified if (generateMazeImages) { PrintMazeToFile(mazeGenome, string.Format("{0}_Structure.bmp", fileBaseName)); } } // If serialize to single output file is turned off, go ahead and write everything out to the file if (isSingleOutputFile) { // Serialize all genomes to XML using ( XmlWriter xmlWriter = XmlWriter.Create(Path.Combine(mazeGenomeOutputDirectory, string.Format("GeneratedMaze_{0}_Genomes_{1}_Height_{2}_Width_{3}_Waypoints_{4}_Walls.xml", numMazes, mazeHeight, mazeWidth, numWaypoints, numInteriorWalls)))) { MazeGenomeXmlIO.WriteComplete(xmlWriter, mazeGenomeList); } } }
/// <summary> /// Computes the natural clustering of the maze genomes along with their respective entropy. /// </summary> /// <param name="mazeGenomeListXml">The list of serialized maze genome XML.</param> /// <param name="isGreedySilhouetteCalculation"> /// Dictates whether optimal clustering is based on number of clusters that /// maximize silhouette score until the first decrease in score (true), or based on the silhouette score calculated for /// a range of clusters with the number of clusters resulting in the maximum score used as the optimal number of /// clusters (false). /// </param> /// <param name="clusterRange">The range of clusters values for which to compute the silhouette width (optional).</param> /// <returns>The cluster diversity unit for maze genomes.</returns> public static ClusterDiversityUnit CalculateMazeClustering(IList <string> mazeGenomeListXml, bool isGreedySilhouetteCalculation, int clusterRange = 0) { // Always start with one cluster const int initClusterCnt = 1; // Initialize list of maze genome objects var mazeGenomes = new List <MazeGenome>(mazeGenomeListXml.Count()); // Convert all maze genome XML strings to maze genome objects foreach (var genomeXml in mazeGenomeListXml) { MazeGenome curMazeGenome; // Create a new, dummy maze genome factory var tempMazeGenomeFactory = new MazeGenomeFactory(); // Convert genome XML to genome object using (var xr = XmlReader.Create(new StringReader(genomeXml))) { curMazeGenome = MazeGenomeXmlIO.ReadSingleGenomeFromRoot(xr, tempMazeGenomeFactory); } // Add to the genome object list mazeGenomes.Add(curMazeGenome); } // Define observation matrix in which to store all gene coordinate vectors for each maze var observationMatrix = new double[mazeGenomes.Count()][]; // Get the maximum observation vector length (max number of maze genes) var maxObservationLength = mazeGenomes.Max(g => g.WallGeneList.Count()); for (var idx = 0; idx < mazeGenomes.Count(); idx++) { // If there are more observations than the total elements in the observation vector, // zero out the rest of the vector if (mazeGenomes[idx].WallGeneList.Count() < maxObservationLength) { observationMatrix[idx] = mazeGenomes[idx].Position.CoordArray.Select(ca => ca.Value) .Concat(Enumerable.Repeat(0.0, maxObservationLength - mazeGenomes[idx].Position.CoordArray.Length)) .ToArray(); } // Otherwise, if the observation and vector length are the same, just set the elements else { observationMatrix[idx] = mazeGenomes[idx].Position.CoordArray.Select(ca => ca.Value).ToArray(); } } // Determine the optimal number of clusters to fit these data var optimalClustering = DetermineOptimalClusters(observationMatrix, initClusterCnt, false, isGreedySilhouetteCalculation, clusterRange); // Compute shannon entropy given optimal clustering var shannonEntropy = ComputeShannonEntropy(optimalClustering); // Compute the silhouette width given optimal clustering var silhouetteWidth = ComputeSilhouetteWidth(optimalClustering.Clusters, observationMatrix, false); // Return the resulting maze genome cluster diversity info return(new ClusterDiversityUnit(optimalClustering.Clusters.Count, silhouetteWidth, shannonEntropy)); }