Conditions | 25 |
Total Lines | 216 |
Lines | 0 |
Ratio | 0 % |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like grortir.externals.pyswarm.pso() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | # pylint: skip-file |
||
20 | def pso(func, lb, ub, ieqcons=[], f_ieqcons=None, args=(), kwargs={}, |
||
21 | swarmsize=100, omega=0.5, phip=0.5, phig=0.5, maxiter=100, |
||
22 | minstep=1e-8, minfunc=1e-8, debug=False, processes=1, |
||
23 | particle_output=False): |
||
24 | """ |
||
25 | Perform a particle swarm optimization (PSO) |
||
26 | |||
27 | Parameters |
||
28 | ========== |
||
29 | func : function |
||
30 | The function to be minimized |
||
31 | lb : array |
||
32 | The lower bounds of the design variable(s) |
||
33 | ub : array |
||
34 | The upper bounds of the design variable(s) |
||
35 | |||
36 | Optional |
||
37 | ======== |
||
38 | ieqcons : list |
||
39 | A list of functions of length n such that ieqcons[j](x,*args) >= 0.0 in |
||
40 | a successfully optimized problem (Default: []) |
||
41 | f_ieqcons : function |
||
42 | Returns a 1-D array in which each element must be greater or equal |
||
43 | to 0.0 in a successfully optimized problem. If f_ieqcons is specified, |
||
44 | ieqcons is ignored (Default: None) |
||
45 | args : tuple |
||
46 | Additional arguments passed to objective and constraint functions |
||
47 | (Default: empty tuple) |
||
48 | kwargs : dict |
||
49 | Additional keyword arguments passed to objective and constraint |
||
50 | functions (Default: empty dict) |
||
51 | swarmsize : int |
||
52 | The number of particles in the swarm (Default: 100) |
||
53 | omega : scalar |
||
54 | Particle velocity scaling factor (Default: 0.5) |
||
55 | phip : scalar |
||
56 | Scaling factor to search away from the particle's best known position |
||
57 | (Default: 0.5) |
||
58 | phig : scalar |
||
59 | Scaling factor to search away from the swarm's best known position |
||
60 | (Default: 0.5) |
||
61 | maxiter : int |
||
62 | The maximum number of iterations for the swarm to search (Default: 100) |
||
63 | minstep : scalar |
||
64 | The minimum stepsize of swarm's best position before the search |
||
65 | terminates (Default: 1e-8) |
||
66 | minfunc : scalar |
||
67 | The minimum change of swarm's best objective value before the search |
||
68 | terminates (Default: 1e-8) |
||
69 | debug : boolean |
||
70 | If True, progress statements will be displayed every iteration |
||
71 | (Default: False) |
||
72 | processes : int |
||
73 | The number of processes to use to evaluate objective function and |
||
74 | constraints (default: 1) |
||
75 | particle_output : boolean |
||
76 | Whether to include the best per-particle position and the objective |
||
77 | values at those. |
||
78 | |||
79 | Returns |
||
80 | ======= |
||
81 | g : array |
||
82 | The swarm's best known position (optimal design) |
||
83 | f : scalar |
||
84 | The objective value at ``g`` |
||
85 | p : array |
||
86 | The best known position per particle |
||
87 | pf: arrray |
||
88 | The objective values at each position in p |
||
89 | |||
90 | """ |
||
91 | |||
92 | assert len(lb)==len(ub), 'Lower- and upper-bounds must be the same length' |
||
93 | assert hasattr(func, '__call__'), 'Invalid function handle' |
||
94 | lb = np.array(lb) |
||
95 | ub = np.array(ub) |
||
96 | assert np.all(ub>lb), 'All upper-bound values must be greater than lower-bound values' |
||
97 | |||
98 | vhigh = np.abs(ub - lb) |
||
99 | vlow = -vhigh |
||
100 | |||
101 | # Initialize objective function |
||
102 | obj = partial(_obj_wrapper, func, args, kwargs) |
||
103 | |||
104 | # Check for constraint function(s) ######################################### |
||
105 | if f_ieqcons is None: |
||
106 | if not len(ieqcons): |
||
107 | if debug: |
||
108 | print('No constraints given.') |
||
109 | cons = _cons_none_wrapper |
||
110 | else: |
||
111 | if debug: |
||
112 | print('Converting ieqcons to a single constraint function') |
||
113 | cons = partial(_cons_ieqcons_wrapper, ieqcons, args, kwargs) |
||
114 | else: |
||
115 | if debug: |
||
116 | print('Single constraint function given in f_ieqcons') |
||
117 | cons = partial(_cons_f_ieqcons_wrapper, f_ieqcons, args, kwargs) |
||
118 | is_feasible = partial(_is_feasible_wrapper, cons) |
||
119 | |||
120 | # Initialize the multiprocessing module if necessary |
||
121 | if processes > 1: |
||
122 | import multiprocessing |
||
123 | mp_pool = multiprocessing.Pool(processes) |
||
124 | |||
125 | # Initialize the particle swarm ############################################ |
||
126 | S = swarmsize |
||
127 | D = len(lb) # the number of dimensions each particle has |
||
128 | x = np.random.rand(S, D) # particle positions |
||
129 | v = np.zeros_like(x) # particle velocities |
||
130 | p = np.zeros_like(x) # best particle positions |
||
131 | fx = np.zeros(S) # current particle function values |
||
132 | fs = np.zeros(S, dtype=bool) # feasibility of each particle |
||
133 | fp = np.ones(S)*np.inf # best particle function values |
||
134 | g = [] # best swarm position |
||
135 | fg = np.inf # best swarm position starting value |
||
136 | |||
137 | # Initialize the particle's position |
||
138 | x = lb + x*(ub - lb) |
||
139 | |||
140 | # Calculate objective and constraints for each particle |
||
141 | if processes > 1: |
||
142 | fx = np.array(mp_pool.map(obj, x)) |
||
143 | fs = np.array(mp_pool.map(is_feasible, x)) |
||
144 | else: |
||
145 | for i in range(S): |
||
146 | fx[i] = obj(x[i, :]) |
||
147 | fs[i] = is_feasible(x[i, :]) |
||
148 | |||
149 | # Store particle's best position (if constraints are satisfied) |
||
150 | i_update = np.logical_and((fx < fp), fs) |
||
151 | p[i_update, :] = x[i_update, :].copy() |
||
152 | fp[i_update] = fx[i_update] |
||
153 | |||
154 | # Update swarm's best position |
||
155 | i_min = np.argmin(fp) |
||
156 | if fp[i_min] < fg: |
||
157 | fg = fp[i_min] |
||
158 | g = p[i_min, :].copy() |
||
159 | else: |
||
160 | # At the start, there may not be any feasible starting point, so just |
||
161 | # give it a temporary "best" point since it's likely to change |
||
162 | g = x[0, :].copy() |
||
163 | |||
164 | # Initialize the particle's velocity |
||
165 | v = vlow + np.random.rand(S, D)*(vhigh - vlow) |
||
166 | |||
167 | # Iterate until termination criterion met ################################## |
||
168 | it = 1 |
||
169 | while it <= maxiter: |
||
170 | rp = np.random.uniform(size=(S, D)) |
||
171 | rg = np.random.uniform(size=(S, D)) |
||
172 | |||
173 | # Update the particles velocities |
||
174 | v = omega*v + phip*rp*(p - x) + phig*rg*(g - x) |
||
175 | # Update the particles' positions |
||
176 | x = x + v |
||
177 | # Correct for bound violations |
||
178 | maskl = x < lb |
||
179 | masku = x > ub |
||
180 | x = x*(~np.logical_or(maskl, masku)) + lb*maskl + ub*masku |
||
181 | |||
182 | # Update objectives and constraints |
||
183 | if processes > 1: |
||
184 | fx = np.array(mp_pool.map(obj, x)) |
||
185 | fs = np.array(mp_pool.map(is_feasible, x)) |
||
186 | else: |
||
187 | for i in range(S): |
||
188 | fx[i] = obj(x[i, :]) |
||
189 | fs[i] = is_feasible(x[i, :]) |
||
190 | |||
191 | # Store particle's best position (if constraints are satisfied) |
||
192 | i_update = np.logical_and((fx < fp), fs) |
||
193 | p[i_update, :] = x[i_update, :].copy() |
||
194 | fp[i_update] = fx[i_update] |
||
195 | |||
196 | # Compare swarm's best position with global best position |
||
197 | i_min = np.argmin(fp) |
||
198 | if fp[i_min] < fg: |
||
199 | if debug: |
||
200 | print('New best for swarm at iteration {:}: {:} {:}'\ |
||
201 | .format(it, p[i_min, :], fp[i_min])) |
||
202 | |||
203 | p_min = p[i_min, :].copy() |
||
204 | stepsize = np.sqrt(np.sum((g - p_min)**2)) |
||
205 | |||
206 | if np.abs(fg - fp[i_min]) <= minfunc: |
||
207 | print('Stopping search: Swarm best objective change less than {:}'\ |
||
208 | .format(minfunc)) |
||
209 | if particle_output: |
||
210 | return p_min, fp[i_min], p, fp |
||
211 | else: |
||
212 | return p_min, fp[i_min] |
||
213 | elif stepsize <= minstep: |
||
214 | print('Stopping search: Swarm best position change less than {:}'\ |
||
215 | .format(minstep)) |
||
216 | if particle_output: |
||
217 | return p_min, fp[i_min], p, fp |
||
218 | else: |
||
219 | return p_min, fp[i_min] |
||
220 | else: |
||
221 | g = p_min.copy() |
||
222 | fg = fp[i_min] |
||
223 | |||
224 | if debug: |
||
225 | print('Best after iteration {:}: {:} {:}'.format(it, g, fg)) |
||
226 | it += 1 |
||
227 | |||
228 | print('Stopping search: maximum iterations reached --> {:}'.format(maxiter)) |
||
229 | |||
230 | if not is_feasible(g): |
||
231 | print("However, the optimization couldn't find a feasible design. Sorry") |
||
232 | if particle_output: |
||
233 | return g, fg, p, fp |
||
234 | else: |
||
235 | return g, fg |