# How to define vector-valued @operator in JuMP

**URL:** <https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, optimization\
**Created:** [January 5, 2025, 12:10am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438 "2025-01-05T00:10:43Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rick](https://avatars.discourse-cdn.com/v4/letter/r/9f8e36/32.png) [@Rick](https://discourse.julialang.org/u/Rick)\
**Post date:** [January 5, 2025, 12:10am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438/1 "2025-01-05T00:10:43Z")

</div>

Hi,

I am trying to solve a nonlinear model using JuMP. Part of the constraints come from the gradient of a nonlinear function. I am trying to compute it using `ForwardDiff`. As such, I need to define an operator and then pass it as a constraint (from the post [@constraint with ForwardDiff.gradient](https://discourse.julialang.org/t/constraint-with-forwarddiff-gradient/104949)).

But I am aware that `@operator` only allows for scalar-valued outputs. I saw from the documentation ([User-defined operators with vector outputs · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/)) that we need to define one operator for each component of the vector. E.g., it is done as follows:

```julia
@operator(model, op_f_1, 2, f[1])
@operator(model, op_f_2, 2, f[2])
@operator(model, op_f_3, 2, f[3])

```

Is there a way to define it using a loop or some other way so I do not need to write an explicit operator line per component? I have a relatively large model, so I wonder if there is a better way than copy pasting and changing the index.

Thanks!

---

<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:** [January 5, 2025, 1:34am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438/2 "2025-01-05T01:34:47Z")

</div>

You can add a user-defined operator without the macro: [Nonlinear Modeling · JuMP](https://jump.dev/JuMP.jl/stable/manual/nonlinear/#Add-an-operator-without-macros)

But there’s probably a better way of doing what you’re trying to achieve.

Can you share a minimal working example of the constraint? Where does the gradient appear?

I assume you want to write something like:

```julia
@constraint(model, ∇f(x)' * d == 0)

```

Instead of creating an operator for `∇f`, do instead something like:

```jjulia
my_operator(x...) = ∇f(x)' * d
@operator(model, op_my_operator, n, my_operator)
@constraint(model, op_my_operator(x...) == 0)

```

---

<div class="post-metadata">

**Author:** ![Rick](https://avatars.discourse-cdn.com/v4/letter/r/9f8e36/32.png) [@Rick](https://discourse.julialang.org/u/Rick)\
**Post date:** [January 5, 2025, 1:57am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438/3 "2025-01-05T01:57:46Z")

</div>

Good idea! Here is a MWE of what I intend to do.

I want to compute

\max\_{y\_1, y\_2} \ y\_1 \quad \ \text{s.t.} \quad \nabla f(y\_1, y\_2) = (.5y\_1y\_2, .02 y\_2^2),

where f(y\_1, y\_2) = y\_1^2 + 2y\_1y\_2 + y\_2.

I can code it up as follows:

```julia

using KNITRO, JuMP, ForwardDiff

# My multivariate function
f(x) = x[1]^2 + 2*x[1]*x[2] + x[2]

# Gradient
my_gradient(x) = ForwardDiff.gradient(f,x)
my_gradient_tmp(_x) = my_gradient(_x)

# JuMP optimization
model = Model(KNITRO.Optimizer)
@variable(model, y[1:2])
@operator(model, f1, 2, (x...) -> my_gradient_tmp(collect(x))[1])
@operator(model, f2, 2, (x...) -> my_gradient_tmp(collect(x))[2])

@constraint(model, f1(y...) == .5*y[1]*y[2])
@constraint(model, f2(y...) == .2*y[2]^2)

@objective(model, Max, y[1])
optimize!(model)
objective_value(model)

```

In terms of the above MWE, my question is that I would love to not having to write f1, f2, f3, etc., separately as I want to set the gradient (vector) to be equal to another vector-valued function.

Let me know if there is anything unclear above. Thanks!!

---

<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:** [January 5, 2025, 4:21am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438/4 "2025-01-05T04:21:39Z")

</div>

I think you actually want to do something like this:

```julia
using JuMP
import ForwardDiff
import Ipopt

f(x) = x[1]^2 + 2*x[1]*x[2] + x[2]

function partial(f::Function, i::Int)
    function (x::Real...)
        y = collect(Real, x)
        return ForwardDiff.derivative(z -> (y[i] = z; f(y)), x[i])
    end
end

model = Model(Ipopt.Optimizer)
@variable(model, y[1:2])
rhs = @expression(model, [0.5 * y[1] * y[2], 0.2 * y[2]^2])
for i in 1:2
    name_i = Symbol("op_my_gradient_$i")
    op = add_nonlinear_operator(model, 2, partial(f, i); name = name_i)
    @constraint(model, op(y...) == rhs[i])
end
@objective(model, Max, y[1])
optimize!(model)
@assert is_solved_and_feasible(model)
value.(y)

```

---

<div class="post-metadata">

**Author:** ![Rick](https://avatars.discourse-cdn.com/v4/letter/r/9f8e36/32.png) [@Rick](https://discourse.julialang.org/u/Rick)\
**Post date:** [January 5, 2025, 4:43am UTC](https://discourse.julialang.org/t/how-to-define-vector-valued-operator-in-jump/124438/5 "2025-01-05T04:43:07Z")

</div>

I see, this is really great, thanks!!
