Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Aug 16, 2023
1 parent 00c464c commit 94ec319
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 33 deletions.
4 changes: 1 addition & 3 deletions docs/src/manual/nonlinear.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ julia> delete(model, con[1])

## Create a nonlinear expression

Use [`@expression`](@ref) to create nonlinear expression objects. The syntax
is identical to [`@expression`](@ref), except that the expression can contain
nonlinear terms.
Use [`@expression`](@ref) to create nonlinear expression objects:

```jldoctest nl_expression
julia> model = Model();
Expand Down
56 changes: 30 additions & 26 deletions src/nlp_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -118,32 +118,29 @@ function GenericNonlinearExpr{V}(
return GenericNonlinearExpr{V}(head, Any[args...])
end

Base.length(x::GenericNonlinearExpr) = length(x.args)
Base.getindex(x::GenericNonlinearExpr, i::Int) = x.args[i]

const _PREFIX_OPERATORS =
(:+, :-, :*, :/, :^, :||, :&&, :>, :<, :(<=), :(>=), :(==))

_needs_parentheses(::Union{Number,AbstractVariableRef}) = false
_needs_parentheses(::Any) = true
function _needs_parentheses(x::GenericNonlinearExpr)
return x.head in _PREFIX_OPERATORS && length(x) > 1
return x.head in _PREFIX_OPERATORS && length(x.args) > 1
end

function function_string(::MIME"text/plain", x::GenericNonlinearExpr)
io, stack = IOBuffer(), Any[x]
while !isempty(stack)
arg = pop!(stack)
if arg isa GenericNonlinearExpr
if arg.head in _PREFIX_OPERATORS && length(arg) > 1
if _needs_parentheses(arg[1])
if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1
if _needs_parentheses(arg.args[1])
print(io, "(")
end
if _needs_parentheses(arg.args[end])
push!(stack, ")")
end
for i in length(arg):-1:2
push!(stack, arg[i])
for i in length(arg.args):-1:2
push!(stack, arg.args[i])
if _needs_parentheses(arg.args[i])
push!(stack, "(")
end
Expand All @@ -152,15 +149,15 @@ function function_string(::MIME"text/plain", x::GenericNonlinearExpr)
push!(stack, ")")
end
end
push!(stack, arg[1])
push!(stack, arg.args[1])
else
print(io, arg.head, "(")
push!(stack, ")")
for i in length(arg):-1:2
push!(stack, arg[i])
for i in length(arg.args):-1:2
push!(stack, arg.args[i])
push!(stack, ", ")
end
push!(stack, arg[1])
push!(stack, arg.args[1])
end
else
print(io, arg)
Expand All @@ -175,22 +172,22 @@ function function_string(::MIME"text/latex", x::GenericNonlinearExpr)
while !isempty(stack)
arg = pop!(stack)
if arg isa GenericNonlinearExpr
if arg.head in _PREFIX_OPERATORS && length(arg) > 1
if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1
print(io, "\\left({")
push!(stack, "}\\right)")
for i in length(arg):-1:2
push!(stack, arg[i])
for i in length(arg.args):-1:2
push!(stack, arg.args[i])
push!(stack, "} $(arg.head) {")
end
push!(stack, arg[1])
push!(stack, arg.args[1])
else
print(io, "\\textsf{", arg.head, "}\\left({")
push!(stack, "}\\right)")
for i in length(arg):-1:2
push!(stack, arg[i])
for i in length(arg.args):-1:2
push!(stack, arg.args[i])
push!(stack, "}, {")
end
push!(stack, arg[1])
push!(stack, arg.args[1])
end
else
print(io, arg)
Expand All @@ -205,8 +202,8 @@ _isequal(x::T, y::T) where {T<:AbstractJuMPScalar} = isequal_canonical(x, y)

function isequal_canonical(x::GenericNonlinearExpr, y::GenericNonlinearExpr)
return x.head == y.head &&
length(x) == length(y) &&
all(i -> _isequal(x[i], y[i]), 1:length(x))
length(x.args) == length(y.args) &&
all(i -> _isequal(x.args[i], y.args[i]), 1:length(x.args))
end

function MOI.Nonlinear.parse_expression(
Expand All @@ -229,9 +226,9 @@ function MOI.Nonlinear.parse_expression(
return
end

function _get_node_type(data, x)
function _get_node_type(data, x::GenericNonlinearExpr)
id = get(data.operators.univariate_operator_to_id, x.head, nothing)
if length(x) == 1 && id !== nothing
if length(x.args) == 1 && id !== nothing
return id, MOI.Nonlinear.NODE_CALL_UNIVARIATE
end
id = get(data.operators.multivariate_operator_to_id, x.head, nothing)
Expand All @@ -249,12 +246,19 @@ function _get_node_type(data, x)
return throw(MOI.UnsupportedNonlinearOperator(x.head))
end

function _parse_without_recursion_inner(stack, data, expr, x, parent)
function _parse_without_recursion_inner(
stack,
data,
expr,
x::GenericNonlinearExpr,
parent,
)
id, node_type = _get_node_type(data, x)
push!(expr.nodes, MOI.Nonlinear.Node(node_type, id, parent))
parent = length(expr.nodes)
for i in length(x):-1:1 # Args need to be pushed onto the stack in reverse
push!(stack, (parent, x[i]))
# Args need to be pushed onto the stack in reverse
for i in length(x.args):-1:1
push!(stack, (parent, x.args[i]))
end
return
end
Expand Down
5 changes: 3 additions & 2 deletions src/optimizer_interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,9 @@ function optimize!(
if _uses_new_nonlinear_interface(model)
error(
"Cannot optimize a model which contains the features from " *
"both the legacy and new nonlinear interfaces. You must use " *
"one or the other.",
"both the legacy (macros beginning with `@NL`) and new " *
"(`NonlinearExpr`) nonlinear interfaces. You must use one or " *
"the other.",
)
end
evaluator = MOI.Nonlinear.Evaluator(
Expand Down
5 changes: 3 additions & 2 deletions test/test_nlp_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -656,8 +656,9 @@ function test_error_both_nl_interfaces()
@NLconstraint(model, log(x) <= 1)
@test_throws(
ErrorException(
"Cannot optimize a model which contains the features from both " *
"the legacy and new nonlinear interfaces. You must use one or " *
"Cannot optimize a model which contains the features from " *
"both the legacy (macros beginning with `@NL`) and new " *
"(`NonlinearExpr`) nonlinear interfaces. You must use one or " *
"the other.",
),
optimize!(model),
Expand Down

0 comments on commit 94ec319

Please sign in to comment.