From fef88eb33123364c01cbea241ee097644ce31da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 10:03:41 +0100 Subject: [PATCH 1/8] Add support for starting values --- src/MOI_wrapper.jl | 127 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 17 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 1b0a763..14ed747 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -319,9 +319,57 @@ function _get_at_index( return _get(optimizer, attr, ci_primal, ci_dual)[idx] end +_minus(::Nothing) = nothing +_minus(x) = -x + +function _dual_attribute(attr::MOI.VariablePrimal) + return MOI.ConstraintDual(attr.result_index) +end + +function _dual_attribute(attr::MOI.VariablePrimalStart) + return MOI.ConstraintDualStart() +end + +function _dual_attribute(attr::MOI.ConstraintDual) + return MOI.ConstraintPrimal(attr.result_index) +end + +function _dual_attribute(attr::MOI.ConstraintDualStart) + return MOI.ConstraintPrimalStart() +end + +function _variable_dual_attribute(attr::MOI.ConstraintDual) + return MOI.VariablePrimal(attr.result_index) +end + +function _variable_dual_attribute(attr::MOI.ConstraintDualStart) + return MOI.VariablePrimalStart() +end + + +function MOI.set( + optimizer::DualOptimizer, + ::MOI.VariablePrimalStart, + vi::MOI.VariableIndex, + value, +) + primal_dual_map = optimizer.dual_problem.primal_dual_map + if vi in keys(primal_dual_map.constrained_var_idx) + error("Setting starting value for variables constrained at creation is not supported yet") + else + MOI.set( + optimizer.dual_problem.dual_model, + _dual_attribute(attr), + get_ci_dual_problem(optimizer, vi), + _minus(value), + ) + end + return +end + function MOI.get( optimizer::DualOptimizer, - ::MOI.VariablePrimal, + ::Union{MOI.VariablePrimal,MOI.VariablePrimalStart}, vi::MOI.VariableIndex, ) primal_dual_map = optimizer.dual_problem.primal_dual_map @@ -330,7 +378,7 @@ function MOI.get( ci_dual = primal_dual_map.constrained_var_dual[ci_primal] return _get_at_index( optimizer, - MOI.ConstraintDual(), + _dual_attribute(attr), ci_primal, ci_dual, idx, @@ -338,15 +386,35 @@ function MOI.get( else return -MOI.get( optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), + _dual_attribute(attr), get_ci_dual_problem(optimizer, vi), ) end end +function MOI.set( + optimizer::DualOptimizer, + attr::MOI.ConstraintDualStart, + ci::MOI.ConstraintIndex, + value, +) + primal_dual_map = optimizer.dual_problem.primal_dual_map + if ci in keys(primal_dual_map.constrained_var_dual) + error("Setting starting value for variables constrained at creation is not supported yet") + else + MOI.set( + optimizer.dual_problem.dual_model, + _variable_dual_attribute(attr), + get_vi_dual_problem(optimizer, ci), + value, + ) + end + return +end + function MOI.get( optimizer::DualOptimizer, - attr::MOI.ConstraintDual, + attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map @@ -358,7 +426,7 @@ function MOI.get( ) do vi return MOI.get( optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), + _variable_dual_attribute(attr), vi, ) end @@ -370,13 +438,13 @@ function MOI.get( ) return MOI.get( optimizer.dual_problem.dual_model, - MOI.ConstraintPrimal(), + _dual_attribute(attr), ci_dual, ) - MOI.constant(set) else return MOI.get( optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), + _variable_dual_attribute(attr), get_vi_dual_problem(optimizer, ci), ) end @@ -384,7 +452,7 @@ end function MOI.get( optimizer::DualOptimizer, - ::MOI.ConstraintDual, + attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map @@ -396,35 +464,60 @@ function MOI.get( ) do vi return MOI.get( optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), + _variable_dual_attribute(attr), vi, ) end end return MOI.get( optimizer.dual_problem.dual_model, - MOI.ConstraintPrimal(), + _dual_attribute(attr), primal_dual_map.constrained_var_dual[ci], ) else return MOI.get.( optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), + _variable_dual_attribute(attr), get_vis_dual_problem(optimizer, ci), ) end end +function MOI.set( + optimizer::DualOptimizer, + attr::MOI.ConstraintPrimalStart, + ci::MOI.ConstraintIndex{F}, + value, +) where {F<:MOI.AbstractScalarFunction} + primal_dual_map = optimizer.dual_problem.primal_dual_map + if ci in keys(primal_dual_map.constrained_var_dual) + error("Setting starting value for variables constrained at creation is not supported yet") + elseif haskey(primal_dual_map.primal_con_dual_con, ci) + # If it has no key then there is no dual constraint + ci_dual_problem = get_ci_dual_problem(optimizer, ci) + if !isnothing(value) && (F <: MOI.AbstractScalarFunction) + value -= get_primal_ci_constant(optimizer, ci) + end + MOI.set( + optimizer.dual_problem.dual_model, + _dual_attribute(attr), + ci_dual_problem, + value, + ) + end + return +end + function MOI.get( optimizer::DualOptimizer, - ::MOI.ConstraintPrimal, + attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map if ci in keys(primal_dual_map.constrained_var_dual) return _get( optimizer, - MOI.ConstraintDual(), + _dual_attribute(attr), ci, primal_dual_map.constrained_var_dual[ci], ) @@ -437,7 +530,7 @@ function MOI.get( ci_dual_problem = get_ci_dual_problem(optimizer, ci) return MOI.get( optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), + _dual_attribute(attr), ci_dual_problem, ) - primal_ci_constant end @@ -445,14 +538,14 @@ end function MOI.get( optimizer::DualOptimizer{T}, - ::MOI.ConstraintPrimal, + attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map if ci in keys(primal_dual_map.constrained_var_dual) return _get( optimizer, - MOI.ConstraintDual(), + _dual_attribute(attr), ci, primal_dual_map.constrained_var_dual[ci], ) @@ -466,7 +559,7 @@ function MOI.get( ci_dual_problem = get_ci_dual_problem(optimizer, ci) return MOI.get( optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), + _dual_attribute(attr), ci_dual_problem, ) end From 895b7310b6fb0b15643fc734729d31f29ff62243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 10:13:30 +0100 Subject: [PATCH 2/8] Fix format --- src/MOI_wrapper.jl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 14ed747..28ba21c 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -346,7 +346,6 @@ function _variable_dual_attribute(attr::MOI.ConstraintDualStart) return MOI.VariablePrimalStart() end - function MOI.set( optimizer::DualOptimizer, ::MOI.VariablePrimalStart, @@ -355,7 +354,9 @@ function MOI.set( ) primal_dual_map = optimizer.dual_problem.primal_dual_map if vi in keys(primal_dual_map.constrained_var_idx) - error("Setting starting value for variables constrained at creation is not supported yet") + error( + "Setting starting value for variables constrained at creation is not supported yet", + ) else MOI.set( optimizer.dual_problem.dual_model, @@ -400,7 +401,9 @@ function MOI.set( ) primal_dual_map = optimizer.dual_problem.primal_dual_map if ci in keys(primal_dual_map.constrained_var_dual) - error("Setting starting value for variables constrained at creation is not supported yet") + error( + "Setting starting value for variables constrained at creation is not supported yet", + ) else MOI.set( optimizer.dual_problem.dual_model, @@ -491,7 +494,9 @@ function MOI.set( ) where {F<:MOI.AbstractScalarFunction} primal_dual_map = optimizer.dual_problem.primal_dual_map if ci in keys(primal_dual_map.constrained_var_dual) - error("Setting starting value for variables constrained at creation is not supported yet") + error( + "Setting starting value for variables constrained at creation is not supported yet", + ) elseif haskey(primal_dual_map.primal_con_dual_con, ci) # If it has no key then there is no dual constraint ci_dual_problem = get_ci_dual_problem(optimizer, ci) From b5cdefd38fbfce43546584939c13a987ca3606f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 10:15:44 +0100 Subject: [PATCH 3/8] Add supports --- src/MOI_wrapper.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 28ba21c..0514b98 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -346,6 +346,8 @@ function _variable_dual_attribute(attr::MOI.ConstraintDualStart) return MOI.VariablePrimalStart() end +MOI.supports(::DualOptimizer, ::MOI.VariablePrimalStart, ::Type{MOI.VariableIndex}) = true + function MOI.set( optimizer::DualOptimizer, ::MOI.VariablePrimalStart, @@ -393,6 +395,14 @@ function MOI.get( end end +function MOI.supports( + ::DualOptimizer, + ::Union{MOI.ConstraintDualStart,MOI.ConstraintPrimalStart}, + ::Type{<:MOI.ConstraintIndex}, +) + return true +end + function MOI.set( optimizer::DualOptimizer, attr::MOI.ConstraintDualStart, From eef957f6ef3b3e0735a317b1c29633c113bba4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 10:32:18 +0100 Subject: [PATCH 4/8] Fixes --- src/MOI_wrapper.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 0514b98..ea1d208 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -322,11 +322,11 @@ end _minus(::Nothing) = nothing _minus(x) = -x -function _dual_attribute(attr::MOI.VariablePrimal) +function _dual_attribute(attr::Union{MOI.VariablePrimal,MOI.ConstraintPrimal}) return MOI.ConstraintDual(attr.result_index) end -function _dual_attribute(attr::MOI.VariablePrimalStart) +function _dual_attribute(::Union{MOI.VariablePrimalStart,MOI.ConstraintPrimalStart}) return MOI.ConstraintDualStart() end @@ -334,7 +334,7 @@ function _dual_attribute(attr::MOI.ConstraintDual) return MOI.ConstraintPrimal(attr.result_index) end -function _dual_attribute(attr::MOI.ConstraintDualStart) +function _dual_attribute(::MOI.ConstraintDualStart) return MOI.ConstraintPrimalStart() end @@ -342,7 +342,7 @@ function _variable_dual_attribute(attr::MOI.ConstraintDual) return MOI.VariablePrimal(attr.result_index) end -function _variable_dual_attribute(attr::MOI.ConstraintDualStart) +function _variable_dual_attribute(::MOI.ConstraintDualStart) return MOI.VariablePrimalStart() end @@ -350,7 +350,7 @@ MOI.supports(::DualOptimizer, ::MOI.VariablePrimalStart, ::Type{MOI.VariableInde function MOI.set( optimizer::DualOptimizer, - ::MOI.VariablePrimalStart, + attr::MOI.VariablePrimalStart, vi::MOI.VariableIndex, value, ) @@ -372,7 +372,7 @@ end function MOI.get( optimizer::DualOptimizer, - ::Union{MOI.VariablePrimal,MOI.VariablePrimalStart}, + attr::Union{MOI.VariablePrimal,MOI.VariablePrimalStart}, vi::MOI.VariableIndex, ) primal_dual_map = optimizer.dual_problem.primal_dual_map From 8dbc07daabfbe93e16ad0f651d2d16e02c75e1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 10:32:23 +0100 Subject: [PATCH 5/8] Fix format --- src/MOI_wrapper.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index ea1d208..19f7aa6 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -326,7 +326,9 @@ function _dual_attribute(attr::Union{MOI.VariablePrimal,MOI.ConstraintPrimal}) return MOI.ConstraintDual(attr.result_index) end -function _dual_attribute(::Union{MOI.VariablePrimalStart,MOI.ConstraintPrimalStart}) +function _dual_attribute( + ::Union{MOI.VariablePrimalStart,MOI.ConstraintPrimalStart}, +) return MOI.ConstraintDualStart() end @@ -346,7 +348,13 @@ function _variable_dual_attribute(::MOI.ConstraintDualStart) return MOI.VariablePrimalStart() end -MOI.supports(::DualOptimizer, ::MOI.VariablePrimalStart, ::Type{MOI.VariableIndex}) = true +function MOI.supports( + ::DualOptimizer, + ::MOI.VariablePrimalStart, + ::Type{MOI.VariableIndex}, +) + return true +end function MOI.set( optimizer::DualOptimizer, @@ -396,9 +404,9 @@ function MOI.get( end function MOI.supports( - ::DualOptimizer, - ::Union{MOI.ConstraintDualStart,MOI.ConstraintPrimalStart}, - ::Type{<:MOI.ConstraintIndex}, + ::DualOptimizer, + ::Union{MOI.ConstraintDualStart,MOI.ConstraintPrimalStart}, + ::Type{<:MOI.ConstraintIndex}, ) return true end From 2438c319b2f08e4950305c76600a54df108af6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 Nov 2023 21:57:54 +0100 Subject: [PATCH 6/8] Improve supports --- src/MOI_wrapper.jl | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 19f7aa6..25d4aac 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -404,11 +404,15 @@ function MOI.get( end function MOI.supports( - ::DualOptimizer, - ::Union{MOI.ConstraintDualStart,MOI.ConstraintPrimalStart}, + optimizer::DualOptimizer, + attr::MOI.ConstraintDualStart, ::Type{<:MOI.ConstraintIndex}, ) - return true + return MOI.supports( + optimizer.dual_problem.dual_model, + _variable_dual_attribute(attr), + MOI.VariableIndex, + ) end function MOI.set( @@ -504,6 +508,18 @@ function MOI.get( end end +function MOI.supports( + ::DualOptimizer, + attr::MOI.ConstraintPrimalStart, + C::Type{<:MOI.ConstraintIndex}, +) + return MOI.supports( + optimizer.dual_problem.dual_model, + _dual_attribute(attr), + C, + ) +end + function MOI.set( optimizer::DualOptimizer, attr::MOI.ConstraintPrimalStart, From d4b6137b6295ac8022490a8e34e228300b53c919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 13 Nov 2023 23:14:00 +0100 Subject: [PATCH 7/8] Add tests --- src/MOI_wrapper.jl | 4 +++- test/Tests/test_MOI_wrapper.jl | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 25d4aac..a9221eb 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -206,9 +206,11 @@ end function MOI.copy_to(dest::DualOptimizer, src::MOI.ModelLike) dualize(src, dest.dual_problem) idx_map = MOI.Utilities.IndexMap() - for vi in MOI.get(src, MOI.ListOfVariableIndices()) + vis_src = MOI.get(src, MOI.ListOfVariableIndices()) + for vi in vis_src setindex!(idx_map, vi, vi) end + MOI.Utilities.pass_attributes(dest, src, idx_map, vis_src) for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) for con in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) setindex!(idx_map, con, con) diff --git a/test/Tests/test_MOI_wrapper.jl b/test/Tests/test_MOI_wrapper.jl index c8741a4..cfa86fc 100644 --- a/test/Tests/test_MOI_wrapper.jl +++ b/test/Tests/test_MOI_wrapper.jl @@ -127,4 +127,18 @@ DualOptimizer{Float32,Caching_OptimizerType} end end + + @testset "Start" begin + model = MOI.Utilities.UniversalFallback(TestModel{Float64}()) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, 2.0 * x, MOI.GreaterThan(0.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.VariablePrimalStart(), x, 1.0) + MOI.set(model, MOI.ConstraintPrimalStart(), c, 3.0) + MOI.set(model, MOI.ConstraintDualStart(), c, 4.0) + dual_problem = Dualization.DualProblem{Float64}(TestModel{Float64}()) + OptimizerType = typeof(dual_problem.dual_model) + dual = DualOptimizer{Float64,OptimizerType}(dual_problem) + index_map = MOI.copy_to(dual, model) + end end From 7bd05eef1a0831f2d60f910535c04a8e9029ad49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 10 Dec 2023 23:13:58 +0100 Subject: [PATCH 8/8] Use result_index --- src/MOI_wrapper.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index a9221eb..8ae6c9b 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -625,20 +625,20 @@ function dual_status(term::MOI.TerminationStatusCode) return term end -function MOI.get(optimizer::DualOptimizer, ::MOI.ObjectiveValue) - return MOI.get(optimizer.dual_problem.dual_model, MOI.DualObjectiveValue()) +function MOI.get(optimizer::DualOptimizer, attr::MOI.ObjectiveValue) + return MOI.get(optimizer.dual_problem.dual_model, MOI.DualObjectiveValue(attr.result_index)) end -function MOI.get(optimizer::DualOptimizer, ::MOI.DualObjectiveValue) - return MOI.get(optimizer.dual_problem.dual_model, MOI.ObjectiveValue()) +function MOI.get(optimizer::DualOptimizer, attr::MOI.DualObjectiveValue) + return MOI.get(optimizer.dual_problem.dual_model, MOI.ObjectiveValue(attr.result_index)) end -function MOI.get(optimizer::DualOptimizer, ::MOI.PrimalStatus) - return MOI.get(optimizer.dual_problem.dual_model, MOI.DualStatus()) +function MOI.get(optimizer::DualOptimizer, attr::MOI.PrimalStatus) + return MOI.get(optimizer.dual_problem.dual_model, MOI.DualStatus(attr.result_index)) end -function MOI.get(optimizer::DualOptimizer, ::MOI.DualStatus) - return MOI.get(optimizer.dual_problem.dual_model, MOI.PrimalStatus()) +function MOI.get(optimizer::DualOptimizer, attr::MOI.DualStatus) + return MOI.get(optimizer.dual_problem.dual_model, MOI.PrimalStatus(attr.result_index)) end function MOI.set(