如何使shgo算法结合COBYLA成功收敛并适配约束与边界?
问题:SHGO结合COBYLA优化时参数越界崩溃,换L-BFGS-B无法得到全局最优
我用SHGO算法做黑箱模拟的全局最大化(目标函数负责运行并评估模拟),设置了5个输入变量,需要通过边界和约束限制几何参数。SHGO本应是适合带约束全局优化的选择,但目前无法让它正确处理边界与约束并收敛。
我的优化代码
bnds = [(50*1e-9,500*1e-9), (50*1e-9,500*1e-9), (1,20), (20*1e-9,80*1e-9), (250*1e-9,800*1e-9)] def constraint1(x): return x[4]-50*1e-9-2*x[0] # x[4] ≤ 2*x[0] - 50nm(阈值) def constraint2(x): return x[1]-x[3]-20*1e-9 # x[1]-x[3] ≥ 20nm(阈值) def constraint3(x): return x[0]-(x[1]/2)*(2.978/x[2])-20*1e-9 cons = ({'type': 'ineq', 'fun': constraint1}, {'type': 'ineq', 'fun': constraint2}, {'type': 'ineq', 'fun': constraint3}) minimizer_kwargs = {'method':'COBYLA', 'bounds': bnds, 'constraints':cons} opts = {'disp':True} res_shgo = shgo(objective, bounds=bnds, constraints=cons, sampling_method='sobol', minimizer_kwargs=minimizer_kwargs, options=opts)
遇到的问题1:COBYLA作为局部优化器时参数越界
全局评估完成33轮后启动极小值池:
Evaluations completed. Search for minimiser pool --- Starting minimization at [3.3828125e-07 4.6484375e-07 1.1984375e+01 6.7812500e-08 7.5703125e-07]...
用COBYLA做局部极小化时,几轮后参数就超出边界,直接导致模拟崩溃。
遇到的问题2:换L-BFGS-B能收敛但无全局最优
将局部优化器改为L-BFGS-B:
minimizer_kwargs = {'method':'L-BFGS-B'}
算法能收敛,但结果不是全局极小值,输出如下:
lres = fun: -20.247226776119533 hess_inv: <5x5 LbfgsInvHessProduct with dtype=float64> jac: array([ 1.70730429e+09, 1.22968297e+09, 0.00000000e+00, -1.82566323e+09, 1.83071706e+09]) message: 'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL' nfev: 6 nit: 0 njev: 1 status: 0 success: True x: array([2.43359375e-07, 2.99609375e-07, 1.48046875e+01, 7.01562500e-08, 6.23828125e-07]) Minimiser pool = SHGO.X_min = [] Successfully completed construction of complex.
现在需要解决的是:如何让SHGO结合COBYLA成功终止,同时保证参数不越界?
解决方案建议
1. 修正COBYLA的约束与边界处理逻辑
COBYLA不原生支持bounds参数,你在minimizer_kwargs里传入bounds是无效的,这就是参数越界的核心原因。必须把边界条件转换成不等式约束,和现有约束合并后传给COBYLA:
# 生成边界约束:将上下界转为COBYLA可识别的不等式约束 bound_cons = [] for idx, (low, high) in enumerate(bnds): # 下界约束:x[idx] ≥ low → 返回值≥0为可行 bound_cons.append({'type': 'ineq', 'fun': lambda x, idx=idx, low=low: x[idx] - low}) # 上界约束:x[idx] ≤ high → 返回值≥0为可行 bound_cons.append({'type': 'ineq', 'fun': lambda x, idx=idx, high=high: high - x[idx]})
2. 修正约束函数的逻辑(关键!)
COBYLA要求不等式约束的返回值≥0时,参数处于可行域。你的constraint1逻辑写反了:
# 原约束1逻辑错误,修正后: def constraint1(x): return (2*x[0] - 50*1e-9) - x[4] # 满足x[4] ≤ 2*x[0]-50nm时,返回值≥0
constraint2逻辑正确,constraint3需要你确认预期的约束方向,确保返回值≥0对应可行区域。
3. 调整SHGO与COBYLA的参数配置
- 给COBYLA设置匹配参数尺度的初始步长(
rhobeg)和收敛阈值,避免大步跳出可行域 - 增加SHGO的初始采样数(
n),提升全局探索能力 - 限制总评估次数,适配黑箱模拟的计算成本
调整后的完整代码示例
from scipy.optimize import shgo bnds = [(50*1e-9,500*1e-9), (50*1e-9,500*1e-9), (1,20), (20*1e-9,80*1e-9), (250*1e-9,800*1e-9)] # 修正后的约束函数 def constraint1(x): return (2*x[0] - 50*1e-9) - x[4] # x[4] ≤ 2*x[0]-50nm → 返回≥0为可行 def constraint2(x): return x[1] - x[3] -20*1e-9 # x[1]-x[3]≥20nm → 返回≥0为可行 def constraint3(x): return x[0] - (x[1]/2)*(2.978/x[2]) -20*1e-9 # 确认约束方向:返回≥0对应可行域 # 生成边界约束 bound_cons = [] for idx, (low, high) in enumerate(bnds): bound_cons.append({'type': 'ineq', 'fun': lambda x, idx=idx, low=low: x[idx] - low}) bound_cons.append({'type': 'ineq', 'fun': lambda x, idx=idx, high=high: high - x[idx]}) # 合并所有约束 all_constraints = ( {'type': 'ineq', 'fun': constraint1}, {'type': 'ineq', 'fun': constraint2}, {'type': 'ineq', 'fun': constraint3} ) + tuple(bound_cons) # COBYLA参数配置:去掉无效的bounds,增加步长和迭代控制 minimizer_kwargs = { 'method': 'COBYLA', 'constraints': all_constraints, 'options': { 'maxiter': 200, 'rhobeg': 1e-10, # 初始步长匹配纳米级参数 'tol': 1e-12 } } # SHGO全局优化配置 shgo_options = { 'disp': True, 'maxfev': 500 # 限制总评估次数,适合黑箱模拟 } res_shgo = shgo( objective, bounds=bnds, constraints=all_constraints, sampling_method='sobol', n=100, # 增加初始采样数,提升全局探索能力 minimizer_kwargs=minimizer_kwargs, options=shgo_options ) print(res_shgo)
为什么L-BFGS-B没得到全局最优?
L-BFGS-B是局部优化器,只会在初始采样点附近收敛。如果SHGO的初始采样未覆盖全局最优区域,局部搜索只能得到局部解。而COBYLA在正确配置约束后,能在可行域内更灵活地搜索,配合SHGO的全局采样,更有机会找到全局最优。
内容的提问来源于stack exchange,提问作者loco39
相关产品推荐
相关产品推荐

