如何准确地使用遗传算法来最小化非线性优化问题?

时间:2019-11-29 12:10:33

标签: python genetic-algorithm nonlinear-optimization

我正在尝试应用遗传算法的概念来解决Python中的非线性优化问题,并将结果与​​其他方法进行比较。我正在尝试求解受以下约束的最小x1.x4(x1 + x2 + x3)+ x3:x1.x2.x3.x4> = 25,x1 ^ 2 + x2 ^ 2 + x3 ^ 2 + x4 ^ 2 == 40,并且x1到x4中的每一个小于或等于5且大于或等于1。此外,初始解为x0 =(1,5,5,1)。 我已经使用scipy和gekko解决了该问题,并得到以下结果:x1 = 1,x2 = 4.743,x3 = 3.8321,x4 = 1.379。

当我运行我的GA代码时,x1,x2,x3始终为1,5,5,并且x4有时为负数或小于1。此外,有时我的目标函数是增加而不是减少(趋势并且每次我运行代码时值都不一致。

下面是我的Python GA代码:


import numpy

# Number of the weights we are looking to optimize.

num_weights = 4 # x1,x2,x3, x4 (4 numbers): no of genes

sol_per_pop = 8 #no. of chromosones; can be changed 

num_parents_mating = 4


fitness=numpy.zeros(sol_per_pop)

def cal_pop_fitness(pop):

    # Calculating the fitness value of each solution in the current population.

    for i in range(sol_per_pop):#(0 till sol_per_pop -1) 

        #objective function: x4*x1^2 + x1*x2*x4 + x1*x3*x4+ x3
        #the fitness value is the objective function

        fitness[i]= ((new_population[i][3])*(new_population[i][0]**2))+ \
        ((new_population[i][0])*(new_population[i][1])*(new_population[i][3]))+\
        ((new_population[i][0])*(new_population[i][2])*(new_population[i][3]))+(new_population[i][2])

    return fitness

def select_mating_pool(pop, fitness, num_parents):

    # Selecting the best individuals in the current generation as parents for producing the offspring of the next generation.

    parents = numpy.empty((num_parents, pop.shape[1]))#initialize an empty parent array of size equal to 4 rows(num_parents_mating)
    #and columns equal to the number of genes(4) which is pop.shape[1] 

    #add constraints
    #initialize functions to be filled by the 6 constraints
    # specifiying the dtype to int64 is to avoid the following warning/error: RuntimeWarning: overflow encountered in long scalars
    a=numpy.zeros(sol_per_pop,dtype='int64') #for 1st cons.
    b=numpy.zeros(sol_per_pop,dtype='int64') #for 2nd cons.
    c=numpy.zeros(sol_per_pop,dtype='int64') #for 3rd cons.
    d=numpy.zeros(sol_per_pop,dtype='int64') #for 4th cons.
    e=numpy.zeros(sol_per_pop,dtype='int64') #for 5th cons.
    f=numpy.zeros(sol_per_pop,dtype='int64') #for 6th cons.


    for i in range(sol_per_pop):#(0 till sol_per_pop -1)

        #filling the 6 functions

        a[i]=(new_population[i][0])*(new_population[i][1])*(new_population[i][2])*(new_population[i][3])
        b[i]=(new_population[i][0]**2)+(new_population[i][1]**2)+(new_population[i][2]**2)+(new_population[i][3]**2)
        c[i]=new_population[i][0]
        d[i]=new_population[i][1]
        e[i]=new_population[i][2]
        f[i]=new_population[i][3]

    for parent_num in range(num_parents):

        #check if all constraints are met


        for i in range(sol_per_pop):


            min_fitness_idx = numpy.where(fitness == numpy.min(fitness))#get index where fitness is minimum (obj function)

            min_fitness_idx = min_fitness_idx[0][0]#takes only the number 

            if a[min_fitness_idx]>=25 and b[min_fitness_idx]==40 and c[min_fitness_idx]>=1 and c[min_fitness_idx]<=5 and d[min_fitness_idx]>=1 and d[min_fitness_idx]<=5 and e[min_fitness_idx]>=1 and e[min_fitness_idx]<=5 and f[min_fitness_idx]>=1 and f[min_fitness_idx]<=5:

                parents[parent_num, :] = pop[min_fitness_idx, :]

                fitness[min_fitness_idx] = +99999999999

                break


            else:
                #remove the fitness value that violates the constraints
                fitness[min_fitness_idx]= 100000

                pass

    return parents

def crossover(parents,offspring_size):

    offspring = numpy.empty(offspring_size) #initialze an empty offspring array of number of rows
    #equal to number of choromosones - number of rows of parent array ; columns equal to number of genes (4)

    # The point at which crossover takes place between two parents. Usually it is at the center.

    crossover_point = numpy.uint8(offspring_size[1]/2)# specifies the point where crossover will take place
    #unit8 is the type of the number :uint8 is Unsigned integer (0 to 255) ;offspring_size[1] is the number of columns(no of genes)

    for k in range(offspring_size[0]): #k : 0 -->3

        # Index of the first parent to mate.

        parent1_idx = k%parents.shape[0] #returns the remainder of k divided by the number of rows of the parent array

        # Index of the second parent to mate.

        parent2_idx = (k+1)%parents.shape[0] #returns the remainder of k+1 divided by the number of rows of the parent array


        # The new offspring will have its first half of its genes taken from the first parent.

        offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]

        # The new offspring will have its second half of its genes taken from the second parent.

        offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]

    return offspring

def mutation(offspring_crossover):

    # Mutation changes a single gene in each offspring randomly.

    for idx in range(offspring_crossover.shape[0]):

        # The random value to be added to the gene.

        random_value = numpy.random.uniform(-1.0, 1.0, 1)#random number from -1 to 1
        offspring_crossover[idx, 3] = offspring_crossover[idx, 3] + random_value #changes the value of the last gene only

    return offspring_crossover

# Defining the population size.

pop_size = (sol_per_pop,num_weights) # The population will have sol_per_pop chromosome where each chromosome has num_weights genes.

#Creating the initial population.

#might need to be changed ( keep only 1st row and other rows put random nos.)

new_population = numpy.zeros(shape=(sol_per_pop,num_weights))

for i in range(num_weights):#columns

    for j in range(sol_per_pop): #rows

        if i==0:
            new_population[j][i]=1


        if i==1:
            new_population[j][i]=5


        if i==2:
            new_population[j][i]=5


        if i==3:
            new_population[j][i]=1

print("the initial population is: \n" , new_population)

num_generations = 5

for generation in range(num_generations):

    print("Generation : ", generation)

    # Measing the fitness of each chromosome in the population.

    fitness = cal_pop_fitness(new_population)



    # Selecting the best parents in the population for mating.

    parents = select_mating_pool(new_population, fitness,  num_parents_mating)



    # Generating next generation using crossover.

    offspring_crossover = crossover(parents, offspring_size=(pop_size[0]-parents.shape[0], num_weights))



    # Adding some variations to the offsrping using mutation.

    offspring_mutation = mutation(offspring_crossover)



    # Creating the new population based on the parents and offspring.

    new_population[0:parents.shape[0], :] = parents

    new_population[parents.shape[0]:, :] = offspring_mutation



    # The best result in the current iteration.

    af=((new_population[generation][3])*(new_population[generation][0]**2))+ \
    ((new_population[generation][0])*(new_population[generation][1])*(new_population[generation][3]))+\
    ((new_population[generation][0])*(new_population[generation][2])*(new_population[generation][3]))+(new_population[generation][2])


    print("Best result : ", numpy.min(af))

# Getting the best solution after iterating finishing all generations.

#At first, the fitness is calculated for each solution in the final generation.

fitness = cal_pop_fitness(new_population)

# Then return the index of that solution corresponding to the best fitness.

best_match_idx = numpy.where(fitness == numpy.min(fitness))



print("Best solution : ", new_population[best_match_idx, :])

print("Best solution fitness : ", fitness[best_match_idx])

谢谢!

0 个答案:

没有答案