# Can I build an expression on dual variables and then query with JuMP.dual?

**URL:** <https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734>\
**Category:** Optimization (Mathematical)\
**Tags:** question, jump\
**Created:** [April 5, 2025, 4:21am UTC](https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734 "2025-04-05T04:21:43Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [April 5, 2025, 4:21am UTC](https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734/1 "2025-04-05T04:21:43Z")

</div>

Consider this simple example

```julia
import JuMP, Gurobi
model = JuMP.Model(Gurobi.Optimizer)
JuMP.@variable(model, x[1:3])
JuMP.@constraint(model, c1, x .<= 2)
JuMP.@constraint(model, c2, x .>= -2)
JuMP.@objective(model, Min, 0.4 * sum(x))
JuMP.optimize!(model); JuMP.assert_is_solved_and_feasible(model)
JuMP.value.(x)
JuMP.dual.(c1)

```

We know that JuMP can manipulate _primal_ variable/expressions flexibly, via `@expression`.  
e.g. before `optimize!`, I can build `@expression(model, expr, -x[3] - x[2])`, then after `optimize!`, I can query with `JuMP.value(expr)`. Note that building `expr` is one-off, if we don’t have this functionality, we have to write the cumbersome `-value(x[3]) - value(x[2])` every time after an `optimize!`.

Now the question is

- can I also enjoy the similar convenience on **dual** variables?

One thing that I don’t fathom is, as long as I can call `JuMP.dual(c1[1])`, `c1[1]` should be deemed a dual variable? But it turns out to be a `JuMP.ConstraintRef`. This implies that we can also call `JuMP.value(c1[1])` properly after `optimize!`, which is somewhat unusual. (Are there really people _intend to_ do this, is it often, I don’t quite understand the point of this query)

The current behavior is that, we can only get raw material from `JuMP.dual` after `optimize!`, e.g. I can do

```julia
optimize!(model)
c1v = -1 * JuMP.dual.(c1) # to make the resulting value positive
c2v = JuMP.dual.(c2)
cv = [c1v; c2v] # store them in a compact array to facilitate carrying

```

The hope is that Can we do this before `optimize!`? Something like

```julia
JuMP.@dual_expression(model, cv, [-c1; c2]) # setting this is one-off
optimize!(model) # we may do this multiple times
JuMP.dual.(cv) # this style is concise and desirable

```

In JuMP v1.25, we cannot yet.

---

<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:** [April 5, 2025, 4:28am UTC](https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734/2 "2025-04-05T04:28:59Z")

</div>

> can I also enjoy the similar convenience on **dual** variables

No. JuMP does not have an explicit representation of dual variables.

> `c1[1]` should be deemed a dual variable

No. `c1` is a reference to the constraint. A constraint has both a primal value (the value of `f(x)` from `f(x) in set`) and a dual value.

> The hope is that Can we do this before `optimize!`? Something like `JuMP.@dual_expression(model, cv, [-c1; c2])`

This is not possible. And I don’t particularly want to add this.

Among the many reasons, JuMP’s primal variables are scalars, and we use Julia collections to construct `Vector`, `Matrix` of them, etc. Constraints (and their primal/dual values) have a shape. So constructing expression of them is non-trivial.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [April 5, 2025, 6:51am UTC](https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734/3 "2025-04-05T06:51:47Z")

</div>

> [@odow](#):
>
> A constraint has both a primal value (the value of `f(x)` from `f(x) in set`)

Sounds unusual. I thought it would be sufficient to have `value` of expressions after `optimize!`.  
Since constraints are built on top of expressions.

> [@odow](#):
>
> we use Julia collections to construct `Vector`, `Matrix` of them, etc

Does this mean that it is superfluous to write `@expression(model, c, [c1; c2])`, since we can write `c = [c1; c2]` directly?

Although JuMP has concise grammar, e.g. with the help of macros, it is not a bad idea to use Julia’s primitive grammar? e.g. adding that sparse constraint yesterday where we employ 4 for-loops.

---

<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:** [April 5, 2025, 8:53pm UTC](https://discourse.julialang.org/t/can-i-build-an-expression-on-dual-variables-and-then-query-with-jump-dual/127734/4 "2025-04-05T20:53:14Z")

</div>

Since you’ve been asking a number of “why does JuMP do this” type questions recently, you might find it interesting to do some background reading on why we chose the current design.

On the `f(x) in set` design, read: [[2002.03447] MathOptInterface: a data structure for mathematical optimization problems](https://arxiv.org/abs/2002.03447)

For the macro design, watch this part of Miles’ 2017 JuMP-dev talk: [https://youtu.be/JaA302TfI7I?feature=shared&t=366](https://youtu.be/JaA302TfI7I?feature=shared&t=366) and read [Performance tips · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/performance_tips/#Use-macros-to-build-expressions)

There’s also this: [[2206.03866] JuMP 1.0: Recent improvements to a modeling language for mathematical optimization](https://arxiv.org/abs/2206.03866)

You can also use `@macroexpand` to see the code that JuMP is actually running:

```Julia
julia> using JuMP

julia> begin
           model = Model()
           @variable(model, x[1:3])
           @constraint(model, c1, x .<= 2)
           @constraint(model, c2, x .>= -2)
           @macroexpand @expression(model, c, [c1; c2])
       end
quote
    #= REPL[52]:6 =#
    JuMP._valid_model(model, :model)
    begin
        #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:407 =#
        JuMP._error_if_cannot_register(model, :c)
        #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:408 =#
        c = (model[:c] = begin
                    #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:399 =#
                    let model = model
                        #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:400 =#
                        begin
                            #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros/@expression.jl:86 =#
                            begin
                                #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:264 =#
                                var"#570###397" = begin
                                        #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:374 =#
                                        let
                                            #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:375 =#
                                            begin
                                                #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:371 =#
                                            end
                                            #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:376 =#
                                            [c1; c2]
                                        end
                                    end
                                #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros.jl:265 =#
                                var"#571###398" = (JuMP.flatten!)((MutableArithmetics).copy_if_mutable(var"#570###397"))
                            end
                            #= /Users/oscar/.julia/packages/JuMP/RGIK3/src/macros/@expression.jl:89 =#
                            JuMP._replace_zero(model, var"#571###398")
                        end
                    end
                end)
    end
end

```

Strip away the fluff and you’ll find

```julia
JuMP._valid_model(model, :model)
JuMP._error_if_cannot_register(model, :c)
c = model[:c] = let model = model
    var"#570###397" = [c1; c2]
    var"#571###398" = (JuMP.flatten!)((MutableArithmetics).copy_if_mutable(var"#570###397"))
    JuMP._replace_zero(model, var"#571###398")
end

```

So it is exactly `c = [c1; c2]`, plus some extra stuff.
