当使用scipy least_squares()来防止sqrt()返回虚数时,如何约束多个变量之间的相互关系?

8oomwypt  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(76)

我试着拟合一个如下形式的方程:
f(r)= B r^2/[1+sqrt(1-(A+1)(B r)^2)]
其中A和B作为拟合系数。当我优化时,平方根内的参数变为负值,因此least_squares将返回错误。
我用它来适应各种独特的数据,所以我想避免做出一个很好的初步猜测。
下面是基本代码:

def base(r,c,K):
    baseSag = c * r**2 / (1 + np.sqrt(1 - (K+1)*(c*r)**2 ))
    return baseSag

def base_residual(toFit,r,trueSag):
    c = toFit[0]
    K = toFit[1]

    arg = 1 - (K+1)*(c*r)**2    

    residual = base(r,c,K) - trueSag
    return residual

truesurf =  # Various input data
inital_guess = np.ones(2)*0.1 
# I have also tried better guesses of inital_guess = [-36,14.35] but I want to avoid doing that 

fit = least_squares(base_residual,inital_guess,
                    args = (r_points,truesurf),
                    verbose = 2,
                    gtol = None,
                    ftol = None)

字符串
我尝试添加一个项,如果sqrt参数为负,则增加残差,但这不起作用。

def base_residual(toFit,r,trueSag):
    c = toFit[0]
    K = toFit[1]

    arg = 1 - (K+1)*(c*r)**2    

    isNeg = False
    for ele in arg:
        if ele < 0:
            isNeg = True
            return isNeg

    if isNeg:
        residual = arg**2
    else:
        residual = base(r,c,K) - trueSag

    return residual


我怎样才能使f(r)不变为虚函数呢?AFAIK没有一种方法可以将多个变量相互约束(即约束1-(A+1)(B r)^2)> 0)
此外,我想将K和c限制在某些值之间-不知道当我将参数 Package 在base_rresidual中时如何做到这一点。-90彡A0.1,B彡-0.1< 90 and B >。这可能吗?

dwbf0jvd

dwbf0jvd1#

其中一个选项是向COBYLA发送一组线性和非线性约束:

from functools import partial

import numpy as np
from numpy.random import default_rng
from scipy.optimize import minimize, NonlinearConstraint, LinearConstraint

def base(r: np.ndarray, c: float, K: float) -> np.ndarray:
    base_sag = c * r**2 / (1 + np.sqrt(1 - (K+1)*(c*r)**2))
    return base_sag

def base_residual(to_fit: np.ndarray, r: np.ndarray, true_sag: np.ndarray) -> float:
    residual = base(r, *to_fit) - true_sag
    return residual.dot(residual)

def argument_constr(to_fit: np.ndarray, r: np.ndarray) -> np.ndarray:
    # The argument to the square root must be kept positive
    c, K = to_fit
    return 1 - (K + 1) * (c*r)**2

def c_constr(to_fit: np.ndarray) -> float:
    # c <= -0.1 or c >= 0.1
    c, K = to_fit
    return c**2

rand = default_rng(seed=0)
r_points = rand.uniform(low=-5e-3, high=5e-3, size=10)
true_surf = np.array([
    -3e-5, -9e-5, -4e-4, -4e-4,
    -1e-4, -3e-4, -2e-5, -9e-5,
    -3e-6, -3e-4])

fit = minimize(
    fun=base_residual, args=(r_points, true_surf),
    x0=(-30, 10),
    tol=1e-12,
    method='COBYLA',
    constraints=(
        LinearConstraint(
            A=(0, 1), lb=-90, ub=90,  # bounds on K
        ),
        NonlinearConstraint(
            fun=partial(argument_constr, r=r_points),
            lb=0, ub=np.inf,
        ),
        NonlinearConstraint(
            fun=c_constr, lb=0.1**2, ub=np.inf,
        ),
    ),
    options={'maxiter': 100_000},
)
assert fit.success, fit.message
print(fit)

个字符

相关问题