diff --git a/src/moi_nlp_model.jl b/src/moi_nlp_model.jl index 190eab5..08fe501 100644 --- a/src/moi_nlp_model.jl +++ b/src/moi_nlp_model.jl @@ -7,6 +7,7 @@ mutable struct MathOptNLPModel <: AbstractNLPModel{Float64, Vector{Float64}} quadcon::QuadraticConstraints nlcon::NonLinearStructure λ::Vector{Float64} + hv::Vector{Float64} obj::Objective counters::Counters end @@ -34,7 +35,8 @@ function nlp_model(moimodel::MOI.ModelLike; hessian::Bool = true, name::String = nlp_data = _nlp_block(moimodel) nnln, nlcon, nl_lcon, nl_ucon = parser_NL(nlp_data, hessian = hessian) - λ = zeros(nnln) # Lagrange multipliers for hess_coord! and hprod! without y + λ = zeros(Float64, nnln) # Lagrange multipliers for hess_coord! and hprod! without y + hv = zeros(Float64, nvar) # workspace for ghjvprod! if nlp_data.has_objective obj = Objective("NONLINEAR", 0.0, spzeros(Float64, nvar), COO(), 0) @@ -67,7 +69,7 @@ function nlp_model(moimodel::MOI.ModelLike; hessian::Bool = true, name::String = name = name, ) - return MathOptNLPModel(meta, nlp_data.evaluator, lincon, quadcon, nlcon, λ, obj, Counters()), + return MathOptNLPModel(meta, nlp_data.evaluator, lincon, quadcon, nlcon, λ, hv, obj, Counters()), index_map end @@ -349,6 +351,38 @@ function NLPModels.hess_coord!( return vals end +function NLPModels.jth_hess_coord!( + nlp::MathOptNLPModel, + x::AbstractVector, + j::Integer, + vals::AbstractVector, +) + increment!(nlp, :neval_jhess) + @rangecheck 1 nlp.meta.ncon j + vals .= 0.0 + if nlp.meta.nlin + 1 ≤ j ≤ nlp.meta.nlin + nlp.quadcon.nquad + index = nlp.obj.nnzh + for i = 1:(nlp.quadcon.nquad) + qcon = nlp.quadcon.constraints[i] + if j == nlp.meta.nlin + i + view(vals, (index + 1):(index + qcon.nnzh)) .= qcon.A.vals + end + index += qcon.nnzh + end + elseif nlp.meta.nlin + nlp.quadcon.nquad + 1 ≤ j ≤ nlp.meta.ncon + nlp.λ[j - nlp.meta.nlin - nlp.quadcon.nquad] = 1.0 + MOI.eval_hessian_lagrangian( + nlp.eval, + view(vals, (nlp.obj.nnzh + nlp.quadcon.nnzh + 1):(nlp.meta.nnzh)), + x, + 0.0, + nlp.λ, + ) + nlp.λ[j - nlp.meta.nlin - nlp.quadcon.nquad] = 0.0 + end + return vals +end + function NLPModels.hprod!( nlp::MathOptNLPModel, x::AbstractVector, @@ -413,3 +447,45 @@ function NLPModels.hprod!( end return hv end + +function NLPModels.jth_hprod!( + nlp::MathOptNLPModel, + x::AbstractVector, + v::AbstractVector, + j::Integer, + hv::AbstractVector, +) + increment!(nlp, :neval_jhprod) + @rangecheck 1 nlp.meta.ncon j + hv .= 0.0 + if nlp.meta.nlin + 1 ≤ j ≤ nlp.meta.nlin + nlp.quadcon.nquad + qcon = nlp.quadcon.constraints[j - nlp.meta.nlin] + coo_sym_add_mul!(qcon.A.rows, qcon.A.cols, qcon.A.vals, v, hv, 1.0) + elseif nlp.meta.nlin + nlp.quadcon.nquad + 1 ≤ j ≤ nlp.meta.ncon + nlp.λ[j - nlp.meta.nlin - nlp.quadcon.nquad] = 1.0 + MOI.eval_hessian_lagrangian_product(nlp.eval, hv, x, v, 0.0, nlp.λ) + nlp.λ[j - nlp.meta.nlin - nlp.quadcon.nquad] = 0.0 + end + return hv +end + +function NLPModels.ghjvprod!( + nlp::MathOptNLPModel, + x::AbstractVector, + g::AbstractVector, + v::AbstractVector, + ghv::AbstractVector, +) + increment!(nlp, :neval_hprod) + ghv .= 0.0 + for i in nlp.meta.nlin + 1 : nlp.meta.nlin + nlp.quadcon.nquad + qcon = nlp.quadcon.constraints[i - nlp.meta.nlin] + ghv[i] = coo_sym_dot(qcon.A.rows, qcon.A.cols, qcon.A.vals, g, v) + end + for i in nlp.meta.nlin + nlp.quadcon.nquad + 1 : nlp.meta.ncon + jth_hprod!(nlp, x, v, i, nlp.hv) + decrement!(nlp, :neval_jhprod) + ghv[i] = dot(g, nlp.hv) + end + return ghv +end diff --git a/test/nlp_consistency.jl b/test/nlp_consistency.jl index a2f034f..9f57b8e 100644 --- a/test/nlp_consistency.jl +++ b/test/nlp_consistency.jl @@ -4,7 +4,7 @@ for problem in nlp_problems problem_f = eval(Symbol(lowercase(problem))) nlp_moi = MathOptNLPModel(problem_f()) nlps = [nlp_manual; nlp_moi] - consistent_nlps(nlps, linear_api = true, test_slack = false) + consistent_nlps(nlps, linear_api = true, test_slack = false, exclude = []) view_subarray_nlp(nlp_moi) end end