223 lines
5.8 KiB
Julia
223 lines
5.8 KiB
Julia
using LinearAlgebra
|
|
using Printf
|
|
using Plots
|
|
|
|
function DIS(f;
|
|
rangeplt::Union{Nothing, Tuple{Real, Real}}=nothing,
|
|
sfgrd::Real=0,
|
|
eps::Real=1e-6,
|
|
MaxFeval::Integer=100,
|
|
plt::Union{Plots.Plot, Nothing}=nothing,
|
|
plotatend::Bool=true,
|
|
Plotg::Integer=0
|
|
)::Tuple{Real, String}
|
|
|
|
# Plotg
|
|
# 1 = the function value / gap are plotted
|
|
# 2 = the function and the model (if used) are plotted
|
|
# all the rest: nothing is plotted
|
|
|
|
resolution = 1000
|
|
|
|
Interactive = false # if we pause at every iteration
|
|
|
|
# reading and checking input- - - - - - - - - - - - - - - - - - - - - - - -
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
if rangeplt == nothing
|
|
(fStar, _, _, rangeplt) = f(nothing)
|
|
else
|
|
fStar = -Inf
|
|
end
|
|
|
|
xm = rangeplt[1] # x_-
|
|
xp = rangeplt[2] # x_+
|
|
|
|
if xm > xp
|
|
throw(ArgumentError("rangeplt is empty"))
|
|
return (NaN, "empty")
|
|
end
|
|
|
|
(fxm, f1xm, _) = f(xm)
|
|
if( f1xm > 0 )
|
|
throw(DomainError(rangeplt, "f'(x_-) must be ≤ 0"))
|
|
return (NaN, "empty")
|
|
end
|
|
|
|
(fxp, f1xp, _) = f(xp)
|
|
if( f1xp < 0 )
|
|
throw(DomainError(rangeplt, "f'(x_+) must be ≥ 0"))
|
|
return (NaN, "empty")
|
|
end
|
|
|
|
if (sfgrd < 0) || (sfgrd ≥ 0.5)
|
|
throw(DomainError(sfgrd, "sfgrd must be in [ 0 , 1/2 )"))
|
|
return (NaN, "empty")
|
|
end
|
|
|
|
if MaxFeval < 2
|
|
throw(ArgumentError("At least two function computations are required"))
|
|
return (NaN, "empty")
|
|
end
|
|
|
|
# initializations - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
feval = 2
|
|
status = "optimal"
|
|
|
|
if f1xm ≥ -eps
|
|
return (xm, status)
|
|
end
|
|
|
|
if f1xp ≤ eps
|
|
return (xp, status)
|
|
end
|
|
|
|
fbest = min(fxp, fxm)
|
|
f1x = min(-f1xp, f1xm)
|
|
|
|
if Plotg == 1
|
|
gap = []
|
|
end
|
|
|
|
if Plotg == 1 && plt == nothing
|
|
plt = plot(yscale = :log,
|
|
xlims=(0, 35),
|
|
ylims=(1e-15, Inf),
|
|
guidefontsize=16)
|
|
elseif Plotg == 2 && plt == nothing
|
|
plt = plot(legend = false)
|
|
end
|
|
|
|
if iszero(sfgrd)
|
|
println("Dichotomic search")
|
|
else
|
|
@printf("Dichotomic search with safeguarded interpolation (%1.4f)\n", sfgrd)
|
|
end
|
|
|
|
if fStar > -Inf
|
|
println("feval\trel gap\t\tx_-\t\tx_+\t\tx\t\tf'(x)")
|
|
else
|
|
println("feval\tfbest\t\tx_-\t\tx_+\t\tx\t\tf'(x)")
|
|
end
|
|
|
|
# main loop - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
x = 0
|
|
|
|
while true
|
|
# stopping criteria - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
if feval > MaxFeval
|
|
status = "stopped"
|
|
break
|
|
end
|
|
|
|
# main logic- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
if iszero(sfgrd)
|
|
# compute the new point as the middle of the inteval
|
|
x = (xm + xp) / 2
|
|
else
|
|
# compute the new point by safeguarded quadratic interpolation
|
|
sfxp = xp - (xp - xm) * sfgrd
|
|
sfxm = xm + (xp - xm) * sfgrd
|
|
x = (xm * f1xp - xp * f1xm) / (f1xp - f1xm)
|
|
x = max(sfxm, min(sfxp, x))
|
|
end
|
|
|
|
(fx, f1x, _) = f(x) # compute f(x) and f'(x)
|
|
|
|
if fx < fbest
|
|
fbest = fx
|
|
end
|
|
|
|
feval += 1
|
|
|
|
# output statistics - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
if fStar > -Inf
|
|
gapk = (fbest - fStar) / max(abs(fStar), 1)
|
|
|
|
if Plotg == 1
|
|
push!(gap, gapk)
|
|
end
|
|
else
|
|
gapk = fbest
|
|
end
|
|
|
|
@printf("%4d\t%1.4e\t%1.8e\t%1.8e\t%1.8e\t%1.4e\n", feval, gapk, xm, xp, x, f1x)
|
|
|
|
if Plotg == 2
|
|
xmp = xm - (xp - xm) / 20
|
|
xpp = xp + (xp - xm) / 20
|
|
|
|
for e in plt.series_list
|
|
e[:linealpha] = 0
|
|
end
|
|
|
|
xx = range(xmp, xpp, resolution)
|
|
yy = map(v -> v[1], f.(xx))
|
|
|
|
xlims!(plt, (xmp, xpp))
|
|
old_ylims = ylims(plt)
|
|
ylims!(plt, (old_ylims[1], max(fxm, fxp)))
|
|
plot!(plt, xx, yy)
|
|
|
|
if !iszero(sfgrd)
|
|
a = (f1xp - f1xm) / (2 * (xp - xm))
|
|
b = (xp * f1xm - xm * f1xp) / (xp - xm)
|
|
# a xm^2 + b xm + c == fxm ==>
|
|
# c == fxm - a xm^2 - b xm
|
|
c = fxm - a * xm^2 - b * xm
|
|
|
|
xlims!(plt, (xmp, xpp))
|
|
new_xticks = ([xmp, xm, sfxm, sfxp, xp, xpp],
|
|
[@sprintf("%1.1g", xmp), "x-", "sx-", "sx+", "x+", @sprintf("%1.1g", xpp)])
|
|
|
|
xticks!(plt, new_xticks)
|
|
plot!(plt, xx, @. a*xx^2 + b*xx + c)
|
|
else
|
|
new_xticks = ([xmp, xm, x, xp, xpp],
|
|
[@sprintf("%1.1g", xmp), "x-", "x", "x+", @sprintf("%1.1g", xpp)])
|
|
|
|
xticks!(plt, new_xticks)
|
|
end
|
|
end
|
|
# stopping criteria - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
if abs(f1x) ≤ eps
|
|
break
|
|
end
|
|
|
|
# restrict the interval based on sign of the derivative in xn - - - - -
|
|
if f1x < 0
|
|
xm = x
|
|
fxm = fx
|
|
f1xm = f1x
|
|
else
|
|
xp = x
|
|
fxp = fx
|
|
f1xp = f1x
|
|
end
|
|
|
|
if Plotg ≠ 0 && Interactive
|
|
IJulia.clear_output(wait=true)
|
|
display(plt)
|
|
sleep(0.1)
|
|
readline()
|
|
end
|
|
|
|
# iterate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
end
|
|
|
|
if Plotg == 1
|
|
plot!(plt,
|
|
gap,
|
|
linewidth=2)
|
|
end
|
|
|
|
if plotatend && Plotg ≠ 0
|
|
display(plt)
|
|
end
|
|
|
|
(x, status)
|
|
end |