Files
cmdla/Lessons/10-12/DIS.jl

223 lines
5.8 KiB
Julia
Raw Normal View History

2023-10-29 02:06:02 +01:00
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