def optimize(self, problem, max_iterations=100, max_seconds=float('inf'),
                 cache_encoded=True, cache_solution=False, clear_cache=True,
                 logging_func=_print_fitnesses,
                 n_processes=0):
        if not isinstance(problem, Problem):
            raise TypeError('problem must be an instance of Problem class')
        if n_processes > 0:
            try:
                pool = multiprocessing.Pool(processes=n_processes)
            except NameError:
                raise ImportError(
                    'pickle, dill, or multiprocessing library is not available.'
                )
        else:
            pool = None
        self.__max_iterations = max_iterations
        self._reset()
        best_solution = {'solution': None, 'fitness': None}
        population = self.initial_population()
        try:
            start = time.clock()
            for self.iteration in itertools.count(1):
                solutions, fitnesses, finished = self._get_fitnesses(
                    problem,
                    population,
                    cache_encoded=cache_encoded,
                    cache_solution=cache_solution,
                    pool=pool)
                best_index, best_fitness = max(
                    enumerate(fitnesses), key=operator.itemgetter(1))
                if best_fitness > best_solution['fitness']:
                    best_solution['fitness'] = best_fitness
                    best_solution['solution'] = solutions[best_index]
                if logging_func:
                    logging_func(self.iteration, population, solutions,
                                 fitnesses, best_solution['solution'],
                                 best_solution['fitness'])
                if finished:
                    self.solution_found = True
                    break
                if time.clock() - start >= max_seconds:
                    break
                if self.iteration >= max_iterations:
                    break
                population = self.next_population(population, fitnesses)
            self.best_solution = best_solution['solution']
            self.best_fitness = best_solution['fitness']
        finally:
            if clear_cache:
                self.__encoded_cache = {}
                self.__solution_cache = {}
                self._get_encoded_key = self._get_encoded_key_type
                self._get_solution_key = self._get_solution_key_type
            try:
                pool.terminate()
                pool.close()
            except AttributeError:
                assert pool is None
        return self.best_solution