class_docstr: An implementation of a Genetic Algorithm which will try to produce the user
  specified target string.
  
  Parameters:
  -----------
  target_string: string
      The string which the GA should try to produce.
  population_size: int
      The number of individuals (possible solutions) in the population.
  mutation_rate: float
      The rate (or probability) of which the alleles (chars in this case) should be
      randomly changed.
code: def run(self, iterations):
      # Initialize new population
      self._initialize()
  
      for epoch in range(iterations):
          population_fitness = self._calculate_fitness()
  
          fittest_individual = self.population[
              np.argmax(population_fitness)
          ]
          highest_fitness = max(population_fitness)
  
          # If we have found individual which matches the target => Done
          if fittest_individual == self.target:
              break
  
          # Set the probability that the individual should be selected as a parent
          # proportionate to the individual's fitness.
          parent_probabilities = [
              fitness / sum(population_fitness)
              for fitness in population_fitness
          ]
  
          # Determine the next generation
          new_population = []
          for i in np.arange(0, self.population_size, 2):
              # Select two parents randomly according to probabilities
              parent1, parent2 = np.random.choice(
                  self.population,
                  size=2,
                  p=parent_probabilities,
                  replace=False,
              )
              # Perform crossover to produce offspring
              child1, child2 = self._crossover(parent1, parent2)
              # Save mutated offspring for next generation
              new_population += [
                  self._mutate(child1),
                  self._mutate(child2),
              ]
  
          print(
              "[%d Closest Candidate: '%s', Fitness: %.2f]"
              % (epoch, fittest_individual, highest_fitness)
          )
          self.population = new_population
  
      print("[%d Answer: '%s']" % (epoch, fittest_individual))
  
file_path: ML-From-Scratch/mlfromscratch/unsupervised_learning/genetic_algorithm.py
imports: from __future__ import print_function, division
  import string
  import numpy
label_desc: Use image node_id 6 for calling the GeneticAlgorithm obj's underlying member method code with example usage: obj.run(iterations) without return types