Draft of Problem Interface
Problems must subtype AbstractProblem
.
abstract type AbstractProblem end
The problem type should indicate what precision is required:
float_type(::Type{<:AbstractProblem})=Float64
We allow to also give objects instead of types:
float_type(mop::mop_Type) where {mop_Type<:AbstractProblem}=float_type(mop_Type)
Like with float_type
, other properties are queried from the problem type, to enable type-based bridging. To this end, we have several attribute types:
abstract type AbstractAttribute end
struct Variables <: AbstractAttribute end
A supertype for something that can be evaluated:
abstract type AbstractFunctionQualifier <: AbstractAttribute end
struct Objectives <: AbstractFunctionQualifier end
struct LinEqConstraints <: AbstractFunctionQualifier end
struct LinIneqConstraints <: AbstractFunctionQualifier end
struct NonlinEqConstraints <: AbstractFunctionQualifier end
struct NonlinIneqConstraints <: AbstractFunctionQualifier end
Dimension Information
Mandatory: Specialize dimval
for Variables
and other attributes as needed. For AbstractFunctionQualifier
s, this should return Val{i}
, where i
is the integer dimension of output vectors.
function dimval(
mop_Type::Type{<:AbstractProblem}, attr_Type::Type{<:AbstractAttribute})::Val
error("`dimval` not applicable for `$(mop_Type)` and `$(attr_Type)`.")
end
dimval(::Type{<:AbstractProblem}, ::Type{<:AbstractFunctionQualifier})=Val(0)
We have some helpers that also take objects instead of types:
function dimval(_mop, _attr)
dimval(_typeof(_mop), _typeof(_attr))
end
_typeof(T::Type)=error("Cannot extract concrete type from `$T`.")
_typeof(T::DataType)=T
_typeof(obj)=typeof(obj)
The integer dimension is returned by dim
:
dim(mop, attr)=_extract_val(dimval(mop, attr))
_extract_val(::Val{i}) where {i} = i
Variable Bounds
An attribute to indicate how variables are constrained:
abstract type AbstractVarBoundsAttr <: AbstractAttribute end
struct NoBounds <: AbstractVarBoundsAttr end
struct LowerBounds <: AbstractVarBoundsAttr end
struct UpperBounds <: AbstractVarBoundsAttr end
struct BoxBounds <: AbstractVarBoundsAttr end
Suggested: var_bounds
defaults to NoBounds()
; adapt as needed:
var_bounds(::Type{<:AbstractProblem}) = NoBounds()
var_bounds(mop::mop_Type) where mop_Type<:AbstractProblem=var_bounds(mop_Type)
If var_bounds
returns LowerBounds()
or BoxBounds()
, then lower_var_bounds
should be implemented.
function lower_var_bounds(mop::AbstractProblem)
return lower_var_bounds(var_bounds(mop), mop)
end
function lower_var_bounds(::Union{LowerBounds, BoxBounds}, mop)
error("`lower_var_bounds` not implemented.")
end
Otherwise, we fall back to -∞:
function lower_var_bounds(var_bounds_attr, mop)
return _bounds_vec(dimval(mop, Variables), float_type(mop), mop, -Inf)
end
Likewise, implement upper_var_bounds
if needed:
function upper_var_bounds(mop::AbstractProblem)
return upper_var_bounds(var_bounds(mop), mop)
end
function upper_var_bounds(::Union{UpperBounds, BoxBounds}, mop)
error("`upper_var_bounds` not implemented.")
end
function upper_var_bounds(var_bounds_attr, mop)
return _bounds_vec(dimval(mop, Variables), float_type(mop), mop, Inf)
end
@generated function _bounds_vec(::Val{nvars}, ::Type{F}, mop, val) where {nvars, F}
return quote
Fval = convert($F, val)
fill(Fval, $nvars)
end
end
const LinConstraints = Union{LinEqConstraints, LinIneqConstraints}
Linear Constraints
If there are linear constraints, as indicated by dimval
, you are advised to implement constraint_matrices
. (There are bridges for 0-dim constraints and to fall back to calc
).
function constraint_matrices(mop::AbstractProblem, attr::LinConstraints)
# return (A, b) for constraints A ≤ b
# return (E, c) for constraints E = c
error("`constraint_matrices` not defined.")
end
Evaluation
If there are evaluators (as indicated by dimval
), implement calc
.
For linear inequality constraints $A x ≤ b$, this should return the residual $A - b$.
For linear equality constraints $E x = c$, this should return the residual $E - c$.
Nonlinear constraints take the form $g(x) ≤ 0$ or $h(x) = 0$.
There are some bridges for 0-dim evaluators and to translate between matrices and vectors.
function calc(
mop::AbstractProblem, attr::AbstractFunctionQualifier, x::AbstractVecOrMat
)
error("`calc` not implemented.")
end
Differentiation
Maybe implement the Jacobian:
function diff(
mop::AbstractProblem, attr::AbstractFunctionQualifier, x::AbstractVector
)
error("`diff` not implemented.")
end
Internals
Are there user defined methods?
function is_implemented(
@nospecialize(func::func_Type),
@nospecialize(args_Type::Type{<:Tuple{mop_Type, Vararg}})
) where {
mop_Type <: AbstractProblem,
func_Type <: Function
}
m = static_which(func, args_Type, Val(false))
if isnothing(m)
return NotImplemented()
end
if m.sig.parameters[2] >: AbstractProblem
return NotImplemented()
end
return IsImplemented()
end
Is there an autodiff backend?
function _backend_Type(::Type{<:AbstractProblem})
return Nothing
end
If _backend_Type
not Nothing
, implement _backend
accordingly:
function _backend(::AbstractProblem)
return nothing
end
Show some info:
function Base.show(io::IO, mop::AbstractProblem)
mop_Type = typeof(mop)
show_problem(io, mop)
if !get(io, :compact, false)
print(io, "\n\t| ")
print(io, "Variables=$(dim(mop, Variables)), ")
print(io, "VarBounds=$(var_bounds(mop_Type)), ")
print(io, "Objectives=$(dim(mop, Objectives)), ")
print(io, "\n\t| ")
print(io, "LinEqConstraints=$(dim(mop, LinEqConstraints)), ")
print(io, "LinIneqConstraints=$(dim(mop, LinIneqConstraints)), ")
print(io, "NonlinEqConstraints=$(dim(mop, NonlinEqConstraints)), ")
print(io, "NonlinIneqConstraints=$(dim(mop, NonlinIneqConstraints)) ")
end
end
function show_problem(io::IO, prop::AbstractProblem)
Base.show_default(io, prop)
end
This page was generated using Literate.jl.