using Plots using LinearAlgebra using Printf function GRS(f; rangeplt::Union{Nothing, Tuple{Real, Real}}=nothing, delta::Real=1e-6, MaxFeval::Integer=100, plt::Union{Plots.Plot, Nothing}=nothing, plotatend::Bool=true, Plotg::Integer=0 )::Tuple{Real, String} # Plotg # 0 = nothing is plotted # 1 = the function value / gap are plotted # 2 = the function and the test points used are 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 if MaxFeval < 2 throw(ArgumentError("At least two function computations are required")) return (NaN, "empty") end # initializations - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r = (sqrt( 5 ) - 1)/2 xmp = xm + (1 - r) * (xp - xm) # x'_- xpp = xm + r * (xp - xm) # x'_+ fxmp = f(xmp)[1] # f(x'_-) fxpp = f(xpp)[1] # f(x'_+) feval = 2 if fxmp ≤ fxpp fx = fxmp else fx = fxpp end status = "optimal" if Plotg > 0 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 print("Golden ratio search\n") if fStar > -Inf print("feval\trel gap\t\tx_-\t\tx_+\n") else print("feval\tfbest\t\tx_-\t\tx_+\n") end # main loop - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - while xp - xm > delta # output statistics - - - - - - - - - - - - - - - - - - - - - - - - - - if fStar > -Inf gapk = (fx - fStar) / max(abs(fStar), 1) if Plotg == 1 push!(gap, gapk) end else gapk = fx end @printf("%4d\t%1.4e\t%1.8e\t%1.8e\n", feval, gapk, xm, xp) if Plotg == 2 xbot = xm - ( xp - xm ) / 20; xtop = xp + ( xp - xm ) / 20; if !isempty(plt.series_list) plt.series_list[end][:linealpha] = 0 end xx = range(xbot, xtop, resolution) yy = map(v -> v[1], f.(xx)) xlims!(plt, (xbot, xtop)) old_ylims = ylims(plt) ylims!(plt, (old_ylims[1], max(fxmp, fxpp))) plot!(plt, xx, yy) # old_xticks = xticks(plt[1]) new_xticks = ([xbot, xm, xmp, xpp, xp, xtop], [@sprintf("%1.1g", xbot), "x-", "x''-", "x''+", "x+", @sprintf("%1.1g", xtop)]) # keep_indices = findall(x -> all(x .≠ new_xticks[1]), old_xticks[1]) # merged_xticks = (old_xticks[1][keep_indices] ∪ new_xticks[1], # old_xticks[2][keep_indices] ∪ new_xticks[2]) # xticks!(merged_xticks) xticks!(plt, new_xticks) end # stopping criteria - - - - - - - - - - - - - - - - - - - - - - - - - - if feval > MaxFeval status = "stopped" break end # main logic- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if fxmp <= fxpp xp = xpp xpp = xmp xmp = xm + (1 - r) * (xp - xm) fxpp = fxmp fx = fxmp fxmp = f(xmp)[1] else xm = xmp xmp = xpp xpp = xm + r * (xp - xm) fxmp = fxpp fx = fxpp fxpp = f(xpp)[1] end feval += 1 if Plotg ≠ 0 && Interactive IJulia.clear_output(wait=true) display(plt) sleep(0.1) readline() end # iterate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - end # end of main loop- - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # select final answer if fxmp ≤ fxpp x = xmp else x = xpp end if Plotg == 1 plot!(plt, gap, linewidth=2) end if plotatend && Plotg ≠ 0 display(plt) end (x, status) end