Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ jobs:
fail-fast: false
matrix:
include:
- version: '1.6'
os: ubuntu-latest
arch: x64
- version: '1'
os: ubuntu-latest
arch: x64
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Manifest.toml
examples/.ipynb_checkpoints
.DS_Store
/Manifest.toml
coverage/lcov.info
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DiffOpt"
uuid = "930fe3bc-9c6b-11ea-2d94-6184641e85e7"
authors = ["Akshay Sharma", "Mathieu Besançon", "Joaquim Dias Garcia", "Benoît Legat", "Oscar Dowson", "Andrew Rosemberg"]
version = "0.5.5"
version = "0.5.6"

[deps]
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"
Expand All @@ -24,4 +24,4 @@ LazyArrays = "1, 2"
MathOptInterface = "1.18"
MathOptSetDistances = "0.2.9"
ParametricOptInterface = "0.14.1"
julia = "1.6"
julia = "1.10"
83 changes: 83 additions & 0 deletions docs/src/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,89 @@ and the following objective types:
| `ScalarQuadraticFunction` |
| `ScalarNonlinearFunction` |

### `VectorNonlinearOracle` bridge (`NonlinearProgram`)

`DiffOpt.NonLinearProgram` supports `MOI.VectorOfVariables` in
`MOI.VectorNonlinearOracle` through an internal bridge that rewrites the vector
oracle into scalar nonlinear constraints.

At a high level, for a vector oracle $f:\mathbb{R}^n \to \mathbb{R}^m$ with
bounds $l \le f(x) \le u$:

- one scalar nonlinear operator is registered per output row $f_i(x)$
- each row is converted into one or two scalar constraints based on bounds:
- finite and equal $l[i] == u[i]$: `EqualTo(l[i])`
- finite lower only: `GreaterThan(l[i])`
- finite upper only: `LessThan(u[i])`
- both finite and different: one `GreaterThan` and one `LessThan`
- infinite bounds are skipped

Callback signature requirements follow MOI:

- univariate (`input_dimension == 1`):
- `f(x)::Real`
- `∇f(x)::Real`
- `∇²f(x)::Real`
- multivariate (`input_dimension > 1`):
- `f(x...)::Real`
- `∇f(g, x...)` fills `g`
- `∇²f(H, x...)` fills the lower-triangular part of `H`

Warm-start mapping for bridged constraints:

- `ConstraintPrimalStart` expects an input vector `x` (not `f(x)`), evaluates
the oracle at `x`, and writes starts for each generated scalar constraint.
- `ConstraintDualStart` accepts either:
- length = output dimension `m`: treated as direct row duals
- length = input dimension `n`: interpreted as `J' * λ` and converted to row
duals `λ` via a least-squares solve

Current limitation:

- for dual starts on rows with both finite bounds (`l[i] < u[i]`), only the
lower-bound side is propagated explicitly (the upper-side split is not
modeled in this bridge-level helper).

Minimal JuMP example:

```julia
using JuMP, DiffOpt, Ipopt
import MathOptInterface as MOI

model = DiffOpt.nonlinear_diff_model(Ipopt.Optimizer)
@variable(model, x[1:2])
@objective(model, Min, x[1]^2 + x[2]^2)

function eval_f(ret, z)
ret[1] = z[1]^2 + z[2]^2
return
end
function eval_jacobian(ret, z)
ret[1] = 2z[1]
ret[2] = 2z[2]
return
end
function eval_hessian_lagrangian(ret, z, μ)
ret[1] = 2μ[1] # (1,1)
ret[2] = 2μ[1] # (2,2)
return
end

set = MOI.VectorNonlinearOracle(;
dimension = 2,
l = [-Inf],
u = [1.0],
eval_f,
jacobian_structure = [(1, 1), (1, 2)],
eval_jacobian,
hessian_lagrangian_structure = [(1, 1), (2, 2)],
eval_hessian_lagrangian,
)

@constraint(model, [x[1], x[2]] in set)
optimize!(model)
```

## Creating a differentiable MOI optimizer

You can create a differentiable optimizer over an existing MOI solver by using the `diff_optimizer` utility.
Expand Down
11 changes: 11 additions & 0 deletions src/ConicProgram/ConicProgram.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ function MOI.supports_constraint(
return MOI.supports_constraint(model.model, F, S)
end

function MOI.supports_constraint(
::Model,
::Type{MOI.VectorAffineFunction{Float64}},
::Type{MOI.VectorNonlinearOracle{Float64}},
)
# VNO constraints require the nonlinear bridge path. If we accept them here,
# the conic projection code later tries to reconstruct the set by dimension
# only, which is invalid for VectorNonlinearOracle.
return false
end

function MOI.supports_constraint(
::Model,
::Type{MOI.VectorAffineFunction{T}},
Expand Down
1 change: 1 addition & 0 deletions src/NonLinearProgram/NonLinearProgram.jl
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ function MOI.empty!(model::Model)
end

include("nlp_utilities.jl")
include("vno_bridge.jl")

"""
_inertia_correction(
Expand Down
Loading