From 1ae3793450f209df7986e2c81a4687b27ed9376f Mon Sep 17 00:00:00 2001 From: joaquimg Date: Wed, 16 Apr 2025 20:10:27 -0700 Subject: [PATCH 1/5] Cleanup renames --- src/constrained_variables.jl | 2 +- src/structures.jl | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 7e86cf1..acc9e3b 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -46,7 +46,7 @@ function _select_constrained_variables( # and not a parameter vi -> !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && - !(vi in params), + !(vi in params), f.variables, ) for (i, vi) in enumerate(f.variables) diff --git a/src/structures.jl b/src/structures.jl index 5b5c771..20626ac 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -45,10 +45,6 @@ Main maps: their internal index from 1 to dimension(set) (if vector constraints: VectorOfVariables-in-Set), 1 otherwise (scalar: VariableIndex-in-Set). - note: this set is important to dualization can keep track of what it - decided to define as a constrained variable. - - consider index 0 to highlight scalars - * `primal_convarcon_to_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps the primal constraint index of constrained variables to the dual model's constraint index of the associated dual constraint. This dual @@ -57,21 +53,12 @@ Main maps: are not in this map, as they are not dualized (See primal_convarcon_to_dual_function). - note: from the above two maps, we can get primal_convar_to_dual_con_and_index - * `primal_var_to_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex}`: maps "free" primal variables to their associated dual (equality) constraints. Free variables as opposed to constrained variables. Note that Dualization will select automatically which variables are free and which are constrained. - note: from the above three maps, we can get primal_var_to_dual_con_and_index - - # TODO: idea, for simmetry, keep only - - primal_convarcon_to_dual_con (as is), analogous to primal_con_to_dual_convarcon - - primal_var_to_dual_con_and_index, analogous to primal_con_to_dual_var_vec - -- the second implicitly tracks which vars were decided to be convar's - * `primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: maps primal constraint indices to vectors of dual variable indices. For scalar constraints those vectors will be single element vectors. @@ -79,9 +66,6 @@ Main maps: map. However, `VariableIndex`-in-set and `VectorOfVariables`-in-set might appear in this map if they were not chosen as the main ones. - note: possibly change this to: - primal_con_to_dual_var_vec_and_convarcon - * `primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps regular primal constraints to dual constrained variable. If the primal constraint's set is EqualTo or Zeros, no constraint is added in the dual From c2a4f4531dd61f6ed37182e14bed1ab8aeb57e22 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 22 Apr 2025 15:20:40 -0700 Subject: [PATCH 2/5] Restructure PDMap --- src/MOI_wrapper.jl | 100 +++++++++++++++--------- src/constrained_variables.jl | 29 ++++--- src/dual_equality_constraints.jl | 9 ++- src/dualize.jl | 9 +++ src/structures.jl | 56 ++++++++++++- test/Tests/test_dualize_conic_linear.jl | 53 +++++++++---- 6 files changed, 188 insertions(+), 68 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 504b04e..116988c 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -126,7 +126,7 @@ function _change_constant( model, ci::MOI.ConstraintIndex{<:MOI.ScalarAffineFunction,S}, constant, - idx, + ::Int, ) where {S} MOI.set(model, MOI.ConstraintSet(), ci, S(constant)) return @@ -136,7 +136,7 @@ function _change_constant( model, ci::MOI.ConstraintIndex{<:MOI.VectorAffineFunction}, constant, - idx, + idx::Int, ) func = MOI.get(model, MOI.ConstraintFunction(), ci) constants = copy(func.constant) @@ -159,23 +159,29 @@ function MOI.modify( constant = -constant end vi = obj_change.variable - if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - ci_primal, index = - primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - if ci_dual === NO_CONSTRAINT - return - end - constant = -constant - else - ci_dual = primal_dual_map.primal_var_to_dual_con[vi] - index = 1 + # if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) + # ci_primal, index = + # primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] + # ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] + # if ci_dual === NO_CONSTRAINT + # return + # end + # constant = -constant + # else + # ci_dual = primal_dual_map.primal_var_to_dual_con[vi] + # index = 1 + # end + data = get(primal_dual_map.primal_variable_data, vi, nothing) + if data === nothing + # error + elseif data.dual_constraint == NO_CONSTRAINT + return end _change_constant( optimizer.dual_problem.dual_model, - ci_dual, + data.dual_constraint, constant, - index, + data.primal_constrained_variable_index, ) return end @@ -246,34 +252,58 @@ function MOI.get( optimizer::DualOptimizer{T}, ::MOI.VariablePrimal, vi::MOI.VariableIndex, -) where {T} +)::T where {T} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - ci_primal, idx = - primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - if ci_dual === NO_CONSTRAINT - return zero(T) - elseif ci_dual isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} - return MOI.get( + data = get(primal_dual_map.primal_variable_data, vi, nothing) + if data === nothing + # error + elseif data.dual_constraint == NO_CONSTRAINT + return zero(T) + elseif data.primal_constrained_variable_constraint === nothing + return -MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + primal_dual_map.primal_var_to_dual_con[vi], + ) + elseif data.dual_constraint isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} + return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - ci_dual, - )[idx] - else - return MOI.get( + data.dual_constraint, + )[data.primal_constrained_variable_index] + else + return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual, ) - end - else - return -MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - primal_dual_map.primal_var_to_dual_con[vi], - ) end + # if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) + # ci_primal, idx = + # primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] + # ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] + # if ci_dual === NO_CONSTRAINT + # return zero(T) + # elseif ci_dual isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} + # return MOI.get( + # optimizer.dual_problem.dual_model, + # MOI.ConstraintDual(), + # ci_dual, + # )[idx] + # else + # return MOI.get( + # optimizer.dual_problem.dual_model, + # MOI.ConstraintDual(), + # ci_dual, + # ) + # end + # else + # return -MOI.get( + # optimizer.dual_problem.dual_model, + # MOI.ConstraintDual(), + # primal_dual_map.primal_var_to_dual_con[vi], + # ) + # end end function MOI.get( diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index acc9e3b..f228ed0 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -45,16 +45,21 @@ function _select_constrained_variables( # no element of the VectorOfVariables is a constrained variable # and not a parameter vi -> - !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && - !(vi in params), + # !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && + !is_constrained(m, vi) && !(vi in params), f.variables, ) + # for (i, vi) in enumerate(f.variables) + # m.primal_convar_to_primal_convarcon_and_index[vi] = (ci, i) + # end + # # Placeholder to indicate this constraint is part of constrained variables, + # # it will be replaced later with a dual constraints + # m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT + # for (i, vi) in enumerate(f.variables) - m.primal_convar_to_primal_convarcon_and_index[vi] = (ci, i) + m.primal_variable_data[vi] = VariableData(ci, i, NO_CONSTRAINT) end - # Placeholder to indicate this constraint is part of constrained variables, - # it will be replaced later with a dual constraints - m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT + end end return @@ -74,16 +79,18 @@ function _select_constrained_variables( f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) # no element of the VectorOfVariables is a constrained variable # and not a parameter - if !haskey(m.primal_convar_to_primal_convarcon_and_index, f) && + if is_constrained(m, vi) + #!haskey(m.primal_convar_to_primal_convarcon_and_index, f) && !(f in params) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) if !iszero(MOI.constant(set)) continue end - m.primal_convar_to_primal_convarcon_and_index[f] = (ci, 1) - # Placeholder to indicate this constraint is part of constrained variables, - # it will be replaced later with a dual constraints - m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT + # m.primal_convar_to_primal_convarcon_and_index[f] = (ci, 1) + # # Placeholder to indicate this constraint is part of constrained variables, + # # it will be replaced later with a dual constraints + # m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT + m.primal_variable_data = VariableData(ci, 0, NO_CONSTRAINT) end end return diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index e6e5d13..a4dcdf5 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -81,10 +81,11 @@ function _add_dual_equality_constraints( # Free variables for primal_vi in non_parameter_variables - if haskey( - primal_dual_map.primal_convar_to_primal_convarcon_and_index, - primal_vi, - ) + if is_constrained(primal_dual_map, primal_vi) + # haskey( + # primal_dual_map.primal_convar_to_primal_convarcon_and_index, + # primal_vi, + # ) continue # constrained variable end # Add equality constraint diff --git a/src/dualize.jl b/src/dualize.jl index cbb7cdf..55f632c 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -96,6 +96,15 @@ function dualize( # If the Set constant of a MOI.VariableIndex-in-Set constraint is non-zero, # the respective primal variable will not be a constrained variable (with # respect to that constraint). + # + # Cache information of which primal variables are `constrained_variables` + # filling the a map: primal_variable_data, from original primal vars to: + # 1) constrained variable constraint (if constrained) and 2) index (if + # vector and constrained) and 3) dual constraint (always). + # the latter is only initialized with a marker NO_CONSTRAINT. + # If the Set constant of a MOI.VariableIndex-in-Set constraint is non-zero, + # the respective primal variable will not be a constrained variable (with + # respect to that constraint). if consider_constrained_variables _select_constrained_variables( dual_problem, diff --git a/src/structures.jl b/src/structures.jl index 20626ac..d47ed77 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -25,11 +25,42 @@ MOI.Utilities.@model( (MOI.VectorAffineFunction,) ) +mutable struct VariableData + # if primal variable is chosen to be a constrained variable by + # Dualization.jl, then this value is different from nothing + primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} + # if variable is a scalar constrained variable then it is 0 + # if variable is not a constrained variable then it is -1 + # if variable is part of a vector constrained variable, the this is the + # position in that vector + primal_constrained_variable_index::Int + # dual constraint associated with the variable + # if the variable is not constrained then the set is EqualTo + # if the variable is a constrained variable then the set is the dual set + # of the constrained variable set + dual_constraint::MOI.ConstraintIndex +end + +# constraints of primal constrained variables are not here +mutable struct ConstraintData + # a vector of primal set constants that are used in MOI getters + primal_set_constants::Vector + # vector of dual variables + # if primal constraint is scalat then, the vector has length = 1 + dual_variables::Vector{MOI.VariableIndex} + dual_constrained_variable_constraint::MOI.ConstraintIndex +end + """ PrimalDualMap{T} Maps information from all structures of the primal to the dual model. +Main maps: + + * `primal_variable_data::Dict{MOI.VariableIndex,Dualization.VariableData}`: + + The following abbreviations are used in the maps: * `var`: variable index @@ -99,9 +130,10 @@ Main maps: * `primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps primal variables (that appear in quadratic objective terms) to dual "slack" variables. These primal variables might appear in other maps. - Future name: primal_var_in_quad_obj_to_dual_slack_var """ mutable struct PrimalDualMap{T} + primal_variable_data::Dict{MOI.VariableIndex,VariableData} + primal_constraint_data::Dict{MOI.VariableIndex,ConstraintData} primal_convar_to_primal_convarcon_and_index::Dict{ MOI.VariableIndex, Tuple{MOI.ConstraintIndex,Int}, @@ -130,6 +162,9 @@ mutable struct PrimalDualMap{T} function PrimalDualMap{T}() where {T} return new( + Dict{MOI.VariableIndex,VariableData}(), + Dict{MOI.ConstraintIndex,ConstraintData}(), + # Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}(), Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), Dict{MOI.VariableIndex,MOI.ConstraintIndex}(), @@ -146,6 +181,15 @@ mutable struct PrimalDualMap{T} end end +function is_constrained(map::PrimalDualMap, vi::MOI.VariableIndex) + data = get(map.primal_variable_data, vi, nothing) + if data !== nothing && + data.primal_constrained_variable_constraint === nothing + return true + end + return false +end + function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} if name === :constrained_var_idx @warn "constrained_var_idx field is deprecated, use primal_convar_to_primal_convarcon_and_index instead" @@ -180,7 +224,10 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} end function is_empty(primal_dual_map::PrimalDualMap{T}) where {T} - return isempty( + return isempty(primal_dual_map.primal_variable_data) && + isempty(primal_dual_map.primal_constraint_data) && + # + isempty( primal_dual_map.primal_convar_to_primal_convarcon_and_index, ) && isempty(primal_dual_map.primal_convarcon_to_dual_con) && @@ -189,11 +236,15 @@ function is_empty(primal_dual_map::PrimalDualMap{T}) where {T} isempty(primal_dual_map.primal_con_to_dual_var_vec) && isempty(primal_dual_map.primal_con_to_dual_convarcon) && isempty(primal_dual_map.primal_con_to_primal_constants_vec) && + # isempty(primal_dual_map.primal_parameter_to_dual_parameter) && isempty(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) end function empty!(primal_dual_map::PrimalDualMap) + Base.empty!(primal_dual_map.primal_variable_data) + Base.empty!(primal_dual_map.primal_constraint_data) + # Base.empty!(primal_dual_map.primal_convar_to_primal_convarcon_and_index) Base.empty!(primal_dual_map.primal_convarcon_to_dual_con) Base.empty!(primal_dual_map.primal_convarcon_to_dual_function) @@ -201,6 +252,7 @@ function empty!(primal_dual_map::PrimalDualMap) Base.empty!(primal_dual_map.primal_con_to_dual_var_vec) Base.empty!(primal_dual_map.primal_con_to_dual_convarcon) Base.empty!(primal_dual_map.primal_con_to_primal_constants_vec) + # Base.empty!(primal_dual_map.primal_parameter_to_dual_parameter) Base.empty!(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) return diff --git a/test/Tests/test_dualize_conic_linear.jl b/test/Tests/test_dualize_conic_linear.jl index e480e68..4719866 100644 --- a/test/Tests/test_dualize_conic_linear.jl +++ b/test/Tests/test_dualize_conic_linear.jl @@ -80,9 +80,15 @@ for i in 1:3 vi = MOI.VariableIndex(i) - @test !haskey(primal_dual_map.primal_var_to_dual_con, vi) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] == - (ci_nneg, i) + # @test !haskey(primal_dual_map.primal_var_to_dual_con, vi) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] == + # (ci_nneg, i) + @test primal_dual_map.primal_variable_data[vi] == + Dualization.VariableData( + ci_nneg, + i, + NO_CONSTRAINT, #? + ) end end @@ -229,20 +235,35 @@ ) @test primal_con_to_dual_var_vec[ci_aff_zero] == MOI.VariableIndex.(1:3) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == ci_eq + # primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + # @test primal_var_to_dual_con[MOI.VariableIndex(1)] == ci_eq + # for i in 2:4 + # vi = MOI.VariableIndex(i) + # @test !haskey(primal_var_to_dual_con, vi) + # end + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(1)] + @test data.dual_constraint == ci_eq for i in 2:4 - vi = MOI.VariableIndex(i) - @test !haskey(primal_var_to_dual_con, vi) + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(i)] + @test data.dual_constraint != Dualization.NO_CONSTRAINT end - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 2, - )] == (ci_npos, 1) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 3, - )] == (ci_nneg, 1) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 4, - )] == (ci_zero, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 2, + # )] == (ci_npos, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 3, + # )] == (ci_nneg, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 4, + # )] == (ci_zero, 1) + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(2)] + @test data.primal_constrained_variable_constraint == ci_npos + @test data.primal_constrained_variable_index == 1 + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(3)] + @test data.primal_constrained_variable_constraint == ci_nneg + @test data.primal_constrained_variable_index == 1 + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(4)] + @test data.primal_constrained_variable_constraint == ci_zero + @test data.primal_constrained_variable_index == 1 end end From ff44c111d946a98349f53f50f1b637eb07578e7c Mon Sep 17 00:00:00 2001 From: joaquimg Date: Thu, 24 Apr 2025 22:43:46 -0300 Subject: [PATCH 3/5] Complete restructure of PrimalDualMap --- src/MOI_wrapper.jl | 150 +++++++++------------- src/constrained_variables.jl | 44 +++---- src/dual_equality_constraints.jl | 135 +++++++++---------- src/dual_model_variables.jl | 18 ++- src/dualize.jl | 15 +-- src/structures.jl | 96 ++++++-------- test/Tests/test_dual_names.jl | 36 ++++-- test/Tests/test_dualize_conic_linear.jl | 56 ++++---- test/Tests/test_dualize_exponential.jl | 23 ++-- test/Tests/test_dualize_power.jl | 19 ++- test/Tests/test_dualize_quadratic.jl | 18 +-- test/Tests/test_dualize_rsoc.jl | 14 +- test/Tests/test_dualize_sdp.jl | 20 +-- test/Tests/test_dualize_soc.jl | 19 +-- test/Tests/test_partial_dual_linear.jl | 46 ++++--- test/Tests/test_partial_dual_quadratic.jl | 69 ++++++---- test/Tests/test_structures.jl | 6 - 17 files changed, 376 insertions(+), 408 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 116988c..410d0a8 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -159,23 +159,13 @@ function MOI.modify( constant = -constant end vi = obj_change.variable - # if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - # ci_primal, index = - # primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - # ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - # if ci_dual === NO_CONSTRAINT - # return - # end - # constant = -constant - # else - # ci_dual = primal_dual_map.primal_var_to_dual_con[vi] - # index = 1 - # end data = get(primal_dual_map.primal_variable_data, vi, nothing) if data === nothing # error - elseif data.dual_constraint == NO_CONSTRAINT + elseif data.dual_constraint === nothing return + elseif data.primal_constrained_variable_constraint !== nothing + constant = -constant end _change_constant( optimizer.dual_problem.dual_model, @@ -257,53 +247,28 @@ function MOI.get( data = get(primal_dual_map.primal_variable_data, vi, nothing) if data === nothing # error - elseif data.dual_constraint == NO_CONSTRAINT + elseif data.dual_constraint === nothing return zero(T) elseif data.primal_constrained_variable_constraint === nothing return -MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - primal_dual_map.primal_var_to_dual_con[vi], + data.dual_constraint, ) - elseif data.dual_constraint isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} + elseif data.dual_constraint isa + MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} return MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - data.dual_constraint, - )[data.primal_constrained_variable_index] + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + data.dual_constraint, + )[data.primal_constrained_variable_index] else return MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - ci_dual, - ) + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + data.dual_constraint, + ) end - # if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - # ci_primal, idx = - # primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - # ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - # if ci_dual === NO_CONSTRAINT - # return zero(T) - # elseif ci_dual isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} - # return MOI.get( - # optimizer.dual_problem.dual_model, - # MOI.ConstraintDual(), - # ci_dual, - # )[idx] - # else - # return MOI.get( - # optimizer.dual_problem.dual_model, - # MOI.ConstraintDual(), - # ci_dual, - # ) - # end - # else - # return -MOI.get( - # optimizer.dual_problem.dual_model, - # MOI.ConstraintDual(), - # primal_dual_map.primal_var_to_dual_con[vi], - # ) - # end end function MOI.get( @@ -312,16 +277,17 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT + if haskey(primal_dual_map.primal_constrained_variables, ci) + vi = primal_dual_map.primal_constrained_variables[ci][] + ci_dual = primal_dual_map.primal_variable_data[vi].dual_constraint + if ci_dual === nothing return MOI.Utilities.eval_variables( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) do vi + primal_dual_map.primal_variable_data[vi].primal_function, + ) do inner_vi return MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - vi, + inner_vi, ) end end @@ -339,7 +305,7 @@ function MOI.get( return MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - primal_dual_map.primal_con_to_dual_var_vec[ci][], + primal_dual_map.primal_constraint_data[ci].dual_variables[], ) end end @@ -350,29 +316,32 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT - return MOI.Utilities.eval_variables( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) do vi - return MOI.get( - optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), - vi, - ) - end + if !haskey(primal_dual_map.primal_constraint_data, ci) + vis = primal_dual_map.primal_constrained_variables[ci] + ci_dual = primal_dual_map.primal_variable_data[vis[1]].dual_constraint + if ci_dual === nothing + return [ + MOI.Utilities.eval_variables( + primal_dual_map.primal_variable_data[vi].primal_function, + ) do inner_vi + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.VariablePrimal(), + inner_vi, + ) + end for vi in vis + ] end return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintPrimal(), - primal_dual_map.primal_convarcon_to_dual_con[ci], + ci_dual, ) else return MOI.get.( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - primal_dual_map.primal_con_to_dual_var_vec[ci], + primal_dual_map.primal_constraint_data[ci].dual_variables, ) end end @@ -383,9 +352,11 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT + data = get(primal_dual_map.primal_constraint_data, ci, nothing) + if data === nothing + first_vi = primal_dual_map.primal_constrained_variables[ci][1] + ci_dual = primal_dual_map.primal_variable_data[first_vi].dual_constraint + if ci_dual === nothing return zero(T) else return MOI.get( @@ -395,17 +366,16 @@ function MOI.get( ) end else - primal_ci_constant = - primal_dual_map.primal_con_to_primal_constants_vec[ci][1] + primal_ci_constant = data.primal_set_constants[1] # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) + ci_dual = data.dual_constrained_variable_constraint + if ci_dual === nothing return -primal_ci_constant end - ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - ci_dual_problem, + ci_dual, ) - primal_ci_constant end end @@ -416,13 +386,12 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT - n = MOI.output_dimension( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) - return zeros(T, n) + data = get(primal_dual_map.primal_constraint_data, ci, nothing) + if data === nothing + vis = primal_dual_map.primal_constrained_variables[ci] + ci_dual = primal_dual_map.primal_variable_data[vis[1]].dual_constraint + if ci_dual === nothing + return zeros(T, length(vis)) else return MOI.get( optimizer.dual_problem.dual_model, @@ -431,18 +400,17 @@ function MOI.get( ) end else + ci_dual = data.dual_constrained_variable_constraint # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) + if ci_dual === nothing # The number of dual variable associated with the primal constraint is the ci dimension - ci_dimension = - length(primal_dual_map.primal_con_to_dual_var_vec[ci]) + ci_dimension = length(data.dual_variables) return zeros(T, ci_dimension) end - ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - ci_dual_problem, + ci_dual, ) end end diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index f228ed0..4ecc19d 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -14,26 +14,27 @@ function _select_constrained_variables( primal_model, ) params = Set(variable_parameters) + selected_variables = Set{MOI.VariableIndex}() for S in single_or_vector_variables_types _select_constrained_variables( dual_problem.primal_dual_map, primal_model, S, params, + selected_variables, ) end return end -const NO_CONSTRAINT = MOI.ConstraintIndex{Nothing,Nothing}(0) - # Function barrier for the type instability of `F` and `S`. function _select_constrained_variables( - m::PrimalDualMap, + m::PrimalDualMap{T}, primal_model, ::Type{S}, params, -) where {S<:MOI.AbstractVectorSet} + selected_variables, +) where {T,S<:MOI.AbstractVectorSet} cis = MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}(), @@ -44,53 +45,40 @@ function _select_constrained_variables( if all( # no element of the VectorOfVariables is a constrained variable # and not a parameter - vi -> - # !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && - !is_constrained(m, vi) && !(vi in params), + vi -> !(vi in selected_variables) && !(vi in params), f.variables, ) - # for (i, vi) in enumerate(f.variables) - # m.primal_convar_to_primal_convarcon_and_index[vi] = (ci, i) - # end - # # Placeholder to indicate this constraint is part of constrained variables, - # # it will be replaced later with a dual constraints - # m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT - # - for (i, vi) in enumerate(f.variables) - m.primal_variable_data[vi] = VariableData(ci, i, NO_CONSTRAINT) + m.primal_constrained_variables[ci] = f.variables + for vi in f.variables + push!(selected_variables, vi) end - end end return end function _select_constrained_variables( - m::PrimalDualMap, + m::PrimalDualMap{T}, primal_model, ::Type{S}, params, -) where {S<:MOI.AbstractScalarSet} + selected_variables, +) where {T,S<:MOI.AbstractScalarSet} cis = MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VariableIndex,S}(), ) for ci in cis - f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) + vi = MOI.get(primal_model, MOI.ConstraintFunction(), ci) # no element of the VectorOfVariables is a constrained variable # and not a parameter - if is_constrained(m, vi) - #!haskey(m.primal_convar_to_primal_convarcon_and_index, f) && - !(f in params) + if !(vi in selected_variables) && !(vi in params) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) if !iszero(MOI.constant(set)) continue end - # m.primal_convar_to_primal_convarcon_and_index[f] = (ci, 1) - # # Placeholder to indicate this constraint is part of constrained variables, - # # it will be replaced later with a dual constraints - # m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT - m.primal_variable_data = VariableData(ci, 0, NO_CONSTRAINT) + m.primal_constrained_variables[ci] = [vi] + push!(selected_variables, vi) end end return diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index a4dcdf5..fbabaa5 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -6,7 +6,7 @@ function _add_dual_equality_constraints( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, - primal_dual_map::PrimalDualMap, + primal_dual_map::PrimalDualMap{T}, dual_names::DualNames, primal_objective::_PrimalObjective{T}, con_types::Vector{Tuple{Type,Type}}, @@ -27,7 +27,7 @@ function _add_dual_equality_constraints( # TODO: flip these signs a priorie instead of require post processing later scalar_affine_terms = _get_scalar_affine_terms( primal_model, - primal_dual_map.primal_con_to_dual_var_vec, + primal_dual_map.primal_constraint_data, all_variables, con_types, T, @@ -59,22 +59,22 @@ function _add_dual_equality_constraints( ) # Constrained variables - for ci in keys(primal_dual_map.primal_convarcon_to_dual_con) + for ci in keys(primal_dual_map.primal_constrained_variables) # Add constraints associated with constrained variables # These are constraints that will not be regular equality constraints # they will be function-in-set, where set is the dual set of the # primal constrained variable. + # If the dual set is Reals, the constraint is not added, butthe function + # is cached inthe primal dual map. # TODO: flip these signs a priori instead of requiring post-processing later _add_constrained_variable_constraint( dual_model, primal_model, - primal_dual_map.primal_convarcon_to_dual_function, - primal_dual_map.primal_convarcon_to_dual_con, + primal_dual_map, ci, scalar_affine_terms, scalar_terms, sense_change, - T, dual_names, ) end @@ -114,7 +114,8 @@ function _add_dual_equality_constraints( ) end # Add primal variable to dual contraint to the link dictionary - primal_dual_map.primal_var_to_dual_con[primal_vi] = dual_ci + primal_dual_map.primal_variable_data[primal_vi] = + VariableData{T}(nothing, -1, dual_ci, nothing) end return scalar_affine_terms end @@ -125,41 +126,39 @@ end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Zeros}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, ) where {T} # The dual is `Reals`, adding a constraint `func`-in-`Reals` is equivalent # to not adding any constraint. - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - zero_map[ci] = MOI.Utilities.vectorize([ - MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), - ) for primal_vi in func_primal.variables - ]) + vis = primal_dual_map.primal_constrained_variables[ci] + for (i, vi) in enumerate(vis) + primal_function = MOI.ScalarAffineFunction( + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), + ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, i, nothing, primal_function) + end return end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} + vis = primal_dual_map.primal_constrained_variables[ci] set_primal = MOI.get(primal_model, MOI.ConstraintSet(), ci) set_dual = MOI.dual_set(set_primal) - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) func_dual = MOI.Utilities.vectorize([ MOI.ScalarAffineFunction( MOI.Utilities.operate_term.( @@ -170,9 +169,13 @@ function _add_constrained_variable_constraint( sense_change * inv(set_dot(i, set_primal, T)) * get(scalar_terms, primal_vi, zero(T)), - ) for (i, primal_vi) in enumerate(func_primal.variables) + ) for (i, primal_vi) in enumerate(vis) ]) - ci_map[ci] = MOI.add_constraint(dual_model, func_dual, set_dual) + dual_ci = MOI.add_constraint(dual_model, func_dual, set_dual) + for (i, vi) in enumerate(vis) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, i, dual_ci, nothing) + end if !is_empty(dual_names) @warn( "dual names for constrained vector of variables not supported yet." @@ -184,56 +187,54 @@ end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, - ci::MOI.ConstraintIndex{MOI.VariableIndex,<:MOI.EqualTo}, + primal_dual_map::PrimalDualMap{T}, + ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} + vi = primal_dual_map.primal_constrained_variables[ci][] # Nothing to add as the set is `EqualTo`. - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - primal_vi = func_primal - zero_map[ci] = MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), + primal_function = MOI.ScalarAffineFunction( + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, 0, nothing, primal_function) return end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VariableIndex}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - primal_vi = func_primal + vi = primal_dual_map.primal_constrained_variables[ci][] func_dual = MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), ) set_primal = MOI.get(primal_model, MOI.ConstraintSet(), ci) set_dual = _dual_set(set_primal) - ci_map[ci] = MOI.Utilities.normalize_and_add_constraint( + dual_ci = MOI.Utilities.normalize_and_add_constraint( dual_model, func_dual, set_dual, ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, 0, dual_ci, nothing) if !is_empty(dual_names) _set_dual_constraint_name( dual_model, primal_model, - primal_vi, - ci_map[ci], + vi, + dual_ci, dual_names.dual_constraint_name_prefix, ) end @@ -340,10 +341,7 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ::Type{F}, ::Type{S}, @@ -351,7 +349,7 @@ function _fill_scalar_affine_terms!( for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) _fill_scalar_affine_terms!( scalar_affine_terms, - primal_con_to_dual_var_vec, + primal_constraint_data, primal_model, ci, ) @@ -361,10 +359,7 @@ end function _get_scalar_affine_terms( primal_model::MOI.ModelLike, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, variables::Vector{MOI.VariableIndex}, con_types::Vector{Tuple{Type,Type}}, ::Type{T}, @@ -376,7 +371,7 @@ function _get_scalar_affine_terms( for (F, S) in con_types _fill_scalar_affine_terms!( scalar_affine_terms, - primal_con_to_dual_var_vec, + primal_constraint_data, primal_model, F, S, @@ -401,16 +396,13 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) for term in moi_function.terms - dual_vi = primal_con_to_dual_var_vec[ci][1] # In this case we only have one vi + dual_vi = primal_constraint_data[ci].dual_variables[1] # In this case we only have one vi _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable], MOI.coefficient(term), @@ -425,22 +417,19 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} - dual_var = get(primal_con_to_dual_var_vec, ci, nothing) - if dual_var === nothing + data = get(primal_constraint_data, ci, nothing) + if data === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to # the dual constraint associated to that primal variable. return end moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - dual_vi = dual_var[1] # In this case we only have one vi + dual_vi = data.dual_variables[1] # In this case we only have one vi _push_to_scalar_affine_terms!( scalar_affine_terms[moi_function], one(T), @@ -454,17 +443,14 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},S}, ) where {T,S<:MOI.AbstractVectorSet} moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for term in moi_function.terms - dual_vi = primal_con_to_dual_var_vec[ci][term.output_index] + dual_vi = primal_constraint_data[ci].dual_variables[term.output_index] # term.output_index is the row of the MOI.VectorAffineFunction, # it corresponds to the dual variable associated with this constraint _push_to_scalar_affine_terms!( @@ -481,15 +467,12 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,S}, ) where {T,S<:MOI.AbstractVectorSet} - dual_vars = get(primal_con_to_dual_var_vec, ci, nothing) - if dual_vars === nothing + data = get(primal_constraint_data, ci, nothing) + if data === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to # the dual constraint associated to that primal variable. @@ -498,7 +481,7 @@ function _fill_scalar_affine_terms!( moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for (i, variable) in enumerate(moi_function.variables) - dual_vi = dual_vars[i] + dual_vi = data.dual_variables[i] _push_to_scalar_affine_terms!( scalar_affine_terms[variable], set_dot(i, set, T) * one(T), diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index bbe6e19..fe44042 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -37,8 +37,7 @@ function _add_dual_vars_in_dual_cones( ) where {T,F,S} for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) # If `F` not one of these two, we can skip the `haskey` check. - if (F === MOI.VectorOfVariables || F === MOI.VariableIndex) && - haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) + if haskey(primal_dual_map.primal_constrained_variables, ci) # primal constraints that are the main constraints of # constrained variables have no dual variable associated # bacause they associated with dual constraints @@ -69,7 +68,13 @@ function _add_dual_variable( ) where {T,F<:MOI.AbstractFunction,S<:MOI.AbstractSet} vis, con_index = _add_dual_cone_constraint(dual_model, primal_model, ci) # Add the map of the added dual variable to the relationated constraint - primal_dual_map.primal_con_to_dual_var_vec[ci] = vis + set_constant = if F <: MOI.AbstractScalarFunction + _get_normalized_constant(primal_model, ci) + else + T[] + end + primal_dual_map.primal_constraint_data[ci] = + ConstraintData(set_constant, vis, con_index) # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary @@ -87,13 +92,6 @@ function _add_dual_variable( MOI.set(dual_model, MOI.VariableName(), vi, pre * ci_name * "_$i") end end - if con_index !== nothing - primal_dual_map.primal_con_to_dual_convarcon[ci] = con_index - end - if F <: MOI.AbstractScalarFunction - primal_dual_map.primal_con_to_primal_constants_vec[ci] = - _get_normalized_constant(primal_model, ci) - end return end diff --git a/src/dualize.jl b/src/dualize.jl index 55f632c..9c490c9 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -88,23 +88,16 @@ function dualize( primal_objective = _get_primal_objective(primal_model, variable_parameters, T) - # Cache information of which primal variables are `constrained_variables` - # creating a map: primal_convar_to_primal_convarcon_and_index, from original primal vars to original - # constrains and their internal index (if vector constrains), 1 otherwise. - # Also, initializes the map: `primal_convarcon_to_dual_con`, from original primal ci - # to the dual constraint (latter is initilized as empty at this point). - # If the Set constant of a MOI.VariableIndex-in-Set constraint is non-zero, - # the respective primal variable will not be a constrained variable (with - # respect to that constraint). - # # Cache information of which primal variables are `constrained_variables` # filling the a map: primal_variable_data, from original primal vars to: # 1) constrained variable constraint (if constrained) and 2) index (if - # vector and constrained) and 3) dual constraint (always). - # the latter is only initialized with a marker NO_CONSTRAINT. + # vector and constrained) and 3) dual constraint (if dual set not `Reals`), + # 4) dual function (if dual set is `Reals`). + # Items 3 and 4 are initialized with `nothing` at this point. # If the Set constant of a MOI.VariableIndex-in-Set constraint is non-zero, # the respective primal variable will not be a constrained variable (with # respect to that constraint). + # Constrained variables are registered in `primal_constrained_variables`. if consider_constrained_variables _select_constrained_variables( dual_problem, diff --git a/src/structures.jl b/src/structures.jl index d47ed77..1c8f2d4 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -25,7 +25,10 @@ MOI.Utilities.@model( (MOI.VectorAffineFunction,) ) -mutable struct VariableData +""" + +""" +struct VariableData{T} # if primal variable is chosen to be a constrained variable by # Dualization.jl, then this value is different from nothing primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} @@ -38,17 +41,34 @@ mutable struct VariableData # if the variable is not constrained then the set is EqualTo # if the variable is a constrained variable then the set is the dual set # of the constrained variable set - dual_constraint::MOI.ConstraintIndex + # if the dual set is Reals then the field is kept as `nothing` + dual_constraint::Union{Nothing,MOI.ConstraintIndex} + # if the constrained variable is: + # * `VectorOfVariables`-in-`Zeros` + # * `VariableIndex`-in-`EqualTo(zero(T))` + # then the dual is `func`-in-`Reals`, which is "irrelevant" to the model. + # But this information is cached for completeness of the `DualOptimizer` for + # `get`ting `ConstraintDuals`. + primal_function::Union{ + Nothing, + MOI.ScalarAffineFunction{T}, + # MOI.VectorAffineFunction{T}, + } end # constraints of primal constrained variables are not here -mutable struct ConstraintData +""" + +""" +struct ConstraintData{T} # a vector of primal set constants that are used in MOI getters - primal_set_constants::Vector + primal_set_constants::Vector{T} # vector of dual variables - # if primal constraint is scalat then, the vector has length = 1 + # if primal constraint is scalar then, the vector has length = 1 dual_variables::Vector{MOI.VariableIndex} - dual_constrained_variable_constraint::MOI.ConstraintIndex + # if primal set is `EqualTo` or `Zeros`, then the dual constraint is `Reals` + # then the dual variable is free (no constraint in the dual model) + dual_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} end """ @@ -58,7 +78,7 @@ Maps information from all structures of the primal to the dual model. Main maps: - * `primal_variable_data::Dict{MOI.VariableIndex,Dualization.VariableData}`: + * `primal_variable_data::Dict{MOI.VariableIndex,Dualization.VariableData{T}}`: The following abbreviations are used in the maps: @@ -76,6 +96,7 @@ Main maps: their internal index from 1 to dimension(set) (if vector constraints: VectorOfVariables-in-Set), 1 otherwise (scalar: VariableIndex-in-Set). + INCOMPLE: needs primal_ci to primal_vi_vec * `primal_convarcon_to_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps the primal constraint index of constrained variables to the dual model's constraint index of the associated dual constraint. This dual @@ -120,7 +141,7 @@ Main maps: * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps parameters in the primal to parameters in the dual model. - * `primal_convarcon_to_dual_function::Dict{MOI.ConstraintIndex,Unions{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}}`: + * `primal_convarcon_to_dual_function::Dict{MOI.ConstraintIndex,Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}}`: caches scalar affine functions or vector affine functions associated with constrained variables of type `VectorOfVariables`-in-`Zeros` or `VariableIndex`-in-`EqualTo(zero(T))` as their duals would be `func`-in-`Reals`, @@ -132,50 +153,27 @@ Main maps: "slack" variables. These primal variables might appear in other maps. """ mutable struct PrimalDualMap{T} - primal_variable_data::Dict{MOI.VariableIndex,VariableData} - primal_constraint_data::Dict{MOI.VariableIndex,ConstraintData} - primal_convar_to_primal_convarcon_and_index::Dict{ - MOI.VariableIndex, - Tuple{MOI.ConstraintIndex,Int}, - } - primal_convarcon_to_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - primal_var_to_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex} - primal_con_to_dual_var_vec::Dict{ + primal_variable_data::Dict{MOI.VariableIndex,VariableData{T}} + primal_constraint_data::Dict{MOI.ConstraintIndex,ConstraintData{T}} + primal_constrained_variables::Dict{ MOI.ConstraintIndex, Vector{MOI.VariableIndex}, } - primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - - primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}} primal_parameter_to_dual_parameter::Dict{ MOI.VariableIndex, MOI.VariableIndex, } - primal_convarcon_to_dual_function::Dict{ - MOI.ConstraintIndex, - Union{MOI.VectorAffineFunction{T},MOI.ScalarAffineFunction{T}}, - } primal_var_in_quad_obj_to_dual_slack_var::Dict{ MOI.VariableIndex, MOI.VariableIndex, } - function PrimalDualMap{T}() where {T} return new( - Dict{MOI.VariableIndex,VariableData}(), - Dict{MOI.ConstraintIndex,ConstraintData}(), - # - Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}(), - Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), - Dict{MOI.VariableIndex,MOI.ConstraintIndex}(), + Dict{MOI.VariableIndex,VariableData{T}}(), + Dict{MOI.ConstraintIndex,ConstraintData{T}}(), Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}(), - Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), - Dict{MOI.ConstraintIndex,Vector{T}}(), + # Dict{MOI.VariableIndex,MOI.VariableIndex}(), - Dict{ - MOI.ConstraintIndex, - Union{MOI.VectorAffineFunction{T},MOI.ScalarAffineFunction{T}}, - }(), Dict{MOI.VariableIndex,MOI.VariableIndex}(), ) end @@ -184,7 +182,7 @@ end function is_constrained(map::PrimalDualMap, vi::MOI.VariableIndex) data = get(map.primal_variable_data, vi, nothing) if data !== nothing && - data.primal_constrained_variable_constraint === nothing + data.primal_constrained_variable_constraint !== nothing return true end return false @@ -225,17 +223,8 @@ end function is_empty(primal_dual_map::PrimalDualMap{T}) where {T} return isempty(primal_dual_map.primal_variable_data) && - isempty(primal_dual_map.primal_constraint_data) && - # - isempty( - primal_dual_map.primal_convar_to_primal_convarcon_and_index, - ) && - isempty(primal_dual_map.primal_convarcon_to_dual_con) && - isempty(primal_dual_map.primal_convarcon_to_dual_function) && - isempty(primal_dual_map.primal_var_to_dual_con) && - isempty(primal_dual_map.primal_con_to_dual_var_vec) && - isempty(primal_dual_map.primal_con_to_dual_convarcon) && - isempty(primal_dual_map.primal_con_to_primal_constants_vec) && + isempty(primal_dual_map.primal_constraint_data) && + isempty(primal_dual_map.primal_constrained_variables) && # isempty(primal_dual_map.primal_parameter_to_dual_parameter) && isempty(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) @@ -244,14 +233,7 @@ end function empty!(primal_dual_map::PrimalDualMap) Base.empty!(primal_dual_map.primal_variable_data) Base.empty!(primal_dual_map.primal_constraint_data) - # - Base.empty!(primal_dual_map.primal_convar_to_primal_convarcon_and_index) - Base.empty!(primal_dual_map.primal_convarcon_to_dual_con) - Base.empty!(primal_dual_map.primal_convarcon_to_dual_function) - Base.empty!(primal_dual_map.primal_var_to_dual_con) - Base.empty!(primal_dual_map.primal_con_to_dual_var_vec) - Base.empty!(primal_dual_map.primal_con_to_dual_convarcon) - Base.empty!(primal_dual_map.primal_con_to_primal_constants_vec) + Base.empty!(primal_dual_map.primal_constrained_variables) # Base.empty!(primal_dual_map.primal_parameter_to_dual_parameter) Base.empty!(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) diff --git a/test/Tests/test_dual_names.jl b/test/Tests/test_dual_names.jl index e9b45f5..9278846 100644 --- a/test/Tests/test_dual_names.jl +++ b/test/Tests/test_dual_names.jl @@ -32,23 +32,29 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, - )][1] - vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables[1] + vi_2 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )][1] + )].dual_variables[1] @test MOI.get(dual_model, MOI.VariableName(), vi_1) == "" @test MOI.get(dual_model, MOI.VariableName(), vi_2) == "" # Query constraint names - ci_1 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(2)] + ci_1 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 1, + )].dual_constraint + ci_2 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 2, + )].dual_constraint @test MOI.get(dual_model, MOI.ConstraintName(), ci_1) == "" @test MOI.get(dual_model, MOI.ConstraintName(), ci_2) == "" @@ -59,22 +65,28 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, - )][1] - vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables[1] + vi_2 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )][1] + )].dual_variables[1] @test MOI.get(dual_model, MOI.VariableName(), vi_2) == "dualvar_lessthan_1" # Query constraint names - ci_1 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(2)] + ci_1 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 1, + )].dual_constraint + ci_2 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 2, + )].dual_constraint @test MOI.get(dual_model, MOI.ConstraintName(), ci_1) == "dualcon_x1" @test MOI.get(dual_model, MOI.ConstraintName(), ci_2) == "dualcon_x2" end diff --git a/test/Tests/test_dualize_conic_linear.jl b/test/Tests/test_dualize_conic_linear.jl index 4719866..7c30047 100644 --- a/test/Tests/test_dualize_conic_linear.jl +++ b/test/Tests/test_dualize_conic_linear.jl @@ -55,7 +55,7 @@ @test MOI.get(dual_model, MOI.ConstraintSet(), ci) == MOI.Nonnegatives(3) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + primal_constraint_data = primal_dual_map.primal_constraint_data ci_zero = first( MOI.get( primal_model, @@ -65,7 +65,8 @@ }(), ), ) - @test primal_con_to_dual_var_vec[ci_zero] == MOI.VariableIndex.(1:2) + @test primal_constraint_data[ci_zero].dual_variables == + MOI.VariableIndex.(1:2) ci_nneg = first( MOI.get( primal_model, @@ -75,20 +76,19 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_nneg) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci + @test !haskey(primal_dual_map.primal_constraint_data, ci_nneg) + vis = primal_dual_map.primal_constrained_variables[ci_nneg] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == ci + end for i in 1:3 vi = MOI.VariableIndex(i) - # @test !haskey(primal_dual_map.primal_var_to_dual_con, vi) - # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] == - # (ci_nneg, i) - @test primal_dual_map.primal_variable_data[vi] == - Dualization.VariableData( - ci_nneg, - i, - NO_CONSTRAINT, #? - ) + data = primal_dual_map.primal_variable_data[vi] + @test data.primal_constrained_variable_constraint == ci_nneg + @test data.primal_constrained_variable_index == i + @test data.dual_constraint == ci + @test data.primal_function === nothing end end @@ -194,14 +194,13 @@ @test MOI.coefficient.(eq_con3_fun.terms) == -[1.0] @test MOI.constant(eq_con3_fun) == [-4.0] - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec ci_zero = first( MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Zeros}(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_zero) + @test !haskey(primal_dual_map.primal_constraint_data, ci_zero) ci_nneg = first( MOI.get( primal_model, @@ -211,8 +210,12 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_nneg) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci_nn + @test !haskey(primal_dual_map.primal_constraint_data, ci_nneg) + vis = primal_dual_map.primal_constrained_variables[ci_nneg] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == + ci_nn + end ci_npos = first( MOI.get( primal_model, @@ -222,8 +225,12 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_npos) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_npos] == ci_np + @test !haskey(primal_dual_map.primal_constraint_data, ci_npos) + vis = primal_dual_map.primal_constrained_variables[ci_npos] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == + ci_np + end ci_aff_zero = first( MOI.get( primal_model, @@ -233,19 +240,14 @@ }(), ), ) - @test primal_con_to_dual_var_vec[ci_aff_zero] == MOI.VariableIndex.(1:3) + @test primal_dual_map.primal_constraint_data[ci_aff_zero].dual_variables == + MOI.VariableIndex.(1:3) - # primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - # @test primal_var_to_dual_con[MOI.VariableIndex(1)] == ci_eq - # for i in 2:4 - # vi = MOI.VariableIndex(i) - # @test !haskey(primal_var_to_dual_con, vi) - # end data = primal_dual_map.primal_variable_data[MOI.VariableIndex(1)] @test data.dual_constraint == ci_eq for i in 2:4 data = primal_dual_map.primal_variable_data[MOI.VariableIndex(i)] - @test data.dual_constraint != Dualization.NO_CONSTRAINT + @test data.dual_constraint !== nothing end # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( # 2, diff --git a/test/Tests/test_dualize_exponential.jl b/test/Tests/test_dualize_exponential.jl index 0e8aee9..5910efb 100644 --- a/test/Tests/test_dualize_exponential.jl +++ b/test/Tests/test_dualize_exponential.jl @@ -125,19 +125,24 @@ ) @test dual_exp_con.variables == MOI.VariableIndex.(3:5) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[eq_con2] == [MOI.VariableIndex(2)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[eq_con2].dual_variables == + [MOI.VariableIndex(2)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone, }( 1, - )] == MOI.VariableIndex.(3:5) + )].dual_variables == MOI.VariableIndex.(3:5) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_power.jl b/test/Tests/test_dualize_power.jl index 2d79afd..d13fa75 100644 --- a/test/Tests/test_dualize_power.jl +++ b/test/Tests/test_dualize_power.jl @@ -125,13 +125,18 @@ dual_pow_con = MOI.get(dual_model, MOI.ConstraintFunction(), pow_con) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[eq_con2] == [MOI.VariableIndex(2)] + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[eq_con2].dual_variables == + [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_quadratic.jl b/test/Tests/test_dualize_quadratic.jl index ee6187e..1e6fd7d 100644 --- a/test/Tests/test_dualize_quadratic.jl +++ b/test/Tests/test_dualize_quadratic.jl @@ -124,36 +124,36 @@ @test MOI.constant.(eq_con3_fun) == 0.0 @test MOI.constant(eq_con3_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, diff --git a/test/Tests/test_dualize_rsoc.jl b/test/Tests/test_dualize_rsoc.jl index 8f11a19..62c17f9 100644 --- a/test/Tests/test_dualize_rsoc.jl +++ b/test/Tests/test_dualize_rsoc.jl @@ -125,16 +125,18 @@ ) @test rsoc_con.variables == MOI.VariableIndex.(1:4) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, }( 1, - )] == MOI.VariableIndex.(1:4) + )].dual_variables == MOI.VariableIndex.(1:4) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 end end diff --git a/test/Tests/test_dualize_sdp.jl b/test/Tests/test_dualize_sdp.jl index 0afb2f7..a588c55 100644 --- a/test/Tests/test_dualize_sdp.jl +++ b/test/Tests/test_dualize_sdp.jl @@ -125,18 +125,22 @@ sdp_con = MOI.get(dual_model, MOI.ConstraintFunction(), spd_con) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle, }( 1, - )] == MOI.VariableIndex.(2:4) + )].dual_variables == MOI.VariableIndex.(2:4) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_soc.jl b/test/Tests/test_dualize_soc.jl index 91b0129..152c32b 100644 --- a/test/Tests/test_dualize_soc.jl +++ b/test/Tests/test_dualize_soc.jl @@ -114,13 +114,13 @@ ) @test soc_con.variables == MOI.VariableIndex.(2:4) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.Zeros, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] primal_soc_con = first( MOI.get( primal_model, @@ -130,12 +130,15 @@ }(), ), ) - @test primal_con_to_dual_var_vec[primal_soc_con] == + @test primal_constraint_data[primal_soc_con].dual_variables == [MOI.VariableIndex(2); MOI.VariableIndex(3); MOI.VariableIndex(4)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_partial_dual_linear.jl b/test/Tests/test_partial_dual_linear.jl index b5302b1..3a436bb 100644 --- a/test/Tests/test_partial_dual_linear.jl +++ b/test/Tests/test_partial_dual_linear.jl @@ -81,16 +81,16 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, @@ -168,7 +168,7 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 3.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + primal_constraint_data = primal_dual_map.primal_constraint_data vaf_npos, = MOI.get( primal_model, MOI.ListOfConstraintIndices{ @@ -176,19 +176,25 @@ MOI.Nonpositives, }(), ) - @test primal_con_to_dual_var_vec[vaf_npos] == + @test primal_constraint_data[vaf_npos].dual_variables == [MOI.VariableIndex(1); MOI.VariableIndex(2)] - vgt, = MOI.get( + vgt1, vgt2 = MOI.get( primal_model, MOI.ListOfConstraintIndices{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }(), ) - @test primal_con_to_dual_var_vec[vgt] == [MOI.VariableIndex(3)] + @test primal_constraint_data[vgt1].dual_variables == + [MOI.VariableIndex(3)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test isempty(primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + vi = MOI.VariableIndex(2) + @test data.primal_constrained_variable_constraint == vgt2 + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.primal_function === nothing + end end @testset "lp12_test - x_1 and x_3 ignored" begin @@ -266,28 +272,28 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 2, - )] == [MOI.VariableIndex(3)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(3)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(2)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(2)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, diff --git a/test/Tests/test_partial_dual_quadratic.jl b/test/Tests/test_partial_dual_quadratic.jl index 6c47482..de14e64 100644 --- a/test/Tests/test_partial_dual_quadratic.jl +++ b/test/Tests/test_partial_dual_quadratic.jl @@ -105,29 +105,29 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, @@ -223,26 +223,37 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] @test !haskey( - primal_con_to_dual_var_vec, + primal_constraint_data, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), ) - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test isempty(primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + @test vi == MOI.VariableIndex(1) + @test data.primal_constrained_variable_constraint == + MOI.ConstraintIndex{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }( + 1, + ) + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.primal_function === nothing + end primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter @@ -330,25 +341,37 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] @test !(haskey( - primal_con_to_dual_var_vec, + primal_constraint_data, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), )) - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - @test isempty(primal_dual_map.primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + @test vi == MOI.VariableIndex(1) + @test data.primal_constrained_variable_constraint == + MOI.ConstraintIndex{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }( + 1, + ) + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.primal_function === nothing + end primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter diff --git a/test/Tests/test_structures.jl b/test/Tests/test_structures.jl index be1a244..2ccfd9d 100644 --- a/test/Tests/test_structures.jl +++ b/test/Tests/test_structures.jl @@ -6,12 +6,6 @@ @testset "structures" begin primal_dual_map = Dualization.PrimalDualMap{Float64}() @test Dualization.is_empty(primal_dual_map) - push!( - primal_dual_map.primal_var_to_dual_con, - MOI.VariableIndex(1) => - MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo}(1), - ) - @test !Dualization.is_empty(primal_dual_map) # Constructors model = lp1_test() From 90f26cfb8af86f97b33f1c07377994c43fe53d9f Mon Sep 17 00:00:00 2001 From: joaquimg Date: Thu, 24 Apr 2025 23:35:01 -0300 Subject: [PATCH 4/5] add docs, fix names and add errors --- docs/src/reference.md | 3 + src/MOI_wrapper.jl | 4 +- src/dual_equality_constraints.jl | 16 +- src/structures.jl | 244 ++++++++++++---------- test/Tests/test_dualize_conic_linear.jl | 2 +- test/Tests/test_partial_dual_linear.jl | 2 +- test/Tests/test_partial_dual_quadratic.jl | 4 +- 7 files changed, 145 insertions(+), 130 deletions(-) diff --git a/docs/src/reference.md b/docs/src/reference.md index 584b68e..e09f1c8 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -4,4 +4,7 @@ Dualization.supported_constraints Dualization.supported_objective Dualization.DualNames +Dualization.VariableData +Dualization.ConstraintData +Dualization.PrimalDualMap ``` \ No newline at end of file diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 410d0a8..d9a603f 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -282,7 +282,7 @@ function MOI.get( ci_dual = primal_dual_map.primal_variable_data[vi].dual_constraint if ci_dual === nothing return MOI.Utilities.eval_variables( - primal_dual_map.primal_variable_data[vi].primal_function, + primal_dual_map.primal_variable_data[vi].dual_function, ) do inner_vi return MOI.get( optimizer.dual_problem.dual_model, @@ -322,7 +322,7 @@ function MOI.get( if ci_dual === nothing return [ MOI.Utilities.eval_variables( - primal_dual_map.primal_variable_data[vi].primal_function, + primal_dual_map.primal_variable_data[vi].dual_function, ) do inner_vi return MOI.get( optimizer.dual_problem.dual_model, diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index fbabaa5..6e68e20 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -81,11 +81,9 @@ function _add_dual_equality_constraints( # Free variables for primal_vi in non_parameter_variables - if is_constrained(primal_dual_map, primal_vi) - # haskey( - # primal_dual_map.primal_convar_to_primal_convarcon_and_index, - # primal_vi, - # ) + data = get(primal_dual_map.primal_variable_data, primal_vi, nothing) + if data !== nothing && + data.primal_constrained_variable_constraint !== nothing continue # constrained variable end # Add equality constraint @@ -136,12 +134,12 @@ function _add_constrained_variable_constraint( # to not adding any constraint. vis = primal_dual_map.primal_constrained_variables[ci] for (i, vi) in enumerate(vis) - primal_function = MOI.ScalarAffineFunction( + dual_function = MOI.ScalarAffineFunction( MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), sense_change * get(scalar_terms, vi, zero(T)), ) primal_dual_map.primal_variable_data[vi] = - VariableData{T}(ci, i, nothing, primal_function) + VariableData{T}(ci, i, nothing, dual_function) end return end @@ -196,12 +194,12 @@ function _add_constrained_variable_constraint( ) where {T} vi = primal_dual_map.primal_constrained_variables[ci][] # Nothing to add as the set is `EqualTo`. - primal_function = MOI.ScalarAffineFunction( + dual_function = MOI.ScalarAffineFunction( MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), sense_change * get(scalar_terms, vi, zero(T)), ) primal_dual_map.primal_variable_data[vi] = - VariableData{T}(ci, 0, nothing, primal_function) + VariableData{T}(ci, 0, nothing, dual_function) return end diff --git a/src/structures.jl b/src/structures.jl index 1c8f2d4..7594324 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -26,48 +26,68 @@ MOI.Utilities.@model( ) """ + VariableData{T} + +Data structure used in `PrimalDualMap` to hold information about primal +variables and their dual counterparts. + + * `primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex}`: + if primal variable is chosen to be a constrained variable by + Dualization.jl, then this value is different from nothing. + + * `primal_constrained_variable_index::Int`: if variable is a scalar + constrained variable then it is 0. If variable is not a constrained variable + then it is -1. If variable is part of a vector constrained variable, then + this is the position in that vector. + + * `dual_constraint::Union{Nothing,MOI.ConstraintIndex}`: dual constraint + associated with the variable. If the variable is not constrained then the + set is EqualTo{T}(zero(T)). If the variable is a constrained variable then + the set is the dual set of the constrained variable set. If the dual set is + `Reals` then the field is kept as `nothing` as teh constraint is not added. + + * `dual_function::Union{Nothing,MOI.ScalarAffineFunction{T}}`: if the + constrained variable is `VectorOfVariables`-in-`Zeros` or + `VariableIndex`-in-`EqualTo(zero(T))` then the dual is `func`-in-`Reals`, + which is "irrelevant" to the model. But this information is cached for + completeness of the `DualOptimizer` for `get`ting `ConstraintDuals`. + +To got from the constrained variable constraint to the primal variable, use the +`primal_constrained_variables` field of `PrimalDualMap`. +See also `PrimalDualMap` and `ConstraintData`. """ struct VariableData{T} - # if primal variable is chosen to be a constrained variable by - # Dualization.jl, then this value is different from nothing primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} - # if variable is a scalar constrained variable then it is 0 - # if variable is not a constrained variable then it is -1 - # if variable is part of a vector constrained variable, the this is the - # position in that vector primal_constrained_variable_index::Int - # dual constraint associated with the variable - # if the variable is not constrained then the set is EqualTo - # if the variable is a constrained variable then the set is the dual set - # of the constrained variable set - # if the dual set is Reals then the field is kept as `nothing` dual_constraint::Union{Nothing,MOI.ConstraintIndex} - # if the constrained variable is: - # * `VectorOfVariables`-in-`Zeros` - # * `VariableIndex`-in-`EqualTo(zero(T))` - # then the dual is `func`-in-`Reals`, which is "irrelevant" to the model. - # But this information is cached for completeness of the `DualOptimizer` for - # `get`ting `ConstraintDuals`. - primal_function::Union{ - Nothing, - MOI.ScalarAffineFunction{T}, - # MOI.VectorAffineFunction{T}, - } + dual_function::Union{Nothing,MOI.ScalarAffineFunction{T}} end # constraints of primal constrained variables are not here """ + ConstraintData{T} + +Data structure used in `PrimalDualMap` to hold information about primal +constraints and their dual counterparts. + +Constraint indices for constrained variables are not in this structure. They are +added in the `primal_constrained_variables` field of `PrimalDualMap`. + + * `primal_set_constants::Vector{T}`: a vector of primal set constants that are + used in MOI getters. This is used to get the primal constants of the primal + constraints. + * `dual_variables::Vector{MOI.VariableIndex}`: vector of dual variables. If + primal constraint is scalar then, the vector has length = 1. + + * `dual_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex}`: + if primal set is `EqualTo` or `Zeros`, then the dual constraint is `Reals` + then the dual variable is free (no constraint in the dual model). """ struct ConstraintData{T} - # a vector of primal set constants that are used in MOI getters primal_set_constants::Vector{T} - # vector of dual variables - # if primal constraint is scalar then, the vector has length = 1 dual_variables::Vector{MOI.VariableIndex} - # if primal set is `EqualTo` or `Zeros`, then the dual constraint is `Reals` - # then the dual variable is free (no constraint in the dual model) dual_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} end @@ -79,74 +99,26 @@ Maps information from all structures of the primal to the dual model. Main maps: * `primal_variable_data::Dict{MOI.VariableIndex,Dualization.VariableData{T}}`: - + maps primal variable indices to their data. The data is a structure that + contains information about the primal variable and its dual counterpart. + In particular, it contains the primal constrained variable constraint index, + the primal constrained variable index, the dual constraint index and the + primal function for the case of constraints that are not added in the dual. -The following abbreviations are used in the maps: + * `primal_constraint_data::Dict{MOI.ConstraintIndex,Dualization.ConstraintData{T}}`: + maps primal constraint indices to their data. The data is a structure that + contains information about the primal constraint and its dual counterpart. + In particular, it contains the primal set constants, the dual variables and + the dual constrained variable constraint index. -* `var`: variable index -* `con`: constraint index -* `convar`: constrained variable, variable index -* `convarcon`: constrained variable, constraint index + * `primal_constrained_variables::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: + maps primal constrained variable constraint indices to their primal + constrained variables. -Main maps: +Addtional maps - * `primal_convar_to_primal_convarcon_and_index::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}`: - maps primal constrained variables to their primal - constraints (the special ones that makes them constrained variables) and - their internal index from 1 to dimension(set) (if vector constraints: - VectorOfVariables-in-Set), 1 otherwise (scalar: VariableIndex-in-Set). - - INCOMPLE: needs primal_ci to primal_vi_vec - * `primal_convarcon_to_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps - the primal constraint index of constrained variables to the dual - model's constraint index of the associated dual constraint. This dual - constraint is a regular constraint (not a constrained variable constraint). - `VectorOfVariables`-in-`Zeros` and `VariableIndex`-in-`EqualTo(zero(T))` - are not in this map, as they are not dualized (See - primal_convarcon_to_dual_function). - - * `primal_var_to_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex}`: maps - "free" primal variables to their associated dual (equality) constraints. - Free variables as opposed to constrained variables. Note that Dualization - will select automatically which variables are free and which are - constrained. - - * `primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: - maps primal constraint indices to vectors of dual variable indices. For - scalar constraints those vectors will be single element vectors. - Primal constrained variables constraints (the main ones) are not in this - map. However, `VariableIndex`-in-set and `VectorOfVariables`-in-set might - appear in this map if they were not chosen as the main ones. - - * `primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: - maps regular primal constraints to dual constrained variable. If the primal - constraint's set is EqualTo or Zeros, no constraint is added in the dual - variable (the dual variable is said to be free). - Primal constrained variables constraints (the main ones) are not in this - map. However, `VariableIndex`-in-set and `VectorOfVariables`-in-set might - appear in this map if they were not chosen as the main ones. - The keys are similar to the (# primal_con_to_dual_var_vec) map, except - that `VariableIndex`-in-`EqualTo(zero(T))` and `VectorOfVariables`-in-`Zeros` - are not in this map, as the dual constraint would belong to the `Reals` set, - and would be innocuous (hence, not added). - - Additional helper maps: - - * `primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}`: maps primal - constraints to their respective constants, which might be inside the set. - This map is used in `MOI.get(::DualOptimizer,::MOI.ConstraintPrimal,ci)` - that requires extra information in the case that the scalar set constains - a constant (`EqualtTo`, `GreaterThan`, `LessThan`). - - * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps - parameters in the primal to parameters in the dual model. - - * `primal_convarcon_to_dual_function::Dict{MOI.ConstraintIndex,Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}}`: - caches scalar affine functions or vector affine functions associated with - constrained variables of type `VectorOfVariables`-in-`Zeros` or - `VariableIndex`-in-`EqualTo(zero(T))` as their duals would be `func`-in-`Reals`, - which are "irrelevant" to the model. This information is cached for - completeness of the `DualOptimizer` for `get`ting `ConstraintDuals`. + * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: + maps parameters in the primal model to parameters in the dual model. * `primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps primal variables (that appear in quadratic objective terms) to dual @@ -179,40 +151,82 @@ mutable struct PrimalDualMap{T} end end -function is_constrained(map::PrimalDualMap, vi::MOI.VariableIndex) - data = get(map.primal_variable_data, vi, nothing) - if data !== nothing && - data.primal_constrained_variable_constraint !== nothing - return true - end - return false -end - function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} if name === :constrained_var_idx - @warn "constrained_var_idx field is deprecated, use primal_convar_to_primal_convarcon_and_index instead" - return getfield(m, :primal_convar_to_primal_convarcon_and_index) + error( + """ + Field `constrained_var_idx` was removed. + From a primal variable index, use the field `primal_variable_data`. + In the data structures returned the constraint can be found at + `primal_constrained_variable_constraint` and the index at + `primal_constrained_variable_index`. + """ + ) elseif name === :constrained_var_dual - @warn "constrained_var_dual field is deprecated, use primal_convarcon_to_dual_con instead" - return getfield(m, :primal_convarcon_to_dual_con) + error( + """ + Field `constrained_var_dual` was removed. + From a primal constrained variable constraint index, use the field + `primal_constrained_variables` to obtain the primal varaibles. + Then, from the primal variable index, use the field + `primal_variable_data`. + In the data structures returned, the constraint can be found at + `dual_constraint`. + """ + ) elseif name === :primal_var_dual_con - @warn "primal_var_dual_con field is deprecated, use primal_var_to_dual_con instead" - return getfield(m, :primal_var_to_dual_con) + error( + """ + Field `primal_var_dual_con` was removed. + From a primal variable index, use the field `primal_variable_data`. + In the data structures returned, the constraint can be found at + `dual_constraint`. + """ + ) elseif name === :primal_con_dual_var - @warn "primal_con_dual_var field is deprecated, use primal_con_to_dual_var_vec instead" - return getfield(m, :primal_con_to_dual_var_vec) + error( + """ + Field `primal_con_dual_var` was removed. + From a primal constraint index, use the field + `primal_constraint_data`. + In the data structure returned, the dual variables can be found at + `dual_varaibles`. + """ + ) elseif name === :primal_con_dual_con - @warn "primal_con_dual_con field is deprecated, use primal_con_to_dual_convarcon instead" - return getfield(m, :primal_con_to_dual_convarcon) + error( + """ + Field `primal_con_dual_con` was removed. + From a primal constraint index, use the field + `primal_constraint_data`. + In the data structure returned, the dual constrained variable + constraint can be found at `dual_constrained_variable_constraint`. + """ + ) elseif name === :primal_con_constants - @warn "primal_con_constants field is deprecated, use primal_con_to_primal_constants_vec instead" - return getfield(m, :primal_con_to_primal_constants_vec) + error( + """ + Field `primal_con_constants` was removed. + From a primal constraint index, use the field `primal_constraint_data`. + In the data structure returned, the primal set constants can be found + at `primal_set_constants`. + """ + ) + elseif name === :constrained_var_zero + error( + """ + Field `constrained_var_zero` was removed. + From a primal constrained variable constraint index, use the field + `primal_constrained_variables` to obtain the primal varaibles. + Then, from the primal variable index, use the field + `primal_variable_data`. + In the data structure returned, the primal function can be found at + `dual_function`. + """ + ) elseif name === :primal_parameter @warn "primal_parameter field is deprecated, use primal_parameter_to_dual_parameter instead" return getfield(m, :primal_parameter_to_dual_parameter) - elseif name === :constrained_var_zero - @warn "constrained_var_zero field is deprecated, use primal_convarcon_to_dual_function instead" - return getfield(m, :primal_convarcon_to_dual_function) elseif name === :primal_var_dual_quad_slack @warn "primal_var_dual_quad_slack field is deprecated, use primal_var_in_quad_obj_to_dual_slack_var instead" return getfield(m, :primal_var_in_quad_obj_to_dual_slack_var) diff --git a/test/Tests/test_dualize_conic_linear.jl b/test/Tests/test_dualize_conic_linear.jl index 7c30047..1ff782c 100644 --- a/test/Tests/test_dualize_conic_linear.jl +++ b/test/Tests/test_dualize_conic_linear.jl @@ -88,7 +88,7 @@ @test data.primal_constrained_variable_constraint == ci_nneg @test data.primal_constrained_variable_index == i @test data.dual_constraint == ci - @test data.primal_function === nothing + @test data.dual_function === nothing end end diff --git a/test/Tests/test_partial_dual_linear.jl b/test/Tests/test_partial_dual_linear.jl index 3a436bb..20455cd 100644 --- a/test/Tests/test_partial_dual_linear.jl +++ b/test/Tests/test_partial_dual_linear.jl @@ -193,7 +193,7 @@ @test data.primal_constrained_variable_constraint == vgt2 @test data.primal_constrained_variable_index == 0 @test data.dual_constraint !== nothing - @test data.primal_function === nothing + @test data.dual_function === nothing end end diff --git a/test/Tests/test_partial_dual_quadratic.jl b/test/Tests/test_partial_dual_quadratic.jl index de14e64..9621345 100644 --- a/test/Tests/test_partial_dual_quadratic.jl +++ b/test/Tests/test_partial_dual_quadratic.jl @@ -252,7 +252,7 @@ ) @test data.primal_constrained_variable_index == 0 @test data.dual_constraint !== nothing - @test data.primal_function === nothing + @test data.dual_function === nothing end primal_parameter_to_dual_parameter = @@ -370,7 +370,7 @@ ) @test data.primal_constrained_variable_index == 0 @test data.dual_constraint !== nothing - @test data.primal_function === nothing + @test data.dual_function === nothing end primal_parameter_to_dual_parameter = From 55a9f2653e4c205645e9c4ed14cbf3df68cd993b Mon Sep 17 00:00:00 2001 From: joaquimg Date: Thu, 24 Apr 2025 23:35:31 -0300 Subject: [PATCH 5/5] format --- src/structures.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/structures.jl b/src/structures.jl index 7594324..64775fc 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -160,7 +160,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} In the data structures returned the constraint can be found at `primal_constrained_variable_constraint` and the index at `primal_constrained_variable_index`. - """ + """, ) elseif name === :constrained_var_dual error( @@ -172,7 +172,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} `primal_variable_data`. In the data structures returned, the constraint can be found at `dual_constraint`. - """ + """, ) elseif name === :primal_var_dual_con error( @@ -181,7 +181,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} From a primal variable index, use the field `primal_variable_data`. In the data structures returned, the constraint can be found at `dual_constraint`. - """ + """, ) elseif name === :primal_con_dual_var error( @@ -191,7 +191,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} `primal_constraint_data`. In the data structure returned, the dual variables can be found at `dual_varaibles`. - """ + """, ) elseif name === :primal_con_dual_con error( @@ -201,7 +201,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} `primal_constraint_data`. In the data structure returned, the dual constrained variable constraint can be found at `dual_constrained_variable_constraint`. - """ + """, ) elseif name === :primal_con_constants error( @@ -210,7 +210,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} From a primal constraint index, use the field `primal_constraint_data`. In the data structure returned, the primal set constants can be found at `primal_set_constants`. - """ + """, ) elseif name === :constrained_var_zero error( @@ -222,7 +222,7 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} `primal_variable_data`. In the data structure returned, the primal function can be found at `dual_function`. - """ + """, ) elseif name === :primal_parameter @warn "primal_parameter field is deprecated, use primal_parameter_to_dual_parameter instead"