有没有办法在Julia中绑定NLsolve搜索的区域?

有没有办法在Julia中绑定NLsolve搜索的区域?,julia,nonlinear-optimization,equation-solving,Julia,Nonlinear Optimization,Equation Solving,我试图找到一个非线性(大致四次)方程的根。 方程总是有四个根,一对接近零的根,一个大的正根和一个大的负根。我想确定两个接近零的根中的任何一个,但是即使初始猜测非常接近这些根,nlsolve也似乎总是收敛于大的正根或负根 函数的曲线图本质上看起来像一个恒定的负值,有一个(非常窄的)均匀有序的极点接近于零,并且在较大的正负根处逐渐上升到过零 有没有什么方法可以限制nlsolve搜索的区域,或者做一些事情使其对函数中的极点更敏感 编辑: 下面是一些重现问题的示例代码: using NLsolve f

我试图找到一个非线性(大致四次)方程的根。 方程总是有四个根,一对接近零的根,一个大的正根和一个大的负根。我想确定两个接近零的根中的任何一个,但是即使初始猜测非常接近这些根,nlsolve也似乎总是收敛于大的正根或负根

函数的曲线图本质上看起来像一个恒定的负值,有一个(非常窄的)均匀有序的极点接近于零,并且在较大的正负根处逐渐上升到过零

有没有什么方法可以限制nlsolve搜索的区域,或者做一些事情使其对函数中的极点更敏感

编辑: 下面是一些重现问题的示例代码:

using NLsolve

function f!(F,x)
    x = x[1]
    F[1] = -15000 + x^4 / (x+1e-5)^2
end
# nlsolve will find the root at -122
nlsolve(f!,[0.0])
作为输出,我得到:

Results of Nonlinear Solver Algorithm
 * Algorithm: Trust-region with dogleg and autoscaling
 * Starting Point: [0.0]
 * Zero: [-122.47447713915808]
 * Inf-norm of residuals: 0.000000
 * Iterations: 15
 * Convergence: true
   * |x - x'| < 0.0e+00: false
   * |f(x)| < 1.0e-08: true
 * Function Calls (f): 16
 * Jacobian Calls (df/dx): 6
产生

4-element Array{Complex{Float64},1}:
     122.47449713915809 - 0.0im
    -122.47447713915808 + 0.0im
 -1.0000000813048448e-5 + 0.0im
  -9.999999186951818e-6 + 0.0im
我很想找到一种方法,在不知道目标函数的精确形式的情况下,确定x=-1e-5极点周围的一对根

编辑2: 正在尝试根目录.jl:

using Roots
f(x) = -15000 + x^4 / (x+1e-5)^2

find_zero(f,0.0) # finds +122... root
find_zero(f,(-1e-4,0.0)) # error, not a bracketing interval
find_zeros(f,-1e-4,0.0) # finds 0-element Array{Float64,1}
find_zeros(f,-1e-4,0.0,no_pts=6) # finds root slightly less than -1e-5
find_zeros(f,-1e-4,0.0,no_pts=10) # finds 0-element Array{Float64,1}, sensitive to value of no_pts
我可以让
find_zero
工作,但它对
no_pts
参数和我选择的端点的精确值非常敏感。在
no_pts
上进行循环并获取第一个非空结果可能会起作用,但更确定的方法会更好

编辑3: 下面是应用Bogumił建议的tanh变换

using NLsolve
function f_tanh!(F,x)
    x = x[1]
    x = -1e-4 * (tanh(x)+1) / 2
    F[1] = -15000 + x^4 / (x+1e-5)^2
end

nlsolve(f_tanh!,[100.0]) # doesn't converge
nlsolve(f_tanh!,[1e5]) # doesn't converge

using Roots
function f_tanh(x)
    x = -1e-4 * (tanh(x)+1) / 2
    return -15000 + x^4 / (x+1e-5)^2
end

find_zeros(f_tanh,-1e10,1e10) # 0-element Array
find_zeros(f_tanh,-1e3,1e3,no_pts=100) # 0-element Array
find_zero(f_tanh,0.0) # convergence failed
find_zero(f_tanh,0.0,max_evals=1_000_000,maxfnevals=1_000_000) # convergence failed
EDIT4:这种技术的组合在95%的时间里至少识别出一个根,这对我来说已经足够好了

using Peaks
using Primes
using Roots

# randomize pole location
a = 1e-4*rand()
f(x) = -15000 + x^4 / (x+a)^2

# do an initial sample to find the pole location
l = 1000
minval = -1e-4
maxval = 0
m = []
sample_r = []
while l < 1e6
    sample_r = range(minval,maxval,length=l)
    rough_sample = f.(sample_r)
    m = maxima(rough_sample)
    if length(m) > 0
        break
    else
        l *= 10
    end
end
guess = sample_r[m[1]]

# functions to compress the range around the estimated pole
cube(x) = (x-guess)^3 + guess 
uncube(x) = cbrt(x-guess) + guess
f_cube(x) = f(cube(x))

shift = l ÷ 1000
low = sample_r[m[1]-shift]
high = sample_r[m[1]+shift]
# search only over prime no_pts, so no samplings divide into each other
# possibly not necessary?
for i in primes(500)
    z = find_zeros(f_cube,uncube(low),uncube(high),no_pts=i)
    if length(z)>0
        println(i)
        println(cube.(z))
        break
    end
end
使用峰值
使用素数
使用根
#随机化极点位置
a=1e-4*rand()
f(x)=-15000+x^4/(x+a)^2
#做一个初始样本以找到磁极位置
l=1000
最小值=-1e-4
maxval=0
m=[]
样本r=[]
而l<1e6
样本=范围(最小值、最大值、长度=l)
粗样品=f(样品r)
m=最大值(粗略样本)
如果长度(m)>0
打破
其他的
l*=10
结束
结束
猜测=样本[m[1]]
#函数压缩估计极点周围的范围
立方体(x)=(x-猜测)^3+猜测
uncube(x)=cbrt(x-猜测)+猜测
f_立方体(x)=f(立方体(x))
班次=l÷1000
低=采样\u r[m[1]-移位]
高=采样频率[m[1]+移位]
#只搜索素数no_pts,这样就不会有样本相互分割
#可能没有必要?
对于素数中的i(500)
z=找到零(f_立方体、未固化(低)、未固化(高)、无固化=i)
如果长度(z)>0
println(i)
println(立方体(z))
打破
结束
结束

如果您提供了有关您的问题的更多信息,则可以给出更多的评论

然而,总的来说:

  • 您的问题似乎是单变量的,在这种情况下,您可以使用root.jl,其中
    find_zero
    find_zero
    给出您要求的接口(即允许指定搜索区域)
  • 如果问题是多变量的,您可以在
    nlsolve
    的问题说明中使用多个选项(因为默认情况下不允许指定边界框AFAICT)。最简单的方法是使用变量转换。例如,您可以对每个变量应用
    ai*tanh(xi)+bi
    变换,选择
    ai
    bi
    ,以便将其限定到所需的间隔
  • 您在定义中遇到的第一个问题是,定义
    f
    的方式从来不会在查找的两个根附近穿过
    0
    ,因为
    Float64
    在编写
    1e-5
    时没有足够的精度。您需要使用更高的计算精度:

    julia> using Roots
    
    julia> f(x) = -15000 + x^4 / (x+1/big(10.0^5))^2
    f (generic function with 1 method)
    
    julia> find_zeros(f,big(-2*10^-5), big(-8*10^-6), no_pts=100)
    2-element Array{BigFloat,1}:
     -1.000000081649671426108658262468117284940444265467160592853348997523986352593615e-05
     -9.999999183503552405580084054429938261707450678661727461293670518591720605751116e-06
    

    并将
    no_pts
    设置为足够大,以便找到包含根的间隔。

    Hi Bogumił,谢谢您的回答。我尝试了您对示例问题的一些建议,请参见我的编辑<代码>查找零似乎偶尔会起作用,具体取决于子间隔的确切数量。我已经对我认为是什么导致了您的问题进行了评论。啊,我明白了,使用大浮点似乎确实有帮助。我现在唯一的问题是
    find_roots
    no_pts
    的精确范围和值很敏感。我在循环中尝试
    no_pts=I
    ,用于素数中的I(200)。这似乎对
    i
    约3/4时间的某些值有效。有什么办法可以加快速度,或者避免做循环吗?我不认为这很容易得到帮助
    find_roots
    需要找到两个点,一个是函数有正值的点,另一个是函数有负值的点,然后搜索这个间隔,所以你的搜索必须足够密集,能够到达两个零之间的点,这两个点在你的情况下非常接近。再次感谢你的帮助,我会接受你的答案。我能稍微加快搜索速度。我在一个范围内对函数进行采样,然后使用
    Peaks.jl
    确定极点的大致位置。然后我将搜索范围缩小到该峰值附近,并进行三次变换(因此近极点区域的采样密度更高),然后进行
    查找根
    (不幸的是,仍然处于
    无点
    的循环中)。cubing还具有与
    Float64
    s协调工作的优点。这仍然无法确定根~5%的时间,但这对我来说已经足够好了。
    julia> using Roots
    
    julia> f(x) = -15000 + x^4 / (x+1/big(10.0^5))^2
    f (generic function with 1 method)
    
    julia> find_zeros(f,big(-2*10^-5), big(-8*10^-6), no_pts=100)
    2-element Array{BigFloat,1}:
     -1.000000081649671426108658262468117284940444265467160592853348997523986352593615e-05
     -9.999999183503552405580084054429938261707450678661727461293670518591720605751116e-06