Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ jobs:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v3
- name: MOI
shell: julia --project=@. {0}
run: |
using Pkg
Pkg.add([
PackageSpec(name="MathOptInterface", rev="bl/qp_block_data"),
])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sources]
MathOptInterface = {url = "https://github.com/jump-dev/MathOptInterface.jl", rev = "bl/qp_block_data"}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't work on Julia v1.10

- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
Expand Down
31 changes: 25 additions & 6 deletions ext/IpoptMathOptInterfaceExt/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

include("utils.jl")
const QPBlockData = MOI.Nonlinear.QPBlockData

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strongly prefer that we use explicit prefixes instead of these extra constants. It's only needed in a few places, and using the explicit prefix makes the code much easier to read.



const _PARAMETER_OFFSET = 0x00f0000000000000

Expand Down Expand Up @@ -53,6 +54,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
nlp_dual_start::Union{Nothing,Vector{Float64}}
mult_g_nlp::Dict{MOI.Nonlinear.ConstraintIndex,Float64}
qp_data::QPBlockData{Float64}
# The number of entries of the Jacobian and of the Hessian of the
# Lagrangian of `qp_data`, computed in `_setup_model`.
qp_nnzj::Int
qp_nnzh::Int
nlp_model::Union{Nothing,MOI.Nonlinear.Model}
callback::Union{Nothing,Function}
barrier_iterations::Int
Expand Down Expand Up @@ -84,6 +89,8 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
nothing,
Dict{MOI.Nonlinear.ConstraintIndex,Float64}(),
QPBlockData{Float64}(),
0,
0,
nothing,
nothing,
0,
Expand Down Expand Up @@ -136,6 +143,8 @@ function MOI.empty!(model::Optimizer)
model.nlp_dual_start = nothing
empty!(model.mult_g_nlp)
model.qp_data = QPBlockData{Float64}()
model.qp_nnzj = 0
model.qp_nnzh = 0
model.nlp_model = nothing
model.callback = nothing
model.barrier_iterations = 0
Expand Down Expand Up @@ -193,6 +202,11 @@ function MOI.add_constrained_variable(
push!(model.list_of_variable_indices, p)
model.parameters[p] =
MOI.Nonlinear.add_parameter(model.nlp_model, set.value)
# `QPBlockData` treats a variable as a parameter if and only if its index
# is a key of `parameters`, so the parameter must be registered before
# any structure query. The value is re-synced in `copy_parameters` before
# every solve.
model.qp_data.parameters[p.value] = set.value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we improve this API somehow?

ci = MOI.ConstraintIndex{MOI.VariableIndex,typeof(set)}(p.value)
return p, ci
end
Expand Down Expand Up @@ -1181,8 +1195,8 @@ function _eval_constraint_jacobian(
end

function MOI.eval_constraint_jacobian(model::Optimizer, values, x)
offset = MOI.eval_constraint_jacobian(model.qp_data, values, x)
offset -= 1 # .qp_data returns one-indexed offset
MOI.eval_constraint_jacobian(model.qp_data, values, x)
offset = model.qp_nnzj
for (f, s) in model.vector_nonlinear_oracle_constraints
offset = _eval_constraint_jacobian(values, offset, x, f, s)
end
Expand Down Expand Up @@ -1234,8 +1248,8 @@ function _eval_hessian_lagrangian(
end

function MOI.eval_hessian_lagrangian(model::Optimizer, H, x, σ, μ)
offset = MOI.eval_hessian_lagrangian(model.qp_data, H, x, σ, μ)
offset -= 1 # .qp_data returns one-indexed offset
MOI.eval_hessian_lagrangian(model.qp_data, H, x, σ, μ)
offset = model.qp_nnzh
μ_offset = length(model.qp_data)
for (f, s) in model.vector_nonlinear_oracle_constraints
offset, μ_offset =
Expand Down Expand Up @@ -1376,8 +1390,13 @@ function _setup_model(model::Optimizer)
MOI.Nonlinear.Evaluator(model.nlp_model, model.ad_backend, vars),
)
end
model.qp_nnzj = length(MOI.jacobian_structure(model.qp_data))
model.qp_nnzh = length(MOI.hessian_lagrangian_structure(model.qp_data))
has_quadratic_constraints =
any(isequal(_kFunctionTypeScalarQuadratic), model.qp_data.function_type)
any(
isequal(MOI.Nonlinear._kFunctionTypeScalarQuadratic),
model.qp_data.function_type,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part of the public API of qp_data?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think QPBlockData should be internal and we should only expose ModelWithQuad, see #548

)
has_nlp_constraints =
!isempty(model.nlp_data.constraint_bounds) ||
!isempty(model.vector_nonlinear_oracle_constraints)
Expand Down
Loading
Loading