def _minimize_pso(fun, x0, confunc=None, friction=.8, max_velocity=5., g_rate=.8, l_rate=.5, max_iter=1000, stable_iter=100, ptol=1e-6, ctol=1e-6, callback=None, verbose=False, savefile=None): """Internal implementation for ``psopy.minimize``. See Also -------- psopy.minimize : The SciPy compatible interface to this function. Refer to its documentation for an explanation of the parameters. psopy.gen_confunc : Utility function to convert SciPy style constraints to the form required by this function. Parameters ---------- x0 : array_like of shape (N, D) Initial position to begin PSO from, where ``N`` is the number of points and ``D`` the dimensionality of each point. For the constrained case these points should satisfy all constraints. fun : callable The objective function to be minimized. Must be in the form ``fun(pos, *args)``. The argument ``pos``, is a 2-D array for initial positions, where each row specifies the position of a different particle, and ``args`` is a tuple of any additional fixed parameters needed to completely specify the function. confunc : callable The function that describes constraints. Must be of the form ``confunc(pos)`` that returns the constraint matrix. Notes ----- Using this function directly allows for a slightly faster implementation that does away with the need for the additional recursive calls needed to wrap the constraint and objective functions for compatibility with Scipy. Examples -------- These examples are identical to those laid out in ``psopy.minimize`` and serve to illustrate the additional overhead in ensuring compatibility. >>> import numpy as np >>> from psopy import _minimize_pso Consider the problem of minimizing the Rosenbrock function implemented as ``scipy.optimize.rosen``. >>> from scipy.optimize import rosen >>> fun = lambda x: np.apply_along_axis(rosen, 1, x) Initialize 1000 particles and run the minimization function. >>> x0 = np.random.uniform(0, 2, (1000, 5)) >>> res = _minimize_pso(fun, x0, stable_iter=50) >>> res.x array([1.00000003, 1.00000017, 1.00000034, 1.0000006 , 1.00000135]) Consider the constrained optimization problem with the objective function defined as: >>> fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 >>> fun_ = lambda x: np.apply_along_axis(fun, 1, x) and constraints defined as: >>> cons = ({'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2}, ... {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6}, ... {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2}, ... {'type': 'ineq', 'fun': lambda x: x[0]}, ... {'type': 'ineq', 'fun': lambda x: x[1]}) Initializing the constraint function and feasible solutions: >>> from psopy import init_feasible, gen_confunc >>> x0 = init_feasible(cons, low=0., high=2., shape=(1000, 2)) >>> confunc = gen_confunc(cons) Running the constrained version of the function: >>> res = _minimize_pso(fun_, x0, confunc=confunc, options={ ... 'g_rate': 1., 'l_rate': 1., 'max_velocity': 4., 'stable_iter': 50}) >>> res.x array([ 1.39985398, 1.69992748]) """ if verbose: message = setup_print(x0.shape[1], max_iter, confunc is not None) if savefile: iterinfo = [] position = np.copy(x0) velocity = np.random.uniform(-max_velocity, max_velocity, position.shape) pbest = np.copy(position) gbest = pbest[np.argmin(fun(pbest))] oldfit = fun(gbest[None])[0] stable_count = 0 for ii in range(max_iter): # Determine global and local gradient. dv_g = g_rate * uniform(0, 1) * (gbest - position) if confunc is not None: leaders = np.argmin(distance.cdist(position, pbest, 'sqeuclidean'), axis=1) dv_l = l_rate * uniform(0, 1) * (pbest[leaders] - position) else: dv_l = l_rate * uniform(0, 1) * (pbest - position) # Update velocity and position of particles. velocity *= friction velocity += (dv_g + dv_l) np.clip(velocity, -max_velocity, max_velocity, out=velocity) position += velocity to_update = (fun(position) < fun(pbest)) if confunc is not None: to_update &= (confunc(position).sum(axis=1) < ctol) if to_update.any(): pbest[to_update] = position[to_update] gbest = pbest[np.argmin(fun(pbest))] # Termination criteria. fval = fun(gbest[None])[0] if np.abs(oldfit - fval) < ptol: stable_count += 1 if stable_count == stable_iter: break else: stable_count = 0 oldfit = fval if verbose or savefile: info = [ii, gbest, fval] if confunc is not None: cv = np.max(confunc(gbest[None])) info.append(cv) if verbose: print(message.format(*info)) if savefile: iterinfo.append(info) # Final callback. if callback is not None: position = callback(position) if savefile: save_info(savefile, iterinfo, constraints=confunc is not None) result = OptimizeResult(x=gbest, fun=fun(gbest[None])[0], nit=ii, nsit=stable_count) violation = False if confunc is not None: convec = confunc(gbest[None]) result.maxcv = np.max(convec) result.cvec = convec if convec.sum() > ctol: violation = True if violation: result.status = 2 elif ii == max_iter: result.status = 1 else: result.status = 0 result.success = not result.status return result
def _minimize_qpso(fun, x0, confunc=None, g=.96, max_iter=1000, stable_iter=40, ptol=1e-6, ctol=1e-6, levy_rate=0, decay_rate=0, reduction_rate=0.5, callback=None, verbose=False, savefile=None): """Internal implementation for ``psopy.minimize_qpso``. See Also -------- psopy.minimize_qpso : The SciPy compatible interface to this function. Refer to its documentation for an explanation of the parameters. psopy.gen_confunc : Utility function to convert SciPy style constraints to the form required by this function. Parameters ---------- x0 : array_like of shape (N, D) Initial position to begin QPSO from, where ``N`` is the number of points and ``D`` the dimensionality of each point. For the constrained case these points should satisfy all constraints. fun : callable The objective function to be minimized. Must be in the form ``fun(pos, *args)``. The argument ``pos``, is a 2-D array for initial positions, where each row specifies the position of a different particle, and ``args`` is a tuple of any additional fixed parameters needed to completely specify the function. confunc : callable The function that describes constraints. Must be of the form ``confunc(pos)`` that returns the constraint matrix. levy_rate: float Whether to run the levy decay qpso or not. > 0 value turns on levy walk decay_rate: float Whether to turn on the decay function or not. > 0 value turns on the decay rate Notes ----- Chaotic Quantum PSO Using this function directly allows for a slightly faster implementation that does away with the need for the additional recursive calls needed to wrap the constraint and objective functions for compatibility with Scipy. """ if verbose: message = setup_print(x0.shape[1], max_iter, confunc is not None) if savefile: iterinfo = [] position = np.copy(x0) nparam = len(position) pbest = np.copy(position) gbest = pbest[np.argmin(fun(pbest))] oldfit = fun(gbest[None])[0] stable_count = 0 dimension = len(position[0]) #simple levy walk. Make a decay function which will push particles around using stable_iter,max_iter is reached, pushing them away from pbest beta = 3 / 2 sigma = (gamma(1 + beta) * sin(pi * beta / 2) / (gamma( (1 + beta) / 2) * beta * 2**((beta - 1) / 2)))**(1 / beta) decay = 1 stepsize = 1.0 for ii in range(max_iter): mbest = np.sum(pbest, axis=0) / pbest.shape[0] u = np.random.normal(0, 1, size=dimension) * sigma v = np.random.normal(0, 1, size=dimension) step = u / abs(v)**(1 / beta) psi_1 = uniform(0, 1) psi_2 = uniform(0, 1) dv_g = psi_1 * gbest if confunc is not None: leaders = np.argmin(distance.cdist(position, pbest, 'sqeuclidean'), axis=1) dv_l = psi_2 * pbest[leaders] else: dv_l = psi_2 * pbest P = (dv_g + dv_l) / (psi_1 + psi_2) u = uniform(0, 1, nparam) stepsize = 1.0 for i in range(0, nparam): if levy_rate > 0: stepsize = 0.01 * step * (1 / (0.0000001 + position[i] - gbest[i])) if decay_rate > 0: decay = stepsize * 5 * (0.001)**(ii / (max_iter * 0.05)) + 1 if uniform(0, 1) > 0.5: position[i] = P[i] - mbest * np.log(1 / u[i]) * decay else: position[i] = P[i] + mbest * np.log(1 / u[i]) * decay to_update = (fun(position) < fun(pbest)) if confunc is not None: to_update &= (confunc(position).sum(axis=1) < ctol) if to_update.any(): pbest[to_update] = position[to_update] gbest = pbest[np.argmin(fun(pbest))] # Termination criteria. fval = fun(gbest[None])[0] if np.abs(oldfit - fval) < ptol: stable_count += 1 if stable_count == stable_iter: break else: stable_count = 0 oldfit = fval if verbose or savefile: info = [ii, gbest, fval] if confunc is not None: cv = np.max(confunc(gbest[None])) info.append(cv) if verbose: print(message.format(*info)) if savefile: iterinfo.append(info) # Final callback. if callback is not None: position = callback(position) if savefile: save_info(savefile, iterinfo, constraints=confunc is not None) result = OptimizeResult(x=gbest, fun=fun(gbest[None])[0], nit=ii, nsit=stable_count) violation = False if confunc is not None: convec = confunc(gbest[None]) result.maxcv = np.max(convec) result.cvec = convec if convec.sum() > ctol: violation = True if violation: result.status = 2 elif ii == max_iter: result.status = 1 else: result.status = 0 result.success = not result.status return result