def _advance(self, **kwargs):
        """
        Advance the optimization algorithm by one iteration.
        """
        pop = self.pop
        X, F = pop.get("X", "F")

        # the number of solutions to sample from the population to detect the change
        n_samples = int(np.ceil(len(pop) * self.PERC_DETECT_CHANGE))

        # choose randomly some individuals of the current population to test if there was a change
        I = np.random.choice(np.arange(len(pop)), size=n_samples)
        samples = self.evaluator.eval(self.problem, Population.new(X=X[I]))

        # calculate the differences between the old and newly evaluated pop
        delta = ((samples.get("F") - F[I]) ** 2).mean()

        # archive the current POS
        self.add_to_ps()

        # if there is an average deviation bigger than eps -> we have a change detected
        change_detected = delta > self.EPS

        if change_detected:

            # increase t counter for unique key of PS
            self.t += 1

            # conduct knowledge reconstruction examination
            pop_useful, pop_useless, c = self.knowledge_reconstruction_examination()

            # Train a naive bayesian classifier
            model = self.naive_bayesian_classifier(pop_useful, pop_useless)

            # generate a lot of random solutions with the dimensions of problem decision space
            X_test = self.random_strategy(self.nr_rand_solutions)

            # introduce noise to vary previously useful solutions
            noise = np.random.normal(0, self.PERTURB_DEV, self.problem.n_var)
            noisy_useful_history = np.asarray(pop_useful) + noise

            # check whether solutions are within bounds
            noisy_useful_history = self.check_boundaries(noisy_useful_history)

            # add noisy useful history to randomly generated solutions
            X_test = np.vstack((X_test, noisy_useful_history))

            # predict whether random solutions are useful or useless
            Y_test = model.predict(X_test)

            # create list of useful predicted solutions
            predicted_pop = self.predicted_population(X_test, Y_test)

            # ------ POPULATION GENERATION --------
            # take a random sample from predicted pop and known useful pop

            nr_sampled_pop_useful = 0
            nr_random_filler_solutions = 0

            if len(predicted_pop) >= self.pop_size - self.C_SIZE:
                init_pop = []
                predicted_pop = random.sample(
                    predicted_pop, self.pop_size - self.C_SIZE
                )

                # add sampled solutions to init_pop
                for solution in predicted_pop:
                    init_pop.append(solution)

                # add cluster centroids to init_pop
                for solution in c:
                    init_pop.append(np.asarray(solution))

            else:

                # if not enough predicted solutions are available, add all predicted solutions to init_pop
                init_pop = []

                for solution in predicted_pop:
                    init_pop.append(solution)

                # add cluster centroids to init_pop
                for solution in c:
                    init_pop.append(np.asarray(solution))

            # if there are still not enough solutions in init_pop randomly sample previously useful solutions directly without noise to init_pop
            if len(init_pop) < self.pop_size:

                # fill up init_pop with randomly sampled solutions from pop_useful
                if len(pop_useful) >= self.pop_size - len(init_pop):

                    nr_sampled_pop_useful = self.pop_size - len(init_pop)

                    init_pop = np.vstack(
                        (
                            init_pop,
                            random.sample(pop_useful, self.pop_size - len(init_pop)),
                        )
                    )
                else:
                    # if not enough solutions are available, add all previously known useful solutions without noise to init_pop
                    for solution in pop_useful:
                        init_pop.append(solution)

                    nr_sampled_pop_useful = len(pop_useful)

            # if there are still not enough solutions in init_pop generate random solutions with the dimensions of problem decision space
            if len(init_pop) < self.pop_size:

                nr_random_filler_solutions = self.pop_size - len(init_pop)

                # fill up with random solutions
                init_pop = np.vstack(
                    (init_pop, self.random_strategy(self.pop_size - len(init_pop)))
                )

            # recreate the current population without being evaluated
            pop = Population.new(X=init_pop)

            # reevaluate because we know there was a change
            self.evaluator.eval(self.problem, pop)

            # do a survival to recreate rank and crowding of all individuals
            pop = self.survival.do(self.problem, pop, n_survive=len(pop))

        # create the offsprings from the current population
        off = self.mating.do(self.problem, pop, self.n_offsprings, algorithm=self)
        self.evaluator.eval(self.problem, off)

        # merge the parent population and offsprings
        pop = Population.merge(pop, off)

        # execute the survival to find the fittest solutions
        self.pop = self.survival.do(
            self.problem, pop, n_survive=self.pop_size, algorithm=self
        )

        # dump self.ps to file
        if self.save_ps:
            with open("ps.json", "w") as fp:
                json.dump(self.ps, fp)

        Advance the optimization algorithm by one iteration.
        
pymoo/algorithms/moo/kgb.py