From bd8237bc2c6cffd231ee4dda122188cd7771b68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 21 Apr 2026 17:21:52 +0200 Subject: [PATCH 1/2] Fix dualize for arbitrary coefficient --- ext/DualizationJuMPExt/DualizationJuMPExt.jl | 48 ++++++++++---------- test/Tests/test_JuMP_dualize.jl | 17 +++++++ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/ext/DualizationJuMPExt/DualizationJuMPExt.jl b/ext/DualizationJuMPExt/DualizationJuMPExt.jl index a42ce06..733c9cc 100644 --- a/ext/DualizationJuMPExt/DualizationJuMPExt.jl +++ b/ext/DualizationJuMPExt/DualizationJuMPExt.jl @@ -10,16 +10,16 @@ import JuMP import MathOptInterface as MOI function Dualization.dualize( - model::JuMP.Model, + model::JuMP.GenericModel{T}, optimizer_constructor = nothing; kwargs..., -) +) where {T} mode = JuMP.mode(model) if mode != JuMP.AUTOMATIC error("Dualization does not support solvers in $(mode) mode") end - dual_model = JuMP.Model() - dual_problem = Dualization.DualProblem(JuMP.backend(dual_model)) + dual_model = JuMP.GenericModel{T}() + dual_problem = Dualization.DualProblem{T}(JuMP.backend(dual_model)) Dualization.dualize(JuMP.backend(model), dual_problem; kwargs...) _fill_obj_dict_with_variables!(dual_model) _fill_obj_dict_with_constraints!(dual_model) @@ -31,7 +31,7 @@ function Dualization.dualize( return dual_model end -function _fill_obj_dict_with_variables!(model::JuMP.Model) +function _fill_obj_dict_with_variables!(model::JuMP.GenericModel) list = MOI.get(model, MOI.ListOfVariableAttributesSet()) if !(MOI.VariableName() in list) return @@ -39,13 +39,13 @@ function _fill_obj_dict_with_variables!(model::JuMP.Model) for vi in MOI.get(model, MOI.ListOfVariableIndices()) name = MOI.get(JuMP.backend(model), MOI.VariableName(), vi) if !isempty(name) - model[Symbol(name)] = JuMP.VariableRef(model, vi) + model[Symbol(name)] = JuMP.GenericVariableRef(model, vi) end end return end -function _fill_obj_dict_with_constraints!(model::JuMP.Model) +function _fill_obj_dict_with_constraints!(model::JuMP.GenericModel) con_types = MOI.get(model, MOI.ListOfConstraintTypesPresent()) for (F, S) in con_types _fill_obj_dict_with_constraints!(model, F, S) @@ -54,7 +54,7 @@ function _fill_obj_dict_with_constraints!(model::JuMP.Model) end function _fill_obj_dict_with_constraints!( - model::JuMP.Model, + model::JuMP.GenericModel, ::Type{F}, ::Type{S}, ) where {F,S} @@ -71,13 +71,13 @@ function _fill_obj_dict_with_constraints!( return end -function _get_primal_dual_map(model::JuMP.Model) +function _get_primal_dual_map(model::JuMP.GenericModel) return model.ext[:_Dualization_jl_PrimalDualMap] end function Dualization._get_dual_constraint( dual_model, - primal_ref::JuMP.VariableRef, + primal_ref::JuMP.GenericVariableRef, ) map = _get_primal_dual_map(dual_model) moi_primal_vi = JuMP.index(primal_ref) @@ -91,8 +91,8 @@ function Dualization._get_dual_constraint( end function Dualization._get_primal_constraint( - dual_model::JuMP.Model, - primal_vi::JuMP.VariableRef, + dual_model::JuMP.GenericModel, + primal_vi::JuMP.GenericVariableRef, ) primal_model = JuMP.owner_model(primal_vi) map = _get_primal_dual_map(dual_model) @@ -105,9 +105,9 @@ function Dualization._get_primal_constraint( end function Dualization._get_dual_variables( - dual_model::JuMP.Model, + dual_model::JuMP.GenericModel{T}, primal_ref::JuMP.ConstraintRef, -) +) where {T} map = _get_primal_dual_map(dual_model) moi_primal_ci = JuMP.index(primal_ref) moi_dual_vis = Dualization._get_dual_variables(map, moi_primal_ci) @@ -115,12 +115,12 @@ function Dualization._get_dual_variables( # main constraint of a constrained variable return nothing end - return [JuMP.VariableRef(dual_model, vi) for vi in moi_dual_vis] + return [JuMP.GenericVariableRef{T}(dual_model, vi) for vi in moi_dual_vis] end # this is a constrained variable constraint function Dualization._get_dual_constraint( - dual_model::JuMP.Model, + dual_model::JuMP.GenericModel, primal_ref::JuMP.ConstraintRef, ) map = _get_primal_dual_map(dual_model) @@ -136,24 +136,24 @@ function Dualization._get_dual_constraint( end function Dualization._get_dual_parameter( - dual_model::JuMP.Model, - primal_ref::JuMP.VariableRef, -) + dual_model::JuMP.GenericModel{T}, + primal_ref::JuMP.GenericVariableRef, +) where {T} map = _get_primal_dual_map(dual_model) moi_primal_vi = JuMP.index(primal_ref) moi_dual_vi = Dualization._get_dual_parameter(map, moi_primal_vi) # the above line might error - return JuMP.VariableRef(dual_model, moi_dual_vi) + return JuMP.GenericVariableRef{T}(dual_model, moi_dual_vi) end function Dualization._get_dual_slack_variable( - dual_model::JuMP.Model, - primal_ref::JuMP.VariableRef, -) + dual_model::JuMP.GenericModel{T}, + primal_ref::JuMP.GenericVariableRef, +) where {T} map = _get_primal_dual_map(dual_model) moi_primal_vi = JuMP.index(primal_ref) moi_dual_vi = Dualization._get_dual_slack_variable(map, moi_primal_vi) - return JuMP.VariableRef(dual_model, moi_dual_vi) + return JuMP.GenericVariableRef{T}(dual_model, moi_dual_vi) end end # module DualizationJuMPExt diff --git a/test/Tests/test_JuMP_dualize.jl b/test/Tests/test_JuMP_dualize.jl index 5118256..a92620d 100644 --- a/test/Tests/test_JuMP_dualize.jl +++ b/test/Tests/test_JuMP_dualize.jl @@ -128,6 +128,23 @@ end con = Dualization._get_dual_constraint(dual_model, cv) @test con isa ConstraintRef end + @testset "GenericModel{$T}" for T in (Float32, BigFloat) + model = JuMP.GenericModel{T}() + JuMP.@variable(model, x >= zero(T)) + JuMP.@constraint(model, c, x <= one(T) + one(T)) + JuMP.@objective(model, Max, T(2) * x + one(T)) + dual_model = Dualization.dualize( + model; + dual_names = DualNames("dual_", "dual_"), + ) + @test dual_model isa JuMP.GenericModel{T} + @test num_variables(dual_model) == 2 + con = Dualization._get_dual_constraint(dual_model, x) + @test con[1] isa ConstraintRef + var = Dualization._get_dual_variables(dual_model, c) + @test length(var) == 1 + @test var[] isa JuMP.GenericVariableRef{T} + end @testset "JuMP parametric quadratic" begin model = Model() @variable(model, x) From 38dd7bd50a8ce86a4164926fadc174dbd5a3c077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 21 Apr 2026 17:23:09 +0200 Subject: [PATCH 2/2] Fix --- test/Tests/test_JuMP_dualize.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Tests/test_JuMP_dualize.jl b/test/Tests/test_JuMP_dualize.jl index a92620d..37162b5 100644 --- a/test/Tests/test_JuMP_dualize.jl +++ b/test/Tests/test_JuMP_dualize.jl @@ -136,6 +136,7 @@ end dual_model = Dualization.dualize( model; dual_names = DualNames("dual_", "dual_"), + consider_constrained_variables = false, ) @test dual_model isa JuMP.GenericModel{T} @test num_variables(dual_model) == 2