From 1532a5869312c3c9b42007bffce6b6bfc2a16b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 15 Aug 2026 14:03:51 +0000 Subject: [PATCH 1/4] Use MOI.Nonlinear.ModelWithQuad instead of QPBlockData Store the parsed quadratic constraints in a MOI.Nonlinear.ModelWithQuad and evaluate them through the corresponding MOI.Nonlinear.EvaluatorWithQuad, instead of using the QPBlockData and its private helpers directly. The per-constraint methods (jth_hess_coord!, jth_hprod! and ghjvprod!) evaluate the Hessian of the Lagrangian of the block with a basis multiplier vector, which removes the per-constraint Hessian offsets. The products of the evaluator overwrite their output, so the Jacobian-transpose and Hessian products go through a new hv workspace before being accumulated. --- src/moi_nlp_model.jl | 91 +++++++++++++++++--------------------------- src/moi_nls_model.jl | 54 +++++++++++--------------- src/utils.jl | 67 +++++++++++++++----------------- 3 files changed, 88 insertions(+), 124 deletions(-) diff --git a/src/moi_nlp_model.jl b/src/moi_nlp_model.jl index 0b3b929..6bfd41e 100644 --- a/src/moi_nlp_model.jl +++ b/src/moi_nlp_model.jl @@ -132,7 +132,7 @@ function NLPModels.cons_nln!(nlp::MathOptNLPModel, x::AbstractVector, c::Abstrac offset = 0 if nlp.quadcon.nquad > 0 offset += nlp.quadcon.nquad - MOI.eval_constraint(nlp.quadcon.block, view(c, 1:(nlp.quadcon.nquad)), x) + MOI.eval_constraint(nlp.quadcon.evaluator, view(c, 1:(nlp.quadcon.nquad)), x) end if nlp.nlcon.nnln > 0 offset += nlp.nlcon.nnln @@ -164,7 +164,7 @@ function NLPModels.cons!(nlp::MathOptNLPModel, x::AbstractVector, c::AbstractVec if nlp.quadcon.nquad > 0 offset += nlp.quadcon.nquad ind_quad = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - MOI.eval_constraint(nlp.quadcon.block, view(c, ind_quad), x) + MOI.eval_constraint(nlp.quadcon.evaluator, view(c, ind_quad), x) end if nlp.nlcon.nnln > 0 offset += nlp.nlcon.nnln @@ -290,7 +290,7 @@ function NLPModels.jac_nln_coord!(nlp::MathOptNLPModel, x::AbstractVector, vals: offset = 0 if nlp.quadcon.nquad > 0 ind_quad = 1:(nlp.quadcon.nnzj) - MOI.eval_constraint_jacobian(nlp.quadcon.block, view(vals, ind_quad), x) + MOI.eval_constraint_jacobian(nlp.quadcon.evaluator, view(vals, ind_quad), x) offset += nlp.quadcon.nnzj end if nlp.nlcon.nnln > 0 @@ -323,7 +323,7 @@ function NLPModels.jac_coord!(nlp::MathOptNLPModel, x::AbstractVector, vals::Abs end if nlp.quadcon.nquad > 0 ind_quad = (nlp.lincon.nnzj + 1):(nlp.lincon.nnzj + nlp.quadcon.nnzj) - MOI.eval_constraint_jacobian(nlp.quadcon.block, view(vals, ind_quad), x) + MOI.eval_constraint_jacobian(nlp.quadcon.evaluator, view(vals, ind_quad), x) offset += nlp.quadcon.nnzj end if nlp.nlcon.nnln > 0 @@ -373,8 +373,7 @@ function NLPModels.jprod_nln!( increment!(nlp, :neval_jprod_nln) if nlp.quadcon.nquad > 0 ind_quad = 1:(nlp.quadcon.nquad) - view(Jv, ind_quad) .= 0.0 - MOI.Nonlinear.add_constraint_jacobian_product(nlp.quadcon.block, view(Jv, ind_quad), x, v) + MOI.eval_constraint_jacobian_product(nlp.quadcon.evaluator, view(Jv, ind_quad), x, v) end if nlp.nlcon.nnln > 0 ind_nnln = (nlp.quadcon.nquad + 1):(nlp.quadcon.nquad + nlp.nlcon.nnln) @@ -382,7 +381,7 @@ function NLPModels.jprod_nln!( end if nlp.oracles.ncon > 0 for i = - (nlp.quadcon.nquad + nlp.nlcon.nnln + 1):(nlp.quadcon.nquad + nlp.nlcon.nnln + nlp.oracles.ncon) + (nlp.quadcon.nquad + nlp.nlcon.nnln + 1):(nlp.quadcon.nquad + nlp.nlcon.nnln + nlp.oracles.ncon) Jv[i] = 0 end @@ -427,8 +426,7 @@ function NLPModels.jprod!( end if nlp.quadcon.nquad > 0 ind_quad = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - view(Jv, ind_quad) .= 0.0 - MOI.Nonlinear.add_constraint_jacobian_product(nlp.quadcon.block, view(Jv, ind_quad), x, v) + MOI.eval_constraint_jacobian_product(nlp.quadcon.evaluator, view(Jv, ind_quad), x, v) end if nlp.nlcon.nnln > 0 ind_nnln = @@ -489,13 +487,16 @@ function NLPModels.jtprod_nln!( end (nlp.nlcon.nnln == 0) && (Jtv .= 0.0) if nlp.quadcon.nquad > 0 - # Jtv += Jᵀ * v[1:nquad], where J is the Jacobian of the quadratic block - MOI.Nonlinear.add_constraint_jacobian_transpose_product( - nlp.quadcon.block, - Jtv, + # Jtv += Jᵀ * v[1:nquad], where J is the Jacobian of the quadratic block. + # The product of the evaluator overwrites its output, so it goes through + # the workspace `nlp.hv`. + MOI.eval_constraint_jacobian_transpose_product( + nlp.quadcon.evaluator, + nlp.hv, x, view(v, 1:(nlp.quadcon.nquad)), ) + Jtv .+= nlp.hv end if nlp.oracles.ncon > 0 row_offset = nlp.quadcon.nquad + nlp.nlcon.nnln @@ -544,12 +545,13 @@ function NLPModels.jtprod!( end if nlp.quadcon.nquad > 0 ind_quad = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - MOI.Nonlinear.add_constraint_jacobian_transpose_product( - nlp.quadcon.block, - Jtv, + MOI.eval_constraint_jacobian_transpose_product( + nlp.quadcon.evaluator, + nlp.hv, x, view(v, ind_quad), ) + Jtv .+= nlp.hv end if nlp.oracles.ncon > 0 row_offset = nlp.meta.nlin + nlp.quadcon.nquad + nlp.nlcon.nnln @@ -637,13 +639,7 @@ function NLPModels.hess_coord!( if nlp.quadcon.nquad > 0 ind = (nlp.obj.nnzh + 1):(nlp.obj.nnzh + nlp.quadcon.nnzh) ind_quad = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - MOI.eval_hessian_lagrangian( - nlp.quadcon.block, - view(vals, ind), - x, - 0.0, - view(y, ind_quad), - ) + MOI.eval_hessian_lagrangian(nlp.quadcon.evaluator, view(vals, ind), x, 0.0, view(y, ind_quad)) end # 4. Oracle Hessian blocks are appended at the very end @@ -715,15 +711,11 @@ function NLPModels.jth_hess_coord!( # Quadratic constraints if nlp.meta.nlin + 1 ≤ j ≤ nlp.meta.nlin + nlp.quadcon.nquad - i = j - nlp.meta.nlin - index = nlp.obj.nnzh + nlp.quadcon.hess_offset[i] - nnzh_i = nlp.quadcon.hess_offset[i + 1] - nlp.quadcon.hess_offset[i] - MOI.Nonlinear._eval_sparse_hessian( - view(vals, (index + 1):(index + nnzh_i)), - nlp.quadcon.block.constraints[i], - 1.0, - nlp.quadcon.block.parameters, - ) + μ = nlp.quadcon.y_scratch + μ[j - nlp.meta.nlin] = 1.0 + ind = (nlp.obj.nnzh + 1):(nlp.obj.nnzh + nlp.quadcon.nnzh) + MOI.eval_hessian_lagrangian(nlp.quadcon.evaluator, view(vals, ind), x, 0.0, μ) + μ[j - nlp.meta.nlin] = 0.0 end # Non-oracle nonlinear constraints @@ -797,14 +789,8 @@ function NLPModels.hprod!( if nlp.quadcon.nquad > 0 (nlp.obj.type == "LINEAR") && (nlp.nlcon.nnln == 0) && (hv .= 0.0) ind_quad = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - MOI.Nonlinear.add_hessian_lagrangian_product( - nlp.quadcon.block, - hv, - x, - v, - 0.0, - view(y, ind_quad), - ) + MOI.eval_hessian_lagrangian_product(nlp.quadcon.evaluator, nlp.hv, x, v, 0.0, view(y, ind_quad)) + hv .+= nlp.hv end if nlp.oracles.ncon > 0 (nlp.obj.type == "LINEAR") && (nlp.meta.nnln == nlp.oracles.ncon) && (hv .= 0.0) @@ -874,14 +860,10 @@ function NLPModels.jth_hprod!( @rangecheck 1 nlp.meta.ncon j hv .= 0.0 if nlp.meta.nlin + 1 ≤ j ≤ nlp.meta.nlin + nlp.quadcon.nquad - MOI.Nonlinear._add_Hv_product( - nlp.quadcon.block.constraints[j - nlp.meta.nlin], - hv, - x, - v, - 1.0, - nlp.quadcon.block.parameters, - ) + μ = nlp.quadcon.y_scratch + μ[j - nlp.meta.nlin] = 1.0 + MOI.eval_hessian_lagrangian_product(nlp.quadcon.evaluator, hv, x, v, 0.0, μ) + μ[j - nlp.meta.nlin] = 0.0 elseif nlp.meta.nlin + nlp.quadcon.nquad + 1 ≤ j ≤ nlp.meta.nlin + nlp.quadcon.nquad + nlp.nlcon.nnln @@ -933,16 +915,11 @@ function NLPModels.ghjvprod!( error("The function ghjvprod! is not supported by this MathOptNLPModel.") increment!(nlp, :neval_hprod) ghv .= 0.0 + μ = nlp.quadcon.y_scratch for i = (nlp.meta.nlin + 1):(nlp.meta.nlin + nlp.quadcon.nquad) - fill!(nlp.hv, 0.0) - MOI.Nonlinear._add_Hv_product( - nlp.quadcon.block.constraints[i - nlp.meta.nlin], - nlp.hv, - x, - v, - 1.0, - nlp.quadcon.block.parameters, - ) + μ[i - nlp.meta.nlin] = 1.0 + MOI.eval_hessian_lagrangian_product(nlp.quadcon.evaluator, nlp.hv, x, v, 0.0, μ) + μ[i - nlp.meta.nlin] = 0.0 ghv[i] = dot(g, nlp.hv) end for i = (nlp.meta.nlin + nlp.quadcon.nquad + 1):(nlp.meta.ncon) diff --git a/src/moi_nls_model.jl b/src/moi_nls_model.jl index 25892eb..7b3cef0 100644 --- a/src/moi_nls_model.jl +++ b/src/moi_nls_model.jl @@ -13,6 +13,7 @@ mutable struct MathOptNLSModel <: AbstractNLSModel{Float64, Vector{Float64}} nlcon::NonLinearStructure oracles::Oracles λ::Vector{Float64} + hv::Vector{Float64} counters::NLSCounters end @@ -88,6 +89,7 @@ function MathOptNLSModel(cmodel::JuMP.Model, F; hessian::Bool = true, name::Stri nlcon, oracles, λ, + zeros(nvar), nls_counters, ) end @@ -270,7 +272,7 @@ function NLPModels.cons_nln!(nls::MathOptNLSModel, x::AbstractVector, c::Abstrac offset = 0 if nls.quadcon.nquad > 0 offset += nls.quadcon.nquad - MOI.eval_constraint(nls.quadcon.block, view(c, 1:(nls.quadcon.nquad)), x) + MOI.eval_constraint(nls.quadcon.evaluator, view(c, 1:(nls.quadcon.nquad)), x) end if nls.nlcon.nnln > 0 offset += nls.nlcon.nnln @@ -301,7 +303,7 @@ function NLPModels.cons!(nls::MathOptNLSModel, x::AbstractVector, c::AbstractVec if nls.quadcon.nquad > 0 offset += nls.quadcon.nquad ind_quad = (nls.meta.nlin + 1):(nls.meta.nlin + nls.quadcon.nquad) - MOI.eval_constraint(nls.quadcon.block, view(c, ind_quad), x) + MOI.eval_constraint(nls.quadcon.evaluator, view(c, ind_quad), x) end if nls.nlcon.nnln > 0 offset += nls.nlcon.nnln @@ -420,7 +422,7 @@ function NLPModels.jac_nln_coord!(nls::MathOptNLSModel, x::AbstractVector, vals: offset = 0 if nls.quadcon.nquad > 0 ind_quad = 1:(nls.quadcon.nnzj) - MOI.eval_constraint_jacobian(nls.quadcon.block, view(vals, ind_quad), x) + MOI.eval_constraint_jacobian(nls.quadcon.evaluator, view(vals, ind_quad), x) offset += nls.quadcon.nnzj end if nls.nlcon.nnln > 0 @@ -452,7 +454,7 @@ function NLPModels.jac_coord!(nls::MathOptNLSModel, x::AbstractVector, vals::Abs end if nls.quadcon.nquad > 0 ind_quad = (nls.lincon.nnzj + 1):(nls.lincon.nnzj + nls.quadcon.nnzj) - MOI.eval_constraint_jacobian(nls.quadcon.block, view(vals, ind_quad), x) + MOI.eval_constraint_jacobian(nls.quadcon.evaluator, view(vals, ind_quad), x) offset += nls.quadcon.nnzj end if nls.nlcon.nnln > 0 @@ -501,8 +503,7 @@ function NLPModels.jprod_nln!( increment!(nls, :neval_jprod_nln) if nls.quadcon.nquad > 0 ind_quad = 1:(nls.quadcon.nquad) - view(Jv, ind_quad) .= 0.0 - MOI.Nonlinear.add_constraint_jacobian_product(nls.quadcon.block, view(Jv, ind_quad), x, v) + MOI.eval_constraint_jacobian_product(nls.quadcon.evaluator, view(Jv, ind_quad), x, v) end if nls.nlcon.nnln > 0 ind_nnln = (nls.quadcon.nquad + 1):(nls.quadcon.nquad + nls.nlcon.nnln) @@ -510,7 +511,7 @@ function NLPModels.jprod_nln!( end if nls.oracles.ncon > 0 for i = - (nls.quadcon.nquad + nls.nlcon.nnln + 1):(nls.quadcon.nquad + nls.nlcon.nnln + nls.oracles.ncon) + (nls.quadcon.nquad + nls.nlcon.nnln + 1):(nls.quadcon.nquad + nls.nlcon.nnln + nls.oracles.ncon) Jv[i] = 0 end @@ -555,8 +556,7 @@ function NLPModels.jprod!( end if nls.quadcon.nquad > 0 ind_quad = (nls.meta.nlin + 1):(nls.meta.nlin + nls.quadcon.nquad) - view(Jv, ind_quad) .= 0.0 - MOI.Nonlinear.add_constraint_jacobian_product(nls.quadcon.block, view(Jv, ind_quad), x, v) + MOI.eval_constraint_jacobian_product(nls.quadcon.evaluator, view(Jv, ind_quad), x, v) end if nls.nlcon.nnln > 0 ind_nnln = @@ -617,13 +617,16 @@ function NLPModels.jtprod_nln!( end (nls.nlcon.nnln == 0) && (Jtv .= 0.0) if nls.quadcon.nquad > 0 - # Jtv += Jᵀ * v[1:nquad], where J is the Jacobian of the quadratic block - MOI.Nonlinear.add_constraint_jacobian_transpose_product( - nls.quadcon.block, - Jtv, + # Jtv += Jᵀ * v[1:nquad], where J is the Jacobian of the quadratic block. + # The product of the evaluator overwrites its output, so it goes through + # the workspace `nls.hv`. + MOI.eval_constraint_jacobian_transpose_product( + nls.quadcon.evaluator, + nls.hv, x, view(v, 1:(nls.quadcon.nquad)), ) + Jtv .+= nls.hv end if nls.oracles.ncon > 0 row_offset = nls.quadcon.nquad + nls.nlcon.nnln @@ -672,12 +675,13 @@ function NLPModels.jtprod!( end if nls.quadcon.nquad > 0 ind_quad = (nls.meta.nlin + 1):(nls.meta.nlin + nls.quadcon.nquad) - MOI.Nonlinear.add_constraint_jacobian_transpose_product( - nls.quadcon.block, - Jtv, + MOI.eval_constraint_jacobian_transpose_product( + nls.quadcon.evaluator, + nls.hv, x, view(v, ind_quad), ) + Jtv .+= nls.hv end if nls.oracles.ncon > 0 row_offset = nls.meta.nlin + nls.quadcon.nquad + nls.nlcon.nnln @@ -752,13 +756,7 @@ function NLPModels.hess_coord!( # irrelevant. ind = (nls.lls.nnzh + 1):(nls.lls.nnzh + nls.quadcon.nnzh) ind_quad = (nls.meta.nlin + 1):(nls.meta.nlin + nls.quadcon.nquad) - MOI.eval_hessian_lagrangian( - nls.quadcon.block, - view(vals, ind), - x, - 0.0, - view(y, ind_quad), - ) + MOI.eval_hessian_lagrangian(nls.quadcon.evaluator, view(vals, ind), x, 0.0, view(y, ind_quad)) end if (nls.nls_meta.nnln > 0) || (nls.nlcon.nnln > 0) λ = view( @@ -855,14 +853,8 @@ function NLPModels.hprod!( end if nls.quadcon.nquad > 0 ind_quad = (nls.meta.nlin + 1):(nls.meta.nlin + nls.quadcon.nquad) - MOI.Nonlinear.add_hessian_lagrangian_product( - nls.quadcon.block, - hv, - x, - v, - 0.0, - view(y, ind_quad), - ) + MOI.eval_hessian_lagrangian_product(nls.quadcon.evaluator, nls.hv, x, v, 0.0, view(y, ind_quad)) + hv .+= nls.hv end if nls.oracles.ncon > 0 offset_y = nls.meta.nlin + nls.quadcon.nquad + nls.nlcon.nnln diff --git a/src/utils.jl b/src/utils.jl index edf9447..29f813f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -77,52 +77,50 @@ end """ QuadraticConstraints -The quadratic constraints, stored in a `MOI.Nonlinear.QPBlockData` whose rows -are the constraints in the order they were parsed. The Jacobian and Hessian -structures of the block are precomputed; `hess_offset[i]` is the number of -Hessian entries before those of constraint `i`, so that the entries of -constraint `i` are `hess_offset[i]+1:hess_offset[i+1]`. +The quadratic constraints, stored in a `MOI.Nonlinear.ModelWithQuad` whose +rows are the constraints in the order they were parsed, and evaluated through +the corresponding `MOI.Nonlinear.EvaluatorWithQuad`. The Jacobian and Hessian +structures of the block are precomputed. `y_scratch` holds basis multiplier +vectors for the per-constraint methods (`jth_hess_coord!`, `jth_hprod!` and +`ghjvprod!`). """ mutable struct QuadraticConstraints nquad::Int - block::MOI.Nonlinear.QPBlockData{Float64} + evaluator::MOI.Nonlinear.EvaluatorWithQuad jac_rows::Vector{Int} jac_cols::Vector{Int} nnzj::Int hess_rows::Vector{Int} hess_cols::Vector{Int} - hess_offset::Vector{Int} nnzh::Int + y_scratch::Vector{Float64} end -function QuadraticConstraints(block::MOI.Nonlinear.QPBlockData{Float64}) - nquad = length(block) - jac_structure = MOI.jacobian_structure(block) +function QuadraticConstraints(quad_model::MOI.Nonlinear.ModelWithQuad, nvar::Int) + nquad = length(quad_model) + evaluator = MOI.Nonlinear.Evaluator( + quad_model, + MOI.Nonlinear.SparseReverseMode(), + MOI.VariableIndex.(1:nvar), + ) + MOI.initialize(evaluator, [:Grad, :Jac, :JacVec, :Hess, :HessVec]) + jac_structure = MOI.jacobian_structure(evaluator) jac_rows = [r for (r, _) in jac_structure] jac_cols = [c for (_, c) in jac_structure] - hess_structure = Tuple{Int, Int}[] - hess_offset = zeros(Int, nquad + 1) - for i = 1:nquad - MOI.Nonlinear._append_sparse_hessian_structure!( - block.constraints[i], - hess_structure, - block.parameters, - ) - hess_offset[i + 1] = length(hess_structure) - end + hess_structure = MOI.hessian_lagrangian_structure(evaluator) # NLPModels expects the lower triangle hess_rows = [max(r, c) for (r, c) in hess_structure] hess_cols = [min(r, c) for (r, c) in hess_structure] return QuadraticConstraints( nquad, - block, + evaluator, jac_rows, jac_cols, length(jac_structure), hess_rows, hess_cols, - hess_offset, length(hess_structure), + zeros(nquad), ) end @@ -354,12 +352,12 @@ end Parse a `ScalarQuadraticFunction` fun with its associated set. `qcons`, `quad_lcon`, `quad_ucon` are updated. """ -function parser_SQF(fun, set, block, index_map) +function parser_SQF(fun, set, quad_model, index_map) f = MOI.Utilities.map_indices(index_map, fun) # The constant is moved into the set, as MOI requires for # scalar-function-in-set constraints. g = SQF(f.quadratic_terms, f.affine_terms, 0.0) - MOI.add_constraint(block, g, MOI.Utilities.shift_constant(set, -f.constant)) + MOI.add_constraint(quad_model, g, MOI.Utilities.shift_constant(set, -f.constant)) return end @@ -367,15 +365,11 @@ _scalar_set(::MOI.Nonnegatives) = MOI.GreaterThan(0.0) _scalar_set(::MOI.Nonpositives) = MOI.LessThan(0.0) _scalar_set(::MOI.Zeros) = MOI.EqualTo(0.0) -function parser_VQF(fun, set, block, index_map) +function parser_VQF(fun, set, quad_model, index_map) f = MOI.Utilities.map_indices(index_map, fun) for fi in MOI.Utilities.scalarize(f) g = SQF(fi.quadratic_terms, fi.affine_terms, 0.0) - MOI.add_constraint( - block, - g, - MOI.Utilities.shift_constant(_scalar_set(set), -fi.constant), - ) + MOI.add_constraint(quad_model, g, MOI.Utilities.shift_constant(_scalar_set(set), -fi.constant)) end return end @@ -397,7 +391,7 @@ function parser_MOI(moimodel, index_map, nvar) # Variables associated to quadratic constraints nquad = 0 - quad_block = MOI.Nonlinear.QPBlockData{Float64}() + quad_model = MOI.Nonlinear.ModelWithQuad(MOI.Nonlinear.Model()) contypes = MOI.get(moimodel, MOI.ListOfConstraintTypesPresent()) for (F, S) in contypes @@ -430,11 +424,11 @@ function parser_MOI(moimodel, index_map, nvar) nlin += set.dimension end if typeof(fun) <: SQF - parser_SQF(fun, set, quad_block, index_map) + parser_SQF(fun, set, quad_model, index_map) nquad += 1 end if typeof(fun) <: VQF - parser_VQF(fun, set, quad_block, index_map) + parser_VQF(fun, set, quad_model, index_map) nquad += set.dimension end end @@ -442,9 +436,10 @@ function parser_MOI(moimodel, index_map, nvar) coo = COO(linrows, lincols, linvals) lin_nnzj = length(linvals) lincon = LinearConstraints(coo, lin_nnzj) - quadcon = QuadraticConstraints(quad_block) - quad_lcon = copy(quad_block.g_L) - quad_ucon = copy(quad_block.g_U) + quadcon = QuadraticConstraints(quad_model, nvar) + quad_bounds = MOI.NLPBlockData(quadcon.evaluator).constraint_bounds + quad_lcon = [b.lower for b in quad_bounds] + quad_ucon = [b.upper for b in quad_bounds] return nlin, lincon, lin_lcon, lin_ucon, quadcon, quad_lcon, quad_ucon end From 5fc47b12edb3dd2d2857e1051918c03c7361e62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 15 Aug 2026 14:21:27 +0000 Subject: [PATCH 2/4] MOI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 630355b..46612c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MathOptInterface", rev="bl/qp_block_data"), + PackageSpec(name="MathOptInterface", rev="bl/model_with_quad"), ]) - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 From 0b01aeb7457d0f336eda7642d01190b0f36404ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 15 Aug 2026 16:34:17 +0000 Subject: [PATCH 3/4] Add the variables to the ModelWithQuad MOI.Nonlinear.ModelWithQuad now owns the variables of the model, so the parser adds them and the evaluator no longer takes ordered_variables. --- src/utils.jl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 29f813f..6862654 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -96,13 +96,9 @@ mutable struct QuadraticConstraints y_scratch::Vector{Float64} end -function QuadraticConstraints(quad_model::MOI.Nonlinear.ModelWithQuad, nvar::Int) +function QuadraticConstraints(quad_model::MOI.Nonlinear.ModelWithQuad) nquad = length(quad_model) - evaluator = MOI.Nonlinear.Evaluator( - quad_model, - MOI.Nonlinear.SparseReverseMode(), - MOI.VariableIndex.(1:nvar), - ) + evaluator = MOI.Nonlinear.Evaluator(quad_model, MOI.Nonlinear.SparseReverseMode()) MOI.initialize(evaluator, [:Grad, :Jac, :JacVec, :Hess, :HessVec]) jac_structure = MOI.jacobian_structure(evaluator) jac_rows = [r for (r, _) in jac_structure] @@ -392,6 +388,9 @@ function parser_MOI(moimodel, index_map, nvar) # Variables associated to quadratic constraints nquad = 0 quad_model = MOI.Nonlinear.ModelWithQuad(MOI.Nonlinear.Model()) + for _ = 1:nvar + MOI.add_variable(quad_model) + end contypes = MOI.get(moimodel, MOI.ListOfConstraintTypesPresent()) for (F, S) in contypes @@ -436,7 +435,7 @@ function parser_MOI(moimodel, index_map, nvar) coo = COO(linrows, lincols, linvals) lin_nnzj = length(linvals) lincon = LinearConstraints(coo, lin_nnzj) - quadcon = QuadraticConstraints(quad_model, nvar) + quadcon = QuadraticConstraints(quad_model) quad_bounds = MOI.NLPBlockData(quadcon.evaluator).constraint_bounds quad_lcon = [b.lower for b in quad_bounds] quad_ucon = [b.upper for b in quad_bounds] From cb6ea9b4fc212f44433548ea8860a930855108ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Aug 2026 08:39:44 +0200 Subject: [PATCH 4/4] MOI --- .github/workflows/Aqua.yml | 7 +++++++ .github/workflows/Documentation.yml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/Aqua.yml b/.github/workflows/Aqua.yml index 73e12cb..0bb7b42 100644 --- a/.github/workflows/Aqua.yml +++ b/.github/workflows/Aqua.yml @@ -13,6 +13,13 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: '1' + - name: MOI + shell: julia --project=@. {0} + run: | + using Pkg + Pkg.add([ + PackageSpec(name="MathOptInterface", rev="bl/model_with_quad"), + ]) - name: Aqua.jl run: | PKG_SRC_PATH=`pwd` diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index be0b865..2d1faff 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -14,6 +14,13 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: '1' + - name: MOI + shell: julia --project=@. {0} + run: | + using Pkg + Pkg.add([ + PackageSpec(name="MathOptInterface", rev="bl/model_with_quad"), + ]) - name: Install dependencies run: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - name: Build and deploy