Exemplo n.º 1
0
    def _advance(self, infills=None, **kwargs):
        self.norm.update(infills)

        pop = Population.merge(Population.merge(self.pop, infills),
                               self.archive)

        F = pop.get("F")

        if self.problem.has_constraints():
            G = pop.get("G")
            C = np.maximum(0, G)
            C = C / np.maximum(1, C.max(axis=0))
            CV = C.sum(axis=1)
        else:
            CV = np.zeros(len(pop))

        F = F - self.norm.ideal(only_feas=False) + 1e-6

        indicator = np.full((len(pop), len(pop)), np.inf)

        for i in range(len(pop)):
            Ci = CV[i]
            Fi = F[i]

            if Ci == 0:

                Ir = np.log(Fi / F)
                MaxIr = np.max(Ir, axis=1)
                MinIr = np.min(Ir, axis=1)

                CVA = MaxIr

                J = MaxIr <= 0
                CVA[J] = MinIr[J]

                val = CVA

            else:
                IC = (Ci + 1e-6) / (CV + 1e-6)
                CVF = np.max(np.maximum(Fi, F) / np.minimum(Fi, F), axis=1)
                val = np.log(np.maximum(CVF, IC))

            indicator[:, i] = val
            indicator[i, i] = np.inf

        feas = np.where(CV <= 0)[0]

        if len(feas) <= self.pop_size:
            self.archive = pop[np.argsort(CV)[:self.pop_size]]
        else:
            feas_F = pop[feas].get("F") - self.norm.ideal(
                only_feas=True) + 1e-6
            I = Selection_Operator_of_PREA(feas_F, indicator[feas][:, feas],
                                           self.pop_size)
            self.archive = pop[feas[I]]

        I = Indicator_based_CHT(F, indicator, self.ref_dirs, self.pop_size)
        self.pop = pop[I]

        self.Ra = 1 - self.n_gen / self.termination.n_max_gen
Exemplo n.º 2
0
def update_ep(EP, Offsprings, nEP, nds):
    """
    Update the external population archive
    """

    # merge population and keep only the first non-dominated front
    EP = Population.merge(EP, Offsprings)
    EP = EP[nds.do(EP.get("F"), only_non_dominated_front=True)]
    N, M = EP.get("F").shape

    # Delete the overcrowded solutions
    D = cdist(EP.get("F"), EP.get("F"))

    # prevent selection of a point with itself
    D[np.eye(len(D), dtype=np.bool)] = np.inf

    removed = np.zeros(N, dtype=np.bool)
    while sum(removed) < N - nEP:
        remain = np.flatnonzero(~removed)
        subDis = np.sort(D[np.ix_(remain, remain)], axis=1)

        # compute viscinity distance among the closest neighbors
        prodDist = np.prod(subDis[:, 0:min(M, len(remain))], axis=1)

        # select the point with the smallest viscinity distance to its neighbors and set it as removed
        worst = np.argmin(prodDist)
        removed[remain[worst]] = True

    return EP[~removed]
Exemplo n.º 3
0
def update_PCpop(pc_pop, off):

    pc_objs = pc_pop.get("F")
    off_objs = off.get("F")
    n = pc_objs.shape[0]

    del_ind = []

    for i in range(n):
        flag = Dominator.get_relation(off_objs[0, :], pc_objs[i, :])
        if flag == 1:
            # off dominates pc_pop[i]
            del_ind.append(i)
            break

        elif flag == -1:
            # pc_pop[i] dominates off
            return pc_pop
        else:
            # flag == 0
            # off and pc_pop[i] are nondominated
            break

    if len(del_ind) > 0:
        pc_index = np.arange(n)
        # Delete element at index positions given by the list 'del_ind'
        pc_index = np.delete(pc_index, del_ind)
        pc_pop = pc_pop[pc_index.tolist()]

    pc_pop = Population.merge(pc_pop, off)

    return pc_pop
Exemplo n.º 4
0
def test_restricted_mating_selection(ref_dirs, evaluator):
    np.random.seed(200)
    selection = RestrictedMating(func_comp=comp_by_cv_dom_then_random)

    problem = C3DTLZ4(n_var=12, n_obj=3)
    ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preCA.x'))
    CA = Population.create(ca_x)
    evaluator.eval(problem, CA)

    da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preDA.x'))
    DA = Population.create(da_x)
    evaluator.eval(problem, DA)

    Hm = Population.merge(CA, DA)
    n_pop = len(CA)

    _, rank = NonDominatedSorting().do(Hm.get('F'), return_rank=True)

    Pc = (rank[:n_pop] == 0).sum() / len(Hm)
    Pd = (rank[n_pop:] == 0).sum() / len(Hm)

    P = selection.do(Hm, len(CA))

    assert P.shape == (91, 2)
    if Pc > Pd:
        assert (P[:, 0] < n_pop).all()
    else:
        assert (P[:, 0] >= n_pop).all()
    assert (P[:, 1] >= n_pop).any()
    assert (P[:, 1] < n_pop).any()
Exemplo n.º 5
0
def test_association(ref_dirs, evaluator):
    problem = C1DTLZ3(n_var=12, n_obj=3)
    ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'preCA.x'))
    CA = Population.create(ca_x)
    evaluator.eval(problem, CA)

    da_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'preDA.x'))
    DA = Population.create(da_x)
    evaluator.eval(problem, DA)

    off_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'offspring.x'))
    off = Population.create(off_x)
    evaluator.eval(problem, off)

    true_assoc = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'feasible_rank0.txt'))
    true_niche = true_assoc[:, 1]
    true_id = true_assoc[:, 0]
    sorted_id = np.argsort(true_id)

    survival = CADASurvival(ref_dirs)
    mixed = Population.merge(CA, off)
    survival.ideal_point = np.min(np.vstack((DA.get("F"), mixed.get("F"))), axis=0)

    fronts = NonDominatedSorting().do(mixed.get("F"), n_stop_if_ranked=len(ref_dirs))
    I = np.concatenate(fronts)
    niche, _ = survival._associate(mixed[I])
    sorted_I = np.argsort(I)

    assert (niche[sorted_I] == true_niche[sorted_id]).all()
Exemplo n.º 6
0
    def _next(self):
        sol = self.opt[0]
        x = sol.get("X")
        jac, hess = self.gradient_and_hessian(sol)

        method = "cholesky"
        func = direction_cholesky if method == "cholesky" else direction_inv
        direction, decrement, adapted, success = func(jac, hess)

        # in case we can not calculate the newton direction fall back to the gradient approach
        if not success:
            direction = -jac
            decrement = np.linalg.norm(direction) ** 2

        if self.damped:
            line = LineSearchProblem(self.problem, sol, direction)
            _next = WolfeSearch().setup(line, evaluator=self.evaluator).run()
            # _next = wolfe_line_search(self.problem, sol, direction, evaluator=self.evaluator)

            # if the newton step was not successful, then try the gradient
            if adapted and (sol.F[0] - _next.F[0]) < 1e-16:
                _next = inexact_line_search(self.problem, sol, -jac, evaluator=self.evaluator)

        else:
            _x = x + direction
            _next = Solution(X=_x)

        if isinstance(_next, Individual):
            _next = Population.create(_next)

        self.pop = Population.merge(self.opt, _next)

        if decrement / 2 <= self.eps:
            self.termination.force_termination = True
Exemplo n.º 7
0
 def _advance(self, infills=None, **kwargs):
     assert infills is not None, "This algorithms uses the AskAndTell interface thus infills must to be provided."
     pop = Population.merge(self.pop, infills)
     self.pop, self.da = self.survival.do(self.problem,
                                          pop,
                                          self.da,
                                          self.pop_size,
                                          algorithm=self)
Exemplo n.º 8
0
def test_update_da(ref_dirs, evaluator):
    problem = C1DTLZ3(n_var=12, n_obj=3)
    for i in range(2):
        ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'preCA.x'))
        CA = Population.create(ca_x)
        evaluator.eval(problem, CA)

        da_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'preDA.x'))
        DA = Population.create(da_x)
        evaluator.eval(problem, DA)

        off_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'offspring.x'))
        off = Population.create(off_x)
        evaluator.eval(problem, off)

        survival = CADASurvival(ref_dirs)
        mixed = Population.merge(CA, off)
        survival.ideal_point = np.min(np.vstack((DA.get("F"), mixed.get("F"))), axis=0)

        post_ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'postCA.x'))
        CA = Population.create(post_ca_x)
        evaluator.eval(problem, CA)

        Hd = Population.merge(DA, off)
        pDA = survival._updateDA(CA, Hd, 91)

        true_S1 = [151, 35, 6, 63, 67, 24, 178, 106, 134, 172, 148, 159, 41,
                   173, 145, 77, 62, 40, 127, 61, 130, 27, 171, 115, 52, 176,
                   22, 75, 55, 87, 36, 149, 154, 47, 78, 170, 90, 15, 53, 175,
                   179, 165, 56, 89, 132, 82, 141, 39, 32, 25, 131, 14, 72, 65,
                   177, 140, 66, 143, 34, 81, 103, 99, 147, 168, 51, 26, 70, 94,
                   54, 97, 158, 107, 29, 120, 50, 108, 157, 11, 85, 174, 80, 0,
                   95, 13, 142, 101, 156, 19, 8, 98, 20]

        true_S2 = [78, 173, 59, 21, 101, 52, 36, 94, 17, 20, 37, 96, 90, 129,
                   150, 136, 162, 70, 146, 75, 138, 154, 65, 179, 98, 32, 97,
                   11, 26, 107, 12, 128, 95, 170, 24, 171, 40, 180, 14, 44, 49,
                   43, 130, 23, 60, 79, 148, 62, 87, 56, 157, 73, 104, 45, 177,
                   74, 15, 152, 164, 28, 80, 113, 41, 33, 158, 57, 77, 34, 114,
                   118, 18, 54, 53, 145, 93, 115, 121, 174, 142, 39, 13, 105,
                   10, 69, 120, 55, 6, 153, 91, 137, 46]
        if i == 0:
            assert np.all(pDA == Hd[true_S1])
        else:
            assert np.all(pDA == Hd[true_S2])
Exemplo n.º 9
0
    def _do(self, problem, pop, off, algorithm=None, **kwargs):

        if self.cnt is None:
            self.cnt = np.zeros(len(pop), dtype=int)

        cnt = self.cnt

        cv = pop.get("CV")[:, 0]

        # cnt = self.cnt
        cnt = algorithm.n_gen - pop.get("n_gen") - 1

        # make sure we never replace the best solution if we would consider feasibility first
        best = FitnessSurvival().do(problem,
                                    Population.merge(pop, off),
                                    n_survive=1)[0]

        eps = np.zeros(len(pop))

        for k, t in enumerate(cnt):

            # cycle = (t // (4 * self.t))
            # max_eps = (2 ** cycle) * self.eps

            max_eps = self.eps

            t = t % (4 * self.t)

            if t < self.t:
                eps[k] = cv[k] + (max_eps - cv[k]) * (t / self.t)
            elif t < 2 * self.t:
                eps[k] = max_eps
            elif t < 3 * self.t:
                eps[k] = max_eps * (1 - ((t % self.t) / self.t))
            else:
                eps[k] = 0.0

        eps_is_zero = np.where(eps <= 0)[0]

        # print(len(eps_is_zero))

        repl = np.full(len(pop), False)
        for k in range(len(pop)):

            if pop[k] == best:
                repl[k] = False
            elif off[k] == best:
                repl[k] = True

            else:
                if rel_eps_constr(pop[k], off[k], eps[k]) <= 0:
                    repl[k] = True

        # self.cnt[repl] = 0
        # self.cnt[~repl] += 1

        return repl
Exemplo n.º 10
0
    def _advance(self, infills=None, **kwargs):

        # if not all solutions suggested by infill() are evaluated we create a more semi (mu+lambda) algorithm
        if len(infills) < self.pop_size:
            infills = Population.merge(infills, self.pop)

        self.pop = self.survival.do(self.problem,
                                    infills,
                                    n_survive=self.pop_size)
Exemplo n.º 11
0
def crossover(crossover,
              a,
              b,
              c=None,
              xl=0,
              xu=1,
              type_var=np.double,
              **kwargs):
    n = a.shape[0]
    _pop = Population.merge(Population.new("X", a), Population.new("X", b))
    _P = np.column_stack([np.arange(n), np.arange(n) + n])

    if c is not None:
        _pop = Population.merge(_pop, Population.new("X", c))
        _P = np.column_stack([_P, np.arange(n) + 2 * n])

    problem = get_problem_func(a.shape[1], xl, xu, type_var)(**kwargs)
    return crossover.do(problem, _pop, _P, **kwargs).get("X")
Exemplo n.º 12
0
    def do(self,
           problem,
           pop,
           *args,
           n_survive=None,
           return_indices=False,
           **kwargs):

        # make sure the population has at least one individual
        if len(pop) == 0:
            return pop

        if n_survive is None:
            n_survive = len(pop)

        n_survive = min(n_survive, len(pop))

        # if the split should be done beforehand
        if self.filter_infeasible and problem.n_constr > 0:

            # split feasible and infeasible solutions
            feas, infeas = split_by_feasibility(pop,
                                                eps=0.0,
                                                sort_infeasbible_by_cv=True)

            if len(feas) == 0:
                survivors = Population()
            else:
                survivors = self._do(problem,
                                     pop[feas],
                                     *args,
                                     n_survive=min(len(feas), n_survive),
                                     **kwargs)

            # calculate how many individuals are still remaining to be filled up with infeasible ones
            n_remaining = n_survive - len(survivors)

            # if infeasible solutions needs to be added
            if n_remaining > 0:
                survivors = Population.merge(survivors,
                                             pop[infeas[:n_remaining]])

        else:
            survivors = self._do(problem,
                                 pop,
                                 *args,
                                 n_survive=n_survive,
                                 **kwargs)

        if return_indices:
            H = {}
            for k, ind in enumerate(pop):
                H[ind] = k
            return [H[survivor] for survivor in survivors]
        else:
            return survivors
Exemplo n.º 13
0
    def _infill(self):
        pop = self.pop

        # actually do the mating given the elite selection and biased crossover
        off = self.mating.do(self.problem, pop, n_offsprings=self.n_offsprings, algorithm=self)

        # create the mutants randomly to fill the population with
        mutants = FloatRandomSampling().do(self.problem, self.n_mutants, algorithm=self)

        # evaluate all the new solutions
        return Population.merge(off, mutants)
Exemplo n.º 14
0
    def _advance(self, infills=None):
        pop = self.pop

        # get all the elites from the current population
        elites = np.where(pop.get("type") == "elite")[0]

        # finally merge everything together and sort by fitness
        pop = Population.merge(pop[elites], infills)

        # the do survival selection - set the elites for the next round
        self.pop = self.survival.do(self.problem, pop, n_survive=len(pop), algorithm=self)
Exemplo n.º 15
0
    def _advance(self, infills=None, **kwargs):

        # merge the offsprings with the current population
        if infills is not None:
            self.pop = Population.merge(self.pop, infills)

        # execute the survival to find the fittest solutions
        self.pop = self.survival.do(self.problem,
                                    self.pop,
                                    n_survive=self.pop_size,
                                    algorithm=self)
Exemplo n.º 16
0
 def do(self, _, pop, da, n_survive=None, **kwargs):
     # Offspring are last of merged population
     off = pop[-n_survive:]
     # Update ideal point
     self.ideal_point = np.min(np.vstack((self.ideal_point, off.get("F"))),
                               axis=0)
     # Update CA
     pop = self._updateCA(pop, n_survive)
     # Update DA
     Hd = Population.merge(da, off)
     da = self._updateDA(pop, Hd, n_survive)
     return pop, da
Exemplo n.º 17
0
    def _advance(self, infills=None, **kwargs):
        assert infills is not None, "This algorithms uses the AskAndTell interface thus infills must to be provided."

        pop = Population.merge(self.pop, infills)
        F, CV = pop.get("F", "CV")
        f, cv = F[:, 0, ], CV[:, 0]

        S = hierarchical_sort(f, cv)
        pop, f, cv, feas = pop[S], f[S], cv[S], cv[S] <= 0

        survivors = list(range(30))
        n_remaining = self.pop_size - len(survivors)

        if feas.sum() > 0:
            f_min, cv_min = f[feas].min(), 0.0
        else:
            f_min, cv_min = np.inf, cv[~feas].min()

        I = np.where(np.logical_and(~feas, f < f_min))[0]
        # survivors.extend(I[:n_remaining])
        survivors.extend(I[cv[I].argsort()][:n_remaining])

        if len(survivors) < n_remaining:
            I = np.where(np.logical_and(~feas, f >= f_min))[0]
            survivors.extend(I[f[I].argsort()][:n_remaining])
            # survivors.extend(np.random.choice(I, size=n_remaining))

        if self.n_gen > 1000:
            import matplotlib.pyplot as plt

            plt.scatter(cv[~feas],
                        f[~feas],
                        facecolor="none",
                        edgecolors="red",
                        alpha=0.5,
                        s=20)
            plt.scatter(cv[feas],
                        f[feas],
                        facecolor="none",
                        edgecolors="blue",
                        alpha=0.5,
                        s=40)

            plt.scatter(cv[survivors],
                        f[survivors],
                        color="black",
                        s=3,
                        alpha=0.9)

            plt.show()

        self.pop = pop[survivors]
        self.pop.set("below", self.pop.get("F")[:, 0] <= f_min)
Exemplo n.º 18
0
    def step(self):
        alpha, max_alpha = self.alpha, self.problem.xu[0]

        if alpha > max_alpha:
            alpha = max_alpha

        infill = Individual(X=np.array([alpha]))
        self.evaluator.eval(self.problem, infill)
        self.pop = Population.merge(self.pop, infill)[-10:]

        if is_better(self.point, infill, eps=0.0) or alpha == max_alpha:
            self.termination.force_termination = True
            return

        self.point = infill
        self.alpha *= 2
Exemplo n.º 19
0
    def _infill(self, **kwargs):

        pop, archive, Ra = self.pop, self.archive, self.Ra

        N = self.pop_size
        Nt = int(np.floor(Ra * N))

        from_pop = pop[permutation(len(pop))[:Nt]]
        from_archive = pop[permutation(len(archive))[:N - Nt]]
        pool = Population.merge(from_pop, from_archive)

        # first do the differential evolution mating
        off = self.mating.do(self.problem,
                             pool,
                             self.n_offsprings,
                             algorithm=self,
                             ideal=self.norm.ideal(only_feas=False))

        return off
Exemplo n.º 20
0
    def _local_initialize_infill(self):
        self.alpha, self.beta, self.gamma, self.delta = self.func_params(self.problem)

        # the corresponding x values of the provided or best found solution
        x0 = self.x0.X

        # if lower and upper bounds are given take 5% of the range and add
        if self.problem.has_bounds():
            self.simplex_scaling = 0.05 * (self.problem.xu - self.problem.xl)

        # no bounds are given do it based on x0 - MATLAB procedure
        else:
            self.simplex_scaling = 0.05 * self.x0.X
            # some value needs to be added if x0 is zero
            self.simplex_scaling[self.simplex_scaling == 0] = 0.00025

        # initialize the simplex
        simplex = pop_from_array_or_individual(self.initialize_simplex(x0))

        return Population.merge(self.x0, simplex)
Exemplo n.º 21
0
    def _infill(self):

        # the offspring population to finally evaluate and attach to the population
        infills = Population()

        # find the potential optimal solution in the current population
        potential_optimal = self._potential_optimal()

        # for each of those solutions execute the division move
        for current in potential_optimal:

            # find the largest dimension the solution has not been evaluated yet
            nxl, nxu = norm_bounds(current, self.problem)
            k = np.argmax(nxu - nxl)

            # the delta value to be used to get left and right - this is one sixth of the range
            xl, xu = current.get("xl"), current.get("xu")

            delta = (xu[k] - xl[k]) / 6

            # create the left individual
            left_x = np.copy(current.X)
            left_x[k] = xl[k] + delta
            left = Individual(X=left_x)

            # create the right individual
            right_x = np.copy(current.X)
            right_x[k] = xu[k] - delta
            right = Individual(X=right_x)

            # update the boundaries for all the points accordingly
            for ind in [current, left, right]:
                update_bounds(ind, xl, xu, k, delta)

            # create the offspring population, evaluate and attach to current population
            _infill = Population.create(left, right)
            _infill.set("depth", current.get("depth") + 1)

            infills = Population.merge(infills, _infill)

        return infills
Exemplo n.º 22
0
    def do(self, problem, pop, n_offsprings, **kwargs):

        # the population object to be used
        off = pop.new()

        # infill counter - counts how often the mating needs to be done to fill up n_offsprings
        n_infills = 0

        # iterate until enough offsprings are created
        while len(off) < n_offsprings:

            # how many offsprings are remaining to be created
            n_remaining = n_offsprings - len(off)

            # do the mating
            _off = self._do(problem, pop, n_remaining, **kwargs)

            # repair the individuals if necessary - disabled if repair is NoRepair
            _off = self.repair.do(problem, _off, **kwargs)

            # eliminate the duplicates - disabled if it is NoRepair
            _off = self.eliminate_duplicates.do(_off, pop, off)

            # if more offsprings than necessary - truncate them randomly
            if len(off) + len(_off) > n_offsprings:

                # IMPORTANT: Interestingly, this makes a difference in performance
                n_remaining = n_offsprings - len(off)
                _off = _off[:n_remaining]

            # add to the offsprings and increase the mating counter
            off = Population.merge(off, _off)
            n_infills += 1

            # if no new offsprings can be generated within a pre-specified number of generations
            if n_infills >= self.n_max_iterations:
                break

        return off
Exemplo n.º 23
0
def test_update(ref_dirs, evaluator):
    problem = C3DTLZ4(n_var=12, n_obj=3)
    ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preCA.x'))
    CA = Population.create(ca_x)
    evaluator.eval(problem, CA)

    da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preDA.x'))
    DA = Population.create(da_x)
    evaluator.eval(problem, DA)

    off_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'offspring.x'))
    off = Population.create(off_x)
    evaluator.eval(problem, off)

    post_ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'postCA.x'))
    true_pCA = Population.create(post_ca_x)
    evaluator.eval(problem, true_pCA)

    post_da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'postDA.x'))
    true_pDA = Population.create(post_da_x)
    evaluator.eval(problem, true_pDA)

    survival = CADASurvival(ref_dirs)
    mixed = Population.merge(CA, off)
    survival.ideal_point = np.array([0., 0., 0.])

    pCA, pDA = survival.do(problem, mixed, DA, len(ref_dirs))

    pCA_X = set([tuple(x) for x in pCA.get("X")])
    tpCA_X = set([tuple(x) for x in true_pCA.get("X")])

    pDA_X = set([tuple(x) for x in pDA.get("X")])
    tpDA_X = set([tuple(x) for x in true_pDA.get("X")])

    assert pCA_X == tpCA_X
    assert pDA_X == tpDA_X
Exemplo n.º 24
0
 def _advance(self, infills=None):
     self.pop = infills if self.opt is None else Population.merge(infills, self.opt)
Exemplo n.º 25
0
    def _advance(self, **kwargs):
        repair, crossover, mutation = self.repair, self.mating.crossover, self.mating.mutation

        pc_pop = self.pc_pop.copy(deep=True)
        npc_pop = self.npc_pop.copy(deep=True)

        ##############################################################
        # PC evolving
        ##############################################################

        # Normalise both poulations according to the PC individuals
        pc_pop, npc_pop = normalize_bothpop(pc_pop, npc_pop)

        PCObj = pc_pop.get("F")
        NPCObj = npc_pop.get("F")
        pc_size = PCObj.shape[0]
        npc_size = NPCObj.shape[0]

        ######################################################
        # Calculate the Euclidean distance among individuals
        ######################################################
        d = np.zeros((pc_size, pc_size))
        d = cdist(PCObj, PCObj, 'euclidean')
        d[d == 0] = np.inf

        # Determine the size of the niche
        if pc_size == 1:
            radius = 0
        else:
            radius = determine_radius(d, pc_size, self.pc_capacity)

        # calculate the radius for individual exploration
        r = pc_size / self.pc_capacity * radius

        ########################################################
        # find the promising individuals in PC for exploration
        ########################################################

        # promising_num: record how many promising individuals in PC
        promising_num = 0
        # count: record how many NPC individuals are located in each PC individual's niche
        count = np.array([])

        d2 = np.zeros((pc_size, npc_size))
        d2 = cdist(PCObj, NPCObj, 'euclidean')

        # Count of True elements in each row (each individual in PC) of 2D Numpy Array
        count = np.count_nonzero(d2 <= r, axis=1)

        # Check if the niche has no NPC individual or has only one NPC individual
        # Record the indices of promising individuals.
        # Since np.nonzero() returns a tuple of arrays, we change the type of promising_index to a numpy.ndarray.
        promising_index = np.nonzero(count <= 1)
        promising_index = np.asarray(promising_index).flatten()

        # Record total number of promising individuals in PC for exploration
        promising_num = len(promising_index)

        ########################################
        # explore these promising individuals
        ########################################

        original_size = pc_size
        off = Individual()

        if promising_num > 0:
            for i in range(promising_num):
                if original_size > 1:
                    parents = Population.new(2)

                    # The explored individual is considered as one parent
                    parents[0] = pc_pop[promising_index[i]]

                    # The remaining parent will be selected randomly from the PC population
                    rnd = np.random.permutation(pc_size)

                    for j in rnd:
                        if j != promising_index[i]:
                            parents[1] = pc_pop[j]
                            break

                    index = np.array([0, 1])
                    parents_shape = index[None, :]

                    # do recombination and create an offspring
                    off = crossover.do(self.problem, parents, parents_shape)[0]

                else:
                    off = pc_pop[0]

                # mutation
                off = Population.create(off)
                off = mutation.do(self.problem, off)

                # evaluate the offspring
                self.evaluator.eval(self.problem, off)

                # update the PC population by the offspring
                self.pc_pop = update_PCpop(self.pc_pop, off)

                # update the ideal point
                self.ideal = np.min(np.vstack([self.ideal,
                                               off.get("F")]),
                                    axis=0)

                # update at most one solution in NPC population
                self.npc_pop = update_NPCpop(self.npc_pop, off, self.ideal,
                                             self.ref_dirs, self.decomp)

        ########################################################
        # NPC evolution based on MOEA/D
        ########################################################

        # iterate for each member of the population in random order
        for i in np.random.permutation(len(self.npc_pop)):
            # get the parents using the neighborhood selection
            P = self.selection.do(self.npc_pop,
                                  1,
                                  self.mating.crossover.n_parents,
                                  k=[i])

            # perform a mating using the default operators (recombination & mutation) - if more than one offspring just pick the first
            off = self.mating.do(self.problem, self.npc_pop, 1, parents=P)[0]

            off = Population.create(off)

            # evaluate the offspring
            self.evaluator.eval(self.problem, off, algorithm=self)

            # update the PC population by the offspring
            self.pc_pop = update_PCpop(self.pc_pop, off)

            # update the ideal point
            self.ideal = np.min(np.vstack([self.ideal, off.get("F")]), axis=0)

            # now actually do the replacement of the individual is better
            self.npc_pop = self._replace(i, off)

        ########################################################
        # population maintenance operation in the PC evolution
        ########################################################

        current_pop = Population.merge(self.pc_pop, self.npc_pop)
        current_pop = Population.merge(current_pop, self.pop)

        # filter duplicate in the population
        pc_pop = self.eliminate_duplicates.do(current_pop)

        pc_size = len(pc_pop)

        if (pc_size > self.pc_capacity):

            # get the objective space values and objects
            pc_objs = pc_pop.get("F")

            fronts, rank = NonDominatedSorting().do(pc_objs, return_rank=True)
            front_0_index = fronts[0]

            # put the nondominated individuals of the NPC population into the PC population
            self.pc_pop = pc_pop[front_0_index]

            if len(self.pc_pop) > self.pc_capacity:
                self.pc_pop = maintain_PCpop(self.pc_pop, self.pc_capacity)

        self.pop = self.pc_pop.copy(deep=True)
Exemplo n.º 26
0
 def _set_optimum(self):
     pop = self.pop if self.opt is None else Population.merge(
         self.opt, self.pop)
     self.opt = filter_optimum(pop, least_infeasible=True)
Exemplo n.º 27
0
    def _updateCA(self, pop, n_survive):
        """Update the Convergence archive (CA)"""
        CV = pop.get("CV").flatten()

        Sc = pop[CV == 0]  # Feasible population
        if len(Sc) == n_survive:  # Exactly n_survive feasible individuals
            F = Sc.get("F")
            fronts, rank = NonDominatedSorting().do(F, return_rank=True)
            Sc.set('rank', rank)
            self.opt = Sc[fronts[0]]
            return Sc
        elif len(Sc) < n_survive:  # Not enough feasible individuals
            remainder = n_survive - len(Sc)
            # Solve sub-problem CV, tche
            SI = pop[CV > 0]
            f1 = SI.get("CV")
            _, f2 = self._associate(SI)
            sub_F = np.column_stack([f1, f2])
            fronts = NonDominatedSorting().do(sub_F,
                                              n_stop_if_ranked=remainder)
            I = []
            for front in fronts:
                if len(I) + len(front) <= remainder:
                    I.extend(front)
                else:
                    n_missing = remainder - len(I)
                    last_front_CV = np.argsort(f1.flatten()[front])
                    I.extend(front[last_front_CV[:n_missing]])
            SI = SI[I]
            S = Population.merge(Sc, SI)
            F = S.get("F")
            fronts, rank = NonDominatedSorting().do(F, return_rank=True)
            S.set('rank', rank)
            self.opt = S[fronts[0]]
            return S
        else:  # Too many feasible individuals
            F = Sc.get("F")
            # Filter by non-dominated sorting
            fronts, rank = NonDominatedSorting().do(F,
                                                    return_rank=True,
                                                    n_stop_if_ranked=n_survive)
            I = np.concatenate(fronts)
            S, rank, F = Sc[I], rank[I], F[I]
            if len(S) > n_survive:
                # Remove individual in most crowded niche and with worst fitness
                niche_of_individuals, FV = self._associate(S)
                index, count = np.unique(niche_of_individuals,
                                         return_counts=True)
                survivors = np.full(S.shape[0], True)
                while survivors.sum() > n_survive:
                    crowdest_niches, = np.where(count == count.max())
                    worst_idx = None
                    worst_niche = None
                    worst_fit = -1
                    for crowdest_niche in crowdest_niches:
                        crowdest, = np.where(
                            (niche_of_individuals == index[crowdest_niche])
                            & survivors)
                        niche_worst = crowdest[FV[crowdest].argmax()]
                        dist_to_max_fit = cdist(F[[niche_worst], :],
                                                F).flatten()
                        dist_to_max_fit[niche_worst] = np.inf
                        dist_to_max_fit[~survivors] = np.inf
                        min_d_to_max_fit = dist_to_max_fit.min()

                        dist_in_niche = squareform(pdist(F[crowdest]))
                        np.fill_diagonal(dist_in_niche, np.inf)

                        delta_d = dist_in_niche - min_d_to_max_fit
                        min_d_i = np.unravel_index(
                            np.argmin(delta_d, axis=None), dist_in_niche.shape)
                        if (delta_d[min_d_i] < 0) or (
                                delta_d[min_d_i] == 0 and
                            (FV[crowdest[list(min_d_i)]] > niche_worst).any()):
                            min_d_i = list(min_d_i)
                            np.random.shuffle(min_d_i)
                            closest = crowdest[min_d_i]
                            niche_worst = closest[np.argmax(FV[closest])]
                        if FV[niche_worst] > worst_fit:
                            worst_fit = FV[niche_worst]
                            worst_idx = niche_worst
                            worst_niche = crowdest_niche
                    survivors[worst_idx] = False
                    count[worst_niche] -= 1
                S, rank = S[survivors], rank[survivors]
            S.set('rank', rank)
            self.opt = S[rank == 0]
            return S
Exemplo n.º 28
0
def update_weight(pop, W, Z, EP, nus):
    """
    Delete crowded weight vectors and add new ones
    """

    N, M = pop.get("F").shape

    # Update the current population by EP
    # Calculate the function value of each solution in Population or EP on each subproblem in W
    combined = Population.merge(pop, EP)
    combinedF = np.abs(combined.get("F") - Z)
    g = np.zeros((len(combined), W.shape[0]))
    for i in range(W.shape[0]):
        g[:, i] = np.max(combinedF * W[i, :], axis=1)

    # Choose the best solution for each subproblem
    best = np.argmin(g, axis=0)
    pop = combined[best]

    ######################################
    # Delete the overcrowded subproblems #
    ######################################

    D = cdist(pop.get("F"), pop.get("F"))

    # avoid selection of solutions with themselves
    D[np.eye(len(D), dtype=np.bool)] = np.inf

    deleted = np.zeros(len(pop), dtype=np.bool)
    while np.sum(deleted) < min(nus, len(EP)):
        remain = np.flatnonzero(~deleted)
        subD = D[np.ix_(remain, remain)]
        subDis = np.sort(subD, axis=1)

        # use viscinity distance to find the most crowded vector among the remaining ones and set it to be deleted
        worst = np.argmin(np.prod(subDis[:, 0:min(M, len(remain))], axis=1))
        deleted[remain[worst]] = True

    pop = pop[~deleted]
    W = W[~deleted, :]

    ######################################
    #       Add new subproblems          #
    ######################################

    # Determine the new solutions be added
    combined = Population.merge(pop, EP)
    selected = np.zeros(len(combined), dtype=np.bool)
    selected[:len(
        pop)] = True  # keep all solutions from pop and add solutions from EP
    D = cdist(combined.get("F"), combined.get("F"))
    D[np.eye(len(D), dtype=np.bool)] = np.inf
    while np.sum(selected) < min(N, len(selected)):
        # get the farthest solutions from already selected solutions using viscinity distance and select it
        subDis = np.sort(D[np.ix_(~selected, selected)], axis=1)
        best = np.argmax(np.prod(subDis[:, 0:min(M, subDis.shape[1])], axis=1))
        remain = np.flatnonzero(~selected)
        selected[remain[best]] = True

    # Add new subproblems to W
    newF = EP[selected[len(pop):]].get("F")

    # transform the weights using the WS transformation described in the paper
    # we don't care about NaNs as they will be eliminated later anyway
    with np.errstate(divide="ignore", invalid='ignore'):
        temp = 1. / (newF - Z)
        W = np.vstack([W, temp / np.sum(temp, axis=1)[:, None]])

    # Add new solutions
    pop = combined[selected]
    return pop, W
Exemplo n.º 29
0
 def _infill(self):
     Hm = Population.merge(self.pop, self.da)
     return self.mating.do(self.problem,
                           Hm,
                           n_offsprings=self.n_offsprings,
                           algorithm=self)
Exemplo n.º 30
0
    def step(self):
        problem, sol = self.problem, self.opt[0]
        self.evaluator.eval(self.problem, sol, evaluate_values_of=["dF"])
        dF = sol.get("dF")[0]

        print(sol)

        if np.linalg.norm(dF)**2 < 1e-8:
            self.termination.force_termination = True
            return

        direction = self.direction(dF)

        line = LineSearchProblem(self.problem,
                                 sol,
                                 direction,
                                 strict_bounds=self.strict_bounds)
        alpha = self.alpha

        if self.strict_bounds:

            if problem.has_bounds():
                line.xu = np.array([
                    max_alpha(sol.X,
                              direction,
                              *problem.bounds(),
                              mode="all_hit_bounds")
                ])

            # remember the step length from the last run
            alpha = min(alpha, line.xu[0])

            if alpha == 0:
                self.termination.force_termination = True
                return

        # make the solution to be the starting point of the univariate search
        x0 = sol.copy(deep=True)
        x0.set("__X__", x0.get("X"))
        x0.set("X", np.zeros(1))

        # determine the brackets to be searched in
        exp = ExponentialSearch(delta=alpha).setup(line,
                                                   evaluator=self.evaluator,
                                                   termination=("n_iter", 20),
                                                   x0=x0)
        a, b = exp.run().pop[-2:]

        # search in the brackets
        res = GoldenSectionSearch().setup(line,
                                          evaluator=self.evaluator,
                                          termination=("n_iter", 20),
                                          a=a,
                                          b=b).run()
        infill = res.opt[0]

        # set the alpha value and revert the X to be the multi-variate one
        infill.set("X", infill.get("__X__"))
        self.alpha = infill.get("alpha")[0]

        # keep always a few historical solutions
        self.pop = Population.merge(self.pop, infill)[-10:]