Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pass additional function arguments to linesearch function #170

Closed
jacob-roth opened this issue Feb 12, 2023 · 1 comment
Closed

pass additional function arguments to linesearch function #170

jacob-roth opened this issue Feb 12, 2023 · 1 comment

Comments

@jacob-roth
Copy link

jacob-roth commented Feb 12, 2023

I have an application where I would like to pass additional function arguments args to my calls to phi and dphi. I have modified the example code to do this one way, but it requires creating an anonymous function a -> phi(a,args) within the main iteration. Is there a way to pass the additional args directly? Creating the anonymous function each time allocates, and I'd like to avoid this.

using LinearAlgebra: norm, dot

mutable struct myargs
  v :: Float64
  i :: Int64
end

function gdoptimize(f, g!, fg!, x0::AbstractArray{T}, linesearch,
                    maxiter::Int = 10000,
                    g_rtol::T = sqrt(eps(T)), g_atol::T = eps(T)) where T <: Number
    x = copy(x0)
    gvec = similar(x)
    args = myargs(0.0, 0)
    g!(gvec, x, args)
    fx = f(x, args)

    gnorm = norm(gvec)
    gtol = max(g_rtol*gnorm, g_atol)

    # Univariate line search functions
    ϕ(α, args) = f(x .+ α.*s, args)
    function dϕ(α, args)
        g!(gvec, x .+ α.*s, args)
        return dot(gvec, s)
    end
    function ϕdϕ(α, args)
        phi = fg!(gvec, x .+ α.*s, args)
        dphi = dot(gvec, s)
        return (phi, dphi)
    end

    s = similar(gvec) # Step direction

    iter = 0
    while iter < maxiter && gnorm > gtol
        iter += 1
        args.i = iter
        s .= -gvec

        dϕ_0 = dot(s, gvec)
        α, fx = linesearch(a -> ϕ(a, args), a -> dϕ(a, args), a -> ϕdϕ(a, args), 1.0, fx, dϕ_0)
        
        @. x = x + α*s
        g!(gvec, x, args)
        gnorm = norm(gvec)
    end

    return (fx, x, iter)
end

f(x, args) = begin
  println("f: args.i = $(args.i)")
  (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2 + args.v
end

function g!(gvec, x, args)
    println("g!: args.i = $(args.i)")
    gvec[1] = -2.0 * (1.0 - x[1]) - 400.0 * (x[2] - x[1]^2) * x[1]
    gvec[2] = 200.0 * (x[2] - x[1]^2)
    gvec
end

function fg!(gvec, x, args)
    println("fg!: args.i = $(args.i)")
    g!(gvec, x, args)
    f(x, args)
end

x0 = [-1., 1.0]

using LineSearches
ls = BackTracking(order=3)
fx_bt3, x_bt3, iter_bt3 = gdoptimize(f, g!, fg!, x0, ls)

ls = StrongWolfe()
fx_sw, x_sw, iter_sw = gdoptimize(f, g!, fg!, x0, ls)
@jacob-roth
Copy link
Author

A good route seems to be a callable struct! (https://discourse.julialang.org/t/avoid-allocating-anonymous-functions/94689)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant