Find primal solution from dual one and vice-versa?
Sometimes, I would find it useful to have a way to get the primal solution from the dual one (and vice-versa), especially when I don't get this solution from a solver (read from a file, got from a heuristic, etc.). For instance, just solving the complementary slackness equations?
I would say that this is low priority: nice to have, but not necessarily soon.
Just to plus one this, this would be super valuable to me. The primary issue seems to be the naming of the new dual constraints, as when variables are in containers, they seem to create dual constraint names which cannot be accessed through the constraint name function. For instance, for the following toy model:
model = Model(Gurobi.Optimizer)
@variable(model, x[1:5])
@constraints(model, begin
c1, x[1] >= 2
c2, x .>=0
c3, x[2] >= 4
end)
@objective(model, Min, sum(x))
when you run dualize, you get the following:
print(dual_model)
Max 4 dualc3_1 + 2 dualc1_1
Subject to
x[1] : dualc1_1 + dualc2_1 == 1
x[2] : dualc2_1 + dualc3_1 == 1
x[3] : dualc2_1 == 1
x[4] : dualc2_1 == 1
x[5] : dualc2_1 == 1
dualc1_1 >= 0
dualc2_1 >= 0
dualc2_1 >= 0
dualc2_1 >= 0
dualc2_1 >= 0
dualc2_1 >= 0
dualc3_1 >= 0
This has duals, but when you try to access them, you get:
dual(dual_model[:x[1]])
ERROR: MethodError: no method matching getindex(::Symbol, ::Int64)
Stacktrace:
[1] top-level scope
@ REPL[55]:1
Seems like something that could be fixed through dual constraint naming conventions? i.e. x_1 instead of x[1].
Thank you for your help!
Can I ask why are you using Dualization.jl with Gurobi?
I assume you need constraint_by_name(dual_model, "x[1]") rather than dual_model[:x[1]].
You need
julia> using JuMP, Dualization, Gurobi
julia> model = Model()
A JuMP Model
├ solver: none
├ objective_sense: FEASIBILITY_SENSE
├ num_variables: 0
├ num_constraints: 0
└ Names registered in the model: none
julia> @variable(model, x[1:5])
5-element Vector{VariableRef}:
x[1]
x[2]
x[3]
x[4]
x[5]
julia> @constraints(model, begin
c1, x[1] >= 2
c2, x .>=0
c3, x[2] >= 4
end)
(c1 : x[1] ≥ 2, ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, ScalarShape}[c2 : x[1] ≥ 0, c2 : x[2] ≥ 0, c2 : x[3] ≥ 0, c2 : x[4] ≥ 0, c2 : x[5] ≥ 0], c3 : x[2] ≥ 4)
julia> @objective(model, Min, sum(x))
x[1] + x[2] + x[3] + x[4] + x[5]
julia> dual_model = dualize(model, Gurobi.Optimizer; dual_names = DualNames("dual", ""))
Set parameter LicenseID to value 890341
A JuMP Model
├ solver: Gurobi
├ objective_sense: MAX_SENSE
│ └ objective_function_type: AffExpr
├ num_variables: 7
├ num_constraints: 12
│ ├ AffExpr in MOI.EqualTo{Float64}: 5
│ └ VariableRef in MOI.GreaterThan{Float64}: 7
└ Names registered in the model
└ :dualc1_1, :dualc2_1, :dualc3_1, :x[1], :x[2], :x[3], :x[4], :x[5]
julia> dual_model[Symbol("x[1]")]
x[1] : dualc1_1 + dualc2_1 = 1
julia> constraint_by_name(dual_model, "x[1]")
x[1] : dualc1_1 + dualc2_1 = 1
Ah fantastic, so just a syntax mistake on my end then. Thank you! I'm using Dualization.jl because I need to repeatedly resolve the dual of a large model while modifying the objective, which seemed much easier with a full JuMP model.
I'm using Dualization.jl because I need to repeatedly resolve the dual of a large model while modifying the objective
Why not modify the right-hand side? If you are solving an LP, there is essentially never a need to use Dualization.jl.
Perhaps post on https://discourse.julialang.org/c/domain/opt/13 with a reproducible example of the problem you are trying to solve and we can point you in the right direction.