You’re right that that can be a confusing topic. We should probably add more explanation to the JuMP docs.

A key realization is that:

- The sign convention is arbitrary. I could assume that \lambda was non-positive if I put a - sign in front of the summation
- JuMP uses the duality conventions from conic duality.

These part of the documentation may be helpful:

The way I remember `JuMP.dual`

is:

- the sign of
`JuMP.dual(constraint)`

does not depend on whether we are minimizing or maximizing - the sign of
`JuMP.dual(constraint)`

is non-negative for \ge and non-positive for \le

In more detail:

- the dual of a \le constraint is non-positive, because f(x) \le y is rewritten to f(x) - y \in \mathbb{R}_- (the
`MOI.Nonpositives`

cone), and the dual cone of`MOI.Nonpositives`

is`MOI.Nonpositives`

- the dual of a \ge constraint is non-negative, because f(x) \ge y is rewritten to f(x) - y \in \mathbb{R}_+ (the
`MOI.Nonnegatives`

cone), and the dual cone of`MOI.Nonnegatives`

is`MOI.Nonnegatives`

. - the dual of a = constraint is free, because f(x) = y is rewritten to f(x) - y \in {0} (the
`MOI.Zeros`

cone), and the dual of the`MOI.Zeros`

cone is`MOI.Reals`

Notably, this is the opposite to your statement that `λ is required to be nonnegative`

, so to convert to your assumed Lagragian, you need to add `-`

sign.

`shadow_price`

and `reduced_cost`

are mostly useful for people coming from a background in linear programming. There, `shadow_price`

is the change in the objective value as the constraint is relaxed, and the sign depends on whether we are maximizing or minimizing.