├── Pareto front.png ├── README.md └── CCMO.py /Pareto front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xavier-MaYiMing/CCMO/HEAD/Pareto front.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### CCMO: A coevolutionary constrained multiobjective optimization (CCMO) framework 2 | 3 | ##### Reference: Tian Y, Zhang T, Xiao J, et al. A coevolutionary framework for constrained multiobjective optimization problems[J]. IEEE Transactions on Evolutionary Computation, 2020, 25(1): 102-116. 4 | 5 | ##### The CCMO belongs to the category of multi-objective evolutionary algorithms (MOEAs). CCMO is a powerful algorithm to solve the constrained multiobjective optimization (CMO) problems. 6 | 7 | | Variables | Meaning | 8 | | ----------- | ---------------------------------------------------- | 9 | | npop | Population size | 10 | | iter | Iteration number | 11 | | lb | Lower bound | 12 | | ub | Upper bound | 13 | | pc | Crossover probability (default = 1) | 14 | | pm | Mutation probability (default = 1) | 15 | | eta_c | Spread factor distribution index (default = 20) | 16 | | eta_m | Perturbance factor distribution index (default = 20) | 17 | | nvar | The dimension of decision space | 18 | | pop1 | Population1 | 19 | | pop2 | Population2 | 20 | | objs1 | Objectives1 | 21 | | objs2 | Objective2 | 22 | | CV1 | Constraint violation1 | 23 | | CV2 | Constraint violation2 | 24 | | F1 | Fitness1 | 25 | | F2 | Fitness2 | 26 | | mating_pool | Mating pool | 27 | | offspring | Offspring | 28 | | pf | Pareto front | 29 | 30 | 31 | 32 | #### Test problem: MW11 33 | 34 | ##### Reference: Ma Z, Wang Y. Evolutionary constrained multiobjective optimization: Test suite construction and performance comparisons[J]. IEEE Transactions on Evolutionary Computation, 2019, 23(6): 972-986. 35 | 36 | Define $m$ is the dimension of objective space, and $n$ is the dimension of decistion space. 37 | 38 | 39 | 40 | $$ 41 | \begin{aligned} 42 | &g3(x)=1+\sum_{i=m}^n2(x_i+(x_{i-1}-0.5)^2-1)^2\\ 43 | &f_1(x)=\sqrt{2}g_3x_1\\ 44 | &f_2(x)=g_3\sqrt{2-(f_1/g_3)^2}\\ 45 | &\text{subject to}\\ 46 | &c_1(x)=(3-f_1^2-f_2)(3-2f_1^2-f_2)\geq0\\ 47 | &c_2(x)=(3-0.625f_1^2-f_2)(3-7f_1^2-f_2)\leq0\\ 48 | &c_3(x)=(1.62-0.18f_1^2-f_2)(1.125-0.125f_1^2-f_2)\geq0\\ 49 | &c_4(x)=(2.07-0.23f_1^2-f_2)(0.63-0.07f_1^2-f_2)\leq0\\ 50 | &0\leq x_i\leq 1, \quad i=1,\cdots,n 51 | \end{aligned} 52 | $$ 53 | 54 | 55 | 56 | #### Example 57 | 58 | ```python 59 | if __name__ == '__main__': 60 | main(100, 500, np.array([0] * 15), np.array([1] * 15)) 61 | ``` 62 | 63 | ##### Output: 64 | 65 | ![](https://github.com/Xavier-MaYiMing/CCMO/blob/main/Pareto%20front.png) 66 | 67 | ```python 68 | Iteration 20 completed. 69 | Iteration 40 completed. 70 | Iteration 60 completed. 71 | Iteration 80 completed. 72 | Iteration 100 completed. 73 | Iteration 120 completed. 74 | Iteration 140 completed. 75 | Iteration 160 completed. 76 | Iteration 180 completed. 77 | Iteration 200 completed. 78 | Iteration 220 completed. 79 | Iteration 240 completed. 80 | Iteration 260 completed. 81 | Iteration 280 completed. 82 | Iteration 300 completed. 83 | Iteration 320 completed. 84 | Iteration 340 completed. 85 | Iteration 360 completed. 86 | Iteration 380 completed. 87 | Iteration 400 completed. 88 | Iteration 420 completed. 89 | Iteration 440 completed. 90 | Iteration 460 completed. 91 | Iteration 480 completed. 92 | Iteration 500 completed. 93 | ``` 94 | 95 | -------------------------------------------------------------------------------- /CCMO.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/7/19 22:38 4 | # @Author : Xavier Ma 5 | # @Email : xavier_mayiming@163.com 6 | # @File : CCMO.py 7 | # @Statement : A coevolutionary constrained multiobjective optimization (CCMO) framework 8 | # @Reference : Tian Y, Zhang T, Xiao J, et al. A coevolutionary framework for constrained multiobjective optimization problems[J]. IEEE Transactions on Evolutionary Computation, 2020, 25(1): 102-116. 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | from scipy.spatial.distance import pdist, squareform 12 | 13 | 14 | def g3(x, nobj, nvar): 15 | return 1 + sum([2 * (x[:, i] + (x[:, i - 1] - 0.5) ** 2 - 1) ** 2 for i in range(nobj - 1, nvar)]) 16 | 17 | 18 | def cal_obj(x, npop, nvar, nobj=2): 19 | # MW11 20 | # Reference: Ma Z, Wang Y. Evolutionary constrained multiobjective optimization: Test suite construction and performance comparisons[J]. IEEE Transactions on Evolutionary Computation, 2019, 23(6): 972-986. 21 | temp = g3(x, nobj, nvar) 22 | f1 = temp * x[:, 0] * np.sqrt(1.9999) 23 | f2 = temp * np.sqrt(2 - (f1 / temp) ** 2) 24 | flag1 = (3 - f1 ** 2 - f2) * (3 - 2 * f1 ** 2 - f2) >= 0 25 | flag2 = (3 - 0.625 * f1 ** 2 - f2) * (3 - 7 * f1 ** 2 - f2) <= 0 26 | flag3 = (1.62 - 0.18 * f1 ** 2 - f2) * (1.125 - 0.125 * f1 ** 2 - f2) >= 0 27 | flag4 = (2.07 - 0.23 * f1 ** 2 - f2) * (0.63 - 0.07 * f1 ** 2 - f2) <= 0 28 | CV = np.sum((~flag1, ~flag2, ~flag3, ~flag4), axis=0) 29 | return np.concatenate((f1.reshape(npop, 1), f2.reshape(npop, 1)), axis=1), CV 30 | 31 | 32 | def dominates(obj1, obj2): 33 | # determine whether obj1 dominates obj2 34 | sum_less = 0 35 | for i in range(len(obj1)): 36 | if obj1[i] > obj2[i]: 37 | return False 38 | elif obj1[i] != obj2[i]: 39 | sum_less += 1 40 | return sum_less > 0 41 | 42 | 43 | def fitness(objs, CV): 44 | # calculate the fitness value 45 | npop = objs.shape[0] 46 | S = np.zeros(npop, dtype=int) # the strength value 47 | R = np.zeros(npop, dtype=int) # the raw fitness 48 | dom = np.full((npop, npop), False) # domination matrix 49 | for i in range(npop - 1): 50 | for j in range(i, npop): 51 | if CV[i] < CV[j]: 52 | S[i] += 1 53 | dom[i, j] = True 54 | elif CV[i] > CV[j]: 55 | S[j] += 1 56 | dom[j, i] = True 57 | elif dominates(objs[i], objs[j]): 58 | S[i] += 1 59 | dom[i, j] = True 60 | elif dominates(objs[j], objs[i]): 61 | S[j] += 1 62 | dom[j, i] = True 63 | for i in range(npop): 64 | R[i] = np.sum(S[dom[:, i]]) 65 | sigma = squareform(pdist(objs, metric='seuclidean'), force='no', checks=True) 66 | sigma_K = np.sort(sigma)[:, int(np.sqrt(npop))] # the K-th shortest distance 67 | D = 1 / (sigma_K + 2) # density 68 | F = R + D # fitness 69 | return sigma, F 70 | 71 | 72 | def selection(pop, F, pc, k=2): 73 | # binary tournament selection 74 | (npop, dim) = pop.shape 75 | nm = int(npop * pc) 76 | nm = nm if nm % 2 == 0 else nm + 1 77 | mating_pool = np.zeros((nm, dim)) 78 | for i in range(nm): 79 | selections = np.random.choice(npop, k, replace=True) 80 | ind = selections[np.argmin(F[selections])] 81 | mating_pool[i] = pop[ind] 82 | return mating_pool 83 | 84 | 85 | def crossover(mating_pool, lb, ub, eta_c): 86 | # simulated binary crossover (SBX) 87 | (noff, dim) = mating_pool.shape 88 | nm = int(noff / 2) 89 | parent1 = mating_pool[:nm] 90 | parent2 = mating_pool[nm:] 91 | beta = np.zeros((nm, dim)) 92 | mu = np.random.random((nm, dim)) 93 | flag1 = mu <= 0.5 94 | flag2 = ~flag1 95 | beta[flag1] = (2 * mu[flag1]) ** (1 / (eta_c + 1)) 96 | beta[flag2] = (2 - 2 * mu[flag2]) ** (-1 / (eta_c + 1)) 97 | offspring1 = (parent1 + parent2) / 2 + beta * (parent1 - parent2) / 2 98 | offspring2 = (parent1 + parent2) / 2 - beta * (parent1 - parent2) / 2 99 | offspring = np.concatenate((offspring1, offspring2), axis=0) 100 | offspring = np.min((offspring, np.tile(ub, (noff, 1))), axis=0) 101 | offspring = np.max((offspring, np.tile(lb, (noff, 1))), axis=0) 102 | return offspring 103 | 104 | 105 | def mutation(pop, lb, ub, pm, eta_m): 106 | # polynomial mutation 107 | (npop, dim) = pop.shape 108 | lb = np.tile(lb, (npop, 1)) 109 | ub = np.tile(ub, (npop, 1)) 110 | site = np.random.random((npop, dim)) < pm / dim 111 | mu = np.random.random((npop, dim)) 112 | delta1 = (pop - lb) / (ub - lb) 113 | delta2 = (ub - pop) / (ub - lb) 114 | temp = np.logical_and(site, mu <= 0.5) 115 | pop[temp] += (ub[temp] - lb[temp]) * ((2 * mu[temp] + (1 - 2 * mu[temp]) * (1 - delta1[temp]) ** (eta_m + 1)) ** (1 / (eta_m + 1)) - 1) 116 | temp = np.logical_and(site, mu > 0.5) 117 | pop[temp] += (ub[temp] - lb[temp]) * (1 - (2 * (1 - mu[temp]) + 2 * (mu[temp] - 0.5) * (1 - delta2[temp]) ** (eta_m + 1)) ** (1 / (eta_m + 1))) 118 | pop = np.min((pop, ub), axis=0) 119 | pop = np.max((pop, lb), axis=0) 120 | return pop 121 | 122 | 123 | def environmental_selection(pop, npop, objs, CV): 124 | # environmental selection 125 | sigma, F = fitness(objs, CV) 126 | index = np.where(F < 1)[0] 127 | if len(index) <= npop: 128 | rank = np.argsort(F)[: npop] 129 | return pop[rank], objs[rank], CV[rank], F[rank] 130 | pop = pop[index] 131 | objs = objs[index] 132 | CV = CV[index] 133 | sigma = sigma[index][:, index] 134 | F = F[index] 135 | eye = np.arange(len(sigma)) 136 | sigma[eye, eye] = np.inf 137 | delete = np.full(len(index), False) 138 | while np.sum(delete) < len(index) - npop: 139 | remain = np.where(~delete)[0] 140 | temp = np.sort(sigma[remain][:, remain]) 141 | delete[remain[np.argmin(temp[:, 0])]] = True 142 | remain = np.where(~delete)[0] 143 | return pop[remain], objs[remain], CV[remain], F[remain] 144 | 145 | 146 | def main(npop, iter, lb, ub, pc=1, pm=1, eta_c=20, eta_m=20): 147 | """ 148 | The main function 149 | :param npop: population size 150 | :param iter: iteration number 151 | :param lb: upper bound 152 | :param ub: lower bound 153 | :param pc: crossover probability (default = 1) 154 | :param pm: mutation probability (default = 1) 155 | :param eta_c: spread factor distribution index (default = 20) 156 | :param eta_m: perturbance factor distribution index (default = 20) 157 | :return: 158 | """ 159 | # Step 1. Initialization 160 | nvar = len(lb) # the dimension of decision space 161 | pop1 = np.random.uniform(lb, ub, (npop, nvar)) # population1 162 | pop2 = np.random.uniform(lb, ub, (npop, nvar)) # population2 163 | objs1, CV1 = cal_obj(pop1, npop, nvar) # objectives1, constraint violation1 164 | objs2, CV2 = cal_obj(pop2, npop, nvar) # objectives2, constraint violation2 165 | F1 = fitness(objs1, CV1)[1] # fitness1 166 | F2 = fitness(objs2, np.zeros(npop))[1] # fitness2 167 | 168 | # Step 2. The main loop 169 | for t in range(iter): 170 | 171 | if (t + 1) % 20 == 0: 172 | print('Iteration ' + str(t + 1) + ' completed.') 173 | 174 | # Step 2.1. Selection + crossover + mutation 175 | mating_pool1 = selection(pop1, F1, pc) 176 | offspring1 = crossover(mating_pool1, lb, ub, eta_c) 177 | offspring1 = mutation(offspring1, lb, ub, pm, eta_m) 178 | mating_pool2 = selection(pop2, F2, pc) 179 | offspring2 = crossover(mating_pool2, lb, ub, eta_c) 180 | offspring2 = mutation(offspring2, lb, ub, pm, eta_m) 181 | off_objs1, off_CV1 = cal_obj(offspring1, npop, nvar) 182 | off_objs2, off_CV2 = cal_obj(offspring2, npop, nvar) 183 | pop1 = np.concatenate((pop1, offspring1, offspring2), axis=0) 184 | pop2 = np.concatenate((pop2, offspring1, offspring2), axis=0) 185 | objs1 = np.concatenate((objs1, off_objs1, off_objs2), axis=0) 186 | objs2 = np.concatenate((objs2, off_objs1, off_objs2), axis=0) 187 | off_CV1 = np.concatenate((CV1, off_CV1, off_CV2), axis=0) 188 | off_CV2 = np.zeros(pop1.shape[0]) 189 | 190 | # Step 2.2. Environmental selection 191 | pop1, objs1, CV1, F1 = environmental_selection(pop1, npop, objs1, off_CV1) 192 | pop2, objs2, CV2, F2 = environmental_selection(pop2, npop, objs2, off_CV2) 193 | 194 | # Step 3. Sort the results 195 | CV2 = cal_obj(pop2, npop, nvar)[1] 196 | objs1 = objs1[np.where(CV1 == 0)[0]] 197 | objs2 = objs2[np.where(CV2 == 0)[0]] 198 | objs = np.concatenate((objs1, objs2), axis=0) 199 | F = fitness(objs, np.zeros(objs.shape[0]))[1] 200 | pf = objs[np.where(F < 1)[0]] 201 | x = [o[0] for o in pf] 202 | y = [o[1] for o in pf] 203 | plt.scatter(x, y) 204 | plt.xlabel('objective 1') 205 | plt.ylabel('objective 2') 206 | plt.title('The Pareto front of MW11') 207 | plt.savefig('Pareto front') 208 | plt.show() 209 | 210 | 211 | if __name__ == '__main__': 212 | main(100, 500, np.array([0] * 15), np.array([1] * 15)) 213 | --------------------------------------------------------------------------------