diff --git a/src/aggregations.jl b/src/aggregations.jl index 9479fb7..6380e55 100755 --- a/src/aggregations.jl +++ b/src/aggregations.jl @@ -4,7 +4,7 @@ getdim(::AbstractAggregation) = 1 @params struct NonNegSumOfSquares <: AbstractAggregation c::Real end -(f::NonNegSumOfSquares)(x::AbstractVector) = f.c * sum(max.(0, x).^2) +(f::NonNegSumOfSquares)(x::AbstractVector) = f.c * sum(max.(0, x) .^ 2) @params struct NonNegSumOfRoots <: AbstractAggregation c::Real diff --git a/src/algorithm.jl b/src/algorithm.jl index 11891fb..4e90fdd 100644 --- a/src/algorithm.jl +++ b/src/algorithm.jl @@ -1,24 +1,31 @@ @params struct AugLag2 <: AbstractOptimizer - primaloptimizer - dualoptimizer + primaloptimizer::Any + dualoptimizer::Any end function AugLag2(; - primaloptimizer = Optim.ConjugateGradient(linesearch=Optim.LineSearches.BackTracking(iterations = 10)), - dualoptimizer = Optim.GradientDescent(linesearch=Optim.LineSearches.BackTracking(iterations = 10)), + primaloptimizer = Optim.ConjugateGradient( + linesearch = Optim.LineSearches.BackTracking(iterations = 10), + ), + dualoptimizer = Optim.GradientDescent( + linesearch = Optim.LineSearches.BackTracking(iterations = 10), + ), ) return AugLag2(primaloptimizer, dualoptimizer) end @params struct AugLag2Options - primaloptions - dualoptions - maxiter - tol - quadfactor + primaloptions::Any + dualoptions::Any + maxiter::Any + tol::Any + quadfactor::Any end -function AugLag2Options(alg::AugLag2; - primaloptions = alg.primaloptimizer isa MMA02 || alg.primaloptimizer isa MMA87 ? MMAOptions(maxiter = 100, tol = Tolerance(kkt = 1e-4)) : Optim.Options(outer_iterations = 10, iterations = 10), +function AugLag2Options( + alg::AugLag2; + primaloptions = alg.primaloptimizer isa MMA02 || alg.primaloptimizer isa MMA87 ? + MMAOptions(maxiter = 100, tol = Tolerance(kkt = 1e-4)) : + Optim.Options(outer_iterations = 10, iterations = 10), dualoptions = Optim.Options(outer_iterations = 10, iterations = 10), maxiter = 10, tol = Tolerance(), @@ -35,7 +42,17 @@ function Solution(lagmodel::AugLag2Model) λ = copy(getlinweights(lagmodel)) g = copy(λ) convstate = ConvergenceState() - return Solution(prevx, x, getlinweights(lagmodel), prevf, f, nothing, g, nothing, convstate) + return Solution( + prevx, + x, + getlinweights(lagmodel), + prevf, + f, + nothing, + g, + nothing, + convstate, + ) end @params mutable struct AugLag2Workspace <: Workspace @@ -52,7 +69,7 @@ end trace::Trace outer_iter::Int iter::Int - fcalls::Int + fcalls::Int end function AugLag2Workspace( model::VecModel, @@ -63,7 +80,11 @@ function AugLag2Workspace( plot_trace::Bool = false, show_plot::Bool = plot_trace, save_plot = nothing, - callback::Function = plot_trace ? LazyPlottingCallback(; show_plot = show_plot, save_plot = save_plot) : NoCallback(), + callback::Function = plot_trace ? + LazyPlottingCallback(; + show_plot = show_plot, + save_plot = save_plot, + ) : NoCallback(), kwargs..., ) T = eltype(x0) @@ -85,11 +106,9 @@ function AugLag2Workspace( x0, optimizer, options, - solution, convcriteria, callback, - trace, outer_iter, iter, @@ -97,7 +116,8 @@ function AugLag2Workspace( ) end -Workspace(model::VecModel, alg::AugLag2, x0::AbstractVector; kwargs...) = AugLag2Workspace(model, alg, x0; kwargs...) +Workspace(model::VecModel, alg::AugLag2, x0::AbstractVector; kwargs...) = + AugLag2Workspace(model, alg, x0; kwargs...) function optimize!(workspace::AugLag2Workspace) @unpack lagmodel, solution, options, convcriteria = workspace @@ -117,18 +137,19 @@ function optimize!(workspace::AugLag2Workspace) auglag = getobjective(lagmodel) - cb = (tr; kwargs...) -> begin - solution = deepcopy(solution) - solution.prevx .= solution.x - solution.x .= getx(lagmodel) - solution.λ .= getλ(lagmodel) - solution.prevf = solution.f - solution.f = getorigobjval(lagmodel) - solution.g .= getorigconstrval(lagmodel) - assess_convergence!(solution, lagmodel, options.tol, convcriteria) - callback(solution) - return hasconverged(solution) - end + cb = + (tr; kwargs...) -> begin + solution = deepcopy(solution) + solution.prevx .= solution.x + solution.x .= getx(lagmodel) + solution.λ .= getλ(lagmodel) + solution.prevf = solution.f + solution.f = getorigobjval(lagmodel) + solution.g .= getorigconstrval(lagmodel) + assess_convergence!(solution, lagmodel, options.tol, convcriteria) + callback(solution) + return hasconverged(solution) + end primaloptimizerfunction(λ) = begin setlinweights!(auglag, λ) @@ -139,7 +160,14 @@ function optimize!(workspace::AugLag2Workspace) primalmodel = Model() addvar!(primalmodel, xl, xu) set_objective!(primalmodel, getprimalobjective(auglag)) - result = optimize(primalmodel, primaloptimizer, clamp.(getx(lagmodel), xl .+ 1e-3, xu .- 1e-3), options = primaloptions, callback = cb, convcriteria = KKTCriteria()) + result = optimize( + primalmodel, + primaloptimizer, + clamp.(getx(lagmodel), xl .+ 1e-3, xu .- 1e-3), + options = primaloptions, + callback = cb, + convcriteria = KKTCriteria(), + ) fcalls += result.fcalls else primalobj = getoptimobj(getprimalobjective(auglag), true) @@ -173,7 +201,7 @@ function optimize!(workspace::AugLag2Workspace) λu = fill(Inf, ni) # Solve the dual problem by minimizing negative the dual objective value - for i in 1:maxiter + for i = 1:maxiter setquadweight!(lagmodel, min(getquadweight(lagmodel) * quadfactor, 1e10)) if debugging[] @show getquadweight(lagmodel) diff --git a/src/model.jl b/src/model.jl index 8085b7e..74d9413 100755 --- a/src/model.jl +++ b/src/model.jl @@ -55,7 +55,8 @@ function (obj::AugLag2Obj)(x::AbstractVector, λ::AbstractVector; penalise = tru savefg!(obj, origobjval, origconstrval) linepenalty = dot(λ, origconstrval) - quadpenalty = penalise ? NonNegSumOfSquares(obj.quadweight[])(origconstrval) : zero(linepenalty) + quadpenalty = + penalise ? NonNegSumOfSquares(obj.quadweight[])(origconstrval) : zero(linepenalty) return origobjval + linepenalty + quadpenalty end @@ -75,7 +76,8 @@ function (obj::AugLag2Obj)(primaloptimizer::Function, λ::AbstractVector; penali savefg!(obj, origobjval, origconstrval) linepenalty = dot(λ, origconstrval) - quadpenalty = penalise ? NonNegSumOfSquares(obj.quadweight[])(origconstrval) : zero(linepenalty) + quadpenalty = + penalise ? NonNegSumOfSquares(obj.quadweight[])(origconstrval) : zero(linepenalty) return origobjval + linepenalty + quadpenalty end @@ -95,7 +97,11 @@ function savexλ!(obj::AugLag2Obj, x::AbstractVector, λ::AbstractVector) setλ!(obj, λ) return obj end -ChainRulesCore.@non_differentiable savexλ!(obj::AugLag2Obj, x::AbstractVector, λ::AbstractVector) +ChainRulesCore.@non_differentiable savexλ!( + obj::AugLag2Obj, + x::AbstractVector, + λ::AbstractVector, +) getparent(f::AugLag2Obj) = f.model @@ -159,11 +165,8 @@ end parent::VecModel objective::AugLag2Obj end -function AugLag2Model( - model::VecModel; - kwargs..., -) - return AugLag2Model(model, AugLag2Obj(model; kwargs...,)) +function AugLag2Model(model::VecModel; kwargs...) + return AugLag2Model(model, AugLag2Obj(model; kwargs...)) end getparent(model::AugLag2Model) = model.parent @@ -174,11 +177,14 @@ getmax(model::AugLag2Model) = getmax(getparent(model)) getobjective(model::AugLag2Model) = model.objective -getineqconstraints(::AugLag2Model) = throw("`getineqconstraints` is not defined for `AugLag2Model`.") +getineqconstraints(::AugLag2Model) = + throw("`getineqconstraints` is not defined for `AugLag2Model`.") -geteqconstraints(::AugLag2Model) = throw("`geteqconstraints` is not defined for `AugLag2Model`.") +geteqconstraints(::AugLag2Model) = + throw("`geteqconstraints` is not defined for `AugLag2Model`.") -getobjectiveconstraints(::AugLag2Model) = throw("`getobjectiveconstraints` is not defined for `AugLag2Model`.") +getobjectiveconstraints(::AugLag2Model) = + throw("`getobjectiveconstraints` is not defined for `AugLag2Model`.") getlinweights(model::AugLag2Model) = getlinweights(getobjective(model)) @@ -224,7 +230,7 @@ end function getresiduals(solution::Solution, model::AugLag2Model, ::GenericCriteria) @unpack prevx, x, prevf, f, g = solution - Δx = maximum(abs(x[j] - prevx[j]) for j in 1:length(x)) + Δx = maximum(abs(x[j] - prevx[j]) for j = 1:length(x)) Δf = abs(f - prevf) infeas = max(0, maximum(g)) return Δx, Δf, infeas diff --git a/test/runtests.jl b/test/runtests.jl index fd9c026..eea5d02 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,19 +2,16 @@ using NonconvexAugLagLab using Test, LinearAlgebra f(x::AbstractVector) = sqrt(x[2]) -g(x::AbstractVector, a, b) = (a*x[1] + b)^3 - x[2] +g(x::AbstractVector, a, b) = (a * x[1] + b)^3 - x[2] m = Model(f) addvar!(m, [1e-4, 1e-4], [10.0, 10.0]) -add_ineq_constraint!( - m, - FunctionWrapper(x -> [g(x, 2, 0), g(x, -1, 1)], 2), -) +add_ineq_constraint!(m, FunctionWrapper(x -> [g(x, 2, 0), g(x, -1, 1)], 2)) x0 = [2.0, 2.0] #alg = AugLag2(primaloptimizer = MMA87()) alg = AugLag2() options = AugLag2Options(alg) r = optimize(m, alg, x0, options = options) -@test abs(r.minimum - sqrt(8/27)) < 1e-4 -@test norm(r.minimizer - [1/3, 8/27]) < 1e-4 +@test abs(r.minimum - sqrt(8 / 27)) < 1e-4 +@test norm(r.minimizer - [1 / 3, 8 / 27]) < 1e-4