diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 2d1d8135..a6ff81eb 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -209,9 +209,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) @@ -322,9 +324,66 @@ function _get_at_index( return _get(optimizer, attr, ci_primal, ci_dual)[idx] end +_minus(::Nothing) = nothing +_minus(x) = -x + +function _dual_attribute(attr::Union{MOI.VariablePrimal,MOI.ConstraintPrimal}) + return MOI.ConstraintDual(attr.result_index) +end + +function _dual_attribute( + ::Union{MOI.VariablePrimalStart,MOI.ConstraintPrimalStart}, +) + return MOI.ConstraintDualStart() +end + +function _dual_attribute(attr::MOI.ConstraintDual) + return MOI.ConstraintPrimal(attr.result_index) +end + +function _dual_attribute(::MOI.ConstraintDualStart) + return MOI.ConstraintPrimalStart() +end + +function _variable_dual_attribute(attr::MOI.ConstraintDual) + return MOI.VariablePrimal(attr.result_index) +end + +function _variable_dual_attribute(::MOI.ConstraintDualStart) + return MOI.VariablePrimalStart() +end + +function MOI.supports( + ::DualOptimizer, + ::MOI.VariablePrimalStart, + ::Type{MOI.VariableIndex}, +) + return true +end + +function MOI.set( + optimizer::DualOptimizer, + attr::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) + msg = "Setting starting value for variables constrained at creation is not supported yet" + throw(MOI.SetAttributeNotAllowed(attr, msg)) + end + MOI.set( + optimizer.dual_problem.dual_model, + _dual_attribute(attr), + get_ci_dual_problem(optimizer, vi), + _minus(value), + ) + return +end + function MOI.get( optimizer::DualOptimizer, - ::MOI.VariablePrimal, + attr::Union{MOI.VariablePrimal,MOI.VariablePrimalStart}, vi::MOI.VariableIndex, ) primal_dual_map = optimizer.dual_problem.primal_dual_map @@ -333,23 +392,58 @@ 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, ) - else - return -MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - get_ci_dual_problem(optimizer, vi), - ) end + ret = MOI.get( + optimizer.dual_problem.dual_model, + _dual_attribute(attr), + get_ci_dual_problem(optimizer, vi), + ) + if ret === nothing + return ret + end + return -ret +end + +function MOI.supports( + optimizer::DualOptimizer, + attr::MOI.ConstraintDualStart, + ::Type{<:MOI.ConstraintIndex}, +) + return MOI.supports( + optimizer.dual_problem.dual_model, + _variable_dual_attribute(attr), + MOI.VariableIndex, + ) +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) + msg = "Setting starting value for variables constrained at creation is not supported yet" + throw(MOI.SetAttributeNotAllowed(attr, msg)) + end + MOI.set( + optimizer.dual_problem.dual_model, + _variable_dual_attribute(attr), + get_vi_dual_problem(optimizer, ci), + value, + ) + 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 @@ -361,7 +455,7 @@ function MOI.get( ) do vi return MOI.get( optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), + _variable_dual_attribute(attr), vi, ) end @@ -373,13 +467,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 @@ -387,7 +481,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 @@ -399,35 +493,74 @@ 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.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, + 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], ) @@ -440,7 +573,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 @@ -448,14 +581,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], ) @@ -469,7 +602,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 @@ -494,20 +627,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( diff --git a/test/Tests/test_MOI_wrapper.jl b/test/Tests/test_MOI_wrapper.jl index c8741a4a..cfa86fc5 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