docs: def _get_fitnesses(self,
                         problem,
                         population,
                         cache_encoded=True,
                         cache_solution=False,
                         pool=None):
          """Get the fitness for every solution in a population.
  
          Args:
              problem: Problem; The problem that defines fitness.
              population: list; List of potential solutions.
              pool: None/multiprocessing.Pool; Pool of processes for parallel
                  decoding and evaluation.
          """
          fitnesses = [None] * len(population)
  
          #############################
          # Decoding
          #############################
          if cache_encoded:
              try:
                  encoded_keys = map(self._get_encoded_key, population)
  
                  # Get all fitnesses from encoded_solution cache
                  to_decode_indices = []
                  for i, encoded_key in enumerate(encoded_keys):
                      try:
                          fitnesses[i] = self.__encoded_cache[encoded_key]
                          # Note that this fitness will never be better than the current best
                          # because we have already evaluted it,
                          # Therefore, we do not need to worry about decoding the solution
                      except KeyError:  # Cache miss
                          to_decode_indices.append(i)
  
              except UnhashableError:  # Cannot hash encoded solution
                  encoded_keys = None
                  to_decode_indices = range(len(population))
          else:
              encoded_keys = None
              to_decode_indices = range(len(population))
  
          # Decode all that need to be decoded, and combine back into list the same length
          # as population
          if encoded_keys is None:
              to_decode_keys = None
          else:
              to_decode_keys = [encoded_keys[i] for i in to_decode_indices]
  
          solutions = [None] * len(population)
          for i, solution in zip(to_decode_indices,
                                 self._pmap(
                                     problem.decode_solution,
                                     [population[i] for i in to_decode_indices],
                                     to_decode_keys,
                                     pool)):
              solutions[i] = solution
  
          #############################
          # Evaluating
          #############################
          if cache_solution:
              try:
                  # Try to make solutions hashable
                  # Use user provided hash function if available
                  if problem.hash_solution:
                      hash_solution_func = problem.hash_solution
                  else:
                      # Otherwise, default to built in "smart" hash function
                      hash_solution_func = self._get_solution_key
  
                  solution_keys = [
                      hash_solution_func(solution)
                      # None corresponds to encoded_solutions found in cache
                      if solution is not None else None for solution in solutions
                  ]
  
                  # Get all fitnesses from solution cache
                  to_eval_indices = []
                  for i, solution_key in enumerate(solution_keys):
                      if solution_key is not None:  # Otherwise, fitness already found in encoded cache
                          try:
                              fitnesses[i] = self.__solution_cache[solution_key]
                          except KeyError:  # Cache miss
                              to_eval_indices.append(i)
  
              except UnhashableError:  # Cannot hash solution
                  solution_keys = None
                  to_eval_indices = to_decode_indices[:]
          else:
              solution_keys = None
              to_eval_indices = to_decode_indices[:]
  
          # Evaluate all that need to be evaluated, and combine back into fitnesses list
          if solution_keys is None:
              if encoded_keys is None:
                  # No way to detect duplicates
                  to_eval_keys = None
              else:
                  # Cannot use decoded keys, default to encoded keys
                  to_eval_keys = [encoded_keys[i] for i in to_eval_indices]
          else:
              to_eval_keys = [solution_keys[i] for i in to_eval_indices]
  
          finished = False
          eval_bookkeeping = {}
          for i, fitness_finished in zip(to_eval_indices,
                                         self._pmap(
                                             problem.get_fitness,
                                             [solutions[i] for i in to_eval_indices],
                                             to_eval_keys,
                                             pool,
                                             bookkeeping_dict=eval_bookkeeping)):
              # Unpack fitness_finished tuple
              try:
                  fitness, maybe_finished = fitness_finished
                  if maybe_finished:
                      finished = True
              except TypeError:  # Not (fitness, finished) tuple
                  fitness = fitness_finished
  
              fitnesses[i] = fitness
  
          #############################
          # Finishing
          #############################
          # Bookkeeping
          # keep track of how many times fitness is evaluated
          self.fitness_runs += len(eval_bookkeeping['key_indices'])  # Evaled once for each unique key
  
          # Add evaluated fitnesses to caches (both of them)
          if cache_encoded and encoded_keys is not None:
              for i in to_decode_indices:  # Encoded cache misses
                  self.__encoded_cache[encoded_keys[i]] = fitnesses[i]
          if cache_solution and solution_keys is not None:
              for i in to_eval_indices:  # Decoded cache misses
                  self.__solution_cache[solution_keys[i]] = fitnesses[i]
  
          # Return
          # assert None not in fitnesses  # Un-comment for debugging
          return solutions, fitnesses, finished