コード例 #1
0
    def execute_ga(self, vlb, vub, bits, pop_size, max_gen, random_state):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        bits : ndarray
            Number of bits to encode the design space for each element of the design vector.
        pop_size : int
            Number of points in the population.
        max_gen : int
            Number of generations to run the GA.
        random_state : np.random.RandomState, int
             Random state (or seed-number) which controls the seed and random draws.

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        int
            Number of successful function evaluations.
        """
        comm = self.comm
        xopt = copy.deepcopy(vlb)
        fopt = np.inf
        self.lchrom = int(np.sum(bits))

        if np.mod(pop_size, 2) == 1:
            pop_size += 1
        self.npop = int(pop_size)
        fitness = np.zeros((self.npop, ))

        Pc = 0.5
        Pm = (self.lchrom + 1.0) / (2.0 * pop_size * np.sum(bits))
        elite = self.elite

        # TODO: from an user-supplied intial population
        # new_gen, lchrom = encode(x0, vlb, vub, bits)
        new_gen = np.round(
            lhs(self.lchrom,
                self.npop,
                criterion='center',
                random_state=random_state))

        # Main Loop
        nfit = 0
        for generation in range(max_gen + 1):
            old_gen = copy.deepcopy(new_gen)
            x_pop = self.decode(old_gen, vlb, vub, bits)

            # Evaluate points in this generation.
            if comm is not None:
                # Parallel

                # Since GA is random, ranks generate different new populations, so just take one
                # and use it on all.
                x_pop = comm.bcast(x_pop, root=0)

                cases = [((item, ii), None) for ii, item in enumerate(x_pop)]

                results = concurrent_eval(self.objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=self.model_mpi)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        val, success, ii = returns
                        if success:
                            fitness[ii] = val
                            nfit += 1

                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

            else:
                # Serial
                for ii in range(self.npop):
                    x = x_pop[ii]
                    fitness[ii], success, _ = self.objfun(x, 0)

                    if success:
                        nfit += 1
                    else:
                        fitness[ii] = np.inf

            # Elitism means replace worst performing point with best from previous generation.
            if elite and generation > 0:
                max_index = np.argmax(fitness)
                old_gen[max_index] = min_gen
                x_pop[max_index] = min_x
                fitness[max_index] = min_fit

            # Find best performing point in this generation.
            min_fit = np.min(fitness)
            min_index = np.argmin(fitness)
            min_gen = old_gen[min_index]
            min_x = x_pop[min_index]

            if min_fit < fopt:
                fopt = min_fit
                xopt = min_x

            # Evolve new generation.
            new_gen = self.tournament(old_gen, fitness)
            new_gen = self.crossover(new_gen, Pc)
            new_gen = self.mutate(new_gen, Pm)

        return xopt, fopt, nfit
コード例 #2
0
    def execute_ga(self, x0, vlb, vub, vob, bits, pop_size, max_gen, random_state, Pm=None, Pc=0.5):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        x0 : ndarray
            Initial design values
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array. This includes over-allocation so that every point falls on an
            integer value.
        vob : ndarray
            Outer bounds array. This is purely for bounds check.
        bits : ndarray
            Number of bits to encode the design space for each element of the design vector.
        pop_size : int
            Number of points in the population.
        max_gen : int
            Number of generations to run the GA.
        random_state : np.random.RandomState, int
            Random state (or seed-number) which controls the seed and random draws.
        Pm : float or None
            Mutation rate
        Pc : float
            Crossover rate

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        int
            Number of successful function evaluations.
        """
        comm = self.comm
        xopt = copy.deepcopy(vlb)
        fopt = np.inf
        self.lchrom = int(np.sum(bits))

        if np.mod(pop_size, 2) == 1:
            pop_size += 1
        self.npop = int(pop_size)
        fitness = np.zeros((self.npop, ))

        # If mutation rate is not provided as input
        if Pm is None:
            Pm = (self.lchrom + 1.0) / (2.0 * pop_size * np.sum(bits))
        elite = self.elite

        new_gen = np.round(lhs(self.lchrom, self.npop, criterion='center',
                               random_state=random_state))
        new_gen[0] = self.encode(x0, vlb, vub, bits)

        # Main Loop
        nfit = 0
        for generation in range(max_gen + 1):
            old_gen = copy.deepcopy(new_gen)
            x_pop = self.decode(old_gen, vlb, vub, bits)

            # Evaluate points in this generation.
            if comm is not None:
                # Parallel

                # Since GA is random, ranks generate different new populations, so just take one
                # and use it on all.
                x_pop = comm.bcast(x_pop, root=0)

                cases = [((item, ii), None) for ii, item in enumerate(x_pop)
                         if np.all(item - vob <= 0)]

                # Pad the cases with some dummy cases to make the cases divisible amongst the procs.
                # TODO: Add a load balancing option to this driver.
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                results = concurrent_eval(self.objfun, cases, comm, allgather=True,
                                          model_mpi=self.model_mpi)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        val, success, ii = returns
                        if success:
                            fitness[ii] = val
                            nfit += 1

                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

            else:
                # Serial
                for ii in range(self.npop):
                    x = x_pop[ii]

                    if np.any(x - vob > 0):
                        # Exceeded bounds for integer variables that are over-allocated.
                        success = False
                    else:
                        fitness[ii], success, _ = self.objfun(x, 0)

                    if success:
                        nfit += 1
                    else:
                        fitness[ii] = np.inf

            # Elitism means replace worst performing point with best from previous generation.
            if elite and generation > 0:
                max_index = np.argmax(fitness)
                old_gen[max_index] = min_gen
                x_pop[max_index] = min_x
                fitness[max_index] = min_fit

            # Find best performing point in this generation.
            min_fit = np.min(fitness)
            min_index = np.argmin(fitness)
            min_gen = old_gen[min_index]
            min_x = x_pop[min_index]

            if min_fit < fopt:
                fopt = min_fit
                xopt = min_x

            # Evolve new generation.
            new_gen = self.tournament(old_gen, fitness)
            new_gen = self.crossover(new_gen, Pc)
            new_gen = self.mutate(new_gen, Pm)

        return xopt, fopt, nfit
コード例 #3
0
    def execute_ga(self,
                   x0,
                   vlb,
                   vub,
                   vob,
                   bits,
                   pop_size,
                   max_gen,
                   random_state,
                   Pm=None,
                   Pc=0.5):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        x0 : ndarray
            Initial design values.
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array. This includes over-allocation so that every point falls on an
            integer value.
        vob : ndarray
            Outer bounds array. This is purely for bounds check.
        bits : ndarray
            Number of bits to encode the design space for each element of the design vector.
        pop_size : int
            Number of points in the population.
        max_gen : int
            Number of generations to run the GA.
        random_state : np.random.RandomState, int
            Random state (or seed-number) which controls the seed and random draws.
        Pm : float or None
            Mutation rate.
        Pc : float
            Crossover rate.

        Returns
        -------
        ndarray
            Best design point.
        float
            Objective value at best design point.
        int
            Number of successful function evaluations.
        """
        comm = self.comm
        nobj = self.nobj
        self.lchrom = int(np.sum(bits))

        if nobj > 1:
            xopt = []
            fopt = []

            # Needs to be divisible by number of objectives because of tournament selection
            # strategy.
            if np.mod(pop_size, nobj) > 0:
                pop_size += nobj - np.mod(pop_size, nobj)
        else:
            xopt = copy.deepcopy(vlb)
            fopt = np.inf

            # Needs to be divisible by two because tournament selection pits one half of the
            # population against the other half.
            if np.mod(pop_size, 2) == 1:
                pop_size += 1

        self.npop = int(pop_size)
        fitness = np.zeros((self.npop, nobj))

        # If mutation rate is not provided as input
        if Pm is None:
            Pm = (self.lchrom + 1.0) / (2.0 * pop_size * np.sum(bits))
        elite = self.elite

        new_gen = np.round(
            lhs(self.lchrom,
                self.npop,
                criterion='center',
                random_state=random_state))
        new_gen[0] = self.encode(x0, vlb, vub, bits)

        # Main Loop
        nfit = 0
        for generation in range(max_gen + 1):
            old_gen = copy.deepcopy(new_gen)
            x_pop = self.decode(old_gen, vlb, vub, bits)

            # Evaluate fitness of points in this generation.
            if comm is not None:
                # Parallel

                # Since GA is random, ranks generate different new populations, so just take one
                # and use it on all.
                x_pop = comm.bcast(x_pop, root=0)

                cases = [((item, ii), None) for ii, item in enumerate(x_pop)
                         if np.all(item - vob <= 0)]

                # Pad the cases with some dummy cases to make the cases divisible amongst the procs.
                # TODO: Add a load balancing option to this driver.
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                results = concurrent_eval(self.objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=self.model_mpi)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        val, success, ii = returns
                        if success:
                            fitness[ii, :] = val
                            nfit += 1

                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

            else:
                # Serial
                for ii in range(self.npop):
                    x = x_pop[ii]

                    if np.any(x - vob > 0):
                        # Exceeded bounds for integer variables that are over-allocated.
                        success = False
                    else:
                        fitness[ii, :], success, _ = self.objfun(x, 0)

                    if success:
                        nfit += 1
                    else:
                        fitness[ii, :] = np.inf

            # Find Pareto front.
            if nobj > 1:
                xopt, fopt = self.eval_pareto(x_pop, fitness, xopt, fopt)

            # Find best objective.
            else:
                # Elitism means replace worst performing point with best from
                # previous generation.
                if elite and generation > 0:
                    max_index = np.argmax(fitness[:, 0])
                    old_gen[max_index] = min_gen
                    x_pop[max_index] = min_x
                    fitness[max_index, 0] = min_fit

                # Find best performing point in this generation.
                min_fit = np.min(fitness)
                min_index = np.argmin(fitness)
                min_gen = old_gen[min_index]
                min_x = x_pop[min_index]

                if min_fit < fopt:
                    fopt = min_fit
                    xopt = min_x

            # Evolve new generation.

            if nobj > 1:
                new_gen, new_obj = self.tournament_multi_obj(old_gen, fitness)
            else:
                new_gen = self.tournament(old_gen, fitness[:, 0])

            new_gen = self.crossover(new_gen, Pc)
            new_gen = self.mutate(new_gen, Pm)

        return xopt, fopt, nfit
コード例 #4
0
    def execute_ga(self, vlb, vub, bits, pop_size, max_gen):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        bits : ndarray
            Number of bits to encode the design space for each element of the design vector.
        pop_size : int
            Number of points in the population.
        max_gen : int
            Number of generations to run the GA.

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        int
            Number of successful function evaluations.
        """
        xopt = copy.deepcopy(vlb)
        fopt = np.inf
        self.lchrom = int(np.sum(bits))

        if np.mod(pop_size, 2) == 1:
            pop_size += 1
        self.npop = int(pop_size)
        fitness = np.zeros((self.npop, ))

        Pc = 0.5
        Pm = (self.lchrom + 1.0) / (2.0 * pop_size * np.sum(bits))
        elite = self.elite

        # TODO: from an user-supplied intial population
        # new_gen, lchrom = encode(x0, vlb, vub, bits)
        new_gen = np.round(lhs(self.lchrom, self.npop, criterion='center'))

        # Main Loop
        nfit = 0
        for generation in range(max_gen + 1):
            old_gen = copy.deepcopy(new_gen)
            x_pop = self.decode(old_gen, vlb, vub, bits)

            # Evaluate points in this generation.
            if self.comm is not None:
                # Parallel
                cases = [((item, ii), None) for ii, item in enumerate(x_pop)]

                results = concurrent_eval(self.objfun, cases, self.comm, allgather=True)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        val, success, ii = returns
                        if success:
                            fitness[ii] = val
                            nfit += 1

                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

            else:
                # Serial
                for ii in range(self.npop):
                    x = x_pop[ii]

                    fitness[ii], success, _ = self.objfun(x, 0)

                    if success:
                        nfit += 1
                    else:
                        fitness[ii] = np.inf

            # Elitism means replace worst performing point with best from previous generation.
            if elite and generation > 0:
                max_index = np.argmax(fitness)
                old_gen[max_index] = min_gen
                x_pop[max_index] = min_x
                fitness[max_index] = min_fit

            # Find best performing point in this generation.
            min_fit = np.min(fitness)
            min_index = np.argmin(fitness)
            min_gen = old_gen[min_index]
            min_x = x_pop[min_index]

            if min_fit < fopt:
                fopt = min_fit
                xopt = min_x

            # Evolve new generation.
            new_gen = self.tournament(old_gen, fitness)
            new_gen = self.crossover(new_gen, Pc)
            new_gen = self.mutate(new_gen, Pm)

        return xopt, fopt, nfit
コード例 #5
0
    def execute(self, x0, vlb, vub, objfun, comm, model_mpi):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        x0 : ndarray
            Initial design values
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        objfun : function
            Objective callback function.

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        """
        xopt = copy.deepcopy(vlb)
        fopt = np.inf
        count = self.lchrom = len(x0)

        pop_size = self.pop_size
        if pop_size == 0:
            pop_size = 20 * len(x0)

        if np.mod(pop_size, 2) == 1:
            pop_size += 1
        self.npop = int(pop_size)

        # use different seeds in different MPI processes
        seed = self.random_state + comm.Get_rank() if comm else 0
        rng = np.random.default_rng(seed)

        # create random initial population, scaled to bounds
        population = rng.random([self.npop, self.lchrom
                                 ]) * (vub - vlb) + vlb  # scale to bounds
        fitness = np.ones(
            self.npop) * np.inf  # initialize fitness to infinitely bad

        # Main Loop
        nfit = 0
        for generation in range(self.max_gen + 1):
            # Evaluate fitness of points in this generation
            if comm is not None:  # Parallel
                # Since GA is random, ranks generate different new populations, so just take one
                # and use it on all.
                population = comm.bcast(population, root=0)

                cases = [((item, ii), None)
                         for ii, item in enumerate(population)]

                # Pad the cases with some dummy cases to make the cases divisible amongst the procs.
                # TODO: Add a load balancing option to this driver.
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                results = concurrent_eval(objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=model_mpi)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        # val, success = returns
                        # if success:
                        #     fitness[ii] = val
                        #     nfit += 1
                        fitness[ii] = returns
                        nfit += 1
                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)
            else:  # Serial
                for ii in range(self.npop):
                    # fitness[ii], success = objfun(population[ii])
                    fitness[ii] = objfun(population[ii])
                    nfit += 1

            # Find best performing point in this generation.
            min_fit = np.min(fitness)
            min_index = np.argmin(fitness)
            min_gen = population[min_index]

            # Update overall best.
            if min_fit < fopt:
                fopt = min_fit
                xopt = min_gen

            if generation == self.max_gen:  # finished
                break

            # Selection: new generation members replace parents, if better (implied elitism)
            if generation == 0:
                parentPop = copy.deepcopy(population)
                parentFitness = copy.deepcopy(fitness)
            else:
                for ii in range(self.npop):
                    if fitness[ii] < parentFitness[
                            ii]:  # if child is better, else parent unchanged
                        parentPop[ii] = population[ii]
                        parentFitness[ii] = fitness[ii]

            # Evolve new generation.
            population = np.zeros(np.shape(parentPop))
            fitness = np.ones(self.npop) * np.inf

            for ii in range(self.npop):
                # randomly select 3 different population members other than the current choice
                a, b, c = ii, ii, ii
                while a == ii:
                    a = rng.integers(0, self.npop)
                while b == ii or b == a:
                    b = rng.integers(0, self.npop)
                while c == ii or c == a or c == b:
                    c = rng.integers(0, self.npop)

                # randomly select chromosome index for forced crossover
                r = rng.integers(0, self.lchrom)

                # crossover and mutation
                population[ii] = parentPop[ii]  # start the same as parent
                # clip mutant so that it cannot be outside the bounds
                mutant = np.clip(
                    parentPop[a] + self.F * (parentPop[b] - parentPop[c]), vlb,
                    vub)
                # sometimes replace parent's feature with mutant's
                rr = rng.random(self.lchrom)
                idx = np.where(rr < self.Pc)
                population[ii][idx] = mutant[idx]
                population[ii][r] = mutant[
                    r]  # always replace at least one with mutant's

        return xopt, fopt
コード例 #6
0
    def execute(self, x0, vlb, vub, objfun, comm=None, model_mpi=None):
        """
        Execute the CMA Evolution Strategy.

        Parameters
        ----------
        x0 : ndarray
            Initial design values
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        objfun : function
            Objective callback function.
        comm : MPI communicator or None
            The MPI communicator that will be used objective evaluation.
        model_mpi : None or tuple
            If the model in objfun is also parallel, then this will contain a tuple with the the
            total number of population points to evaluate concurrently, and the color of the point
            to evaluate on this rank.

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        """
        self.options['bounds'] = [vlb, vub]

        if comm is None:
            # Running non-parallel, use functional interface

            res = cma.fmin(objfun, x0, self.sigma0, options=self.options)

            return res[0], res[1]

        else:
            # Running parallel, use OO interface

            # make sure all procs have the same seed
            seed = self.options['seed']
            if comm.rank == 0 and (not isinstance(seed, int) or seed == 0):
                seed = int(time.time())
            self.options['seed'] = comm.bcast(seed, root=0)

            optim = cma.CMAEvolutionStrategy(x0, self.sigma0, self.options)

            stop = False

            while not stop:  # optim.stop():
                # get candidate solutions
                X = optim.ask()

                # pad candidates to make them divisible into procs.
                cases = [((item, ), None) for ii, item in enumerate(X)]
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                # evaluate candidate solutions concurrently
                results = concurrent_eval(objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=model_mpi)

                # assemble solutions corresponding to X
                f = []
                for i in range(len(X)):
                    returns, traceback = results[i]
                    if returns:
                        # fval, success = returns
                        f.append(returns)
                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

                # do the "update", pass f-values and prepare for next iteration
                optim.tell(X, f)
                optim.disp(20)  # display info every 20th iteration
                optim.logger.add()  # log another "data line", non-standard

                # gather stop conditions, stop if any proc stops
                # (all procs should stop with same stop condition)
                stops = comm.allgather(optim.stop())
                for proc_stop in stops:
                    if len(proc_stop) > 0:
                        stop = True

            # final output
            # print('termination by', stops)
            # print('best f-value =', optim.result[1])
            # print('best solution =', optim.result[0])
            # optim.logger.plot()  # if matplotlib is available

            return optim.result[0], optim.result[1]
コード例 #7
0
    def execute(self, x0, sigma0, CMAOptions):
        """
        Execute the CMA Evolution Strategy.

        Parameters
        ----------
        x0 : ndarray
            Initial design values
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        sigma0 : float
            Initial standard deviation in each coordinate.
        CMAOptions : CMAOptions
            Options for CMAES execution.

        Returns
        -------
        ndarray
            Best design point
        float
            Objective value at best design point.
        """
        comm = self.comm

        if comm is None:
            # Running non-parallel, use functional interface

            res = cma.fmin(self.objfun, x0, sigma0, options=CMAOptions)

            return res[0], res[1]

        else:
            # Running parallel, use OO interface

            # make sure all procs have the same seed
            seed = CMAOptions['seed']
            if comm.rank == 0 and (not isinstance(seed, int) or seed == 0):
                seed = int(time.time())
            CMAOptions['seed'] = comm.bcast(seed, root=0)

            optim = cma.CMAEvolutionStrategy(x0, sigma0, CMAOptions)

            stop = False

            while not stop:  # optim.stop():
                # get candidate solutions
                X = optim.ask()

                # pad candidates to make them divisible into procs.
                cases = [((item, ), None) for ii, item in enumerate(X)]
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                # evaluate candidate solutions concurrently
                results = concurrent_eval(self.objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=self.model_mpi)

                # assemble solutions corresponding to X
                f = []
                for i in range(len(X)):
                    returns, traceback = results[i]
                    if returns:
                        f.append(returns)
                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)

                # do the "update", pass f-values and prepare for next iteration
                optim.tell(X, f)
                optim.disp(20)  # display info every 20th iteration
                optim.logger.add()  # log another "data line", non-standard

                # gather stop conditions, stop if any proc stops
                # (all procs should stop with same stop condition)
                stops = comm.allgather(optim.stop())
                for proc_stop in stops:
                    if len(proc_stop) > 0:
                        stop = True

            # final output
            # print('termination by', stops)
            # print('best f-value =', optim.result[1])
            # print('best solution =', optim.result[0])
            # optim.logger.plot()  # if matplotlib is available

            return optim.result[0], optim.result[1]
コード例 #8
0
    def execute_ga(self,
                   x0,
                   vlb,
                   vub,
                   pop_size,
                   max_gen,
                   random_state,
                   F=0.5,
                   Pc=0.5):
        """
        Perform the genetic algorithm.

        Parameters
        ----------
        x0 : ndarray
            Initial design values.
        vlb : ndarray
            Lower bounds array.
        vub : ndarray
            Upper bounds array.
        pop_size : int
            Number of points in the population.
        max_gen : int
            Number of generations to run the GA.
        random_state : int
            Seed-number which controls the random draws.
        F : float
            Differential rate.
        Pc : float
            Crossover rate.

        Returns
        -------
        ndarray
            Best design point.
        float
            Objective value at best design point.
        int
            Number of successful function evaluations.
        """
        comm = self.comm
        xopt = copy.deepcopy(vlb)
        fopt = np.inf
        self.lchrom = len(x0)

        if np.mod(pop_size, 2) == 1:
            pop_size += 1
        self.npop = int(pop_size)

        if self.comm is not None and self.comm.size > 1:
            if random_state is None:
                # if no random_state is given, generate one on rank 0 and broadcast it to all
                # ranks.  Because we add the rank to the starting random state, no ranks will
                # have the same seed.
                rng = np.random.default_rng()
                random_state = rng.integers(1e15)
                if self.comm.rank == 0:
                    self.comm.bcast(random_state, root=0)
                else:
                    random_state = self.comm.bcast(None, root=0)

            # add rank to ensure different seed in each MPI process
            seed = random_state + self.comm.rank
        else:
            seed = random_state

        rng = np.random.default_rng(seed)

        # create random initial population, scaled to bounds
        population = rng.random([self.npop, self.lchrom
                                 ]) * (vub - vlb) + vlb  # scale to bounds
        fitness = np.ones(
            self.npop) * np.inf  # initialize fitness to infinitely bad

        # Main Loop
        nfit = 0
        for generation in range(max_gen + 1):
            # Evaluate fitness of points in this generation
            if comm is not None:  # Parallel
                # Since GA is random, ranks generate different new populations, so just take one
                # and use it on all.
                population = comm.bcast(population, root=0)

                cases = [((item, ii), None)
                         for ii, item in enumerate(population)]

                # Pad the cases with some dummy cases to make the cases divisible amongst the procs.
                # TODO: Add a load balancing option to this driver.
                extra = len(cases) % comm.size
                if extra > 0:
                    for j in range(comm.size - extra):
                        cases.append(cases[-1])

                results = concurrent_eval(self.objfun,
                                          cases,
                                          comm,
                                          allgather=True,
                                          model_mpi=self.model_mpi)

                fitness[:] = np.inf
                for result in results:
                    returns, traceback = result

                    if returns:
                        val, success, ii = returns
                        if success:
                            fitness[ii] = val
                            nfit += 1
                    else:
                        # Print the traceback if it fails
                        print('A case failed:')
                        print(traceback)
            else:  # Serial
                for ii in range(self.npop):
                    fitness[ii], success, _ = self.objfun(population[ii], 0)
                    nfit += 1

            # Find best performing point in this generation.
            min_fit = np.min(fitness)
            min_index = np.argmin(fitness)
            min_gen = population[min_index]

            # Update overall best.
            if min_fit < fopt:
                fopt = min_fit
                xopt = min_gen

            if generation == max_gen:  # finished
                break

            # Selection: new generation members replace parents, if better (implied elitism)
            if generation == 0:
                parentPop = copy.deepcopy(population)
                parentFitness = copy.deepcopy(fitness)
            else:
                for ii in range(self.npop):
                    if fitness[ii] < parentFitness[
                            ii]:  # if child is better, else parent unchanged
                        parentPop[ii] = population[ii]
                        parentFitness[ii] = fitness[ii]

            # Evolve new generation.
            population = np.zeros(np.shape(parentPop))
            fitness = np.ones(self.npop) * np.inf

            for ii in range(self.npop):
                # randomly select 3 different population members other than the current choice
                a, b, c = ii, ii, ii
                while a == ii:
                    a = rng.integers(0, self.npop)
                while b == ii or b == a:
                    b = rng.integers(0, self.npop)
                while c == ii or c == a or c == b:
                    c = rng.integers(0, self.npop)

                # randomly select chromosome index for forced crossover
                r = rng.integers(0, self.lchrom)

                # crossover and mutation
                population[ii] = parentPop[ii]  # start the same as parent
                # clip mutant so that it cannot be outside the bounds
                mutant = np.clip(
                    parentPop[a] + F * (parentPop[b] - parentPop[c]), vlb, vub)
                # sometimes replace parent's feature with mutant's
                rr = rng.random(self.lchrom)
                idx = np.where(rr < Pc)
                population[ii][idx] = mutant[idx]
                population[ii][r] = mutant[
                    r]  # always replace at least one with mutant's

        return xopt, fopt, nfit
コード例 #9
0
    def run(self):
        """
        Excute the genetic algorithm.

        Returns
        -------
        boolean
            Failure flag; True if failed to converge, False is successful.
        """
        model = self._problem.model

        # Size design variables.
        desvars = self._designvars
        count = 0
        for name, meta in iteritems(desvars):
            size = meta['size']
            self._desvar_idx[name] = (count, count + size)
            count += size

        lower_bound = np.empty((count, ))
        upper_bound = np.empty((count, ))

        x0 = np.empty(count)
        desvar_vals = self.get_design_var_values()

        # Figure out bounds vectors and initial design vars
        for name, meta in iteritems(desvars):
            i, j = self._desvar_idx[name]
            lower_bound[i:j] = meta['lower']
            upper_bound[i:j] = meta['upper']
            x0[i:j] = desvar_vals[name]
        max_iter = self.options['max_iter'] or 1e20
        max_time = self.options['max_time'] or 1e20
        assert max_iter < 1e20 or max_time < 1e20, "max_iter or max_time must be set"

        disp = self.options['disp']

        abs2prom = model._var_abs2prom['output']

        # Initial Design Vars
        desvar_vals = self.get_design_var_values()
        i = 0
        for name, meta in iteritems(self._designvars):
            size = meta['size']
            x0[i:i + size] = desvar_vals[name]
            i += size

        obj_value_x0, success = self.objective_callback(x0, record=True)
        x1 = x0.copy()
        n_iter = 0

        desvar_info = [(abs2prom[name], *self._desvar_idx[name], lower_bound,
                        upper_bound) for name, meta in iteritems(desvars)]
        desvar_dict = {
            name: (x0[i:j].copy(), lbound[i:j], ubound[i:j])
            for (name, i, j, lbound, ubound) in desvar_info
        }
        start = time.time()

        comm = self.comm

        while n_iter < max_iter and time.time() - start < max_time:

            for name, i, j, _, _ in desvar_info:
                desvar_dict[name][0][:] = x0[i:j].copy()

            if comm is not None:
                # We do it in parallel: One case per CPU available
                cases = []
                if comm.rank == 0:
                    for ii in range(comm.size):
                        desvar_dict = self.randomize_func(desvar_dict)
                        x1 = x0.copy()
                        for name, i, j, _, _ in desvar_info:
                            x1[i:j] = desvar_dict[name][0][:]
                        cases.append(((x1, ii), None))

                # Let's make sure we have the same cases everywhere
                cases = comm.bcast(cases, root=0)

                results = concurrent_eval(self.objective_callback,
                                          cases,
                                          comm,
                                          allgather=True)
                one_success = False
                for i, result in enumerate(results):
                    returns, traceback = result
                    obj_value_x1, success = returns
                    if success and obj_value_x1 < obj_value_x0:
                        one_success = True
                        x0 = cases[i][0][0].copy()
                        obj_value_x0 = obj_value_x1
                        # n_iter += 1
                    elif obj_value_x1 < 1e10:
                        one_success = True
                    # n_iter += 1
                if one_success:
                    n_iter += 1
                    obj_value_x0, success = self.objective_callback(
                        x0, record=True)
                    if disp and comm.rank == 0:
                        # obj_value_x0, success = self.objective_callback(x0, record=False)
                        print('rank:', comm.rank, n_iter, obj_value_x0)
            else:
                # We only use one CPU

                desvar_dict = self.randomize_func(desvar_dict)
                for name, i, j, _, _ in desvar_info:
                    x1[i:j] = desvar_dict[name][0][:]

                obj_value_x1, success = self.objective_callback(x1,
                                                                record=False)
                if success and obj_value_x1 < obj_value_x0:
                    obj_value_x1, success = self.objective_callback(
                        x1, record=True)
                    x0 = x1.copy()
                    obj_value_x0 = obj_value_x1
                    n_iter += 1
                    if disp:
                        print(n_iter, obj_value_x1)
                else:
                    if obj_value_x1 < 1e10:
                        n_iter += 1

                if not success or obj_value_x1 > obj_value_x0:
                    obj_value_x1, success = self.objective_callback(
                        x0, record=True)
        return False
コード例 #10
0
ファイル: nsde.py プロジェクト: DARcorporation/nsde
    def __call__(self, pop):
        """
        Evaluate the fitness of the given population.

        Parameters
        ----------
        pop : array_like
            List of chromosomes of the individuals in the population

        Returns
        -------
        fit : np.array
            Fitness of the individuals in the given population
        con : np.array or None
            Constraint violations of the individuals in the given population if present. None otherwise.

        Notes
        -----
        If this class has an MPI communicator the individuals will be evaluated in parallel.
        Otherwise function evaluation will be serial.
        """
        if self.is_initialized:
            fit = np.empty((self.n_pop, self.n_obj))
            con = None if self.n_con is None else np.empty(
                (self.n_pop, self.n_con))
        else:
            fit = pop.shape[0] * [None]
            con = None

        def handle_result(_v, _i, _fit, _con):
            if isinstance(_v, tuple):
                _fit[_i] = np.asarray(_v[0])
                c = np.asarray(_v[1])
                if _con is None:
                    _con = np.empty((pop.shape[0], c.size))
                _con[_i] = c
            else:
                _fit[_i] = _v
            return _fit, _con

        # Evaluate generation
        if self._running_under_mpi:
            # Construct run cases
            cases = [((item, ii), None) for ii, item in enumerate(pop)]

            # Pad the cases with some dummy cases to make the cases divisible amongst the procs.
            extra = len(cases) % self.comm.size
            if extra > 0:
                for j in range(self.comm.size - extra):
                    cases.append(cases[-1])

            # Compute the fitness of all individuals in parallel using MPI
            results = concurrent_eval(self.fobj,
                                      cases,
                                      self.comm,
                                      allgather=True,
                                      model_mpi=self.model_mpi)

            # Gather the results
            for result in results:
                retval, err = result
                if err is not None or retval is None:
                    raise Exception(err)
                else:
                    fit, con = handle_result(*retval, fit, con)
        else:
            # Evaluate the population in serial
            for idx, ind in enumerate(pop):
                val = self.fobj(ind)
                fit, con = handle_result(val, idx, fit, con)

        # Turn all NaNs in the fitnesses into infs
        fit = np.reshape(np.where(np.isnan(fit), np.inf, fit),
                         (pop.shape[0], -1))
        if con is not None:
            con = np.reshape(np.where(np.isnan(con), np.inf, con),
                             (pop.shape[0], -1))
        return fit, con