Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions docs/src/submodules/Nonlinear/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ Nonlinear.AbstractAutomaticDifferentiation
Nonlinear.ExprGraphOnly
Nonlinear.SparseReverseMode
Nonlinear.SymbolicMode
Nonlinear.QPBlockData
Nonlinear.ModelWithQuad
Nonlinear.EvaluatorWithQuad

```

## Data-structure
Expand Down
3 changes: 3 additions & 0 deletions docs/src/submodules/Utilities/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ Utilities.ObjectiveContainer
### `.variables`

```@docs
Utilities.AbstractVectorBounds
Utilities.VariablesContainer
Utilities.FreeVariables
Utilities.variable_bounds
```

### `.constraints`
Expand Down Expand Up @@ -127,6 +129,7 @@ Utilities.load_constants
Utilities.function_constants
Utilities.set_from_constants
Utilities.modify_constants
Utilities.constraint_bounds
```

```@docs
Expand Down
4 changes: 4 additions & 0 deletions src/Nonlinear/Nonlinear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ include("evaluator.jl")
include("ReverseAD/ReverseAD.jl")
include("SymbolicAD/SymbolicAD.jl")

include("qp_block_data.jl")
include("model_with_quad.jl")
include("model_with_oracles.jl")

end # module
20 changes: 17 additions & 3 deletions src/Nonlinear/evaluator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,21 @@ function MOI.constraint_expr(evaluator::Evaluator, i::Int)
end
end

function _objective_sign(sense)
return sense == MOI.MAX_SENSE ? -1 : sense == MOI.MIN_SENSE ? 1 : 0
end

function MOI.eval_objective(evaluator::Evaluator, x)
start = time()
obj = MOI.eval_objective(evaluator.backend, x)
evaluator.eval_objective_timer += time() - start
return obj
return _objective_sign(evaluator.model.objective_sense) * obj
end

function MOI.eval_objective_gradient(evaluator::Evaluator, g, x)
start = time()
MOI.eval_objective_gradient(evaluator.backend, g, x)
g .*= _objective_sign(evaluator.model.objective_sense)
evaluator.eval_objective_gradient_timer += time() - start
return
end
Expand Down Expand Up @@ -219,7 +224,8 @@ end

function MOI.eval_hessian_lagrangian(evaluator::Evaluator, H, x, σ, μ)
start = time()
MOI.eval_hessian_lagrangian(evaluator.backend, H, x, σ, μ)
objective_sign = _objective_sign(evaluator.model.objective_sense)
MOI.eval_hessian_lagrangian(evaluator.backend, H, x, objective_sign * σ, μ)
evaluator.eval_hessian_lagrangian_timer += time() - start
return
end
Expand Down Expand Up @@ -252,7 +258,15 @@ function MOI.eval_hessian_lagrangian_product(
μ,
)
start = time()
MOI.eval_hessian_lagrangian_product(evaluator.backend, H, x, v, σ, μ)
objective_sign = _objective_sign(evaluator.model.objective_sense)
MOI.eval_hessian_lagrangian_product(
evaluator.backend,
H,
x,
v,
objective_sign * σ,
μ,
)
evaluator.eval_hessian_lagrangian_timer += time() - start
return
end
Expand Down
230 changes: 230 additions & 0 deletions src/Nonlinear/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ function MOI.empty!(model::Model)
empty!(model.constraints)
empty!(model.parameters)
model.operators = OperatorRegistry()
model.objective_sense = MOI.FEASIBILITY_SENSE
model.moi_objective = nothing
empty!(model.moi_functions)
empty!(model.constraint_dual_start)
model.last_constraint_index = 0
model.has_deleted_constraint = false
return
end

Expand All @@ -21,9 +26,14 @@ function MOI.is_empty(model::Model)
isempty(model.parameters) &&
isempty(model.operators.registered_univariate_operators) &&
isempty(model.operators.registered_multivariate_operators) &&
model.objective_sense == MOI.FEASIBILITY_SENSE &&
model.moi_objective === nothing &&
isempty(model.moi_functions) &&
isempty(model.constraint_dual_start) &&
model.last_constraint_index === Int64(0)
end

_parameter_values(model::Model) = model.parameters
function Base.copy(::Model)
return error("Copying nonlinear problems not yet implemented")
end
Expand Down Expand Up @@ -69,14 +79,30 @@ julia> MOI.Nonlinear.set_objective(model, nothing)
"""
function set_objective(model::Model, obj)
model.objective = parse_expression(model, obj)
model.moi_objective = obj isa MOI.ScalarNonlinearFunction ? obj : nothing
if model.objective_sense == MOI.FEASIBILITY_SENSE
model.objective_sense = MOI.MIN_SENSE
end
return
end

function set_objective(model::Model, ::Nothing)
model.objective = nothing
model.moi_objective = nothing
return
end

"""
model(backend::AbstractAutomaticDifferentiation)

Return a new MOI model appropriate for the automatic-differentiation
`backend`. Custom backends may overload this method to provide a model that
stores functions in a backend-specific representation.
"""
function model(::AbstractAutomaticDifferentiation)
return ModelWithQuad(ModelWithOracles(Model()))
end

"""
add_expression(model::Model, expr)::ExpressionIndex

Expand Down Expand Up @@ -148,6 +174,9 @@ function add_constraint(
model.last_constraint_index += 1
index = ConstraintIndex(model.last_constraint_index)
model.constraints[index] = Constraint(f, set)
if func isa MOI.ScalarNonlinearFunction
model.moi_functions[index] = func
end
return index
end

Expand Down Expand Up @@ -191,6 +220,9 @@ A Nonlinear.Model with:
"""
function delete(model::Model, c::ConstraintIndex)
delete!(model.constraints, c)
delete!(model.moi_functions, c)
delete!(model.constraint_dual_start, c)
model.has_deleted_constraint = true
return
end

Expand All @@ -202,6 +234,204 @@ function MOI.is_valid(model::Model, index::ConstraintIndex)
return haskey(model.constraints, index)
end

# MathOptInterface model API. The legacy `Nonlinear` API above remains
# available, but model layers and solvers communicate with this model only via
# these methods.

const _ScalarSet{T} =
Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T},MOI.Interval{T}}

function MOI.supports_constraint(
::Model,
::Type{MOI.ScalarNonlinearFunction},
::Type{<:_ScalarSet{Float64}},
)
return true
end

function MOI.add_constraint(
model::Model,
f::MOI.ScalarNonlinearFunction,
s::_ScalarSet{Float64},
)
index = add_constraint(model, f, s)
return MOI.ConstraintIndex{typeof(f),typeof(s)}(index.value)
end

_nonlinear_index(ci::MOI.ConstraintIndex) = ConstraintIndex(ci.value)

function MOI.is_valid(
model::Model,
ci::MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,S},
) where {S<:_ScalarSet{Float64}}
index = _nonlinear_index(ci)
return haskey(model.constraints, index) &&
model.constraints[index].set isa S
end

function MOI.get(
model::Model,
::MOI.ListOfConstraintIndices{F,S},
) where {F<:MOI.ScalarNonlinearFunction,S<:_ScalarSet{Float64}}
return MOI.ConstraintIndex{F,S}[
MOI.ConstraintIndex{F,S}(index.value) for
(index, constraint) in model.constraints if constraint.set isa S
]
end

function MOI.get(
model::Model,
::MOI.NumberOfConstraints{F,S},
) where {F<:MOI.ScalarNonlinearFunction,S<:_ScalarSet{Float64}}
return count(constraint -> constraint.set isa S, values(model.constraints))
end

function MOI.get(model::Model, ::MOI.ListOfConstraintTypesPresent)
types = Tuple{Type,Type}[]
for constraint in values(model.constraints)
pair = (MOI.ScalarNonlinearFunction, typeof(constraint.set))
pair in types || push!(types, pair)
end
return types
end

function MOI.get(
model::Model,
::MOI.ConstraintFunction,
ci::MOI.ConstraintIndex,
)
MOI.throw_if_not_valid(model, ci)
return model.moi_functions[_nonlinear_index(ci)]
end

function MOI.get(model::Model, ::MOI.ConstraintSet, ci::MOI.ConstraintIndex)
MOI.throw_if_not_valid(model, ci)
return model.constraints[_nonlinear_index(ci)].set
end

function MOI.set(
model::Model,
::MOI.ConstraintSet,
ci::MOI.ConstraintIndex,
set,
)
MOI.throw_if_not_valid(model, ci)
index = _nonlinear_index(ci)
constraint = model.constraints[index]
model.constraints[index] = Constraint(constraint.expression, set)
return
end

function MOI.Utilities.rows(model::Model, ci::MOI.ConstraintIndex)
if model.has_deleted_constraint
error(
"`MOI.Utilities.rows` is not supported after a constraint has " *
"been deleted with `MOI.Nonlinear.delete`. For this reason, ",
"`MOI.delete` is not implemented for `MOI.Nonlinear.Model` ",
"solvers using this model as backedn shouldn't implement it ",
"either.",
)
end
MOI.throw_if_not_valid(model, ci)
return ci.value
end

function MOI.Utilities.constraint_bounds(model::Model)
lower = Float64[]
upper = Float64[]
for constraint in values(model.constraints)
bound = _bound(constraint.set)
push!(lower, bound.lower)
push!(upper, bound.upper)
end
return MOI.Utilities.Hyperrectangle(lower, upper)
end

function constraint_dual_starts(model::Model)
return Union{Nothing,Float64}[
get(model.constraint_dual_start, index, nothing) for
index in keys(model.constraints)
]
end

MOI.supports(::Model, ::MOI.ObjectiveSense) = true
MOI.get(model::Model, ::MOI.ObjectiveSense) = model.objective_sense

function MOI.set(
model::Model,
::MOI.ObjectiveSense,
sense::MOI.OptimizationSense,
)
model.objective_sense = sense
return
end

function MOI.supports(
::Model,
::MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction},
)
return true
end

function MOI.set(
model::Model,
::MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction},
f::MOI.ScalarNonlinearFunction,
)
sense = model.objective_sense
set_objective(model, f)
model.objective_sense = sense
return
end

function MOI.get(model::Model, ::MOI.ObjectiveFunctionType)
return model.objective === nothing ? nothing : MOI.ScalarNonlinearFunction
end

function MOI.get(
model::Model,
::MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction},
)
return something(model.moi_objective)
end

MOI.supports(::Model, ::MOI.UserDefinedFunction) = true

function MOI.set(model::Model, attr::MOI.UserDefinedFunction, functions)
return register_operator(model, attr.name, attr.arity, functions...)
end

function MOI.supports(
::Model,
::MOI.ConstraintDualStart,
::Type{<:MOI.ConstraintIndex{MOI.ScalarNonlinearFunction}},
)
return true
end

function MOI.get(
model::Model,
::MOI.ConstraintDualStart,
ci::MOI.ConstraintIndex,
)
return get(model.constraint_dual_start, _nonlinear_index(ci), nothing)
end

function MOI.set(
model::Model,
::MOI.ConstraintDualStart,
ci::MOI.ConstraintIndex,
value::Union{Nothing,Real},
)
index = _nonlinear_index(ci)
if value === nothing
delete!(model.constraint_dual_start, index)
else
model.constraint_dual_start[index] = Float64(value)
end
return
end

"""
add_parameter(model::Model, value::Float64)::ParameterIndex

Expand Down
Loading
Loading