context: /* ***************************************************************************
   * 
   * Copyright 2010, Wesley Tansey (wes@nashcoding.com), People in EvoComp
   * 
   * Some code in this file may have been copied directly from SharpNEAT,
   * for learning purposes only. Any code copied from SharpNEAT 2 is 
   * copyright of Colin Green (sharpneat@gmail.com).
   *
   * Both SharpNEAT and this tutorial are free software: you can redistribute
   * it and/or modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of the 
   * License, or (at your option) any later version.
   *
   * SharpNEAT is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with SharpNEAT.  If not, see <http://www.gnu.org/licenses/>.
   */
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using SharpNeat.Domains;
  using SharpNeat.EvolutionAlgorithms;
  using SharpNeat.Genomes.Neat;
  using SharpNeat.Decoders;
  using System.Threading.Tasks;
  using SharpNeat.Core;
  using SharpNeat.EvolutionAlgorithms.ComplexityRegulation;
  using SharpNeat.Decoders.Neat;
  using SharpNeat.Phenomes;
  using SharpNeat.DistanceMetrics;
  using SharpNeat.SpeciationStrategies;
  using System.Xml;
  
  namespace MinesweeperLib
  {
      /// <summary>
      /// Helper class that hides most of the details of setting up an experiment.
      /// If you're just doing a simple console-based experiment, this is probably
      /// what you want to inherit from. However, if you need more flexibility
      /// (e.g., custom genome/phenome creation or performing complex population
      /// evaluations) then you probably want to implement your own INeatExperiment
      /// class.
      /// </summary>
      public abstract class SimpleNeatExperiment : INeatExperiment
      {
          NeatEvolutionAlgorithmParameters _eaParams;
          NeatGenomeParameters _neatGenomeParams;
          string _name;
          int _populationSize;
          int _specieCount;
          NetworkActivationScheme _activationScheme;
          string _complexityRegulationStr;
          int? _complexityThreshold;
          string _description;
          ParallelOptions _parallelOptions;
  
          #region Abstract properties that subclasses must implement
          public abstract IPhenomeEvaluator<IBlackBox> PhenomeEvaluator { get; }
          public abstract int InputCount { get; }
          public abstract int OutputCount { get; }
          public abstract bool EvaluateParents { get; }
          #endregion
  
          
  
          #region INeatExperiment Members
          public string Description
          {
              get { return _description; }
          }
  
          public string Name
          {
              get { return _name; }
          }
  
          /// <summary>
          /// Gets the default population size to use for the experiment.
          /// </summary>
          public int DefaultPopulationSize
          {
              get { return _populationSize; }
          }
  
          /// <summary>
          /// Gets the NeatEvolutionAlgorithmParameters to be used for the experiment. Parameters on this object can be 
          /// modified. Calls to CreateEvolutionAlgorithm() make a copy of and use this object in whatever state it is in 
          /// at the time of the call.
          /// </summary>
          public NeatEvolutionAlgorithmParameters NeatEvolutionAlgorithmParameters
          {
              get { return _eaParams; }
          }
  
          /// <summary>
          /// Gets the NeatGenomeParameters to be used for the experiment. Parameters on this object can be modified. Calls
          /// to CreateEvolutionAlgorithm() make a copy of and use this object in whatever state it is in at the time of the call.
          /// </summary>
          public NeatGenomeParameters NeatGenomeParameters
          {
              get { return _neatGenomeParams; }
          }
  
          /// <summary>
          /// Initialize the experiment with some optional XML configutation data.
          /// </summary>
          public void Initialize(string name, XmlElement xmlConfig)
          {
              _name = name;
              _populationSize = XmlUtils.GetValueAsInt(xmlConfig, "PopulationSize");
              _specieCount = XmlUtils.GetValueAsInt(xmlConfig, "SpecieCount");
              _activationScheme = ExperimentUtils.CreateActivationScheme(xmlConfig, "Activation");
              _complexityRegulationStr = XmlUtils.TryGetValueAsString(xmlConfig, "ComplexityRegulationStrategy");
              _complexityThreshold = XmlUtils.TryGetValueAsInt(xmlConfig, "ComplexityThreshold");
              _description = XmlUtils.TryGetValueAsString(xmlConfig, "Description");
              _parallelOptions = ExperimentUtils.ReadParallelOptions(xmlConfig);
  
              _eaParams = new NeatEvolutionAlgorithmParameters();
              _eaParams.SpecieCount = _specieCount;
              _neatGenomeParams = new NeatGenomeParameters();
          }
  
          /// <summary>
          /// Load a population of genomes from an XmlReader and returns the genomes in a new list.
          /// The genome2 factory for the genomes can be obtained from any one of the genomes.
          /// </summary>
          public List<NeatGenome> LoadPopulation(XmlReader xr)
          {
  			NeatGenomeFactory genomeFactory = (NeatGenomeFactory)CreateGenomeFactory();
              return NeatGenomeXmlIO.ReadCompleteGenomeList(xr, false, genomeFactory);
          }
  
          /// <summary>
          /// Save a population of genomes to an XmlWriter.
          /// </summary>
          public void SavePopulation(XmlWriter xw, IList<NeatGenome> genomeList)
          {
              // Writing node IDs is not necessary for NEAT.
              NeatGenomeXmlIO.WriteComplete(xw, genomeList, false);
          }
  
          /// <summary>
          /// Create a genome2 factory for the experiment.
          /// Create a genome2 factory with our neat genome2 parameters object and the appropriate number of input and output neuron genes.
          /// </summary>
          public IGenomeFactory<NeatGenome> CreateGenomeFactory()
          {
              return new NeatGenomeFactory(InputCount, OutputCount, _neatGenomeParams);
          }
  
          /// <summary>
          /// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
          /// of the algorithm are also constructed and connected up.
          /// This overload requires no parameters and uses the default population size.
          /// </summary>
          public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm()
          {
              return CreateEvolutionAlgorithm(DefaultPopulationSize);
          }
  
          /// <summary>
          /// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
          /// of the algorithm are also constructed and connected up.
          /// This overload accepts a population size parameter that specifies how many genomes to create in an initial randomly
          /// generated population.
          /// </summary>
          public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(int populationSize)
          {
              // Create a genome2 factory with our neat genome2 parameters object and the appropriate number of input and output neuron genes.
              IGenomeFactory<NeatGenome> genomeFactory = CreateGenomeFactory();
  
              // Create an initial population of randomly generated genomes.
              List<NeatGenome> genomeList = genomeFactory.CreateGenomeList(populationSize, 0);
  
              // Create evolution algorithm.
              return CreateEvolutionAlgorithm(genomeFactory, genomeList);
          }
  
          /// <summary>
          /// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
          /// of the algorithm are also constructed and connected up.
          /// This overload accepts a pre-built genome2 population and their associated/parent genome2 factory.
          /// </summary>
          public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList)
          {
              // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference.
              IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0);
              ISpeciationStrategy<NeatGenome> speciationStrategy = new ParallelKMeansClusteringStrategy<NeatGenome>(distanceMetric, _parallelOptions);
  
              // Create complexity regulation strategy.
              IComplexityRegulationStrategy complexityRegulationStrategy = ExperimentUtils.CreateComplexityRegulationStrategy(_complexityRegulationStr, _complexityThreshold);
  
              // Create the evolution algorithm.
              NeatEvolutionAlgorithm<NeatGenome> ea = new NeatEvolutionAlgorithm<NeatGenome>(_eaParams, speciationStrategy, complexityRegulationStrategy);
  
              // Create genome2 decoder.
              IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new NeatGenomeDecoder(_activationScheme);
  
              // Create a genome2 list evaluator. This packages up the genome2 decoder with the genome2 evaluator.
              IGenomeListEvaluator<NeatGenome> genomeListEvaluator = new ParallelGenomeListEvaluator<NeatGenome, IBlackBox>(genomeDecoder, PhenomeEvaluator, _parallelOptions);
  
              // Wrap the list evaluator in a 'selective' evaulator that will only evaluate new genomes. That is, we skip re-evaluating any genomes
              // that were in the population in previous generations (elite genomes). This is determiend by examining each genome2's evaluation info object.
              if(!EvaluateParents)
                  genomeListEvaluator = new SelectiveGenomeListEvaluator<NeatGenome>(genomeListEvaluator,
                                           SelectiveGenomeListEvaluator<NeatGenome>.CreatePredicate_OnceOnly());
  
              // Initialize the evolution algorithm.
              ea.Initialize(genomeListEvaluator, genomeFactory, genomeList);
  
              // Finished. Return the evolution algorithm
              return ea;
          }
  
          /// <summary>
          /// Creates a new genome decoder that can be used to convert a genome into a phenome.
          /// </summary>
          public IGenomeDecoder<NeatGenome, IBlackBox> CreateGenomeDecoder()
          {
  
gt:             return new NeatGenomeDecoder(_activationScheme);