Scipy-optimize-最小化非线性方程SLSQP:不等式约束仍然给出不受约束的答案

luaexgnf  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(147)

我试图解决11个非线性联立方程与11个变量,其中所有变量都是积极的。
即使设置了所有解都应该为正的约束条件,我仍然得到否定的答案。例如,我设置f[6]= Pz,我理解为Pz〉=0,然而,最小化输出的Pz解是-1.38777878e-17。为什么会发生这种情况?我该怎么做才能解决这个问题(建议提出其他求解算法,如Nelder米德,为我的一套方程与这些约束是受欢迎的)。
我注意到,当我改变初始猜测时,解不再是负的,但我仍然认为,当我已经通过不等式约束指定所有解都是正的时,解不应该给出负的答案。

import numpy as np
from scipy.optimize import minimize
import sympy as sym
import math as math

###defining argument values
Cpz= 2 #argument
intensity=  0.04996599987353118 #argument
length = 10 #argument
Ax, Ay, Az, Bx, By, Bz, Cx, Cy, Cz, Dx, Dy, Dz = 0, length, 0, length, length,0,  0, 0, 0, length, 0, 0
ni, nm= 1.1, 1 #refrac index of optical grease and scint crystal #argument
thetacrit= sym.asin(nm/ni) #gives in radians
print (f"thetacrit in radians is {thetacrit}")

def my_fun(param):
    thetaC1= param[0]
    thetaC2= param[1]
    Cpx= param[2]
    Px=param[3]
    Pz=param[4]
    Cphorizdist= param[5]
    Cpvertdist= param[6]
    Cpdist3D= param[7]
    Chorizdist= param[8]
    Cvertdist= param[9]
    Cdist3D= param[10]
    f= np.zeros(11)
    
    f[0]= sym.asin((nm/ni)*sym.sin(thetaC2))- thetaC1
    f[1]= np.absolute(Px-Cpx)- Cphorizdist
    f[2]= np.absolute(Pz-Cpz)- Cpvertdist
    f[3]=( (Cphorizdist)**2 + (Cpvertdist)**2 )**(1/2)-Cpdist3D
    f[4]= np.absolute(Cpx-Cx)- Chorizdist
    f[5]= np.absolute(Cpz-Cz)-Cvertdist
    f[6]= (Chorizdist**2 + Cvertdist**2)**(1/2)- Cdist3D 
    f[7]= Cphorizdist/Cpdist3D-sym.sin(thetaC1)
    f[8]= Cpx/Cdist3D- sym.sin(thetaC2)
    f[9]= Cphorizdist/Cpvertdist- sym.tan(thetaC1)
    f[10]= 1/((Cpdist3D+Cdist3D)**2)-intensity
    
    return np.dot(f,f) #maybe add more

def my_cons(param):
    thetaC1= param[0]
    thetaC2= param[1]
    Cpx= param[2]
    Px=param[3]
    Pz=param[4]
    Cphorizdist= param[5]
    Cpvertdist= param[6]
    Cpdist3D= param[7]
    Chorizdist= param[8]
    Cvertdist= param[9]
    Cdist3D= param[10]

    f = np.zeros(13)
  
    #bc for solving method SLSQP, constraints dict type is ineq, these f[] become >=0
    #f[1] = -thetaC1

    
    f[0]= thetaC1
    f[1]= math.pi/2-thetaC1
    f[2]= thetaC2
    f[3]= math.pi/2-thetaC2
    f[4]= Cpx
    f[5]= Px
    f[6]= Pz
    f[7]= Cphorizdist
    f[8]= Cpvertdist
    f[9]= Cpdist3D
    f[10]= Chorizdist
    f[11] = Cvertdist
    f[12] = Cdist3D
    return f

cons = {'type' : 'ineq', 'fun': my_cons}
res = minimize(my_fun, (0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5), method='SLSQP',\
           constraints=cons,options= {"maxiter": 5000})
print(f"thetaC1,thetaC2,Cpx,Px,Pz,Cphorizdist,Cpvertdist,Cpdist3D,\
Chorizdist,Cvertdist,Cdist3D is {res}")
9o685dep

9o685dep1#

你的约束实际上只是优化变量的边界,所以我建议把它们作为边界传递,而不是使用更一般的约束:

x0 = 0.5*np.ones(12)
bounds = [(0, None) for _ in range(x0.size)]
bounds[0] = (0, np.pi/2) # thetaC1
bounds[1] = (0, np.pi/2) # thetaC2

cons = {'type' : 'ineq', 'fun': my_cons}
res = minimize(my_fun, x0=x0, bounds=bounds, method='SLSQP',\
           constraints=cons,options= {"maxiter": 5000})

话虽如此,还值得一提的是

  • 你应该尽量避免绝对值。由于绝对值的存在,你的目标函数不是连续可微的。因此,你违反了SLSQP算法的数学假设,一旦算法到达一个不连续点(即绝对值为零的点x),就会导致非常奇怪的行为。
  • 真实的上并不需要混合sympy,numpy和math模块。Numpy已经提供了所有你正在使用的函数和常量(np.arcins,np.sin,np.pi等)。

相关问题