/** {@inheritDoc} */
 @Override
 public void mutateWeight(
     final Random rnd, final NEATLinkGene linkGene, final double weightRange) {
   final double delta = rnd.nextGaussian() * this.sigma;
   double w = linkGene.getWeight() + delta;
   w = NEATPopulation.clampWeight(w, weightRange);
   linkGene.setWeight(w);
 }
  private void saveSpecies(final EncogWriteHelper out, final Species species) {
    out.addColumn("s");
    out.addColumn(species.getAge());
    out.addColumn(species.getBestScore());
    out.addColumn(species.getGensNoImprovement());
    out.writeLine();

    for (final Genome genome : species.getMembers()) {
      final NEATGenome neatGenome = (NEATGenome) genome;
      out.addColumn("g");
      out.addColumn(neatGenome.getAdjustedScore());
      out.addColumn(neatGenome.getScore());
      out.addColumn(neatGenome.getBirthGeneration());
      out.writeLine();

      for (final NEATNeuronGene neatNeuronGene : neatGenome.getNeuronsChromosome()) {
        out.addColumn("n");
        out.addColumn(neatNeuronGene.getId());
        out.addColumn(neatNeuronGene.getActivationFunction());
        out.addColumn(PersistNEATPopulation.neuronTypeToString(neatNeuronGene.getNeuronType()));
        out.addColumn(neatNeuronGene.getInnovationId());
        out.writeLine();
      }
      for (final NEATLinkGene neatLinkGene : neatGenome.getLinksChromosome()) {
        out.addColumn("l");
        out.addColumn(neatLinkGene.getId());
        out.addColumn(neatLinkGene.isEnabled());
        out.addColumn(neatLinkGene.getFromNeuronID());
        out.addColumn(neatLinkGene.getToNeuronID());
        out.addColumn(neatLinkGene.getWeight());
        out.addColumn(neatLinkGene.getInnovationId());
        out.writeLine();
      }
    }
  }
  @Override
  public Object read(final InputStream is) {
    long nextInnovationID = 0;
    long nextGeneID = 0;

    final NEATPopulation result = new NEATPopulation();
    final NEATInnovationList innovationList = new NEATInnovationList();
    innovationList.setPopulation(result);
    result.setInnovations(innovationList);
    final EncogReadHelper in = new EncogReadHelper(is);
    EncogFileSection section;

    while ((section = in.readNextSection()) != null) {
      if (section.getSectionName().equals("NEAT-POPULATION")
          && section.getSubSectionName().equals("INNOVATIONS")) {
        for (final String line : section.getLines()) {
          final List<String> cols = EncogFileSection.splitColumns(line);
          final NEATInnovation innovation = new NEATInnovation();
          final int innovationID = Integer.parseInt(cols.get(1));
          innovation.setInnovationID(innovationID);
          innovation.setNeuronID(Integer.parseInt(cols.get(2)));
          result.getInnovations().getInnovations().put(cols.get(0), innovation);
          nextInnovationID = Math.max(nextInnovationID, innovationID + 1);
        }
      } else if (section.getSectionName().equals("NEAT-POPULATION")
          && section.getSubSectionName().equals("SPECIES")) {
        NEATGenome lastGenome = null;
        BasicSpecies lastSpecies = null;

        for (final String line : section.getLines()) {
          final List<String> cols = EncogFileSection.splitColumns(line);

          if (cols.get(0).equalsIgnoreCase("s")) {
            lastSpecies = new BasicSpecies();
            lastSpecies.setPopulation(result);
            lastSpecies.setAge(Integer.parseInt(cols.get(1)));
            lastSpecies.setBestScore(CSVFormat.EG_FORMAT.parse(cols.get(2)));
            lastSpecies.setGensNoImprovement(Integer.parseInt(cols.get(3)));
            result.getSpecies().add(lastSpecies);
          } else if (cols.get(0).equalsIgnoreCase("g")) {
            final boolean isLeader = lastGenome == null;
            lastGenome = new NEATGenome();
            lastGenome.setInputCount(result.getInputCount());
            lastGenome.setOutputCount(result.getOutputCount());
            lastGenome.setSpecies(lastSpecies);
            lastGenome.setAdjustedScore(CSVFormat.EG_FORMAT.parse(cols.get(1)));
            lastGenome.setScore(CSVFormat.EG_FORMAT.parse(cols.get(2)));
            lastGenome.setBirthGeneration(Integer.parseInt(cols.get(3)));
            lastSpecies.add(lastGenome);
            if (isLeader) {
              lastSpecies.setLeader(lastGenome);
            }
          } else if (cols.get(0).equalsIgnoreCase("n")) {
            final NEATNeuronGene neuronGene = new NEATNeuronGene();
            final int geneID = Integer.parseInt(cols.get(1));
            neuronGene.setId(geneID);

            final ActivationFunction af = EncogFileSection.parseActivationFunction(cols.get(2));
            neuronGene.setActivationFunction(af);

            neuronGene.setNeuronType(PersistNEATPopulation.stringToNeuronType(cols.get(3)));
            neuronGene.setInnovationId(Integer.parseInt(cols.get(4)));
            lastGenome.getNeuronsChromosome().add(neuronGene);
            nextGeneID = Math.max(geneID + 1, nextGeneID);
          } else if (cols.get(0).equalsIgnoreCase("l")) {
            final NEATLinkGene linkGene = new NEATLinkGene();
            linkGene.setId(Integer.parseInt(cols.get(1)));
            linkGene.setEnabled(Integer.parseInt(cols.get(2)) > 0);
            linkGene.setFromNeuronID(Integer.parseInt(cols.get(3)));
            linkGene.setToNeuronID(Integer.parseInt(cols.get(4)));
            linkGene.setWeight(CSVFormat.EG_FORMAT.parse(cols.get(5)));
            linkGene.setInnovationId(Integer.parseInt(cols.get(6)));
            lastGenome.getLinksChromosome().add(linkGene);
          }
        }

      } else if (section.getSectionName().equals("NEAT-POPULATION")
          && section.getSubSectionName().equals("CONFIG")) {
        final Map<String, String> params = section.parseParams();

        final String afStr = params.get(NEATPopulation.PROPERTY_NEAT_ACTIVATION);

        if (afStr.equalsIgnoreCase(PersistNEATPopulation.TYPE_CPPN)) {
          HyperNEATGenome.buildCPPNActivationFunctions(result.getActivationFunctions());
        } else {
          result.setNEATActivationFunction(
              EncogFileSection.parseActivationFunction(
                  params, NEATPopulation.PROPERTY_NEAT_ACTIVATION));
        }

        result.setActivationCycles(
            EncogFileSection.parseInt(params, PersistConst.ACTIVATION_CYCLES));
        result.setInputCount(EncogFileSection.parseInt(params, PersistConst.INPUT_COUNT));
        result.setOutputCount(EncogFileSection.parseInt(params, PersistConst.OUTPUT_COUNT));
        result.setPopulationSize(
            EncogFileSection.parseInt(params, NEATPopulation.PROPERTY_POPULATION_SIZE));
        result.setSurvivalRate(
            EncogFileSection.parseDouble(params, NEATPopulation.PROPERTY_SURVIVAL_RATE));
        result.setActivationCycles(
            EncogFileSection.parseInt(params, NEATPopulation.PROPERTY_CYCLES));
      }
    }

    // set factories
    if (result.isHyperNEAT()) {
      result.setGenomeFactory(new FactorHyperNEATGenome());
      result.setCODEC(new HyperNEATCODEC());
    } else {
      result.setGenomeFactory(new FactorNEATGenome());
      result.setCODEC(new NEATCODEC());
    }

    // set the next ID's
    result.getInnovationIDGenerate().setCurrentID(nextInnovationID);
    result.getGeneIDGenerate().setCurrentID(nextGeneID);

    // find first genome, which should be the best genome
    if (result.getSpecies().size() > 0) {
      final Species species = result.getSpecies().get(0);
      if (species.getMembers().size() > 0) {
        result.setBestGenome(species.getMembers().get(0));
      }
    }

    return result;
  }