# Procedural Nonlinear Constaint Generation - String to NLconstraint

**URL:** <https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, optim, optimization\
**Created:** [February 18, 2020, 9:08am UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799 "2020-02-18T09:08:43Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [February 18, 2020, 9:08am UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/1 "2020-02-18T09:08:43Z")

</div>

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](http://www.juliaopt.org/JuMP.jl/stable/nlp/)

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

```julia-auto
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

```julia-auto
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

```julia-auto
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

```julia-auto
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

```julia-auto
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)

```

---

<div class="post-metadata">

**Author:** ![miles.lubin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/miles.lubin/32/279_2.png) [@miles.lubin](https://discourse.julialang.org/u/miles.lubin)\
**Post date:** [February 18, 2020, 12:46pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/2 "2020-02-18T12:46:20Z")

</div>

The relevant section of the documentation is here: [Nonlinear Modeling · JuMP](http://www.juliaopt.org/JuMP.jl/v0.21.0/nlp/#Raw-expression-input-1). You must create a Julia [`Expr` object](https://docs.julialang.org/en/v1/manual/metaprogramming/) with the JuMP variables spliced into the expression tree. For example:

```julia
m = Model(Ipopt.Optimizer)
@variable(m, x[1:2])
# Use Meta.parse and custom transformations to create this object from a string. eval() is not needed.
expr = :($(x[1])^3 + 5.0 * $(x[1]) * $(x[2]))
add_NL_constraint(m, :($expr <= 5))

```

The [string name](http://www.juliaopt.org/JuMP.jl/v0.21.0/variables/#Variable-names-1) of the variable is irrelevant in this setting as far as JuMP is concerned. The same code would work for anonymous variables `x = @variable(m, [1:2])` that have no names.

---

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [February 20, 2020, 9:34am UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/3 "2020-02-20T09:34:22Z")

</div>

Your comment makes sense; I am now struggling with the implementation details surrounding this comment:

```julia
# Use Meta.parse and custom transformations to create this object from a string. eval() is not needed.

```

Could more granular guidance be offered on how to go about transforming the below string (or a similar modified string)

```julia
str = "x1^3 + 5.0*x1*x2"

```

into

```julia
expr = :($(x[1])^3 + 5.0 * $(x[1]) * $(x[2]))

```

given

```julia
m = Model(Ipopt.Optimizer)
@variable(m, x[1:2])

```

For example, the following results in _“Unrecognized expression x[1]. JuMP variable objects and input coefficients should be spliced directly into expressions.”_

```julia
str = "$(x[1])^3 + 5.0*$(x[1])*$(x[2])"
expr = Meta.parse(str)
add_NL_constraint(m, :($expr <= 3))

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [February 20, 2020, 4:01pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/4 "2020-02-20T16:01:29Z")

</div>

```nohighlight
using JuMP

substitute_args(ex, vars) = ex
substitute_args(ex::Symbol, vars) = get(vars, ex, ex)
function substitute_args(ex::Expr, vars)
    for (i, arg) in enumerate(ex.args)
        ex.args[i] = substitute_args(arg, vars)
    end
    return ex
end

model = Model()
@variable(model, x[1:2])
str = "x1^3 + 5 * x1 * x2"
ex = Meta.parse(str)
vars = Dict(:x1 => x[1], :x2 => x[2])

set_NL_objective(
    model, 
    MOI.MIN_SENSE,
    substitute_args(ex, vars)
)

```

---

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [February 20, 2020, 8:18pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/5 "2020-02-20T20:18:35Z")

</div>

The forloop makes sense as does the dictionary associating symbols in the expression with jump variables.

Would you be able to elaborate on how the function “substitute\_args” works? Specifically I do not understand -

1. Why are these two lines needed before the definition of “substitute\_args”?  
“”"  
substitute\_args(ex, vars) = ex  
substitute\_args(ex::Symbol, vars) = get(vars, ex, ex)  
“”"
2. How can the function “substitute\_args” be called within the definition of “substitute\_args”?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [February 20, 2020, 10:14pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/6 "2020-02-20T22:14:02Z")

</div>

Hopefully these answer your questions:

> Why are these two lines needed before the definition of “substitute\_args”?

They are different methods of the same function: [Methods · The Julia Language](https://docs.julialang.org/en/v1.3/manual/methods/)

This is a fundamentally cool feature of Julia, so it’s worth learning in detail.

> How can the function “substitute\_args” be called within the definition of “substitute\_args”?

Most programming languages allow this. See, e.g., [Recursion (computer science) - Wikipedia](https://en.wikipedia.org/wiki/Recursion_(computer_science))

---

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [March 22, 2020, 9:27pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/7 "2020-03-22T21:27:45Z")

</div>

Thank you for this. After taking time to digest and implement, this reply explains in full how the _substitute\_args_ function works.

Thanks you for these answers, they solve the stated problem.

---

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [March 29, 2020, 11:32pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/8 "2020-03-29T23:32:22Z")

</div>

An interesting follow on has risen from development of a unit test exercising the core functionality of `substitute_args`.

The expectation is that that `ex_out_desired` will be identical to `ex_in` after it is acted upon by `substitute_args`. Though the two expressions appear equal, the test fails per below stack trace.

What nuance am I missing that causes `ex_out_desired == ex_out` to evaluate to false? How could this unit test be restructured to verify that `substitute_args` has indeed spliced the JuMP variables into the expression tree?

**Trace:**

```julia
JuMP Variable Substitution: Test Failed at /home/test/substitute_args.jl:22
  Expression: ex_out_desired == ex_out
   Evaluated: s[1] + s[2] == s[1] + s[2]

```

**Unit Test:**

```julia
using Test
using JuMP

sa(ex, vars) = ex
sa(ex::Symbol, vars) = get(vars, ex, ex)
function sa(ex::Expr, vars)
    for (i, arg) in enumerate(ex.args)
        ex.args[i] = sa(arg, vars)
    end
    return ex
end

@testset "Substitute Arguments Tests" begin
    @testset "JuMP Variable Substitution" begin
        md = Model()
        @variable(md, s[1:2])

        ex_in = Meta.parse("x + y")
        ex_out_desired = :(s[1] + s[2])
        vars = Dict(:x => s[1], :y => s[2])   
        sa(ex_in, vars)
        @test ex_in == ex_out_desired

    end
end

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 29, 2020, 11:42pm UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/9 "2020-03-29T23:42:57Z")

</div>

You need to interpolate the variable into `ex_out_desired`: `:($(s[1]) + $(s[2]))`.

You can see the expression with `dump(ex_out_desired)`.

---

<div class="post-metadata">

**Author:** ![r-b0](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/r-b0/32/12927_2.png) [@r-b0](https://discourse.julialang.org/u/r-b0)\
**Post date:** [March 30, 2020, 1:53am UTC](https://discourse.julialang.org/t/procedural-nonlinear-constaint-generation-string-to-nlconstraint/34799/10 "2020-03-30T01:53:29Z")

</div>

Excellent, this worked.
