input: Write a python function to run a genetic algorithm to optimize a population for a given fitness function.
  assert isinstance(genetic_algorithm([random.uniform(-100, 100) for _ in range(100)]), list)
  assert len(genetic_algorithm([random.uniform(-100, 100) for _ in range(100)])) == 100
  assert isinstance(opt_solution, float)
output: import random
  
  def genetic_algorithm(population, fitness_fn=lambda x: x**2, generations=100, mutation_rate=0.01, crossover_rate=0.7):
      def select(pop, fit_fn):
          total_fitness = sum(fit_fn(ind) for ind in pop)
          probs = [fit_fn(ind) / total_fitness for ind in pop]
          return random.choices(pop, weights=probs, k=len(pop))
  
      def crossover(parent1, parent2):
          if random.random() < crossover_rate:
              point = random.randint(1, len(parent1) - 1)
              return parent1[:point] + parent2[point:], parent2[:point] + parent1[point:]
          return parent1, parent2
  
      def mutate(ind):
          if random.random() < mutation_rate:
              idx = random.randint(0, len(ind) - 1)
              ind[idx] = random.uniform(-100, 100)
          return ind
  
      for _ in range(generations):
          population = [mutate(child) for parent1, parent2 in zip(select(population, fitness_fn)[::2], select(population, fitness_fn)[1::2]) for child in crossover(parent1, parent2)]
          
      opt_solution = max(population, key=fitness_fn)
      return population