I am implementing a large scale nonlinear program solver using the JuMP interface to IPOPT.
The Nonlinear Modeling section of JuMP v0.21.0 documentation clearly explicates that all expressions used in NLconstraint and NLobjective must be scalar operations. Nonlinear Modeling · JuMP
For this particular appreciation, manual derivation of scalar NLconstraint expressions is prohibitive due to their quantity and complexity.
As an attempt to work around this limitation, scalar nonlinear expressions were derived and delivered as strings using meta programming/symbolic math packages; The thought being that these strings could be parsed, evaluated and passed to NLconstraint. In practice this does not work.
The question is thus -
How can a string be parsed into an expression ingestible by NLconstraint/NLobjective?
With julia’s meta-programming faculty there must be a way to accomplish this without altering nonlinear modeling/autodiff source code.
The task is illustrated on the toy problem below.
Suppose the nonlinear constraint has been delivered as a string taking one of the following forms, parse it such that it may be passed to NLconstraint
“x[1]^3 + 5.0*x[1]*x[2]”
or
“x1^3 + 5.0*x1*x2”
Working Toy Problem
using JuMP
using Ipopt
m = Model(with_optimizer(Ipopt.Optimizer))
@variable(m, x[1:2])
set_lower_bound(x[1], 0.0)
set_upper_bound(x[1], 2.0)
set_lower_bound(x[2], -3000.0)
set_upper_bound(x[2], 3000.0)
@NLobjective(m, Max, 5*x[1]^2*x[2]-3*x[2]^2)
@NLconstraint(m, x[1]^3 + 5.0*x[1]*x[2] <= 3.0)
optimize!(m)
Optimal Solution
obj val: 1.5689952589360394
x[1] val: 1.0697545858077664
x[2] val: 0.3320013320392786
println("obj val: ", objective_value(m0))
println("x[1]: ", value(x[1]))
println("x[2]: ", value(x[2]))
Here are three methods which are unsuccessful in converting a string to an expression ingestible by NLconstraint
Attempt 1
m = Model(with_optimizer(Ipopt.Optimizer))
@variable(m, x[1:2])
f = eval(Meta.parse("x[1]^3 + 5.0*x[1]*x[2]"))
@NLconstraint(m, f <= 3.0)
optimize!(m)
Attempt 2
m = Model(with_optimizer(Ipopt.Optimizer))
@variable(m, x[1:2])
@NLconstraint(m, eval(Meta.parse("x[1]^3 + 5.0*x[1]*x[2]")) <= 3.0)
optimize!(m)
Attempt 3
m = Model(with_optimizer(Ipopt.Optimizer))
@variable(m, x[1:2])
function f(x1, x2) eval(Meta.parse("x1^3 + 5.0*x1*x2")) end
register(m, :my_f, 2, f, autodiff=true)
@NLconstraint(m, my_f(x[1], x[2]) <= 3.0)
optimize!(m)