Skip to content

Commit

Permalink
Add VectorNonlinearFunction
Browse files Browse the repository at this point in the history
Add convert and parse
  • Loading branch information
odow committed Feb 2, 2023
1 parent 15a3913 commit 1f3ca0f
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/src/manual/standard_form.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The function types implemented in MathOptInterface.jl are:
| [`VectorAffineFunction`](@ref) | ``A x + b``, where ``A`` is a matrix and ``b`` is a vector. |
| [`ScalarQuadraticFunction`](@ref) | ``\frac{1}{2} x^T Q x + a^T x + b``, where ``Q`` is a symmetric matrix, ``a`` is a vector, and ``b`` is a constant. |
| [`VectorQuadraticFunction`](@ref) | A vector of scalar-valued quadratic functions. |
| [`VectorNonlinearFunction`](@ref) | ``f(x)``, where ``f`` is a vector-valued nonlinear function. |

Extensions for nonlinear programming are present but not yet well documented.

Expand Down
1 change: 1 addition & 0 deletions docs/src/reference/standard_form.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ScalarQuadraticTerm
ScalarQuadraticFunction
VectorQuadraticTerm
VectorQuadraticFunction
VectorNonlinearFunction
```

### Utilities
Expand Down
22 changes: 22 additions & 0 deletions src/Nonlinear/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,28 @@ function parse_expression(
return
end

function parse_expression(
data::Model,
expr::Expression,
x::MOI.ScalarAffineFunction{T},
parent::Int,
) where {T}
f = convert(MOI.ScalarNonlinearFunction{T}, x)
parse_expression(data, expr, f, parent)
return
end

function parse_expression(
data::Model,
expr::Expression,
x::MOI.ScalarQuadraticFunction{T},
parent::Int,
) where {T}
f = convert(MOI.ScalarNonlinearFunction{T}, x)
parse_expression(data, expr, f, parent)
return
end

function parse_expression(::Model, expr::Expression, x::Real, parent_index::Int)
push!(expr.values, convert(Float64, x)::Float64)
push!(expr.nodes, Node(NODE_VALUE, length(expr.values), parent_index))
Expand Down
19 changes: 18 additions & 1 deletion src/Test/test_basic_constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ function _function(
)
end

function _function(
::Type{T},
::Type{MOI.VectorNonlinearFunction},
x::Vector{MOI.VariableIndex},
) where {T}
f = MOI.ScalarNonlinearFunction{T}(
:+,
Any[MOI.ScalarNonlinearFunction{T}(:^, Any[xi, 2]) for xi in x],
)
return MOI.VectorNonlinearFunction{T}(Any[f; x])
end

# Default fallback.
_set(::Any, ::Type{S}) where {S} = _set(S)

Expand Down Expand Up @@ -324,7 +336,12 @@ for s in [
:ScalarNonlinearFunction,
)
else
(:VectorOfVariables, :VectorAffineFunction, :VectorQuadraticFunction)
(
:VectorOfVariables,
:VectorAffineFunction,
:VectorQuadraticFunction,
:VectorNonlinearFunction,
)
end
for f in functions
func = Symbol("test_basic_$(f)_$(s)")
Expand Down
21 changes: 21 additions & 0 deletions src/Utilities/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ function map_indices(
)
end

function map_indices(
index_map::F,
f::MOI.VectorNonlinearFunction{T},
) where {F<:Function,T}
return MOI.VectorNonlinearFunction{T}(
Any[map_indices(index_map, arg) for arg in f.args],
)
end

# Function changes

function map_indices(
Expand Down Expand Up @@ -516,6 +525,8 @@ See also [`scalarize`](@ref).
"""
eachscalar(f::MOI.AbstractVectorFunction) = ScalarFunctionIterator(f)

eachscalar(f::MOI.VectorNonlinearFunction) = f.args

"""
eachscalar(f::MOI.AbstractVector)
Expand Down Expand Up @@ -818,6 +829,7 @@ function canonicalize!(
end

canonicalize!(f::MOI.ScalarNonlinearFunction) = f
canonicalize!(f::MOI.VectorNonlinearFunction) = f

"""
canonicalize!(f::Union{ScalarQuadraticFunction, VectorQuadraticFunction})
Expand Down Expand Up @@ -3355,6 +3367,15 @@ end

is_coefficient_type(::Type{<:MOI.ScalarNonlinearFunction}, ::Type) = false

function is_coefficient_type(
::Type{MOI.VectorNonlinearFunction{T}},
::Type{T},
) where {T}
return true
end

is_coefficient_type(::Type{<:MOI.VectorNonlinearFunction}, ::Type) = false

function similar_type(::Type{<:MOI.ScalarAffineFunction}, ::Type{T}) where {T}
return MOI.ScalarAffineFunction{T}
end
Expand Down
6 changes: 5 additions & 1 deletion src/Utilities/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,11 @@ const LessThanIndicatorZero{T} =
MOI.ScalarNonlinearFunction,
),
(MOI.VectorOfVariables,),
(MOI.VectorAffineFunction, MOI.VectorQuadraticFunction)
(
MOI.VectorAffineFunction,
MOI.VectorQuadraticFunction,
MOI.VectorNonlinearFunction,
)
)

@doc raw"""
Expand Down
16 changes: 15 additions & 1 deletion src/Utilities/parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ function _parse_function(ex, ::Type{T} = Float64) where {T}
else
if isexpr(ex, :call, 2) && ex.args[1] == :ScalarNonlinearFunction
return ex
elseif isexpr(ex, :call, 2) && ex.args[1] == :VectorNonlinearFunction
return ex
end
# For simplicity, only accept Expr(:call, :+, ...); no recursive
# expressions
Expand Down Expand Up @@ -240,12 +242,24 @@ _parsed_to_moi(model, s::Number) = s

function _parsed_to_moi(model, s::Expr)
if isexpr(s, :call, 2) && s.args[1] == :ScalarNonlinearFunction
return _parsed_to_moi(model, s.args[2])
return _parsed_scalar_to_moi(model, s.args[2])
elseif isexpr(s, :call, 2) && s.args[1] == :VectorNonlinearFunction
return _parsed_vector_to_moi(model, s.args[2])
end
args = Any[_parsed_to_moi(model, arg) for arg in s.args[2:end]]
return MOI.ScalarNonlinearFunction{Float64}(s.args[1], args)
end

function _parsed_scalar_to_moi(model, s::Expr)
args = Any[_parsed_to_moi(model, arg) for arg in s.args[2:end]]
return MOI.ScalarNonlinearFunction{Float64}(s.args[1], args)
end

function _parsed_vector_to_moi(model, s::Expr)
args = Any[_parsed_to_moi(model, arg) for arg in s.args]
return MOI.VectorNonlinearFunction{Float64}(args)
end

for typename in [
:_ParsedScalarAffineTerm,
:_ParsedScalarAffineFunction,
Expand Down
4 changes: 4 additions & 0 deletions src/Utilities/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ _to_string(::_PrintOptions, ::typeof(in)) = @static Sys.iswindows() ? "in" : "
# Functions
#------------------------------------------------------------------------

function _to_string(options::_PrintOptions, ::MOI.ModelLike, c::Real)
return _shorten(options, c)
end

function _to_string(
options::_PrintOptions,
model::MOI.ModelLike,
Expand Down
102 changes: 102 additions & 0 deletions src/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,46 @@ struct ScalarNonlinearFunction{T} <: AbstractScalarFunction
args::Vector{Any}
end

"""
VectorNonlinearFunction{T<:Number}(args::Vector{Any})
The vector-valued nonlinear function composed of a vector of scalar functions.
## `args`
The vector `args` contains the scalar elements of the nonlinear function. Each
element must be one of the folowing:
* A number of type `T`
* A variable of type [`VariableIndex`](@ref)
* A [`ScalarAffineFunction`](@ref)
* A [`ScalarQuadraticFunction`](@ref)
* A [`ScalarNonlinearFunction`](@ref)
## Example
To represent the function ``f(x) = [sin(x)^2, x]``, do:
```jldoctest
julia> using MathOptInterface; const MOI = MathOptInterface;
julia> x = MOI.VariableIndex(1)
MathOptInterface.VariableIndex(1)
julia> g = MOI.ScalarNonlinearFunction{Float64}(
:^,
Any[MOI.ScalarNonlinearFunction{Float64}(:sin, Any[x]), 2],
)
MathOptInterface.ScalarNonlinearFunction{Float64}(:^, Any[MathOptInterface.ScalarNonlinearFunction{Float64}(:sin, Any[MathOptInterface.VariableIndex(1)]), 2])
julia> MOI.VectorNonlinearFunction{Float64}(Any[g, x])
MathOptInterface.VectorNonlinearFunction{Float64}(Any[MathOptInterface.ScalarNonlinearFunction{Float64}(:^, Any[MathOptInterface.ScalarNonlinearFunction{Float64}(:sin, Any[MathOptInterface.VariableIndex(1)]), 2]), MathOptInterface.VariableIndex(1)])
```
"""
struct VectorNonlinearFunction{T} <: AbstractVectorFunction
args::Vector{Any}
end

# Function modifications

"""
Expand Down Expand Up @@ -473,6 +513,22 @@ function Base.isapprox(
return true
end

function Base.isapprox(
f::VectorNonlinearFunction{T},
g::VectorNonlinearFunction{T};
kwargs...,
) where {T}
if length(f.args) != length(g.args)
return false
end
for (fi, gi) in zip(f.args, g.args)
if !isapprox(fi, gi; kwargs...)
return false
end
end
return true
end

function constant(f::Union{ScalarAffineFunction,ScalarQuadraticFunction}, ::Any)
return constant(f)
end
Expand Down Expand Up @@ -536,6 +592,10 @@ function Base.copy(f::ScalarNonlinearFunction{T}) where {T}
return ScalarNonlinearFunction{T}(f.head, copy(f.args))
end

function Base.copy(f::VectorNonlinearFunction{T}) where {T}
return VectorNonlinearFunction{T}(copy(f.args))
end

# Define shortcuts for
# VariableIndex -> ScalarAffineFunction
function ScalarAffineFunction{T}(f::VariableIndex) where {T}
Expand Down Expand Up @@ -713,3 +773,45 @@ function Base.convert(
[g.constant],
)
end

function Base.convert(
::Type{ScalarNonlinearFunction{T}},
term::ScalarAffineTerm{T},
) where {T}
return ScalarNonlinearFunction{T}(:*, Any[term.coefficient, term.variable])
end

function Base.convert(
F::Type{ScalarNonlinearFunction{T}},
f::ScalarAffineFunction{T},
) where {T}
args = Any[convert(ScalarNonlinearFunction{T}, term) for term in f.terms]
if !iszero(f.constant)
push!(args, f.constant)
end
return ScalarNonlinearFunction{T}(:+, args)
end

function Base.convert(
::Type{ScalarNonlinearFunction{T}},
term::ScalarQuadraticTerm{T},
) where {T}
return ScalarNonlinearFunction{T}(
:*,
Any[term.coefficient, term.variable_1, term.variable_2],
)
end

function Base.convert(
F::Type{ScalarNonlinearFunction{T}},
f::ScalarQuadraticFunction{T},
) where {T}
args = Any[convert(F, term) for term in f.quadratic_terms]
for term in f.affine_terms
push!(args, convert(F, term))
end
if !iszero(f.constant)
push!(args, f.constant)
end
return ScalarNonlinearFunction{T}(:+, args)
end

0 comments on commit 1f3ca0f

Please sign in to comment.